summaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/gui/CMakeLists.txt27
-rw-r--r--api/gui/DesktopServices.cpp174
-rw-r--r--api/gui/DesktopServices.h42
-rw-r--r--api/gui/SkinUtils.cpp26
-rw-r--r--api/gui/SkinUtils.h2
-rw-r--r--api/gui/icons/IconList.cpp581
-rw-r--r--api/gui/icons/IconList.h74
-rw-r--r--api/gui/icons/MMCIcon.cpp124
-rw-r--r--api/gui/icons/MMCIcon.h40
-rw-r--r--api/logic/BaseInstaller.cpp38
-rw-r--r--api/logic/BaseInstaller.h20
-rw-r--r--api/logic/BaseInstance.cpp261
-rw-r--r--api/logic/BaseInstance.h395
-rw-r--r--api/logic/BaseInstanceProvider.h57
-rw-r--r--api/logic/BaseVersion.h56
-rw-r--r--api/logic/BaseVersionList.cpp88
-rw-r--r--api/logic/BaseVersionList.h136
-rw-r--r--api/logic/CMakeLists.txt771
-rw-r--r--api/logic/Commandline.cpp732
-rw-r--r--api/logic/Commandline.h326
-rw-r--r--api/logic/DefaultVariable.h54
-rw-r--r--api/logic/Env.cpp236
-rw-r--r--api/logic/Env.h44
-rw-r--r--api/logic/Exception.h36
-rw-r--r--api/logic/ExponentialSeries.h43
-rw-r--r--api/logic/FileSystem.cpp641
-rw-r--r--api/logic/FileSystem.h50
-rw-r--r--api/logic/FileSystem_test.cpp274
-rw-r--r--api/logic/Filter.cpp16
-rw-r--r--api/logic/Filter.h30
-rw-r--r--api/logic/FolderInstanceProvider.cpp462
-rw-r--r--api/logic/FolderInstanceProvider.h75
-rw-r--r--api/logic/GZip.cpp208
-rw-r--r--api/logic/GZip.h4
-rw-r--r--api/logic/GZip_test.cpp74
-rw-r--r--api/logic/InstanceCopyTask.cpp65
-rw-r--r--api/logic/InstanceCopyTask.h22
-rw-r--r--api/logic/InstanceCreationTask.cpp34
-rw-r--r--api/logic/InstanceCreationTask.h10
-rw-r--r--api/logic/InstanceImportTask.cpp678
-rw-r--r--api/logic/InstanceImportTask.h53
-rw-r--r--api/logic/InstanceList.cpp982
-rw-r--r--api/logic/InstanceList.h189
-rw-r--r--api/logic/InstanceTask.h84
-rw-r--r--api/logic/Json.cpp268
-rw-r--r--api/logic/Json.h160
-rw-r--r--api/logic/LoggedProcess.cpp214
-rw-r--r--api/logic/LoggedProcess.h72
-rw-r--r--api/logic/MMCStrings.cpp126
-rw-r--r--api/logic/MMCStrings.h2
-rw-r--r--api/logic/MMCZip.cpp400
-rw-r--r--api/logic/MMCZip.h72
-rw-r--r--api/logic/MessageLevel.cpp54
-rw-r--r--api/logic/MessageLevel.h20
-rw-r--r--api/logic/NullInstance.h139
-rw-r--r--api/logic/ProblemProvider.h52
-rw-r--r--api/logic/QObjectPtr.h104
-rw-r--r--api/logic/RWStorage.h108
-rw-r--r--api/logic/RecursiveFileSystemWatcher.cpp142
-rw-r--r--api/logic/RecursiveFileSystemWatcher.h80
-rw-r--r--api/logic/SeparatorPrefixTree.h544
-rw-r--r--api/logic/Usable.h58
-rw-r--r--api/logic/Version.cpp94
-rw-r--r--api/logic/Version.h179
-rw-r--r--api/logic/Version_test.cpp98
-rw-r--r--api/logic/WatchLock.h20
-rw-r--r--api/logic/icons/IIconList.h24
-rw-r--r--api/logic/icons/IconUtils.cpp62
-rw-r--r--api/logic/icons/IconUtils.h14
-rw-r--r--api/logic/java/JavaChecker.cpp250
-rw-r--r--api/logic/java/JavaChecker.h72
-rw-r--r--api/logic/java/JavaCheckerJob.cpp34
-rw-r--r--api/logic/java/JavaCheckerJob.h55
-rw-r--r--api/logic/java/JavaInstall.cpp28
-rw-r--r--api/logic/java/JavaInstall.h48
-rw-r--r--api/logic/java/JavaInstallList.cpp224
-rw-r--r--api/logic/java/JavaInstallList.h62
-rw-r--r--api/logic/java/JavaUtils.cpp518
-rw-r--r--api/logic/java/JavaUtils.h14
-rw-r--r--api/logic/java/JavaVersion.cpp164
-rw-r--r--api/logic/java/JavaVersion.h68
-rw-r--r--api/logic/java/JavaVersion_test.cpp194
-rw-r--r--api/logic/java/launch/CheckJava.cpp196
-rw-r--r--api/logic/java/launch/CheckJava.h30
-rw-r--r--api/logic/launch/LaunchStep.cpp14
-rw-r--r--api/logic/launch/LaunchStep.h36
-rw-r--r--api/logic/launch/LaunchTask.cpp332
-rw-r--r--api/logic/launch/LaunchTask.h136
-rw-r--r--api/logic/launch/LogModel.cpp218
-rw-r--r--api/logic/launch/LogModel.h70
-rw-r--r--api/logic/launch/steps/PostLaunchCommand.cpp96
-rw-r--r--api/logic/launch/steps/PostLaunchCommand.h28
-rw-r--r--api/logic/launch/steps/PreLaunchCommand.cpp98
-rw-r--r--api/logic/launch/steps/PreLaunchCommand.h28
-rw-r--r--api/logic/launch/steps/TextPrint.cpp18
-rw-r--r--api/logic/launch/steps/TextPrint.h20
-rw-r--r--api/logic/launch/steps/Update.cpp86
-rw-r--r--api/logic/launch/steps/Update.h24
-rw-r--r--api/logic/meta/BaseEntity.cpp202
-rw-r--r--api/logic/meta/BaseEntity.h54
-rw-r--r--api/logic/meta/Index.cpp164
-rw-r--r--api/logic/meta/Index.h50
-rw-r--r--api/logic/meta/Index_test.cpp52
-rw-r--r--api/logic/meta/JsonFormat.cpp260
-rw-r--r--api/logic/meta/JsonFormat.h46
-rw-r--r--api/logic/meta/Version.cpp106
-rw-r--r--api/logic/meta/Version.h138
-rw-r--r--api/logic/meta/VersionList.cpp278
-rw-r--r--api/logic/meta/VersionList.h118
-rw-r--r--api/logic/minecraft/AssetsUtils.cpp442
-rw-r--r--api/logic/minecraft/AssetsUtils.h31
-rw-r--r--api/logic/minecraft/Component.cpp608
-rw-r--r--api/logic/minecraft/Component.h138
-rw-r--r--api/logic/minecraft/ComponentList.cpp1919
-rw-r--r--api/logic/minecraft/ComponentList.h144
-rw-r--r--api/logic/minecraft/ComponentList_p.h45
-rw-r--r--api/logic/minecraft/ComponentUpdateTask.cpp1129
-rw-r--r--api/logic/minecraft/ComponentUpdateTask.h30
-rw-r--r--api/logic/minecraft/ComponentUpdateTask_p.h32
-rw-r--r--api/logic/minecraft/GradleSpecifier.h266
-rw-r--r--api/logic/minecraft/GradleSpecifier_test.cpp126
-rw-r--r--api/logic/minecraft/LaunchProfile.cpp324
-rw-r--r--api/logic/minecraft/LaunchProfile.h140
-rw-r--r--api/logic/minecraft/Library.cpp537
-rw-r--r--api/logic/minecraft/Library.h336
-rw-r--r--api/logic/minecraft/Library_test.cpp498
-rw-r--r--api/logic/minecraft/MinecraftInstance.cpp1458
-rw-r--r--api/logic/minecraft/MinecraftInstance.h210
-rw-r--r--api/logic/minecraft/MinecraftLoadAndCheck.cpp50
-rw-r--r--api/logic/minecraft/MinecraftLoadAndCheck.h21
-rw-r--r--api/logic/minecraft/MinecraftUpdate.cpp250
-rw-r--r--api/logic/minecraft/MinecraftUpdate.h36
-rw-r--r--api/logic/minecraft/Mod.cpp378
-rw-r--r--api/logic/minecraft/Mod.h142
-rw-r--r--api/logic/minecraft/ModList.cpp367
-rw-r--r--api/logic/minecraft/ModList.h121
-rw-r--r--api/logic/minecraft/ModList_test.cpp53
-rw-r--r--api/logic/minecraft/MojangDownloadInfo.h110
-rw-r--r--api/logic/minecraft/MojangVersionFormat.cpp578
-rw-r--r--api/logic/minecraft/MojangVersionFormat.h20
-rw-r--r--api/logic/minecraft/MojangVersionFormat_test.cpp76
-rw-r--r--api/logic/minecraft/OneSixVersionFormat.cpp650
-rw-r--r--api/logic/minecraft/OneSixVersionFormat.h28
-rw-r--r--api/logic/minecraft/OpSys.cpp38
-rw-r--r--api/logic/minecraft/OpSys.h10
-rw-r--r--api/logic/minecraft/ParseUtils.cpp36
-rw-r--r--api/logic/minecraft/ParseUtils_test.cpp58
-rw-r--r--api/logic/minecraft/ProfileUtils.cpp264
-rw-r--r--api/logic/minecraft/Rule.cpp108
-rw-r--r--api/logic/minecraft/Rule.h96
-rw-r--r--api/logic/minecraft/SkinUpload.cpp80
-rw-r--r--api/logic/minecraft/SkinUpload.h33
-rw-r--r--api/logic/minecraft/VersionFile.cpp68
-rw-r--r--api/logic/minecraft/VersionFile.h118
-rw-r--r--api/logic/minecraft/VersionFilterData.cpp104
-rw-r--r--api/logic/minecraft/VersionFilterData.h24
-rw-r--r--api/logic/minecraft/World.cpp618
-rw-r--r--api/logic/minecraft/World.h117
-rw-r--r--api/logic/minecraft/WorldList.cpp519
-rw-r--r--api/logic/minecraft/WorldList.h178
-rw-r--r--api/logic/minecraft/auth/AuthSession.cpp32
-rw-r--r--api/logic/minecraft/auth/AuthSession.h70
-rw-r--r--api/logic/minecraft/auth/MojangAccount.cpp444
-rw-r--r--api/logic/minecraft/auth/MojangAccount.h172
-rw-r--r--api/logic/minecraft/auth/MojangAccountList.cpp684
-rw-r--r--api/logic/minecraft/auth/MojangAccountList.h306
-rw-r--r--api/logic/minecraft/auth/YggdrasilTask.cpp358
-rw-r--r--api/logic/minecraft/auth/YggdrasilTask.h213
-rw-r--r--api/logic/minecraft/auth/flows/AuthenticateTask.cpp314
-rw-r--r--api/logic/minecraft/auth/flows/AuthenticateTask.h16
-rw-r--r--api/logic/minecraft/auth/flows/RefreshTask.cpp190
-rw-r--r--api/logic/minecraft/auth/flows/RefreshTask.h14
-rw-r--r--api/logic/minecraft/auth/flows/ValidateTask.cpp34
-rw-r--r--api/logic/minecraft/auth/flows/ValidateTask.h14
-rw-r--r--api/logic/minecraft/forge/ForgeXzDownload.cpp644
-rw-r--r--api/logic/minecraft/forge/ForgeXzDownload.h48
-rw-r--r--api/logic/minecraft/gameoptions/GameOptions.cpp144
-rw-r--r--api/logic/minecraft/gameoptions/GameOptions.h34
-rw-r--r--api/logic/minecraft/launch/ClaimAccount.cpp20
-rw-r--r--api/logic/minecraft/launch/ClaimAccount.h24
-rw-r--r--api/logic/minecraft/launch/CreateServerResourcePacksFolder.cpp14
-rw-r--r--api/logic/minecraft/launch/CreateServerResourcePacksFolder.h18
-rw-r--r--api/logic/minecraft/launch/DirectJavaLaunch.cpp198
-rw-r--r--api/logic/minecraft/launch/DirectJavaLaunch.h40
-rw-r--r--api/logic/minecraft/launch/ExtractNatives.cpp122
-rw-r--r--api/logic/minecraft/launch/ExtractNatives.h20
-rw-r--r--api/logic/minecraft/launch/LauncherPartLaunch.cpp294
-rw-r--r--api/logic/minecraft/launch/LauncherPartLaunch.h44
-rw-r--r--api/logic/minecraft/launch/ModMinecraftJar.cpp92
-rw-r--r--api/logic/minecraft/launch/ModMinecraftJar.h22
-rw-r--r--api/logic/minecraft/launch/PrintInstanceInfo.cpp97
-rw-r--r--api/logic/minecraft/launch/PrintInstanceInfo.h20
-rw-r--r--api/logic/minecraft/launch/ReconstructAssets.cpp36
-rw-r--r--api/logic/minecraft/launch/ReconstructAssets.h33
-rw-r--r--api/logic/minecraft/launch/ScanModFolders.cpp54
-rw-r--r--api/logic/minecraft/launch/ScanModFolders.h42
-rw-r--r--api/logic/minecraft/legacy/LegacyInstance.cpp314
-rw-r--r--api/logic/minecraft/legacy/LegacyInstance.h220
-rw-r--r--api/logic/minecraft/legacy/LegacyModList.cpp231
-rw-r--r--api/logic/minecraft/legacy/LegacyModList.h41
-rw-r--r--api/logic/minecraft/legacy/LegacyUpgradeTask.cpp201
-rw-r--r--api/logic/minecraft/legacy/LegacyUpgradeTask.h20
-rw-r--r--api/logic/minecraft/mod/LocalModParseTask.cpp298
-rw-r--r--api/logic/minecraft/mod/LocalModParseTask.h37
-rw-r--r--api/logic/minecraft/mod/Mod.cpp151
-rw-r--r--api/logic/minecraft/mod/Mod.h117
-rw-r--r--api/logic/minecraft/mod/ModDetails.h17
-rw-r--r--api/logic/minecraft/mod/ModFolderLoadTask.cpp18
-rw-r--r--api/logic/minecraft/mod/ModFolderLoadTask.h29
-rw-r--r--api/logic/minecraft/mod/ModFolderModel.cpp554
-rw-r--r--api/logic/minecraft/mod/ModFolderModel.h145
-rw-r--r--api/logic/minecraft/mod/ModFolderModel_test.cpp53
-rw-r--r--api/logic/minecraft/testdata/lib-native-arch.json88
-rw-r--r--api/logic/minecraft/testdata/lib-native.json100
-rw-r--r--api/logic/minecraft/testdata/lib-simple.json18
-rw-r--r--api/logic/minecraft/update/AssetUpdateTask.cpp129
-rw-r--r--api/logic/minecraft/update/AssetUpdateTask.h22
-rw-r--r--api/logic/minecraft/update/FMLLibrariesTask.cpp189
-rw-r--r--api/logic/minecraft/update/FMLLibrariesTask.h21
-rw-r--r--api/logic/minecraft/update/FoldersTask.cpp20
-rw-r--r--api/logic/minecraft/update/FoldersTask.h10
-rw-r--r--api/logic/minecraft/update/LibrariesTask.cpp123
-rw-r--r--api/logic/minecraft/update/LibrariesTask.h17
-rw-r--r--api/logic/modplatform/flame/FileResolvingTask.cpp150
-rw-r--r--api/logic/modplatform/flame/FileResolvingTask.h24
-rw-r--r--api/logic/modplatform/flame/PackManifest.cpp148
-rw-r--r--api/logic/modplatform/flame/PackManifest.h69
-rw-r--r--api/logic/modplatform/flame/UrlResolvingTask.cpp175
-rw-r--r--api/logic/modplatform/flame/UrlResolvingTask.h43
-rw-r--r--api/logic/modplatform/ftb/FtbPackFetchTask.cpp243
-rw-r--r--api/logic/modplatform/ftb/FtbPackFetchTask.h31
-rw-r--r--api/logic/modplatform/ftb/FtbPackInstallTask.cpp297
-rw-r--r--api/logic/modplatform/ftb/FtbPackInstallTask.h51
-rw-r--r--api/logic/modplatform/ftb/FtbPrivatePackManager.cpp37
-rw-r--r--api/logic/modplatform/ftb/FtbPrivatePackManager.h40
-rw-r--r--api/logic/modplatform/ftb/PackHelpers.h47
-rw-r--r--api/logic/net/ByteArraySink.h88
-rw-r--r--api/logic/net/ChecksumValidator.h78
-rw-r--r--api/logic/net/Download.cpp418
-rw-r--r--api/logic/net/Download.h62
-rw-r--r--api/logic/net/FileSink.cpp142
-rw-r--r--api/logic/net/FileSink.h24
-rw-r--r--api/logic/net/HttpMetaCache.cpp368
-rw-r--r--api/logic/net/HttpMetaCache.h154
-rw-r--r--api/logic/net/MetaCacheSink.cpp78
-rw-r--r--api/logic/net/MetaCacheSink.h14
-rw-r--r--api/logic/net/Mode.h4
-rw-r--r--api/logic/net/NetAction.h134
-rw-r--r--api/logic/net/NetJob.cpp314
-rw-r--r--api/logic/net/NetJob.h98
-rw-r--r--api/logic/net/PasteUpload.cpp128
-rw-r--r--api/logic/net/PasteUpload.h63
-rw-r--r--api/logic/net/Sink.h102
-rw-r--r--api/logic/net/URLConstants.cpp4
-rw-r--r--api/logic/net/URLConstants.h26
-rw-r--r--api/logic/net/Validator.h12
-rw-r--r--api/logic/news/NewsChecker.cpp146
-rw-r--r--api/logic/news/NewsChecker.h118
-rw-r--r--api/logic/news/NewsEntry.cpp74
-rw-r--r--api/logic/news/NewsEntry.h54
-rw-r--r--api/logic/notifications/NotificationChecker.cpp158
-rw-r--r--api/logic/notifications/NotificationChecker.h74
-rw-r--r--api/logic/pathmatcher/FSTreeMatcher.h18
-rw-r--r--api/logic/pathmatcher/IPathMatcher.h6
-rw-r--r--api/logic/pathmatcher/MultiMatcher.h42
-rw-r--r--api/logic/pathmatcher/RegexpMatcher.h66
-rw-r--r--api/logic/screenshots/ImgurAlbumCreation.cpp112
-rw-r--r--api/logic/screenshots/ImgurAlbumCreation.h46
-rw-r--r--api/logic/screenshots/ImgurUpload.cpp160
-rw-r--r--api/logic/screenshots/ImgurUpload.h28
-rw-r--r--api/logic/screenshots/Screenshot.h16
-rw-r--r--api/logic/settings/INIFile.cpp204
-rw-r--r--api/logic/settings/INIFile.h18
-rw-r--r--api/logic/settings/INIFile_test.cpp97
-rw-r--r--api/logic/settings/INISettingsObject.cpp110
-rw-r--r--api/logic/settings/INISettingsObject.h50
-rw-r--r--api/logic/settings/OverrideSetting.cpp30
-rw-r--r--api/logic/settings/OverrideSetting.h20
-rw-r--r--api/logic/settings/PassthroughSetting.cpp60
-rw-r--r--api/logic/settings/PassthroughSetting.h20
-rw-r--r--api/logic/settings/Setting.cpp34
-rw-r--r--api/logic/settings/Setting.h148
-rw-r--r--api/logic/settings/SettingsObject.cpp142
-rw-r--r--api/logic/settings/SettingsObject.h314
-rw-r--r--api/logic/status/StatusChecker.cpp168
-rw-r--r--api/logic/status/StatusChecker.h38
-rw-r--r--api/logic/tasks/SequentialTask.cpp56
-rw-r--r--api/logic/tasks/SequentialTask.h21
-rw-r--r--api/logic/tasks/Task.cpp168
-rw-r--r--api/logic/tasks/Task.h108
-rw-r--r--api/logic/tools/BaseExternalTool.cpp10
-rw-r--r--api/logic/tools/BaseExternalTool.h34
-rw-r--r--api/logic/tools/BaseProfiler.cpp27
-rw-r--r--api/logic/tools/BaseProfiler.h21
-rw-r--r--api/logic/tools/JProfiler.cpp116
-rw-r--r--api/logic/tools/JProfiler.h10
-rw-r--r--api/logic/tools/JVisualVM.cpp100
-rw-r--r--api/logic/tools/JVisualVM.h10
-rw-r--r--api/logic/tools/MCEditTool.cpp84
-rw-r--r--api/logic/tools/MCEditTool.h12
-rw-r--r--api/logic/translations/POTranslator.cpp373
-rw-r--r--api/logic/translations/POTranslator.h16
-rw-r--r--api/logic/translations/TranslationsModel.cpp804
-rw-r--r--api/logic/translations/TranslationsModel.h54
-rw-r--r--api/logic/updater/DownloadTask.cpp191
-rw-r--r--api/logic/updater/DownloadTask.h111
-rw-r--r--api/logic/updater/DownloadTask_test.cpp336
-rw-r--r--api/logic/updater/GoUpdate.cpp382
-rw-r--r--api/logic/updater/GoUpdate.h140
-rw-r--r--api/logic/updater/UpdateChecker.cpp400
-rw-r--r--api/logic/updater/UpdateChecker.h170
-rw-r--r--api/logic/updater/UpdateChecker_test.cpp243
-rw-r--r--api/logic/updater/testdata/1.json82
-rw-r--r--api/logic/updater/testdata/2.json58
-rw-r--r--api/logic/updater/testdata/channels.json42
-rw-r--r--api/logic/updater/testdata/errorChannels.json42
-rw-r--r--api/logic/updater/testdata/garbageChannels.json40
-rw-r--r--api/logic/updater/testdata/index.json14
-rw-r--r--api/logic/updater/testdata/noChannels.json6
-rw-r--r--api/logic/updater/testdata/oneChannel.json18
320 files changed, 25292 insertions, 23179 deletions
diff --git a/api/gui/CMakeLists.txt b/api/gui/CMakeLists.txt
index deae735f..ad116a43 100644
--- a/api/gui/CMakeLists.txt
+++ b/api/gui/CMakeLists.txt
@@ -1,17 +1,17 @@
project(MultiMC_gui LANGUAGES CXX)
set(GUI_SOURCES
- DesktopServices.h
- DesktopServices.cpp
+ DesktopServices.h
+ DesktopServices.cpp
- # Icons
- icons/MMCIcon.h
- icons/MMCIcon.cpp
- icons/IconList.h
- icons/IconList.cpp
+ # Icons
+ icons/MMCIcon.h
+ icons/MMCIcon.cpp
+ icons/IconList.h
+ icons/IconList.cpp
- SkinUtils.cpp
- SkinUtils.h
+ SkinUtils.cpp
+ SkinUtils.h
)
################################ COMPILE ################################
@@ -21,15 +21,14 @@ set_target_properties(MultiMC_gui PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBI
generate_export_header(MultiMC_gui)
# Link
-target_link_libraries(MultiMC_gui MultiMC_iconfix MultiMC_logic)
-qt5_use_modules(MultiMC_gui Gui)
+target_link_libraries(MultiMC_gui MultiMC_iconfix MultiMC_logic Qt5::Gui)
# Mark and export headers
target_include_directories(MultiMC_gui PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
# Install it
install(
- TARGETS MultiMC_gui
- RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
- LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
+ TARGETS MultiMC_gui
+ RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
+ LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
) \ No newline at end of file
diff --git a/api/gui/DesktopServices.cpp b/api/gui/DesktopServices.cpp
index 3154ea01..5368ddc8 100644
--- a/api/gui/DesktopServices.cpp
+++ b/api/gui/DesktopServices.cpp
@@ -17,132 +17,132 @@
template <typename T>
bool IndirectOpen(T callable, qint64 *pid_forked = nullptr)
{
- auto pid = fork();
- if(pid_forked)
- {
- if(pid > 0)
- *pid_forked = pid;
- else
- *pid_forked = 0;
- }
- if(pid == -1)
- {
- qWarning() << "IndirectOpen failed to fork: " << errno;
- return false;
- }
- // child - do the stuff
- if(pid == 0)
- {
- // unset all this garbage so it doesn't get passed to the child process
- qunsetenv("LD_PRELOAD");
- qunsetenv("LD_LIBRARY_PATH");
- qunsetenv("LD_DEBUG");
- qunsetenv("QT_PLUGIN_PATH");
- qunsetenv("QT_FONTPATH");
+ auto pid = fork();
+ if(pid_forked)
+ {
+ if(pid > 0)
+ *pid_forked = pid;
+ else
+ *pid_forked = 0;
+ }
+ if(pid == -1)
+ {
+ qWarning() << "IndirectOpen failed to fork: " << errno;
+ return false;
+ }
+ // child - do the stuff
+ if(pid == 0)
+ {
+ // unset all this garbage so it doesn't get passed to the child process
+ qunsetenv("LD_PRELOAD");
+ qunsetenv("LD_LIBRARY_PATH");
+ qunsetenv("LD_DEBUG");
+ qunsetenv("QT_PLUGIN_PATH");
+ qunsetenv("QT_FONTPATH");
- // open the URL
- auto status = callable();
+ // open the URL
+ auto status = callable();
- // detach from the parent process group.
- setsid();
+ // detach from the parent process group.
+ setsid();
- // die. now. do not clean up anything, it would just hang forever.
- _exit(status ? 0 : 1);
- }
- else
- {
- //parent - assume it worked.
- int status;
- while (waitpid(pid, &status, 0))
- {
- if(WIFEXITED(status))
- {
- return WEXITSTATUS(status) == 0;
- }
- if(WIFSIGNALED(status))
- {
- return false;
- }
- }
- return true;
- }
+ // die. now. do not clean up anything, it would just hang forever.
+ _exit(status ? 0 : 1);
+ }
+ else
+ {
+ //parent - assume it worked.
+ int status;
+ while (waitpid(pid, &status, 0))
+ {
+ if(WIFEXITED(status))
+ {
+ return WEXITSTATUS(status) == 0;
+ }
+ if(WIFSIGNALED(status))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
}
#endif
namespace DesktopServices {
bool openDirectory(const QString &path, bool ensureExists)
{
- qDebug() << "Opening directory" << path;
- QDir parentPath;
- QDir dir(path);
- if (!dir.exists())
- {
- parentPath.mkpath(dir.absolutePath());
- }
- auto f = [&]()
- {
- return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
- };
+ qDebug() << "Opening directory" << path;
+ QDir parentPath;
+ QDir dir(path);
+ if (!dir.exists())
+ {
+ parentPath.mkpath(dir.absolutePath());
+ }
+ auto f = [&]()
+ {
+ return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
+ };
#if defined(Q_OS_LINUX)
- return IndirectOpen(f);
+ return IndirectOpen(f);
#else
- return f();
+ return f();
#endif
}
bool openFile(const QString &path)
{
- qDebug() << "Opening file" << path;
- auto f = [&]()
- {
- return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
- };
+ qDebug() << "Opening file" << path;
+ auto f = [&]()
+ {
+ return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
+ };
#if defined(Q_OS_LINUX)
- return IndirectOpen(f);
+ return IndirectOpen(f);
#else
- return f();
+ return f();
#endif
}
bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid)
{
- qDebug() << "Opening file" << path << "using" << application;
+ qDebug() << "Opening file" << path << "using" << application;
#if defined(Q_OS_LINUX)
- // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
- return IndirectOpen([&]()
- {
- return QProcess::startDetached(application, QStringList() << path, workingDirectory);
- }, pid);
+ // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
+ return IndirectOpen([&]()
+ {
+ return QProcess::startDetached(application, QStringList() << path, workingDirectory);
+ }, pid);
#else
- return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
+ return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
#endif
}
bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid)
{
- qDebug() << "Running" << application << "with args" << args.join(' ');
+ qDebug() << "Running" << application << "with args" << args.join(' ');
#if defined(Q_OS_LINUX)
- // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
- return IndirectOpen([&]()
- {
- return QProcess::startDetached(application, args, workingDirectory);
- }, pid);
+ // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
+ return IndirectOpen([&]()
+ {
+ return QProcess::startDetached(application, args, workingDirectory);
+ }, pid);
#else
- return QProcess::startDetached(application, args, workingDirectory, pid);
+ return QProcess::startDetached(application, args, workingDirectory, pid);
#endif
}
bool openUrl(const QUrl &url)
{
- qDebug() << "Opening URL" << url.toString();
- auto f = [&]()
- {
- return QDesktopServices::openUrl(url);
- };
+ qDebug() << "Opening URL" << url.toString();
+ auto f = [&]()
+ {
+ return QDesktopServices::openUrl(url);
+ };
#if defined(Q_OS_LINUX)
- return IndirectOpen(f);
+ return IndirectOpen(f);
#else
- return f();
+ return f();
#endif
}
diff --git a/api/gui/DesktopServices.h b/api/gui/DesktopServices.h
index 9daf192a..606fa52c 100644
--- a/api/gui/DesktopServices.h
+++ b/api/gui/DesktopServices.h
@@ -10,28 +10,28 @@
*/
namespace DesktopServices
{
- /**
- * Open a file in whatever application is applicable
- */
- MULTIMC_GUI_EXPORT bool openFile(const QString &path);
+ /**
+ * Open a file in whatever application is applicable
+ */
+ MULTIMC_GUI_EXPORT bool openFile(const QString &path);
- /**
- * Open a file in the specified application
- */
- MULTIMC_GUI_EXPORT bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0);
+ /**
+ * Open a file in the specified application
+ */
+ MULTIMC_GUI_EXPORT bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0);
- /**
- * Run an application
- */
- MULTIMC_GUI_EXPORT bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0);
+ /**
+ * Run an application
+ */
+ MULTIMC_GUI_EXPORT bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0);
- /**
- * Open a directory
- */
- MULTIMC_GUI_EXPORT bool openDirectory(const QString &path, bool ensureExists = false);
+ /**
+ * Open a directory
+ */
+ MULTIMC_GUI_EXPORT bool openDirectory(const QString &path, bool ensureExists = false);
- /**
- * Open the URL, most likely in a browser. Maybe.
- */
- MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url);
-};
+ /**
+ * Open the URL, most likely in a browser. Maybe.
+ */
+ MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url);
+}
diff --git a/api/gui/SkinUtils.cpp b/api/gui/SkinUtils.cpp
index 3950cbc0..9b6f63a4 100644
--- a/api/gui/SkinUtils.cpp
+++ b/api/gui/SkinUtils.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,19 +29,19 @@ namespace SkinUtils
*/
QPixmap getFaceFromCache(QString username, int height, int width)
{
- QFile fskin(ENV.metacache()
- ->resolveEntry("skins", username + ".png")
- ->getFullPath());
+ QFile fskin(ENV.metacache()
+ ->resolveEntry("skins", username + ".png")
+ ->getFullPath());
- if (fskin.exists())
- {
- QPixmap skin(fskin.fileName());
- if(!skin.isNull())
- {
- return skin.copy(8, 8, 8, 8).scaled(height, width, Qt::KeepAspectRatio);
- }
- }
+ if (fskin.exists())
+ {
+ QPixmap skin(fskin.fileName());
+ if(!skin.isNull())
+ {
+ return skin.copy(8, 8, 8, 8).scaled(height, width, Qt::KeepAspectRatio);
+ }
+ }
- return QPixmap();
+ return QPixmap();
}
}
diff --git a/api/gui/SkinUtils.h b/api/gui/SkinUtils.h
index f042b908..a7f680e6 100644
--- a/api/gui/SkinUtils.h
+++ b/api/gui/SkinUtils.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/api/gui/icons/IconList.cpp b/api/gui/icons/IconList.cpp
index 997a03db..72edb46f 100644
--- a/api/gui/icons/IconList.cpp
+++ b/api/gui/icons/IconList.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,394 +27,393 @@
IconList::IconList(const QStringList &builtinPaths, QString path, QObject *parent) : QAbstractListModel(parent)
{
- QSet<QString> builtinNames;
-
- // add builtin icons
- for(auto & builtinPath: builtinPaths)
- {
- QDir instance_icons(builtinPath);
- auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name);
- for (auto file_info : file_info_list)
- {
- builtinNames.insert(file_info.baseName());
- }
- }
- for(auto & builtinName : builtinNames)
- {
- addThemeIcon(builtinName);
- }
-
- m_watcher.reset(new QFileSystemWatcher());
- is_watching = false;
- connect(m_watcher.get(), SIGNAL(directoryChanged(QString)),
- SLOT(directoryChanged(QString)));
- connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString)));
-
- directoryChanged(path);
+ QSet<QString> builtinNames;
+
+ // add builtin icons
+ for(auto & builtinPath: builtinPaths)
+ {
+ QDir instance_icons(builtinPath);
+ auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name);
+ for (auto file_info : file_info_list)
+ {
+ builtinNames.insert(file_info.baseName());
+ }
+ }
+ for(auto & builtinName : builtinNames)
+ {
+ addThemeIcon(builtinName);
+ }
+
+ m_watcher.reset(new QFileSystemWatcher());
+ is_watching = false;
+ connect(m_watcher.get(), SIGNAL(directoryChanged(QString)),
+ SLOT(directoryChanged(QString)));
+ connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString)));
+
+ directoryChanged(path);
}
void IconList::directoryChanged(const QString &path)
{
- QDir new_dir (path);
- if(m_dir.absolutePath() != new_dir.absolutePath())
- {
- m_dir.setPath(path);
- m_dir.refresh();
- if(is_watching)
- stopWatching();
- startWatching();
- }
- if(!m_dir.exists())
- if(!FS::ensureFolderPathExists(m_dir.absolutePath()))
- return;
- m_dir.refresh();
- auto new_list = m_dir.entryList(QDir::Files, QDir::Name);
- for (auto it = new_list.begin(); it != new_list.end(); it++)
- {
- QString &foo = (*it);
- foo = m_dir.filePath(foo);
- }
- auto new_set = new_list.toSet();
- QList<QString> current_list;
- for (auto &it : icons)
- {
- if (!it.has(IconType::FileBased))
- continue;
- current_list.push_back(it.m_images[IconType::FileBased].filename);
- }
- QSet<QString> current_set = current_list.toSet();
-
- QSet<QString> to_remove = current_set;
- to_remove -= new_set;
-
- QSet<QString> to_add = new_set;
- to_add -= current_set;
-
- for (auto remove : to_remove)
- {
- qDebug() << "Removing " << remove;
- QFileInfo rmfile(remove);
- QString key = rmfile.baseName();
- int idx = getIconIndex(key);
- if (idx == -1)
- continue;
- icons[idx].remove(IconType::FileBased);
- if (icons[idx].type() == IconType::ToBeDeleted)
- {
- beginRemoveRows(QModelIndex(), idx, idx);
- icons.remove(idx);
- reindex();
- endRemoveRows();
- }
- else
- {
- dataChanged(index(idx), index(idx));
- }
- m_watcher->removePath(remove);
- emit iconUpdated(key);
- }
-
- for (auto add : to_add)
- {
- qDebug() << "Adding " << add;
- QFileInfo addfile(add);
- QString key = addfile.baseName();
- if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased))
- {
- m_watcher->addPath(add);
- emit iconUpdated(key);
- }
- }
+ QDir new_dir (path);
+ if(m_dir.absolutePath() != new_dir.absolutePath())
+ {
+ m_dir.setPath(path);
+ m_dir.refresh();
+ if(is_watching)
+ stopWatching();
+ startWatching();
+ }
+ if(!m_dir.exists())
+ if(!FS::ensureFolderPathExists(m_dir.absolutePath()))
+ return;
+ m_dir.refresh();
+ auto new_list = m_dir.entryList(QDir::Files, QDir::Name);
+ for (auto it = new_list.begin(); it != new_list.end(); it++)
+ {
+ QString &foo = (*it);
+ foo = m_dir.filePath(foo);
+ }
+ auto new_set = new_list.toSet();
+ QList<QString> current_list;
+ for (auto &it : icons)
+ {
+ if (!it.has(IconType::FileBased))
+ continue;
+ current_list.push_back(it.m_images[IconType::FileBased].filename);
+ }
+ QSet<QString> current_set = current_list.toSet();
+
+ QSet<QString> to_remove = current_set;
+ to_remove -= new_set;
+
+ QSet<QString> to_add = new_set;
+ to_add -= current_set;
+
+ for (auto remove : to_remove)
+ {
+ qDebug() << "Removing " << remove;
+ QFileInfo rmfile(remove);
+ QString key = rmfile.baseName();
+ int idx = getIconIndex(key);
+ if (idx == -1)
+ continue;
+ icons[idx].remove(IconType::FileBased);
+ if (icons[idx].type() == IconType::ToBeDeleted)
+ {
+ beginRemoveRows(QModelIndex(), idx, idx);
+ icons.remove(idx);
+ reindex();
+ endRemoveRows();
+ }
+ else
+ {
+ dataChanged(index(idx), index(idx));
+ }
+ m_watcher->removePath(remove);
+ emit iconUpdated(key);
+ }
+
+ for (auto add : to_add)
+ {
+ qDebug() << "Adding " << add;
+ QFileInfo addfile(add);
+ QString key = addfile.baseName();
+ if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased))
+ {
+ m_watcher->addPath(add);
+ emit iconUpdated(key);
+ }
+ }
}
void IconList::fileChanged(const QString &path)
{
- qDebug() << "Checking " << path;
- QFileInfo checkfile(path);
- if (!checkfile.exists())
- return;
- QString key = checkfile.baseName();
- int idx = getIconIndex(key);
- if (idx == -1)
- return;
- QIcon icon(path);
- if (!icon.availableSizes().size())
- return;
-
- icons[idx].m_images[IconType::FileBased].icon = icon;
- dataChanged(index(idx), index(idx));
- emit iconUpdated(key);
+ qDebug() << "Checking " << path;
+ QFileInfo checkfile(path);
+ if (!checkfile.exists())
+ return;
+ QString key = checkfile.baseName();
+ int idx = getIconIndex(key);
+ if (idx == -1)
+ return;
+ QIcon icon(path);
+ if (!icon.availableSizes().size())
+ return;
+
+ icons[idx].m_images[IconType::FileBased].icon = icon;
+ dataChanged(index(idx), index(idx));
+ emit iconUpdated(key);
}
void IconList::SettingChanged(const Setting &setting, QVariant value)
{
- if(setting.id() != "IconsDir")
- return;
+ if(setting.id() != "IconsDir")
+ return;
- directoryChanged(value.toString());
+ directoryChanged(value.toString());
}
void IconList::startWatching()
{
- auto abs_path = m_dir.absolutePath();
- FS::ensureFolderPathExists(abs_path);
- is_watching = m_watcher->addPath(abs_path);
- if (is_watching)
- {
- qDebug() << "Started watching " << abs_path;
- }
- else
- {
- qDebug() << "Failed to start watching " << abs_path;
- }
+ auto abs_path = m_dir.absolutePath();
+ FS::ensureFolderPathExists(abs_path);
+ is_watching = m_watcher->addPath(abs_path);
+ if (is_watching)
+ {
+ qDebug() << "Started watching " << abs_path;
+ }
+ else
+ {
+ qDebug() << "Failed to start watching " << abs_path;
+ }
}
void IconList::stopWatching()
{
- m_watcher->removePaths(m_watcher->files());
- m_watcher->removePaths(m_watcher->directories());
- is_watching = false;
+ m_watcher->removePaths(m_watcher->files());
+ m_watcher->removePaths(m_watcher->directories());
+ is_watching = false;
}
QStringList IconList::mimeTypes() const
{
- QStringList types;
- types << "text/uri-list";
- return types;
+ QStringList types;
+ types << "text/uri-list";
+ return types;
}
Qt::DropActions IconList::supportedDropActions() const
{
- return Qt::CopyAction;
+ return Qt::CopyAction;
}
-bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
- const QModelIndex &parent)
+bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
- if (action == Qt::IgnoreAction)
- return true;
- // check if the action is supported
- if (!data || !(action & supportedDropActions()))
- return false;
-
- // files dropped from outside?
- if (data->hasUrls())
- {
- auto urls = data->urls();
- QStringList iconFiles;
- for (auto url : urls)
- {
- // only local files may be dropped...
- if (!url.isLocalFile())
- continue;
- iconFiles += url.toLocalFile();
- }
- installIcons(iconFiles);
- return true;
- }
- return false;
+ if (action == Qt::IgnoreAction)
+ return true;
+ // check if the action is supported
+ if (!data || !(action & supportedDropActions()))
+ return false;
+
+ // files dropped from outside?
+ if (data->hasUrls())
+ {
+ auto urls = data->urls();
+ QStringList iconFiles;
+ for (auto url : urls)
+ {
+ // only local files may be dropped...
+ if (!url.isLocalFile())
+ continue;
+ iconFiles += url.toLocalFile();
+ }
+ installIcons(iconFiles);
+ return true;
+ }
+ return false;
}
Qt::ItemFlags IconList::flags(const QModelIndex &index) const
{
- Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
- if (index.isValid())
- return Qt::ItemIsDropEnabled | defaultFlags;
- else
- return Qt::ItemIsDropEnabled | defaultFlags;
+ Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
+ if (index.isValid())
+ return Qt::ItemIsDropEnabled | defaultFlags;
+ else
+ return Qt::ItemIsDropEnabled | defaultFlags;
}
QVariant IconList::data(const QModelIndex &index, int role) const
{
- if (!index.isValid())
- return QVariant();
-
- int row = index.row();
-
- if (row < 0 || row >= icons.size())
- return QVariant();
-
- switch (role)
- {
- case Qt::DecorationRole:
- return icons[row].icon();
- case Qt::DisplayRole:
- return icons[row].name();
- case Qt::UserRole:
- return icons[row].m_key;
- default:
- return QVariant();
- }
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+
+ if (row < 0 || row >= icons.size())
+ return QVariant();
+
+ switch (role)
+ {
+ case Qt::DecorationRole:
+ return icons[row].icon();
+ case Qt::DisplayRole:
+ return icons[row].name();
+ case Qt::UserRole:
+ return icons[row].m_key;
+ default:
+ return QVariant();
+ }
}
int IconList::rowCount(const QModelIndex &parent) const
{
- return icons.size();
+ return icons.size();
}
void IconList::installIcons(const QStringList &iconFiles)
{
- for (QString file : iconFiles)
- {
- QFileInfo fileinfo(file);
- if (!fileinfo.isReadable() || !fileinfo.isFile())
- continue;
- QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName());
-
- QString suffix = fileinfo.suffix();
- if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg")
- continue;
-
- if (!QFile::copy(file, target))
- continue;
- }
+ for (QString file : iconFiles)
+ {
+ QFileInfo fileinfo(file);
+ if (!fileinfo.isReadable() || !fileinfo.isFile())
+ continue;
+ QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName());
+
+ QString suffix = fileinfo.suffix();
+ if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
+ continue;
+
+ if (!QFile::copy(file, target))
+ continue;
+ }
}
void IconList::installIcon(const QString &file, const QString &name)
{
- QFileInfo fileinfo(file);
- if(!fileinfo.isReadable() || !fileinfo.isFile())
- return;
+ QFileInfo fileinfo(file);
+ if(!fileinfo.isReadable() || !fileinfo.isFile())
+ return;
- QString target = FS::PathCombine(m_dir.dirName(), name);
+ QString target = FS::PathCombine(m_dir.dirName(), name);
- QFile::copy(file, target);
+ QFile::copy(file, target);
}
bool IconList::iconFileExists(const QString &key) const
{
- auto iconEntry = icon(key);
- if(!iconEntry)
- {
- return false;
- }
- return iconEntry->has(IconType::FileBased);
+ auto iconEntry = icon(key);
+ if(!iconEntry)
+ {
+ return false;
+ }
+ return iconEntry->has(IconType::FileBased);
}
const MMCIcon *IconList::icon(const QString &key) const
{
- int iconIdx = getIconIndex(key);
- if (iconIdx == -1)
- return nullptr;
- return &icons[iconIdx];
+ int iconIdx = getIconIndex(key);
+ if (iconIdx == -1)
+ return nullptr;
+ return &icons[iconIdx];
}
bool IconList::deleteIcon(const QString &key)
{
- int iconIdx = getIconIndex(key);
- if (iconIdx == -1)
- return false;
- auto &iconEntry = icons[iconIdx];
- if (iconEntry.has(IconType::FileBased))
- {
- return QFile::remove(iconEntry.m_images[IconType::FileBased].filename);
- }
- return false;
+ int iconIdx = getIconIndex(key);
+ if (iconIdx == -1)
+ return false;
+ auto &iconEntry = icons[iconIdx];
+ if (iconEntry.has(IconType::FileBased))
+ {
+ return QFile::remove(iconEntry.m_images[IconType::FileBased].filename);
+ }
+ return false;
}
bool IconList::addThemeIcon(const QString& key)
{
- auto iter = name_index.find(key);
- if (iter != name_index.end())
- {
- auto &oldOne = icons[*iter];
- oldOne.replace(Builtin, key);
- dataChanged(index(*iter), index(*iter));
- return true;
- }
- else
- {
- // add a new icon
- beginInsertRows(QModelIndex(), icons.size(), icons.size());
- {
- MMCIcon mmc_icon;
- mmc_icon.m_name = key;
- mmc_icon.m_key = key;
- mmc_icon.replace(Builtin, key);
- icons.push_back(mmc_icon);
- name_index[key] = icons.size() - 1;
- }
- endInsertRows();
- return true;
- }
+ auto iter = name_index.find(key);
+ if (iter != name_index.end())
+ {
+ auto &oldOne = icons[*iter];
+ oldOne.replace(Builtin, key);
+ dataChanged(index(*iter), index(*iter));
+ return true;
+ }
+ else
+ {
+ // add a new icon
+ beginInsertRows(QModelIndex(), icons.size(), icons.size());
+ {
+ MMCIcon mmc_icon;
+ mmc_icon.m_name = key;
+ mmc_icon.m_key = key;
+ mmc_icon.replace(Builtin, key);
+ icons.push_back(mmc_icon);
+ name_index[key] = icons.size() - 1;
+ }
+ endInsertRows();
+ return true;
+ }
}
bool IconList::addIcon(const QString &key, const QString &name, const QString &path, const IconType type)
{
- // replace the icon even? is the input valid?
- QIcon icon(path);
- if (icon.isNull())
- return false;
- auto iter = name_index.find(key);
- if (iter != name_index.end())
- {
- auto &oldOne = icons[*iter];
- oldOne.replace(type, icon, path);
- dataChanged(index(*iter), index(*iter));
- return true;
- }
- else
- {
- // add a new icon
- beginInsertRows(QModelIndex(), icons.size(), icons.size());
- {
- MMCIcon mmc_icon;
- mmc_icon.m_name = name;
- mmc_icon.m_key = key;
- mmc_icon.replace(type, icon, path);
- icons.push_back(mmc_icon);
- name_index[key] = icons.size() - 1;
- }
- endInsertRows();
- return true;
- }
+ // replace the icon even? is the input valid?
+ QIcon icon(path);
+ if (icon.isNull())
+ return false;
+ auto iter = name_index.find(key);
+ if (iter != name_index.end())
+ {
+ auto &oldOne = icons[*iter];
+ oldOne.replace(type, icon, path);
+ dataChanged(index(*iter), index(*iter));
+ return true;
+ }
+ else
+ {
+ // add a new icon
+ beginInsertRows(QModelIndex(), icons.size(), icons.size());
+ {
+ MMCIcon mmc_icon;
+ mmc_icon.m_name = name;
+ mmc_icon.m_key = key;
+ mmc_icon.replace(type, icon, path);
+ icons.push_back(mmc_icon);
+ name_index[key] = icons.size() - 1;
+ }
+ endInsertRows();
+ return true;
+ }
}
void IconList::saveIcon(const QString &key, const QString &path, const char * format) const
{
- auto icon = getIcon(key);
- auto pixmap = icon.pixmap(128, 128);
- pixmap.save(path, format);
+ auto icon = getIcon(key);
+ auto pixmap = icon.pixmap(128, 128);
+ pixmap.save(path, format);
}
void IconList::reindex()
{
- name_index.clear();
- int i = 0;
- for (auto &iter : icons)
- {
- name_index[iter.m_key] = i;
- i++;
- }
+ name_index.clear();
+ int i = 0;
+ for (auto &iter : icons)
+ {
+ name_index[iter.m_key] = i;
+ i++;
+ }
}
QIcon IconList::getIcon(const QString &key) const
{
- int icon_index = getIconIndex(key);
+ int icon_index = getIconIndex(key);
- if (icon_index != -1)
- return icons[icon_index].icon();
+ if (icon_index != -1)
+ return icons[icon_index].icon();
- // Fallback for icons that don't exist.
- icon_index = getIconIndex("infinity");
+ // Fallback for icons that don't exist.
+ icon_index = getIconIndex("infinity");
- if (icon_index != -1)
- return icons[icon_index].icon();
- return QIcon();
+ if (icon_index != -1)
+ return icons[icon_index].icon();
+ return QIcon();
}
int IconList::getIconIndex(const QString &key) const
{
- auto iter = name_index.find(key == "default" ? "infinity" : key);
- if (iter != name_index.end())
- return *iter;
+ auto iter = name_index.find(key == "default" ? "infinity" : key);
+ if (iter != name_index.end())
+ return *iter;
- return -1;
+ return -1;
}
QString IconList::getDirectory() const
{
- return m_dir.absolutePath();
+ return m_dir.absolutePath();
}
//#include "IconList.moc"
diff --git a/api/gui/icons/IconList.h b/api/gui/icons/IconList.h
index fad3336f..274a9f02 100644
--- a/api/gui/icons/IconList.h
+++ b/api/gui/icons/IconList.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,57 +32,57 @@ class QFileSystemWatcher;
class MULTIMC_GUI_EXPORT IconList : public QAbstractListModel, public IIconList
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit IconList(const QStringList &builtinPaths, QString path, QObject *parent = 0);
- virtual ~IconList() {};
+ explicit IconList(const QStringList &builtinPaths, QString path, QObject *parent = 0);
+ virtual ~IconList() {};
- QIcon getIcon(const QString &key) const;
- int getIconIndex(const QString &key) const;
- QString getDirectory() const;
+ QIcon getIcon(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 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- bool addThemeIcon(const QString &key);
- bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) override;
- void saveIcon(const QString &key, const QString &path, const char * format) const override;
- bool deleteIcon(const QString &key) override;
- bool iconFileExists(const QString &key) const override;
+ bool addThemeIcon(const QString &key);
+ bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) override;
+ void saveIcon(const QString &key, const QString &path, const char * format) const override;
+ bool deleteIcon(const QString &key) override;
+ bool iconFileExists(const QString &key) const override;
- virtual QStringList mimeTypes() const override;
- virtual Qt::DropActions supportedDropActions() const override;
- virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
- virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
+ virtual QStringList mimeTypes() const override;
+ virtual Qt::DropActions supportedDropActions() const override;
+ virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
- void installIcons(const QStringList &iconFiles) override;
- void installIcon(const QString &file, const QString &name) 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;
- void startWatching();
- void stopWatching();
+ void startWatching();
+ void stopWatching();
signals:
- void iconUpdated(QString key);
+ void iconUpdated(QString key);
private:
- // hide copy constructor
- IconList(const IconList &) = delete;
- // hide assign op
- IconList &operator=(const IconList &) = delete;
- void reindex();
+ // hide copy constructor
+ IconList(const IconList &) = delete;
+ // hide assign op
+ IconList &operator=(const IconList &) = delete;
+ void reindex();
public slots:
- void directoryChanged(const QString &path);
+ void directoryChanged(const QString &path);
protected slots:
- void fileChanged(const QString &path);
- void SettingChanged(const Setting & setting, QVariant value);
+ void fileChanged(const QString &path);
+ void SettingChanged(const Setting & setting, QVariant value);
private:
- std::shared_ptr<QFileSystemWatcher> m_watcher;
- bool is_watching;
- QMap<QString, int> name_index;
- QVector<MMCIcon> icons;
- QDir m_dir;
+ shared_qobject_ptr<QFileSystemWatcher> m_watcher;
+ bool is_watching;
+ QMap<QString, int> name_index;
+ QVector<MMCIcon> icons;
+ QDir m_dir;
};
diff --git a/api/gui/icons/MMCIcon.cpp b/api/gui/icons/MMCIcon.cpp
index 92518e01..5058717a 100644
--- a/api/gui/icons/MMCIcon.cpp
+++ b/api/gui/icons/MMCIcon.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,86 +19,100 @@
IconType operator--(IconType &t, int)
{
- IconType temp = t;
- switch (t)
- {
- case IconType::Builtin:
- t = IconType::ToBeDeleted;
- break;
- case IconType::Transient:
- t = IconType::Builtin;
- break;
- case IconType::FileBased:
- t = IconType::Transient;
- break;
- default:
- {
- }
- }
- return temp;
+ IconType temp = t;
+ switch (t)
+ {
+ case IconType::Builtin:
+ t = IconType::ToBeDeleted;
+ break;
+ case IconType::Transient:
+ t = IconType::Builtin;
+ break;
+ case IconType::FileBased:
+ t = IconType::Transient;
+ break;
+ default:
+ {
+ }
+ }
+ return temp;
}
IconType MMCIcon::type() const
{
- return m_current_type;
+ return m_current_type;
}
QString MMCIcon::name() const
{
- if (m_name.size())
- return m_name;
- return m_key;
+ if (m_name.size())
+ return m_name;
+ return m_key;
}
bool MMCIcon::has(IconType _type) const
{
- return m_images[_type].present();
+ return m_images[_type].present();
}
QIcon MMCIcon::icon() const
{
- if (m_current_type == IconType::ToBeDeleted)
- return QIcon();
- auto & icon = m_images[m_current_type].icon;
- if(!icon.isNull())
- return icon;
- // FIXME: inject this.
- return XdgIcon::fromTheme(m_images[m_current_type].key);
+ if (m_current_type == IconType::ToBeDeleted)
+ return QIcon();
+ auto & icon = m_images[m_current_type].icon;
+ if(!icon.isNull())
+ return icon;
+ // FIXME: inject this.
+ return XdgIcon::fromTheme(m_images[m_current_type].key);
}
void MMCIcon::remove(IconType rm_type)
{
- m_images[rm_type].filename = QString();
- m_images[rm_type].icon = QIcon();
- for (auto iter = rm_type; iter != IconType::ToBeDeleted; iter--)
- {
- if (m_images[iter].present())
- {
- m_current_type = iter;
- return;
- }
- }
- m_current_type = IconType::ToBeDeleted;
+ m_images[rm_type].filename = QString();
+ m_images[rm_type].icon = QIcon();
+ for (auto iter = rm_type; iter != IconType::ToBeDeleted; iter--)
+ {
+ if (m_images[iter].present())
+ {
+ m_current_type = iter;
+ return;
+ }
+ }
+ m_current_type = IconType::ToBeDeleted;
}
void MMCIcon::replace(IconType new_type, QIcon icon, QString path)
{
- if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted)
- {
- m_current_type = new_type;
- }
- m_images[new_type].icon = icon;
- m_images[new_type].filename = path;
- m_images[new_type].key = QString();
+ if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted)
+ {
+ m_current_type = new_type;
+ }
+ m_images[new_type].icon = icon;
+ m_images[new_type].filename = path;
+ m_images[new_type].key = QString();
}
void MMCIcon::replace(IconType new_type, const QString& key)
{
- if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted)
- {
- m_current_type = new_type;
- }
- m_images[new_type].icon = QIcon();
- m_images[new_type].filename = QString();
- m_images[new_type].key = key;
+ if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted)
+ {
+ m_current_type = new_type;
+ }
+ m_images[new_type].icon = QIcon();
+ m_images[new_type].filename = QString();
+ 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;
}
diff --git a/api/gui/icons/MMCIcon.h b/api/gui/icons/MMCIcon.h
index 8c6752ea..9fd1b9a2 100644
--- a/api/gui/icons/MMCIcon.h
+++ b/api/gui/icons/MMCIcon.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,27 +23,29 @@
struct MULTIMC_GUI_EXPORT MMCImage
{
- QIcon icon;
- QString key;
- QString filename;
- bool present() const
- {
- return !icon.isNull() || !key.isEmpty();
- }
+ QIcon icon;
+ QString key;
+ QString filename;
+ bool present() const
+ {
+ return !icon.isNull() || !key.isEmpty();
+ }
};
struct MULTIMC_GUI_EXPORT MMCIcon
{
- QString m_key;
- QString m_name;
- MMCImage m_images[ICONS_TOTAL];
- IconType m_current_type = ToBeDeleted;
+ QString m_key;
+ QString m_name;
+ MMCImage m_images[ICONS_TOTAL];
+ IconType m_current_type = ToBeDeleted;
- IconType type() const;
- QString name() const;
- bool has(IconType _type) const;
- QIcon icon() const;
- void remove(IconType rm_type);
- void replace(IconType new_type, QIcon icon, QString path = QString());
- void replace(IconType new_type, const QString &key);
+ IconType type() const;
+ QString name() const;
+ bool has(IconType _type) const;
+ QIcon icon() const;
+ void remove(IconType rm_type);
+ void replace(IconType new_type, QIcon icon, QString path = QString());
+ void replace(IconType new_type, const QString &key);
+ bool isBuiltIn() const;
+ QString getFilePath() const;
};
diff --git a/api/logic/BaseInstaller.cpp b/api/logic/BaseInstaller.cpp
index 51f66293..3819a4e4 100644
--- a/api/logic/BaseInstaller.cpp
+++ b/api/logic/BaseInstaller.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,37 +25,37 @@ BaseInstaller::BaseInstaller()
bool BaseInstaller::isApplied(MinecraftInstance *on)
{
- return QFile::exists(filename(on->instanceRoot()));
+ return QFile::exists(filename(on->instanceRoot()));
}
bool BaseInstaller::add(MinecraftInstance *to)
{
- if (!patchesDir(to->instanceRoot()).exists())
- {
- QDir(to->instanceRoot()).mkdir("patches");
- }
-
- if (isApplied(to))
- {
- if (!remove(to))
- {
- return false;
- }
- }
-
- return true;
+ if (!patchesDir(to->instanceRoot()).exists())
+ {
+ QDir(to->instanceRoot()).mkdir("patches");
+ }
+
+ if (isApplied(to))
+ {
+ if (!remove(to))
+ {
+ return false;
+ }
+ }
+
+ return true;
}
bool BaseInstaller::remove(MinecraftInstance *from)
{
- return QFile::remove(filename(from->instanceRoot()));
+ return QFile::remove(filename(from->instanceRoot()));
}
QString BaseInstaller::filename(const QString &root) const
{
- return patchesDir(root).absoluteFilePath(id() + ".json");
+ return patchesDir(root).absoluteFilePath(id() + ".json");
}
QDir BaseInstaller::patchesDir(const QString &root) const
{
- return QDir(root + "/patches/");
+ return QDir(root + "/patches/");
}
diff --git a/api/logic/BaseInstaller.h b/api/logic/BaseInstaller.h
index afe11d55..59e00e06 100644
--- a/api/logic/BaseInstaller.h
+++ b/api/logic/BaseInstaller.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,17 +30,17 @@ typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
class MULTIMC_LOGIC_EXPORT BaseInstaller
{
public:
- BaseInstaller();
- virtual ~BaseInstaller(){};
- bool isApplied(MinecraftInstance *on);
+ BaseInstaller();
+ virtual ~BaseInstaller(){};
+ bool isApplied(MinecraftInstance *on);
- virtual bool add(MinecraftInstance *to);
- virtual bool remove(MinecraftInstance *from);
+ virtual bool add(MinecraftInstance *to);
+ virtual bool remove(MinecraftInstance *from);
- virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
+ virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
protected:
- virtual QString id() const = 0;
- QString filename(const QString &root) const;
- QDir patchesDir(const QString &root) const;
+ virtual QString id() const = 0;
+ QString filename(const QString &root) const;
+ QDir patchesDir(const QString &root) const;
};
diff --git a/api/logic/BaseInstance.cpp b/api/logic/BaseInstance.cpp
index 7e652e0d..7a95e255 100644
--- a/api/logic/BaseInstance.cpp
+++ b/api/logic/BaseInstance.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,281 +27,228 @@
#include "Commandline.h"
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
- : QObject()
+ : QObject()
{
- m_settings = settings;
- m_rootDir = rootDir;
-
- m_settings->registerSetting("name", "Unnamed Instance");
- m_settings->registerSetting("iconKey", "default");
- m_settings->registerSetting("notes", "");
- m_settings->registerSetting("lastLaunchTime", 0);
- m_settings->registerSetting("totalTimePlayed", 0);
-
- // Custom Commands
- auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
- m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting);
- m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting);
- m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting);
-
- // Console
- auto consoleSetting = m_settings->registerSetting("OverrideConsole", false);
- m_settings->registerOverride(globalSettings->getSetting("ShowConsole"), consoleSetting);
- m_settings->registerOverride(globalSettings->getSetting("AutoCloseConsole"), consoleSetting);
- m_settings->registerOverride(globalSettings->getSetting("ShowConsoleOnError"), consoleSetting);
- m_settings->registerOverride(globalSettings->getSetting("LogPrePostOutput"), consoleSetting);
-
- m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
- m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
+ m_settings = settings;
+ m_rootDir = rootDir;
+
+ m_settings->registerSetting("name", "Unnamed Instance");
+ m_settings->registerSetting("iconKey", "default");
+ m_settings->registerSetting("notes", "");
+ m_settings->registerSetting("lastLaunchTime", 0);
+ m_settings->registerSetting("totalTimePlayed", 0);
+
+ // Custom Commands
+ auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
+ m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting);
+ m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting);
+ m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting);
+
+ // Console
+ auto consoleSetting = m_settings->registerSetting("OverrideConsole", false);
+ m_settings->registerOverride(globalSettings->getSetting("ShowConsole"), consoleSetting);
+ m_settings->registerOverride(globalSettings->getSetting("AutoCloseConsole"), consoleSetting);
+ m_settings->registerOverride(globalSettings->getSetting("ShowConsoleOnError"), consoleSetting);
+ m_settings->registerOverride(globalSettings->getSetting("LogPrePostOutput"), consoleSetting);
+
+ m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
+ m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
}
QString BaseInstance::getPreLaunchCommand()
{
- return settings()->get("PreLaunchCommand").toString();
+ return settings()->get("PreLaunchCommand").toString();
}
QString BaseInstance::getWrapperCommand()
{
- return settings()->get("WrapperCommand").toString();
+ return settings()->get("WrapperCommand").toString();
}
QString BaseInstance::getPostExitCommand()
{
- return settings()->get("PostExitCommand").toString();
+ return settings()->get("PostExitCommand").toString();
}
int BaseInstance::getConsoleMaxLines() const
{
- auto lineSetting = settings()->getSetting("ConsoleMaxLines");
- bool conversionOk = false;
- int maxLines = lineSetting->get().toInt(&conversionOk);
- if(!conversionOk)
- {
- maxLines = lineSetting->defValue().toInt();
- qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
- }
- return maxLines;
+ auto lineSetting = settings()->getSetting("ConsoleMaxLines");
+ bool conversionOk = false;
+ int maxLines = lineSetting->get().toInt(&conversionOk);
+ if(!conversionOk)
+ {
+ maxLines = lineSetting->defValue().toInt();
+ qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
+ }
+ return maxLines;
}
bool BaseInstance::shouldStopOnConsoleOverflow() const
{
- return settings()->get("ConsoleOverflowStop").toBool();
+ return settings()->get("ConsoleOverflowStop").toBool();
}
void BaseInstance::iconUpdated(QString key)
{
- if(iconKey() == key)
- {
- emit propertiesChanged(this);
- }
+ if(iconKey() == key)
+ {
+ emit propertiesChanged(this);
+ }
}
void BaseInstance::invalidate()
{
- changeStatus(Status::Gone);
- qDebug() << "Instance" << id() << "has been invalidated.";
-}
-
-void BaseInstance::nuke()
-{
- changeStatus(Status::Gone);
- qDebug() << "Instance" << id() << "has been deleted by MultiMC.";
- FS::deletePath(instanceRoot());
+ changeStatus(Status::Gone);
+ qDebug() << "Instance" << id() << "has been invalidated.";
}
void BaseInstance::changeStatus(BaseInstance::Status newStatus)
{
- Status status = currentStatus();
- if(status != newStatus)
- {
- m_status = newStatus;
- emit statusChanged(status, newStatus);
- }
+ Status status = currentStatus();
+ if(status != newStatus)
+ {
+ m_status = newStatus;
+ emit statusChanged(status, newStatus);
+ }
}
BaseInstance::Status BaseInstance::currentStatus() const
{
- return m_status;
+ return m_status;
}
QString BaseInstance::id() const
{
- return QFileInfo(instanceRoot()).fileName();
+ return QFileInfo(instanceRoot()).fileName();
}
bool BaseInstance::isRunning() const
{
- return m_isRunning;
+ return m_isRunning;
}
void BaseInstance::setRunning(bool running)
{
- if(running == m_isRunning)
- return;
-
- m_isRunning = running;
-
- if(running)
- {
- m_timeStarted = QDateTime::currentDateTime();
- }
- else
- {
- qint64 current = settings()->get("totalTimePlayed").toLongLong();
- QDateTime timeEnded = QDateTime::currentDateTime();
- settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded));
- emit propertiesChanged(this);
- }
-
- emit runningStatusChanged(running);
+ if(running == m_isRunning)
+ return;
+
+ m_isRunning = running;
+
+ if(running)
+ {
+ m_timeStarted = QDateTime::currentDateTime();
+ }
+ else
+ {
+ qint64 current = settings()->get("totalTimePlayed").toLongLong();
+ QDateTime timeEnded = QDateTime::currentDateTime();
+ settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded));
+ emit propertiesChanged(this);
+ }
+
+ emit runningStatusChanged(running);
}
int64_t BaseInstance::totalTimePlayed() const
{
- qint64 current = settings()->get("totalTimePlayed").toLongLong();
- if(m_isRunning)
- {
- QDateTime timeNow = QDateTime::currentDateTime();
- return current + m_timeStarted.secsTo(timeNow);
- }
- return current;
+ qint64 current = settings()->get("totalTimePlayed").toLongLong();
+ if(m_isRunning)
+ {
+ QDateTime timeNow = QDateTime::currentDateTime();
+ return current + m_timeStarted.secsTo(timeNow);
+ }
+ return current;
}
void BaseInstance::resetTimePlayed()
{
- settings()->reset("totalTimePlayed");
+ settings()->reset("totalTimePlayed");
}
QString BaseInstance::instanceType() const
{
- return m_settings->get("InstanceType").toString();
+ return m_settings->get("InstanceType").toString();
}
QString BaseInstance::instanceRoot() const
{
- return m_rootDir;
-}
-
-InstancePtr BaseInstance::getSharedPtr()
-{
- return shared_from_this();
+ return m_rootDir;
}
SettingsObjectPtr BaseInstance::settings() const
{
- return m_settings;
+ return m_settings;
}
bool BaseInstance::canLaunch() const
{
- return (!hasVersionBroken() && !isRunning());
+ return (!hasVersionBroken() && !isRunning());
}
bool BaseInstance::reloadSettings()
{
- return m_settings->reload();
+ return m_settings->reload();
}
qint64 BaseInstance::lastLaunch() const
{
- return m_settings->get("lastLaunchTime").value<qint64>();
+ return m_settings->get("lastLaunchTime").value<qint64>();
}
void BaseInstance::setLastLaunch(qint64 val)
{
- //FIXME: if no change, do not set. setting involves saving a file.
- m_settings->set("lastLaunchTime", val);
- 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;
+ //FIXME: if no change, do not set. setting involves saving a file.
+ m_settings->set("lastLaunchTime", val);
+ emit propertiesChanged(this);
}
void BaseInstance::setNotes(QString val)
{
- //FIXME: if no change, do not set. setting involves saving a file.
- m_settings->set("notes", val);
+ //FIXME: if no change, do not set. setting involves saving a file.
+ m_settings->set("notes", val);
}
QString BaseInstance::notes() const
{
- return m_settings->get("notes").toString();
+ return m_settings->get("notes").toString();
}
void BaseInstance::setIconKey(QString val)
{
- //FIXME: if no change, do not set. setting involves saving a file.
- m_settings->set("iconKey", val);
- emit propertiesChanged(this);
+ //FIXME: if no change, do not set. setting involves saving a file.
+ m_settings->set("iconKey", val);
+ emit propertiesChanged(this);
}
QString BaseInstance::iconKey() const
{
- return m_settings->get("iconKey").toString();
+ return m_settings->get("iconKey").toString();
}
void BaseInstance::setName(QString val)
{
- //FIXME: if no change, do not set. setting involves saving a file.
- m_settings->set("name", val);
- emit propertiesChanged(this);
+ //FIXME: if no change, do not set. setting involves saving a file.
+ m_settings->set("name", val);
+ emit propertiesChanged(this);
}
QString BaseInstance::name() const
{
- return m_settings->get("name").toString();
+ return m_settings->get("name").toString();
}
QString BaseInstance::windowTitle() const
{
- return "MultiMC: " + name();
+ return "MultiMC: " + name().replace(QRegExp("[ \n\r\t]+"), " ");
}
// FIXME: why is this here? move it to MinecraftInstance!!!
QStringList BaseInstance::extraArguments() const
{
- return Commandline::splitArgs(settings()->get("JvmArgs").toString());
-}
-
-std::shared_ptr<LaunchTask> BaseInstance::getLaunchTask()
-{
- 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;
+ return Commandline::splitArgs(settings()->get("JvmArgs").toString());
}
-BaseInstanceProvider* BaseInstance::provider() const
+shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
{
- return m_provider;
+ return m_launchProcess;
}
diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h
index 282bfb70..3c342cb3 100644
--- a/api/logic/BaseInstance.h
+++ b/api/logic/BaseInstance.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,6 @@ class QDir;
class Task;
class LaunchTask;
class BaseInstance;
-class BaseInstanceProvider;
// pointer for lazy people
typedef std::shared_ptr<BaseInstance> InstancePtr;
@@ -53,231 +52,217 @@ typedef std::shared_ptr<BaseInstance> InstancePtr;
*/
class MULTIMC_LOGIC_EXPORT BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
{
- Q_OBJECT
+ Q_OBJECT
protected:
- /// no-touchy!
- BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
+ /// no-touchy!
+ BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
public: /* types */
- enum class Status
- {
- Present,
- Gone // either nuked or invalidated
- };
+ enum class Status
+ {
+ Present,
+ Gone // either nuked or invalidated
+ };
public:
- /// virtual destructor to make sure the destruction is COMPLETE
- virtual ~BaseInstance() {};
+ /// virtual destructor to make sure the destruction is COMPLETE
+ 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,
+ * but it has not necessarily been deleted.
+ *
+ * Happens when the instance folder changes to some other location, or the instance is removed by external means.
+ */
+ void invalidate();
- /***
- * the instance has been invalidated - it is no longer tracked by MultiMC for some reason,
- * but it has not necessarily been deleted.
- *
- * Happens when the instance folder changes to some other location, or the instance is removed by external means.
- */
- void invalidate();
-
- /// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to
- /// be unique.
- virtual QString id() const;
-
- void setRunning(bool running);
- bool isRunning() const;
- int64_t totalTimePlayed() const;
- void resetTimePlayed();
-
- void setProvider(BaseInstanceProvider * provider);
- BaseInstanceProvider * provider() const;
-
- /// get the type of this instance
- QString instanceType() const;
-
- /// Path to the instance's root directory.
- QString instanceRoot() const;
-
- QString name() const;
- void setName(QString val);
-
- /// Value used for instance window titles
- QString windowTitle() const;
-
- QString iconKey() const;
- void setIconKey(QString val);
-
- QString notes() const;
- void setNotes(QString val);
-
- QString group() const;
- void setGroupInitial(QString val);
- void setGroupPost(QString val);
-
- QString getPreLaunchCommand();
- QString getPostExitCommand();
- QString getWrapperCommand();
-
- /// guess log level from a line of game log
- virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
- {
- return level;
- };
-
- virtual QStringList extraArguments() const;
-
- /// Traits. Normally inside the version, depends on instance implementation.
- virtual QSet <QString> traits() const = 0;
-
- /**
- * Gets the time that the instance was last launched.
- * Stored in milliseconds since epoch.
- */
- qint64 lastLaunch() const;
- /// Sets the last launched time to 'val' milliseconds since epoch
- void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
-
- InstancePtr getSharedPtr();
-
- /*!
- * \brief Gets this instance's settings object.
- * This settings object stores instance-specific settings.
- * \return A pointer to this instance's settings object.
- */
- virtual SettingsObjectPtr settings() const;
-
- /// returns a valid update task
- virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
-
- /// returns a valid launcher (task container)
- virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
-
- /// returns the current launch task (if any)
- std::shared_ptr<LaunchTask> getLaunchTask();
-
- /*!
- * Create envrironment variables for running the instance
- */
- virtual QProcessEnvironment createEnvironment() = 0;
-
- /*!
- * Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
- */
- virtual IPathMatcher::Ptr getLogFileMatcher() = 0;
-
- /*!
- * Returns the root folder to use for looking up log files
- */
- virtual QString getLogFileRoot() = 0;
-
- virtual QString getStatusbarDescription() = 0;
-
- /// FIXME: this really should be elsewhere...
- virtual QString instanceConfigFolder() const = 0;
-
- /// get variables this instance exports
- virtual QMap<QString, QString> getVariables() const = 0;
-
- virtual QString typeName() const = 0;
-
- bool hasVersionBroken() const
- {
- return m_hasBrokenVersion;
- }
- void setVersionBroken(bool value)
- {
- if(m_hasBrokenVersion != value)
- {
- m_hasBrokenVersion = value;
- emit propertiesChanged(this);
- }
- }
-
- bool hasUpdateAvailable() const
- {
- return m_hasUpdate;
- }
- void setUpdateAvailable(bool value)
- {
- if(m_hasUpdate != value)
- {
- m_hasUpdate = value;
- emit propertiesChanged(this);
- }
- }
-
- bool hasCrashed() const
- {
- return m_crashed;
- }
- void setCrashed(bool value)
- {
- if(m_crashed != value)
- {
- m_crashed = value;
- emit propertiesChanged(this);
- }
- }
-
- virtual bool canLaunch() const;
- virtual bool canEdit() const = 0;
- virtual bool canExport() const = 0;
-
- bool reloadSettings();
-
- /**
- * 'print' a verbose desription of the instance into a QStringList
- */
- virtual QStringList verboseDescription(AuthSessionPtr session) = 0;
-
- Status currentStatus() const;
-
- int getConsoleMaxLines() const;
- bool shouldStopOnConsoleOverflow() const;
+ /// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to
+ /// be unique.
+ virtual QString id() const;
+
+ void setRunning(bool running);
+ bool isRunning() const;
+ int64_t totalTimePlayed() const;
+ void resetTimePlayed();
+
+ /// get the type of this instance
+ QString instanceType() const;
+
+ /// Path to the instance's root directory.
+ QString instanceRoot() const;
+
+ /// Path to the instance's game root directory.
+ virtual QString gameRoot() const
+ {
+ return instanceRoot();
+ }
+
+ QString name() const;
+ void setName(QString val);
+
+ /// Value used for instance window titles
+ QString windowTitle() const;
+
+ QString iconKey() const;
+ void setIconKey(QString val);
+
+ QString notes() const;
+ void setNotes(QString val);
+
+ QString getPreLaunchCommand();
+ QString getPostExitCommand();
+ QString getWrapperCommand();
+
+ /// guess log level from a line of game log
+ virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
+ {
+ return level;
+ };
+
+ virtual QStringList extraArguments() const;
+
+ /// Traits. Normally inside the version, depends on instance implementation.
+ virtual QSet <QString> traits() const = 0;
+
+ /**
+ * Gets the time that the instance was last launched.
+ * Stored in milliseconds since epoch.
+ */
+ qint64 lastLaunch() const;
+ /// Sets the last launched time to 'val' milliseconds since epoch
+ void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
+
+ /*!
+ * \brief Gets this instance's settings object.
+ * This settings object stores instance-specific settings.
+ * \return A pointer to this instance's settings object.
+ */
+ virtual SettingsObjectPtr settings() const;
+
+ /// returns a valid update task
+ virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
+
+ /// returns a valid launcher (task container)
+ virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
+
+ /// returns the current launch task (if any)
+ shared_qobject_ptr<LaunchTask> getLaunchTask();
+
+ /*!
+ * Create envrironment variables for running the instance
+ */
+ virtual QProcessEnvironment createEnvironment() = 0;
+
+ /*!
+ * Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
+ */
+ virtual IPathMatcher::Ptr getLogFileMatcher() = 0;
+
+ /*!
+ * Returns the root folder to use for looking up log files
+ */
+ virtual QString getLogFileRoot() = 0;
+
+ virtual QString getStatusbarDescription() = 0;
+
+ /// FIXME: this really should be elsewhere...
+ virtual QString instanceConfigFolder() const = 0;
+
+ /// get variables this instance exports
+ virtual QMap<QString, QString> getVariables() const = 0;
+
+ virtual QString typeName() const = 0;
+
+ bool hasVersionBroken() const
+ {
+ return m_hasBrokenVersion;
+ }
+ void setVersionBroken(bool value)
+ {
+ if(m_hasBrokenVersion != value)
+ {
+ m_hasBrokenVersion = value;
+ emit propertiesChanged(this);
+ }
+ }
+
+ bool hasUpdateAvailable() const
+ {
+ return m_hasUpdate;
+ }
+ void setUpdateAvailable(bool value)
+ {
+ if(m_hasUpdate != value)
+ {
+ m_hasUpdate = value;
+ emit propertiesChanged(this);
+ }
+ }
+
+ bool hasCrashed() const
+ {
+ return m_crashed;
+ }
+ void setCrashed(bool value)
+ {
+ if(m_crashed != value)
+ {
+ m_crashed = value;
+ emit propertiesChanged(this);
+ }
+ }
+
+ virtual bool canLaunch() const;
+ virtual bool canEdit() const = 0;
+ virtual bool canExport() const = 0;
+
+ bool reloadSettings();
+
+ /**
+ * 'print' a verbose desription of the instance into a QStringList
+ */
+ virtual QStringList verboseDescription(AuthSessionPtr session) = 0;
+
+ Status currentStatus() const;
+
+ int getConsoleMaxLines() const;
+ bool shouldStopOnConsoleOverflow() const;
protected:
- void changeStatus(Status newStatus);
+ void changeStatus(Status newStatus);
signals:
- /*!
- * \brief Signal emitted when properties relevant to the instance view change
- */
- void propertiesChanged(BaseInstance *inst);
- /*!
- * \brief Signal emitted when groups are affected in any way
- */
- void groupChanged();
+ /*!
+ * \brief Signal emitted when properties relevant to the instance view change
+ */
+ void propertiesChanged(BaseInstance *inst);
- void launchTaskChanged(std::shared_ptr<LaunchTask>);
+ void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
- void runningStatusChanged(bool running);
+ void runningStatusChanged(bool running);
- void statusChanged(Status from, Status to);
+ void statusChanged(Status from, Status to);
protected slots:
- void iconUpdated(QString key);
+ void iconUpdated(QString key);
protected: /* data */
- QString m_rootDir;
- QString m_group;
- SettingsObjectPtr m_settings;
- // InstanceFlags m_flags;
- bool m_isRunning = false;
- std::shared_ptr<LaunchTask> m_launchProcess;
- QDateTime m_timeStarted;
- BaseInstanceProvider * m_provider = nullptr;
+ QString m_rootDir;
+ SettingsObjectPtr m_settings;
+ // InstanceFlags m_flags;
+ bool m_isRunning = false;
+ shared_qobject_ptr<LaunchTask> m_launchProcess;
+ QDateTime m_timeStarted;
private: /* data */
- Status m_status = Status::Present;
- bool m_crashed = false;
- bool m_hasUpdate = false;
- bool m_hasBrokenVersion = false;
+ Status m_status = Status::Present;
+ bool m_crashed = false;
+ bool m_hasUpdate = 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_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)
diff --git a/api/logic/BaseInstanceProvider.h b/api/logic/BaseInstanceProvider.h
deleted file mode 100644
index 34489c5d..00000000
--- a/api/logic/BaseInstanceProvider.h
+++ /dev/null
@@ -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;
-};
diff --git a/api/logic/BaseVersion.h b/api/logic/BaseVersion.h
index e49d6277..15e64bc0 100644
--- a/api/logic/BaseVersion.h
+++ b/api/logic/BaseVersion.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,33 +25,33 @@
class BaseVersion
{
public:
- virtual ~BaseVersion() {}
- /*!
- * A string used to identify this version in config files.
- * This should be unique within the version list or shenanigans will occur.
- */
- virtual QString descriptor() = 0;
-
- /*!
- * The name of this version as it is displayed to the user.
- * For example: "1.5.1"
- */
- virtual QString name() = 0;
-
- /*!
- * This should return a string that describes
- * the kind of version this is (Stable, Beta, Snapshot, whatever)
- */
- virtual QString typeString() const = 0;
-
- virtual bool operator<(BaseVersion &a)
- {
- return name() < a.name();
- };
- virtual bool operator>(BaseVersion &a)
- {
- return name() > a.name();
- };
+ virtual ~BaseVersion() {}
+ /*!
+ * A string used to identify this version in config files.
+ * This should be unique within the version list or shenanigans will occur.
+ */
+ virtual QString descriptor() = 0;
+
+ /*!
+ * The name of this version as it is displayed to the user.
+ * For example: "1.5.1"
+ */
+ virtual QString name() = 0;
+
+ /*!
+ * This should return a string that describes
+ * the kind of version this is (Stable, Beta, Snapshot, whatever)
+ */
+ virtual QString typeString() const = 0;
+
+ virtual bool operator<(BaseVersion &a)
+ {
+ return name() < a.name();
+ };
+ virtual bool operator>(BaseVersion &a)
+ {
+ return name() > a.name();
+ };
};
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
diff --git a/api/logic/BaseVersionList.cpp b/api/logic/BaseVersionList.cpp
index 31a635d7..b3f44463 100644
--- a/api/logic/BaseVersionList.cpp
+++ b/api/logic/BaseVersionList.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,78 +22,78 @@ BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor)
{
- for (int i = 0; i < count(); i++)
- {
- if (at(i)->descriptor() == descriptor)
- return at(i);
- }
- return BaseVersionPtr();
+ for (int i = 0; i < count(); i++)
+ {
+ if (at(i)->descriptor() == descriptor)
+ return at(i);
+ }
+ return BaseVersionPtr();
}
BaseVersionPtr BaseVersionList::getRecommended() const
{
- if (count() <= 0)
- return BaseVersionPtr();
- else
- return at(0);
+ if (count() <= 0)
+ return BaseVersionPtr();
+ else
+ return at(0);
}
QVariant BaseVersionList::data(const QModelIndex &index, int role) const
{
- if (!index.isValid())
- return QVariant();
+ if (!index.isValid())
+ return QVariant();
- if (index.row() > count())
- return QVariant();
+ if (index.row() > count())
+ return QVariant();
- BaseVersionPtr version = at(index.row());
+ BaseVersionPtr version = at(index.row());
- switch (role)
- {
- case VersionPointerRole:
- return qVariantFromValue(version);
+ switch (role)
+ {
+ case VersionPointerRole:
+ return qVariantFromValue(version);
- case VersionRole:
- return version->name();
+ case VersionRole:
+ return version->name();
- case VersionIdRole:
- return version->descriptor();
+ case VersionIdRole:
+ return version->descriptor();
- case TypeRole:
- return version->typeString();
+ case TypeRole:
+ return version->typeString();
- default:
- return QVariant();
- }
+ default:
+ return QVariant();
+ }
}
BaseVersionList::RoleList BaseVersionList::providesRoles() const
{
- return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole};
+ return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole};
}
int BaseVersionList::rowCount(const QModelIndex &parent) const
{
- // Return count
- return count();
+ // Return count
+ return count();
}
int BaseVersionList::columnCount(const QModelIndex &parent) const
{
- return 1;
+ return 1;
}
QHash<int, QByteArray> BaseVersionList::roleNames() const
{
- QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
- roles.insert(VersionRole, "version");
- roles.insert(VersionIdRole, "versionId");
- roles.insert(ParentVersionRole, "parentGameVersion");
- roles.insert(RecommendedRole, "recommended");
- roles.insert(LatestRole, "latest");
- roles.insert(TypeRole, "type");
- roles.insert(BranchRole, "branch");
- roles.insert(PathRole, "path");
- roles.insert(ArchitectureRole, "architecture");
- return roles;
+ QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
+ roles.insert(VersionRole, "version");
+ roles.insert(VersionIdRole, "versionId");
+ roles.insert(ParentVersionRole, "parentGameVersion");
+ roles.insert(RecommendedRole, "recommended");
+ roles.insert(LatestRole, "latest");
+ roles.insert(TypeRole, "type");
+ roles.insert(BranchRole, "branch");
+ roles.insert(PathRole, "path");
+ roles.insert(ArchitectureRole, "architecture");
+ return roles;
}
diff --git a/api/logic/BaseVersionList.h b/api/logic/BaseVersionList.h
index b609e039..64a33d3e 100644
--- a/api/logic/BaseVersionList.h
+++ b/api/logic/BaseVersionList.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,85 +38,85 @@
*/
class MULTIMC_LOGIC_EXPORT BaseVersionList : public QAbstractListModel
{
- Q_OBJECT
+ Q_OBJECT
public:
- enum ModelRoles
- {
- VersionPointerRole = Qt::UserRole,
- VersionRole,
- VersionIdRole,
- ParentVersionRole,
- RecommendedRole,
- LatestRole,
- TypeRole,
- BranchRole,
- PathRole,
- ArchitectureRole,
- SortRole
- };
- typedef QList<int> RoleList;
+ enum ModelRoles
+ {
+ VersionPointerRole = Qt::UserRole,
+ VersionRole,
+ VersionIdRole,
+ ParentVersionRole,
+ RecommendedRole,
+ LatestRole,
+ TypeRole,
+ BranchRole,
+ PathRole,
+ ArchitectureRole,
+ SortRole
+ };
+ typedef QList<int> RoleList;
- explicit BaseVersionList(QObject *parent = 0);
+ explicit BaseVersionList(QObject *parent = 0);
- /*!
- * \brief Gets a task that will reload the version list.
- * Simply execute the task to load the list.
- * The task returned by this function should reset the model when it's done.
- * \return A pointer to a task that reloads the version list.
- */
- virtual shared_qobject_ptr<Task> getLoadTask() = 0;
+ /*!
+ * \brief Gets a task that will reload the version list.
+ * Simply execute the task to load the list.
+ * The task returned by this function should reset the model when it's done.
+ * \return A pointer to a task that reloads the version list.
+ */
+ virtual shared_qobject_ptr<Task> getLoadTask() = 0;
- //! Checks whether or not the list is loaded. If this returns false, the list should be
- //loaded.
- virtual bool isLoaded() = 0;
+ //! Checks whether or not the list is loaded. If this returns false, the list should be
+ //loaded.
+ virtual bool isLoaded() = 0;
- //! Gets the version at the given index.
- virtual const BaseVersionPtr at(int i) const = 0;
+ //! Gets the version at the given index.
+ virtual const BaseVersionPtr at(int i) const = 0;
- //! Returns the number of versions in the list.
- virtual int count() const = 0;
+ //! Returns the number of versions in the list.
+ virtual int count() const = 0;
- //////// List Model Functions ////////
- QVariant data(const QModelIndex &index, int role) const override;
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QHash<int, QByteArray> roleNames() const override;
+ //////// List Model Functions ////////
+ QVariant data(const QModelIndex &index, int role) const override;
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QHash<int, QByteArray> roleNames() const override;
- //! which roles are provided by this version list?
- virtual RoleList providesRoles() const;
+ //! which roles are provided by this version list?
+ virtual RoleList providesRoles() const;
- /*!
- * \brief Finds a version by its descriptor.
- * \param descriptor The descriptor of the version to find.
- * \return A const pointer to the version with the given descriptor. NULL if
- * one doesn't exist.
- */
- virtual BaseVersionPtr findVersion(const QString &descriptor);
+ /*!
+ * \brief Finds a version by its descriptor.
+ * \param descriptor The descriptor of the version to find.
+ * \return A const pointer to the version with the given descriptor. NULL if
+ * one doesn't exist.
+ */
+ virtual BaseVersionPtr findVersion(const QString &descriptor);
- /*!
- * \brief Gets the recommended version from this list
- * If the list doesn't support recommended versions, this works exactly as getLatestStable
- */
- virtual BaseVersionPtr getRecommended() const;
+ /*!
+ * \brief Gets the recommended version from this list
+ * If the list doesn't support recommended versions, this works exactly as getLatestStable
+ */
+ virtual BaseVersionPtr getRecommended() const;
- /*!
- * Sorts the version list.
- */
- virtual void sortVersions() = 0;
+ /*!
+ * Sorts the version list.
+ */
+ virtual void sortVersions() = 0;
protected
slots:
- /*!
- * Updates this list with the given list of versions.
- * This is done by copying each version in the given list and inserting it
- * into this one.
- * We need to do this so that we can set the parents of the versions are set to this
- * version list. This can't be done in the load task, because the versions the load
- * task creates are on the load task's thread and Qt won't allow their parents
- * to be set to something created on another thread.
- * To get around that problem, we invoke this method on the GUI thread, which
- * then copies the versions and sets their parents correctly.
- * \param versions List of versions whose parents should be set.
- */
- virtual void updateListData(QList<BaseVersionPtr> versions) = 0;
+ /*!
+ * Updates this list with the given list of versions.
+ * This is done by copying each version in the given list and inserting it
+ * into this one.
+ * We need to do this so that we can set the parents of the versions are set to this
+ * version list. This can't be done in the load task, because the versions the load
+ * task creates are on the load task's thread and Qt won't allow their parents
+ * to be set to something created on another thread.
+ * To get around that problem, we invoke this method on the GUI thread, which
+ * then copies the versions and sets their parents correctly.
+ * \param versions List of versions whose parents should be set.
+ */
+ virtual void updateListData(QList<BaseVersionPtr> versions) = 0;
};
diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt
index d6b61d23..db1a7dec 100644
--- a/api/logic/CMakeLists.txt
+++ b/api/logic/CMakeLists.txt
@@ -3,444 +3,467 @@ project(MultiMC_logic)
include (UnitTest)
set(CORE_SOURCES
- # LOGIC - Base classes and infrastructure
- BaseInstaller.h
- BaseInstaller.cpp
- BaseVersionList.h
- BaseVersionList.cpp
- InstanceList.h
- InstanceList.cpp
- InstanceTask.h
- InstanceTask.cpp
- LoggedProcess.h
- LoggedProcess.cpp
- MessageLevel.cpp
- MessageLevel.h
- BaseInstanceProvider.h
- FolderInstanceProvider.h
- FolderInstanceProvider.cpp
- BaseVersion.h
- BaseInstance.h
- BaseInstance.cpp
- NullInstance.h
- MMCZip.h
- MMCZip.cpp
- MMCStrings.h
- MMCStrings.cpp
-
- # Basic instance manipulation tasks (derived from InstanceTask)
- InstanceCreationTask.h
- InstanceCreationTask.cpp
- InstanceCopyTask.h
- InstanceCopyTask.cpp
- InstanceImportTask.h
- InstanceImportTask.cpp
-
- # Use tracking separate from memory management
- Usable.h
-
- # Prefix tree where node names are strings between separators
- SeparatorPrefixTree.h
-
- # WARNING: globals live here
- Env.h
- Env.cpp
-
- # String filters
- Filter.h
- Filter.cpp
-
- # JSON parsing helpers
- Json.h
- Json.cpp
-
- FileSystem.h
- FileSystem.cpp
-
- Exception.h
-
- # RW lock protected map
- RWStorage.h
-
- # A variable that has an implicit default value and keeps track of changes
- DefaultVariable.h
-
- # a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
- QObjectPtr.h
-
- # Compression support
- GZip.h
- GZip.cpp
-
- # Command line parameter parsing
- Commandline.h
- Commandline.cpp
-
- # Version number string support
- Version.h
- Version.cpp
-
- # A Recursive file system watcher
- RecursiveFileSystemWatcher.h
- RecursiveFileSystemWatcher.cpp
+ # LOGIC - Base classes and infrastructure
+ BaseInstaller.h
+ BaseInstaller.cpp
+ BaseVersionList.h
+ BaseVersionList.cpp
+ InstanceList.h
+ InstanceList.cpp
+ InstanceTask.h
+ InstanceTask.cpp
+ LoggedProcess.h
+ LoggedProcess.cpp
+ MessageLevel.cpp
+ MessageLevel.h
+ BaseVersion.h
+ BaseInstance.h
+ BaseInstance.cpp
+ NullInstance.h
+ MMCZip.h
+ MMCZip.cpp
+ MMCStrings.h
+ MMCStrings.cpp
+
+ # Basic instance manipulation tasks (derived from InstanceTask)
+ InstanceCreationTask.h
+ InstanceCreationTask.cpp
+ InstanceCopyTask.h
+ InstanceCopyTask.cpp
+ InstanceImportTask.h
+ InstanceImportTask.cpp
+
+ # Use tracking separate from memory management
+ Usable.h
+
+ # Prefix tree where node names are strings between separators
+ SeparatorPrefixTree.h
+
+ # WARNING: globals live here
+ Env.h
+ Env.cpp
+
+ # String filters
+ Filter.h
+ Filter.cpp
+
+ # JSON parsing helpers
+ Json.h
+ Json.cpp
+
+ FileSystem.h
+ FileSystem.cpp
+
+ Exception.h
+
+ # RW lock protected map
+ RWStorage.h
+
+ # A variable that has an implicit default value and keeps track of changes
+ DefaultVariable.h
+
+ # a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
+ QObjectPtr.h
+
+ # Compression support
+ GZip.h
+ GZip.cpp
+
+ # Command line parameter parsing
+ Commandline.h
+ Commandline.cpp
+
+ # Version number string support
+ Version.h
+ Version.cpp
+
+ # A Recursive file system watcher
+ RecursiveFileSystemWatcher.h
+ RecursiveFileSystemWatcher.cpp
)
add_unit_test(FileSystem
- SOURCES FileSystem_test.cpp
- LIBS MultiMC_logic
- DATA testdata
- )
+ SOURCES FileSystem_test.cpp
+ LIBS MultiMC_logic
+ DATA testdata
+ )
add_unit_test(GZip
- SOURCES GZip_test.cpp
- LIBS MultiMC_logic
- )
+ SOURCES GZip_test.cpp
+ LIBS MultiMC_logic
+ )
set(PATHMATCHER_SOURCES
- # Path matchers
- pathmatcher/FSTreeMatcher.h
- pathmatcher/IPathMatcher.h
- pathmatcher/MultiMatcher.h
- pathmatcher/RegexpMatcher.h
+ # Path matchers
+ pathmatcher/FSTreeMatcher.h
+ pathmatcher/IPathMatcher.h
+ pathmatcher/MultiMatcher.h
+ pathmatcher/RegexpMatcher.h
)
set(NET_SOURCES
- # network stuffs
- net/ByteArraySink.h
- net/ChecksumValidator.h
- net/Download.cpp
- net/Download.h
- net/FileSink.cpp
- net/FileSink.h
- net/HttpMetaCache.cpp
- net/HttpMetaCache.h
- net/MetaCacheSink.cpp
- net/MetaCacheSink.h
- net/NetAction.h
- net/NetJob.cpp
- net/NetJob.h
- net/PasteUpload.cpp
- net/PasteUpload.h
- net/Sink.h
- net/URLConstants.cpp
- net/URLConstants.h
- net/Validator.h
+ # network stuffs
+ net/ByteArraySink.h
+ net/ChecksumValidator.h
+ net/Download.cpp
+ net/Download.h
+ net/FileSink.cpp
+ net/FileSink.h
+ net/HttpMetaCache.cpp
+ net/HttpMetaCache.h
+ net/MetaCacheSink.cpp
+ net/MetaCacheSink.h
+ net/NetAction.h
+ net/NetJob.cpp
+ net/NetJob.h
+ net/PasteUpload.cpp
+ net/PasteUpload.h
+ net/Sink.h
+ net/URLConstants.cpp
+ net/URLConstants.h
+ net/Validator.h
)
# Game launch logic
set(LAUNCH_SOURCES
- launch/steps/PostLaunchCommand.cpp
- launch/steps/PostLaunchCommand.h
- launch/steps/PreLaunchCommand.cpp
- launch/steps/PreLaunchCommand.h
- launch/steps/TextPrint.cpp
- launch/steps/TextPrint.h
- launch/steps/Update.cpp
- launch/steps/Update.h
- launch/LaunchStep.cpp
- launch/LaunchStep.h
- launch/LaunchTask.cpp
- launch/LaunchTask.h
- launch/LogModel.cpp
- launch/LogModel.h
+ launch/steps/PostLaunchCommand.cpp
+ launch/steps/PostLaunchCommand.h
+ launch/steps/PreLaunchCommand.cpp
+ launch/steps/PreLaunchCommand.h
+ launch/steps/TextPrint.cpp
+ launch/steps/TextPrint.h
+ launch/steps/Update.cpp
+ launch/steps/Update.h
+ launch/LaunchStep.cpp
+ launch/LaunchStep.h
+ launch/LaunchTask.cpp
+ launch/LaunchTask.h
+ launch/LogModel.cpp
+ launch/LogModel.h
)
# Old update system
set(UPDATE_SOURCES
- updater/GoUpdate.h
- updater/GoUpdate.cpp
- updater/UpdateChecker.h
- updater/UpdateChecker.cpp
- updater/DownloadTask.h
- updater/DownloadTask.cpp
+ updater/GoUpdate.h
+ updater/GoUpdate.cpp
+ updater/UpdateChecker.h
+ updater/UpdateChecker.cpp
+ updater/DownloadTask.h
+ updater/DownloadTask.cpp
)
add_unit_test(UpdateChecker
- SOURCES updater/UpdateChecker_test.cpp
- LIBS MultiMC_logic
- DATA updater/testdata
- )
+ SOURCES updater/UpdateChecker_test.cpp
+ LIBS MultiMC_logic
+ DATA updater/testdata
+ )
add_unit_test(DownloadTask
- SOURCES updater/DownloadTask_test.cpp
- LIBS MultiMC_logic
- DATA updater/testdata
- )
+ SOURCES updater/DownloadTask_test.cpp
+ LIBS MultiMC_logic
+ DATA updater/testdata
+ )
# Rarely used notifications
set(NOTIFICATIONS_SOURCES
- # Notifications - short warning messages
- notifications/NotificationChecker.h
- notifications/NotificationChecker.cpp
+ # Notifications - short warning messages
+ notifications/NotificationChecker.h
+ notifications/NotificationChecker.cpp
)
# Backend for the news bar... there's usually no news.
set(NEWS_SOURCES
- # News System
- news/NewsChecker.h
- news/NewsChecker.cpp
- news/NewsEntry.h
- news/NewsEntry.cpp
+ # News System
+ news/NewsChecker.h
+ news/NewsChecker.cpp
+ news/NewsEntry.h
+ news/NewsEntry.cpp
)
# Icon interface
set(ICONS_SOURCES
- # News System
- icons/IIconList.h
- icons/IIconList.cpp
+ # Icons System and related code
+ icons/IIconList.h
+ icons/IIconList.cpp
+ icons/IconUtils.h
+ icons/IconUtils.cpp
)
# Minecraft services status checker
set(STATUS_SOURCES
- # Status system
- status/StatusChecker.h
- status/StatusChecker.cpp
+ # Status system
+ status/StatusChecker.h
+ status/StatusChecker.cpp
)
# Support for Minecraft instances and launch
set(MINECRAFT_SOURCES
- # Minecraft support
- minecraft/auth/AuthSession.h
- minecraft/auth/AuthSession.cpp
- minecraft/auth/MojangAccountList.h
- minecraft/auth/MojangAccountList.cpp
- minecraft/auth/MojangAccount.h
- minecraft/auth/MojangAccount.cpp
- minecraft/auth/YggdrasilTask.h
- minecraft/auth/YggdrasilTask.cpp
- minecraft/auth/flows/AuthenticateTask.h
- minecraft/auth/flows/AuthenticateTask.cpp
- minecraft/auth/flows/RefreshTask.cpp
- minecraft/auth/flows/RefreshTask.cpp
- minecraft/auth/flows/ValidateTask.h
- minecraft/auth/flows/ValidateTask.cpp
- minecraft/update/AssetUpdateTask.h
- minecraft/update/AssetUpdateTask.cpp
- minecraft/update/FMLLibrariesTask.cpp
- minecraft/update/FMLLibrariesTask.h
- minecraft/update/FoldersTask.cpp
- minecraft/update/FoldersTask.h
- minecraft/update/LibrariesTask.cpp
- minecraft/update/LibrariesTask.h
- minecraft/launch/ClaimAccount.cpp
- minecraft/launch/ClaimAccount.h
- minecraft/launch/CreateServerResourcePacksFolder.cpp
- minecraft/launch/CreateServerResourcePacksFolder.h
- minecraft/launch/ModMinecraftJar.cpp
- minecraft/launch/ModMinecraftJar.h
- minecraft/launch/DirectJavaLaunch.cpp
- minecraft/launch/DirectJavaLaunch.h
- minecraft/launch/ExtractNatives.cpp
- minecraft/launch/ExtractNatives.h
- minecraft/launch/LauncherPartLaunch.cpp
- minecraft/launch/LauncherPartLaunch.h
- minecraft/launch/PrintInstanceInfo.cpp
- minecraft/launch/PrintInstanceInfo.h
- minecraft/legacy/LegacyModList.h
- minecraft/legacy/LegacyModList.cpp
- minecraft/legacy/LegacyInstance.h
- minecraft/legacy/LegacyInstance.cpp
- minecraft/legacy/LegacyUpgradeTask.h
- minecraft/legacy/LegacyUpgradeTask.cpp
- minecraft/GradleSpecifier.h
- minecraft/MinecraftInstance.cpp
- minecraft/MinecraftInstance.h
- minecraft/LaunchProfile.cpp
- minecraft/LaunchProfile.h
- minecraft/Component.cpp
- minecraft/Component.h
- minecraft/ComponentList.cpp
- minecraft/ComponentList.h
- minecraft/ComponentUpdateTask.cpp
- minecraft/ComponentUpdateTask.h
- minecraft/MinecraftLoadAndCheck.h
- minecraft/MinecraftLoadAndCheck.cpp
- minecraft/MinecraftUpdate.h
- minecraft/MinecraftUpdate.cpp
- minecraft/MojangVersionFormat.cpp
- minecraft/MojangVersionFormat.h
- minecraft/Rule.cpp
- minecraft/Rule.h
- minecraft/OneSixVersionFormat.cpp
- minecraft/OneSixVersionFormat.h
- minecraft/OpSys.cpp
- minecraft/OpSys.h
- minecraft/ParseUtils.cpp
- minecraft/ParseUtils.h
- minecraft/ProfileUtils.cpp
- minecraft/ProfileUtils.h
- minecraft/Library.cpp
- minecraft/Library.h
- minecraft/MojangDownloadInfo.h
- minecraft/VersionFile.cpp
- minecraft/VersionFile.h
- minecraft/VersionFilterData.h
- minecraft/VersionFilterData.cpp
- minecraft/Mod.h
- minecraft/Mod.cpp
- minecraft/ModList.h
- minecraft/ModList.cpp
- minecraft/World.h
- minecraft/World.cpp
- minecraft/WorldList.h
- minecraft/WorldList.cpp
-
- # Assets
- minecraft/AssetsUtils.h
- minecraft/AssetsUtils.cpp
-
- # Forge and all things forge related
- minecraft/forge/ForgeXzDownload.h
- minecraft/forge/ForgeXzDownload.cpp
-
- # Skin upload utilities
- minecraft/SkinUpload.cpp
- minecraft/SkinUpload.h
- )
+ # Minecraft support
+ minecraft/auth/AuthSession.h
+ minecraft/auth/AuthSession.cpp
+ minecraft/auth/MojangAccountList.h
+ minecraft/auth/MojangAccountList.cpp
+ minecraft/auth/MojangAccount.h
+ minecraft/auth/MojangAccount.cpp
+ minecraft/auth/YggdrasilTask.h
+ minecraft/auth/YggdrasilTask.cpp
+ minecraft/auth/flows/AuthenticateTask.h
+ minecraft/auth/flows/AuthenticateTask.cpp
+ minecraft/auth/flows/RefreshTask.cpp
+ minecraft/auth/flows/RefreshTask.cpp
+ minecraft/auth/flows/ValidateTask.h
+ minecraft/auth/flows/ValidateTask.cpp
+
+ minecraft/gameoptions/GameOptions.h
+ minecraft/gameoptions/GameOptions.cpp
+
+ minecraft/update/AssetUpdateTask.h
+ minecraft/update/AssetUpdateTask.cpp
+ minecraft/update/FMLLibrariesTask.cpp
+ minecraft/update/FMLLibrariesTask.h
+ minecraft/update/FoldersTask.cpp
+ minecraft/update/FoldersTask.h
+ minecraft/update/LibrariesTask.cpp
+ minecraft/update/LibrariesTask.h
+
+ minecraft/launch/ClaimAccount.cpp
+ minecraft/launch/ClaimAccount.h
+ minecraft/launch/CreateServerResourcePacksFolder.cpp
+ minecraft/launch/CreateServerResourcePacksFolder.h
+ minecraft/launch/ModMinecraftJar.cpp
+ minecraft/launch/ModMinecraftJar.h
+ minecraft/launch/DirectJavaLaunch.cpp
+ minecraft/launch/DirectJavaLaunch.h
+ minecraft/launch/ExtractNatives.cpp
+ minecraft/launch/ExtractNatives.h
+ minecraft/launch/LauncherPartLaunch.cpp
+ minecraft/launch/LauncherPartLaunch.h
+ minecraft/launch/PrintInstanceInfo.cpp
+ minecraft/launch/PrintInstanceInfo.h
+ minecraft/launch/ReconstructAssets.cpp
+ minecraft/launch/ReconstructAssets.h
+ minecraft/launch/ScanModFolders.cpp
+ minecraft/launch/ScanModFolders.h
+
+ minecraft/legacy/LegacyModList.h
+ minecraft/legacy/LegacyModList.cpp
+ minecraft/legacy/LegacyInstance.h
+ minecraft/legacy/LegacyInstance.cpp
+ minecraft/legacy/LegacyUpgradeTask.h
+ minecraft/legacy/LegacyUpgradeTask.cpp
+
+ minecraft/GradleSpecifier.h
+ minecraft/MinecraftInstance.cpp
+ minecraft/MinecraftInstance.h
+ minecraft/LaunchProfile.cpp
+ minecraft/LaunchProfile.h
+ minecraft/Component.cpp
+ minecraft/Component.h
+ minecraft/ComponentList.cpp
+ minecraft/ComponentList.h
+ minecraft/ComponentUpdateTask.cpp
+ minecraft/ComponentUpdateTask.h
+ minecraft/MinecraftLoadAndCheck.h
+ minecraft/MinecraftLoadAndCheck.cpp
+ minecraft/MinecraftUpdate.h
+ minecraft/MinecraftUpdate.cpp
+ minecraft/MojangVersionFormat.cpp
+ minecraft/MojangVersionFormat.h
+ minecraft/Rule.cpp
+ minecraft/Rule.h
+ minecraft/OneSixVersionFormat.cpp
+ minecraft/OneSixVersionFormat.h
+ minecraft/OpSys.cpp
+ minecraft/OpSys.h
+ minecraft/ParseUtils.cpp
+ minecraft/ParseUtils.h
+ minecraft/ProfileUtils.cpp
+ minecraft/ProfileUtils.h
+ minecraft/Library.cpp
+ minecraft/Library.h
+ minecraft/MojangDownloadInfo.h
+ minecraft/VersionFile.cpp
+ minecraft/VersionFile.h
+ minecraft/VersionFilterData.h
+ minecraft/VersionFilterData.cpp
+ minecraft/World.h
+ minecraft/World.cpp
+ minecraft/WorldList.h
+ minecraft/WorldList.cpp
+
+ minecraft/mod/Mod.h
+ minecraft/mod/Mod.cpp
+ minecraft/mod/ModDetails.h
+ minecraft/mod/ModFolderModel.h
+ minecraft/mod/ModFolderModel.cpp
+ minecraft/mod/ModFolderLoadTask.h
+ minecraft/mod/ModFolderLoadTask.cpp
+ minecraft/mod/LocalModParseTask.h
+ minecraft/mod/LocalModParseTask.cpp
+
+ # Assets
+ minecraft/AssetsUtils.h
+ minecraft/AssetsUtils.cpp
+
+ # Forge and all things forge related
+ minecraft/forge/ForgeXzDownload.h
+ minecraft/forge/ForgeXzDownload.cpp
+
+ # Skin upload utilities
+ minecraft/SkinUpload.cpp
+ minecraft/SkinUpload.h
+ )
add_unit_test(GradleSpecifier
- SOURCES minecraft/GradleSpecifier_test.cpp
- LIBS MultiMC_logic
- )
+ SOURCES minecraft/GradleSpecifier_test.cpp
+ LIBS MultiMC_logic
+ )
add_unit_test(MojangVersionFormat
- SOURCES minecraft/MojangVersionFormat_test.cpp
- LIBS MultiMC_logic
- DATA minecraft/testdata
- )
+ SOURCES minecraft/MojangVersionFormat_test.cpp
+ LIBS MultiMC_logic
+ DATA minecraft/testdata
+ )
add_unit_test(Library
- SOURCES minecraft/Library_test.cpp
- LIBS MultiMC_logic
- )
+ SOURCES minecraft/Library_test.cpp
+ LIBS MultiMC_logic
+ )
# FIXME: shares data with FileSystem test
-add_unit_test(ModList
- SOURCES minecraft/ModList_test.cpp
- DATA testdata
- LIBS MultiMC_logic
- )
+add_unit_test(ModFolderModel
+ SOURCES minecraft/mod/ModFolderModel_test.cpp
+ DATA testdata
+ LIBS MultiMC_logic
+ )
add_unit_test(ParseUtils
- SOURCES minecraft/ParseUtils_test.cpp
- LIBS MultiMC_logic
- )
+ SOURCES minecraft/ParseUtils_test.cpp
+ LIBS MultiMC_logic
+ )
# the screenshots feature
set(SCREENSHOTS_SOURCES
- screenshots/Screenshot.h
- screenshots/ImgurUpload.h
- screenshots/ImgurUpload.cpp
- screenshots/ImgurAlbumCreation.h
- screenshots/ImgurAlbumCreation.cpp
+ screenshots/Screenshot.h
+ screenshots/ImgurUpload.h
+ screenshots/ImgurUpload.cpp
+ screenshots/ImgurAlbumCreation.h
+ screenshots/ImgurAlbumCreation.cpp
)
set(TASKS_SOURCES
- # Tasks
- tasks/Task.h
- tasks/Task.cpp
- tasks/SequentialTask.h
- tasks/SequentialTask.cpp
+ # Tasks
+ tasks/Task.h
+ tasks/Task.cpp
+ tasks/SequentialTask.h
+ tasks/SequentialTask.cpp
)
set(SETTINGS_SOURCES
- # Settings
- settings/INIFile.cpp
- settings/INIFile.h
- settings/INISettingsObject.cpp
- settings/INISettingsObject.h
- settings/OverrideSetting.cpp
- settings/OverrideSetting.h
- settings/PassthroughSetting.cpp
- settings/PassthroughSetting.h
- settings/Setting.cpp
- settings/Setting.h
- settings/SettingsObject.cpp
- settings/SettingsObject.h
+ # Settings
+ settings/INIFile.cpp
+ settings/INIFile.h
+ settings/INISettingsObject.cpp
+ settings/INISettingsObject.h
+ settings/OverrideSetting.cpp
+ settings/OverrideSetting.h
+ settings/PassthroughSetting.cpp
+ settings/PassthroughSetting.h
+ settings/Setting.cpp
+ settings/Setting.h
+ settings/SettingsObject.cpp
+ settings/SettingsObject.h
)
add_unit_test(INIFile
- SOURCES settings/INIFile_test.cpp
- LIBS MultiMC_logic
- )
+ SOURCES settings/INIFile_test.cpp
+ LIBS MultiMC_logic
+ )
set(JAVA_SOURCES
- # Java related code
- java/launch/CheckJava.cpp
- java/launch/CheckJava.h
- java/JavaChecker.h
- java/JavaChecker.cpp
- java/JavaCheckerJob.h
- java/JavaCheckerJob.cpp
- java/JavaInstall.h
- java/JavaInstall.cpp
- java/JavaInstallList.h
- java/JavaInstallList.cpp
- java/JavaUtils.h
- java/JavaUtils.cpp
- java/JavaVersion.h
- java/JavaVersion.cpp
+ # Java related code
+ java/launch/CheckJava.cpp
+ java/launch/CheckJava.h
+ java/JavaChecker.h
+ java/JavaChecker.cpp
+ java/JavaCheckerJob.h
+ java/JavaCheckerJob.cpp
+ java/JavaInstall.h
+ java/JavaInstall.cpp
+ java/JavaInstallList.h
+ java/JavaInstallList.cpp
+ java/JavaUtils.h
+ java/JavaUtils.cpp
+ java/JavaVersion.h
+ java/JavaVersion.cpp
)
add_unit_test(JavaVersion
- SOURCES java/JavaVersion_test.cpp
- LIBS MultiMC_logic
- )
+ SOURCES java/JavaVersion_test.cpp
+ LIBS MultiMC_logic
+ )
set(TRANSLATIONS_SOURCES
- translations/TranslationsModel.h
- translations/TranslationsModel.cpp
+ translations/TranslationsModel.h
+ translations/TranslationsModel.cpp
+ translations/POTranslator.h
+ translations/POTranslator.cpp
)
set(TOOLS_SOURCES
- # Tools
- tools/BaseExternalTool.cpp
- tools/BaseExternalTool.h
- tools/BaseProfiler.cpp
- tools/BaseProfiler.h
- tools/JProfiler.cpp
- tools/JProfiler.h
- tools/JVisualVM.cpp
- tools/JVisualVM.h
- tools/MCEditTool.cpp
- tools/MCEditTool.h
+ # Tools
+ tools/BaseExternalTool.cpp
+ tools/BaseExternalTool.h
+ tools/BaseProfiler.cpp
+ tools/BaseProfiler.h
+ tools/JProfiler.cpp
+ tools/JProfiler.h
+ tools/JVisualVM.cpp
+ tools/JVisualVM.h
+ tools/MCEditTool.cpp
+ tools/MCEditTool.h
)
set(META_SOURCES
- # Metadata sources
- meta/JsonFormat.cpp
- meta/JsonFormat.h
- meta/BaseEntity.cpp
- meta/BaseEntity.h
- meta/VersionList.cpp
- meta/VersionList.h
- meta/Version.cpp
- meta/Version.h
- meta/Index.cpp
- meta/Index.h
+ # Metadata sources
+ meta/JsonFormat.cpp
+ meta/JsonFormat.h
+ meta/BaseEntity.cpp
+ meta/BaseEntity.h
+ meta/VersionList.cpp
+ meta/VersionList.h
+ meta/Version.cpp
+ meta/Version.h
+ meta/Index.cpp
+ meta/Index.h
)
set(FTB_SOURCES
- modplatform/ftb/FtbPackFetchTask.h
- modplatform/ftb/FtbPackFetchTask.cpp
- modplatform/ftb/FtbPackInstallTask.h
- modplatform/ftb/FtbPackInstallTask.cpp
+ modplatform/ftb/FtbPackFetchTask.h
+ modplatform/ftb/FtbPackFetchTask.cpp
+ modplatform/ftb/FtbPackInstallTask.h
+ modplatform/ftb/FtbPackInstallTask.cpp
- modplatform/ftb/PackHelpers.h
+ 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
+ # Flame
+ modplatform/flame/PackManifest.h
+ modplatform/flame/PackManifest.cpp
+ modplatform/flame/FileResolvingTask.h
+ modplatform/flame/FileResolvingTask.cpp
+ modplatform/flame/UrlResolvingTask.h
+ modplatform/flame/UrlResolvingTask.cpp
)
add_unit_test(Index
- SOURCES meta/Index_test.cpp
- LIBS MultiMC_logic
- )
+ SOURCES meta/Index_test.cpp
+ LIBS MultiMC_logic
+ )
################################ COMPILE ################################
@@ -448,25 +471,25 @@ add_unit_test(Index
find_package(ZLIB REQUIRED)
set(LOGIC_SOURCES
- ${CORE_SOURCES}
- ${PATHMATCHER_SOURCES}
- ${NET_SOURCES}
- ${LAUNCH_SOURCES}
- ${UPDATE_SOURCES}
- ${NOTIFICATIONS_SOURCES}
- ${NEWS_SOURCES}
- ${STATUS_SOURCES}
- ${MINECRAFT_SOURCES}
- ${SCREENSHOTS_SOURCES}
- ${TASKS_SOURCES}
- ${SETTINGS_SOURCES}
- ${JAVA_SOURCES}
- ${TRANSLATIONS_SOURCES}
- ${TOOLS_SOURCES}
- ${META_SOURCES}
- ${ICONS_SOURCES}
- ${FTB_SOURCES}
- ${FLAME_SOURCES}
+ ${CORE_SOURCES}
+ ${PATHMATCHER_SOURCES}
+ ${NET_SOURCES}
+ ${LAUNCH_SOURCES}
+ ${UPDATE_SOURCES}
+ ${NOTIFICATIONS_SOURCES}
+ ${NEWS_SOURCES}
+ ${STATUS_SOURCES}
+ ${MINECRAFT_SOURCES}
+ ${SCREENSHOTS_SOURCES}
+ ${TASKS_SOURCES}
+ ${SETTINGS_SOURCES}
+ ${JAVA_SOURCES}
+ ${TRANSLATIONS_SOURCES}
+ ${TOOLS_SOURCES}
+ ${META_SOURCES}
+ ${ICONS_SOURCES}
+ ${FTB_SOURCES}
+ ${FLAME_SOURCES}
)
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
@@ -476,14 +499,14 @@ generate_export_header(MultiMC_logic)
# Link
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
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}
+ TARGETS MultiMC_logic
+ RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
+ LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
)
diff --git a/api/logic/Commandline.cpp b/api/logic/Commandline.cpp
index eac9db09..59f1666b 100644
--- a/api/logic/Commandline.cpp
+++ b/api/logic/Commandline.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
@@ -27,453 +27,453 @@ namespace Commandline
// commandline splitter
QStringList splitArgs(QString args)
{
- QStringList argv;
- QString current;
- bool escape = false;
- QChar inquotes;
- for (int i = 0; i < args.length(); i++)
- {
- QChar cchar = args.at(i);
-
- // \ escaped
- if (escape)
- {
- current += cchar;
- escape = false;
- // in "quotes"
- }
- else if (!inquotes.isNull())
- {
- if (cchar == '\\')
- escape = true;
- else if (cchar == inquotes)
- inquotes = 0;
- else
- current += cchar;
- // otherwise
- }
- else
- {
- if (cchar == ' ')
- {
- if (!current.isEmpty())
- {
- argv << current;
- current.clear();
- }
- }
- else if (cchar == '"' || cchar == '\'')
- inquotes = cchar;
- else
- current += cchar;
- }
- }
- if (!current.isEmpty())
- argv << current;
- return argv;
+ QStringList argv;
+ QString current;
+ bool escape = false;
+ QChar inquotes;
+ for (int i = 0; i < args.length(); i++)
+ {
+ QChar cchar = args.at(i);
+
+ // \ escaped
+ if (escape)
+ {
+ current += cchar;
+ escape = false;
+ // in "quotes"
+ }
+ else if (!inquotes.isNull())
+ {
+ if (cchar == '\\')
+ escape = true;
+ else if (cchar == inquotes)
+ inquotes = 0;
+ else
+ current += cchar;
+ // otherwise
+ }
+ else
+ {
+ if (cchar == ' ')
+ {
+ if (!current.isEmpty())
+ {
+ argv << current;
+ current.clear();
+ }
+ }
+ else if (cchar == '"' || cchar == '\'')
+ inquotes = cchar;
+ else
+ current += cchar;
+ }
+ }
+ if (!current.isEmpty())
+ argv << current;
+ return argv;
}
Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle)
{
- m_flagStyle = flagStyle;
- m_argStyle = argStyle;
+ m_flagStyle = flagStyle;
+ m_argStyle = argStyle;
}
// styles setter/getter
void Parser::setArgumentStyle(ArgumentStyle::Enum style)
{
- m_argStyle = style;
+ m_argStyle = style;
}
ArgumentStyle::Enum Parser::argumentStyle()
{
- return m_argStyle;
+ return m_argStyle;
}
void Parser::setFlagStyle(FlagStyle::Enum style)
{
- m_flagStyle = style;
+ m_flagStyle = style;
}
FlagStyle::Enum Parser::flagStyle()
{
- return m_flagStyle;
+ return m_flagStyle;
}
// setup methods
void Parser::addSwitch(QString name, bool def)
{
- if (m_params.contains(name))
- throw "Name not unique";
-
- OptionDef *param = new OptionDef;
- param->type = otSwitch;
- param->name = name;
- param->metavar = QString("<%1>").arg(name);
- param->def = def;
-
- m_options[name] = param;
- m_params[name] = (CommonDef *)param;
- m_optionList.append(param);
+ if (m_params.contains(name))
+ throw "Name not unique";
+
+ OptionDef *param = new OptionDef;
+ param->type = otSwitch;
+ param->name = name;
+ param->metavar = QString("<%1>").arg(name);
+ param->def = def;
+
+ m_options[name] = param;
+ m_params[name] = (CommonDef *)param;
+ m_optionList.append(param);
}
void Parser::addOption(QString name, QVariant def)
{
- if (m_params.contains(name))
- throw "Name not unique";
-
- OptionDef *param = new OptionDef;
- param->type = otOption;
- param->name = name;
- param->metavar = QString("<%1>").arg(name);
- param->def = def;
-
- m_options[name] = param;
- m_params[name] = (CommonDef *)param;
- m_optionList.append(param);
+ if (m_params.contains(name))
+ throw "Name not unique";
+
+ OptionDef *param = new OptionDef;
+ param->type = otOption;
+ param->name = name;
+ param->metavar = QString("<%1>").arg(name);
+ param->def = def;
+
+ m_options[name] = param;
+ m_params[name] = (CommonDef *)param;
+ m_optionList.append(param);
}
void Parser::addArgument(QString name, bool required, QVariant def)
{
- if (m_params.contains(name))
- throw "Name not unique";
+ if (m_params.contains(name))
+ throw "Name not unique";
- PositionalDef *param = new PositionalDef;
- param->name = name;
- param->def = def;
- param->required = required;
- param->metavar = name;
+ PositionalDef *param = new PositionalDef;
+ param->name = name;
+ param->def = def;
+ param->required = required;
+ param->metavar = name;
- m_positionals.append(param);
- m_params[name] = (CommonDef *)param;
+ m_positionals.append(param);
+ m_params[name] = (CommonDef *)param;
}
void Parser::addDocumentation(QString name, QString doc, QString metavar)
{
- if (!m_params.contains(name))
- throw "Name does not exist";
+ if (!m_params.contains(name))
+ throw "Name does not exist";
- CommonDef *param = m_params[name];
- param->doc = doc;
- if (!metavar.isNull())
- param->metavar = metavar;
+ CommonDef *param = m_params[name];
+ param->doc = doc;
+ if (!metavar.isNull())
+ param->metavar = metavar;
}
void Parser::addShortOpt(QString name, QChar flag)
{
- if (!m_params.contains(name))
- throw "Name does not exist";
- if (!m_options.contains(name))
- throw "Name is not an Option or Swtich";
-
- OptionDef *param = m_options[name];
- m_flags[flag] = param;
- param->flag = flag;
+ if (!m_params.contains(name))
+ throw "Name does not exist";
+ if (!m_options.contains(name))
+ throw "Name is not an Option or Swtich";
+
+ OptionDef *param = m_options[name];
+ m_flags[flag] = param;
+ param->flag = flag;
}
// help methods
QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
{
- QStringList help;
- help << compileUsage(progName, useFlags) << "\r\n";
-
- // positionals
- if (!m_positionals.isEmpty())
- {
- help << "\r\n";
- help << "Positional arguments:\r\n";
- QListIterator<PositionalDef *> it2(m_positionals);
- while (it2.hasNext())
- {
- PositionalDef *param = it2.next();
- help << " " << param->metavar;
- help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
- help << param->doc << "\r\n";
- }
- }
-
- // Options
- if (!m_optionList.isEmpty())
- {
- help << "\r\n";
- QString optPrefix, flagPrefix;
- getPrefix(optPrefix, flagPrefix);
-
- help << "Options & Switches:\r\n";
- QListIterator<OptionDef *> it(m_optionList);
- while (it.hasNext())
- {
- OptionDef *option = it.next();
- help << " ";
- int nameLength = optPrefix.length() + option->name.length();
- if (!option->flag.isNull())
- {
- nameLength += 3 + flagPrefix.length();
- help << flagPrefix << option->flag << ", ";
- }
- help << optPrefix << option->name;
- if (option->type == otOption)
- {
- QString arg = QString("%1%2").arg(
- ((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
- nameLength += arg.length();
- help << arg;
- }
- help << " " << QString(helpIndent - nameLength - 1, ' ');
- help << option->doc << "\r\n";
- }
- }
-
- return help.join("");
+ QStringList help;
+ help << compileUsage(progName, useFlags) << "\r\n";
+
+ // positionals
+ if (!m_positionals.isEmpty())
+ {
+ help << "\r\n";
+ help << "Positional arguments:\r\n";
+ QListIterator<PositionalDef *> it2(m_positionals);
+ while (it2.hasNext())
+ {
+ PositionalDef *param = it2.next();
+ help << " " << param->metavar;
+ help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
+ help << param->doc << "\r\n";
+ }
+ }
+
+ // Options
+ if (!m_optionList.isEmpty())
+ {
+ help << "\r\n";
+ QString optPrefix, flagPrefix;
+ getPrefix(optPrefix, flagPrefix);
+
+ help << "Options & Switches:\r\n";
+ QListIterator<OptionDef *> it(m_optionList);
+ while (it.hasNext())
+ {
+ OptionDef *option = it.next();
+ help << " ";
+ int nameLength = optPrefix.length() + option->name.length();
+ if (!option->flag.isNull())
+ {
+ nameLength += 3 + flagPrefix.length();
+ help << flagPrefix << option->flag << ", ";
+ }
+ help << optPrefix << option->name;
+ if (option->type == otOption)
+ {
+ QString arg = QString("%1%2").arg(
+ ((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
+ nameLength += arg.length();
+ help << arg;
+ }
+ help << " " << QString(helpIndent - nameLength - 1, ' ');
+ help << option->doc << "\r\n";
+ }
+ }
+
+ return help.join("");
}
QString Parser::compileUsage(QString progName, bool useFlags)
{
- QStringList usage;
- usage << "Usage: " << progName;
-
- QString optPrefix, flagPrefix;
- getPrefix(optPrefix, flagPrefix);
-
- // options
- QListIterator<OptionDef *> it(m_optionList);
- while (it.hasNext())
- {
- OptionDef *option = it.next();
- usage << " [";
- if (!option->flag.isNull() && useFlags)
- usage << flagPrefix << option->flag;
- else
- usage << optPrefix << option->name;
- if (option->type == otOption)
- usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
- usage << "]";
- }
-
- // arguments
- QListIterator<PositionalDef *> it2(m_positionals);
- while (it2.hasNext())
- {
- PositionalDef *param = it2.next();
- usage << " " << (param->required ? "<" : "[");
- usage << param->metavar;
- usage << (param->required ? ">" : "]");
- }
-
- return usage.join("");
+ QStringList usage;
+ usage << "Usage: " << progName;
+
+ QString optPrefix, flagPrefix;
+ getPrefix(optPrefix, flagPrefix);
+
+ // options
+ QListIterator<OptionDef *> it(m_optionList);
+ while (it.hasNext())
+ {
+ OptionDef *option = it.next();
+ usage << " [";
+ if (!option->flag.isNull() && useFlags)
+ usage << flagPrefix << option->flag;
+ else
+ usage << optPrefix << option->name;
+ if (option->type == otOption)
+ usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
+ usage << "]";
+ }
+
+ // arguments
+ QListIterator<PositionalDef *> it2(m_positionals);
+ while (it2.hasNext())
+ {
+ PositionalDef *param = it2.next();
+ usage << " " << (param->required ? "<" : "[");
+ usage << param->metavar;
+ usage << (param->required ? ">" : "]");
+ }
+
+ return usage.join("");
}
// parsing
QHash<QString, QVariant> Parser::parse(QStringList argv)
{
- QHash<QString, QVariant> map;
+ QHash<QString, QVariant> map;
- QStringListIterator it(argv);
- QString programName = it.next();
+ QStringListIterator it(argv);
+ QString programName = it.next();
- QString optionPrefix;
- QString flagPrefix;
- QListIterator<PositionalDef *> positionals(m_positionals);
- QStringList expecting;
+ QString optionPrefix;
+ QString flagPrefix;
+ QListIterator<PositionalDef *> positionals(m_positionals);
+ QStringList expecting;
- getPrefix(optionPrefix, flagPrefix);
+ getPrefix(optionPrefix, flagPrefix);
- while (it.hasNext())
- {
- QString arg = it.next();
+ while (it.hasNext())
+ {
+ QString arg = it.next();
- if (!expecting.isEmpty())
- // we were expecting an argument
- {
- QString name = expecting.first();
+ if (!expecting.isEmpty())
+ // we were expecting an argument
+ {
+ QString name = expecting.first();
/*
- if (map.contains(name))
- throw ParsingError(
- QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
+ if (map.contains(name))
+ throw ParsingError(
+ QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
*/
- map[name] = QVariant(arg);
-
- expecting.removeFirst();
- continue;
- }
-
- if (arg.startsWith(optionPrefix))
- // we have an option
- {
- // qDebug("Found option %s", qPrintable(arg));
-
- QString name = arg.mid(optionPrefix.length());
- QString equals;
-
- if ((m_argStyle == ArgumentStyle::Equals ||
- m_argStyle == ArgumentStyle::SpaceAndEquals) &&
- name.contains("="))
- {
- int i = name.indexOf("=");
- equals = name.mid(i + 1);
- name = name.left(i);
- }
-
- if (m_options.contains(name))
- {
- /*
- if (map.contains(name))
- throw ParsingError(QString("Option %2%1 was given multiple times")
- .arg(name, optionPrefix));
+ map[name] = QVariant(arg);
+
+ expecting.removeFirst();
+ continue;
+ }
+
+ if (arg.startsWith(optionPrefix))
+ // we have an option
+ {
+ // qDebug("Found option %s", qPrintable(arg));
+
+ QString name = arg.mid(optionPrefix.length());
+ QString equals;
+
+ if ((m_argStyle == ArgumentStyle::Equals ||
+ m_argStyle == ArgumentStyle::SpaceAndEquals) &&
+ name.contains("="))
+ {
+ int i = name.indexOf("=");
+ equals = name.mid(i + 1);
+ name = name.left(i);
+ }
+
+ if (m_options.contains(name))
+ {
+ /*
+ if (map.contains(name))
+ throw ParsingError(QString("Option %2%1 was given multiple times")
+ .arg(name, optionPrefix));
*/
- OptionDef *option = m_options[name];
- if (option->type == otSwitch)
- map[name] = true;
- else // if (option->type == otOption)
- {
- if (m_argStyle == ArgumentStyle::Space)
- expecting.append(name);
- else if (!equals.isNull())
- map[name] = equals;
- else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
- expecting.append(name);
- else
- throw ParsingError(QString("Option %2%1 reqires an argument.")
- .arg(name, optionPrefix));
- }
-
- continue;
- }
-
- throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
- }
-
- if (arg.startsWith(flagPrefix))
- // we have (a) flag(s)
- {
- // qDebug("Found flags %s", qPrintable(arg));
-
- QString flags = arg.mid(flagPrefix.length());
- QString equals;
-
- if ((m_argStyle == ArgumentStyle::Equals ||
- m_argStyle == ArgumentStyle::SpaceAndEquals) &&
- flags.contains("="))
- {
- int i = flags.indexOf("=");
- equals = flags.mid(i + 1);
- flags = flags.left(i);
- }
-
- for (int i = 0; i < flags.length(); i++)
- {
- QChar flag = flags.at(i);
-
- if (!m_flags.contains(flag))
- throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
-
- OptionDef *option = m_flags[flag];
+ OptionDef *option = m_options[name];
+ if (option->type == otSwitch)
+ map[name] = true;
+ else // if (option->type == otOption)
+ {
+ if (m_argStyle == ArgumentStyle::Space)
+ expecting.append(name);
+ else if (!equals.isNull())
+ map[name] = equals;
+ else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
+ expecting.append(name);
+ else
+ throw ParsingError(QString("Option %2%1 reqires an argument.")
+ .arg(name, optionPrefix));
+ }
+
+ continue;
+ }
+
+ throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
+ }
+
+ if (arg.startsWith(flagPrefix))
+ // we have (a) flag(s)
+ {
+ // qDebug("Found flags %s", qPrintable(arg));
+
+ QString flags = arg.mid(flagPrefix.length());
+ QString equals;
+
+ if ((m_argStyle == ArgumentStyle::Equals ||
+ m_argStyle == ArgumentStyle::SpaceAndEquals) &&
+ flags.contains("="))
+ {
+ int i = flags.indexOf("=");
+ equals = flags.mid(i + 1);
+ flags = flags.left(i);
+ }
+
+ for (int i = 0; i < flags.length(); i++)
+ {
+ QChar flag = flags.at(i);
+
+ if (!m_flags.contains(flag))
+ throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
+
+ OptionDef *option = m_flags[flag];
/*
- if (map.contains(option->name))
- throw ParsingError(QString("Option %2%1 was given multiple times")
- .arg(option->name, optionPrefix));
+ if (map.contains(option->name))
+ throw ParsingError(QString("Option %2%1 was given multiple times")
+ .arg(option->name, optionPrefix));
*/
- if (option->type == otSwitch)
- map[option->name] = true;
- else // if (option->type == otOption)
- {
- if (m_argStyle == ArgumentStyle::Space)
- expecting.append(option->name);
- else if (!equals.isNull())
- if (i == flags.length() - 1)
- map[option->name] = equals;
- else
- throw ParsingError(QString("Flag %4%2 of Argument-requiring Option "
- "%1 not last flag in %4%3")
- .arg(option->name, flag, flags, flagPrefix));
- else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
- expecting.append(option->name);
- else
- throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)")
- .arg(option->name, flag, flagPrefix));
- }
- }
-
- continue;
- }
-
- // must be a positional argument
- if (!positionals.hasNext())
- throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
-
- PositionalDef *param = positionals.next();
-
- map[param->name] = arg;
- }
-
- // check if we're missing something
- if (!expecting.isEmpty())
- throw ParsingError(QString("Was still expecting arguments for %2%1").arg(
- expecting.join(QString(", ") + optionPrefix), optionPrefix));
-
- while (positionals.hasNext())
- {
- PositionalDef *param = positionals.next();
- if (param->required)
- throw ParsingError(
- QString("Missing required positional argument '%1'").arg(param->name));
- else
- map[param->name] = param->def;
- }
-
- // fill out gaps
- QListIterator<OptionDef *> iter(m_optionList);
- while (iter.hasNext())
- {
- OptionDef *option = iter.next();
- if (!map.contains(option->name))
- map[option->name] = option->def;
- }
-
- return map;
+ if (option->type == otSwitch)
+ map[option->name] = true;
+ else // if (option->type == otOption)
+ {
+ if (m_argStyle == ArgumentStyle::Space)
+ expecting.append(option->name);
+ else if (!equals.isNull())
+ if (i == flags.length() - 1)
+ map[option->name] = equals;
+ else
+ throw ParsingError(QString("Flag %4%2 of Argument-requiring Option "
+ "%1 not last flag in %4%3")
+ .arg(option->name, flag, flags, flagPrefix));
+ else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
+ expecting.append(option->name);
+ else
+ throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)")
+ .arg(option->name, flag, flagPrefix));
+ }
+ }
+
+ continue;
+ }
+
+ // must be a positional argument
+ if (!positionals.hasNext())
+ throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
+
+ PositionalDef *param = positionals.next();
+
+ map[param->name] = arg;
+ }
+
+ // check if we're missing something
+ if (!expecting.isEmpty())
+ throw ParsingError(QString("Was still expecting arguments for %2%1").arg(
+ expecting.join(QString(", ") + optionPrefix), optionPrefix));
+
+ while (positionals.hasNext())
+ {
+ PositionalDef *param = positionals.next();
+ if (param->required)
+ throw ParsingError(
+ QString("Missing required positional argument '%1'").arg(param->name));
+ else
+ map[param->name] = param->def;
+ }
+
+ // fill out gaps
+ QListIterator<OptionDef *> iter(m_optionList);
+ while (iter.hasNext())
+ {
+ OptionDef *option = iter.next();
+ if (!map.contains(option->name))
+ map[option->name] = option->def;
+ }
+
+ return map;
}
// clear defs
void Parser::clear()
{
- m_flags.clear();
- m_params.clear();
- m_options.clear();
-
- QMutableListIterator<OptionDef *> it(m_optionList);
- while (it.hasNext())
- {
- OptionDef *option = it.next();
- it.remove();
- delete option;
- }
-
- QMutableListIterator<PositionalDef *> it2(m_positionals);
- while (it2.hasNext())
- {
- PositionalDef *arg = it2.next();
- it2.remove();
- delete arg;
- }
+ m_flags.clear();
+ m_params.clear();
+ m_options.clear();
+
+ QMutableListIterator<OptionDef *> it(m_optionList);
+ while (it.hasNext())
+ {
+ OptionDef *option = it.next();
+ it.remove();
+ delete option;
+ }
+
+ QMutableListIterator<PositionalDef *> it2(m_positionals);
+ while (it2.hasNext())
+ {
+ PositionalDef *arg = it2.next();
+ it2.remove();
+ delete arg;
+ }
}
// Destructor
Parser::~Parser()
{
- clear();
+ clear();
}
// getPrefix
void Parser::getPrefix(QString &opt, QString &flag)
{
- if (m_flagStyle == FlagStyle::Windows)
- opt = flag = "/";
- else if (m_flagStyle == FlagStyle::Unix)
- opt = flag = "-";
- // else if (m_flagStyle == FlagStyle::GNU)
- else
- {
- opt = "--";
- flag = "-";
- }
+ if (m_flagStyle == FlagStyle::Windows)
+ opt = flag = "/";
+ else if (m_flagStyle == FlagStyle::Unix)
+ opt = flag = "-";
+ // else if (m_flagStyle == FlagStyle::GNU)
+ else
+ {
+ opt = "--";
+ flag = "-";
+ }
}
// ParsingError
diff --git a/api/logic/Commandline.h b/api/logic/Commandline.h
index c8c8be29..5f6e5510 100644
--- a/api/logic/Commandline.h
+++ b/api/logic/Commandline.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
@@ -51,13 +51,13 @@ namespace FlagStyle
{
enum Enum
{
- GNU, /**< --option and -o (GNU Style) */
- Unix, /**< -option and -o (Unix Style) */
- Windows, /**< /option and /o (Windows Style) */
+ GNU, /**< --option and -o (GNU Style) */
+ Unix, /**< -option and -o (Unix Style) */
+ Windows, /**< /option and /o (Windows Style) */
#ifdef Q_OS_WIN32
- Default = Windows
+ Default = Windows
#else
- Default = GNU
+ Default = GNU
#endif
};
}
@@ -69,13 +69,13 @@ namespace ArgumentStyle
{
enum Enum
{
- Space, /**< --option=value */
- Equals, /**< --option value */
- SpaceAndEquals, /**< --option[= ]value */
+ Space, /**< --option=value */
+ Equals, /**< --option value */
+ SpaceAndEquals, /**< --option[= ]value */
#ifdef Q_OS_WIN32
- Default = Equals
+ Default = Equals
#else
- Default = SpaceAndEquals
+ Default = SpaceAndEquals
#endif
};
}
@@ -86,7 +86,7 @@ enum Enum
class MULTIMC_LOGIC_EXPORT ParsingError : public std::runtime_error
{
public:
- ParsingError(const QString &what);
+ ParsingError(const QString &what);
};
/**
@@ -95,158 +95,158 @@ public:
class MULTIMC_LOGIC_EXPORT Parser
{
public:
- /**
- * @brief Parser constructor
- * @param flagStyle the FlagStyle to use in this Parser
- * @param argStyle the ArgumentStyle to use in this Parser
- */
- Parser(FlagStyle::Enum flagStyle = FlagStyle::Default,
- ArgumentStyle::Enum argStyle = ArgumentStyle::Default);
-
- /**
- * @brief set the flag style
- * @param style
- */
- void setFlagStyle(FlagStyle::Enum style);
-
- /**
- * @brief get the flag style
- * @return
- */
- FlagStyle::Enum flagStyle();
-
- /**
- * @brief set the argument style
- * @param style
- */
- void setArgumentStyle(ArgumentStyle::Enum style);
-
- /**
- * @brief get the argument style
- * @return
- */
- ArgumentStyle::Enum argumentStyle();
-
- /**
- * @brief define a boolean switch
- * @param name the parameter name
- * @param def the default value
- */
- void addSwitch(QString name, bool def = false);
-
- /**
- * @brief define an option that takes an additional argument
- * @param name the parameter name
- * @param def the default value
- */
- void addOption(QString name, QVariant def = QVariant());
-
- /**
- * @brief define a positional argument
- * @param name the parameter name
- * @param required wether this argument is required
- * @param def the default value
- */
- void addArgument(QString name, bool required = true, QVariant def = QVariant());
-
- /**
- * @brief adds a flag to an existing parameter
- * @param name the (existing) parameter name
- * @param flag the flag character
- * @see addSwitch addArgument addOption
- * Note: any one parameter can only have one flag
- */
- void addShortOpt(QString name, QChar flag);
-
- /**
- * @brief adds documentation to a Parameter
- * @param name the parameter name
- * @param metavar a string to be displayed as placeholder for the value
- * @param doc a QString containing the documentation
- * Note: on positional arguments, metavar replaces the name as displayed.
- * on options , metavar replaces the value placeholder
- */
- void addDocumentation(QString name, QString doc, QString metavar = QString());
-
- /**
- * @brief generate a help message
- * @param progName the program name to use in the help message
- * @param helpIndent how much the parameter documentation should be indented
- * @param flagsInUsage whether we should use flags instead of options in the usage
- * @return a help message
- */
- QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true);
-
- /**
- * @brief generate a short usage message
- * @param progName the program name to use in the usage message
- * @param useFlags whether we should use flags instead of options
- * @return a usage message
- */
- QString compileUsage(QString progName, bool useFlags = true);
-
- /**
- * @brief parse
- * @param argv a QStringList containing the program ARGV
- * @return a QHash mapping argument names to their values
- */
- QHash<QString, QVariant> parse(QStringList argv);
-
- /**
- * @brief clear all definitions
- */
- void clear();
-
- ~Parser();
+ /**
+ * @brief Parser constructor
+ * @param flagStyle the FlagStyle to use in this Parser
+ * @param argStyle the ArgumentStyle to use in this Parser
+ */
+ Parser(FlagStyle::Enum flagStyle = FlagStyle::Default,
+ ArgumentStyle::Enum argStyle = ArgumentStyle::Default);
+
+ /**
+ * @brief set the flag style
+ * @param style
+ */
+ void setFlagStyle(FlagStyle::Enum style);
+
+ /**
+ * @brief get the flag style
+ * @return
+ */
+ FlagStyle::Enum flagStyle();
+
+ /**
+ * @brief set the argument style
+ * @param style
+ */
+ void setArgumentStyle(ArgumentStyle::Enum style);
+
+ /**
+ * @brief get the argument style
+ * @return
+ */
+ ArgumentStyle::Enum argumentStyle();
+
+ /**
+ * @brief define a boolean switch
+ * @param name the parameter name
+ * @param def the default value
+ */
+ void addSwitch(QString name, bool def = false);
+
+ /**
+ * @brief define an option that takes an additional argument
+ * @param name the parameter name
+ * @param def the default value
+ */
+ void addOption(QString name, QVariant def = QVariant());
+
+ /**
+ * @brief define a positional argument
+ * @param name the parameter name
+ * @param required wether this argument is required
+ * @param def the default value
+ */
+ void addArgument(QString name, bool required = true, QVariant def = QVariant());
+
+ /**
+ * @brief adds a flag to an existing parameter
+ * @param name the (existing) parameter name
+ * @param flag the flag character
+ * @see addSwitch addArgument addOption
+ * Note: any one parameter can only have one flag
+ */
+ void addShortOpt(QString name, QChar flag);
+
+ /**
+ * @brief adds documentation to a Parameter
+ * @param name the parameter name
+ * @param metavar a string to be displayed as placeholder for the value
+ * @param doc a QString containing the documentation
+ * Note: on positional arguments, metavar replaces the name as displayed.
+ * on options , metavar replaces the value placeholder
+ */
+ void addDocumentation(QString name, QString doc, QString metavar = QString());
+
+ /**
+ * @brief generate a help message
+ * @param progName the program name to use in the help message
+ * @param helpIndent how much the parameter documentation should be indented
+ * @param flagsInUsage whether we should use flags instead of options in the usage
+ * @return a help message
+ */
+ QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true);
+
+ /**
+ * @brief generate a short usage message
+ * @param progName the program name to use in the usage message
+ * @param useFlags whether we should use flags instead of options
+ * @return a usage message
+ */
+ QString compileUsage(QString progName, bool useFlags = true);
+
+ /**
+ * @brief parse
+ * @param argv a QStringList containing the program ARGV
+ * @return a QHash mapping argument names to their values
+ */
+ QHash<QString, QVariant> parse(QStringList argv);
+
+ /**
+ * @brief clear all definitions
+ */
+ void clear();
+
+ ~Parser();
private:
- FlagStyle::Enum m_flagStyle;
- ArgumentStyle::Enum m_argStyle;
-
- enum OptionType
- {
- otSwitch,
- otOption
- };
-
- // Important: the common part MUST BE COMMON ON ALL THREE structs
- struct CommonDef
- {
- QString name;
- QString doc;
- QString metavar;
- QVariant def;
- };
-
- struct OptionDef
- {
- // common
- QString name;
- QString doc;
- QString metavar;
- QVariant def;
- // option
- OptionType type;
- QChar flag;
- };
-
- struct PositionalDef
- {
- // common
- QString name;
- QString doc;
- QString metavar;
- QVariant def;
- // positional
- bool required;
- };
-
- QHash<QString, OptionDef *> m_options;
- QHash<QChar, OptionDef *> m_flags;
- QHash<QString, CommonDef *> m_params;
- QList<PositionalDef *> m_positionals;
- QList<OptionDef *> m_optionList;
-
- void getPrefix(QString &opt, QString &flag);
+ FlagStyle::Enum m_flagStyle;
+ ArgumentStyle::Enum m_argStyle;
+
+ enum OptionType
+ {
+ otSwitch,
+ otOption
+ };
+
+ // Important: the common part MUST BE COMMON ON ALL THREE structs
+ struct CommonDef
+ {
+ QString name;
+ QString doc;
+ QString metavar;
+ QVariant def;
+ };
+
+ struct OptionDef
+ {
+ // common
+ QString name;
+ QString doc;
+ QString metavar;
+ QVariant def;
+ // option
+ OptionType type;
+ QChar flag;
+ };
+
+ struct PositionalDef
+ {
+ // common
+ QString name;
+ QString doc;
+ QString metavar;
+ QVariant def;
+ // positional
+ bool required;
+ };
+
+ QHash<QString, OptionDef *> m_options;
+ QHash<QChar, OptionDef *> m_flags;
+ QHash<QString, CommonDef *> m_params;
+ QList<PositionalDef *> m_positionals;
+ QList<OptionDef *> m_optionList;
+
+ void getPrefix(QString &opt, QString &flag);
};
}
diff --git a/api/logic/DefaultVariable.h b/api/logic/DefaultVariable.h
index 38d7ecc2..5c069bd3 100644
--- a/api/logic/DefaultVariable.h
+++ b/api/logic/DefaultVariable.h
@@ -4,32 +4,32 @@ template <typename T>
class DefaultVariable
{
public:
- DefaultVariable(const T & value)
- {
- defaultValue = value;
- }
- DefaultVariable<T> & operator =(const T & value)
- {
- currentValue = value;
- is_default = currentValue == defaultValue;
- is_explicit = true;
- return *this;
- }
- operator const T &() const
- {
- return is_default ? defaultValue : currentValue;
- }
- bool isDefault() const
- {
- return is_default;
- }
- bool isExplicit() const
- {
- return is_explicit;
- }
+ DefaultVariable(const T & value)
+ {
+ defaultValue = value;
+ }
+ DefaultVariable<T> & operator =(const T & value)
+ {
+ currentValue = value;
+ is_default = currentValue == defaultValue;
+ is_explicit = true;
+ return *this;
+ }
+ operator const T &() const
+ {
+ return is_default ? defaultValue : currentValue;
+ }
+ bool isDefault() const
+ {
+ return is_default;
+ }
+ bool isExplicit() const
+ {
+ return is_explicit;
+ }
private:
- T currentValue;
- T defaultValue;
- bool is_default = true;
- bool is_explicit = false;
+ T currentValue;
+ T defaultValue;
+ bool is_default = true;
+ bool is_explicit = false;
};
diff --git a/api/logic/Env.cpp b/api/logic/Env.cpp
index 04a5ab23..77546bbc 100644
--- a/api/logic/Env.cpp
+++ b/api/logic/Env.cpp
@@ -15,11 +15,12 @@
struct Env::Private
{
- QNetworkAccessManager m_qnam;
- shared_qobject_ptr<HttpMetaCache> m_metacache;
- std::shared_ptr<IIconList> m_iconlist;
- shared_qobject_ptr<Meta::Index> m_metadataIndex;
- QString m_jarsPath;
+ QNetworkAccessManager m_qnam;
+ shared_qobject_ptr<HttpMetaCache> m_metacache;
+ std::shared_ptr<IIconList> m_iconlist;
+ shared_qobject_ptr<Meta::Index> m_metadataIndex;
+ QString m_jarsPath;
+ QSet<QString> m_features;
};
static Env * instance;
@@ -30,152 +31,179 @@ static Env * instance;
Env::Env()
{
- d = new Private();
+ d = new Private();
}
Env::~Env()
{
- delete d;
+ delete d;
}
Env& Env::Env::getInstance()
{
- if(!instance)
- {
- instance = new Env();
- }
- return *instance;
+ if(!instance)
+ {
+ instance = new Env();
+ }
+ return *instance;
}
void Env::dispose()
{
- delete instance;
- instance = nullptr;
+ delete instance;
+ instance = nullptr;
}
shared_qobject_ptr< HttpMetaCache > Env::metacache()
{
- return d->m_metacache;
+ return d->m_metacache;
}
QNetworkAccessManager& Env::qnam() const
{
- return d->m_qnam;
+ return d->m_qnam;
}
std::shared_ptr<IIconList> Env::icons()
{
- return d->m_iconlist;
+ return d->m_iconlist;
}
void Env::registerIconList(std::shared_ptr<IIconList> iconlist)
{
- d->m_iconlist = iconlist;
+ d->m_iconlist = iconlist;
}
shared_qobject_ptr<Meta::Index> Env::metadataIndex()
{
- if (!d->m_metadataIndex)
- {
- d->m_metadataIndex.reset(new Meta::Index());
- }
- return d->m_metadataIndex;
+ if (!d->m_metadataIndex)
+ {
+ d->m_metadataIndex.reset(new Meta::Index());
+ }
+ return d->m_metadataIndex;
}
void Env::initHttpMetaCache()
{
- auto &m_metacache = d->m_metacache;
- m_metacache.reset(new HttpMetaCache("metacache"));
- m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
- m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
- m_metacache->addBase("versions", QDir("versions").absolutePath());
- m_metacache->addBase("libraries", QDir("libraries").absolutePath());
- m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
- m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
- m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
- m_metacache->addBase("general", QDir("cache").absolutePath());
- m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
- m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
- m_metacache->addBase("root", QDir::currentPath());
- m_metacache->addBase("translations", QDir("translations").absolutePath());
- m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
- m_metacache->addBase("meta", QDir("meta").absolutePath());
- m_metacache->Load();
+ auto &m_metacache = d->m_metacache;
+ m_metacache.reset(new HttpMetaCache("metacache"));
+ m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
+ m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
+ m_metacache->addBase("versions", QDir("versions").absolutePath());
+ m_metacache->addBase("libraries", QDir("libraries").absolutePath());
+ m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
+ m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
+ m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
+ m_metacache->addBase("general", QDir("cache").absolutePath());
+ m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
+ m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
+ m_metacache->addBase("root", QDir::currentPath());
+ m_metacache->addBase("translations", QDir("translations").absolutePath());
+ m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
+ m_metacache->addBase("meta", QDir("meta").absolutePath());
+ m_metacache->Load();
}
void Env::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password)
{
- // Set the application proxy settings.
- if (proxyTypeStr == "SOCKS5")
- {
- QNetworkProxy::setApplicationProxy(
- QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password));
- }
- else if (proxyTypeStr == "HTTP")
- {
- QNetworkProxy::setApplicationProxy(
- QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password));
- }
- else if (proxyTypeStr == "None")
- {
- // If we have no proxy set, set no proxy and return.
- QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
- }
- else
- {
- // If we have "Default" selected, set Qt to use the system proxy settings.
- QNetworkProxyFactory::setUseSystemConfiguration(true);
- }
-
- qDebug() << "Detecting proxy settings...";
- QNetworkProxy proxy = QNetworkProxy::applicationProxy();
- d->m_qnam.setProxy(proxy);
- QString proxyDesc;
- if (proxy.type() == QNetworkProxy::NoProxy)
- {
- qDebug() << "Using no proxy is an option!";
- return;
- }
- switch (proxy.type())
- {
- case QNetworkProxy::DefaultProxy:
- proxyDesc = "Default proxy: ";
- break;
- case QNetworkProxy::Socks5Proxy:
- proxyDesc = "Socks5 proxy: ";
- break;
- case QNetworkProxy::HttpProxy:
- proxyDesc = "HTTP proxy: ";
- break;
- case QNetworkProxy::HttpCachingProxy:
- proxyDesc = "HTTP caching: ";
- break;
- case QNetworkProxy::FtpCachingProxy:
- proxyDesc = "FTP caching: ";
- break;
- default:
- proxyDesc = "DERP proxy: ";
- break;
- }
- proxyDesc += QString("%3@%1:%2 pass %4")
- .arg(proxy.hostName())
- .arg(proxy.port())
- .arg(proxy.user())
- .arg(proxy.password());
- qDebug() << proxyDesc;
+ // Set the application proxy settings.
+ if (proxyTypeStr == "SOCKS5")
+ {
+ QNetworkProxy::setApplicationProxy(
+ QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password));
+ }
+ else if (proxyTypeStr == "HTTP")
+ {
+ QNetworkProxy::setApplicationProxy(
+ QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password));
+ }
+ else if (proxyTypeStr == "None")
+ {
+ // If we have no proxy set, set no proxy and return.
+ QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
+ }
+ else
+ {
+ // If we have "Default" selected, set Qt to use the system proxy settings.
+ QNetworkProxyFactory::setUseSystemConfiguration(true);
+ }
+
+ qDebug() << "Detecting proxy settings...";
+ QNetworkProxy proxy = QNetworkProxy::applicationProxy();
+ d->m_qnam.setProxy(proxy);
+ QString proxyDesc;
+ if (proxy.type() == QNetworkProxy::NoProxy)
+ {
+ qDebug() << "Using no proxy is an option!";
+ return;
+ }
+ switch (proxy.type())
+ {
+ case QNetworkProxy::DefaultProxy:
+ proxyDesc = "Default proxy: ";
+ break;
+ case QNetworkProxy::Socks5Proxy:
+ proxyDesc = "Socks5 proxy: ";
+ break;
+ case QNetworkProxy::HttpProxy:
+ proxyDesc = "HTTP proxy: ";
+ break;
+ case QNetworkProxy::HttpCachingProxy:
+ proxyDesc = "HTTP caching: ";
+ break;
+ case QNetworkProxy::FtpCachingProxy:
+ proxyDesc = "FTP caching: ";
+ break;
+ default:
+ proxyDesc = "DERP proxy: ";
+ break;
+ }
+ proxyDesc += QString("%3@%1:%2 pass %4")
+ .arg(proxy.hostName())
+ .arg(proxy.port())
+ .arg(proxy.user())
+ .arg(proxy.password());
+ qDebug() << proxyDesc;
}
QString Env::getJarsPath()
{
- if(d->m_jarsPath.isEmpty())
- {
- return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
- }
- return d->m_jarsPath;
+ if(d->m_jarsPath.isEmpty())
+ {
+ return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
+ }
+ return d->m_jarsPath;
}
void Env::setJarsPath(const QString& path)
{
- d->m_jarsPath = path;
+ 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;
}
diff --git a/api/logic/Env.h b/api/logic/Env.h
index 276d762d..8b9b827e 100644
--- a/api/logic/Env.h
+++ b/api/logic/Env.h
@@ -20,40 +20,46 @@ class Index;
}
#if defined(ENV)
- #undef ENV
+ #undef ENV
#endif
#define ENV (Env::getInstance())
class MULTIMC_LOGIC_EXPORT Env
{
- friend class MultiMC;
+ friend class MultiMC;
private:
- struct Private;
- Env();
- ~Env();
- static void dispose();
+ struct Private;
+ Env();
+ ~Env();
+ static void dispose();
public:
- static Env& getInstance();
+ static Env& getInstance();
- QNetworkAccessManager &qnam() const;
+ QNetworkAccessManager &qnam() const;
- shared_qobject_ptr<HttpMetaCache> metacache();
+ shared_qobject_ptr<HttpMetaCache> metacache();
- std::shared_ptr<IIconList> icons();
+ std::shared_ptr<IIconList> icons();
- /// init the cache. FIXME: possible future hook point
- void initHttpMetaCache();
+ /// init the cache. FIXME: possible future hook point
+ void initHttpMetaCache();
- /// Updates the application proxy settings from the settings object.
- void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
+ /// Updates the application proxy settings from the settings object.
+ void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
- void registerIconList(std::shared_ptr<IIconList> iconlist);
+ void registerIconList(std::shared_ptr<IIconList> iconlist);
- shared_qobject_ptr<Meta::Index> metadataIndex();
+ shared_qobject_ptr<Meta::Index> metadataIndex();
+
+ QString getJarsPath();
+ void setJarsPath(const QString & path);
+
+ bool isFeatureEnabled(const QString & featureName) const;
+ void enableFeature(const QString & featureName, bool state = true);
+ void getEnabledFeatures(QSet<QString> & features) const;
+ void setEnabledFeatures(const QSet<QString> & features) const;
- QString getJarsPath();
- void setJarsPath(const QString & path);
protected:
- Private * d;
+ Private * d;
};
diff --git a/api/logic/Exception.h b/api/logic/Exception.h
index 30c7aa45..9400b3f8 100644
--- a/api/logic/Exception.h
+++ b/api/logic/Exception.h
@@ -11,24 +11,24 @@
class MULTIMC_LOGIC_EXPORT Exception : public std::exception
{
public:
- Exception(const QString &message) : std::exception(), m_message(message)
- {
- qCritical() << "Exception:" << message;
- }
- Exception(const Exception &other)
- : std::exception(), m_message(other.cause())
- {
- }
- virtual ~Exception() noexcept {}
- const char *what() const noexcept
- {
- return m_message.toLatin1().constData();
- }
- QString cause() const
- {
- return m_message;
- }
+ Exception(const QString &message) : std::exception(), m_message(message)
+ {
+ qCritical() << "Exception:" << message;
+ }
+ Exception(const Exception &other)
+ : std::exception(), m_message(other.cause())
+ {
+ }
+ virtual ~Exception() noexcept {}
+ const char *what() const noexcept
+ {
+ return m_message.toLatin1().constData();
+ }
+ QString cause() const
+ {
+ return m_message;
+ }
private:
- QString m_message;
+ QString m_message;
};
diff --git a/api/logic/ExponentialSeries.h b/api/logic/ExponentialSeries.h
new file mode 100644
index 00000000..a9487f0a
--- /dev/null
+++ b/api/logic/ExponentialSeries.h
@@ -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;
+};
diff --git a/api/logic/FileSystem.cpp b/api/logic/FileSystem.cpp
index 4b47f415..49af2927 100644
--- a/api/logic/FileSystem.cpp
+++ b/api/logic/FileSystem.cpp
@@ -12,262 +12,267 @@
#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>
+ #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>
+ #include <utime.h>
#endif
namespace FS {
void ensureExists(const QDir &dir)
{
- if (!QDir().mkpath(dir.absolutePath()))
- {
- throw FileSystemException("Unable to create folder " + dir.dirName() + " (" +
- dir.absolutePath() + ")");
- }
+ if (!QDir().mkpath(dir.absolutePath()))
+ {
+ throw FileSystemException("Unable to create folder " + dir.dirName() + " (" +
+ dir.absolutePath() + ")");
+ }
}
void write(const QString &filename, const QByteArray &data)
{
- ensureExists(QFileInfo(filename).dir());
- QSaveFile file(filename);
- if (!file.open(QSaveFile::WriteOnly))
- {
- throw FileSystemException("Couldn't open " + filename + " for writing: " +
- file.errorString());
- }
- if (data.size() != file.write(data))
- {
- throw FileSystemException("Error writing data to " + filename + ": " +
- file.errorString());
- }
- if (!file.commit())
- {
- throw FileSystemException("Error while committing data to " + filename + ": " +
- file.errorString());
- }
+ ensureExists(QFileInfo(filename).dir());
+ QSaveFile file(filename);
+ if (!file.open(QSaveFile::WriteOnly))
+ {
+ throw FileSystemException("Couldn't open " + filename + " for writing: " +
+ file.errorString());
+ }
+ if (data.size() != file.write(data))
+ {
+ throw FileSystemException("Error writing data to " + filename + ": " +
+ file.errorString());
+ }
+ if (!file.commit())
+ {
+ throw FileSystemException("Error while committing data to " + filename + ": " +
+ file.errorString());
+ }
}
QByteArray read(const QString &filename)
{
- QFile file(filename);
- if (!file.open(QFile::ReadOnly))
- {
- throw FileSystemException("Unable to open " + filename + " for reading: " +
- file.errorString());
- }
- const qint64 size = file.size();
- QByteArray data(int(size), 0);
- const qint64 ret = file.read(data.data(), size);
- if (ret == -1 || ret != size)
- {
- throw FileSystemException("Error reading data from " + filename + ": " +
- file.errorString());
- }
- return data;
+ QFile file(filename);
+ if (!file.open(QFile::ReadOnly))
+ {
+ throw FileSystemException("Unable to open " + filename + " for reading: " +
+ file.errorString());
+ }
+ const qint64 size = file.size();
+ QByteArray data(int(size), 0);
+ const qint64 ret = file.read(data.data(), size);
+ if (ret == -1 || ret != size)
+ {
+ throw FileSystemException("Error reading data from " + filename + ": " +
+ file.errorString());
+ }
+ return data;
}
bool updateTimestamp(const QString& filename)
{
#ifdef Q_OS_WIN32
- std::wstring filename_utf_16 = filename.toStdWString();
- return (_wutime64(filename_utf_16.c_str(), nullptr) == 0);
+ std::wstring filename_utf_16 = filename.toStdWString();
+ return (_wutime64(filename_utf_16.c_str(), nullptr) == 0);
#else
- QByteArray filenameBA = QFile::encodeName(filename);
- return (utime(filenameBA.data(), nullptr) == 0);
+ QByteArray filenameBA = QFile::encodeName(filename);
+ return (utime(filenameBA.data(), nullptr) == 0);
#endif
}
bool ensureFilePathExists(QString filenamepath)
{
- QFileInfo a(filenamepath);
- QDir dir;
- QString ensuredPath = a.path();
- bool success = dir.mkpath(ensuredPath);
- return success;
+ QFileInfo a(filenamepath);
+ QDir dir;
+ QString ensuredPath = a.path();
+ bool success = dir.mkpath(ensuredPath);
+ return success;
}
bool ensureFolderPathExists(QString foldernamepath)
{
- QFileInfo a(foldernamepath);
- QDir dir;
- QString ensuredPath = a.filePath();
- bool success = dir.mkpath(ensuredPath);
- return success;
+ QFileInfo a(foldernamepath);
+ QDir dir;
+ QString ensuredPath = a.filePath();
+ bool success = dir.mkpath(ensuredPath);
+ return success;
}
bool copy::operator()(const QString &offset)
{
- //NOTE always deep copy on windows. the alternatives are too messy.
- #if defined Q_OS_WIN32
- m_followSymlinks = true;
- #endif
-
- auto src = PathCombine(m_src.absolutePath(), offset);
- auto dst = PathCombine(m_dst.absolutePath(), offset);
-
- QFileInfo currentSrc(src);
- if (!currentSrc.exists())
- return false;
-
- if(!m_followSymlinks && currentSrc.isSymLink())
- {
- qDebug() << "creating symlink" << src << " - " << dst;
- if (!ensureFilePathExists(dst))
- {
- qWarning() << "Cannot create path!";
- return false;
- }
- return QFile::link(currentSrc.symLinkTarget(), dst);
- }
- else if(currentSrc.isFile())
- {
- qDebug() << "copying file" << src << " - " << dst;
- if (!ensureFilePathExists(dst))
- {
- qWarning() << "Cannot create path!";
- return false;
- }
- return QFile::copy(src, dst);
- }
- else if(currentSrc.isDir())
- {
- qDebug() << "recursing" << offset;
- if (!ensureFolderPathExists(dst))
- {
- qWarning() << "Cannot create path!";
- return false;
- }
- QDir currentDir(src);
- for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System))
- {
- auto inner_offset = PathCombine(offset, f);
- // ignore and skip stuff that matches the blacklist.
- if(m_blacklist && m_blacklist->matches(inner_offset))
- {
- continue;
- }
- if(!operator()(inner_offset))
- {
- qWarning() << "Failed to copy" << inner_offset;
- return false;
- }
- }
- }
- else
- {
- qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
- return false;
- }
- return true;
+ //NOTE always deep copy on windows. the alternatives are too messy.
+ #if defined Q_OS_WIN32
+ m_followSymlinks = true;
+ #endif
+
+ auto src = PathCombine(m_src.absolutePath(), offset);
+ auto dst = PathCombine(m_dst.absolutePath(), offset);
+
+ QFileInfo currentSrc(src);
+ if (!currentSrc.exists())
+ return false;
+
+ if(!m_followSymlinks && currentSrc.isSymLink())
+ {
+ qDebug() << "creating symlink" << src << " - " << dst;
+ if (!ensureFilePathExists(dst))
+ {
+ qWarning() << "Cannot create path!";
+ return false;
+ }
+ return QFile::link(currentSrc.symLinkTarget(), dst);
+ }
+ else if(currentSrc.isFile())
+ {
+ qDebug() << "copying file" << src << " - " << dst;
+ if (!ensureFilePathExists(dst))
+ {
+ qWarning() << "Cannot create path!";
+ return false;
+ }
+ return QFile::copy(src, dst);
+ }
+ else if(currentSrc.isDir())
+ {
+ qDebug() << "recursing" << offset;
+ if (!ensureFolderPathExists(dst))
+ {
+ qWarning() << "Cannot create path!";
+ return false;
+ }
+ QDir currentDir(src);
+ for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System))
+ {
+ auto inner_offset = PathCombine(offset, f);
+ // ignore and skip stuff that matches the blacklist.
+ if(m_blacklist && m_blacklist->matches(inner_offset))
+ {
+ continue;
+ }
+ if(!operator()(inner_offset))
+ {
+ qWarning() << "Failed to copy" << inner_offset;
+ return false;
+ }
+ }
+ }
+ else
+ {
+ qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
+ return false;
+ }
+ return true;
}
bool deletePath(QString path)
{
- bool OK = true;
- QDir dir(path);
-
- if (!dir.exists())
- {
- return OK;
- }
- auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
- QDir::AllDirs | QDir::Files,
- QDir::DirsFirst);
-
- for(auto & info: allEntries)
- {
+ bool OK = true;
+ QFileInfo finfo(path);
+ if(finfo.isFile()) {
+ return QFile::remove(path);
+ }
+
+ QDir dir(path);
+
+ if (!dir.exists())
+ {
+ return OK;
+ }
+ auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
+ QDir::AllDirs | QDir::Files,
+ QDir::DirsFirst);
+
+ for(auto & info: allEntries)
+ {
#if defined Q_OS_WIN32
- QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath());
- auto wString = nativePath.toStdWString();
- DWORD dwAttrs = GetFileAttributesW(wString.c_str());
- // Windows: check for junctions, reparse points and other nasty things of that sort
- if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT)
- {
- if (info.isFile())
- {
- OK &= QFile::remove(info.absoluteFilePath());
- }
- else if (info.isDir())
- {
- OK &= dir.rmdir(info.absoluteFilePath());
- }
- }
+ QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath());
+ auto wString = nativePath.toStdWString();
+ DWORD dwAttrs = GetFileAttributesW(wString.c_str());
+ // Windows: check for junctions, reparse points and other nasty things of that sort
+ if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ if (info.isFile())
+ {
+ OK &= QFile::remove(info.absoluteFilePath());
+ }
+ else if (info.isDir())
+ {
+ OK &= dir.rmdir(info.absoluteFilePath());
+ }
+ }
#else
- // We do not trust Qt with reparse points, but do trust it with unix symlinks.
- if(info.isSymLink())
- {
- OK &= QFile::remove(info.absoluteFilePath());
- }
+ // We do not trust Qt with reparse points, but do trust it with unix symlinks.
+ if(info.isSymLink())
+ {
+ OK &= QFile::remove(info.absoluteFilePath());
+ }
#endif
- else if (info.isDir())
- {
- OK &= deletePath(info.absoluteFilePath());
- }
- else if (info.isFile())
- {
- OK &= QFile::remove(info.absoluteFilePath());
- }
- else
- {
- OK = false;
- qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath();
- }
- }
- OK &= dir.rmdir(dir.absolutePath());
- return OK;
+ else if (info.isDir())
+ {
+ OK &= deletePath(info.absoluteFilePath());
+ }
+ else if (info.isFile())
+ {
+ OK &= QFile::remove(info.absoluteFilePath());
+ }
+ else
+ {
+ OK = false;
+ qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath();
+ }
+ }
+ OK &= dir.rmdir(dir.absolutePath());
+ return OK;
}
QString PathCombine(const QString & path1, const QString & path2)
{
- if(!path1.size())
- return path2;
- if(!path2.size())
- return path1;
+ if(!path1.size())
+ return path2;
+ if(!path2.size())
+ return path1;
return QDir::cleanPath(path1 + QDir::separator() + path2);
}
QString PathCombine(const QString & path1, const QString & path2, const QString & path3)
{
- return PathCombine(PathCombine(path1, path2), path3);
+ return PathCombine(PathCombine(path1, path2), path3);
}
QString PathCombine(const QString & path1, const QString & path2, const QString & path3, const QString & path4)
{
- return PathCombine(PathCombine(path1, path2, path3), path4);
+ return PathCombine(PathCombine(path1, path2, path3), path4);
}
QString AbsolutePath(QString path)
{
- return QFileInfo(path).absolutePath();
+ return QFileInfo(path).absolutePath();
}
QString ResolveExecutable(QString path)
{
- if (path.isEmpty())
- {
- return QString();
- }
- if(!path.contains('/'))
- {
- path = QStandardPaths::findExecutable(path);
- }
- QFileInfo pathInfo(path);
- if(!pathInfo.exists() || !pathInfo.isExecutable())
- {
- return QString();
- }
- return pathInfo.absoluteFilePath();
+ if (path.isEmpty())
+ {
+ return QString();
+ }
+ if(!path.contains('/'))
+ {
+ path = QStandardPaths::findExecutable(path);
+ }
+ QFileInfo pathInfo(path);
+ if(!pathInfo.exists() || !pathInfo.isExecutable())
+ {
+ return QString();
+ }
+ return pathInfo.absoluteFilePath();
}
/**
@@ -278,66 +283,66 @@ QString ResolveExecutable(QString path)
*/
QString NormalizePath(QString path)
{
- QDir a = QDir::currentPath();
- QString currentAbsolute = a.absolutePath();
-
- QDir b(path);
- QString newAbsolute = b.absolutePath();
-
- if (newAbsolute.startsWith(currentAbsolute))
- {
- return a.relativeFilePath(newAbsolute);
- }
- else
- {
- return newAbsolute;
- }
+ QDir a = QDir::currentPath();
+ QString currentAbsolute = a.absolutePath();
+
+ QDir b(path);
+ QString newAbsolute = b.absolutePath();
+
+ if (newAbsolute.startsWith(currentAbsolute))
+ {
+ return a.relativeFilePath(newAbsolute);
+ }
+ else
+ {
+ return newAbsolute;
+ }
}
-QString badFilenameChars = "\"\\/?<>:*|!";
+QString badFilenameChars = "\"\\/?<>:*|!+\r\n";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{
- for (int i = 0; i < string.length(); i++)
- {
- if (badFilenameChars.contains(string[i]))
- {
- string[i] = replaceWith;
- }
- }
- return string;
+ for (int i = 0; i < string.length(); i++)
+ {
+ if (badFilenameChars.contains(string[i]))
+ {
+ string[i] = replaceWith;
+ }
+ }
+ return string;
}
QString DirNameFromString(QString string, QString inDir)
{
- int num = 0;
- QString baseName = RemoveInvalidFilenameChars(string, '-');
- QString dirName;
- do
- {
- if(num == 0)
- {
- dirName = baseName;
- }
- else
- {
- dirName = baseName + QString::number(num);;
- }
-
- // If it's over 9000
- if (num > 9000)
- return "";
- num++;
- } while (QFileInfo(PathCombine(inDir, dirName)).exists());
- return dirName;
+ int num = 0;
+ QString baseName = RemoveInvalidFilenameChars(string, '-');
+ QString dirName;
+ do
+ {
+ if(num == 0)
+ {
+ dirName = baseName;
+ }
+ else
+ {
+ dirName = baseName + QString::number(num);;
+ }
+
+ // If it's over 9000
+ if (num > 9000)
+ return "";
+ num++;
+ } while (QFileInfo(PathCombine(inDir, dirName)).exists());
+ return dirName;
}
// Does the folder path contain any '!'? If yes, return true, otherwise false.
// (This is a problem for Java)
bool checkProblemticPathJava(QDir folder)
{
- QString pathfoldername = folder.absolutePath();
- return pathfoldername.contains("!", Qt::CaseInsensitive);
+ QString pathfoldername = folder.absolutePath();
+ return pathfoldername.contains("!", Qt::CaseInsensitive);
}
// Win32 crap
@@ -347,106 +352,106 @@ bool called_coinit = false;
HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
{
- HRESULT hres;
-
- if (!called_coinit)
- {
- hres = CoInitialize(NULL);
- called_coinit = true;
-
- if (!SUCCEEDED(hres))
- {
- qWarning("Failed to initialize COM. Error 0x%08X", hres);
- return hres;
- }
- }
-
- IShellLink *link;
- hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
- (LPVOID *)&link);
-
- if (SUCCEEDED(hres))
- {
- IPersistFile *persistFile;
-
- link->SetPath(targetPath);
- link->SetArguments(args);
-
- hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
- if (SUCCEEDED(hres))
- {
- WCHAR wstr[MAX_PATH];
-
- MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
-
- hres = persistFile->Save(wstr, TRUE);
- persistFile->Release();
- }
- link->Release();
- }
- return hres;
+ HRESULT hres;
+
+ if (!called_coinit)
+ {
+ hres = CoInitialize(NULL);
+ called_coinit = true;
+
+ if (!SUCCEEDED(hres))
+ {
+ qWarning("Failed to initialize COM. Error 0x%08lX", hres);
+ return hres;
+ }
+ }
+
+ IShellLink *link;
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
+ (LPVOID *)&link);
+
+ if (SUCCEEDED(hres))
+ {
+ IPersistFile *persistFile;
+
+ link->SetPath(targetPath);
+ link->SetArguments(args);
+
+ hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
+ if (SUCCEEDED(hres))
+ {
+ WCHAR wstr[MAX_PATH];
+
+ MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
+
+ hres = persistFile->Save(wstr, TRUE);
+ persistFile->Release();
+ }
+ link->Release();
+ }
+ return hres;
}
#endif
QString getDesktopDir()
{
- return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
+ return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
}
// Cross-platform Shortcut creation
bool createShortCut(QString location, QString dest, QStringList args, QString name,
- QString icon)
+ QString icon)
{
#if defined Q_OS_LINUX
- location = PathCombine(location, name + ".desktop");
+ location = PathCombine(location, name + ".desktop");
- QFile f(location);
- f.open(QIODevice::WriteOnly | QIODevice::Text);
- QTextStream stream(&f);
+ QFile f(location);
+ f.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream stream(&f);
- QString argstring;
- if (!args.empty())
- argstring = " '" + args.join("' '") + "'";
+ QString argstring;
+ if (!args.empty())
+ argstring = " '" + args.join("' '") + "'";
- stream << "[Desktop Entry]"
- << "\n";
- stream << "Type=Application"
- << "\n";
- stream << "TryExec=" << dest.toLocal8Bit() << "\n";
- stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
- stream << "Name=" << name.toLocal8Bit() << "\n";
- stream << "Icon=" << icon.toLocal8Bit() << "\n";
+ stream << "[Desktop Entry]"
+ << "\n";
+ stream << "Type=Application"
+ << "\n";
+ stream << "TryExec=" << dest.toLocal8Bit() << "\n";
+ stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
+ stream << "Name=" << name.toLocal8Bit() << "\n";
+ stream << "Icon=" << icon.toLocal8Bit() << "\n";
- stream.flush();
- f.close();
+ stream.flush();
+ f.close();
- f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
- QFileDevice::ExeOther);
+ f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
+ QFileDevice::ExeOther);
- return true;
+ return true;
#elif defined Q_OS_WIN
- // TODO: Fix
- // QFile file(PathCombine(location, name + ".lnk"));
- // WCHAR *file_w;
- // WCHAR *dest_w;
- // WCHAR *args_w;
- // file.fileName().toWCharArray(file_w);
- // dest.toWCharArray(dest_w);
-
- // QString argStr;
- // for (int i = 0; i < args.count(); i++)
- // {
- // argStr.append(args[i]);
- // argStr.append(" ");
- // }
- // argStr.toWCharArray(args_w);
-
- // return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
- return false;
+ // TODO: Fix
+ // QFile file(PathCombine(location, name + ".lnk"));
+ // WCHAR *file_w;
+ // WCHAR *dest_w;
+ // WCHAR *args_w;
+ // file.fileName().toWCharArray(file_w);
+ // dest.toWCharArray(dest_w);
+
+ // QString argStr;
+ // for (int i = 0; i < args.count(); i++)
+ // {
+ // argStr.append(args[i]);
+ // argStr.append(" ");
+ // }
+ // argStr.toWCharArray(args_w);
+
+ // return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
+ return false;
#else
- qWarning("Desktop Shortcuts not supported on your platform!");
- return false;
+ qWarning("Desktop Shortcuts not supported on your platform!");
+ return false;
#endif
}
}
diff --git a/api/logic/FileSystem.h b/api/logic/FileSystem.h
index de8774ff..55ec6a58 100644
--- a/api/logic/FileSystem.h
+++ b/api/logic/FileSystem.h
@@ -15,7 +15,7 @@ namespace FS
class MULTIMC_LOGIC_EXPORT FileSystemException : public ::Exception
{
public:
- FileSystemException(const QString &message) : Exception(message) {}
+ FileSystemException(const QString &message) : Exception(message) {}
};
/**
@@ -48,34 +48,34 @@ MULTIMC_LOGIC_EXPORT bool ensureFolderPathExists(QString filenamepath);
class MULTIMC_LOGIC_EXPORT copy
{
public:
- copy(const QString & src, const QString & dst)
- {
- m_src = src;
- m_dst = dst;
- }
- copy & followSymlinks(const bool follow)
- {
- m_followSymlinks = follow;
- return *this;
- }
- copy & blacklist(const IPathMatcher * filter)
- {
- m_blacklist = filter;
- return *this;
- }
- bool operator()()
- {
- return operator()(QString());
- }
+ copy(const QString & src, const QString & dst)
+ {
+ m_src = src;
+ m_dst = dst;
+ }
+ copy & followSymlinks(const bool follow)
+ {
+ m_followSymlinks = follow;
+ return *this;
+ }
+ copy & blacklist(const IPathMatcher * filter)
+ {
+ m_blacklist = filter;
+ return *this;
+ }
+ bool operator()()
+ {
+ return operator()(QString());
+ }
private:
- bool operator()(const QString &offset);
+ bool operator()(const QString &offset);
private:
- bool m_followSymlinks = true;
- const IPathMatcher * m_blacklist = nullptr;
- QDir m_src;
- QDir m_dst;
+ bool m_followSymlinks = true;
+ const IPathMatcher * m_blacklist = nullptr;
+ QDir m_src;
+ QDir m_dst;
};
/**
diff --git a/api/logic/FileSystem_test.cpp b/api/logic/FileSystem_test.cpp
index d5e1eedb..df653ea1 100644
--- a/api/logic/FileSystem_test.cpp
+++ b/api/logic/FileSystem_test.cpp
@@ -7,155 +7,155 @@
class FileSystemTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
- const QString bothSlash = "/foo/";
- const QString trailingSlash = "foo/";
- const QString leadingSlash = "/foo";
+ const QString bothSlash = "/foo/";
+ const QString trailingSlash = "foo/";
+ const QString leadingSlash = "/foo";
private
slots:
- void test_pathCombine()
- {
- QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash));
- QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash));
- QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash));
-
- QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash));
- QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash));
- QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash));
- }
-
- void test_PathCombine1_data()
- {
- QTest::addColumn<QString>("result");
- QTest::addColumn<QString>("path1");
- QTest::addColumn<QString>("path2");
-
- QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
- QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
+ void test_pathCombine()
+ {
+ QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash));
+ QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash));
+ QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash));
+
+ QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash));
+ QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash));
+ QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash));
+ }
+
+ void test_PathCombine1_data()
+ {
+ QTest::addColumn<QString>("result");
+ QTest::addColumn<QString>("path1");
+ QTest::addColumn<QString>("path2");
+
+ QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
+ QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
#if defined(Q_OS_WIN)
- QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
- QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
- QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
+ QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
+ QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
+ QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
#endif
- }
-
- void test_PathCombine1()
- {
- QFETCH(QString, result);
- QFETCH(QString, path1);
- QFETCH(QString, path2);
-
- QCOMPARE(FS::PathCombine(path1, path2), result);
- }
-
- void test_PathCombine2_data()
- {
- QTest::addColumn<QString>("result");
- QTest::addColumn<QString>("path1");
- QTest::addColumn<QString>("path2");
- QTest::addColumn<QString>("path3");
-
- QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
- QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
- QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
- QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
+ }
+
+ void test_PathCombine1()
+ {
+ QFETCH(QString, result);
+ QFETCH(QString, path1);
+ QFETCH(QString, path2);
+
+ QCOMPARE(FS::PathCombine(path1, path2), result);
+ }
+
+ void test_PathCombine2_data()
+ {
+ QTest::addColumn<QString>("result");
+ QTest::addColumn<QString>("path1");
+ QTest::addColumn<QString>("path2");
+ QTest::addColumn<QString>("path3");
+
+ QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
+ QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
+ QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
+ QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
#if defined(Q_OS_WIN)
- QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
- QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
- QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
- QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
+ QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
+ QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
+ QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
+ QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
#endif
- }
-
- void test_PathCombine2()
- {
- QFETCH(QString, result);
- QFETCH(QString, path1);
- QFETCH(QString, path2);
- QFETCH(QString, path3);
-
- QCOMPARE(FS::PathCombine(path1, path2, path3), result);
- }
-
- void test_copy()
- {
- QString folder = QFINDTESTDATA("data/test_folder");
- auto f = [&folder]()
- {
- QTemporaryDir tempDir;
- tempDir.setAutoRemove(true);
- qDebug() << "From:" << folder << "To:" << tempDir.path();
-
- QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
- qDebug() << tempDir.path();
- qDebug() << target_dir.path();
- FS::copy c(folder, target_dir.path());
- c();
-
- for(auto entry: target_dir.entryList())
- {
- qDebug() << entry;
- }
- QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
- QVERIFY(target_dir.entryList().contains("assets"));
- };
-
- // first try variant without trailing /
- QVERIFY(!folder.endsWith('/'));
- f();
-
- // then variant with trailing /
- folder.append('/');
- QVERIFY(folder.endsWith('/'));
- f();
- }
-
- void test_getDesktop()
- {
- QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
- }
+ }
+
+ void test_PathCombine2()
+ {
+ QFETCH(QString, result);
+ QFETCH(QString, path1);
+ QFETCH(QString, path2);
+ QFETCH(QString, path3);
+
+ QCOMPARE(FS::PathCombine(path1, path2, path3), result);
+ }
+
+ void test_copy()
+ {
+ QString folder = QFINDTESTDATA("data/test_folder");
+ auto f = [&folder]()
+ {
+ QTemporaryDir tempDir;
+ tempDir.setAutoRemove(true);
+ qDebug() << "From:" << folder << "To:" << tempDir.path();
+
+ QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
+ qDebug() << tempDir.path();
+ qDebug() << target_dir.path();
+ FS::copy c(folder, target_dir.path());
+ c();
+
+ for(auto entry: target_dir.entryList())
+ {
+ qDebug() << entry;
+ }
+ QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
+ QVERIFY(target_dir.entryList().contains("assets"));
+ };
+
+ // first try variant without trailing /
+ QVERIFY(!folder.endsWith('/'));
+ f();
+
+ // then variant with trailing /
+ folder.append('/');
+ QVERIFY(folder.endsWith('/'));
+ f();
+ }
+
+ void test_getDesktop()
+ {
+ QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
+ }
// this is only valid on linux
// FIXME: implement on windows, OSX, then test.
#if defined(Q_OS_LINUX)
- void test_createShortcut_data()
- {
- QTest::addColumn<QString>("location");
- QTest::addColumn<QString>("dest");
- QTest::addColumn<QStringList>("args");
- QTest::addColumn<QString>("name");
- QTest::addColumn<QString>("iconLocation");
- QTest::addColumn<QByteArray>("result");
-
- QTest::newRow("unix") << QDir::currentPath()
- << "asdfDest"
- << (QStringList() << "arg1" << "arg2")
- << "asdf"
- << QString()
- #if defined(Q_OS_LINUX)
- << MULTIMC_GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
- #elif defined(Q_OS_WIN)
- << QByteArray()
- #endif
- ;
- }
-
- void test_createShortcut()
- {
- QFETCH(QString, location);
- QFETCH(QString, dest);
- QFETCH(QStringList, args);
- QFETCH(QString, name);
- QFETCH(QString, iconLocation);
- QFETCH(QByteArray, result);
-
- QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
- QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
-
- //QDir().remove(location);
- }
+ void test_createShortcut_data()
+ {
+ QTest::addColumn<QString>("location");
+ QTest::addColumn<QString>("dest");
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<QString>("iconLocation");
+ QTest::addColumn<QByteArray>("result");
+
+ QTest::newRow("unix") << QDir::currentPath()
+ << "asdfDest"
+ << (QStringList() << "arg1" << "arg2")
+ << "asdf"
+ << QString()
+ #if defined(Q_OS_LINUX)
+ << MULTIMC_GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
+ #elif defined(Q_OS_WIN)
+ << QByteArray()
+ #endif
+ ;
+ }
+
+ void test_createShortcut()
+ {
+ QFETCH(QString, location);
+ QFETCH(QString, dest);
+ QFETCH(QStringList, args);
+ QFETCH(QString, name);
+ QFETCH(QString, iconLocation);
+ QFETCH(QByteArray, result);
+
+ QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
+ QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
+
+ //QDir().remove(location);
+ }
#endif
};
diff --git a/api/logic/Filter.cpp b/api/logic/Filter.cpp
index 7f6667ae..c65ca0ce 100644
--- a/api/logic/Filter.cpp
+++ b/api/logic/Filter.cpp
@@ -6,26 +6,26 @@ ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){}
ContainsFilter::~ContainsFilter(){}
bool ContainsFilter::accepts(const QString& value)
{
- return value.contains(pattern);
+ return value.contains(pattern);
}
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){}
ExactFilter::~ExactFilter(){}
bool ExactFilter::accepts(const QString& value)
{
- return value.contains(pattern);
+ return value == pattern;
}
RegexpFilter::RegexpFilter(const QString& regexp, bool invert)
- :invert(invert)
+ :invert(invert)
{
- pattern.setPattern(regexp);
- pattern.optimize();
+ 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);
+ auto match = pattern.match(value);
+ bool matched = match.hasMatch();
+ return invert ? (!matched) : (matched);
}
diff --git a/api/logic/Filter.h b/api/logic/Filter.h
index 8de7d8f9..1ba48e48 100644
--- a/api/logic/Filter.h
+++ b/api/logic/Filter.h
@@ -8,37 +8,37 @@
class MULTIMC_LOGIC_EXPORT Filter
{
public:
- virtual ~Filter();
- virtual bool accepts(const QString & value) = 0;
+ 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;
+ ContainsFilter(const QString &pattern);
+ virtual ~ContainsFilter();
+ bool accepts(const QString & value) override;
private:
- QString pattern;
+ QString pattern;
};
class MULTIMC_LOGIC_EXPORT ExactFilter: public Filter
{
public:
- ExactFilter(const QString &pattern);
- virtual ~ExactFilter();
- bool accepts(const QString & value) override;
+ ExactFilter(const QString &pattern);
+ virtual ~ExactFilter();
+ bool accepts(const QString & value) override;
private:
- QString pattern;
+ QString pattern;
};
class MULTIMC_LOGIC_EXPORT RegexpFilter: public Filter
{
public:
- RegexpFilter(const QString &regexp, bool invert);
- virtual ~RegexpFilter();
- bool accepts(const QString & value) override;
+ RegexpFilter(const QString &regexp, bool invert);
+ virtual ~RegexpFilter();
+ bool accepts(const QString & value) override;
private:
- QRegularExpression pattern;
- bool invert = false;
+ QRegularExpression pattern;
+ bool invert = false;
};
diff --git a/api/logic/FolderInstanceProvider.cpp b/api/logic/FolderInstanceProvider.cpp
deleted file mode 100644
index 69ba6c82..00000000
--- a/api/logic/FolderInstanceProvider.cpp
+++ /dev/null
@@ -1,462 +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 | 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;
- }
- 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 "InstanceTask.h"
-Task * FolderInstanceProvider::wrapInstanceTask(InstanceTask * task)
-{
- auto stagingPath = getStagedInstancePath();
- task->setStagingPath(stagingPath);
- task->setParentSettings(m_globalSettings);
- return new FolderInstanceStaging(this, task, stagingPath, task->name(), task->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"
diff --git a/api/logic/FolderInstanceProvider.h b/api/logic/FolderInstanceProvider.h
deleted file mode 100644
index e13dcfe9..00000000
--- a/api/logic/FolderInstanceProvider.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#pragma once
-
-#include "BaseInstanceProvider.h"
-#include <QMap>
-
-class QFileSystemWatcher;
-class InstanceTask;
-
-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);
-
- //create FtbInstance
- Task * ftbCreationTask(FtbPackDownloader *downloader, const QString &instName, const QString &instGroup, const QString &instIcon);
-
- // migrate an instance to the current format
- Task * legacyUpgradeTask(const InstancePtr& oldInstance);
-*/
-
- // 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() 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;
-};
diff --git a/api/logic/GZip.cpp b/api/logic/GZip.cpp
index 38605df6..0368c32d 100644
--- a/api/logic/GZip.cpp
+++ b/api/logic/GZip.cpp
@@ -4,112 +4,112 @@
bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes)
{
- if (compressedBytes.size() == 0)
- {
- uncompressedBytes = compressedBytes;
- return true;
- }
-
- unsigned uncompLength = compressedBytes.size();
- uncompressedBytes.clear();
- uncompressedBytes.resize(uncompLength);
-
- z_stream strm;
- memset(&strm, 0, sizeof(strm));
- strm.next_in = (Bytef *)compressedBytes.data();
- strm.avail_in = compressedBytes.size();
-
- bool done = false;
-
- if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK)
- {
- return false;
- }
-
- int err = Z_OK;
-
- while (!done)
- {
- // If our output buffer is too small
- if (strm.total_out >= uncompLength)
- {
- uncompressedBytes.resize(uncompLength * 2);
- uncompLength *= 2;
- }
-
- strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
- strm.avail_out = uncompLength - strm.total_out;
-
- // Inflate another chunk.
- err = inflate(&strm, Z_SYNC_FLUSH);
- if (err == Z_STREAM_END)
- done = true;
- else if (err != Z_OK)
- {
- break;
- }
- }
-
- if (inflateEnd(&strm) != Z_OK || !done)
- {
- return false;
- }
-
- uncompressedBytes.resize(strm.total_out);
- return true;
+ if (compressedBytes.size() == 0)
+ {
+ uncompressedBytes = compressedBytes;
+ return true;
+ }
+
+ unsigned uncompLength = compressedBytes.size();
+ uncompressedBytes.clear();
+ uncompressedBytes.resize(uncompLength);
+
+ z_stream strm;
+ memset(&strm, 0, sizeof(strm));
+ strm.next_in = (Bytef *)compressedBytes.data();
+ strm.avail_in = compressedBytes.size();
+
+ bool done = false;
+
+ if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK)
+ {
+ return false;
+ }
+
+ int err = Z_OK;
+
+ while (!done)
+ {
+ // If our output buffer is too small
+ if (strm.total_out >= uncompLength)
+ {
+ uncompressedBytes.resize(uncompLength * 2);
+ uncompLength *= 2;
+ }
+
+ strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
+ strm.avail_out = uncompLength - strm.total_out;
+
+ // Inflate another chunk.
+ err = inflate(&strm, Z_SYNC_FLUSH);
+ if (err == Z_STREAM_END)
+ done = true;
+ else if (err != Z_OK)
+ {
+ break;
+ }
+ }
+
+ if (inflateEnd(&strm) != Z_OK || !done)
+ {
+ return false;
+ }
+
+ uncompressedBytes.resize(strm.total_out);
+ return true;
}
bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
{
- if (uncompressedBytes.size() == 0)
- {
- compressedBytes = uncompressedBytes;
- return true;
- }
-
- unsigned compLength = std::min(uncompressedBytes.size(), 16);
- compressedBytes.clear();
- compressedBytes.resize(compLength);
-
- z_stream zs;
- memset(&zs, 0, sizeof(zs));
-
- if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK)
- {
- return false;
- }
-
- zs.next_in = (Bytef*)uncompressedBytes.data();
- zs.avail_in = uncompressedBytes.size();
-
- int ret;
- compressedBytes.resize(uncompressedBytes.size());
-
- unsigned offset = 0;
- unsigned temp = 0;
- do
- {
- auto remaining = compressedBytes.size() - offset;
- if(remaining < 1)
- {
- compressedBytes.resize(compressedBytes.size() * 2);
- }
- zs.next_out = (Bytef *) (compressedBytes.data() + offset);
- temp = zs.avail_out = compressedBytes.size() - offset;
- ret = deflate(&zs, Z_FINISH);
- offset += temp - zs.avail_out;
- } while (ret == Z_OK);
-
- compressedBytes.resize(offset);
-
- if (deflateEnd(&zs) != Z_OK)
- {
- return false;
- }
-
- if (ret != Z_STREAM_END)
- {
- return false;
- }
- return true;
+ if (uncompressedBytes.size() == 0)
+ {
+ compressedBytes = uncompressedBytes;
+ return true;
+ }
+
+ unsigned compLength = std::min(uncompressedBytes.size(), 16);
+ compressedBytes.clear();
+ compressedBytes.resize(compLength);
+
+ z_stream zs;
+ memset(&zs, 0, sizeof(zs));
+
+ if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK)
+ {
+ return false;
+ }
+
+ zs.next_in = (Bytef*)uncompressedBytes.data();
+ zs.avail_in = uncompressedBytes.size();
+
+ int ret;
+ compressedBytes.resize(uncompressedBytes.size());
+
+ unsigned offset = 0;
+ unsigned temp = 0;
+ do
+ {
+ auto remaining = compressedBytes.size() - offset;
+ if(remaining < 1)
+ {
+ compressedBytes.resize(compressedBytes.size() * 2);
+ }
+ zs.next_out = (Bytef *) (compressedBytes.data() + offset);
+ temp = zs.avail_out = compressedBytes.size() - offset;
+ ret = deflate(&zs, Z_FINISH);
+ offset += temp - zs.avail_out;
+ } while (ret == Z_OK);
+
+ compressedBytes.resize(offset);
+
+ if (deflateEnd(&zs) != Z_OK)
+ {
+ return false;
+ }
+
+ if (ret != Z_STREAM_END)
+ {
+ return false;
+ }
+ return true;
} \ No newline at end of file
diff --git a/api/logic/GZip.h b/api/logic/GZip.h
index 6993a222..c7eddbb3 100644
--- a/api/logic/GZip.h
+++ b/api/logic/GZip.h
@@ -6,7 +6,7 @@
class MULTIMC_LOGIC_EXPORT GZip
{
public:
- static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
- static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
+ static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
+ static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
};
diff --git a/api/logic/GZip_test.cpp b/api/logic/GZip_test.cpp
index f4c9214c..3f4d181c 100644
--- a/api/logic/GZip_test.cpp
+++ b/api/logic/GZip_test.cpp
@@ -6,50 +6,50 @@
void fib(int &prev, int &cur)
{
- auto ret = prev + cur;
- prev = cur;
- cur = ret;
+ auto ret = prev + cur;
+ prev = cur;
+ cur = ret;
}
class GZipTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
private
slots:
- void test_Through()
- {
- // test up to 10 MB
- static const int size = 10 * 1024 * 1024;
- QByteArray random;
- QByteArray compressed;
- QByteArray decompressed;
- std::default_random_engine eng((std::random_device())());
- std::uniform_int_distribution<uint8_t> idis(0, std::numeric_limits<uint8_t>::max());
-
- // initialize random buffer
- for(int i = 0; i < size; i++)
- {
- random.append((char)idis(eng));
- }
-
- // initialize fibonacci
- int prev = 1;
- int cur = 1;
-
- // test if fibonacci long random buffers pass through GZip
- do
- {
- QByteArray copy = random;
- copy.resize(cur);
- compressed.clear();
- decompressed.clear();
- QVERIFY(GZip::zip(copy, compressed));
- QVERIFY(GZip::unzip(compressed, decompressed));
- QCOMPARE(decompressed, copy);
- fib(prev, cur);
- } while (cur < size);
- }
+ void test_Through()
+ {
+ // test up to 10 MB
+ static const int size = 10 * 1024 * 1024;
+ QByteArray random;
+ QByteArray compressed;
+ QByteArray decompressed;
+ std::default_random_engine eng((std::random_device())());
+ std::uniform_int_distribution<uint8_t> idis(0, std::numeric_limits<uint8_t>::max());
+
+ // initialize random buffer
+ for(int i = 0; i < size; i++)
+ {
+ random.append((char)idis(eng));
+ }
+
+ // initialize fibonacci
+ int prev = 1;
+ int cur = 1;
+
+ // test if fibonacci long random buffers pass through GZip
+ do
+ {
+ QByteArray copy = random;
+ copy.resize(cur);
+ compressed.clear();
+ decompressed.clear();
+ QVERIFY(GZip::zip(copy, compressed));
+ QVERIFY(GZip::unzip(compressed, decompressed));
+ QCOMPARE(decompressed, copy);
+ fib(prev, cur);
+ } while (cur < size);
+ }
};
QTEST_GUILESS_MAIN(GZipTest)
diff --git a/api/logic/InstanceCopyTask.cpp b/api/logic/InstanceCopyTask.cpp
index 62c22362..a576a0fd 100644
--- a/api/logic/InstanceCopyTask.cpp
+++ b/api/logic/InstanceCopyTask.cpp
@@ -1,5 +1,4 @@
#include "InstanceCopyTask.h"
-#include "BaseInstanceProvider.h"
#include "settings/INISettingsObject.h"
#include "FileSystem.h"
#include "NullInstance.h"
@@ -8,50 +7,50 @@
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves)
{
- m_origInstance = origInstance;
-
- if(!copySaves)
- {
- // FIXME: get this from the original instance type...
- auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
- matcherReal->caseSensitive(false);
- m_matcher.reset(matcherReal);
- }
+ m_origInstance = origInstance;
+
+ if(!copySaves)
+ {
+ // FIXME: get this from the original instance type...
+ auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
+ matcherReal->caseSensitive(false);
+ m_matcher.reset(matcherReal);
+ }
}
void InstanceCopyTask::executeTask()
{
- setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
+ setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
- FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
- folderCopy.followSymlinks(false).blacklist(m_matcher.get());
+ FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
+ folderCopy.followSymlinks(false).blacklist(m_matcher.get());
- m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
- connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
- connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
- m_copyFutureWatcher.setFuture(m_copyFuture);
+ m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
+ connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
+ connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
+ m_copyFutureWatcher.setFuture(m_copyFuture);
}
void InstanceCopyTask::copyFinished()
{
- auto successful = m_copyFuture.result();
- if(!successful)
- {
- emitFailed(tr("Instance folder copy failed."));
- return;
- }
- // FIXME: shouldn't this be able to report errors?
- auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
- instanceSettings->registerSetting("InstanceType", "Legacy");
-
- InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
- inst->setName(m_instName);
- inst->setIconKey(m_instIcon);
- emitSucceeded();
+ auto successful = m_copyFuture.result();
+ if(!successful)
+ {
+ emitFailed(tr("Instance folder copy failed."));
+ return;
+ }
+ // FIXME: shouldn't this be able to report errors?
+ auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+
+ InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
+ inst->setName(m_instName);
+ inst->setIconKey(m_instIcon);
+ emitSucceeded();
}
void InstanceCopyTask::copyAborted()
{
- emitFailed(tr("Instance folder copy has been aborted."));
- return;
+ emitFailed(tr("Instance folder copy has been aborted."));
+ return;
}
diff --git a/api/logic/InstanceCopyTask.h b/api/logic/InstanceCopyTask.h
index a8dc9783..8dd55b40 100644
--- a/api/logic/InstanceCopyTask.h
+++ b/api/logic/InstanceCopyTask.h
@@ -11,23 +11,21 @@
#include "BaseInstance.h"
#include "InstanceTask.h"
-class BaseInstanceProvider;
-
class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves);
+ explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves);
protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
- void copyFinished();
- void copyAborted();
+ //! Entry point for tasks.
+ virtual void executeTask() override;
+ void copyFinished();
+ void copyAborted();
private: /* data */
- InstancePtr m_origInstance;
- QFuture<bool> m_copyFuture;
- QFutureWatcher<bool> m_copyFutureWatcher;
- std::unique_ptr<IPathMatcher> m_matcher;
+ InstancePtr m_origInstance;
+ QFuture<bool> m_copyFuture;
+ QFutureWatcher<bool> m_copyFutureWatcher;
+ std::unique_ptr<IPathMatcher> m_matcher;
};
diff --git a/api/logic/InstanceCreationTask.cpp b/api/logic/InstanceCreationTask.cpp
index 6dc2496c..978d4687 100644
--- a/api/logic/InstanceCreationTask.cpp
+++ b/api/logic/InstanceCreationTask.cpp
@@ -1,5 +1,4 @@
#include "InstanceCreationTask.h"
-#include "BaseInstanceProvider.h"
#include "settings/INISettingsObject.h"
#include "FileSystem.h"
@@ -9,25 +8,24 @@
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
{
- m_version = version;
+ m_version = version;
}
void InstanceCreationTask::executeTask()
{
- setStatus(tr("Creating instance from version %1").arg(m_version->name()));
- {
- auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
- instanceSettings->suspendSave();
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
- MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
- auto components = inst.getComponentList();
- components->buildingFromScratch();
- components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
- inst.setName(m_instName);
- inst.setIconKey(m_instIcon);
- inst.init();
- instanceSettings->resumeSave();
- }
- emitSucceeded();
+ setStatus(tr("Creating instance from version %1").arg(m_version->name()));
+ {
+ auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
+ instanceSettings->suspendSave();
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+ instanceSettings->set("InstanceType", "OneSix");
+ MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
+ auto components = inst.getComponentList();
+ components->buildingFromScratch();
+ components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
+ inst.setName(m_instName);
+ inst.setIconKey(m_instIcon);
+ instanceSettings->resumeSave();
+ }
+ emitSucceeded();
}
diff --git a/api/logic/InstanceCreationTask.h b/api/logic/InstanceCreationTask.h
index e06eacbb..154a854f 100644
--- a/api/logic/InstanceCreationTask.h
+++ b/api/logic/InstanceCreationTask.h
@@ -10,14 +10,14 @@
class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public InstanceTask
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit InstanceCreationTask(BaseVersionPtr version);
+ explicit InstanceCreationTask(BaseVersionPtr version);
protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
+ //! Entry point for tasks.
+ virtual void executeTask() override;
private: /* data */
- BaseVersionPtr m_version;
+ BaseVersionPtr m_version;
};
diff --git a/api/logic/InstanceImportTask.cpp b/api/logic/InstanceImportTask.cpp
index b5375cbc..1e93174c 100644
--- a/api/logic/InstanceImportTask.cpp
+++ b/api/logic/InstanceImportTask.cpp
@@ -1,12 +1,12 @@
#include "InstanceImportTask.h"
#include "BaseInstance.h"
-#include "BaseInstanceProvider.h"
#include "FileSystem.h"
#include "Env.h"
#include "MMCZip.h"
#include "NullInstance.h"
#include "settings/INISettingsObject.h"
#include "icons/IIconList.h"
+#include "icons/IconUtils.h"
#include <QtConcurrentRun>
// FIXME: this does not belong here, it's Minecraft/Flame specific
@@ -18,394 +18,394 @@
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
{
- m_sourceUrl = sourceUrl;
+ m_sourceUrl = sourceUrl;
}
void InstanceImportTask::executeTask()
{
- InstancePtr newInstance;
+ InstancePtr newInstance;
- if (m_sourceUrl.isLocalFile())
- {
- m_archivePath = m_sourceUrl.toLocalFile();
- processZipPack();
- }
- else
- {
- setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
- m_downloadRequired = true;
+ if (m_sourceUrl.isLocalFile())
+ {
+ m_archivePath = m_sourceUrl.toLocalFile();
+ processZipPack();
+ }
+ else
+ {
+ setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
+ m_downloadRequired = true;
- const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
- auto entry = ENV.metacache()->resolveEntry("general", path);
- entry->setStale(true);
- m_filesNetJob.reset(new NetJob(tr("Modpack download")));
- m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
- m_archivePath = entry->getFullPath();
- auto job = m_filesNetJob.get();
- connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
- connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
- connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
- m_filesNetJob->start();
- }
+ const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
+ auto entry = ENV.metacache()->resolveEntry("general", path);
+ entry->setStale(true);
+ m_filesNetJob.reset(new NetJob(tr("Modpack download")));
+ m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
+ m_archivePath = entry->getFullPath();
+ auto job = m_filesNetJob.get();
+ connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
+ connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
+ connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
+ m_filesNetJob->start();
+ }
}
void InstanceImportTask::downloadSucceeded()
{
- processZipPack();
- m_filesNetJob.reset();
+ processZipPack();
+ m_filesNetJob.reset();
}
void InstanceImportTask::downloadFailed(QString reason)
{
- emitFailed(reason);
- m_filesNetJob.reset();
+ emitFailed(reason);
+ m_filesNetJob.reset();
}
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
{
- setProgress(current / 2, total);
+ setProgress(current / 2, total);
}
void InstanceImportTask::processZipPack()
{
- setStatus(tr("Extracting modpack"));
- QDir extractDir(m_stagingPath);
- qDebug() << "Attempting to create instance from" << m_archivePath;
+ setStatus(tr("Extracting modpack"));
+ QDir extractDir(m_stagingPath);
+ qDebug() << "Attempting to create instance from" << m_archivePath;
- // open the zip and find relevant files in it
- m_packZip.reset(new QuaZip(m_archivePath));
- if (!m_packZip->open(QuaZip::mdUnzip))
- {
- emitFailed(tr("Unable to open supplied modpack zip file."));
- return;
- }
+ // open the zip and find relevant files in it
+ m_packZip.reset(new QuaZip(m_archivePath));
+ if (!m_packZip->open(QuaZip::mdUnzip))
+ {
+ emitFailed(tr("Unable to open supplied modpack zip file."));
+ return;
+ }
- QStringList blacklist = {"instance.cfg", "manifest.json"};
- QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
- QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
- QString root;
- if(!mmcFound.isNull())
- {
- // process as MultiMC instance/pack
- qDebug() << "MultiMC:" << mmcFound;
- root = mmcFound;
- m_modpackType = ModpackType::MultiMC;
- }
- else if(!flameFound.isNull())
- {
- // process as Flame pack
- qDebug() << "Flame:" << flameFound;
- root = flameFound;
- m_modpackType = ModpackType::Flame;
- }
+ QStringList blacklist = {"instance.cfg", "manifest.json"};
+ QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
+ QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
+ QString root;
+ if(!mmcFound.isNull())
+ {
+ // process as MultiMC instance/pack
+ qDebug() << "MultiMC:" << mmcFound;
+ root = mmcFound;
+ m_modpackType = ModpackType::MultiMC;
+ }
+ else if(!flameFound.isNull())
+ {
+ // process as Flame pack
+ qDebug() << "Flame:" << flameFound;
+ root = flameFound;
+ m_modpackType = ModpackType::Flame;
+ }
- if(m_modpackType == ModpackType::Unknown)
- {
- emitFailed(tr("Archive does not contain a recognized modpack type."));
- return;
- }
+ if(m_modpackType == ModpackType::Unknown)
+ {
+ emitFailed(tr("Archive does not contain a recognized modpack type."));
+ return;
+ }
- // make sure we extract just the pack
- m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
- m_extractFutureWatcher.setFuture(m_extractFuture);
+ // make sure we extract just the pack
+ m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
+ m_extractFutureWatcher.setFuture(m_extractFuture);
}
void InstanceImportTask::extractFinished()
{
- m_packZip.reset();
- if (m_extractFuture.result().isEmpty())
- {
- emitFailed(tr("Failed to extract modpack"));
- return;
- }
- QDir extractDir(m_stagingPath);
+ m_packZip.reset();
+ if (m_extractFuture.result().isEmpty())
+ {
+ emitFailed(tr("Failed to extract modpack"));
+ return;
+ }
+ QDir extractDir(m_stagingPath);
- qDebug() << "Fixing permissions for extracted pack files...";
- QDirIterator it(extractDir, QDirIterator::Subdirectories);
- while (it.hasNext())
- {
- auto filepath = it.next();
- QFileInfo file(filepath);
- auto permissions = QFile::permissions(filepath);
- auto origPermissions = permissions;
- if(file.isDir())
- {
- // Folder +rwx for current user
- permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
- }
- else
- {
- // File +rw for current user
- permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
- }
- if(origPermissions != permissions)
- {
- if(!QFile::setPermissions(filepath, permissions))
- {
- logWarning(tr("Could not fix permissions for %1").arg(filepath));
- }
- else
- {
- qDebug() << "Fixed" << filepath;
- }
- }
- }
+ qDebug() << "Fixing permissions for extracted pack files...";
+ QDirIterator it(extractDir, QDirIterator::Subdirectories);
+ while (it.hasNext())
+ {
+ auto filepath = it.next();
+ QFileInfo file(filepath);
+ auto permissions = QFile::permissions(filepath);
+ auto origPermissions = permissions;
+ if(file.isDir())
+ {
+ // Folder +rwx for current user
+ permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
+ }
+ else
+ {
+ // File +rw for current user
+ permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
+ }
+ if(origPermissions != permissions)
+ {
+ if(!QFile::setPermissions(filepath, permissions))
+ {
+ logWarning(tr("Could not fix permissions for %1").arg(filepath));
+ }
+ else
+ {
+ qDebug() << "Fixed" << filepath;
+ }
+ }
+ }
- switch(m_modpackType)
- {
- case ModpackType::Flame:
- processFlame();
- return;
- case ModpackType::MultiMC:
- processMultiMC();
- return;
- case ModpackType::Unknown:
- emitFailed(tr("Archive does not contain a recognized modpack type."));
- return;
- }
+ switch(m_modpackType)
+ {
+ case ModpackType::Flame:
+ processFlame();
+ return;
+ case ModpackType::MultiMC:
+ processMultiMC();
+ return;
+ case ModpackType::Unknown:
+ emitFailed(tr("Archive does not contain a recognized modpack type."));
+ return;
+ }
}
void InstanceImportTask::extractAborted()
{
- emitFailed(tr("Instance import has been aborted."));
- return;
+ emitFailed(tr("Instance import has been aborted."));
+ return;
}
void InstanceImportTask::processFlame()
{
- const static QMap<QString,QString> forgemap = {
- {"1.2.5", "3.4.9.171"},
- {"1.4.2", "6.0.1.355"},
- {"1.4.7", "6.6.2.534"},
- {"1.5.2", "7.8.1.737"}
- };
- Flame::Manifest pack;
- try
- {
- QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
- Flame::loadManifest(pack, configPath);
- QFile::remove(configPath);
- }
- catch (JSONValidationError & e)
- {
- emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
- return;
- }
- if(!pack.overrides.isEmpty())
- {
- QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
- if (QFile::exists(overridePath))
- {
- QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
- if (!QFile::rename(overridePath, mcPath))
- {
- emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
- return;
- }
- }
- else
- {
- logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
- }
- }
+ const static QMap<QString,QString> forgemap = {
+ {"1.2.5", "3.4.9.171"},
+ {"1.4.2", "6.0.1.355"},
+ {"1.4.7", "6.6.2.534"},
+ {"1.5.2", "7.8.1.737"}
+ };
+ Flame::Manifest pack;
+ try
+ {
+ QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
+ Flame::loadManifest(pack, configPath);
+ QFile::remove(configPath);
+ }
+ catch (const JSONValidationError &e)
+ {
+ emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
+ return;
+ }
+ if(!pack.overrides.isEmpty())
+ {
+ QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
+ if (QFile::exists(overridePath))
+ {
+ QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
+ if (!QFile::rename(overridePath, mcPath))
+ {
+ emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
+ return;
+ }
+ }
+ else
+ {
+ logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
+ }
+ }
- QString forgeVersion;
- for(auto &loader: pack.minecraft.modLoaders)
- {
- auto id = loader.id;
- if(id.startsWith("forge-"))
- {
- id.remove("forge-");
- forgeVersion = id;
- continue;
- }
- logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
- }
+ QString forgeVersion;
+ for(auto &loader: pack.minecraft.modLoaders)
+ {
+ auto id = loader.id;
+ if(id.startsWith("forge-"))
+ {
+ id.remove("forge-");
+ forgeVersion = id;
+ continue;
+ }
+ logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
+ }
- QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
- MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
- auto mcVersion = pack.minecraft.version;
- // Hack to correct some 'special sauce'...
- if(mcVersion.endsWith('.'))
- {
- mcVersion.remove(QRegExp("[.]+$"));
- logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
- }
- auto components = instance.getComponentList();
- components->buildingFromScratch();
- components->setComponentVersion("net.minecraft", mcVersion, true);
- if(!forgeVersion.isEmpty())
- {
- // FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
- if(forgeVersion == "recommended")
- {
- if(forgemap.contains(mcVersion))
- {
- forgeVersion = forgemap[mcVersion];
- }
- else
- {
- logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
- }
- }
- components->setComponentVersion("net.minecraftforge", forgeVersion);
- }
- if (m_instIcon != "default")
- {
- instance.setIconKey(m_instIcon);
- }
- else
- {
- if(pack.name.contains("Direwolf20"))
- {
- instance.setIconKey("steve");
- }
- else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
- {
- instance.setIconKey("ftb_logo");
- }
- else
- {
- // default to something other than the MultiMC default to distinguish these
- instance.setIconKey("flame");
- }
- }
- instance.init();
- QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
- QFileInfo jarmodsInfo(jarmodsPath);
- if(jarmodsInfo.isDir())
- {
- // install all the jar mods
- qDebug() << "Found jarmods:";
- QDir jarmodsDir(jarmodsPath);
- QStringList jarMods;
- for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
- {
- qDebug() << info.fileName();
- jarMods.push_back(info.absoluteFilePath());
- }
- auto profile = instance.getComponentList();
- profile->installJarMods(jarMods);
- // nuke the original files
- FS::deletePath(jarmodsPath);
- }
- instance.setName(m_instName);
- m_modIdResolver.reset(new Flame::FileResolvingTask(pack));
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
- {
- auto results = m_modIdResolver->getResults();
- m_filesNetJob.reset(new NetJob(tr("Mod download")));
- for(auto result: results.files)
- {
- QString filename = result.fileName;
- if(!result.required)
- {
- filename += ".disabled";
- }
+ QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
+ auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+ instanceSettings->set("InstanceType", "OneSix");
+ MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
+ auto mcVersion = pack.minecraft.version;
+ // Hack to correct some 'special sauce'...
+ if(mcVersion.endsWith('.'))
+ {
+ mcVersion.remove(QRegExp("[.]+$"));
+ logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
+ }
+ auto components = instance.getComponentList();
+ components->buildingFromScratch();
+ components->setComponentVersion("net.minecraft", mcVersion, true);
+ if(!forgeVersion.isEmpty())
+ {
+ // FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
+ if(forgeVersion == "recommended")
+ {
+ if(forgemap.contains(mcVersion))
+ {
+ forgeVersion = forgemap[mcVersion];
+ }
+ else
+ {
+ logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
+ }
+ }
+ components->setComponentVersion("net.minecraftforge", forgeVersion);
+ }
+ if (m_instIcon != "default")
+ {
+ instance.setIconKey(m_instIcon);
+ }
+ else
+ {
+ if(pack.name.contains("Direwolf20"))
+ {
+ instance.setIconKey("steve");
+ }
+ else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
+ {
+ instance.setIconKey("ftb_logo");
+ }
+ else
+ {
+ // default to something other than the MultiMC default to distinguish these
+ instance.setIconKey("flame");
+ }
+ }
+ QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
+ QFileInfo jarmodsInfo(jarmodsPath);
+ if(jarmodsInfo.isDir())
+ {
+ // install all the jar mods
+ qDebug() << "Found jarmods:";
+ QDir jarmodsDir(jarmodsPath);
+ QStringList jarMods;
+ for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
+ {
+ qDebug() << info.fileName();
+ jarMods.push_back(info.absoluteFilePath());
+ }
+ auto profile = instance.getComponentList();
+ profile->installJarMods(jarMods);
+ // nuke the original files
+ FS::deletePath(jarmodsPath);
+ }
+ instance.setName(m_instName);
+ m_modIdResolver.reset(new Flame::FileResolvingTask(pack));
+ connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
+ {
+ auto results = m_modIdResolver->getResults();
+ m_filesNetJob.reset(new NetJob(tr("Mod download")));
+ for(auto result: results.files)
+ {
+ QString filename = result.fileName;
+ if(!result.required)
+ {
+ filename += ".disabled";
+ }
- auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
- auto path = FS::PathCombine(m_stagingPath , relpath);
+ auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
+ auto path = FS::PathCombine(m_stagingPath , relpath);
- switch(result.type)
- {
- case Flame::File::Type::Folder:
- {
- logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
- // fall-through intentional, we treat these as plain old mods and dump them wherever.
- }
- case Flame::File::Type::SingleFile:
- case Flame::File::Type::Mod:
- {
- qDebug() << "Will download" << result.url << "to" << path;
- auto dl = Net::Download::makeFile(result.url, path);
- m_filesNetJob->addNetAction(dl);
- break;
- }
- case Flame::File::Type::Modpack:
- logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
- break;
- case Flame::File::Type::Cmod2:
- case Flame::File::Type::Ctoc:
- case Flame::File::Type::Unknown:
- logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
- break;
- }
- }
- m_modIdResolver.reset();
- connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
- {
- m_filesNetJob.reset();
- emitSucceeded();
- }
- );
- connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
- {
- m_filesNetJob.reset();
- emitFailed(reason);
- });
- connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
- {
- setProgress(current, total);
- });
- setStatus(tr("Downloading mods..."));
- m_filesNetJob->start();
- }
- );
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
- {
- m_modIdResolver.reset();
- emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
- });
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
- {
- setProgress(current, total);
- });
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
- {
- setStatus(status);
- });
- m_modIdResolver->start();
+ switch(result.type)
+ {
+ case Flame::File::Type::Folder:
+ {
+ logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
+ // fall-through intentional, we treat these as plain old mods and dump them wherever.
+ }
+ case Flame::File::Type::SingleFile:
+ case Flame::File::Type::Mod:
+ {
+ qDebug() << "Will download" << result.url << "to" << path;
+ auto dl = Net::Download::makeFile(result.url, path);
+ m_filesNetJob->addNetAction(dl);
+ break;
+ }
+ case Flame::File::Type::Modpack:
+ logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
+ break;
+ case Flame::File::Type::Cmod2:
+ case Flame::File::Type::Ctoc:
+ case Flame::File::Type::Unknown:
+ logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
+ break;
+ }
+ }
+ m_modIdResolver.reset();
+ connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
+ {
+ m_filesNetJob.reset();
+ emitSucceeded();
+ }
+ );
+ connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
+ {
+ m_filesNetJob.reset();
+ emitFailed(reason);
+ });
+ connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
+ {
+ setProgress(current, total);
+ });
+ setStatus(tr("Downloading mods..."));
+ m_filesNetJob->start();
+ }
+ );
+ connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
+ {
+ m_modIdResolver.reset();
+ emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
+ });
+ connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
+ {
+ setProgress(current, total);
+ });
+ connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
+ {
+ setStatus(status);
+ });
+ m_modIdResolver->start();
}
void InstanceImportTask::processMultiMC()
{
- // FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
- QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
- instanceSettings->registerSetting("InstanceType", "Legacy");
+ // FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
+ QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
+ auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
+ instanceSettings->registerSetting("InstanceType", "Legacy");
- NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
+ NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
- // reset time played on import... because packs.
- instance.resetTimePlayed();
+ // reset time played on import... because packs.
+ instance.resetTimePlayed();
- // set a new nice name
- instance.setName(m_instName);
+ // set a new nice name
+ instance.setName(m_instName);
- // if the icon was specified by user, use that. otherwise pull icon from the pack
- if (m_instIcon != "default")
- {
- instance.setIconKey(m_instIcon);
- }
- else
- {
- m_instIcon = instance.iconKey();
- auto importIconPath = FS::PathCombine(instance.instanceRoot(), m_instIcon + ".png");
- if (QFile::exists(importIconPath))
- {
- // import icon
- auto iconList = ENV.icons();
- if (iconList->iconFileExists(m_instIcon))
- {
- iconList->deleteIcon(m_instIcon);
- }
- iconList->installIcons({importIconPath});
- }
- }
- emitSucceeded();
+ // if the icon was specified by user, use that. otherwise pull icon from the pack
+ if (m_instIcon != "default")
+ {
+ instance.setIconKey(m_instIcon);
+ }
+ else
+ {
+ m_instIcon = instance.iconKey();
+
+ auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
+ if (!importIconPath.isNull() && QFile::exists(importIconPath))
+ {
+ // import icon
+ auto iconList = ENV.icons();
+ if (iconList->iconFileExists(m_instIcon))
+ {
+ iconList->deleteIcon(m_instIcon);
+ }
+ iconList->installIcons({importIconPath});
+ }
+ }
+ emitSucceeded();
}
diff --git a/api/logic/InstanceImportTask.h b/api/logic/InstanceImportTask.h
index 06778dfe..d326391b 100644
--- a/api/logic/InstanceImportTask.h
+++ b/api/logic/InstanceImportTask.h
@@ -10,46 +10,45 @@
#include "QObjectPtr.h"
class QuaZip;
-class BaseInstanceProvider;
namespace Flame
{
- class FileResolvingTask;
+ class FileResolvingTask;
}
class MULTIMC_LOGIC_EXPORT InstanceImportTask : public InstanceTask
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit InstanceImportTask(const QUrl sourceUrl);
+ explicit InstanceImportTask(const QUrl sourceUrl);
protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
+ //! Entry point for tasks.
+ virtual void executeTask() override;
private:
- void processZipPack();
- void processMultiMC();
- void processFlame();
+ void processZipPack();
+ void processMultiMC();
+ void processFlame();
private slots:
- void downloadSucceeded();
- void downloadFailed(QString reason);
- void downloadProgressChanged(qint64 current, qint64 total);
- void extractFinished();
- void extractAborted();
+ void downloadSucceeded();
+ void downloadFailed(QString reason);
+ void downloadProgressChanged(qint64 current, qint64 total);
+ void extractFinished();
+ void extractAborted();
private: /* data */
- NetJobPtr m_filesNetJob;
- shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
- QUrl m_sourceUrl;
- QString m_archivePath;
- bool m_downloadRequired = false;
- std::unique_ptr<QuaZip> m_packZip;
- QFuture<QStringList> m_extractFuture;
- QFutureWatcher<QStringList> m_extractFutureWatcher;
- enum class ModpackType{
- Unknown,
- MultiMC,
- Flame
- } m_modpackType = ModpackType::Unknown;
+ NetJobPtr m_filesNetJob;
+ shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
+ QUrl m_sourceUrl;
+ QString m_archivePath;
+ bool m_downloadRequired = false;
+ std::unique_ptr<QuaZip> m_packZip;
+ QFuture<QStringList> m_extractFuture;
+ QFutureWatcher<QStringList> m_extractFutureWatcher;
+ enum class ModpackType{
+ Unknown,
+ MultiMC,
+ Flame
+ } m_modpackType = ModpackType::Unknown;
};
diff --git a/api/logic/InstanceList.cpp b/api/logic/InstanceList.cpp
index 75b523e4..58e2ee0c 100644
--- a/api/logic/InstanceList.cpp
+++ b/api/logic/InstanceList.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,22 +14,49 @@
*/
#include <QDir>
+#include <QDirIterator>
#include <QSet>
#include <QFile>
#include <QThread>
#include <QTextStream>
#include <QXmlStreamReader>
+#include <QTimer>
#include <QDebug>
+#include <QFileSystemWatcher>
+#include <QUuid>
+#include <QJsonArray>
+#include <QJsonDocument>
#include "InstanceList.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)
- : QAbstractListModel(parent)
+InstanceList::InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *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()
@@ -38,310 +65,773 @@ InstanceList::~InstanceList()
int InstanceList::rowCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
- return m_instances.count();
+ Q_UNUSED(parent);
+ return m_instances.count();
}
QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const
{
- Q_UNUSED(parent);
- if (row < 0 || row >= m_instances.size())
- return QModelIndex();
- return createIndex(row, column, (void *)m_instances.at(row).get());
+ Q_UNUSED(parent);
+ if (row < 0 || row >= m_instances.size())
+ return QModelIndex();
+ return createIndex(row, column, (void *)m_instances.at(row).get());
}
QVariant InstanceList::data(const QModelIndex &index, int role) const
{
- if (!index.isValid())
- {
- return QVariant();
- }
- BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
- switch (role)
- {
- case InstancePointerRole:
- {
- QVariant v = qVariantFromValue((void *)pdata);
- return v;
- }
- case InstanceIDRole:
+ if (!index.isValid())
+ {
+ return QVariant();
+ }
+ BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
+ switch (role)
+ {
+ case InstancePointerRole:
+ {
+ QVariant v = qVariantFromValue((void *)pdata);
+ return v;
+ }
+ case InstanceIDRole:
{
return pdata->id();
}
- case Qt::DisplayRole:
- {
- return pdata->name();
- }
- case Qt::ToolTipRole:
- {
- return pdata->instanceRoot();
- }
- case Qt::DecorationRole:
- {
- return pdata->iconKey();
- }
- // HACK: see GroupView.h in gui!
- case GroupRole:
- {
- return pdata->group();
- }
- default:
- break;
- }
- return QVariant();
+ case Qt::EditRole:
+ case Qt::DisplayRole:
+ {
+ return pdata->name();
+ }
+ case Qt::AccessibleTextRole:
+ {
+ return tr("%1 Instance").arg(pdata->name());
+ }
+ case Qt::ToolTipRole:
+ {
+ return pdata->instanceRoot();
+ }
+ case Qt::DecorationRole:
+ {
+ return pdata->iconKey();
+ }
+ // HACK: see GroupView.h in gui!
+ case GroupRole:
+ {
+ return getInstanceGroup(pdata->id());
+ }
+ default:
+ break;
+ }
+ 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 f;
- if (index.isValid())
- {
- f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
- }
- return f;
+ Qt::ItemFlags f;
+ if (index.isValid())
+ {
+ f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
+ }
+ 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()
{
- return m_groups.toList();
+ return m_groups.toList();
}
void InstanceList::deleteGroup(const QString& name)
{
- for(auto & instance: m_instances)
- {
- auto instGroupName = instance->group();
- if(instGroupName == name)
- {
- instance->setGroupPost(QString());
- }
- }
+ bool removed = false;
+ qDebug() << "Delete group" << name;
+ for(auto & instance: m_instances)
+ {
+ const auto & instID = instance->id();
+ auto instGroupName = getInstanceGroup(instID);
+ if(instGroupName == name)
+ {
+ 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 (deleted externally?).";
+ 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)
{
- QMap<InstanceId, InstanceLocator> out;
- int i = 0;
- for(auto & item: list)
- {
- auto id = item->id();
- if(out.contains(id))
- {
- qWarning() << "Duplicate ID" << id << "in instance list";
- }
- out[id] = std::make_pair(item, i);
- i++;
- }
- return out;
-}
-
-InstanceList::InstListError InstanceList::loadList(bool complete)
-{
- auto existingIds = getIdMapping(m_instances);
-
- QList<InstancePtr> newList;
-
- auto processIds = [&](BaseInstanceProvider * provider, QList<InstanceId> ids)
- {
- for(auto & id: ids)
- {
- if(existingIds.contains(id))
- {
- auto instPair = existingIds[id];
- /*
- auto & instPtr = instPair.first;
- auto & instIdx = instPair.second;
- */
- existingIds.remove(id);
- qDebug() << "Should keep and soft-reload" << id;
- }
- else
- {
- InstancePtr instPtr = provider->loadInstance(id);
- if(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.
- if(!existingIds.isEmpty())
- {
- // get the list of removed instances and sort it by their original index, from last to first
- auto deadList = existingIds.values();
- auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool
- {
- return a.second > b.second;
- };
- std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
- // remove the contiguous ranges of rows
- int front_bookmark = -1;
- int back_bookmark = -1;
- int currentItem = -1;
- auto removeNow = [&]()
- {
- beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
- m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
- endRemoveRows();
- front_bookmark = -1;
- back_bookmark = currentItem;
- };
- for(auto & removedItem: deadList)
- {
- auto instPtr = removedItem.first;
- if(!complete && !m_updatedProviders.contains(instPtr->provider()))
- {
- continue;
- }
- instPtr->invalidate();
- currentItem = removedItem.second;
- if(back_bookmark == -1)
- {
- // no bookmark yet
- back_bookmark = currentItem;
- }
- else if(currentItem == front_bookmark - 1)
- {
- // part of contiguous sequence, continue
- }
- else
- {
- // seam between previous and current item
- removeNow();
- }
- front_bookmark = currentItem;
- }
- if(back_bookmark != -1)
- {
- removeNow();
- }
- }
- if(newList.size())
- {
- add(newList);
- }
- m_updatedProviders.clear();
- return NoError;
+ QMap<InstanceId, InstanceLocator> out;
+ int i = 0;
+ for(auto & item: list)
+ {
+ auto id = item->id();
+ if(out.contains(id))
+ {
+ qWarning() << "Duplicate ID" << id << "in instance list";
+ }
+ out[id] = std::make_pair(item, i);
+ i++;
+ }
+ return out;
+}
+
+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);
+
+ QList<InstancePtr> newList;
+
+ for(auto & id: discoverInstances())
+ {
+ if(existingIds.contains(id))
+ {
+ auto instPair = existingIds[id];
+ existingIds.remove(id);
+ qDebug() << "Should keep and soft-reload" << id;
+ }
+ else
+ {
+ InstancePtr instPtr = loadInstance(id);
+ if(instPtr)
+ {
+ newList.append(instPtr);
+ }
+ }
+ }
+
+ // TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
+ if(!existingIds.isEmpty())
+ {
+ // get the list of removed instances and sort it by their original index, from last to first
+ auto deadList = existingIds.values();
+ auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool
+ {
+ return a.second > b.second;
+ };
+ std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
+ // remove the contiguous ranges of rows
+ int front_bookmark = -1;
+ int back_bookmark = -1;
+ int currentItem = -1;
+ auto removeNow = [&]()
+ {
+ beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
+ m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
+ endRemoveRows();
+ front_bookmark = -1;
+ back_bookmark = currentItem;
+ };
+ for(auto & removedItem: deadList)
+ {
+ auto instPtr = removedItem.first;
+ instPtr->invalidate();
+ currentItem = removedItem.second;
+ if(back_bookmark == -1)
+ {
+ // no bookmark yet
+ back_bookmark = currentItem;
+ }
+ else if(currentItem == front_bookmark - 1)
+ {
+ // part of contiguous sequence, continue
+ }
+ else
+ {
+ // seam between previous and current item
+ removeNow();
+ }
+ front_bookmark = currentItem;
+ }
+ if(back_bookmark != -1)
+ {
+ removeNow();
+ }
+ }
+ if(newList.size())
+ {
+ add(newList);
+ }
+ m_dirty = false;
+ return NoError;
}
void InstanceList::saveNow()
{
- for(auto & item: m_instances)
- {
- item->saveNow();
- }
+ for(auto & item: m_instances)
+ {
+ item->saveNow();
+ }
}
void InstanceList::add(const QList<InstancePtr> &t)
{
- beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
- m_instances.append(t);
- for(auto & ptr : t)
- {
- connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
- }
- endInsertRows();
+ beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
+ m_instances.append(t);
+ for(auto & ptr : t)
+ {
+ connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
+ }
+ endInsertRows();
}
void InstanceList::resumeWatch()
{
- if(m_watchLevel > 0)
- {
- qWarning() << "Bad suspend level resume in instance list";
- return;
- }
- m_watchLevel++;
- if(m_watchLevel > 0 && !m_updatedProviders.isEmpty())
- {
- loadList();
- }
+ if(m_watchLevel > 0)
+ {
+ qWarning() << "Bad suspend level resume in instance list";
+ return;
+ }
+ m_watchLevel++;
+ if(m_watchLevel > 0 && m_dirty)
+ {
+ loadList();
+ }
}
void InstanceList::suspendWatch()
{
- m_watchLevel --;
+ m_watchLevel --;
}
void InstanceList::providerUpdated()
{
- auto provider = dynamic_cast<BaseInstanceProvider *>(QObject::sender());
- if(!provider)
- {
- qWarning() << "InstanceList::providerUpdated triggered by a non-provider";
- return;
- }
- m_updatedProviders.insert(provider);
- if(m_watchLevel == 1)
- {
- loadList();
- }
+ m_dirty = true;
+ if(m_watchLevel == 1)
+ {
+ loadList();
+ }
}
-void InstanceList::groupsPublished(QSet<QString> newGroups)
+InstancePtr InstanceList::getInstanceById(QString instId) const
{
- m_groups.unite(newGroups);
+ if(instId.isEmpty())
+ return InstancePtr();
+ for(auto & inst: m_instances)
+ {
+ if (inst->id() == instId)
+ {
+ return inst;
+ }
+ }
+ return InstancePtr();
}
-void InstanceList::addInstanceProvider(BaseInstanceProvider* provider)
+QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
{
- connect(provider, &BaseInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated);
- connect(provider, &BaseInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished);
- m_providers.append(provider);
+ return index(getInstIndex(getInstanceById(id).get()));
}
-InstancePtr InstanceList::getInstanceById(QString instId) const
+int InstanceList::getInstIndex(BaseInstance *inst) const
{
- if(instId.isEmpty())
- return InstancePtr();
- for(auto & inst: m_instances)
- {
- if (inst->id() == instId)
- {
- return inst;
- }
- }
- return InstancePtr();
+ int count = m_instances.count();
+ for (int i = 0; i < count; i++)
+ {
+ if (inst == m_instances[i].get())
+ {
+ return i;
+ }
+ }
+ return -1;
}
-QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
+void InstanceList::propertiesChanged(BaseInstance *inst)
{
- return index(getInstIndex(getInstanceById(id).get()));
+ int i = getInstIndex(inst);
+ if (i != -1)
+ {
+ emit dataChanged(index(i), index(i));
+ }
}
-int InstanceList::getInstIndex(BaseInstance *inst) const
+InstancePtr InstanceList::loadInstance(const InstanceId& id)
{
- int count = m_instances.count();
- for (int i = 0; i < count; i++)
- {
- if (inst == m_instances[i].get())
- {
- return i;
- }
- }
- return -1;
+ 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::propertiesChanged(BaseInstance *inst)
+void InstanceList::saveGroupList()
{
- int i = getInstIndex(inst);
- if (i != -1)
- {
- emit dataChanged(index(i), index(i));
- }
+ 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();
+ emit instanceSelectRequest(instID);
+ }
+ saveGroupList();
+ return true;
+}
+
+bool InstanceList::destroyStagingPath(const QString& keyPath)
+{
+ return FS::deletePath(keyPath);
+}
+
+#include "InstanceList.moc" \ No newline at end of file
diff --git a/api/logic/InstanceList.h b/api/logic/InstanceList.h
index bb879c83..e0abd890 100644
--- a/api/logic/InstanceList.h
+++ b/api/logic/InstanceList.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,85 +21,146 @@
#include <QList>
#include "BaseInstance.h"
-#include "BaseInstanceProvider.h"
#include "multimc_logic_export.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
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit InstanceList(QObject *parent = 0);
- virtual ~InstanceList();
+ explicit InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent = 0);
+ virtual ~InstanceList();
public:
- QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
- QVariant data(const QModelIndex &index, int role) const;
- Qt::ItemFlags flags(const QModelIndex &index) const;
-
- enum AdditionalRoles
- {
- GroupRole = Qt::UserRole,
- InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
- InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
- };
- /*!
- * \brief Error codes returned by functions in the InstanceList class.
- * NoError Indicates that no error occurred.
- * UnknownError indicates that an unspecified error occurred.
- */
- enum InstListError
- {
- NoError = 0,
- UnknownError
- };
-
- InstancePtr at(int i) const
- {
- return m_instances.at(i);
- }
-
- int count() const
- {
- return m_instances.count();
- }
-
- InstListError loadList(bool complete = false);
- 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;
- QModelIndex getInstanceIndexById(const QString &id) const;
- QStringList getGroups();
-
- void deleteGroup(const QString & name);
+ QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+
+ bool setData(const QModelIndex & index, const QVariant & value, int role) override;
+
+ enum AdditionalRoles
+ {
+ GroupRole = Qt::UserRole,
+ InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
+ InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
+ };
+ /*!
+ * \brief Error codes returned by functions in the InstanceList class.
+ * NoError Indicates that no error occurred.
+ * UnknownError indicates that an unspecified error occurred.
+ */
+ enum InstListError
+ {
+ NoError = 0,
+ UnknownError
+ };
+
+ InstancePtr at(int i) const
+ {
+ return m_instances.at(i);
+ }
+
+ int count() const
+ {
+ return m_instances.count();
+ }
+
+ InstListError loadList();
+ void saveNow();
+
+
+ InstancePtr getInstanceById(QString id) const;
+ QModelIndex getInstanceIndexById(const QString &id) const;
+ QStringList getGroups();
+ GroupId getInstanceGroup(const InstanceId & id) const;
+ void setInstanceGroup(const InstanceId & id, const GroupId& 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:
- void dataIsInvalid();
+ void dataIsInvalid();
+ void instancesChanged();
+ void instanceSelectRequest(QString instanceId);
+ void groupsChanged(QSet<QString> groups);
+
+public slots:
+ void on_InstFolderChanged(const Setting &setting, QVariant value);
private slots:
- void propertiesChanged(BaseInstance *inst);
- void groupsPublished(QSet<QString>);
- void providerUpdated();
+ void propertiesChanged(BaseInstance *inst);
+ void providerUpdated();
+ void instanceDirContentsChanged(const QString &path);
+
+private:
+ int getInstIndex(BaseInstance *inst) const;
+ void suspendWatch();
+ void resumeWatch();
+ void add(const QList<InstancePtr> &list);
+ void loadGroupList();
+ void saveGroupList();
+ QList<InstanceId> discoverInstances();
+ InstancePtr loadInstance(const InstanceId& id);
private:
- int getInstIndex(BaseInstance *inst) const;
- void suspendWatch();
- void resumeWatch();
- void add(const QList<InstancePtr> &list);
-
-protected:
- int m_watchLevel = 0;
- QSet<BaseInstanceProvider *> m_updatedProviders;
- QList<InstancePtr> m_instances;
- QSet<QString> m_groups;
- QVector<shared_qobject_ptr<BaseInstanceProvider>> m_providers;
+ int m_watchLevel = 0;
+ bool m_dirty = false;
+ QList<InstancePtr> m_instances;
+ QSet<QString> m_groups;
+
+ 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;
};
diff --git a/api/logic/InstanceTask.h b/api/logic/InstanceTask.h
index 8fc98eb7..c5f6c7fd 100644
--- a/api/logic/InstanceTask.h
+++ b/api/logic/InstanceTask.h
@@ -4,52 +4,50 @@
#include "multimc_logic_export.h"
#include "settings/SettingsObject.h"
-class BaseInstanceProvider;
-
class MULTIMC_LOGIC_EXPORT InstanceTask : public Task
{
- Q_OBJECT
+ 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;
- }
+ 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;
+ SettingsObjectPtr m_globalSettings;
+ QString m_instName;
+ QString m_instIcon;
+ QString m_instGroup;
+ QString m_stagingPath;
};
diff --git a/api/logic/Json.cpp b/api/logic/Json.cpp
index f2cbc8a3..37ada1aa 100644
--- a/api/logic/Json.cpp
+++ b/api/logic/Json.cpp
@@ -11,262 +11,262 @@ namespace Json
{
void write(const QJsonDocument &doc, const QString &filename)
{
- FS::write(filename, doc.toJson());
+ FS::write(filename, doc.toJson());
}
void write(const QJsonObject &object, const QString &filename)
{
- write(QJsonDocument(object), filename);
+ write(QJsonDocument(object), filename);
}
void write(const QJsonArray &array, const QString &filename)
{
- write(QJsonDocument(array), filename);
+ write(QJsonDocument(array), filename);
}
QByteArray toBinary(const QJsonObject &obj)
{
- return QJsonDocument(obj).toBinaryData();
+ return QJsonDocument(obj).toBinaryData();
}
QByteArray toBinary(const QJsonArray &array)
{
- return QJsonDocument(array).toBinaryData();
+ return QJsonDocument(array).toBinaryData();
}
QByteArray toText(const QJsonObject &obj)
{
- return QJsonDocument(obj).toJson(QJsonDocument::Compact);
+ return QJsonDocument(obj).toJson(QJsonDocument::Compact);
}
QByteArray toText(const QJsonArray &array)
{
- return QJsonDocument(array).toJson(QJsonDocument::Compact);
+ return QJsonDocument(array).toJson(QJsonDocument::Compact);
}
static bool isBinaryJson(const QByteArray &data)
{
- decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag;
- return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0;
+ decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag;
+ return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0;
}
QJsonDocument requireDocument(const QByteArray &data, const QString &what)
{
- if (isBinaryJson(data))
- {
- QJsonDocument doc = QJsonDocument::fromBinaryData(data);
- if (doc.isNull())
- {
- throw JsonException(what + ": Invalid JSON (binary JSON detected)");
- }
- return doc;
- }
- else
- {
- QJsonParseError error;
- QJsonDocument doc = QJsonDocument::fromJson(data, &error);
- if (error.error != QJsonParseError::NoError)
- {
- throw JsonException(what + ": Error parsing JSON: " + error.errorString());
- }
- return doc;
- }
+ if (isBinaryJson(data))
+ {
+ QJsonDocument doc = QJsonDocument::fromBinaryData(data);
+ if (doc.isNull())
+ {
+ throw JsonException(what + ": Invalid JSON (binary JSON detected)");
+ }
+ return doc;
+ }
+ else
+ {
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &error);
+ if (error.error != QJsonParseError::NoError)
+ {
+ throw JsonException(what + ": Error parsing JSON: " + error.errorString());
+ }
+ return doc;
+ }
}
QJsonDocument requireDocument(const QString &filename, const QString &what)
{
- return requireDocument(FS::read(filename), what);
+ return requireDocument(FS::read(filename), what);
}
QJsonObject requireObject(const QJsonDocument &doc, const QString &what)
{
- if (!doc.isObject())
- {
- throw JsonException(what + " is not an object");
- }
- return doc.object();
+ if (!doc.isObject())
+ {
+ throw JsonException(what + " is not an object");
+ }
+ return doc.object();
}
QJsonArray requireArray(const QJsonDocument &doc, const QString &what)
{
- if (!doc.isArray())
- {
- throw JsonException(what + " is not an array");
- }
- return doc.array();
+ if (!doc.isArray())
+ {
+ throw JsonException(what + " is not an array");
+ }
+ return doc.array();
}
void writeString(QJsonObject &to, const QString &key, const QString &value)
{
- if (!value.isEmpty())
- {
- to.insert(key, value);
- }
+ if (!value.isEmpty())
+ {
+ to.insert(key, value);
+ }
}
void writeStringList(QJsonObject &to, const QString &key, const QStringList &values)
{
- if (!values.isEmpty())
- {
- QJsonArray array;
- for(auto value: values)
- {
- array.append(value);
- }
- to.insert(key, array);
- }
+ if (!values.isEmpty())
+ {
+ QJsonArray array;
+ for(auto value: values)
+ {
+ array.append(value);
+ }
+ to.insert(key, array);
+ }
}
template<>
QJsonValue toJson<QUrl>(const QUrl &url)
{
- return QJsonValue(url.toString(QUrl::FullyEncoded));
+ return QJsonValue(url.toString(QUrl::FullyEncoded));
}
template<>
QJsonValue toJson<QByteArray>(const QByteArray &data)
{
- return QJsonValue(QString::fromLatin1(data.toHex()));
+ return QJsonValue(QString::fromLatin1(data.toHex()));
}
template<>
QJsonValue toJson<QDateTime>(const QDateTime &datetime)
{
- return QJsonValue(datetime.toString(Qt::ISODate));
+ return QJsonValue(datetime.toString(Qt::ISODate));
}
template<>
QJsonValue toJson<QDir>(const QDir &dir)
{
- return QDir::current().relativeFilePath(dir.absolutePath());
+ return QDir::current().relativeFilePath(dir.absolutePath());
}
template<>
QJsonValue toJson<QUuid>(const QUuid &uuid)
{
- return uuid.toString();
+ return uuid.toString();
}
template<>
QJsonValue toJson<QVariant>(const QVariant &variant)
{
- return QJsonValue::fromVariant(variant);
+ return QJsonValue::fromVariant(variant);
}
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what)
{
- const QString string = ensureIsType<QString>(value, what);
- // ensure that the string can be safely cast to Latin1
- if (string != QString::fromLatin1(string.toLatin1()))
- {
- throw JsonException(what + " is not encodable as Latin1");
- }
- return QByteArray::fromHex(string.toLatin1());
+ const QString string = ensureIsType<QString>(value, what);
+ // ensure that the string can be safely cast to Latin1
+ if (string != QString::fromLatin1(string.toLatin1()))
+ {
+ throw JsonException(what + " is not encodable as Latin1");
+ }
+ return QByteArray::fromHex(string.toLatin1());
}
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what)
{
- if (!value.isArray())
- {
- throw JsonException(what + " is not an array");
- }
- return value.toArray();
+ if (!value.isArray())
+ {
+ throw JsonException(what + " is not an array");
+ }
+ return value.toArray();
}
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what)
{
- if (!value.isString())
- {
- throw JsonException(what + " is not a string");
- }
- return value.toString();
+ if (!value.isString())
+ {
+ throw JsonException(what + " is not a string");
+ }
+ return value.toString();
}
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what)
{
- if (!value.isBool())
- {
- throw JsonException(what + " is not a bool");
- }
- return value.toBool();
+ if (!value.isBool())
+ {
+ throw JsonException(what + " is not a bool");
+ }
+ return value.toBool();
}
template<> double requireIsType<double>(const QJsonValue &value, const QString &what)
{
- if (!value.isDouble())
- {
- throw JsonException(what + " is not a double");
- }
- return value.toDouble();
+ if (!value.isDouble())
+ {
+ throw JsonException(what + " is not a double");
+ }
+ return value.toDouble();
}
template<> int requireIsType<int>(const QJsonValue &value, const QString &what)
{
- const double doubl = requireIsType<double>(value, what);
- if (fmod(doubl, 1) != 0)
- {
- throw JsonException(what + " is not an integer");
- }
- return int(doubl);
+ const double doubl = requireIsType<double>(value, what);
+ if (fmod(doubl, 1) != 0)
+ {
+ throw JsonException(what + " is not an integer");
+ }
+ return int(doubl);
}
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what)
{
- const QString string = requireIsType<QString>(value, what);
- const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate);
- if (!datetime.isValid())
- {
- throw JsonException(what + " is not a ISO formatted date/time value");
- }
- return datetime;
+ const QString string = requireIsType<QString>(value, what);
+ const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate);
+ if (!datetime.isValid())
+ {
+ throw JsonException(what + " is not a ISO formatted date/time value");
+ }
+ return datetime;
}
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what)
{
- const QString string = ensureIsType<QString>(value, what);
- if (string.isEmpty())
- {
- return QUrl();
- }
- const QUrl url = QUrl(string, QUrl::StrictMode);
- if (!url.isValid())
- {
- throw JsonException(what + " is not a correctly formatted URL");
- }
- return url;
+ const QString string = ensureIsType<QString>(value, what);
+ if (string.isEmpty())
+ {
+ return QUrl();
+ }
+ const QUrl url = QUrl(string, QUrl::StrictMode);
+ if (!url.isValid())
+ {
+ throw JsonException(what + " is not a correctly formatted URL");
+ }
+ return url;
}
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what)
{
- const QString string = requireIsType<QString>(value, what);
- // FIXME: does not handle invalid characters!
- return QDir::current().absoluteFilePath(string);
+ const QString string = requireIsType<QString>(value, what);
+ // FIXME: does not handle invalid characters!
+ return QDir::current().absoluteFilePath(string);
}
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what)
{
- const QString string = requireIsType<QString>(value, what);
- const QUuid uuid = QUuid(string);
- if (uuid.toString() != string) // converts back => valid
- {
- throw JsonException(what + " is not a valid UUID");
- }
- return uuid;
+ const QString string = requireIsType<QString>(value, what);
+ const QUuid uuid = QUuid(string);
+ if (uuid.toString() != string) // converts back => valid
+ {
+ throw JsonException(what + " is not a valid UUID");
+ }
+ return uuid;
}
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what)
{
- if (!value.isObject())
- {
- throw JsonException(what + " is not an object");
- }
- return value.toObject();
+ if (!value.isObject())
+ {
+ throw JsonException(what + " is not an object");
+ }
+ return value.toObject();
}
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what)
{
- if (value.isNull() || value.isUndefined())
- {
- throw JsonException(what + " is null or undefined");
- }
- return value.toVariant();
+ if (value.isNull() || value.isUndefined())
+ {
+ throw JsonException(what + " is null or undefined");
+ }
+ return value.toVariant();
}
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what)
{
- if (value.isNull() || value.isUndefined())
- {
- throw JsonException(what + " is null or undefined");
- }
- return value;
+ if (value.isNull() || value.isUndefined())
+ {
+ throw JsonException(what + " is null or undefined");
+ }
+ return value;
}
}
diff --git a/api/logic/Json.h b/api/logic/Json.h
index e59b25de..34ff6fe2 100644
--- a/api/logic/Json.h
+++ b/api/logic/Json.h
@@ -19,7 +19,7 @@ namespace Json
class MULTIMC_LOGIC_EXPORT JsonException : public ::Exception
{
public:
- JsonException(const QString &message) : Exception(message) {}
+ JsonException(const QString &message) : Exception(message) {}
};
/// @throw FileSystemException
@@ -51,7 +51,7 @@ void writeStringList(QJsonObject & to, const QString &key, const QStringList &va
template<typename T>
QJsonValue toJson(const T &t)
{
- return QJsonValue(t);
+ return QJsonValue(t);
}
template<>
QJsonValue toJson<QUrl>(const QUrl &url);
@@ -69,12 +69,12 @@ QJsonValue toJson<QVariant>(const QVariant &variant);
template<typename T>
QJsonArray toJsonArray(const QList<T> &container)
{
- QJsonArray array;
- for (const T item : container)
- {
- array.append(toJson<T>(item));
- }
- return array;
+ QJsonArray array;
+ for (const T item : container)
+ {
+ array.append(toJson<T>(item));
+ }
+ return array;
}
////////////////// READING ////////////////////
@@ -115,119 +115,119 @@ template<> MULTIMC_LOGIC_EXPORT QUrl requireIsType<QUrl>(const QJsonValue &value
template <typename T>
T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value")
{
- if (value.isUndefined() || value.isNull())
- {
- return default_;
- }
- try
- {
- return requireIsType<T>(value, what);
- }
- catch (JsonException &)
- {
- return default_;
- }
+ if (value.isUndefined() || value.isNull())
+ {
+ return default_;
+ }
+ try
+ {
+ return requireIsType<T>(value, what);
+ }
+ catch (const JsonException &)
+ {
+ return default_;
+ }
}
/// @throw JsonException
template <typename T>
T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
{
- const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
- if (!parent.contains(key))
- {
- throw JsonException(localWhat + "s parent does not contain " + localWhat);
- }
- return requireIsType<T>(parent.value(key), localWhat);
+ const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
+ if (!parent.contains(key))
+ {
+ throw JsonException(localWhat + "s parent does not contain " + localWhat);
+ }
+ return requireIsType<T>(parent.value(key), localWhat);
}
template <typename T>
T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__")
{
- const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
- if (!parent.contains(key))
- {
- return default_;
- }
- return ensureIsType<T>(parent.value(key), default_, localWhat);
+ const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
+ if (!parent.contains(key))
+ {
+ return default_;
+ }
+ return ensureIsType<T>(parent.value(key), default_, localWhat);
}
template <typename T>
QVector<T> requireIsArrayOf(const QJsonDocument &doc)
{
- const QJsonArray array = requireArray(doc);
- QVector<T> out;
- for (const QJsonValue val : array)
- {
- out.append(requireIsType<T>(val, "Document"));
- }
- return out;
+ const QJsonArray array = requireArray(doc);
+ QVector<T> out;
+ for (const QJsonValue val : array)
+ {
+ out.append(requireIsType<T>(val, "Document"));
+ }
+ return out;
}
template <typename T>
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value")
{
- const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
- QVector<T> out;
- for (const QJsonValue val : array)
- {
- out.append(requireIsType<T>(val, what));
- }
- return out;
+ const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
+ QVector<T> out;
+ for (const QJsonValue val : array)
+ {
+ out.append(requireIsType<T>(val, what));
+ }
+ return out;
}
template <typename T>
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, const QString &what = "Value")
{
- if (value.isUndefined())
- {
- return default_;
- }
- return ensureIsArrayOf<T>(value, what);
+ if (value.isUndefined())
+ {
+ return default_;
+ }
+ return ensureIsArrayOf<T>(value, what);
}
/// @throw JsonException
template <typename T>
QVector<T> requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
{
- const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
- if (!parent.contains(key))
- {
- throw JsonException(localWhat + "s parent does not contain " + localWhat);
- }
- return ensureIsArrayOf<T>(parent.value(key), localWhat);
+ const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
+ if (!parent.contains(key))
+ {
+ throw JsonException(localWhat + "s parent does not contain " + localWhat);
+ }
+ return ensureIsArrayOf<T>(parent.value(key), localWhat);
}
template <typename T>
QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key,
- const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__")
+ const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__")
{
- const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
- if (!parent.contains(key))
- {
- return default_;
- }
- return ensureIsArrayOf<T>(parent.value(key), default_, localWhat);
+ const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
+ if (!parent.contains(key))
+ {
+ return default_;
+ }
+ return ensureIsArrayOf<T>(parent.value(key), default_, localWhat);
}
// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers
#define JSON_HELPERFUNCTIONS(NAME, TYPE) \
- inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \
- { \
- return requireIsType<TYPE>(value, what); \
- } \
- inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \
- { \
- return ensureIsType<TYPE>(value, default_, what); \
- } \
- inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \
- { \
- return requireIsType<TYPE>(parent, key, what); \
- } \
- inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \
- { \
- return ensureIsType<TYPE>(parent, key, default_, what); \
- }
+ inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \
+ { \
+ return requireIsType<TYPE>(value, what); \
+ } \
+ inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \
+ { \
+ return ensureIsType<TYPE>(value, default_, what); \
+ } \
+ inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \
+ { \
+ return requireIsType<TYPE>(parent, key, what); \
+ } \
+ inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \
+ { \
+ return ensureIsType<TYPE>(parent, key, default_, what); \
+ }
JSON_HELPERFUNCTIONS(Array, QJsonArray)
JSON_HELPERFUNCTIONS(Object, QJsonObject)
diff --git a/api/logic/LoggedProcess.cpp b/api/logic/LoggedProcess.cpp
index f89b4acc..822c0f04 100644
--- a/api/logic/LoggedProcess.cpp
+++ b/api/logic/LoggedProcess.cpp
@@ -4,157 +4,157 @@
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
{
- // QProcess has a strange interface... let's map a lot of those into a few.
- connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
- connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
- connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
- connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
- connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
+ // QProcess has a strange interface... let's map a lot of those into a few.
+ connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
+ connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
+ connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
+ connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
+ connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
}
LoggedProcess::~LoggedProcess()
{
- if(m_is_detachable)
- {
- setProcessState(QProcess::NotRunning);
- }
+ if(m_is_detachable)
+ {
+ setProcessState(QProcess::NotRunning);
+ }
}
QStringList reprocess(const QByteArray & data, QString & leftover)
{
- QString str = leftover + QString::fromLocal8Bit(data);
+ QString str = leftover + QString::fromLocal8Bit(data);
- str.remove('\r');
- QStringList lines = str.split("\n");
- leftover = lines.takeLast();
- return lines;
+ str.remove('\r');
+ QStringList lines = str.split("\n");
+ leftover = lines.takeLast();
+ return lines;
}
void LoggedProcess::on_stdErr()
{
- auto lines = reprocess(readAllStandardError(), m_err_leftover);
- emit log(lines, MessageLevel::StdErr);
+ auto lines = reprocess(readAllStandardError(), m_err_leftover);
+ emit log(lines, MessageLevel::StdErr);
}
void LoggedProcess::on_stdOut()
{
- auto lines = reprocess(readAllStandardOutput(), m_out_leftover);
- emit log(lines, MessageLevel::StdOut);
+ auto lines = reprocess(readAllStandardOutput(), m_out_leftover);
+ emit log(lines, MessageLevel::StdOut);
}
void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
{
- // save the exit code
- m_exit_code = exit_code;
-
- // Flush console window
- if (!m_err_leftover.isEmpty())
- {
- emit log({m_err_leftover}, MessageLevel::StdErr);
- m_err_leftover.clear();
- }
- if (!m_out_leftover.isEmpty())
- {
- emit log({m_err_leftover}, MessageLevel::StdOut);
- m_out_leftover.clear();
- }
-
- // based on state, send signals
- if (!m_is_aborting)
- {
- if (status == QProcess::NormalExit)
- {
- //: Message displayed on instance exit
- emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::MultiMC);
- changeState(LoggedProcess::Finished);
- }
- else
- {
- //: Message displayed on instance crashed
- if(exit_code == -1)
- emit log({tr("Process crashed.")}, MessageLevel::MultiMC);
- else
- emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::MultiMC);
- changeState(LoggedProcess::Crashed);
- }
- }
- else
- {
- //: Message displayed after the instance exits due to kill request
- emit log({tr("Process was killed by user.")}, MessageLevel::Error);
- changeState(LoggedProcess::Aborted);
- }
+ // save the exit code
+ m_exit_code = exit_code;
+
+ // Flush console window
+ if (!m_err_leftover.isEmpty())
+ {
+ emit log({m_err_leftover}, MessageLevel::StdErr);
+ m_err_leftover.clear();
+ }
+ if (!m_out_leftover.isEmpty())
+ {
+ emit log({m_err_leftover}, MessageLevel::StdOut);
+ m_out_leftover.clear();
+ }
+
+ // based on state, send signals
+ if (!m_is_aborting)
+ {
+ if (status == QProcess::NormalExit)
+ {
+ //: Message displayed on instance exit
+ emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::MultiMC);
+ changeState(LoggedProcess::Finished);
+ }
+ else
+ {
+ //: Message displayed on instance crashed
+ if(exit_code == -1)
+ emit log({tr("Process crashed.")}, MessageLevel::MultiMC);
+ else
+ emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::MultiMC);
+ changeState(LoggedProcess::Crashed);
+ }
+ }
+ else
+ {
+ //: Message displayed after the instance exits due to kill request
+ emit log({tr("Process was killed by user.")}, MessageLevel::Error);
+ changeState(LoggedProcess::Aborted);
+ }
}
void LoggedProcess::on_error(QProcess::ProcessError error)
{
- switch(error)
- {
- case QProcess::FailedToStart:
- {
- emit log({tr("The process failed to start.")}, MessageLevel::Fatal);
- changeState(LoggedProcess::FailedToStart);
- break;
- }
- // we'll just ignore those... never needed them
- case QProcess::Crashed:
- case QProcess::ReadError:
- case QProcess::Timedout:
- case QProcess::UnknownError:
- case QProcess::WriteError:
- break;
- }
+ switch(error)
+ {
+ case QProcess::FailedToStart:
+ {
+ emit log({tr("The process failed to start.")}, MessageLevel::Fatal);
+ changeState(LoggedProcess::FailedToStart);
+ break;
+ }
+ // we'll just ignore those... never needed them
+ case QProcess::Crashed:
+ case QProcess::ReadError:
+ case QProcess::Timedout:
+ case QProcess::UnknownError:
+ case QProcess::WriteError:
+ break;
+ }
}
void LoggedProcess::kill()
{
- m_is_aborting = true;
- QProcess::kill();
+ m_is_aborting = true;
+ QProcess::kill();
}
int LoggedProcess::exitCode() const
{
- return m_exit_code;
+ return m_exit_code;
}
void LoggedProcess::changeState(LoggedProcess::State state)
{
- if(state == m_state)
- return;
- m_state = state;
- emit stateChanged(m_state);
+ if(state == m_state)
+ return;
+ m_state = state;
+ emit stateChanged(m_state);
}
LoggedProcess::State LoggedProcess::state() const
{
- return m_state;
+ return m_state;
}
void LoggedProcess::on_stateChange(QProcess::ProcessState state)
{
- switch(state)
- {
- case QProcess::NotRunning:
- break; // let's not - there are too many that handle this already.
- case QProcess::Starting:
- {
- if(m_state != LoggedProcess::NotRunning)
- {
- qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting;
- }
- changeState(LoggedProcess::Starting);
- return;
- }
- case QProcess::Running:
- {
- if(m_state != LoggedProcess::Starting)
- {
- qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running;
- }
- changeState(LoggedProcess::Running);
- return;
- }
- }
+ switch(state)
+ {
+ case QProcess::NotRunning:
+ break; // let's not - there are too many that handle this already.
+ case QProcess::Starting:
+ {
+ if(m_state != LoggedProcess::NotRunning)
+ {
+ qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting;
+ }
+ changeState(LoggedProcess::Starting);
+ return;
+ }
+ case QProcess::Running:
+ {
+ if(m_state != LoggedProcess::Starting)
+ {
+ qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running;
+ }
+ changeState(LoggedProcess::Running);
+ return;
+ }
+ }
}
#if defined Q_OS_WIN32
@@ -172,5 +172,5 @@ qint64 LoggedProcess::processId() const
void LoggedProcess::setDetachable(bool detachable)
{
- m_is_detachable = detachable;
+ m_is_detachable = detachable;
}
diff --git a/api/logic/LoggedProcess.h b/api/logic/LoggedProcess.h
index 6a80365f..256c0c45 100644
--- a/api/logic/LoggedProcess.h
+++ b/api/logic/LoggedProcess.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,54 +27,54 @@ class MULTIMC_LOGIC_EXPORT LoggedProcess : public QProcess
{
Q_OBJECT
public:
- enum State
- {
- NotRunning,
- Starting,
- FailedToStart,
- Running,
- Finished,
- Crashed,
- Aborted
- };
+ enum State
+ {
+ NotRunning,
+ Starting,
+ FailedToStart,
+ Running,
+ Finished,
+ Crashed,
+ Aborted
+ };
public:
- explicit LoggedProcess(QObject* parent = 0);
- virtual ~LoggedProcess();
+ explicit LoggedProcess(QObject* parent = 0);
+ virtual ~LoggedProcess();
- State state() const;
- int exitCode() const;
- qint64 processId() const;
+ State state() const;
+ int exitCode() const;
+ qint64 processId() const;
- void setDetachable(bool detachable);
+ void setDetachable(bool detachable);
signals:
- void log(QStringList lines, MessageLevel::Enum level);
- void stateChanged(LoggedProcess::State state);
+ void log(QStringList lines, MessageLevel::Enum level);
+ void stateChanged(LoggedProcess::State state);
public slots:
- /**
- * @brief kill the process - equivalent to kill -9
- */
- void kill();
+ /**
+ * @brief kill the process - equivalent to kill -9
+ */
+ void kill();
private slots:
- void on_stdErr();
- void on_stdOut();
- void on_exit(int exit_code, QProcess::ExitStatus status);
- void on_error(QProcess::ProcessError error);
- void on_stateChange(QProcess::ProcessState);
+ void on_stdErr();
+ void on_stdOut();
+ void on_exit(int exit_code, QProcess::ExitStatus status);
+ void on_error(QProcess::ProcessError error);
+ void on_stateChange(QProcess::ProcessState);
private:
- void changeState(LoggedProcess::State state);
+ void changeState(LoggedProcess::State state);
private:
- QString m_err_leftover;
- QString m_out_leftover;
- bool m_killed = false;
- State m_state = NotRunning;
- int m_exit_code = 0;
- bool m_is_aborting = false;
- bool m_is_detachable = false;
+ QString m_err_leftover;
+ QString m_out_leftover;
+ bool m_killed = false;
+ State m_state = NotRunning;
+ int m_exit_code = 0;
+ bool m_is_aborting = false;
+ bool m_is_detachable = false;
};
diff --git a/api/logic/MMCStrings.cpp b/api/logic/MMCStrings.cpp
index c50d596e..dc91c8d6 100644
--- a/api/logic/MMCStrings.cpp
+++ b/api/logic/MMCStrings.cpp
@@ -3,74 +3,74 @@
/// TAKEN FROM Qt, because it doesn't expose it intelligently
static inline QChar getNextChar(const QString &s, int location)
{
- return (location < s.length()) ? s.at(location) : QChar();
+ return (location < s.length()) ? s.at(location) : QChar();
}
/// TAKEN FROM Qt, because it doesn't expose it intelligently
int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
{
- for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2)
- {
- // skip spaces, tabs and 0's
- QChar c1 = getNextChar(s1, l1);
- while (c1.isSpace())
- c1 = getNextChar(s1, ++l1);
- QChar c2 = getNextChar(s2, l2);
- while (c2.isSpace())
- c2 = getNextChar(s2, ++l2);
+ for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2)
+ {
+ // skip spaces, tabs and 0's
+ QChar c1 = getNextChar(s1, l1);
+ while (c1.isSpace())
+ c1 = getNextChar(s1, ++l1);
+ QChar c2 = getNextChar(s2, l2);
+ while (c2.isSpace())
+ c2 = getNextChar(s2, ++l2);
- if (c1.isDigit() && c2.isDigit())
- {
- while (c1.digitValue() == 0)
- c1 = getNextChar(s1, ++l1);
- while (c2.digitValue() == 0)
- c2 = getNextChar(s2, ++l2);
+ if (c1.isDigit() && c2.isDigit())
+ {
+ while (c1.digitValue() == 0)
+ c1 = getNextChar(s1, ++l1);
+ while (c2.digitValue() == 0)
+ c2 = getNextChar(s2, ++l2);
- int lookAheadLocation1 = l1;
- int lookAheadLocation2 = l2;
- int currentReturnValue = 0;
- // find the last digit, setting currentReturnValue as we go if it isn't equal
- for (QChar lookAhead1 = c1, lookAhead2 = c2;
- (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
- lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
- lookAhead2 = getNextChar(s2, ++lookAheadLocation2))
- {
- bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
- bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
- if (!is1ADigit && !is2ADigit)
- break;
- if (!is1ADigit)
- return -1;
- if (!is2ADigit)
- return 1;
- if (currentReturnValue == 0)
- {
- if (lookAhead1 < lookAhead2)
- {
- currentReturnValue = -1;
- }
- else if (lookAhead1 > lookAhead2)
- {
- currentReturnValue = 1;
- }
- }
- }
- if (currentReturnValue != 0)
- return currentReturnValue;
- }
- if (cs == Qt::CaseInsensitive)
- {
- if (!c1.isLower())
- c1 = c1.toLower();
- if (!c2.isLower())
- c2 = c2.toLower();
- }
- int r = QString::localeAwareCompare(c1, c2);
- if (r < 0)
- return -1;
- if (r > 0)
- return 1;
- }
- // The two strings are the same (02 == 2) so fall back to the normal sort
- return QString::compare(s1, s2, cs);
+ int lookAheadLocation1 = l1;
+ int lookAheadLocation2 = l2;
+ int currentReturnValue = 0;
+ // find the last digit, setting currentReturnValue as we go if it isn't equal
+ for (QChar lookAhead1 = c1, lookAhead2 = c2;
+ (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
+ lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
+ lookAhead2 = getNextChar(s2, ++lookAheadLocation2))
+ {
+ bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
+ bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
+ if (!is1ADigit && !is2ADigit)
+ break;
+ if (!is1ADigit)
+ return -1;
+ if (!is2ADigit)
+ return 1;
+ if (currentReturnValue == 0)
+ {
+ if (lookAhead1 < lookAhead2)
+ {
+ currentReturnValue = -1;
+ }
+ else if (lookAhead1 > lookAhead2)
+ {
+ currentReturnValue = 1;
+ }
+ }
+ }
+ if (currentReturnValue != 0)
+ return currentReturnValue;
+ }
+ if (cs == Qt::CaseInsensitive)
+ {
+ if (!c1.isLower())
+ c1 = c1.toLower();
+ if (!c2.isLower())
+ c2 = c2.toLower();
+ }
+ int r = QString::localeAwareCompare(c1, c2);
+ if (r < 0)
+ return -1;
+ if (r > 0)
+ return 1;
+ }
+ // The two strings are the same (02 == 2) so fall back to the normal sort
+ return QString::compare(s1, s2, cs);
}
diff --git a/api/logic/MMCStrings.h b/api/logic/MMCStrings.h
index 5606b909..493ba3d2 100644
--- a/api/logic/MMCStrings.h
+++ b/api/logic/MMCStrings.h
@@ -6,5 +6,5 @@
namespace Strings
{
- int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
+ int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
}
diff --git a/api/logic/MMCZip.cpp b/api/logic/MMCZip.cpp
index 1e7ad51d..3afdbf5e 100644
--- a/api/logic/MMCZip.cpp
+++ b/api/logic/MMCZip.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,231 +25,231 @@
// ours
bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const JlCompress::FilterFunction filter)
{
- QuaZip modZip(from.filePath());
- modZip.open(QuaZip::mdUnzip);
-
- QuaZipFile fileInsideMod(&modZip);
- QuaZipFile zipOutFile(into);
- for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
- {
- QString filename = modZip.getCurrentFileName();
- if (filter && !filter(filename))
- {
- qDebug() << "Skipping file " << filename << " from "
- << from.fileName() << " - filtered";
- continue;
- }
- if (contained.contains(filename))
- {
- qDebug() << "Skipping already contained file " << filename << " from "
- << from.fileName();
- continue;
- }
- contained.insert(filename);
-
- if (!fileInsideMod.open(QIODevice::ReadOnly))
- {
- qCritical() << "Failed to open " << filename << " from " << from.fileName();
- return false;
- }
-
- QuaZipNewInfo info_out(fileInsideMod.getActualFileName());
-
- if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
- {
- qCritical() << "Failed to open " << filename << " in the jar";
- fileInsideMod.close();
- return false;
- }
- if (!JlCompress::copyData(fileInsideMod, zipOutFile))
- {
- zipOutFile.close();
- fileInsideMod.close();
- qCritical() << "Failed to copy data of " << filename << " into the jar";
- return false;
- }
- zipOutFile.close();
- fileInsideMod.close();
- }
- return true;
+ QuaZip modZip(from.filePath());
+ modZip.open(QuaZip::mdUnzip);
+
+ QuaZipFile fileInsideMod(&modZip);
+ QuaZipFile zipOutFile(into);
+ for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
+ {
+ QString filename = modZip.getCurrentFileName();
+ if (filter && !filter(filename))
+ {
+ qDebug() << "Skipping file " << filename << " from "
+ << from.fileName() << " - filtered";
+ continue;
+ }
+ if (contained.contains(filename))
+ {
+ qDebug() << "Skipping already contained file " << filename << " from "
+ << from.fileName();
+ continue;
+ }
+ contained.insert(filename);
+
+ if (!fileInsideMod.open(QIODevice::ReadOnly))
+ {
+ qCritical() << "Failed to open " << filename << " from " << from.fileName();
+ return false;
+ }
+
+ QuaZipNewInfo info_out(fileInsideMod.getActualFileName());
+
+ if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
+ {
+ qCritical() << "Failed to open " << filename << " in the jar";
+ fileInsideMod.close();
+ return false;
+ }
+ if (!JlCompress::copyData(fileInsideMod, zipOutFile))
+ {
+ zipOutFile.close();
+ fileInsideMod.close();
+ qCritical() << "Failed to copy data of " << filename << " into the jar";
+ return false;
+ }
+ zipOutFile.close();
+ fileInsideMod.close();
+ }
+ return true;
}
// ours
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods)
{
- QuaZip zipOut(targetJarPath);
- if (!zipOut.open(QuaZip::mdCreate))
- {
- QFile::remove(targetJarPath);
- qCritical() << "Failed to open the minecraft.jar for modding";
- return false;
- }
- // Files already added to the jar.
- // These files will be skipped.
- QSet<QString> addedFiles;
-
- // Modify the jar
- QListIterator<Mod> i(mods);
+ QuaZip zipOut(targetJarPath);
+ if (!zipOut.open(QuaZip::mdCreate))
+ {
+ QFile::remove(targetJarPath);
+ qCritical() << "Failed to open the minecraft.jar for modding";
+ return false;
+ }
+ // Files already added to the jar.
+ // These files will be skipped.
+ QSet<QString> addedFiles;
+
+ // Modify the jar
+ QListIterator<Mod> i(mods);
i.toBack();
while (i.hasPrevious())
- {
- const Mod &mod = i.previous();
- // do not merge disabled mods.
- if (!mod.enabled())
- continue;
- if (mod.type() == Mod::MOD_ZIPFILE)
- {
- if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles))
- {
- zipOut.close();
- QFile::remove(targetJarPath);
- qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
- return false;
- }
- }
- else if (mod.type() == Mod::MOD_SINGLEFILE)
- {
- // FIXME: buggy - does not work with addedFiles
- auto filename = mod.filename();
- if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
- {
- zipOut.close();
- QFile::remove(targetJarPath);
- qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
- return false;
- }
- addedFiles.insert(filename.fileName());
- }
- else if (mod.type() == Mod::MOD_FOLDER)
- {
- // FIXME: buggy - does not work with addedFiles
- auto filename = mod.filename();
- QString what_to_zip = filename.absoluteFilePath();
- QDir dir(what_to_zip);
- dir.cdUp();
- QString parent_dir = dir.absolutePath();
- if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles))
- {
- zipOut.close();
- QFile::remove(targetJarPath);
- qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
- return false;
- }
- qDebug() << "Adding folder " << filename.fileName() << " from "
- << filename.absoluteFilePath();
- }
- else
- {
- // Make sure we do not continue launching when something is missing or undefined...
- zipOut.close();
- QFile::remove(targetJarPath);
- qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar.";
- return false;
- }
- }
-
- if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");}))
- {
- zipOut.close();
- QFile::remove(targetJarPath);
- qCritical() << "Failed to insert minecraft.jar contents.";
- return false;
- }
-
- // Recompress the jar
- zipOut.close();
- if (zipOut.getZipError() != 0)
- {
- QFile::remove(targetJarPath);
- qCritical() << "Failed to finalize minecraft.jar!";
- return false;
- }
- return true;
+ {
+ const Mod &mod = i.previous();
+ // do not merge disabled mods.
+ if (!mod.enabled())
+ continue;
+ if (mod.type() == Mod::MOD_ZIPFILE)
+ {
+ if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles))
+ {
+ zipOut.close();
+ QFile::remove(targetJarPath);
+ qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
+ return false;
+ }
+ }
+ else if (mod.type() == Mod::MOD_SINGLEFILE)
+ {
+ // FIXME: buggy - does not work with addedFiles
+ auto filename = mod.filename();
+ if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
+ {
+ zipOut.close();
+ QFile::remove(targetJarPath);
+ qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
+ return false;
+ }
+ addedFiles.insert(filename.fileName());
+ }
+ else if (mod.type() == Mod::MOD_FOLDER)
+ {
+ // FIXME: buggy - does not work with addedFiles
+ auto filename = mod.filename();
+ QString what_to_zip = filename.absoluteFilePath();
+ QDir dir(what_to_zip);
+ dir.cdUp();
+ QString parent_dir = dir.absolutePath();
+ if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles))
+ {
+ zipOut.close();
+ QFile::remove(targetJarPath);
+ qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
+ return false;
+ }
+ qDebug() << "Adding folder " << filename.fileName() << " from "
+ << filename.absoluteFilePath();
+ }
+ else
+ {
+ // Make sure we do not continue launching when something is missing or undefined...
+ zipOut.close();
+ QFile::remove(targetJarPath);
+ qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar.";
+ return false;
+ }
+ }
+
+ if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");}))
+ {
+ zipOut.close();
+ QFile::remove(targetJarPath);
+ qCritical() << "Failed to insert minecraft.jar contents.";
+ return false;
+ }
+
+ // Recompress the jar
+ zipOut.close();
+ if (zipOut.getZipError() != 0)
+ {
+ QFile::remove(targetJarPath);
+ qCritical() << "Failed to finalize minecraft.jar!";
+ return false;
+ }
+ return true;
}
// ours
QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root)
{
- QuaZipDir rootDir(zip, root);
- for(auto fileName: rootDir.entryList(QDir::Files))
- {
- if(fileName == what)
- return root;
- }
- for(auto fileName: rootDir.entryList(QDir::Dirs))
- {
- QString result = findFolderOfFileInZip(zip, what, root + fileName);
- if(!result.isEmpty())
- {
- return result;
- }
- }
- return QString();
+ QuaZipDir rootDir(zip, root);
+ for(auto fileName: rootDir.entryList(QDir::Files))
+ {
+ if(fileName == what)
+ return root;
+ }
+ for(auto fileName: rootDir.entryList(QDir::Dirs))
+ {
+ QString result = findFolderOfFileInZip(zip, what, root + fileName);
+ if(!result.isEmpty())
+ {
+ return result;
+ }
+ }
+ return QString();
}
// ours
bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root)
{
- QuaZipDir rootDir(zip, root);
- for(auto fileName: rootDir.entryList(QDir::Files))
- {
- if(fileName == what)
- {
- result.append(root);
- return true;
- }
- }
- for(auto fileName: rootDir.entryList(QDir::Dirs))
- {
- findFilesInZip(zip, what, result, root + fileName);
- }
- return !result.isEmpty();
+ QuaZipDir rootDir(zip, root);
+ for(auto fileName: rootDir.entryList(QDir::Files))
+ {
+ if(fileName == what)
+ {
+ result.append(root);
+ return true;
+ }
+ }
+ for(auto fileName: rootDir.entryList(QDir::Dirs))
+ {
+ findFilesInZip(zip, what, result, root + fileName);
+ }
+ return !result.isEmpty();
}
// ours
QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
{
- QDir directory(target);
- QStringList extracted;
- qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
- if (!zip->goToFirstFile())
- {
- qWarning() << "Failed to seek to first file in zip";
- return QStringList();
- }
- do
- {
- QString name = zip->getCurrentFileName();
- if(!name.startsWith(subdir))
- {
- continue;
- }
- name.remove(0, subdir.size());
- QString absFilePath = directory.absoluteFilePath(name);
- if(name.isEmpty())
- {
- absFilePath += "/";
- }
- if (!JlCompress::extractFile(zip, "", absFilePath))
- {
- qWarning() << "Failed to extract file" << name << "to" << absFilePath;
- JlCompress::removeFile(extracted);
- return QStringList();
- }
- extracted.append(absFilePath);
- qDebug() << "Extracted file" << name;
- } while (zip->goToNextFile());
- return extracted;
+ QDir directory(target);
+ QStringList extracted;
+ qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
+ if (!zip->goToFirstFile())
+ {
+ qWarning() << "Failed to seek to first file in zip";
+ return QStringList();
+ }
+ do
+ {
+ QString name = zip->getCurrentFileName();
+ if(!name.startsWith(subdir))
+ {
+ continue;
+ }
+ name.remove(0, subdir.size());
+ QString absFilePath = directory.absoluteFilePath(name);
+ if(name.isEmpty())
+ {
+ absFilePath += "/";
+ }
+ if (!JlCompress::extractFile(zip, "", absFilePath))
+ {
+ qWarning() << "Failed to extract file" << name << "to" << absFilePath;
+ JlCompress::removeFile(extracted);
+ return QStringList();
+ }
+ extracted.append(absFilePath);
+ qDebug() << "Extracted file" << name;
+ } while (zip->goToNextFile());
+ return extracted;
}
// ours
QStringList MMCZip::extractDir(QString fileCompressed, QString dir)
{
- QuaZip zip(fileCompressed);
- if (!zip.open(QuaZip::mdUnzip))
- {
- return {};
- }
- return MMCZip::extractSubDir(&zip, "", dir);
+ QuaZip zip(fileCompressed);
+ if (!zip.open(QuaZip::mdUnzip))
+ {
+ return {};
+ }
+ return MMCZip::extractSubDir(&zip, "", dir);
}
diff --git a/api/logic/MMCZip.h b/api/logic/MMCZip.h
index 68094b2c..85ac7802 100644
--- a/api/logic/MMCZip.h
+++ b/api/logic/MMCZip.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
#include <QString>
#include <QFileInfo>
#include <QSet>
-#include "minecraft/Mod.h"
+#include "minecraft/mod/Mod.h"
#include <functional>
#include "multimc_logic_export.h"
@@ -28,44 +28,44 @@
namespace MMCZip
{
- /**
- * Merge two zip files, using a filter function
- */
- bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
- const JlCompress::FilterFunction filter = nullptr);
+ /**
+ * Merge two zip files, using a filter function
+ */
+ bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
+ const JlCompress::FilterFunction filter = nullptr);
- /**
- * take a source jar, add mods to it, resulting in target jar
- */
- bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
+ /**
+ * take a source jar, add mods to it, resulting in target jar
+ */
+ bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
- /**
- * Find a single file in archive by file name (not path)
- *
- * \return the path prefix where the file is
- */
- QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
+ /**
+ * Find a single file in archive by file name (not path)
+ *
+ * \return the path prefix where the file is
+ */
+ QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
- /**
- * Find a multiple files of the same name in archive by file name
- * If a file is found in a path, no deeper paths are searched
- *
- * \return true if anything was found
- */
- bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
+ /**
+ * Find a multiple files of the same name in archive by file name
+ * If a file is found in a path, no deeper paths are searched
+ *
+ * \return true if anything was found
+ */
+ bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
- /**
- * Extract a subdirectory from an archive
- */
- QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
+ /**
+ * Extract a subdirectory from an archive
+ */
+ QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
- /**
- * Extract a whole archive.
- *
- * \param fileCompressed The name of the archive.
- * \param dir The directory to extract to, the current directory if left empty.
- * \return The list of the full paths of the files extracted, empty on failure.
- */
- QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir);
+ /**
+ * Extract a whole archive.
+ *
+ * \param fileCompressed The name of the archive.
+ * \param dir The directory to extract to, the current directory if left empty.
+ * \return The list of the full paths of the files extracted, empty on failure.
+ */
+ QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir);
}
diff --git a/api/logic/MessageLevel.cpp b/api/logic/MessageLevel.cpp
index a5191290..0a2cd23d 100644
--- a/api/logic/MessageLevel.cpp
+++ b/api/logic/MessageLevel.cpp
@@ -2,35 +2,35 @@
MessageLevel::Enum MessageLevel::getLevel(const QString& levelName)
{
- if (levelName == "MultiMC")
- return MessageLevel::MultiMC;
- else if (levelName == "Debug")
- return MessageLevel::Debug;
- else if (levelName == "Info")
- return MessageLevel::Info;
- else if (levelName == "Message")
- return MessageLevel::Message;
- else if (levelName == "Warning")
- return MessageLevel::Warning;
- else if (levelName == "Error")
- return MessageLevel::Error;
- else if (levelName == "Fatal")
- return MessageLevel::Fatal;
- // Skip PrePost, it's not exposed to !![]!
- // Also skip StdErr and StdOut
- else
- return MessageLevel::Unknown;
+ if (levelName == "MultiMC")
+ return MessageLevel::MultiMC;
+ else if (levelName == "Debug")
+ return MessageLevel::Debug;
+ else if (levelName == "Info")
+ return MessageLevel::Info;
+ else if (levelName == "Message")
+ return MessageLevel::Message;
+ else if (levelName == "Warning")
+ return MessageLevel::Warning;
+ else if (levelName == "Error")
+ return MessageLevel::Error;
+ else if (levelName == "Fatal")
+ return MessageLevel::Fatal;
+ // Skip PrePost, it's not exposed to !![]!
+ // Also skip StdErr and StdOut
+ else
+ return MessageLevel::Unknown;
}
MessageLevel::Enum MessageLevel::fromLine(QString &line)
{
- // Level prefix
- int endmark = line.indexOf("]!");
- if (line.startsWith("!![") && endmark != -1)
- {
- auto level = MessageLevel::getLevel(line.left(endmark).mid(3));
- line = line.mid(endmark + 2);
- return level;
- }
- return MessageLevel::Unknown;
+ // Level prefix
+ int endmark = line.indexOf("]!");
+ if (line.startsWith("!![") && endmark != -1)
+ {
+ auto level = MessageLevel::getLevel(line.left(endmark).mid(3));
+ line = line.mid(endmark + 2);
+ return level;
+ }
+ return MessageLevel::Unknown;
}
diff --git a/api/logic/MessageLevel.h b/api/logic/MessageLevel.h
index 0128148d..f19048e3 100644
--- a/api/logic/MessageLevel.h
+++ b/api/logic/MessageLevel.h
@@ -10,16 +10,16 @@ namespace MessageLevel
{
enum Enum
{
- Unknown, /**< No idea what this is or where it came from */
- StdOut, /**< Undetermined stderr messages */
- StdErr, /**< Undetermined stdout messages */
- MultiMC, /**< MultiMC Messages */
- Debug, /**< Debug Messages */
- Info, /**< Info Messages */
- Message, /**< Standard Messages */
- Warning, /**< Warnings */
- Error, /**< Errors */
- Fatal, /**< Fatal Errors */
+ Unknown, /**< No idea what this is or where it came from */
+ StdOut, /**< Undetermined stderr messages */
+ StdErr, /**< Undetermined stdout messages */
+ MultiMC, /**< MultiMC Messages */
+ Debug, /**< Debug Messages */
+ Info, /**< Info Messages */
+ Message, /**< Standard Messages */
+ Warning, /**< Warnings */
+ Error, /**< Errors */
+ Fatal, /**< Fatal Errors */
};
MessageLevel::Enum getLevel(const QString &levelName);
diff --git a/api/logic/NullInstance.h b/api/logic/NullInstance.h
index 64965277..e9ba1a13 100644
--- a/api/logic/NullInstance.h
+++ b/api/logic/NullInstance.h
@@ -1,77 +1,76 @@
#pragma once
#include "BaseInstance.h"
+#include "launch/LaunchTask.h"
class NullInstance: public BaseInstance
{
+ Q_OBJECT
public:
- NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
- :BaseInstance(globalSettings, settings, rootDir)
- {
- setVersionBroken(true);
- }
- virtual ~NullInstance() {};
- virtual void init() override
- {
- }
- virtual void saveNow() override
- {
- }
- virtual QString getStatusbarDescription() override
- {
- return tr("Unknown instance type");
- };
- virtual QSet< QString > traits() const override
- {
- return {};
- };
- virtual QString instanceConfigFolder() const override
- {
- return instanceRoot();
- };
- virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
- {
- return nullptr;
- }
- virtual shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
- {
- return nullptr;
- }
- virtual QProcessEnvironment createEnvironment() override
- {
- return QProcessEnvironment();
- }
- virtual QMap<QString, QString> getVariables() const override
- {
- return QMap<QString, QString>();
- }
- virtual IPathMatcher::Ptr getLogFileMatcher() override
- {
- return nullptr;
- }
- virtual QString getLogFileRoot() override
- {
- return instanceRoot();
- }
- virtual QString typeName() const override
- {
- return "Null";
- }
- bool canExport() const override
- {
- return false;
- }
- bool canEdit() const override
- {
- return false;
- }
- bool canLaunch() const override
- {
- return false;
- }
- QStringList verboseDescription(AuthSessionPtr session) override
- {
- QStringList out;
- out << "Null instance - placeholder.";
- return out;
- }
+ NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
+ :BaseInstance(globalSettings, settings, rootDir)
+ {
+ setVersionBroken(true);
+ }
+ virtual ~NullInstance() {};
+ void saveNow() override
+ {
+ }
+ QString getStatusbarDescription() override
+ {
+ return tr("Unknown instance type");
+ };
+ QSet< QString > traits() const override
+ {
+ return {};
+ };
+ QString instanceConfigFolder() const override
+ {
+ return instanceRoot();
+ };
+ shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
+ {
+ return nullptr;
+ }
+ shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
+ {
+ return nullptr;
+ }
+ QProcessEnvironment createEnvironment() override
+ {
+ return QProcessEnvironment();
+ }
+ QMap<QString, QString> getVariables() const override
+ {
+ return QMap<QString, QString>();
+ }
+ IPathMatcher::Ptr getLogFileMatcher() override
+ {
+ return nullptr;
+ }
+ QString getLogFileRoot() override
+ {
+ return instanceRoot();
+ }
+ QString typeName() const override
+ {
+ return "Null";
+ }
+ bool canExport() const override
+ {
+ return false;
+ }
+ bool canEdit() const override
+ {
+ return false;
+ }
+ bool canLaunch() const override
+ {
+ return false;
+ }
+ QStringList verboseDescription(AuthSessionPtr session) override
+ {
+ QStringList out;
+ out << "Null instance - placeholder.";
+ return out;
+ }
};
diff --git a/api/logic/ProblemProvider.h b/api/logic/ProblemProvider.h
index 978710f0..33c9d364 100644
--- a/api/logic/ProblemProvider.h
+++ b/api/logic/ProblemProvider.h
@@ -4,46 +4,46 @@
enum class ProblemSeverity
{
- None,
- Warning,
- Error
+ None,
+ Warning,
+ Error
};
struct PatchProblem
{
- ProblemSeverity m_severity;
- QString m_description;
+ ProblemSeverity m_severity;
+ QString m_description;
};
class MULTIMC_LOGIC_EXPORT ProblemProvider
{
public:
- virtual ~ProblemProvider() {};
- virtual const QList<PatchProblem> getProblems() const = 0;
- virtual ProblemSeverity getProblemSeverity() const = 0;
+ virtual ~ProblemProvider() {};
+ virtual const QList<PatchProblem> getProblems() const = 0;
+ virtual ProblemSeverity getProblemSeverity() const = 0;
};
class MULTIMC_LOGIC_EXPORT ProblemContainer : public ProblemProvider
{
public:
- const QList<PatchProblem> getProblems() const override
- {
- return m_problems;
- }
- ProblemSeverity getProblemSeverity() const override
- {
- return m_problemSeverity;
- }
- virtual void addProblem(ProblemSeverity severity, const QString &description)
- {
- if(severity > m_problemSeverity)
- {
- m_problemSeverity = severity;
- }
- m_problems.append({severity, description});
- }
+ const QList<PatchProblem> getProblems() const override
+ {
+ return m_problems;
+ }
+ ProblemSeverity getProblemSeverity() const override
+ {
+ return m_problemSeverity;
+ }
+ virtual void addProblem(ProblemSeverity severity, const QString &description)
+ {
+ if(severity > m_problemSeverity)
+ {
+ m_problemSeverity = severity;
+ }
+ m_problems.append({severity, description});
+ }
private:
- QList<PatchProblem> m_problems;
- ProblemSeverity m_problemSeverity = ProblemSeverity::None;
+ QList<PatchProblem> m_problems;
+ ProblemSeverity m_problemSeverity = ProblemSeverity::None;
};
diff --git a/api/logic/QObjectPtr.h b/api/logic/QObjectPtr.h
index 4a42f728..0ff51136 100644
--- a/api/logic/QObjectPtr.h
+++ b/api/logic/QObjectPtr.h
@@ -8,10 +8,10 @@ namespace details
{
struct DeleteQObjectLater
{
- void operator()(QObject *obj) const
- {
- obj->deleteLater();
- }
+ void operator()(QObject *obj) const
+ {
+ obj->deleteLater();
+ }
};
}
/**
@@ -28,56 +28,56 @@ template <typename T>
class shared_qobject_ptr
{
public:
- shared_qobject_ptr(){}
- shared_qobject_ptr(T * wrap)
- {
- reset(wrap);
- }
- shared_qobject_ptr(const shared_qobject_ptr<T>& other)
- {
- m_ptr = other.m_ptr;
- }
- template<typename Derived>
- shared_qobject_ptr(const shared_qobject_ptr<Derived> &other)
- {
- m_ptr = other.unwrap();
- }
+ shared_qobject_ptr(){}
+ shared_qobject_ptr(T * wrap)
+ {
+ reset(wrap);
+ }
+ shared_qobject_ptr(const shared_qobject_ptr<T>& other)
+ {
+ m_ptr = other.m_ptr;
+ }
+ template<typename Derived>
+ shared_qobject_ptr(const shared_qobject_ptr<Derived> &other)
+ {
+ m_ptr = other.unwrap();
+ }
public:
- void reset(T * wrap)
- {
- using namespace std::placeholders;
- m_ptr.reset(wrap, std::bind(&QObject::deleteLater, _1));
- }
- void reset(const shared_qobject_ptr<T> &other)
- {
- m_ptr = other.m_ptr;
- }
- void reset()
- {
- m_ptr.reset();
- }
- T * get() const
- {
- return m_ptr.get();
- }
- T * operator->() const
- {
- return m_ptr.get();
- }
- T & operator*() const
- {
- return *m_ptr.get();
- }
- operator bool() const
- {
- return m_ptr.get() != nullptr;
- }
- const std::shared_ptr <T> unwrap() const
- {
- return m_ptr;
- }
+ void reset(T * wrap)
+ {
+ using namespace std::placeholders;
+ m_ptr.reset(wrap, std::bind(&QObject::deleteLater, _1));
+ }
+ void reset(const shared_qobject_ptr<T> &other)
+ {
+ m_ptr = other.m_ptr;
+ }
+ void reset()
+ {
+ m_ptr.reset();
+ }
+ T * get() const
+ {
+ return m_ptr.get();
+ }
+ T * operator->() const
+ {
+ return m_ptr.get();
+ }
+ T & operator*() const
+ {
+ return *m_ptr.get();
+ }
+ operator bool() const
+ {
+ return m_ptr.get() != nullptr;
+ }
+ const std::shared_ptr <T> unwrap() const
+ {
+ return m_ptr;
+ }
private:
- std::shared_ptr <T> m_ptr;
+ std::shared_ptr <T> m_ptr;
};
diff --git a/api/logic/RWStorage.h b/api/logic/RWStorage.h
index 4afbd96c..5d792367 100644
--- a/api/logic/RWStorage.h
+++ b/api/logic/RWStorage.h
@@ -5,59 +5,59 @@ template <typename K, typename V>
class RWStorage
{
public:
- void add(K key, V value)
- {
- QWriteLocker l(&lock);
- cache[key] = value;
- stale_entries.remove(key);
- }
- V get(K key)
- {
- QReadLocker l(&lock);
- if(cache.contains(key))
- {
- return cache[key];
- }
- else return V();
- }
- bool get(K key, V& value)
- {
- QReadLocker l(&lock);
- if(cache.contains(key))
- {
- value = cache[key];
- return true;
- }
- else return false;
- }
- bool has(K key)
- {
- QReadLocker l(&lock);
- return cache.contains(key);
- }
- bool stale(K key)
- {
- QReadLocker l(&lock);
- if(!cache.contains(key))
- return true;
- return stale_entries.contains(key);
- }
- void setStale(K key)
- {
- QWriteLocker l(&lock);
- if(cache.contains(key))
- {
- stale_entries.insert(key);
- }
- }
- void clear()
- {
- QWriteLocker l(&lock);
- cache.clear();
- stale_entries.clear();
- }
+ void add(K key, V value)
+ {
+ QWriteLocker l(&lock);
+ cache[key] = value;
+ stale_entries.remove(key);
+ }
+ V get(K key)
+ {
+ QReadLocker l(&lock);
+ if(cache.contains(key))
+ {
+ return cache[key];
+ }
+ else return V();
+ }
+ bool get(K key, V& value)
+ {
+ QReadLocker l(&lock);
+ if(cache.contains(key))
+ {
+ value = cache[key];
+ return true;
+ }
+ else return false;
+ }
+ bool has(K key)
+ {
+ QReadLocker l(&lock);
+ return cache.contains(key);
+ }
+ bool stale(K key)
+ {
+ QReadLocker l(&lock);
+ if(!cache.contains(key))
+ return true;
+ return stale_entries.contains(key);
+ }
+ void setStale(K key)
+ {
+ QWriteLocker l(&lock);
+ if(cache.contains(key))
+ {
+ stale_entries.insert(key);
+ }
+ }
+ void clear()
+ {
+ QWriteLocker l(&lock);
+ cache.clear();
+ stale_entries.clear();
+ }
private:
- QReadWriteLock lock;
- QMap<K, V> cache;
- QSet<K> stale_entries;
+ QReadWriteLock lock;
+ QMap<K, V> cache;
+ QSet<K> stale_entries;
};
diff --git a/api/logic/RecursiveFileSystemWatcher.cpp b/api/logic/RecursiveFileSystemWatcher.cpp
index 59c3f0f0..b7417cdf 100644
--- a/api/logic/RecursiveFileSystemWatcher.cpp
+++ b/api/logic/RecursiveFileSystemWatcher.cpp
@@ -4,108 +4,108 @@
#include <QDebug>
RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent)
- : QObject(parent), m_watcher(new QFileSystemWatcher(this))
+ : QObject(parent), m_watcher(new QFileSystemWatcher(this))
{
- connect(m_watcher, &QFileSystemWatcher::fileChanged, this,
- &RecursiveFileSystemWatcher::fileChange);
- connect(m_watcher, &QFileSystemWatcher::directoryChanged, this,
- &RecursiveFileSystemWatcher::directoryChange);
+ connect(m_watcher, &QFileSystemWatcher::fileChanged, this,
+ &RecursiveFileSystemWatcher::fileChange);
+ connect(m_watcher, &QFileSystemWatcher::directoryChanged, this,
+ &RecursiveFileSystemWatcher::directoryChange);
}
void RecursiveFileSystemWatcher::setRootDir(const QDir &root)
{
- bool wasEnabled = m_isEnabled;
- disable();
- m_root = root;
- setFiles(scanRecursive(m_root));
- if (wasEnabled)
- {
- enable();
- }
+ bool wasEnabled = m_isEnabled;
+ disable();
+ m_root = root;
+ setFiles(scanRecursive(m_root));
+ if (wasEnabled)
+ {
+ enable();
+ }
}
void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles)
{
- bool wasEnabled = m_isEnabled;
- disable();
- m_watchFiles = watchFiles;
- if (wasEnabled)
- {
- enable();
- }
+ bool wasEnabled = m_isEnabled;
+ disable();
+ m_watchFiles = watchFiles;
+ if (wasEnabled)
+ {
+ enable();
+ }
}
void RecursiveFileSystemWatcher::enable()
{
- if (m_isEnabled)
- {
- return;
- }
- Q_ASSERT(m_root != QDir::root());
- addFilesToWatcherRecursive(m_root);
- m_isEnabled = true;
+ if (m_isEnabled)
+ {
+ return;
+ }
+ Q_ASSERT(m_root != QDir::root());
+ addFilesToWatcherRecursive(m_root);
+ m_isEnabled = true;
}
void RecursiveFileSystemWatcher::disable()
{
- if (!m_isEnabled)
- {
- return;
- }
- m_isEnabled = false;
- m_watcher->removePaths(m_watcher->files());
- m_watcher->removePaths(m_watcher->directories());
+ if (!m_isEnabled)
+ {
+ return;
+ }
+ m_isEnabled = false;
+ m_watcher->removePaths(m_watcher->files());
+ m_watcher->removePaths(m_watcher->directories());
}
void RecursiveFileSystemWatcher::setFiles(const QStringList &files)
{
- if (files != m_files)
- {
- m_files = files;
- emit filesChanged();
- }
+ if (files != m_files)
+ {
+ m_files = files;
+ emit filesChanged();
+ }
}
void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir)
{
- m_watcher->addPath(dir.absolutePath());
- for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
- {
- addFilesToWatcherRecursive(dir.absoluteFilePath(directory));
- }
- if (m_watchFiles)
- {
- for (const QFileInfo &info : dir.entryInfoList(QDir::Files))
- {
- m_watcher->addPath(info.absoluteFilePath());
- }
- }
+ m_watcher->addPath(dir.absolutePath());
+ for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
+ {
+ addFilesToWatcherRecursive(dir.absoluteFilePath(directory));
+ }
+ if (m_watchFiles)
+ {
+ for (const QFileInfo &info : dir.entryInfoList(QDir::Files))
+ {
+ m_watcher->addPath(info.absoluteFilePath());
+ }
+ }
}
QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory)
{
- QStringList ret;
- if(!m_matcher)
- {
- return {};
- }
- for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden))
- {
- ret.append(scanRecursive(directory.absoluteFilePath(dir)));
- }
- for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden))
- {
- auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file));
- if (m_matcher->matches(relPath))
- {
- ret.append(relPath);
- }
- }
- return ret;
+ QStringList ret;
+ if(!m_matcher)
+ {
+ return {};
+ }
+ for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden))
+ {
+ ret.append(scanRecursive(directory.absoluteFilePath(dir)));
+ }
+ for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden))
+ {
+ auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file));
+ if (m_matcher->matches(relPath))
+ {
+ ret.append(relPath);
+ }
+ }
+ return ret;
}
void RecursiveFileSystemWatcher::fileChange(const QString &path)
{
- emit fileChanged(path);
+ emit fileChanged(path);
}
void RecursiveFileSystemWatcher::directoryChange(const QString &path)
{
- setFiles(scanRecursive(m_root));
+ setFiles(scanRecursive(m_root));
}
diff --git a/api/logic/RecursiveFileSystemWatcher.h b/api/logic/RecursiveFileSystemWatcher.h
index 07bce0b9..c9c39f49 100644
--- a/api/logic/RecursiveFileSystemWatcher.h
+++ b/api/logic/RecursiveFileSystemWatcher.h
@@ -8,56 +8,56 @@
class MULTIMC_LOGIC_EXPORT RecursiveFileSystemWatcher : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- RecursiveFileSystemWatcher(QObject *parent);
-
- void setRootDir(const QDir &root);
- QDir rootDir() const
- {
- return m_root;
- }
-
- // WARNING: setting this to true may be bad for performance
- void setWatchFiles(const bool watchFiles);
- bool watchFiles() const
- {
- return m_watchFiles;
- }
-
- void setMatcher(IPathMatcher::Ptr matcher)
- {
- m_matcher = matcher;
- }
-
- QStringList files() const
- {
- return m_files;
- }
+ RecursiveFileSystemWatcher(QObject *parent);
+
+ void setRootDir(const QDir &root);
+ QDir rootDir() const
+ {
+ return m_root;
+ }
+
+ // WARNING: setting this to true may be bad for performance
+ void setWatchFiles(const bool watchFiles);
+ bool watchFiles() const
+ {
+ return m_watchFiles;
+ }
+
+ void setMatcher(IPathMatcher::Ptr matcher)
+ {
+ m_matcher = matcher;
+ }
+
+ QStringList files() const
+ {
+ return m_files;
+ }
signals:
- void filesChanged();
- void fileChanged(const QString &path);
+ void filesChanged();
+ void fileChanged(const QString &path);
public slots:
- void enable();
- void disable();
+ void enable();
+ void disable();
private:
- QDir m_root;
- bool m_watchFiles = false;
- bool m_isEnabled = false;
- IPathMatcher::Ptr m_matcher;
+ QDir m_root;
+ bool m_watchFiles = false;
+ bool m_isEnabled = false;
+ IPathMatcher::Ptr m_matcher;
- QFileSystemWatcher *m_watcher;
+ QFileSystemWatcher *m_watcher;
- QStringList m_files;
- void setFiles(const QStringList &files);
+ QStringList m_files;
+ void setFiles(const QStringList &files);
- void addFilesToWatcherRecursive(const QDir &dir);
- QStringList scanRecursive(const QDir &dir);
+ void addFilesToWatcherRecursive(const QDir &dir);
+ QStringList scanRecursive(const QDir &dir);
private slots:
- void fileChange(const QString &path);
- void directoryChange(const QString &path);
+ void fileChange(const QString &path);
+ void directoryChange(const QString &path);
};
diff --git a/api/logic/SeparatorPrefixTree.h b/api/logic/SeparatorPrefixTree.h
index fd149af0..7a841cb7 100644
--- a/api/logic/SeparatorPrefixTree.h
+++ b/api/logic/SeparatorPrefixTree.h
@@ -7,292 +7,292 @@ template <char Tseparator>
class SeparatorPrefixTree
{
public:
- SeparatorPrefixTree(QStringList paths)
- {
- insert(paths);
- }
+ SeparatorPrefixTree(QStringList paths)
+ {
+ insert(paths);
+ }
- SeparatorPrefixTree(bool contained = false)
- {
- m_contained = contained;
- }
+ SeparatorPrefixTree(bool contained = false)
+ {
+ m_contained = contained;
+ }
- void insert(QStringList paths)
- {
- for(auto &path: paths)
- {
- insert(path);
- }
- }
+ void insert(QStringList paths)
+ {
+ for(auto &path: paths)
+ {
+ insert(path);
+ }
+ }
- /// insert an exact path into the tree
- SeparatorPrefixTree & insert(QString path)
- {
- auto sepIndex = path.indexOf(Tseparator);
- if(sepIndex == -1)
- {
- children[path] = SeparatorPrefixTree(true);
- return children[path];
- }
- else
- {
- auto prefix = path.left(sepIndex);
- if(!children.contains(prefix))
- {
- children[prefix] = SeparatorPrefixTree(false);
- }
- return children[prefix].insert(path.mid(sepIndex + 1));
- }
- }
+ /// insert an exact path into the tree
+ SeparatorPrefixTree & insert(QString path)
+ {
+ auto sepIndex = path.indexOf(Tseparator);
+ if(sepIndex == -1)
+ {
+ children[path] = SeparatorPrefixTree(true);
+ return children[path];
+ }
+ else
+ {
+ auto prefix = path.left(sepIndex);
+ if(!children.contains(prefix))
+ {
+ children[prefix] = SeparatorPrefixTree(false);
+ }
+ return children[prefix].insert(path.mid(sepIndex + 1));
+ }
+ }
- /// is the path fully contained in the tree?
- bool contains(QString path) const
- {
- auto node = find(path);
- return node != nullptr;
- }
+ /// is the path fully contained in the tree?
+ bool contains(QString path) const
+ {
+ auto node = find(path);
+ return node != nullptr;
+ }
- /// does the tree cover a path? That means the prefix of the path is contained in the tree
- bool covers(QString path) const
- {
- // if we found some valid node, it's good enough. the tree covers the path
- if(m_contained)
- {
- return true;
- }
- auto sepIndex = path.indexOf(Tseparator);
- if(sepIndex == -1)
- {
- auto found = children.find(path);
- if(found == children.end())
- {
- return false;
- }
- return (*found).covers(QString());
- }
- else
- {
- auto prefix = path.left(sepIndex);
- auto found = children.find(prefix);
- if(found == children.end())
- {
- return false;
- }
- return (*found).covers(path.mid(sepIndex + 1));
- }
- }
+ /// does the tree cover a path? That means the prefix of the path is contained in the tree
+ bool covers(QString path) const
+ {
+ // if we found some valid node, it's good enough. the tree covers the path
+ if(m_contained)
+ {
+ return true;
+ }
+ auto sepIndex = path.indexOf(Tseparator);
+ if(sepIndex == -1)
+ {
+ auto found = children.find(path);
+ if(found == children.end())
+ {
+ return false;
+ }
+ return (*found).covers(QString());
+ }
+ else
+ {
+ auto prefix = path.left(sepIndex);
+ auto found = children.find(prefix);
+ if(found == children.end())
+ {
+ return false;
+ }
+ return (*found).covers(path.mid(sepIndex + 1));
+ }
+ }
- /// return the contained path that covers the path specified
- QString cover(QString path) const
- {
- // if we found some valid node, it's good enough. the tree covers the path
- if(m_contained)
- {
- return QString("");
- }
- auto sepIndex = path.indexOf(Tseparator);
- if(sepIndex == -1)
- {
- auto found = children.find(path);
- if(found == children.end())
- {
- return QString();
- }
- auto nested = (*found).cover(QString());
- if(nested.isNull())
- {
- return nested;
- }
- if(nested.isEmpty())
- return path;
- return path + Tseparator + nested;
- }
- else
- {
- auto prefix = path.left(sepIndex);
- auto found = children.find(prefix);
- if(found == children.end())
- {
- return QString();
- }
- auto nested = (*found).cover(path.mid(sepIndex + 1));
- if(nested.isNull())
- {
- return nested;
- }
- if(nested.isEmpty())
- return prefix;
- return prefix + Tseparator + nested;
- }
- }
+ /// return the contained path that covers the path specified
+ QString cover(QString path) const
+ {
+ // if we found some valid node, it's good enough. the tree covers the path
+ if(m_contained)
+ {
+ return QString("");
+ }
+ auto sepIndex = path.indexOf(Tseparator);
+ if(sepIndex == -1)
+ {
+ auto found = children.find(path);
+ if(found == children.end())
+ {
+ return QString();
+ }
+ auto nested = (*found).cover(QString());
+ if(nested.isNull())
+ {
+ return nested;
+ }
+ if(nested.isEmpty())
+ return path;
+ return path + Tseparator + nested;
+ }
+ else
+ {
+ auto prefix = path.left(sepIndex);
+ auto found = children.find(prefix);
+ if(found == children.end())
+ {
+ return QString();
+ }
+ auto nested = (*found).cover(path.mid(sepIndex + 1));
+ if(nested.isNull())
+ {
+ return nested;
+ }
+ if(nested.isEmpty())
+ return prefix;
+ return prefix + Tseparator + nested;
+ }
+ }
- /// Does the path-specified node exist in the tree? It does not have to be contained.
- bool exists(QString path) const
- {
- auto sepIndex = path.indexOf(Tseparator);
- if(sepIndex == -1)
- {
- auto found = children.find(path);
- if(found == children.end())
- {
- return false;
- }
- return true;
- }
- else
- {
- auto prefix = path.left(sepIndex);
- auto found = children.find(prefix);
- if(found == children.end())
- {
- return false;
- }
- return (*found).exists(path.mid(sepIndex + 1));
- }
- }
+ /// Does the path-specified node exist in the tree? It does not have to be contained.
+ bool exists(QString path) const
+ {
+ auto sepIndex = path.indexOf(Tseparator);
+ if(sepIndex == -1)
+ {
+ auto found = children.find(path);
+ if(found == children.end())
+ {
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ auto prefix = path.left(sepIndex);
+ auto found = children.find(prefix);
+ if(found == children.end())
+ {
+ return false;
+ }
+ return (*found).exists(path.mid(sepIndex + 1));
+ }
+ }
- /// find a node in the tree by name
- const SeparatorPrefixTree * find(QString path) const
- {
- auto sepIndex = path.indexOf(Tseparator);
- if(sepIndex == -1)
- {
- auto found = children.find(path);
- if(found == children.end())
- {
- return nullptr;
- }
- return &(*found);
- }
- else
- {
- auto prefix = path.left(sepIndex);
- auto found = children.find(prefix);
- if(found == children.end())
- {
- return nullptr;
- }
- return (*found).find(path.mid(sepIndex + 1));
- }
- }
+ /// find a node in the tree by name
+ const SeparatorPrefixTree * find(QString path) const
+ {
+ auto sepIndex = path.indexOf(Tseparator);
+ if(sepIndex == -1)
+ {
+ auto found = children.find(path);
+ if(found == children.end())
+ {
+ return nullptr;
+ }
+ return &(*found);
+ }
+ else
+ {
+ auto prefix = path.left(sepIndex);
+ auto found = children.find(prefix);
+ if(found == children.end())
+ {
+ return nullptr;
+ }
+ return (*found).find(path.mid(sepIndex + 1));
+ }
+ }
- /// is this a leaf node?
- bool leaf() const
- {
- return children.isEmpty();
- }
+ /// is this a leaf node?
+ bool leaf() const
+ {
+ return children.isEmpty();
+ }
- /// is this node actually contained in the tree, or is it purely structural?
- bool contained() const
- {
- return m_contained;
- }
+ /// is this node actually contained in the tree, or is it purely structural?
+ bool contained() const
+ {
+ return m_contained;
+ }
- /// Remove a path from the tree
- bool remove(QString path)
- {
- return removeInternal(path) != Failed;
- }
+ /// Remove a path from the tree
+ bool remove(QString path)
+ {
+ return removeInternal(path) != Failed;
+ }
- /// Clear all children of this node tree node
- void clear()
- {
- children.clear();
- }
+ /// Clear all children of this node tree node
+ void clear()
+ {
+ children.clear();
+ }
- QStringList toStringList() const
- {
- QStringList collected;
- // collecting these is more expensive.
- auto iter = children.begin();
- while(iter != children.end())
- {
- QStringList list = iter.value().toStringList();
- for(int i = 0; i < list.size(); i++)
- {
- list[i] = iter.key() + Tseparator + list[i];
- }
- collected.append(list);
- if((*iter).m_contained)
- {
- collected.append(iter.key());
- }
- iter++;
- }
- return collected;
- }
+ QStringList toStringList() const
+ {
+ QStringList collected;
+ // collecting these is more expensive.
+ auto iter = children.begin();
+ while(iter != children.end())
+ {
+ QStringList list = iter.value().toStringList();
+ for(int i = 0; i < list.size(); i++)
+ {
+ list[i] = iter.key() + Tseparator + list[i];
+ }
+ collected.append(list);
+ if((*iter).m_contained)
+ {
+ collected.append(iter.key());
+ }
+ iter++;
+ }
+ return collected;
+ }
private:
- enum Removal
- {
- Failed,
- Succeeded,
- HasChildren
- };
- Removal removeInternal(QString path = QString())
- {
- if(path.isEmpty())
- {
- if(!m_contained)
- {
- // remove all children - we are removing a prefix
- clear();
- return Succeeded;
- }
- m_contained = false;
- if(children.size())
- {
- return HasChildren;
- }
- return Succeeded;
- }
- Removal remStatus = Failed;
- QString childToRemove;
- auto sepIndex = path.indexOf(Tseparator);
- if(sepIndex == -1)
- {
- childToRemove = path;
- auto found = children.find(childToRemove);
- if(found == children.end())
- {
- return Failed;
- }
- remStatus = (*found).removeInternal();
- }
- else
- {
- childToRemove = path.left(sepIndex);
- auto found = children.find(childToRemove);
- if(found == children.end())
- {
- return Failed;
- }
- remStatus = (*found).removeInternal(path.mid(sepIndex + 1));
- }
- switch (remStatus)
- {
- case Failed:
- case HasChildren:
- {
- return remStatus;
- }
- case Succeeded:
- {
- children.remove(childToRemove);
- if(m_contained)
- {
- return HasChildren;
- }
- if(children.size())
- {
- return HasChildren;
- }
- return Succeeded;
- }
- }
- return Failed;
- }
+ enum Removal
+ {
+ Failed,
+ Succeeded,
+ HasChildren
+ };
+ Removal removeInternal(QString path = QString())
+ {
+ if(path.isEmpty())
+ {
+ if(!m_contained)
+ {
+ // remove all children - we are removing a prefix
+ clear();
+ return Succeeded;
+ }
+ m_contained = false;
+ if(children.size())
+ {
+ return HasChildren;
+ }
+ return Succeeded;
+ }
+ Removal remStatus = Failed;
+ QString childToRemove;
+ auto sepIndex = path.indexOf(Tseparator);
+ if(sepIndex == -1)
+ {
+ childToRemove = path;
+ auto found = children.find(childToRemove);
+ if(found == children.end())
+ {
+ return Failed;
+ }
+ remStatus = (*found).removeInternal();
+ }
+ else
+ {
+ childToRemove = path.left(sepIndex);
+ auto found = children.find(childToRemove);
+ if(found == children.end())
+ {
+ return Failed;
+ }
+ remStatus = (*found).removeInternal(path.mid(sepIndex + 1));
+ }
+ switch (remStatus)
+ {
+ case Failed:
+ case HasChildren:
+ {
+ return remStatus;
+ }
+ case Succeeded:
+ {
+ children.remove(childToRemove);
+ if(m_contained)
+ {
+ return HasChildren;
+ }
+ if(children.size())
+ {
+ return HasChildren;
+ }
+ return Succeeded;
+ }
+ }
+ return Failed;
+ }
private:
- QMap<QString,SeparatorPrefixTree<Tseparator>> children;
- bool m_contained = false;
+ QMap<QString,SeparatorPrefixTree<Tseparator>> children;
+ bool m_contained = false;
};
diff --git a/api/logic/Usable.h b/api/logic/Usable.h
index 1168be4d..83dd083d 100644
--- a/api/logic/Usable.h
+++ b/api/logic/Usable.h
@@ -12,27 +12,27 @@ class Usable;
*/
class Usable
{
- friend class UseLock;
+ friend class UseLock;
public:
- std::size_t useCount()
- {
- return m_useCount;
- }
- bool isInUse()
- {
- return m_useCount > 0;
- }
+ std::size_t useCount()
+ {
+ return m_useCount;
+ }
+ bool isInUse()
+ {
+ return m_useCount > 0;
+ }
protected:
- virtual void decrementUses()
- {
- m_useCount--;
- }
- virtual void incrementUses()
- {
- m_useCount++;
- }
+ virtual void decrementUses()
+ {
+ m_useCount--;
+ }
+ virtual void incrementUses()
+ {
+ m_useCount++;
+ }
private:
- std::size_t m_useCount = 0;
+ std::size_t m_useCount = 0;
};
/**
@@ -43,16 +43,16 @@ private:
class UseLock
{
public:
- UseLock(std::shared_ptr<Usable> usable)
- : m_usable(usable)
- {
- // this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate.
- m_usable->incrementUses();
- }
- ~UseLock()
- {
- m_usable->decrementUses();
- }
+ UseLock(std::shared_ptr<Usable> usable)
+ : m_usable(usable)
+ {
+ // this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate.
+ m_usable->incrementUses();
+ }
+ ~UseLock()
+ {
+ m_usable->decrementUses();
+ }
private:
- std::shared_ptr<Usable> m_usable;
+ std::shared_ptr<Usable> m_usable;
};
diff --git a/api/logic/Version.cpp b/api/logic/Version.cpp
index 2c83374f..42eac669 100644
--- a/api/logic/Version.cpp
+++ b/api/logic/Version.cpp
@@ -7,79 +7,79 @@
Version::Version(const QString &str) : m_string(str)
{
- parse();
+ parse();
}
bool Version::operator<(const Version &other) const
{
- const int size = qMax(m_sections.size(), other.m_sections.size());
- for (int i = 0; i < size; ++i)
- {
- const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
- const Section sec2 =
- (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
- if (sec1 != sec2)
- {
- return sec1 < sec2;
- }
- }
+ const int size = qMax(m_sections.size(), other.m_sections.size());
+ for (int i = 0; i < size; ++i)
+ {
+ const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
+ const Section sec2 =
+ (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
+ if (sec1 != sec2)
+ {
+ return sec1 < sec2;
+ }
+ }
- return false;
+ return false;
}
bool Version::operator<=(const Version &other) const
{
- return *this < other || *this == other;
+ return *this < other || *this == other;
}
bool Version::operator>(const Version &other) const
{
- const int size = qMax(m_sections.size(), other.m_sections.size());
- for (int i = 0; i < size; ++i)
- {
- const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
- const Section sec2 =
- (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
- if (sec1 != sec2)
- {
- return sec1 > sec2;
- }
- }
+ const int size = qMax(m_sections.size(), other.m_sections.size());
+ for (int i = 0; i < size; ++i)
+ {
+ const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
+ const Section sec2 =
+ (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
+ if (sec1 != sec2)
+ {
+ return sec1 > sec2;
+ }
+ }
- return false;
+ return false;
}
bool Version::operator>=(const Version &other) const
{
- return *this > other || *this == other;
+ return *this > other || *this == other;
}
bool Version::operator==(const Version &other) const
{
- const int size = qMax(m_sections.size(), other.m_sections.size());
- for (int i = 0; i < size; ++i)
- {
- const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
- const Section sec2 =
- (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
- if (sec1 != sec2)
- {
- return false;
- }
- }
+ const int size = qMax(m_sections.size(), other.m_sections.size());
+ for (int i = 0; i < size; ++i)
+ {
+ const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
+ const Section sec2 =
+ (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
+ if (sec1 != sec2)
+ {
+ return false;
+ }
+ }
- return true;
+ return true;
}
bool Version::operator!=(const Version &other) const
{
- return !operator==(other);
+ return !operator==(other);
}
void Version::parse()
{
- m_sections.clear();
+ m_sections.clear();
- // FIXME: this is bad. versions can contain a lot more separators...
- QStringList parts = m_string.split('.');
+ // FIXME: this is bad. versions can contain a lot more separators...
+ QStringList parts = m_string.split('.');
- for (const auto part : parts)
- {
- m_sections.append(Section(part));
- }
+ for (const auto part : parts)
+ {
+ m_sections.append(Section(part));
+ }
}
diff --git a/api/logic/Version.h b/api/logic/Version.h
index 08dfb6e9..c5d93081 100644
--- a/api/logic/Version.h
+++ b/api/logic/Version.h
@@ -7,100 +7,101 @@
class QUrl;
-struct MULTIMC_LOGIC_EXPORT Version
+class MULTIMC_LOGIC_EXPORT Version
{
- Version(const QString &str);
- Version() {}
+public:
+ Version(const QString &str);
+ Version() {}
- bool operator<(const Version &other) const;
- bool operator<=(const Version &other) const;
- bool operator>(const Version &other) const;
- bool operator>=(const Version &other) const;
- bool operator==(const Version &other) const;
- bool operator!=(const Version &other) const;
+ bool operator<(const Version &other) const;
+ bool operator<=(const Version &other) const;
+ bool operator>(const Version &other) const;
+ bool operator>=(const Version &other) const;
+ bool operator==(const Version &other) const;
+ bool operator!=(const Version &other) const;
- QString toString() const
- {
- return m_string;
- }
+ QString toString() const
+ {
+ return m_string;
+ }
private:
- QString m_string;
- struct Section
- {
- explicit Section(const QString &fullString)
- {
- m_fullString = fullString;
- int cutoff = m_fullString.size();
- for(int i = 0; i < m_fullString.size(); i++)
- {
- if(!m_fullString[i].isDigit())
- {
- cutoff = i;
- break;
- }
- }
- auto numPart = m_fullString.leftRef(cutoff);
- if(numPart.size())
- {
- numValid = true;
- m_numPart = numPart.toInt();
- }
- auto stringPart = m_fullString.midRef(cutoff);
- if(stringPart.size())
- {
- m_stringPart = stringPart.toString();
- }
- }
- explicit Section() {}
- bool numValid = false;
- int m_numPart = 0;
- QString m_stringPart;
- QString m_fullString;
+ QString m_string;
+ struct Section
+ {
+ explicit Section(const QString &fullString)
+ {
+ m_fullString = fullString;
+ int cutoff = m_fullString.size();
+ for(int i = 0; i < m_fullString.size(); i++)
+ {
+ if(!m_fullString[i].isDigit())
+ {
+ cutoff = i;
+ break;
+ }
+ }
+ auto numPart = m_fullString.leftRef(cutoff);
+ if(numPart.size())
+ {
+ numValid = true;
+ m_numPart = numPart.toInt();
+ }
+ auto stringPart = m_fullString.midRef(cutoff);
+ if(stringPart.size())
+ {
+ m_stringPart = stringPart.toString();
+ }
+ }
+ explicit Section() {}
+ bool numValid = false;
+ int m_numPart = 0;
+ QString m_stringPart;
+ QString m_fullString;
- inline bool operator!=(const Section &other) const
- {
- if(numValid && other.numValid)
- {
- return m_numPart != other.m_numPart || m_stringPart != other.m_stringPart;
- }
- else
- {
- return m_fullString != other.m_fullString;
- }
- }
- inline bool operator<(const Section &other) const
- {
- if(numValid && other.numValid)
- {
- if(m_numPart < other.m_numPart)
- return true;
- if(m_numPart == other.m_numPart && m_stringPart < other.m_stringPart)
- return true;
- return false;
- }
- else
- {
- return m_fullString < other.m_fullString;
- }
- }
- inline bool operator>(const Section &other) const
- {
- if(numValid && other.numValid)
- {
- if(m_numPart > other.m_numPart)
- return true;
- if(m_numPart == other.m_numPart && m_stringPart > other.m_stringPart)
- return true;
- return false;
- }
- else
- {
- return m_fullString > other.m_fullString;
- }
- }
- };
- QList<Section> m_sections;
+ inline bool operator!=(const Section &other) const
+ {
+ if(numValid && other.numValid)
+ {
+ return m_numPart != other.m_numPart || m_stringPart != other.m_stringPart;
+ }
+ else
+ {
+ return m_fullString != other.m_fullString;
+ }
+ }
+ inline bool operator<(const Section &other) const
+ {
+ if(numValid && other.numValid)
+ {
+ if(m_numPart < other.m_numPart)
+ return true;
+ if(m_numPart == other.m_numPart && m_stringPart < other.m_stringPart)
+ return true;
+ return false;
+ }
+ else
+ {
+ return m_fullString < other.m_fullString;
+ }
+ }
+ inline bool operator>(const Section &other) const
+ {
+ if(numValid && other.numValid)
+ {
+ if(m_numPart > other.m_numPart)
+ return true;
+ if(m_numPart == other.m_numPart && m_stringPart > other.m_stringPart)
+ return true;
+ return false;
+ }
+ else
+ {
+ return m_fullString > other.m_fullString;
+ }
+ }
+ };
+ QList<Section> m_sections;
- void parse();
+ void parse();
};
diff --git a/api/logic/Version_test.cpp b/api/logic/Version_test.cpp
index b8e05768..9c222a70 100644
--- a/api/logic/Version_test.cpp
+++ b/api/logic/Version_test.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,64 +20,64 @@
class ModUtilsTest : public QObject
{
- Q_OBJECT
- void setupVersions()
- {
- QTest::addColumn<QString>("first");
- QTest::addColumn<QString>("second");
- QTest::addColumn<bool>("lessThan");
- QTest::addColumn<bool>("equal");
+ Q_OBJECT
+ void setupVersions()
+ {
+ QTest::addColumn<QString>("first");
+ QTest::addColumn<QString>("second");
+ QTest::addColumn<bool>("lessThan");
+ QTest::addColumn<bool>("equal");
- QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true;
- QTest::newRow("equal, implicit 1") << "1.2" << "1.2.0" << false << true;
- QTest::newRow("equal, implicit 2") << "1.2.0" << "1.2" << false << true;
- QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true;
+ QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true;
+ QTest::newRow("equal, implicit 1") << "1.2" << "1.2.0" << false << true;
+ QTest::newRow("equal, implicit 2") << "1.2.0" << "1.2" << false << true;
+ QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true;
- QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false;
- QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false;
- QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false;
- QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.1" << true << false;
- QTest::newRow("lessThan, implicit 2") << "1.2" << "1.3.0" << true << false;
- QTest::newRow("lessThan, implicit 3") << "1.2" << "2.2.0" << true << false;
- QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false;
+ QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false;
+ QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false;
+ QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false;
+ QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.1" << true << false;
+ QTest::newRow("lessThan, implicit 2") << "1.2" << "1.3.0" << true << false;
+ QTest::newRow("lessThan, implicit 3") << "1.2" << "2.2.0" << true << false;
+ QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false;
- QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false;
- QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false;
- QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false;
- QTest::newRow("greaterThan, implicit 1") << "1.2.1" << "1.2" << false << false;
- QTest::newRow("greaterThan, implicit 2") << "1.3.0" << "1.2" << false << false;
- QTest::newRow("greaterThan, implicit 3") << "2.2.0" << "1.2" << false << false;
- QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false;
- }
+ QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false;
+ QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false;
+ QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false;
+ QTest::newRow("greaterThan, implicit 1") << "1.2.1" << "1.2" << false << false;
+ QTest::newRow("greaterThan, implicit 2") << "1.3.0" << "1.2" << false << false;
+ QTest::newRow("greaterThan, implicit 3") << "2.2.0" << "1.2" << false << false;
+ QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false;
+ }
private slots:
- void initTestCase()
- {
+ void initTestCase()
+ {
- }
- void cleanupTestCase()
- {
+ }
+ void cleanupTestCase()
+ {
- }
+ }
- void test_versionCompare_data()
- {
- setupVersions();
- }
- void test_versionCompare()
- {
- QFETCH(QString, first);
- QFETCH(QString, second);
- QFETCH(bool, lessThan);
- QFETCH(bool, equal);
+ void test_versionCompare_data()
+ {
+ setupVersions();
+ }
+ void test_versionCompare()
+ {
+ QFETCH(QString, first);
+ QFETCH(QString, second);
+ QFETCH(bool, lessThan);
+ QFETCH(bool, equal);
- const auto v1 = Version(first);
- const auto v2 = Version(second);
+ const auto v1 = Version(first);
+ const auto v2 = Version(second);
- QCOMPARE(v1 < v2, lessThan);
- QCOMPARE(v1 > v2, !lessThan && !equal);
- QCOMPARE(v1 == v2, equal);
- }
+ QCOMPARE(v1 < v2, lessThan);
+ QCOMPARE(v1 > v2, !lessThan && !equal);
+ QCOMPARE(v1 == v2, equal);
+ }
};
QTEST_GUILESS_MAIN(ModUtilsTest)
diff --git a/api/logic/WatchLock.h b/api/logic/WatchLock.h
new file mode 100644
index 00000000..3e08b413
--- /dev/null
+++ b/api/logic/WatchLock.h
@@ -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;
+};
diff --git a/api/logic/icons/IIconList.h b/api/logic/icons/IIconList.h
index e6c16d50..9a3fe022 100644
--- a/api/logic/icons/IIconList.h
+++ b/api/logic/icons/IIconList.h
@@ -6,21 +6,21 @@
enum IconType : unsigned
{
- Builtin,
- Transient,
- FileBased,
- ICONS_TOTAL,
- ToBeDeleted
+ Builtin,
+ Transient,
+ FileBased,
+ ICONS_TOTAL,
+ ToBeDeleted
};
class MULTIMC_LOGIC_EXPORT IIconList
{
public:
- virtual ~IIconList();
- virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0;
- virtual bool deleteIcon(const QString &key) = 0;
- virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
- virtual bool iconFileExists(const QString &key) const = 0;
- virtual void installIcons(const QStringList &iconFiles) = 0;
- virtual void installIcon(const QString &file, const QString &name) = 0;
+ virtual ~IIconList();
+ virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0;
+ virtual bool deleteIcon(const QString &key) = 0;
+ virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
+ virtual bool iconFileExists(const QString &key) const = 0;
+ virtual void installIcons(const QStringList &iconFiles) = 0;
+ virtual void installIcon(const QString &file, const QString &name) = 0;
};
diff --git a/api/logic/icons/IconUtils.cpp b/api/logic/icons/IconUtils.cpp
new file mode 100644
index 00000000..bf530c16
--- /dev/null
+++ b/api/logic/icons/IconUtils.cpp
@@ -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;
+}
+
+}
+
diff --git a/api/logic/icons/IconUtils.h b/api/logic/icons/IconUtils.h
new file mode 100644
index 00000000..ce236946
--- /dev/null
+++ b/api/logic/icons/IconUtils.h
@@ -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();
+
+}
diff --git a/api/logic/java/JavaChecker.cpp b/api/logic/java/JavaChecker.cpp
index f0b71e48..ca0f4bde 100644
--- a/api/logic/java/JavaChecker.cpp
+++ b/api/logic/java/JavaChecker.cpp
@@ -16,149 +16,149 @@ JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
void JavaChecker::performCheck()
{
- QString checkerJar = FS::PathCombine(ENV.getJarsPath(), "JavaCheck.jar");
-
- QStringList args;
-
- process.reset(new QProcess());
- if(m_args.size())
- {
- auto extraArgs = Commandline::splitArgs(m_args);
- args.append(extraArgs);
- }
- if(m_minMem != 0)
- {
- args << QString("-Xms%1m").arg(m_minMem);
- }
- if(m_maxMem != 0)
- {
- args << QString("-Xmx%1m").arg(m_maxMem);
- }
- if(m_permGen != 64)
- {
- args << QString("-XX:PermSize=%1m").arg(m_permGen);
- }
-
- args.append({"-jar", checkerJar});
- process->setArguments(args);
- process->setProgram(m_path);
- process->setProcessChannelMode(QProcess::SeparateChannels);
- process->setProcessEnvironment(CleanEnviroment());
- qDebug() << "Running java checker: " + m_path + args.join(" ");;
-
- connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
- connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
- connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady()));
- connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady()));
- connect(&killTimer, SIGNAL(timeout()), SLOT(timeout()));
- killTimer.setSingleShot(true);
- killTimer.start(15000);
- process->start();
+ QString checkerJar = FS::PathCombine(ENV.getJarsPath(), "JavaCheck.jar");
+
+ QStringList args;
+
+ process.reset(new QProcess());
+ if(m_args.size())
+ {
+ auto extraArgs = Commandline::splitArgs(m_args);
+ args.append(extraArgs);
+ }
+ if(m_minMem != 0)
+ {
+ args << QString("-Xms%1m").arg(m_minMem);
+ }
+ if(m_maxMem != 0)
+ {
+ args << QString("-Xmx%1m").arg(m_maxMem);
+ }
+ if(m_permGen != 64)
+ {
+ args << QString("-XX:PermSize=%1m").arg(m_permGen);
+ }
+
+ args.append({"-jar", checkerJar});
+ process->setArguments(args);
+ process->setProgram(m_path);
+ process->setProcessChannelMode(QProcess::SeparateChannels);
+ process->setProcessEnvironment(CleanEnviroment());
+ qDebug() << "Running java checker: " + m_path + args.join(" ");;
+
+ connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
+ connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
+ connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady()));
+ connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady()));
+ connect(&killTimer, SIGNAL(timeout()), SLOT(timeout()));
+ killTimer.setSingleShot(true);
+ killTimer.start(15000);
+ process->start();
}
void JavaChecker::stdoutReady()
{
- QByteArray data = process->readAllStandardOutput();
- QString added = QString::fromLocal8Bit(data);
- added.remove('\r');
- m_stdout += added;
+ QByteArray data = process->readAllStandardOutput();
+ QString added = QString::fromLocal8Bit(data);
+ added.remove('\r');
+ m_stdout += added;
}
void JavaChecker::stderrReady()
{
- QByteArray data = process->readAllStandardError();
- QString added = QString::fromLocal8Bit(data);
- added.remove('\r');
- m_stderr += added;
+ QByteArray data = process->readAllStandardError();
+ QString added = QString::fromLocal8Bit(data);
+ added.remove('\r');
+ m_stderr += added;
}
void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
{
- killTimer.stop();
- QProcessPtr _process;
- _process.swap(process);
-
- JavaCheckResult result;
- {
- result.path = m_path;
- result.id = m_id;
- }
- result.errorLog = m_stderr;
- result.outLog = m_stdout;
- qDebug() << "STDOUT" << m_stdout;
- qWarning() << "STDERR" << m_stderr;
- qDebug() << "Java checker finished with status " << status << " exit code " << exitcode;
-
- if (status == QProcess::CrashExit || exitcode == 1)
- {
- result.validity = JavaCheckResult::Validity::Errored;
- emit checkFinished(result);
- return;
- }
-
- bool success = true;
-
- QMap<QString, QString> results;
- QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts);
- for(QString line : lines)
- {
- line = line.trimmed();
-
- auto parts = line.split('=', QString::SkipEmptyParts);
- if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
- {
- success = false;
- }
- else
- {
- results.insert(parts[0], parts[1]);
- }
- }
-
- if(!results.contains("os.arch") || !results.contains("java.version") || !success)
- {
- result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
- emit checkFinished(result);
- return;
- }
-
- auto os_arch = results["os.arch"];
- auto java_version = results["java.version"];
- bool is_64 = os_arch == "x86_64" || os_arch == "amd64";
-
-
- result.validity = JavaCheckResult::Validity::Valid;
- result.is_64bit = is_64;
- result.mojangPlatform = is_64 ? "64" : "32";
- result.realPlatform = os_arch;
- result.javaVersion = java_version;
- qDebug() << "Java checker succeeded.";
- emit checkFinished(result);
+ killTimer.stop();
+ QProcessPtr _process = process;
+ process.reset();
+
+ JavaCheckResult result;
+ {
+ result.path = m_path;
+ result.id = m_id;
+ }
+ result.errorLog = m_stderr;
+ result.outLog = m_stdout;
+ qDebug() << "STDOUT" << m_stdout;
+ qWarning() << "STDERR" << m_stderr;
+ qDebug() << "Java checker finished with status " << status << " exit code " << exitcode;
+
+ if (status == QProcess::CrashExit || exitcode == 1)
+ {
+ result.validity = JavaCheckResult::Validity::Errored;
+ emit checkFinished(result);
+ return;
+ }
+
+ bool success = true;
+
+ QMap<QString, QString> results;
+ QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts);
+ for(QString line : lines)
+ {
+ line = line.trimmed();
+
+ auto parts = line.split('=', QString::SkipEmptyParts);
+ if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
+ {
+ success = false;
+ }
+ else
+ {
+ results.insert(parts[0], parts[1]);
+ }
+ }
+
+ if(!results.contains("os.arch") || !results.contains("java.version") || !success)
+ {
+ result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
+ emit checkFinished(result);
+ return;
+ }
+
+ auto os_arch = results["os.arch"];
+ auto java_version = results["java.version"];
+ bool is_64 = os_arch == "x86_64" || os_arch == "amd64";
+
+
+ result.validity = JavaCheckResult::Validity::Valid;
+ result.is_64bit = is_64;
+ result.mojangPlatform = is_64 ? "64" : "32";
+ result.realPlatform = os_arch;
+ result.javaVersion = java_version;
+ qDebug() << "Java checker succeeded.";
+ emit checkFinished(result);
}
void JavaChecker::error(QProcess::ProcessError err)
{
- if(err == QProcess::FailedToStart)
- {
- killTimer.stop();
- qDebug() << "Java checker has failed to start.";
- JavaCheckResult result;
- {
- result.path = m_path;
- result.id = m_id;
- }
-
- emit checkFinished(result);
- return;
- }
+ if(err == QProcess::FailedToStart)
+ {
+ killTimer.stop();
+ qDebug() << "Java checker has failed to start.";
+ JavaCheckResult result;
+ {
+ result.path = m_path;
+ result.id = m_id;
+ }
+
+ emit checkFinished(result);
+ return;
+ }
}
void JavaChecker::timeout()
{
- // NO MERCY. NO ABUSE.
- if(process)
- {
- qDebug() << "Java checker has been killed by timeout.";
- process->kill();
- }
+ // NO MERCY. NO ABUSE.
+ if(process)
+ {
+ qDebug() << "Java checker has been killed by timeout.";
+ process->kill();
+ }
}
diff --git a/api/logic/java/JavaChecker.h b/api/logic/java/JavaChecker.h
index c6bd697c..af0dcb90 100644
--- a/api/logic/java/JavaChecker.h
+++ b/api/logic/java/JavaChecker.h
@@ -3,6 +3,8 @@
#include <QTimer>
#include <memory>
+#include "QObjectPtr.h"
+
#include "multimc_logic_export.h"
#include "JavaVersion.h"
@@ -11,50 +13,50 @@ class JavaChecker;
struct MULTIMC_LOGIC_EXPORT JavaCheckResult
{
- QString path;
- QString mojangPlatform;
- QString realPlatform;
- JavaVersion javaVersion;
- QString outLog;
- QString errorLog;
- bool is_64bit = false;
- int id;
- enum class Validity
- {
- Errored,
- ReturnedInvalidData,
- Valid
- } validity = Validity::Errored;
+ QString path;
+ QString mojangPlatform;
+ QString realPlatform;
+ JavaVersion javaVersion;
+ QString outLog;
+ QString errorLog;
+ bool is_64bit = false;
+ int id;
+ enum class Validity
+ {
+ Errored,
+ ReturnedInvalidData,
+ Valid
+ } validity = Validity::Errored;
};
-typedef std::shared_ptr<QProcess> QProcessPtr;
-typedef std::shared_ptr<JavaChecker> JavaCheckerPtr;
+typedef shared_qobject_ptr<QProcess> QProcessPtr;
+typedef shared_qobject_ptr<JavaChecker> JavaCheckerPtr;
class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit JavaChecker(QObject *parent = 0);
- void performCheck();
+ explicit JavaChecker(QObject *parent = 0);
+ void performCheck();
- QString m_path;
- QString m_args;
- int m_id = 0;
- int m_minMem = 0;
- int m_maxMem = 0;
- int m_permGen = 64;
+ QString m_path;
+ QString m_args;
+ int m_id = 0;
+ int m_minMem = 0;
+ int m_maxMem = 0;
+ int m_permGen = 64;
signals:
- void checkFinished(JavaCheckResult result);
+ void checkFinished(JavaCheckResult result);
private:
- QProcessPtr process;
- QTimer killTimer;
- QString m_stdout;
- QString m_stderr;
+ QProcessPtr process;
+ QTimer killTimer;
+ QString m_stdout;
+ QString m_stderr;
public
slots:
- void timeout();
- void finished(int exitcode, QProcess::ExitStatus);
- void error(QProcess::ProcessError);
- void stdoutReady();
- void stderrReady();
+ void timeout();
+ void finished(int exitcode, QProcess::ExitStatus);
+ void error(QProcess::ProcessError);
+ void stdoutReady();
+ void stderrReady();
};
diff --git a/api/logic/java/JavaCheckerJob.cpp b/api/logic/java/JavaCheckerJob.cpp
index fabb5aaa..223d8f55 100644
--- a/api/logic/java/JavaCheckerJob.cpp
+++ b/api/logic/java/JavaCheckerJob.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,26 +19,26 @@
void JavaCheckerJob::partFinished(JavaCheckResult result)
{
- num_finished++;
- qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/"
- << javacheckers.size();
- setProgress(num_finished, javacheckers.size());
+ num_finished++;
+ qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/"
+ << javacheckers.size();
+ setProgress(num_finished, javacheckers.size());
- javaresults.replace(result.id, result);
+ javaresults.replace(result.id, result);
- if (num_finished == javacheckers.size())
- {
- emitSucceeded();
- }
+ if (num_finished == javacheckers.size())
+ {
+ emitSucceeded();
+ }
}
void JavaCheckerJob::executeTask()
{
- qDebug() << m_job_name.toLocal8Bit() << " started.";
- for (auto iter : javacheckers)
- {
- javaresults.append(JavaCheckResult());
- connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult)));
- iter->performCheck();
- }
+ qDebug() << m_job_name.toLocal8Bit() << " started.";
+ for (auto iter : javacheckers)
+ {
+ javaresults.append(JavaCheckResult());
+ connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult)));
+ iter->performCheck();
+ }
}
diff --git a/api/logic/java/JavaCheckerJob.h b/api/logic/java/JavaCheckerJob.h
index cac2b638..24d0d1b8 100644
--- a/api/logic/java/JavaCheckerJob.h
+++ b/api/logic/java/JavaCheckerJob.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,41 +20,42 @@
#include "tasks/Task.h"
class JavaCheckerJob;
-typedef std::shared_ptr<JavaCheckerJob> JavaCheckerJobPtr;
+typedef shared_qobject_ptr<JavaCheckerJob> JavaCheckerJobPtr;
// FIXME: this just seems horribly redundant
class JavaCheckerJob : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
-
- bool addJavaCheckerAction(JavaCheckerPtr base)
- {
- javacheckers.append(base);
- // if this is already running, the action needs to be started right away!
- if (isRunning())
- {
- setProgress(num_finished, javacheckers.size());
- connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished);
- base->performCheck();
- }
- return true;
- }
- QList<JavaCheckResult> getResults()
- {
- return javaresults;
- }
+ explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
+ virtual ~JavaCheckerJob() {};
+
+ bool addJavaCheckerAction(JavaCheckerPtr base)
+ {
+ javacheckers.append(base);
+ // if this is already running, the action needs to be started right away!
+ if (isRunning())
+ {
+ setProgress(num_finished, javacheckers.size());
+ connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished);
+ base->performCheck();
+ }
+ return true;
+ }
+ QList<JavaCheckResult> getResults()
+ {
+ return javaresults;
+ }
private slots:
- void partFinished(JavaCheckResult result);
+ void partFinished(JavaCheckResult result);
protected:
- virtual void executeTask() override;
+ virtual void executeTask() override;
private:
- QString m_job_name;
- QList<JavaCheckerPtr> javacheckers;
- QList<JavaCheckResult> javaresults;
- int num_finished = 0;
+ QString m_job_name;
+ QList<JavaCheckerPtr> javacheckers;
+ QList<JavaCheckResult> javaresults;
+ int num_finished = 0;
};
diff --git a/api/logic/java/JavaInstall.cpp b/api/logic/java/JavaInstall.cpp
index bb262b6e..5bcf7bcb 100644
--- a/api/logic/java/JavaInstall.cpp
+++ b/api/logic/java/JavaInstall.cpp
@@ -3,26 +3,26 @@
bool JavaInstall::operator<(const JavaInstall &rhs)
{
- auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
- if(archCompare != 0)
- return archCompare < 0;
- if(id < rhs.id)
- {
- return true;
- }
- if(id > rhs.id)
- {
- return false;
- }
- return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
+ auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
+ if(archCompare != 0)
+ return archCompare < 0;
+ if(id < rhs.id)
+ {
+ return true;
+ }
+ if(id > rhs.id)
+ {
+ return false;
+ }
+ return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
}
bool JavaInstall::operator==(const JavaInstall &rhs)
{
- return arch == rhs.arch && id == rhs.id && path == rhs.path;
+ return arch == rhs.arch && id == rhs.id && path == rhs.path;
}
bool JavaInstall::operator>(const JavaInstall &rhs)
{
- return (!operator<(rhs)) && (!operator==(rhs));
+ return (!operator<(rhs)) && (!operator==(rhs));
}
diff --git a/api/logic/java/JavaInstall.h b/api/logic/java/JavaInstall.h
index 882c7386..64be40d1 100644
--- a/api/logic/java/JavaInstall.h
+++ b/api/logic/java/JavaInstall.h
@@ -5,34 +5,34 @@
struct JavaInstall : public BaseVersion
{
- JavaInstall(){}
- JavaInstall(QString id, QString arch, QString path)
- : id(id), arch(arch), path(path)
- {
- }
- virtual QString descriptor()
- {
- return id.toString();
- }
+ JavaInstall(){}
+ JavaInstall(QString id, QString arch, QString path)
+ : id(id), arch(arch), path(path)
+ {
+ }
+ virtual QString descriptor()
+ {
+ return id.toString();
+ }
- virtual QString name()
- {
- return id.toString();
- }
+ virtual QString name()
+ {
+ return id.toString();
+ }
- virtual QString typeString() const
- {
- return arch;
- }
+ virtual QString typeString() const
+ {
+ return arch;
+ }
- bool operator<(const JavaInstall & rhs);
- bool operator==(const JavaInstall & rhs);
- bool operator>(const JavaInstall & rhs);
+ bool operator<(const JavaInstall & rhs);
+ bool operator==(const JavaInstall & rhs);
+ bool operator>(const JavaInstall & rhs);
- JavaVersion id;
- QString arch;
- QString path;
- bool recommended = false;
+ JavaVersion id;
+ QString arch;
+ QString path;
+ bool recommended = false;
};
typedef std::shared_ptr<JavaInstall> JavaInstallPtr;
diff --git a/api/logic/java/JavaInstallList.cpp b/api/logic/java/JavaInstallList.cpp
index 9d2e2f8b..a71a7dbe 100644
--- a/api/logic/java/JavaInstallList.cpp
+++ b/api/logic/java/JavaInstallList.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,111 +31,111 @@ JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
shared_qobject_ptr<Task> JavaInstallList::getLoadTask()
{
- load();
- return getCurrentTask();
+ load();
+ return getCurrentTask();
}
shared_qobject_ptr<Task> JavaInstallList::getCurrentTask()
{
- if(m_status == Status::InProgress)
- {
- return m_loadTask;
- }
- return nullptr;
+ if(m_status == Status::InProgress)
+ {
+ return m_loadTask;
+ }
+ return nullptr;
}
void JavaInstallList::load()
{
- if(m_status != Status::InProgress)
- {
- m_status = Status::InProgress;
- m_loadTask = new JavaListLoadTask(this);
- m_loadTask->start();
- }
+ if(m_status != Status::InProgress)
+ {
+ m_status = Status::InProgress;
+ m_loadTask = new JavaListLoadTask(this);
+ m_loadTask->start();
+ }
}
const BaseVersionPtr JavaInstallList::at(int i) const
{
- return m_vlist.at(i);
+ return m_vlist.at(i);
}
bool JavaInstallList::isLoaded()
{
- return m_status == JavaInstallList::Status::Done;
+ return m_status == JavaInstallList::Status::Done;
}
int JavaInstallList::count() const
{
- return m_vlist.count();
+ return m_vlist.count();
}
QVariant JavaInstallList::data(const QModelIndex &index, int role) const
{
- if (!index.isValid())
- return QVariant();
-
- if (index.row() > count())
- return QVariant();
-
- auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]);
- switch (role)
- {
- case VersionPointerRole:
- return qVariantFromValue(m_vlist[index.row()]);
- case VersionIdRole:
- return version->descriptor();
- case VersionRole:
- return version->id.toString();
- case RecommendedRole:
- return version->recommended;
- case PathRole:
- return version->path;
- case ArchitectureRole:
- return version->arch;
- default:
- return QVariant();
- }
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.row() > count())
+ return QVariant();
+
+ auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]);
+ switch (role)
+ {
+ case VersionPointerRole:
+ return qVariantFromValue(m_vlist[index.row()]);
+ case VersionIdRole:
+ return version->descriptor();
+ case VersionRole:
+ return version->id.toString();
+ case RecommendedRole:
+ return version->recommended;
+ case PathRole:
+ return version->path;
+ case ArchitectureRole:
+ return version->arch;
+ default:
+ return QVariant();
+ }
}
BaseVersionList::RoleList JavaInstallList::providesRoles() const
{
- return {VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole};
+ return {VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole};
}
void JavaInstallList::updateListData(QList<BaseVersionPtr> versions)
{
- beginResetModel();
- m_vlist = versions;
- sortVersions();
- if(m_vlist.size())
- {
- auto best = std::dynamic_pointer_cast<JavaInstall>(m_vlist[0]);
- best->recommended = true;
- }
- endResetModel();
- m_status = Status::Done;
- m_loadTask.reset();
+ beginResetModel();
+ m_vlist = versions;
+ sortVersions();
+ if(m_vlist.size())
+ {
+ auto best = std::dynamic_pointer_cast<JavaInstall>(m_vlist[0]);
+ best->recommended = true;
+ }
+ endResetModel();
+ m_status = Status::Done;
+ m_loadTask.reset();
}
bool sortJavas(BaseVersionPtr left, BaseVersionPtr right)
{
- auto rleft = std::dynamic_pointer_cast<JavaInstall>(left);
- auto rright = std::dynamic_pointer_cast<JavaInstall>(right);
- return (*rleft) > (*rright);
+ auto rleft = std::dynamic_pointer_cast<JavaInstall>(left);
+ auto rright = std::dynamic_pointer_cast<JavaInstall>(right);
+ return (*rleft) > (*rright);
}
void JavaInstallList::sortVersions()
{
- beginResetModel();
- std::sort(m_vlist.begin(), m_vlist.end(), sortJavas);
- endResetModel();
+ beginResetModel();
+ std::sort(m_vlist.begin(), m_vlist.end(), sortJavas);
+ endResetModel();
}
JavaListLoadTask::JavaListLoadTask(JavaInstallList *vlist) : Task()
{
- m_list = vlist;
- m_currentRecommended = NULL;
+ m_list = vlist;
+ m_currentRecommended = NULL;
}
JavaListLoadTask::~JavaListLoadTask()
@@ -144,65 +144,65 @@ JavaListLoadTask::~JavaListLoadTask()
void JavaListLoadTask::executeTask()
{
- setStatus(tr("Detecting Java installations..."));
+ setStatus(tr("Detecting Java installations..."));
- JavaUtils ju;
- QList<QString> candidate_paths = ju.FindJavaPaths();
+ JavaUtils ju;
+ QList<QString> candidate_paths = ju.FindJavaPaths();
- m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection"));
- connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
- connect(m_job.get(), &Task::progress, this, &Task::setProgress);
+ m_job = new JavaCheckerJob("Java detection");
+ connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
+ connect(m_job.get(), &Task::progress, this, &Task::setProgress);
- qDebug() << "Probing the following Java paths: ";
- int id = 0;
- for(QString candidate : candidate_paths)
- {
- qDebug() << " " << candidate;
+ qDebug() << "Probing the following Java paths: ";
+ int id = 0;
+ for(QString candidate : candidate_paths)
+ {
+ qDebug() << " " << candidate;
- auto candidate_checker = new JavaChecker();
- candidate_checker->m_path = candidate;
- candidate_checker->m_id = id;
- m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
+ auto candidate_checker = new JavaChecker();
+ candidate_checker->m_path = candidate;
+ candidate_checker->m_id = id;
+ m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
- id++;
- }
+ id++;
+ }
- m_job->start();
+ m_job->start();
}
void JavaListLoadTask::javaCheckerFinished()
{
- QList<JavaInstallPtr> candidates;
- auto results = m_job->getResults();
-
- qDebug() << "Found the following valid Java installations:";
- for(JavaCheckResult result : results)
- {
- if(result.validity == JavaCheckResult::Validity::Valid)
- {
- JavaInstallPtr javaVersion(new JavaInstall());
-
- javaVersion->id = result.javaVersion;
- javaVersion->arch = result.mojangPlatform;
- javaVersion->path = result.path;
- candidates.append(javaVersion);
-
- qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path;
- }
- }
-
- QList<BaseVersionPtr> javas_bvp;
- for (auto java : candidates)
- {
- //qDebug() << java->id << java->arch << " at " << java->path;
- BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);
-
- if (bp_java)
- {
- javas_bvp.append(java);
- }
- }
-
- m_list->updateListData(javas_bvp);
- emitSucceeded();
+ QList<JavaInstallPtr> candidates;
+ auto results = m_job->getResults();
+
+ qDebug() << "Found the following valid Java installations:";
+ for(JavaCheckResult result : results)
+ {
+ if(result.validity == JavaCheckResult::Validity::Valid)
+ {
+ JavaInstallPtr javaVersion(new JavaInstall());
+
+ javaVersion->id = result.javaVersion;
+ javaVersion->arch = result.mojangPlatform;
+ javaVersion->path = result.path;
+ candidates.append(javaVersion);
+
+ qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path;
+ }
+ }
+
+ QList<BaseVersionPtr> javas_bvp;
+ for (auto java : candidates)
+ {
+ //qDebug() << java->id << java->arch << " at " << java->path;
+ BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);
+
+ if (bp_java)
+ {
+ javas_bvp.append(java);
+ }
+ }
+
+ m_list->updateListData(javas_bvp);
+ emitSucceeded();
}
diff --git a/api/logic/java/JavaInstallList.h b/api/logic/java/JavaInstallList.h
index 39f37b80..b98908f3 100644
--- a/api/logic/java/JavaInstallList.h
+++ b/api/logic/java/JavaInstallList.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,58 +24,60 @@
#include "JavaCheckerJob.h"
#include "JavaInstall.h"
+#include "QObjectPtr.h"
+
#include "multimc_logic_export.h"
class JavaListLoadTask;
class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList
{
- Q_OBJECT
- enum class Status
- {
- NotDone,
- InProgress,
- Done
- };
+ Q_OBJECT
+ enum class Status
+ {
+ NotDone,
+ InProgress,
+ Done
+ };
public:
- explicit JavaInstallList(QObject *parent = 0);
+ explicit JavaInstallList(QObject *parent = 0);
- shared_qobject_ptr<Task> getLoadTask() override;
- bool isLoaded() override;
- const BaseVersionPtr at(int i) const override;
- int count() const override;
- void sortVersions() override;
+ shared_qobject_ptr<Task> getLoadTask() override;
+ bool isLoaded() override;
+ const BaseVersionPtr at(int i) const override;
+ int count() const override;
+ void sortVersions() override;
- QVariant data(const QModelIndex &index, int role) const override;
- RoleList providesRoles() const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ RoleList providesRoles() const override;
public slots:
- void updateListData(QList<BaseVersionPtr> versions) override;
+ void updateListData(QList<BaseVersionPtr> versions) override;
protected:
- void load();
- shared_qobject_ptr<Task> getCurrentTask();
+ void load();
+ shared_qobject_ptr<Task> getCurrentTask();
protected:
- Status m_status = Status::NotDone;
- shared_qobject_ptr<JavaListLoadTask> m_loadTask;
- QList<BaseVersionPtr> m_vlist;
+ Status m_status = Status::NotDone;
+ shared_qobject_ptr<JavaListLoadTask> m_loadTask;
+ QList<BaseVersionPtr> m_vlist;
};
class JavaListLoadTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit JavaListLoadTask(JavaInstallList *vlist);
- ~JavaListLoadTask();
+ explicit JavaListLoadTask(JavaInstallList *vlist);
+ virtual ~JavaListLoadTask();
- void executeTask() override;
+ void executeTask() override;
public slots:
- void javaCheckerFinished();
+ void javaCheckerFinished();
protected:
- std::shared_ptr<JavaCheckerJob> m_job;
- JavaInstallList *m_list;
- JavaInstall *m_currentRecommended;
+ shared_qobject_ptr<JavaCheckerJob> m_job;
+ JavaInstallList *m_list;
+ JavaInstall *m_currentRecommended;
};
diff --git a/api/logic/java/JavaUtils.cpp b/api/logic/java/JavaUtils.cpp
index 4a77bc7e..1f9719a8 100644
--- a/api/logic/java/JavaUtils.cpp
+++ b/api/logic/java/JavaUtils.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,313 +31,315 @@ JavaUtils::JavaUtils()
{
}
+#ifdef Q_OS_LINUX
static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH)
{
- QDir mmcBin(QCoreApplication::applicationDirPath());
- auto items = LD_LIBRARY_PATH.split(':');
- QStringList final;
- for(auto & item: items)
- {
- QDir test(item);
- if(test == mmcBin)
- {
- qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item;
- continue;
- }
- final.append(item);
- }
- return final.join(':');
+ QDir mmcBin(QCoreApplication::applicationDirPath());
+ auto items = LD_LIBRARY_PATH.split(':');
+ QStringList final;
+ for(auto & item: items)
+ {
+ QDir test(item);
+ if(test == mmcBin)
+ {
+ qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item;
+ continue;
+ }
+ final.append(item);
+ }
+ return final.join(':');
}
+#endif
QProcessEnvironment CleanEnviroment()
{
- // prepare the process environment
- QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment();
- QProcessEnvironment env;
-
- QStringList ignored =
- {
- "JAVA_ARGS",
- "CLASSPATH",
- "CONFIGPATH",
- "JAVA_HOME",
- "JRE_HOME",
- "_JAVA_OPTIONS",
- "JAVA_OPTIONS",
- "JAVA_TOOL_OPTIONS"
- };
- for(auto key: rawenv.keys())
- {
- auto value = rawenv.value(key);
- // filter out dangerous java crap
- if(ignored.contains(key))
- {
- qDebug() << "Env: ignoring" << key << value;
- continue;
- }
- // filter MultiMC-related things
- if(key.startsWith("QT_"))
- {
- qDebug() << "Env: ignoring" << key << value;
- continue;
- }
+ // prepare the process environment
+ QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment();
+ QProcessEnvironment env;
+
+ QStringList ignored =
+ {
+ "JAVA_ARGS",
+ "CLASSPATH",
+ "CONFIGPATH",
+ "JAVA_HOME",
+ "JRE_HOME",
+ "_JAVA_OPTIONS",
+ "JAVA_OPTIONS",
+ "JAVA_TOOL_OPTIONS"
+ };
+ for(auto key: rawenv.keys())
+ {
+ auto value = rawenv.value(key);
+ // filter out dangerous java crap
+ if(ignored.contains(key))
+ {
+ qDebug() << "Env: ignoring" << key << value;
+ continue;
+ }
+ // filter MultiMC-related things
+ if(key.startsWith("QT_"))
+ {
+ qDebug() << "Env: ignoring" << key << value;
+ continue;
+ }
#ifdef Q_OS_LINUX
- // Do not pass LD_* variables to java. They were intended for MultiMC
- if(key.startsWith("LD_"))
- {
- qDebug() << "Env: ignoring" << key << value;
- continue;
- }
- // Strip IBus
- // IBus is a Linux IME framework. For some reason, it breaks MC?
- if (key == "XMODIFIERS" && value.contains(IBUS))
- {
- QString save = value;
- value.replace(IBUS, "");
- qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value;
- }
- if(key == "GAME_PRELOAD")
- {
- env.insert("LD_PRELOAD", value);
- continue;
- }
- if(key == "GAME_LIBRARY_PATH")
- {
- env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value));
- continue;
- }
+ // Do not pass LD_* variables to java. They were intended for MultiMC
+ if(key.startsWith("LD_"))
+ {
+ qDebug() << "Env: ignoring" << key << value;
+ continue;
+ }
+ // Strip IBus
+ // IBus is a Linux IME framework. For some reason, it breaks MC?
+ if (key == "XMODIFIERS" && value.contains(IBUS))
+ {
+ QString save = value;
+ value.replace(IBUS, "");
+ qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value;
+ }
+ if(key == "GAME_PRELOAD")
+ {
+ env.insert("LD_PRELOAD", value);
+ continue;
+ }
+ if(key == "GAME_LIBRARY_PATH")
+ {
+ env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value));
+ continue;
+ }
#endif
- // qDebug() << "Env: " << key << value;
- env.insert(key, value);
- }
+ // qDebug() << "Env: " << key << value;
+ env.insert(key, value);
+ }
#ifdef Q_OS_LINUX
- // HACK: Workaround for QTBUG42500
- if(!env.contains("LD_LIBRARY_PATH"))
- {
- env.insert("LD_LIBRARY_PATH", "");
- }
+ // HACK: Workaround for QTBUG42500
+ if(!env.contains("LD_LIBRARY_PATH"))
+ {
+ env.insert("LD_LIBRARY_PATH", "");
+ }
#endif
- return env;
+ return env;
}
JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch)
{
- JavaInstallPtr javaVersion(new JavaInstall());
+ JavaInstallPtr javaVersion(new JavaInstall());
- javaVersion->id = id;
- javaVersion->arch = arch;
- javaVersion->path = path;
+ javaVersion->id = id;
+ javaVersion->arch = arch;
+ javaVersion->path = path;
- return javaVersion;
+ return javaVersion;
}
JavaInstallPtr JavaUtils::GetDefaultJava()
{
- JavaInstallPtr javaVersion(new JavaInstall());
+ JavaInstallPtr javaVersion(new JavaInstall());
- javaVersion->id = "java";
- javaVersion->arch = "unknown";
+ javaVersion->id = "java";
+ javaVersion->arch = "unknown";
#if defined(Q_OS_WIN32)
- javaVersion->path = "javaw";
+ javaVersion->path = "javaw";
#else
- javaVersion->path = "java";
+ javaVersion->path = "java";
#endif
- return javaVersion;
+ return javaVersion;
}
#if defined(Q_OS_WIN32)
QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName)
{
- QList<JavaInstallPtr> javas;
-
- QString archType = "unknown";
- if (keyType == KEY_WOW64_64KEY)
- archType = "64";
- else if (keyType == KEY_WOW64_32KEY)
- archType = "32";
-
- HKEY jreKey;
- if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0,
- KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS)
- {
- // Read the current type version from the registry.
- // This will be used to find any key that contains the JavaHome value.
- char *value = new char[0];
- DWORD valueSz = 0;
- if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) ==
- ERROR_MORE_DATA)
- {
- value = new char[valueSz];
- RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz);
- }
-
- QString recommended = value;
-
- TCHAR subKeyName[255];
- DWORD subKeyNameSize, numSubKeys, retCode;
-
- // Get the number of subkeys
- RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL);
-
- // Iterate until RegEnumKeyEx fails
- if (numSubKeys > 0)
- {
- for (DWORD i = 0; i < numSubKeys; i++)
- {
- subKeyNameSize = 255;
- retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL,
- NULL);
- if (retCode == ERROR_SUCCESS)
- {
- // Now open the registry key for the version that we just got.
- QString newKeyName = keyName + "\\" + subKeyName;
-
- HKEY newKey;
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0,
- KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS)
- {
- // Read the JavaHome value to find where Java is installed.
- value = new char[0];
- valueSz = 0;
- if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
- &valueSz) == ERROR_MORE_DATA)
- {
- value = new char[valueSz];
- RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
- &valueSz);
-
- // Now, we construct the version object and add it to the list.
- JavaInstallPtr javaVersion(new JavaInstall());
-
- javaVersion->id = subKeyName;
- javaVersion->arch = archType;
- javaVersion->path =
- QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe");
- javas.append(javaVersion);
- }
-
- RegCloseKey(newKey);
- }
- }
- }
- }
-
- RegCloseKey(jreKey);
- }
-
- return javas;
+ QList<JavaInstallPtr> javas;
+
+ QString archType = "unknown";
+ if (keyType == KEY_WOW64_64KEY)
+ archType = "64";
+ else if (keyType == KEY_WOW64_32KEY)
+ archType = "32";
+
+ HKEY jreKey;
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0,
+ KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS)
+ {
+ // Read the current type version from the registry.
+ // This will be used to find any key that contains the JavaHome value.
+ char *value = new char[0];
+ DWORD valueSz = 0;
+ if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) ==
+ ERROR_MORE_DATA)
+ {
+ value = new char[valueSz];
+ RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz);
+ }
+
+ QString recommended = value;
+
+ TCHAR subKeyName[255];
+ DWORD subKeyNameSize, numSubKeys, retCode;
+
+ // Get the number of subkeys
+ RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL);
+
+ // Iterate until RegEnumKeyEx fails
+ if (numSubKeys > 0)
+ {
+ for (DWORD i = 0; i < numSubKeys; i++)
+ {
+ subKeyNameSize = 255;
+ retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL,
+ NULL);
+ if (retCode == ERROR_SUCCESS)
+ {
+ // Now open the registry key for the version that we just got.
+ QString newKeyName = keyName + "\\" + subKeyName;
+
+ HKEY newKey;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0,
+ KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS)
+ {
+ // Read the JavaHome value to find where Java is installed.
+ value = new char[0];
+ valueSz = 0;
+ if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
+ &valueSz) == ERROR_MORE_DATA)
+ {
+ value = new char[valueSz];
+ RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
+ &valueSz);
+
+ // Now, we construct the version object and add it to the list.
+ JavaInstallPtr javaVersion(new JavaInstall());
+
+ javaVersion->id = subKeyName;
+ javaVersion->arch = archType;
+ javaVersion->path =
+ QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe");
+ javas.append(javaVersion);
+ }
+
+ RegCloseKey(newKey);
+ }
+ }
+ }
+ }
+
+ RegCloseKey(jreKey);
+ }
+
+ return javas;
}
QList<QString> JavaUtils::FindJavaPaths()
{
- QList<JavaInstallPtr> java_candidates;
-
- QList<JavaInstallPtr> JRE64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
- QList<JavaInstallPtr> JDK64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
- QList<JavaInstallPtr> JRE32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
- QList<JavaInstallPtr> JDK32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
-
- java_candidates.append(JRE64s);
- java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe"));
- java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
- java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
- java_candidates.append(JDK64s);
- java_candidates.append(JRE32s);
- java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"));
- java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
- java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
- java_candidates.append(JDK32s);
- java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
-
- QList<QString> candidates;
- for(JavaInstallPtr java_candidate : java_candidates)
- {
- if(!candidates.contains(java_candidate->path))
- {
- candidates.append(java_candidate->path);
- }
- }
-
- return candidates;
+ QList<JavaInstallPtr> java_candidates;
+
+ QList<JavaInstallPtr> JRE64s = this->FindJavaFromRegistryKey(
+ KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
+ QList<JavaInstallPtr> JDK64s = this->FindJavaFromRegistryKey(
+ KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
+ QList<JavaInstallPtr> JRE32s = this->FindJavaFromRegistryKey(
+ KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
+ QList<JavaInstallPtr> JDK32s = this->FindJavaFromRegistryKey(
+ KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
+
+ java_candidates.append(JRE64s);
+ java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
+ java_candidates.append(JDK64s);
+ java_candidates.append(JRE32s);
+ java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
+ java_candidates.append(JDK32s);
+ java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
+
+ QList<QString> candidates;
+ for(JavaInstallPtr java_candidate : java_candidates)
+ {
+ if(!candidates.contains(java_candidate->path))
+ {
+ candidates.append(java_candidate->path);
+ }
+ }
+
+ return candidates;
}
#elif defined(Q_OS_MAC)
QList<QString> JavaUtils::FindJavaPaths()
{
- QList<QString> javas;
- javas.append(this->GetDefaultJava()->path);
- javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java");
- javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java");
- javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java");
- QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/");
- QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
- foreach (const QString &java, libraryJVMJavas) {
- javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
- javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java");
- }
- QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/");
- QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
- foreach (const QString &java, systemLibraryJVMJavas) {
- javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
- javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
- }
- return javas;
+ QList<QString> javas;
+ javas.append(this->GetDefaultJava()->path);
+ javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java");
+ javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java");
+ javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java");
+ QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/");
+ QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QString &java, libraryJVMJavas) {
+ javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
+ javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java");
+ }
+ QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/");
+ QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QString &java, systemLibraryJVMJavas) {
+ javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
+ javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
+ }
+ return javas;
}
#elif defined(Q_OS_LINUX)
QList<QString> JavaUtils::FindJavaPaths()
{
- qDebug() << "Linux Java detection incomplete - defaulting to \"java\"";
-
- QList<QString> javas;
- javas.append(this->GetDefaultJava()->path);
- auto scanJavaDir = [&](const QString & dirPath)
- {
- QDir dir(dirPath);
- if(!dir.exists())
- return;
- auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
- for(auto & entry: entries)
- {
-
- QString prefix;
- if(entry.isAbsolute())
- {
- prefix = entry.absoluteFilePath();
- }
- else
- {
- prefix = entry.filePath();
- }
-
- javas.append(FS::PathCombine(prefix, "jre/bin/java"));
- javas.append(FS::PathCombine(prefix, "bin/java"));
- }
- };
- // oracle RPMs
- scanJavaDir("/usr/java");
- // general locations used by distro packaging
- scanJavaDir("/usr/lib/jvm");
- scanJavaDir("/usr/lib32/jvm");
- // javas stored in MultiMC's folder
- scanJavaDir("java");
- return javas;
+ qDebug() << "Linux Java detection incomplete - defaulting to \"java\"";
+
+ QList<QString> javas;
+ javas.append(this->GetDefaultJava()->path);
+ auto scanJavaDir = [&](const QString & dirPath)
+ {
+ QDir dir(dirPath);
+ if(!dir.exists())
+ return;
+ auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
+ for(auto & entry: entries)
+ {
+
+ QString prefix;
+ if(entry.isAbsolute())
+ {
+ prefix = entry.absoluteFilePath();
+ }
+ else
+ {
+ prefix = entry.filePath();
+ }
+
+ javas.append(FS::PathCombine(prefix, "jre/bin/java"));
+ javas.append(FS::PathCombine(prefix, "bin/java"));
+ }
+ };
+ // oracle RPMs
+ scanJavaDir("/usr/java");
+ // general locations used by distro packaging
+ scanJavaDir("/usr/lib/jvm");
+ scanJavaDir("/usr/lib32/jvm");
+ // javas stored in MultiMC's folder
+ scanJavaDir("java");
+ return javas;
}
#else
QList<QString> JavaUtils::FindJavaPaths()
{
- qDebug() << "Unknown operating system build - defaulting to \"java\"";
+ qDebug() << "Unknown operating system build - defaulting to \"java\"";
- QList<QString> javas;
- javas.append(this->GetDefaultJava()->path);
+ QList<QString> javas;
+ javas.append(this->GetDefaultJava()->path);
- return javas;
+ return javas;
}
#endif
diff --git a/api/logic/java/JavaUtils.h b/api/logic/java/JavaUtils.h
index b43e93cf..100d942f 100644
--- a/api/logic/java/JavaUtils.h
+++ b/api/logic/java/JavaUtils.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,15 +30,15 @@ QProcessEnvironment CleanEnviroment();
class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- JavaUtils();
+ JavaUtils();
- JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown");
- QList<QString> FindJavaPaths();
- JavaInstallPtr GetDefaultJava();
+ JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown");
+ QList<QString> FindJavaPaths();
+ JavaInstallPtr GetDefaultJava();
#ifdef Q_OS_WIN
- QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName);
+ QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName);
#endif
};
diff --git a/api/logic/java/JavaVersion.cpp b/api/logic/java/JavaVersion.cpp
index 27050da3..179ccd8d 100644
--- a/api/logic/java/JavaVersion.cpp
+++ b/api/logic/java/JavaVersion.cpp
@@ -6,116 +6,116 @@
JavaVersion & JavaVersion::operator=(const QString & javaVersionString)
{
- m_string = javaVersionString;
+ m_string = javaVersionString;
- auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int
- {
- auto str = match.captured(what);
- if(str.isEmpty())
- {
- return 0;
- }
- return str.toInt();
- };
+ auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int
+ {
+ auto str = match.captured(what);
+ if(str.isEmpty())
+ {
+ return 0;
+ }
+ return str.toInt();
+ };
- QRegularExpression pattern;
- if(javaVersionString.startsWith("1."))
- {
- pattern = QRegularExpression ("1[.](?<major>[0-9]+)([.](?<minor>[0-9]+))?(_(?<security>[0-9]+)?)?(-(?<prerelease>[a-zA-Z0-9]+))?");
- }
- else
- {
- pattern = QRegularExpression("(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?");
- }
+ QRegularExpression pattern;
+ if(javaVersionString.startsWith("1."))
+ {
+ pattern = QRegularExpression ("1[.](?<major>[0-9]+)([.](?<minor>[0-9]+))?(_(?<security>[0-9]+)?)?(-(?<prerelease>[a-zA-Z0-9]+))?");
+ }
+ else
+ {
+ pattern = QRegularExpression("(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?");
+ }
- auto match = pattern.match(m_string);
- m_parseable = match.hasMatch();
- m_major = getCapturedInteger(match, "major");
- m_minor = getCapturedInteger(match, "minor");
- m_security = getCapturedInteger(match, "security");
- m_prerelease = match.captured("prerelease");
- return *this;
+ auto match = pattern.match(m_string);
+ m_parseable = match.hasMatch();
+ m_major = getCapturedInteger(match, "major");
+ m_minor = getCapturedInteger(match, "minor");
+ m_security = getCapturedInteger(match, "security");
+ m_prerelease = match.captured("prerelease");
+ return *this;
}
JavaVersion::JavaVersion(const QString &rhs)
{
- operator=(rhs);
+ operator=(rhs);
}
QString JavaVersion::toString()
{
- return m_string;
+ return m_string;
}
bool JavaVersion::requiresPermGen()
{
- if(m_parseable)
- {
- return m_major < 8;
- }
- return true;
+ if(m_parseable)
+ {
+ return m_major < 8;
+ }
+ return true;
}
bool JavaVersion::operator<(const JavaVersion &rhs)
{
- if(m_parseable && rhs.m_parseable)
- {
- auto major = m_major;
- auto rmajor = rhs.m_major;
+ if(m_parseable && rhs.m_parseable)
+ {
+ auto major = m_major;
+ auto rmajor = rhs.m_major;
- // HACK: discourage using java 9
- if(major > 8)
- major = -major;
- if(rmajor > 8)
- rmajor = -rmajor;
+ // HACK: discourage using java 9
+ if(major > 8)
+ major = -major;
+ if(rmajor > 8)
+ rmajor = -rmajor;
- if(major < rmajor)
- return true;
- if(major > rmajor)
- return false;
- if(m_minor < rhs.m_minor)
- return true;
- if(m_minor > rhs.m_minor)
- return false;
- if(m_security < rhs.m_security)
- return true;
- if(m_security > rhs.m_security)
- return false;
+ if(major < rmajor)
+ return true;
+ if(major > rmajor)
+ return false;
+ if(m_minor < rhs.m_minor)
+ return true;
+ if(m_minor > rhs.m_minor)
+ return false;
+ if(m_security < rhs.m_security)
+ return true;
+ if(m_security > rhs.m_security)
+ return false;
- // everything else being equal, consider prerelease status
- bool thisPre = !m_prerelease.isEmpty();
- bool rhsPre = !rhs.m_prerelease.isEmpty();
- if(thisPre && !rhsPre)
- {
- // this is a prerelease and the other one isn't -> lesser
- return true;
- }
- else if(!thisPre && rhsPre)
- {
- // this isn't a prerelease and the other one is -> greater
- return false;
- }
- else if(thisPre && rhsPre)
- {
- // both are prereleases - use natural compare...
- return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;
- }
- // neither is prerelease, so they are the same -> this cannot be less than rhs
- return false;
- }
- else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;
+ // everything else being equal, consider prerelease status
+ bool thisPre = !m_prerelease.isEmpty();
+ bool rhsPre = !rhs.m_prerelease.isEmpty();
+ if(thisPre && !rhsPre)
+ {
+ // this is a prerelease and the other one isn't -> lesser
+ return true;
+ }
+ else if(!thisPre && rhsPre)
+ {
+ // this isn't a prerelease and the other one is -> greater
+ return false;
+ }
+ else if(thisPre && rhsPre)
+ {
+ // both are prereleases - use natural compare...
+ return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;
+ }
+ // neither is prerelease, so they are the same -> this cannot be less than rhs
+ return false;
+ }
+ else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;
}
bool JavaVersion::operator==(const JavaVersion &rhs)
{
- if(m_parseable && rhs.m_parseable)
- {
- return m_major == rhs.m_major && m_minor == rhs.m_minor && m_security == rhs.m_security && m_prerelease == rhs.m_prerelease;
- }
- return m_string == rhs.m_string;
+ if(m_parseable && rhs.m_parseable)
+ {
+ return m_major == rhs.m_major && m_minor == rhs.m_minor && m_security == rhs.m_security && m_prerelease == rhs.m_prerelease;
+ }
+ return m_string == rhs.m_string;
}
bool JavaVersion::operator>(const JavaVersion &rhs)
{
- return (!operator<(rhs)) && (!operator==(rhs));
+ return (!operator<(rhs)) && (!operator==(rhs));
}
diff --git a/api/logic/java/JavaVersion.h b/api/logic/java/JavaVersion.h
index de13998c..8589c21a 100644
--- a/api/logic/java/JavaVersion.h
+++ b/api/logic/java/JavaVersion.h
@@ -5,46 +5,46 @@
// NOTE: apparently the GNU C library pollutes the global namespace with these... undef them.
#ifdef major
- #undef major
+ #undef major
#endif
#ifdef minor
- #undef minor
+ #undef minor
#endif
class MULTIMC_LOGIC_EXPORT JavaVersion
{
- friend class JavaVersionTest;
+ friend class JavaVersionTest;
public:
- JavaVersion() {};
- JavaVersion(const QString & rhs);
-
- JavaVersion & operator=(const QString & rhs);
-
- bool operator<(const JavaVersion & rhs);
- bool operator==(const JavaVersion & rhs);
- bool operator>(const JavaVersion & rhs);
-
- bool requiresPermGen();
-
- QString toString();
-
- int major()
- {
- return m_major;
- }
- int minor()
- {
- return m_minor;
- }
- int security()
- {
- return m_security;
- }
+ JavaVersion() {};
+ JavaVersion(const QString & rhs);
+
+ JavaVersion & operator=(const QString & rhs);
+
+ bool operator<(const JavaVersion & rhs);
+ bool operator==(const JavaVersion & rhs);
+ bool operator>(const JavaVersion & rhs);
+
+ bool requiresPermGen();
+
+ QString toString();
+
+ int major()
+ {
+ return m_major;
+ }
+ int minor()
+ {
+ return m_minor;
+ }
+ int security()
+ {
+ return m_security;
+ }
private:
- QString m_string;
- int m_major = 0;
- int m_minor = 0;
- int m_security = 0;
- bool m_parseable = false;
- QString m_prerelease;
+ QString m_string;
+ int m_major = 0;
+ int m_minor = 0;
+ int m_security = 0;
+ bool m_parseable = false;
+ QString m_prerelease;
};
diff --git a/api/logic/java/JavaVersion_test.cpp b/api/logic/java/JavaVersion_test.cpp
index e719ffc8..10ae13a7 100644
--- a/api/logic/java/JavaVersion_test.cpp
+++ b/api/logic/java/JavaVersion_test.cpp
@@ -5,110 +5,110 @@
class JavaVersionTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
private
slots:
- void test_Parse_data()
- {
- QTest::addColumn<QString>("string");
- QTest::addColumn<int>("major");
- QTest::addColumn<int>("minor");
- QTest::addColumn<int>("security");
- QTest::addColumn<QString>("prerelease");
+ void test_Parse_data()
+ {
+ QTest::addColumn<QString>("string");
+ QTest::addColumn<int>("major");
+ QTest::addColumn<int>("minor");
+ QTest::addColumn<int>("security");
+ QTest::addColumn<QString>("prerelease");
- QTest::newRow("old format") << "1.6.0_33" << 6 << 0 << 33 << QString();
- QTest::newRow("old format prerelease") << "1.9.0_1-ea" << 9 << 0 << 1 << "ea";
+ QTest::newRow("old format") << "1.6.0_33" << 6 << 0 << 33 << QString();
+ QTest::newRow("old format prerelease") << "1.9.0_1-ea" << 9 << 0 << 1 << "ea";
- QTest::newRow("new format major") << "9" << 9 << 0 << 0 << QString();
- QTest::newRow("new format minor") << "9.1" << 9 << 1 << 0 << QString();
- QTest::newRow("new format security") << "9.0.1" << 9 << 0 << 1 << QString();
- QTest::newRow("new format prerelease") << "9-ea" << 9 << 0 << 0 << "ea";
- QTest::newRow("new format long prerelease") << "9.0.1-ea" << 9 << 0 << 1 << "ea";
- }
- void test_Parse()
- {
- QFETCH(QString, string);
- QFETCH(int, major);
- QFETCH(int, minor);
- QFETCH(int, security);
- QFETCH(QString, prerelease);
+ QTest::newRow("new format major") << "9" << 9 << 0 << 0 << QString();
+ QTest::newRow("new format minor") << "9.1" << 9 << 1 << 0 << QString();
+ QTest::newRow("new format security") << "9.0.1" << 9 << 0 << 1 << QString();
+ QTest::newRow("new format prerelease") << "9-ea" << 9 << 0 << 0 << "ea";
+ QTest::newRow("new format long prerelease") << "9.0.1-ea" << 9 << 0 << 1 << "ea";
+ }
+ void test_Parse()
+ {
+ QFETCH(QString, string);
+ QFETCH(int, major);
+ QFETCH(int, minor);
+ QFETCH(int, security);
+ QFETCH(QString, prerelease);
- JavaVersion test(string);
- QCOMPARE(test.m_string, string);
- QCOMPARE(test.toString(), string);
- QCOMPARE(test.m_major, major);
- QCOMPARE(test.m_minor, minor);
- QCOMPARE(test.m_security, security);
- QCOMPARE(test.m_prerelease, prerelease);
- }
+ JavaVersion test(string);
+ QCOMPARE(test.m_string, string);
+ QCOMPARE(test.toString(), string);
+ QCOMPARE(test.m_major, major);
+ QCOMPARE(test.m_minor, minor);
+ QCOMPARE(test.m_security, security);
+ QCOMPARE(test.m_prerelease, prerelease);
+ }
- void test_Sort_data()
- {
- QTest::addColumn<QString>("lhs");
- QTest::addColumn<QString>("rhs");
- QTest::addColumn<bool>("smaller");
- QTest::addColumn<bool>("equal");
- QTest::addColumn<bool>("bigger");
+ void test_Sort_data()
+ {
+ QTest::addColumn<QString>("lhs");
+ QTest::addColumn<QString>("rhs");
+ QTest::addColumn<bool>("smaller");
+ QTest::addColumn<bool>("equal");
+ QTest::addColumn<bool>("bigger");
- // old format and new format equivalence
- QTest::newRow("1.6.0_33 == 6.0.33") << "1.6.0_33" << "6.0.33" << false << true << false;
- // old format major version
- QTest::newRow("1.5.0_33 < 1.6.0_33") << "1.5.0_33" << "1.6.0_33" << true << false << false;
- // new format - first release vs first security patch
- QTest::newRow("9 < 9.0.1") << "9" << "9.0.1" << true << false << false;
- QTest::newRow("9.0.1 > 9") << "9.0.1" << "9" << false << false << true;
- // new format - first minor vs first release/security patch
- QTest::newRow("9.1 > 9.0.1") << "9.1" << "9.0.1" << false << false << true;
- QTest::newRow("9.0.1 < 9.1") << "9.0.1" << "9.1" << true << false << false;
- QTest::newRow("9.1 > 9") << "9.1" << "9" << false << false << true;
- QTest::newRow("9 > 9.1") << "9" << "9.1" << true << false << false;
- // new format - omitted numbers
- QTest::newRow("9 == 9.0") << "9" << "9.0" << false << true << false;
- QTest::newRow("9 == 9.0.0") << "9" << "9.0.0" << false << true << false;
- QTest::newRow("9.0 == 9.0.0") << "9.0" << "9.0.0" << false << true << false;
- // early access and prereleases compared to final release
- QTest::newRow("9-ea < 9") << "9-ea" << "9" << true << false << false;
- QTest::newRow("9 < 9.0.1-ea") << "9" << "9.0.1-ea" << true << false << false;
- QTest::newRow("9.0.1-ea > 9") << "9.0.1-ea" << "9" << false << false << true;
- // prerelease difference only testing
- QTest::newRow("9-1 == 9-1") << "9-1" << "9-1" << false << true << false;
- QTest::newRow("9-1 < 9-2") << "9-1" << "9-2" << true << false << false;
- QTest::newRow("9-5 < 9-20") << "9-5" << "9-20" << true << false << false;
- QTest::newRow("9-rc1 < 9-rc2") << "9-rc1" << "9-rc2" << true << false << false;
- QTest::newRow("9-rc5 < 9-rc20") << "9-rc5" << "9-rc20" << true << false << false;
- QTest::newRow("9-rc < 9-rc2") << "9-rc" << "9-rc2" << true << false << false;
- QTest::newRow("9-ea < 9-rc") << "9-ea" << "9-rc" << true << false << false;
- }
- void test_Sort()
- {
- QFETCH(QString, lhs);
- QFETCH(QString, rhs);
- QFETCH(bool, smaller);
- QFETCH(bool, equal);
- QFETCH(bool, bigger);
- JavaVersion lver(lhs);
- JavaVersion rver(rhs);
- QCOMPARE(lver < rver, smaller);
- QCOMPARE(lver == rver, equal);
- QCOMPARE(lver > rver, bigger);
- }
- void test_PermGen_data()
- {
- QTest::addColumn<QString>("version");
- QTest::addColumn<bool>("needs_permgen");
- QTest::newRow("1.6.0_33") << "1.6.0_33" << true;
- QTest::newRow("1.7.0_60") << "1.7.0_60" << true;
- QTest::newRow("1.8.0_22") << "1.8.0_22" << false;
- QTest::newRow("9-ea") << "9-ea" << false;
- QTest::newRow("9.2.4") << "9.2.4" << false;
- }
- void test_PermGen()
- {
- QFETCH(QString, version);
- QFETCH(bool, needs_permgen);
- JavaVersion v(version);
- QCOMPARE(needs_permgen, v.requiresPermGen());
- }
+ // old format and new format equivalence
+ QTest::newRow("1.6.0_33 == 6.0.33") << "1.6.0_33" << "6.0.33" << false << true << false;
+ // old format major version
+ QTest::newRow("1.5.0_33 < 1.6.0_33") << "1.5.0_33" << "1.6.0_33" << true << false << false;
+ // new format - first release vs first security patch
+ QTest::newRow("9 < 9.0.1") << "9" << "9.0.1" << true << false << false;
+ QTest::newRow("9.0.1 > 9") << "9.0.1" << "9" << false << false << true;
+ // new format - first minor vs first release/security patch
+ QTest::newRow("9.1 > 9.0.1") << "9.1" << "9.0.1" << false << false << true;
+ QTest::newRow("9.0.1 < 9.1") << "9.0.1" << "9.1" << true << false << false;
+ QTest::newRow("9.1 > 9") << "9.1" << "9" << false << false << true;
+ QTest::newRow("9 > 9.1") << "9" << "9.1" << true << false << false;
+ // new format - omitted numbers
+ QTest::newRow("9 == 9.0") << "9" << "9.0" << false << true << false;
+ QTest::newRow("9 == 9.0.0") << "9" << "9.0.0" << false << true << false;
+ QTest::newRow("9.0 == 9.0.0") << "9.0" << "9.0.0" << false << true << false;
+ // early access and prereleases compared to final release
+ QTest::newRow("9-ea < 9") << "9-ea" << "9" << true << false << false;
+ QTest::newRow("9 < 9.0.1-ea") << "9" << "9.0.1-ea" << true << false << false;
+ QTest::newRow("9.0.1-ea > 9") << "9.0.1-ea" << "9" << false << false << true;
+ // prerelease difference only testing
+ QTest::newRow("9-1 == 9-1") << "9-1" << "9-1" << false << true << false;
+ QTest::newRow("9-1 < 9-2") << "9-1" << "9-2" << true << false << false;
+ QTest::newRow("9-5 < 9-20") << "9-5" << "9-20" << true << false << false;
+ QTest::newRow("9-rc1 < 9-rc2") << "9-rc1" << "9-rc2" << true << false << false;
+ QTest::newRow("9-rc5 < 9-rc20") << "9-rc5" << "9-rc20" << true << false << false;
+ QTest::newRow("9-rc < 9-rc2") << "9-rc" << "9-rc2" << true << false << false;
+ QTest::newRow("9-ea < 9-rc") << "9-ea" << "9-rc" << true << false << false;
+ }
+ void test_Sort()
+ {
+ QFETCH(QString, lhs);
+ QFETCH(QString, rhs);
+ QFETCH(bool, smaller);
+ QFETCH(bool, equal);
+ QFETCH(bool, bigger);
+ JavaVersion lver(lhs);
+ JavaVersion rver(rhs);
+ QCOMPARE(lver < rver, smaller);
+ QCOMPARE(lver == rver, equal);
+ QCOMPARE(lver > rver, bigger);
+ }
+ void test_PermGen_data()
+ {
+ QTest::addColumn<QString>("version");
+ QTest::addColumn<bool>("needs_permgen");
+ QTest::newRow("1.6.0_33") << "1.6.0_33" << true;
+ QTest::newRow("1.7.0_60") << "1.7.0_60" << true;
+ QTest::newRow("1.8.0_22") << "1.8.0_22" << false;
+ QTest::newRow("9-ea") << "9-ea" << false;
+ QTest::newRow("9.2.4") << "9.2.4" << false;
+ }
+ void test_PermGen()
+ {
+ QFETCH(QString, version);
+ QFETCH(bool, needs_permgen);
+ JavaVersion v(version);
+ QCOMPARE(needs_permgen, v.requiresPermGen());
+ }
};
QTEST_GUILESS_MAIN(JavaVersionTest)
diff --git a/api/logic/java/launch/CheckJava.cpp b/api/logic/java/launch/CheckJava.cpp
index 24f26682..b75c6dc6 100644
--- a/api/logic/java/launch/CheckJava.cpp
+++ b/api/logic/java/launch/CheckJava.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,115 +22,115 @@
void CheckJava::executeTask()
{
- auto instance = m_parent->instance();
- auto settings = instance->settings();
- m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString());
- bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool();
+ auto instance = m_parent->instance();
+ auto settings = instance->settings();
+ m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString());
+ bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool();
- auto realJavaPath = QStandardPaths::findExecutable(m_javaPath);
- if (realJavaPath.isEmpty())
- {
- if (perInstance)
- {
- emit logLine(
- tr("The java binary \"%1\" couldn't be found. Please fix the java path "
- "override in the instance's settings or disable it.").arg(m_javaPath),
- MessageLevel::Warning);
- }
- else
- {
- emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in "
- "the settings.").arg(m_javaPath),
- MessageLevel::Warning);
- }
- emitFailed(tr("Java path is not valid."));
- return;
- }
- else
- {
- emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::MultiMC);
- }
+ auto realJavaPath = QStandardPaths::findExecutable(m_javaPath);
+ if (realJavaPath.isEmpty())
+ {
+ if (perInstance)
+ {
+ emit logLine(
+ tr("The java binary \"%1\" couldn't be found. Please fix the java path "
+ "override in the instance's settings or disable it.").arg(m_javaPath),
+ MessageLevel::Warning);
+ }
+ else
+ {
+ emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in "
+ "the settings.").arg(m_javaPath),
+ MessageLevel::Warning);
+ }
+ emitFailed(tr("Java path is not valid."));
+ return;
+ }
+ else
+ {
+ emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::MultiMC);
+ }
- QFileInfo javaInfo(realJavaPath);
- qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
- auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
- auto storedArchitecture = settings->get("JavaArchitecture").toString();
- auto storedVersion = settings->get("JavaVersion").toString();
- m_javaUnixTime = javaUnixTime;
- // if timestamps are not the same, or something is missing, check!
- if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
- {
- m_JavaChecker = std::make_shared<JavaChecker>();
- emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
- connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
- m_JavaChecker->m_path = realJavaPath;
- m_JavaChecker->performCheck();
- return;
- }
- else
- {
- auto verString = instance->settings()->get("JavaVersion").toString();
- auto archString = instance->settings()->get("JavaArchitecture").toString();
- printJavaInfo(verString, archString);
- }
- emitSucceeded();
+ QFileInfo javaInfo(realJavaPath);
+ qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
+ auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
+ auto storedArchitecture = settings->get("JavaArchitecture").toString();
+ auto storedVersion = settings->get("JavaVersion").toString();
+ m_javaUnixTime = javaUnixTime;
+ // if timestamps are not the same, or something is missing, check!
+ if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
+ {
+ m_JavaChecker = new JavaChecker();
+ emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
+ connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
+ m_JavaChecker->m_path = realJavaPath;
+ m_JavaChecker->performCheck();
+ return;
+ }
+ else
+ {
+ auto verString = instance->settings()->get("JavaVersion").toString();
+ auto archString = instance->settings()->get("JavaArchitecture").toString();
+ printJavaInfo(verString, archString);
+ }
+ emitSucceeded();
}
void CheckJava::checkJavaFinished(JavaCheckResult result)
{
- switch (result.validity)
- {
- case JavaCheckResult::Validity::Errored:
- {
- // Error message displayed if java can't start
- emit logLine(tr("Could not start java:"), MessageLevel::Error);
- emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
- emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC);
- printSystemInfo(false, false);
- emitFailed(tr("Could not start java!"));
- return;
- }
- case JavaCheckResult::Validity::ReturnedInvalidData:
- {
- emit logLine(tr("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
- emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
- emit logLine("\nMinecraft might not start properly.", MessageLevel::MultiMC);
- printSystemInfo(false, false);
- emitSucceeded();
- return;
- }
- case JavaCheckResult::Validity::Valid:
- {
- auto instance = m_parent->instance();
- printJavaInfo(result.javaVersion.toString(), result.mojangPlatform);
- instance->settings()->set("JavaVersion", result.javaVersion.toString());
- instance->settings()->set("JavaArchitecture", result.mojangPlatform);
- instance->settings()->set("JavaTimestamp", m_javaUnixTime);
- emitSucceeded();
- return;
- }
- }
+ switch (result.validity)
+ {
+ case JavaCheckResult::Validity::Errored:
+ {
+ // Error message displayed if java can't start
+ emit logLine(tr("Could not start java:"), MessageLevel::Error);
+ emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
+ emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC);
+ printSystemInfo(false, false);
+ emitFailed(tr("Could not start java!"));
+ return;
+ }
+ case JavaCheckResult::Validity::ReturnedInvalidData:
+ {
+ emit logLine(tr("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
+ emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
+ emit logLine("\nMinecraft might not start properly.", MessageLevel::MultiMC);
+ printSystemInfo(false, false);
+ emitSucceeded();
+ return;
+ }
+ case JavaCheckResult::Validity::Valid:
+ {
+ auto instance = m_parent->instance();
+ printJavaInfo(result.javaVersion.toString(), result.mojangPlatform);
+ instance->settings()->set("JavaVersion", result.javaVersion.toString());
+ instance->settings()->set("JavaArchitecture", result.mojangPlatform);
+ instance->settings()->set("JavaTimestamp", m_javaUnixTime);
+ emitSucceeded();
+ return;
+ }
+ }
}
void CheckJava::printJavaInfo(const QString& version, const QString& architecture)
{
- emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC);
- printSystemInfo(true, architecture == "64");
+ emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC);
+ printSystemInfo(true, architecture == "64");
}
void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit)
{
- auto cpu64 = Sys::isCPU64bit();
- auto system64 = Sys::isSystem64bit();
- if(cpu64 != system64)
- {
- emit logLine(tr("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
- }
- if(javaIsKnown)
- {
- if(javaIs64bit != system64)
- {
- emit logLine(tr("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
- }
- }
+ auto cpu64 = Sys::isCPU64bit();
+ auto system64 = Sys::isSystem64bit();
+ if(cpu64 != system64)
+ {
+ emit logLine(tr("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
+ }
+ if(javaIsKnown)
+ {
+ if(javaIs64bit != system64)
+ {
+ emit logLine(tr("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
+ }
+ }
}
diff --git a/api/logic/java/launch/CheckJava.h b/api/logic/java/launch/CheckJava.h
index 82508cd4..f0dd2308 100644
--- a/api/logic/java/launch/CheckJava.h
+++ b/api/logic/java/launch/CheckJava.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,25 +21,25 @@
class CheckJava: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit CheckJava(LaunchTask *parent) :LaunchStep(parent){};
- virtual ~CheckJava() {};
+ explicit CheckJava(LaunchTask *parent) :LaunchStep(parent){};
+ virtual ~CheckJava() {};
- virtual void executeTask();
- virtual bool canAbort() const
- {
- return false;
- }
+ virtual void executeTask();
+ virtual bool canAbort() const
+ {
+ return false;
+ }
private slots:
- void checkJavaFinished(JavaCheckResult result);
+ void checkJavaFinished(JavaCheckResult result);
private:
- void printJavaInfo(const QString & version, const QString & architecture);
- void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
+ void printJavaInfo(const QString & version, const QString & architecture);
+ void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
private:
- QString m_javaPath;
- qlonglong m_javaUnixTime;
- JavaCheckerPtr m_JavaChecker;
+ QString m_javaPath;
+ qlonglong m_javaUnixTime;
+ JavaCheckerPtr m_JavaChecker;
};
diff --git a/api/logic/launch/LaunchStep.cpp b/api/logic/launch/LaunchStep.cpp
index 01f72a0a..bcaca406 100644
--- a/api/logic/launch/LaunchStep.cpp
+++ b/api/logic/launch/LaunchStep.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,10 @@
void LaunchStep::bind(LaunchTask *parent)
{
- m_parent = parent;
- connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch);
- connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine);
- connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines);
- connect(this, &LaunchStep::finished, parent, &LaunchTask::onStepFinished);
- connect(this, &LaunchStep::progressReportingRequest, parent, &LaunchTask::onProgressReportingRequested);
+ m_parent = parent;
+ connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch);
+ connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine);
+ connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines);
+ connect(this, &LaunchStep::finished, parent, &LaunchTask::onStepFinished);
+ connect(this, &LaunchStep::progressReportingRequest, parent, &LaunchTask::onProgressReportingRequested);
}
diff --git a/api/logic/launch/LaunchStep.h b/api/logic/launch/LaunchStep.h
index 80778e34..dd380e43 100644
--- a/api/logic/launch/LaunchStep.h
+++ b/api/logic/launch/LaunchStep.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,28 +23,28 @@
class LaunchTask;
class LaunchStep: public Task
{
- Q_OBJECT
+ Q_OBJECT
public: /* methods */
- explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent)
- {
- bind(parent);
- };
- virtual ~LaunchStep() {};
+ explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent)
+ {
+ bind(parent);
+ };
+ virtual ~LaunchStep() {};
-protected: /* methods */
- virtual void bind(LaunchTask *parent);
+private: /* methods */
+ void bind(LaunchTask *parent);
signals:
- void logLines(QStringList lines, MessageLevel::Enum level);
- void logLine(QString line, MessageLevel::Enum level);
- void readyForLaunch();
- void progressReportingRequest();
+ void logLines(QStringList lines, MessageLevel::Enum level);
+ void logLine(QString line, MessageLevel::Enum level);
+ void readyForLaunch();
+ void progressReportingRequest();
public slots:
- virtual void proceed() {};
- // called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends
- virtual void finalize() {};
+ virtual void proceed() {};
+ // called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends
+ virtual void finalize() {};
protected: /* data */
- LaunchTask *m_parent;
-}; \ No newline at end of file
+ LaunchTask *m_parent;
+};
diff --git a/api/logic/launch/LaunchTask.cpp b/api/logic/launch/LaunchTask.cpp
index 99c16721..841b8363 100644
--- a/api/logic/launch/LaunchTask.cpp
+++ b/api/logic/launch/LaunchTask.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
@@ -30,251 +30,251 @@
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));
- proc->init();
- return proc;
+ shared_qobject_ptr<LaunchTask> proc(new LaunchTask(inst));
+ proc->init();
+ return proc;
}
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);
}
void LaunchTask::executeTask()
{
- m_instance->setCrashed(false);
- if(!m_steps.size())
- {
- state = LaunchTask::Finished;
- emitSucceeded();
- }
- state = LaunchTask::Running;
- onStepFinished();
+ m_instance->setCrashed(false);
+ if(!m_steps.size())
+ {
+ state = LaunchTask::Finished;
+ emitSucceeded();
+ }
+ state = LaunchTask::Running;
+ onStepFinished();
}
void LaunchTask::onReadyForLaunch()
{
- state = LaunchTask::Waiting;
- emit readyForLaunch();
+ state = LaunchTask::Waiting;
+ emit readyForLaunch();
}
void LaunchTask::onStepFinished()
{
- // initial -> just start the first step
- if(currentStep == -1)
- {
- currentStep ++;
- m_steps[currentStep]->start();
- return;
- }
+ // initial -> just start the first step
+ if(currentStep == -1)
+ {
+ currentStep ++;
+ m_steps[currentStep]->start();
+ return;
+ }
- auto step = m_steps[currentStep];
- if(step->wasSuccessful())
- {
- // end?
- if(currentStep == m_steps.size() - 1)
- {
- finalizeSteps(true, QString());
- }
- else
- {
- currentStep ++;
- step = m_steps[currentStep];
- step->start();
- }
- }
- else
- {
- finalizeSteps(false, step->failReason());
- }
+ auto step = m_steps[currentStep];
+ if(step->wasSuccessful())
+ {
+ // end?
+ if(currentStep == m_steps.size() - 1)
+ {
+ finalizeSteps(true, QString());
+ }
+ else
+ {
+ currentStep ++;
+ step = m_steps[currentStep];
+ step->start();
+ }
+ }
+ else
+ {
+ finalizeSteps(false, step->failReason());
+ }
}
void LaunchTask::finalizeSteps(bool successful, const QString& error)
{
- for(auto step = currentStep; step >= 0; step--)
- {
- m_steps[step]->finalize();
- }
- if(successful)
- {
- emitSucceeded();
- }
- else
- {
- emitFailed(error);
- }
+ for(auto step = currentStep; step >= 0; step--)
+ {
+ m_steps[step]->finalize();
+ }
+ if(successful)
+ {
+ emitSucceeded();
+ }
+ else
+ {
+ emitFailed(error);
+ }
}
void LaunchTask::onProgressReportingRequested()
{
- state = LaunchTask::Waiting;
- emit requestProgress(m_steps[currentStep].get());
+ state = LaunchTask::Waiting;
+ emit requestProgress(m_steps[currentStep].get());
}
void LaunchTask::setCensorFilter(QMap<QString, QString> filter)
{
- m_censorFilter = filter;
+ m_censorFilter = filter;
}
QString LaunchTask::censorPrivateInfo(QString in)
{
- auto iter = m_censorFilter.begin();
- while (iter != m_censorFilter.end())
- {
- in.replace(iter.key(), iter.value());
- iter++;
- }
- return in;
+ auto iter = m_censorFilter.begin();
+ while (iter != m_censorFilter.end())
+ {
+ in.replace(iter.key(), iter.value());
+ iter++;
+ }
+ return in;
}
void LaunchTask::proceed()
{
- if(state != LaunchTask::Waiting)
- {
- return;
- }
- m_steps[currentStep]->proceed();
+ if(state != LaunchTask::Waiting)
+ {
+ return;
+ }
+ m_steps[currentStep]->proceed();
}
bool LaunchTask::canAbort() const
{
- switch(state)
- {
- case LaunchTask::Aborted:
- case LaunchTask::Failed:
- case LaunchTask::Finished:
- return false;
- case LaunchTask::NotStarted:
- return true;
- case LaunchTask::Running:
- case LaunchTask::Waiting:
- {
- auto step = m_steps[currentStep];
- return step->canAbort();
- }
- }
- return false;
+ switch(state)
+ {
+ case LaunchTask::Aborted:
+ case LaunchTask::Failed:
+ case LaunchTask::Finished:
+ return false;
+ case LaunchTask::NotStarted:
+ return true;
+ case LaunchTask::Running:
+ case LaunchTask::Waiting:
+ {
+ auto step = m_steps[currentStep];
+ return step->canAbort();
+ }
+ }
+ return false;
}
bool LaunchTask::abort()
{
- switch(state)
- {
- case LaunchTask::Aborted:
- case LaunchTask::Failed:
- case LaunchTask::Finished:
- return true;
- case LaunchTask::NotStarted:
- {
- state = LaunchTask::Aborted;
- emitFailed("Aborted");
- return true;
- }
- case LaunchTask::Running:
- case LaunchTask::Waiting:
- {
- auto step = m_steps[currentStep];
- if(!step->canAbort())
- {
- return false;
- }
- if(step->abort())
- {
- state = LaunchTask::Aborted;
- return true;
- }
- }
- default:
- break;
- }
- return false;
+ switch(state)
+ {
+ case LaunchTask::Aborted:
+ case LaunchTask::Failed:
+ case LaunchTask::Finished:
+ return true;
+ case LaunchTask::NotStarted:
+ {
+ state = LaunchTask::Aborted;
+ emitFailed("Aborted");
+ return true;
+ }
+ case LaunchTask::Running:
+ case LaunchTask::Waiting:
+ {
+ auto step = m_steps[currentStep];
+ if(!step->canAbort())
+ {
+ return false;
+ }
+ if(step->abort())
+ {
+ state = LaunchTask::Aborted;
+ return true;
+ }
+ }
+ default:
+ break;
+ }
+ return false;
}
shared_qobject_ptr<LogModel> LaunchTask::getLogModel()
{
- if(!m_logModel)
- {
- m_logModel.reset(new LogModel());
- m_logModel->setMaxLines(m_instance->getConsoleMaxLines());
- m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow());
- // FIXME: should this really be here?
- m_logModel->setOverflowMessage(tr("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n"
- "You may have to fix your mods because the game is still logging to files and"
- " likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines()));
- }
- return m_logModel;
+ if(!m_logModel)
+ {
+ m_logModel.reset(new LogModel());
+ m_logModel->setMaxLines(m_instance->getConsoleMaxLines());
+ m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow());
+ // FIXME: should this really be here?
+ m_logModel->setOverflowMessage(tr("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n"
+ "You may have to fix your mods because the game is still logging to files and"
+ " likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines()));
+ }
+ return m_logModel;
}
void LaunchTask::onLogLines(const QStringList &lines, MessageLevel::Enum defaultLevel)
{
- for (auto & line: lines)
- {
- onLogLine(line, defaultLevel);
- }
+ for (auto & line: lines)
+ {
+ onLogLine(line, defaultLevel);
+ }
}
void LaunchTask::onLogLine(QString line, MessageLevel::Enum level)
{
- // if the launcher part set a log level, use it
- auto innerLevel = MessageLevel::fromLine(line);
- if(innerLevel != MessageLevel::Unknown)
- {
- level = innerLevel;
- }
+ // if the launcher part set a log level, use it
+ auto innerLevel = MessageLevel::fromLine(line);
+ if(innerLevel != MessageLevel::Unknown)
+ {
+ level = innerLevel;
+ }
- // If the level is still undetermined, guess level
- if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown)
- {
- level = m_instance->guessLevel(line, level);
- }
+ // If the level is still undetermined, guess level
+ if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown)
+ {
+ level = m_instance->guessLevel(line, level);
+ }
- // censor private user info
- line = censorPrivateInfo(line);
+ // censor private user info
+ line = censorPrivateInfo(line);
- auto &model = *getLogModel();
- model.append(level, line);
+ auto &model = *getLogModel();
+ model.append(level, line);
}
void LaunchTask::emitSucceeded()
{
- m_instance->setRunning(false);
- Task::emitSucceeded();
+ m_instance->setRunning(false);
+ Task::emitSucceeded();
}
void LaunchTask::emitFailed(QString reason)
{
- m_instance->setRunning(false);
- m_instance->setCrashed(true);
- Task::emitFailed(reason);
+ m_instance->setRunning(false);
+ m_instance->setCrashed(true);
+ Task::emitFailed(reason);
}
QString LaunchTask::substituteVariables(const QString &cmd) const
{
- QString out = cmd;
- auto variables = m_instance->getVariables();
- for (auto it = variables.begin(); it != variables.end(); ++it)
- {
- out.replace("$" + it.key(), it.value());
- }
- auto env = QProcessEnvironment::systemEnvironment();
- for (auto var : env.keys())
- {
- out.replace("$" + var, env.value(var));
- }
- return out;
+ QString out = cmd;
+ auto variables = m_instance->getVariables();
+ for (auto it = variables.begin(); it != variables.end(); ++it)
+ {
+ out.replace("$" + it.key(), it.value());
+ }
+ auto env = QProcessEnvironment::systemEnvironment();
+ for (auto var : env.keys())
+ {
+ out.replace("$" + var, env.value(var));
+ }
+ return out;
}
diff --git a/api/logic/launch/LaunchTask.h b/api/logic/launch/LaunchTask.h
index 746d6d19..ee04bd9a 100644
--- a/api/logic/launch/LaunchTask.h
+++ b/api/logic/launch/LaunchTask.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -28,98 +28,98 @@
class MULTIMC_LOGIC_EXPORT LaunchTask: public Task
{
- Q_OBJECT
+ Q_OBJECT
protected:
- explicit LaunchTask(InstancePtr instance);
- void init();
+ explicit LaunchTask(InstancePtr instance);
+ void init();
public:
- enum State
- {
- NotStarted,
- Running,
- Waiting,
- Failed,
- Aborted,
- Finished
- };
+ enum State
+ {
+ NotStarted,
+ Running,
+ Waiting,
+ Failed,
+ Aborted,
+ Finished
+ };
public: /* methods */
- static std::shared_ptr<LaunchTask> create(InstancePtr inst);
- virtual ~LaunchTask() {};
+ static shared_qobject_ptr<LaunchTask> create(InstancePtr inst);
+ virtual ~LaunchTask() {};
- void appendStep(std::shared_ptr<LaunchStep> step);
- void prependStep(std::shared_ptr<LaunchStep> step);
- void setCensorFilter(QMap<QString, QString> filter);
+ void appendStep(shared_qobject_ptr<LaunchStep> step);
+ void prependStep(shared_qobject_ptr<LaunchStep> step);
+ void setCensorFilter(QMap<QString, QString> filter);
- InstancePtr instance()
- {
- return m_instance;
- }
+ InstancePtr instance()
+ {
+ return m_instance;
+ }
- void setPid(qint64 pid)
- {
- m_pid = pid;
- }
+ void setPid(qint64 pid)
+ {
+ m_pid = pid;
+ }
- qint64 pid()
- {
- return m_pid;
- }
+ qint64 pid()
+ {
+ return m_pid;
+ }
- /**
- * @brief prepare the process for launch (for multi-stage launch)
- */
- virtual void executeTask() override;
+ /**
+ * @brief prepare the process for launch (for multi-stage launch)
+ */
+ virtual void executeTask() override;
- /**
- * @brief launch the armed instance
- */
- void proceed();
+ /**
+ * @brief launch the armed instance
+ */
+ void proceed();
- /**
- * @brief abort launch
- */
- bool abort() override;
+ /**
+ * @brief abort launch
+ */
+ bool abort() override;
- bool canAbort() const override;
+ bool canAbort() const override;
- shared_qobject_ptr<LogModel> getLogModel();
+ shared_qobject_ptr<LogModel> getLogModel();
public:
- QString substituteVariables(const QString &cmd) const;
- QString censorPrivateInfo(QString in);
+ QString substituteVariables(const QString &cmd) const;
+ QString censorPrivateInfo(QString in);
protected: /* methods */
- virtual void emitFailed(QString reason) override;
- virtual void emitSucceeded() override;
+ virtual void emitFailed(QString reason) override;
+ virtual void emitSucceeded() override;
signals:
- /**
- * @brief emitted when the launch preparations are done
- */
- void readyForLaunch();
+ /**
+ * @brief emitted when the launch preparations are done
+ */
+ void readyForLaunch();
- void requestProgress(Task *task);
+ void requestProgress(Task *task);
- void requestLogging();
+ void requestLogging();
public slots:
- void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
- void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
- void onReadyForLaunch();
- void onStepFinished();
- void onProgressReportingRequested();
+ void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
+ void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
+ void onReadyForLaunch();
+ void onStepFinished();
+ void onProgressReportingRequested();
private: /*methods */
- void finalizeSteps(bool successful, const QString & error);
+ void finalizeSteps(bool successful, const QString & error);
protected: /* data */
- InstancePtr m_instance;
- shared_qobject_ptr<LogModel> m_logModel;
- QList <std::shared_ptr<LaunchStep>> m_steps;
- QMap<QString, QString> m_censorFilter;
- int currentStep = -1;
- State state = NotStarted;
- qint64 m_pid = -1;
+ InstancePtr m_instance;
+ shared_qobject_ptr<LogModel> m_logModel;
+ QList <shared_qobject_ptr<LaunchStep>> m_steps;
+ QMap<QString, QString> m_censorFilter;
+ int currentStep = -1;
+ State state = NotStarted;
+ qint64 m_pid = -1;
};
diff --git a/api/logic/launch/LogModel.cpp b/api/logic/launch/LogModel.cpp
index 72b076e9..92f9487a 100644
--- a/api/logic/launch/LogModel.cpp
+++ b/api/logic/launch/LogModel.cpp
@@ -2,166 +2,166 @@
LogModel::LogModel(QObject *parent):QAbstractListModel(parent)
{
- m_content.resize(m_maxLines);
+ m_content.resize(m_maxLines);
}
int LogModel::rowCount(const QModelIndex &parent) const
{
- if (parent.isValid())
- return 0;
+ if (parent.isValid())
+ return 0;
- return m_numLines;
+ return m_numLines;
}
QVariant LogModel::data(const QModelIndex &index, int role) const
{
- if (index.row() < 0 || index.row() >= m_numLines)
- return QVariant();
-
- auto row = index.row();
- auto realRow = (row + m_firstLine) % m_maxLines;
- if (role == Qt::DisplayRole || role == Qt::EditRole)
- {
- return m_content[realRow].line;
- }
- if(role == LevelRole)
- {
- return m_content[realRow].level;
- }
-
- return QVariant();
+ if (index.row() < 0 || index.row() >= m_numLines)
+ return QVariant();
+
+ auto row = index.row();
+ auto realRow = (row + m_firstLine) % m_maxLines;
+ if (role == Qt::DisplayRole || role == Qt::EditRole)
+ {
+ return m_content[realRow].line;
+ }
+ if(role == LevelRole)
+ {
+ return m_content[realRow].level;
+ }
+
+ return QVariant();
}
void LogModel::append(MessageLevel::Enum level, QString line)
{
- if(m_suspended)
- {
- return;
- }
- int lineNum = (m_firstLine + m_numLines) % m_maxLines;
- // overflow
- if(m_numLines == m_maxLines)
- {
- if(m_stopOnOverflow)
- {
- // nothing more to do, the buffer is full
- return;
- }
- beginRemoveRows(QModelIndex(), 0, 0);
- m_firstLine = (m_firstLine + 1) % m_maxLines;
- m_numLines --;
- endRemoveRows();
- }
- else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow)
- {
- level = MessageLevel::Fatal;
- line = m_overflowMessage;
- }
- beginInsertRows(QModelIndex(), m_numLines, m_numLines);
- m_numLines ++;
- m_content[lineNum].level = level;
- m_content[lineNum].line = line;
- endInsertRows();
+ if(m_suspended)
+ {
+ return;
+ }
+ int lineNum = (m_firstLine + m_numLines) % m_maxLines;
+ // overflow
+ if(m_numLines == m_maxLines)
+ {
+ if(m_stopOnOverflow)
+ {
+ // nothing more to do, the buffer is full
+ return;
+ }
+ beginRemoveRows(QModelIndex(), 0, 0);
+ m_firstLine = (m_firstLine + 1) % m_maxLines;
+ m_numLines --;
+ endRemoveRows();
+ }
+ else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow)
+ {
+ level = MessageLevel::Fatal;
+ line = m_overflowMessage;
+ }
+ beginInsertRows(QModelIndex(), m_numLines, m_numLines);
+ m_numLines ++;
+ m_content[lineNum].level = level;
+ m_content[lineNum].line = line;
+ endInsertRows();
}
void LogModel::suspend(bool suspend)
{
- m_suspended = suspend;
+ m_suspended = suspend;
}
bool LogModel::suspended()
{
- return m_suspended;
+ return m_suspended;
}
void LogModel::clear()
{
- beginResetModel();
- m_firstLine = 0;
- m_numLines = 0;
- endResetModel();
+ beginResetModel();
+ m_firstLine = 0;
+ m_numLines = 0;
+ endResetModel();
}
QString LogModel::toPlainText()
{
- QString out;
- out.reserve(m_numLines * 80);
- for(int i = 0; i < m_numLines; i++)
- {
- QString & line = m_content[(m_firstLine + i) % m_maxLines].line;
- out.append(line + '\n');
- }
- out.squeeze();
- return out;
+ QString out;
+ out.reserve(m_numLines * 80);
+ for(int i = 0; i < m_numLines; i++)
+ {
+ QString & line = m_content[(m_firstLine + i) % m_maxLines].line;
+ out.append(line + '\n');
+ }
+ out.squeeze();
+ return out;
}
void LogModel::setMaxLines(int maxLines)
{
- // no-op
- if(maxLines == m_maxLines)
- {
- return;
- }
- // if it all still fits in the buffer, just resize it
- if(m_firstLine + m_numLines < m_maxLines)
- {
- m_maxLines = maxLines;
- m_content.resize(maxLines);
- return;
- }
- // otherwise, we need to reorganize the data because it crosses the wrap boundary
- QVector<entry> newContent;
- newContent.resize(maxLines);
- if(m_numLines <= maxLines)
- {
- // if it all fits in the new buffer, just copy it over
- for(int i = 0; i < m_numLines; i++)
- {
- newContent[i] = m_content[(m_firstLine + i) % m_maxLines];
- }
- m_content.swap(newContent);
- }
- else
- {
- // if it doesn't fit, part of the data needs to be thrown away (the oldest log messages)
- int lead = m_numLines - maxLines;
- beginRemoveRows(QModelIndex(), 0, lead - 1);
- for(int i = 0; i < maxLines; i++)
- {
- newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines];
- }
- m_numLines = m_maxLines;
- m_content.swap(newContent);
- endRemoveRows();
- }
- m_firstLine = 0;
- m_maxLines = maxLines;
+ // no-op
+ if(maxLines == m_maxLines)
+ {
+ return;
+ }
+ // if it all still fits in the buffer, just resize it
+ if(m_firstLine + m_numLines < m_maxLines)
+ {
+ m_maxLines = maxLines;
+ m_content.resize(maxLines);
+ return;
+ }
+ // otherwise, we need to reorganize the data because it crosses the wrap boundary
+ QVector<entry> newContent;
+ newContent.resize(maxLines);
+ if(m_numLines <= maxLines)
+ {
+ // if it all fits in the new buffer, just copy it over
+ for(int i = 0; i < m_numLines; i++)
+ {
+ newContent[i] = m_content[(m_firstLine + i) % m_maxLines];
+ }
+ m_content.swap(newContent);
+ }
+ else
+ {
+ // if it doesn't fit, part of the data needs to be thrown away (the oldest log messages)
+ int lead = m_numLines - maxLines;
+ beginRemoveRows(QModelIndex(), 0, lead - 1);
+ for(int i = 0; i < maxLines; i++)
+ {
+ newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines];
+ }
+ m_numLines = m_maxLines;
+ m_content.swap(newContent);
+ endRemoveRows();
+ }
+ m_firstLine = 0;
+ m_maxLines = maxLines;
}
int LogModel::getMaxLines()
{
- return m_maxLines;
+ return m_maxLines;
}
void LogModel::setStopOnOverflow(bool stop)
{
- m_stopOnOverflow = stop;
+ m_stopOnOverflow = stop;
}
void LogModel::setOverflowMessage(const QString& overflowMessage)
{
- m_overflowMessage = overflowMessage;
+ m_overflowMessage = overflowMessage;
}
void LogModel::setLineWrap(bool state)
{
- if(m_lineWrap != state)
- {
- m_lineWrap = state;
- }
+ if(m_lineWrap != state)
+ {
+ m_lineWrap = state;
+ }
}
bool LogModel::wrapLines() const
{
- return m_lineWrap;
+ return m_lineWrap;
}
diff --git a/api/logic/launch/LogModel.h b/api/logic/launch/LogModel.h
index e6deac89..bccceaef 100644
--- a/api/logic/launch/LogModel.h
+++ b/api/logic/launch/LogModel.h
@@ -8,53 +8,53 @@
class MULTIMC_LOGIC_EXPORT LogModel : public QAbstractListModel
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit LogModel(QObject *parent = 0);
+ explicit LogModel(QObject *parent = 0);
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
- QVariant data(const QModelIndex &index, int role) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role) const;
- void append(MessageLevel::Enum, QString line);
- void clear();
+ void append(MessageLevel::Enum, QString line);
+ void clear();
- void suspend(bool suspend);
- bool suspended();
+ void suspend(bool suspend);
+ bool suspended();
- QString toPlainText();
+ QString toPlainText();
- int getMaxLines();
- void setMaxLines(int maxLines);
- void setStopOnOverflow(bool stop);
- void setOverflowMessage(const QString & overflowMessage);
+ int getMaxLines();
+ void setMaxLines(int maxLines);
+ void setStopOnOverflow(bool stop);
+ void setOverflowMessage(const QString & overflowMessage);
- void setLineWrap(bool state);
- bool wrapLines() const;
+ void setLineWrap(bool state);
+ bool wrapLines() const;
- enum Roles
- {
- LevelRole = Qt::UserRole
- };
+ enum Roles
+ {
+ LevelRole = Qt::UserRole
+ };
private /* types */:
- struct entry
- {
- MessageLevel::Enum level;
- QString line;
- };
+ struct entry
+ {
+ MessageLevel::Enum level;
+ QString line;
+ };
private: /* data */
- QVector <entry> m_content;
- int m_maxLines = 1000;
- // first line in the circular buffer
- int m_firstLine = 0;
- // number of lines occupied in the circular buffer
- int m_numLines = 0;
- bool m_stopOnOverflow = false;
- QString m_overflowMessage = "OVERFLOW";
- bool m_suspended = false;
- bool m_lineWrap = true;
+ QVector <entry> m_content;
+ int m_maxLines = 1000;
+ // first line in the circular buffer
+ int m_firstLine = 0;
+ // number of lines occupied in the circular buffer
+ int m_numLines = 0;
+ bool m_stopOnOverflow = false;
+ QString m_overflowMessage = "OVERFLOW";
+ bool m_suspended = false;
+ bool m_lineWrap = true;
private:
- Q_DISABLE_COPY(LogModel)
+ Q_DISABLE_COPY(LogModel)
};
diff --git a/api/logic/launch/steps/PostLaunchCommand.cpp b/api/logic/launch/steps/PostLaunchCommand.cpp
index bc3b3ffe..e0b2b6fc 100644
--- a/api/logic/launch/steps/PostLaunchCommand.cpp
+++ b/api/logic/launch/steps/PostLaunchCommand.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,67 +18,67 @@
PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
{
- auto instance = m_parent->instance();
- m_command = instance->getPostExitCommand();
- m_process.setProcessEnvironment(instance->createEnvironment());
- connect(&m_process, &LoggedProcess::log, this, &PostLaunchCommand::logLines);
- connect(&m_process, &LoggedProcess::stateChanged, this, &PostLaunchCommand::on_state);
+ auto instance = m_parent->instance();
+ m_command = instance->getPostExitCommand();
+ m_process.setProcessEnvironment(instance->createEnvironment());
+ connect(&m_process, &LoggedProcess::log, this, &PostLaunchCommand::logLines);
+ connect(&m_process, &LoggedProcess::stateChanged, this, &PostLaunchCommand::on_state);
}
void PostLaunchCommand::executeTask()
{
- QString postlaunch_cmd = m_parent->substituteVariables(m_command);
- emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::MultiMC);
- m_process.start(postlaunch_cmd);
+ QString postlaunch_cmd = m_parent->substituteVariables(m_command);
+ emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::MultiMC);
+ m_process.start(postlaunch_cmd);
}
void PostLaunchCommand::on_state(LoggedProcess::State state)
{
- auto getError = [&]()
- {
- return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
- };
- switch(state)
- {
- case LoggedProcess::Aborted:
- case LoggedProcess::Crashed:
- case LoggedProcess::FailedToStart:
- {
- auto error = getError();
- emit logLine(error, MessageLevel::Fatal);
- emitFailed(error);
- return;
- }
- case LoggedProcess::Finished:
- {
- if(m_process.exitCode() != 0)
- {
- auto error = getError();
- emit logLine(error, MessageLevel::Fatal);
- emitFailed(error);
- }
- else
- {
- emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
- emitSucceeded();
- }
- }
- default:
- break;
- }
+ auto getError = [&]()
+ {
+ return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
+ };
+ switch(state)
+ {
+ case LoggedProcess::Aborted:
+ case LoggedProcess::Crashed:
+ case LoggedProcess::FailedToStart:
+ {
+ auto error = getError();
+ emit logLine(error, MessageLevel::Fatal);
+ emitFailed(error);
+ return;
+ }
+ case LoggedProcess::Finished:
+ {
+ if(m_process.exitCode() != 0)
+ {
+ auto error = getError();
+ emit logLine(error, MessageLevel::Fatal);
+ emitFailed(error);
+ }
+ else
+ {
+ emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
+ emitSucceeded();
+ }
+ }
+ default:
+ break;
+ }
}
void PostLaunchCommand::setWorkingDirectory(const QString &wd)
{
- m_process.setWorkingDirectory(wd);
+ m_process.setWorkingDirectory(wd);
}
bool PostLaunchCommand::abort()
{
- auto state = m_process.state();
- if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
- {
- m_process.kill();
- }
- return true;
+ auto state = m_process.state();
+ if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
+ {
+ m_process.kill();
+ }
+ return true;
}
diff --git a/api/logic/launch/steps/PostLaunchCommand.h b/api/logic/launch/steps/PostLaunchCommand.h
index 36b3b96f..da200534 100644
--- a/api/logic/launch/steps/PostLaunchCommand.h
+++ b/api/logic/launch/steps/PostLaunchCommand.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,20 +20,22 @@
class PostLaunchCommand: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit PostLaunchCommand(LaunchTask *parent);
- virtual void executeTask();
- virtual bool abort();
- virtual bool canAbort() const
- {
- return true;
- }
- void setWorkingDirectory(const QString &wd);
+ explicit PostLaunchCommand(LaunchTask *parent);
+ virtual ~PostLaunchCommand() {};
+
+ virtual void executeTask();
+ virtual bool abort();
+ virtual bool canAbort() const
+ {
+ return true;
+ }
+ void setWorkingDirectory(const QString &wd);
private slots:
- void on_state(LoggedProcess::State state);
+ void on_state(LoggedProcess::State state);
private:
- LoggedProcess m_process;
- QString m_command;
+ LoggedProcess m_process;
+ QString m_command;
};
diff --git a/api/logic/launch/steps/PreLaunchCommand.cpp b/api/logic/launch/steps/PreLaunchCommand.cpp
index 7c37d5cb..6f1f280d 100644
--- a/api/logic/launch/steps/PreLaunchCommand.cpp
+++ b/api/logic/launch/steps/PreLaunchCommand.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,68 +18,68 @@
PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
{
- auto instance = m_parent->instance();
- m_command = instance->getPreLaunchCommand();
- m_process.setProcessEnvironment(instance->createEnvironment());
- connect(&m_process, &LoggedProcess::log, this, &PreLaunchCommand::logLines);
- connect(&m_process, &LoggedProcess::stateChanged, this, &PreLaunchCommand::on_state);
+ auto instance = m_parent->instance();
+ m_command = instance->getPreLaunchCommand();
+ m_process.setProcessEnvironment(instance->createEnvironment());
+ connect(&m_process, &LoggedProcess::log, this, &PreLaunchCommand::logLines);
+ connect(&m_process, &LoggedProcess::stateChanged, this, &PreLaunchCommand::on_state);
}
void PreLaunchCommand::executeTask()
{
- //FIXME: where to put this?
- QString prelaunch_cmd = m_parent->substituteVariables(m_command);
- emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::MultiMC);
- m_process.start(prelaunch_cmd);
+ //FIXME: where to put this?
+ QString prelaunch_cmd = m_parent->substituteVariables(m_command);
+ emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::MultiMC);
+ m_process.start(prelaunch_cmd);
}
void PreLaunchCommand::on_state(LoggedProcess::State state)
{
- auto getError = [&]()
- {
- return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
- };
- switch(state)
- {
- case LoggedProcess::Aborted:
- case LoggedProcess::Crashed:
- case LoggedProcess::FailedToStart:
- {
- auto error = getError();
- emit logLine(error, MessageLevel::Fatal);
- emitFailed(error);
- return;
- }
- case LoggedProcess::Finished:
- {
- if(m_process.exitCode() != 0)
- {
- auto error = getError();
- emit logLine(error, MessageLevel::Fatal);
- emitFailed(error);
- }
- else
- {
- emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
- emitSucceeded();
- }
- }
- default:
- break;
- }
+ auto getError = [&]()
+ {
+ return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
+ };
+ switch(state)
+ {
+ case LoggedProcess::Aborted:
+ case LoggedProcess::Crashed:
+ case LoggedProcess::FailedToStart:
+ {
+ auto error = getError();
+ emit logLine(error, MessageLevel::Fatal);
+ emitFailed(error);
+ return;
+ }
+ case LoggedProcess::Finished:
+ {
+ if(m_process.exitCode() != 0)
+ {
+ auto error = getError();
+ emit logLine(error, MessageLevel::Fatal);
+ emitFailed(error);
+ }
+ else
+ {
+ emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
+ emitSucceeded();
+ }
+ }
+ default:
+ break;
+ }
}
void PreLaunchCommand::setWorkingDirectory(const QString &wd)
{
- m_process.setWorkingDirectory(wd);
+ m_process.setWorkingDirectory(wd);
}
bool PreLaunchCommand::abort()
{
- auto state = m_process.state();
- if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
- {
- m_process.kill();
- }
- return true;
+ auto state = m_process.state();
+ if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
+ {
+ m_process.kill();
+ }
+ return true;
}
diff --git a/api/logic/launch/steps/PreLaunchCommand.h b/api/logic/launch/steps/PreLaunchCommand.h
index 880cc076..f1e9db5d 100644
--- a/api/logic/launch/steps/PreLaunchCommand.h
+++ b/api/logic/launch/steps/PreLaunchCommand.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,20 +20,22 @@
class PreLaunchCommand: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit PreLaunchCommand(LaunchTask *parent);
- virtual void executeTask();
- virtual bool abort();
- virtual bool canAbort() const
- {
- return true;
- }
- void setWorkingDirectory(const QString &wd);
+ explicit PreLaunchCommand(LaunchTask *parent);
+ virtual ~PreLaunchCommand() {};
+
+ virtual void executeTask();
+ virtual bool abort();
+ virtual bool canAbort() const
+ {
+ return true;
+ }
+ void setWorkingDirectory(const QString &wd);
private slots:
- void on_state(LoggedProcess::State state);
+ void on_state(LoggedProcess::State state);
private:
- LoggedProcess m_process;
- QString m_command;
+ LoggedProcess m_process;
+ QString m_command;
};
diff --git a/api/logic/launch/steps/TextPrint.cpp b/api/logic/launch/steps/TextPrint.cpp
index f307b1fd..0c1f320c 100644
--- a/api/logic/launch/steps/TextPrint.cpp
+++ b/api/logic/launch/steps/TextPrint.cpp
@@ -2,28 +2,28 @@
TextPrint::TextPrint(LaunchTask * parent, const QStringList &lines, MessageLevel::Enum level) : LaunchStep(parent)
{
- m_lines = lines;
- m_level = level;
+ m_lines = lines;
+ m_level = level;
}
TextPrint::TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level) : LaunchStep(parent)
{
- m_lines.append(line);
- m_level = level;
+ m_lines.append(line);
+ m_level = level;
}
void TextPrint::executeTask()
{
- emit logLines(m_lines, m_level);
- emitSucceeded();
+ emit logLines(m_lines, m_level);
+ emitSucceeded();
}
bool TextPrint::canAbort() const
{
- return true;
+ return true;
}
bool TextPrint::abort()
{
- emitFailed("Aborted.");
- return true;
+ emitFailed("Aborted.");
+ return true;
}
diff --git a/api/logic/launch/steps/TextPrint.h b/api/logic/launch/steps/TextPrint.h
index 3e3e06cb..099fdc13 100644
--- a/api/logic/launch/steps/TextPrint.h
+++ b/api/logic/launch/steps/TextPrint.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,17 +27,17 @@
class MULTIMC_LOGIC_EXPORT TextPrint: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit TextPrint(LaunchTask *parent, const QStringList &lines, MessageLevel::Enum level);
- explicit TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level);
- virtual ~TextPrint(){};
+ explicit TextPrint(LaunchTask *parent, const QStringList &lines, MessageLevel::Enum level);
+ explicit TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level);
+ virtual ~TextPrint(){};
- virtual void executeTask();
- virtual bool canAbort() const;
- virtual bool abort();
+ virtual void executeTask();
+ virtual bool canAbort() const;
+ virtual bool abort();
private:
- QStringList m_lines;
- MessageLevel::Enum m_level;
+ QStringList m_lines;
+ MessageLevel::Enum m_level;
};
diff --git a/api/logic/launch/steps/Update.cpp b/api/logic/launch/steps/Update.cpp
index d057febb..6eb012f7 100644
--- a/api/logic/launch/steps/Update.cpp
+++ b/api/logic/launch/steps/Update.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,63 +18,63 @@
void Update::executeTask()
{
- if(m_aborted)
- {
- emitFailed(tr("Task aborted."));
- return;
- }
- m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode));
- if(m_updateTask)
- {
- connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
- connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress);
- connect(m_updateTask.get(), &Task::status, this, &Task::setStatus);
- emit progressReportingRequest();
- return;
- }
- emitSucceeded();
+ if(m_aborted)
+ {
+ emitFailed(tr("Task aborted."));
+ return;
+ }
+ m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode));
+ if(m_updateTask)
+ {
+ connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
+ connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress);
+ connect(m_updateTask.get(), &Task::status, this, &Task::setStatus);
+ emit progressReportingRequest();
+ return;
+ }
+ emitSucceeded();
}
void Update::proceed()
{
- m_updateTask->start();
+ m_updateTask->start();
}
void Update::updateFinished()
{
- if(m_updateTask->wasSuccessful())
- {
- m_updateTask.reset();
- emitSucceeded();
- }
- else
- {
- QString reason = tr("Instance update failed because: %1.\n\n").arg(m_updateTask->failReason());
- m_updateTask.reset();
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(reason);
- }
+ if(m_updateTask->wasSuccessful())
+ {
+ m_updateTask.reset();
+ emitSucceeded();
+ }
+ else
+ {
+ QString reason = tr("Instance update failed because: %1\n\n").arg(m_updateTask->failReason());
+ m_updateTask.reset();
+ emit logLine(reason, MessageLevel::Fatal);
+ emitFailed(reason);
+ }
}
bool Update::canAbort() const
{
- if(m_updateTask)
- {
- return m_updateTask->canAbort();
- }
- return true;
+ if(m_updateTask)
+ {
+ return m_updateTask->canAbort();
+ }
+ return true;
}
bool Update::abort()
{
- m_aborted = true;
- if(m_updateTask)
- {
- if(m_updateTask->canAbort())
- {
- return m_updateTask->abort();
- }
- }
- return true;
+ m_aborted = true;
+ if(m_updateTask)
+ {
+ if(m_updateTask->canAbort())
+ {
+ return m_updateTask->abort();
+ }
+ }
+ return true;
}
diff --git a/api/logic/launch/steps/Update.h b/api/logic/launch/steps/Update.h
index 7a14011a..331698bd 100644
--- a/api/logic/launch/steps/Update.h
+++ b/api/logic/launch/steps/Update.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,22 +24,22 @@
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
class Update: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {};
- virtual ~Update() {};
+ explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {};
+ virtual ~Update() {};
- void executeTask() override;
- bool canAbort() const override;
- void proceed() override;
+ void executeTask() override;
+ bool canAbort() const override;
+ void proceed() override;
public slots:
- bool abort() override;
+ bool abort() override;
private slots:
- void updateFinished();
+ void updateFinished();
private:
- shared_qobject_ptr<Task> m_updateTask;
- bool m_aborted = false;
- Net::Mode m_mode = Net::Mode::Offline;
+ shared_qobject_ptr<Task> m_updateTask;
+ bool m_aborted = false;
+ Net::Mode m_mode = Net::Mode::Offline;
};
diff --git a/api/logic/meta/BaseEntity.cpp b/api/logic/meta/BaseEntity.cpp
index 5c2339cb..ce0be859 100644
--- a/api/logic/meta/BaseEntity.cpp
+++ b/api/logic/meta/BaseEntity.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,45 +27,45 @@
class ParsingValidator : public Net::Validator
{
public: /* con/des */
- ParsingValidator(Meta::BaseEntity *entity) : m_entity(entity)
- {
- };
- virtual ~ParsingValidator()
- {
- };
+ ParsingValidator(Meta::BaseEntity *entity) : m_entity(entity)
+ {
+ };
+ virtual ~ParsingValidator()
+ {
+ };
public: /* methods */
- bool init(QNetworkRequest &) override
- {
- return true;
- }
- bool write(QByteArray & data) override
- {
- this->data.append(data);
- return true;
- }
- bool abort() override
- {
- return true;
- }
- bool validate(QNetworkReply &) override
- {
- auto fname = m_entity->localFilename();
- try
- {
- m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname));
- return true;
- }
- catch (Exception &e)
- {
- qWarning() << "Unable to parse response:" << e.cause();
- return false;
- }
- }
+ bool init(QNetworkRequest &) override
+ {
+ return true;
+ }
+ bool write(QByteArray & data) override
+ {
+ this->data.append(data);
+ return true;
+ }
+ bool abort() override
+ {
+ return true;
+ }
+ bool validate(QNetworkReply &) override
+ {
+ auto fname = m_entity->localFilename();
+ try
+ {
+ m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname));
+ return true;
+ }
+ catch (const Exception &e)
+ {
+ qWarning() << "Unable to parse response:" << e.cause();
+ return false;
+ }
+ }
private: /* data */
- QByteArray data;
- Meta::BaseEntity *m_entity;
+ QByteArray data;
+ Meta::BaseEntity *m_entity;
};
Meta::BaseEntity::~BaseEntity()
@@ -74,89 +74,89 @@ Meta::BaseEntity::~BaseEntity()
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()
{
- const QString fname = QDir("meta").absoluteFilePath(localFilename());
- if (!QFile::exists(fname))
- {
- return false;
- }
- // TODO: check if the file has the expected checksum
- try
- {
- parse(Json::requireObject(Json::requireDocument(fname, fname), fname));
- return true;
- }
- catch (Exception &e)
- {
- qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
- // just make sure it's gone and we never consider it again.
- QFile::remove(fname);
- return false;
- }
+ const QString fname = QDir("meta").absoluteFilePath(localFilename());
+ if (!QFile::exists(fname))
+ {
+ return false;
+ }
+ // TODO: check if the file has the expected checksum
+ try
+ {
+ parse(Json::requireObject(Json::requireDocument(fname, fname), fname));
+ return true;
+ }
+ catch (const Exception &e)
+ {
+ qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
+ // just make sure it's gone and we never consider it again.
+ QFile::remove(fname);
+ return false;
+ }
}
void Meta::BaseEntity::load(Net::Mode loadType)
{
- // load local file if nothing is loaded yet
- if(!isLoaded())
- {
- if(loadLocalFile())
- {
- m_loadStatus = LoadStatus::Local;
- }
- }
- // if we need remote update, run the update task
- if(loadType == Net::Mode::Offline || !shouldStartRemoteUpdate())
- {
- return;
- }
- NetJob *job = new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()));
- auto url = this->url();
- auto entry = ENV.metacache()->resolveEntry("meta", localFilename());
- entry->setStale(true);
- auto dl = Net::Download::makeCached(url, entry);
- /*
- * The validator parses the file and loads it into the object.
- * If that fails, the file is not written to storage.
- */
- dl->addValidator(new ParsingValidator(this));
- job->addNetAction(dl);
- m_updateStatus = UpdateStatus::InProgress;
- m_updateTask.reset(job);
- QObject::connect(job, &NetJob::succeeded, [&]()
- {
- m_loadStatus = LoadStatus::Remote;
- m_updateStatus = UpdateStatus::Succeeded;
- m_updateTask.reset();
- });
- QObject::connect(job, &NetJob::failed, [&]()
- {
- m_updateStatus = UpdateStatus::Failed;
- m_updateTask.reset();
- });
- m_updateTask->start();
+ // load local file if nothing is loaded yet
+ if(!isLoaded())
+ {
+ if(loadLocalFile())
+ {
+ m_loadStatus = LoadStatus::Local;
+ }
+ }
+ // if we need remote update, run the update task
+ if(loadType == Net::Mode::Offline || !shouldStartRemoteUpdate())
+ {
+ return;
+ }
+ NetJob *job = new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()));
+ auto url = this->url();
+ auto entry = ENV.metacache()->resolveEntry("meta", localFilename());
+ entry->setStale(true);
+ auto dl = Net::Download::makeCached(url, entry);
+ /*
+ * The validator parses the file and loads it into the object.
+ * If that fails, the file is not written to storage.
+ */
+ dl->addValidator(new ParsingValidator(this));
+ job->addNetAction(dl);
+ m_updateStatus = UpdateStatus::InProgress;
+ m_updateTask.reset(job);
+ QObject::connect(job, &NetJob::succeeded, [&]()
+ {
+ m_loadStatus = LoadStatus::Remote;
+ m_updateStatus = UpdateStatus::Succeeded;
+ m_updateTask.reset();
+ });
+ QObject::connect(job, &NetJob::failed, [&]()
+ {
+ m_updateStatus = UpdateStatus::Failed;
+ m_updateTask.reset();
+ });
+ m_updateTask->start();
}
bool Meta::BaseEntity::isLoaded() const
{
- return m_loadStatus > LoadStatus::NotLoaded;
+ return m_loadStatus > LoadStatus::NotLoaded;
}
bool Meta::BaseEntity::shouldStartRemoteUpdate() const
{
- // TODO: version-locks and offline mode?
- return m_updateStatus != UpdateStatus::InProgress;
+ // TODO: version-locks and offline mode?
+ return m_updateStatus != UpdateStatus::InProgress;
}
shared_qobject_ptr<Task> Meta::BaseEntity::getCurrentTask()
{
- if(m_updateStatus == UpdateStatus::InProgress)
- {
- return m_updateTask;
- }
- return nullptr;
+ if(m_updateStatus == UpdateStatus::InProgress)
+ {
+ return m_updateTask;
+ }
+ return nullptr;
}
diff --git a/api/logic/meta/BaseEntity.h b/api/logic/meta/BaseEntity.h
index 418c979f..5fc5981a 100644
--- a/api/logic/meta/BaseEntity.h
+++ b/api/logic/meta/BaseEntity.h
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,41 +28,41 @@ namespace Meta
class MULTIMC_LOGIC_EXPORT BaseEntity
{
public: /* types */
- using Ptr = std::shared_ptr<BaseEntity>;
- enum class LoadStatus
- {
- NotLoaded,
- Local,
- Remote
- };
- enum class UpdateStatus
- {
- NotDone,
- InProgress,
- Failed,
- Succeeded
- };
+ using Ptr = std::shared_ptr<BaseEntity>;
+ enum class LoadStatus
+ {
+ NotLoaded,
+ Local,
+ Remote
+ };
+ enum class UpdateStatus
+ {
+ NotDone,
+ InProgress,
+ Failed,
+ Succeeded
+ };
public:
- virtual ~BaseEntity();
+ virtual ~BaseEntity();
- virtual void parse(const QJsonObject &obj) = 0;
+ virtual void parse(const QJsonObject &obj) = 0;
- virtual QString localFilename() const = 0;
- virtual QUrl url() const;
+ virtual QString localFilename() const = 0;
+ virtual QUrl url() const;
- bool isLoaded() const;
- bool shouldStartRemoteUpdate() const;
+ bool isLoaded() const;
+ bool shouldStartRemoteUpdate() const;
- void load(Net::Mode loadType);
- shared_qobject_ptr<Task> getCurrentTask();
+ void load(Net::Mode loadType);
+ shared_qobject_ptr<Task> getCurrentTask();
protected: /* methods */
- bool loadLocalFile();
+ bool loadLocalFile();
private:
- LoadStatus m_loadStatus = LoadStatus::NotLoaded;
- UpdateStatus m_updateStatus = UpdateStatus::NotDone;
- shared_qobject_ptr<Task> m_updateTask;
+ LoadStatus m_loadStatus = LoadStatus::NotLoaded;
+ UpdateStatus m_updateStatus = UpdateStatus::NotDone;
+ shared_qobject_ptr<Task> m_updateTask;
};
}
diff --git a/api/logic/meta/Index.cpp b/api/logic/meta/Index.cpp
index 6e1e34cd..4e0026b3 100644
--- a/api/logic/meta/Index.cpp
+++ b/api/logic/meta/Index.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,128 +21,128 @@
namespace Meta
{
Index::Index(QObject *parent)
- : QAbstractListModel(parent)
+ : QAbstractListModel(parent)
{
}
Index::Index(const QVector<VersionListPtr> &lists, QObject *parent)
- : QAbstractListModel(parent), m_lists(lists)
+ : QAbstractListModel(parent), m_lists(lists)
{
- for (int i = 0; i < m_lists.size(); ++i)
- {
- m_uids.insert(m_lists.at(i)->uid(), m_lists.at(i));
- connectVersionList(i, m_lists.at(i));
- }
+ for (int i = 0; i < m_lists.size(); ++i)
+ {
+ m_uids.insert(m_lists.at(i)->uid(), m_lists.at(i));
+ connectVersionList(i, m_lists.at(i));
+ }
}
QVariant Index::data(const QModelIndex &index, int role) const
{
- if (index.parent().isValid() || index.row() < 0 || index.row() >= m_lists.size())
- {
- return QVariant();
- }
+ if (index.parent().isValid() || index.row() < 0 || index.row() >= m_lists.size())
+ {
+ return QVariant();
+ }
- VersionListPtr list = m_lists.at(index.row());
- switch (role)
- {
- case Qt::DisplayRole:
- switch (index.column())
- {
- case 0: return list->humanReadable();
- default: break;
- }
- case UidRole: return list->uid();
- case NameRole: return list->name();
- case ListPtrRole: return QVariant::fromValue(list);
- }
- return QVariant();
+ VersionListPtr list = m_lists.at(index.row());
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (index.column())
+ {
+ case 0: return list->humanReadable();
+ default: break;
+ }
+ case UidRole: return list->uid();
+ case NameRole: return list->name();
+ case ListPtrRole: return QVariant::fromValue(list);
+ }
+ return QVariant();
}
int Index::rowCount(const QModelIndex &parent) const
{
- return m_lists.size();
+ return m_lists.size();
}
int Index::columnCount(const QModelIndex &parent) const
{
- return 1;
+ return 1;
}
QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const
{
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0)
- {
- return tr("Name");
- }
- else
- {
- return QVariant();
- }
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0)
+ {
+ return tr("Name");
+ }
+ else
+ {
+ return QVariant();
+ }
}
bool Index::hasUid(const QString &uid) const
{
- return m_uids.contains(uid);
+ return m_uids.contains(uid);
}
VersionListPtr Index::get(const QString &uid)
{
- VersionListPtr out = m_uids.value(uid, nullptr);
- if(!out)
- {
- out = std::make_shared<VersionList>(uid);
- m_uids[uid] = out;
- }
- return out;
+ VersionListPtr out = m_uids.value(uid, nullptr);
+ if(!out)
+ {
+ out = std::make_shared<VersionList>(uid);
+ m_uids[uid] = out;
+ }
+ return out;
}
VersionPtr Index::get(const QString &uid, const QString &version)
{
- auto list = get(uid);
- return list->getVersion(version);
+ auto list = get(uid);
+ return list->getVersion(version);
}
void Index::parse(const QJsonObject& obj)
{
- parseIndex(obj, this);
+ parseIndex(obj, this);
}
void Index::merge(const std::shared_ptr<Index> &other)
{
- const QVector<VersionListPtr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;
- // initial load, no need to merge
- if (m_lists.isEmpty())
- {
- beginResetModel();
- m_lists = lists;
- for (int i = 0; i < lists.size(); ++i)
- {
- m_uids.insert(lists.at(i)->uid(), lists.at(i));
- connectVersionList(i, lists.at(i));
- }
- endResetModel();
- }
- else
- {
- for (const VersionListPtr &list : lists)
- {
- if (m_uids.contains(list->uid()))
- {
- m_uids[list->uid()]->mergeFromIndex(list);
- }
- else
- {
- beginInsertRows(QModelIndex(), m_lists.size(), m_lists.size());
- connectVersionList(m_lists.size(), list);
- m_lists.append(list);
- m_uids.insert(list->uid(), list);
- endInsertRows();
- }
- }
- }
+ const QVector<VersionListPtr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;
+ // initial load, no need to merge
+ if (m_lists.isEmpty())
+ {
+ beginResetModel();
+ m_lists = lists;
+ for (int i = 0; i < lists.size(); ++i)
+ {
+ m_uids.insert(lists.at(i)->uid(), lists.at(i));
+ connectVersionList(i, lists.at(i));
+ }
+ endResetModel();
+ }
+ else
+ {
+ for (const VersionListPtr &list : lists)
+ {
+ if (m_uids.contains(list->uid()))
+ {
+ m_uids[list->uid()]->mergeFromIndex(list);
+ }
+ else
+ {
+ beginInsertRows(QModelIndex(), m_lists.size(), m_lists.size());
+ connectVersionList(m_lists.size(), list);
+ m_lists.append(list);
+ m_uids.insert(list->uid(), list);
+ endInsertRows();
+ }
+ }
+ }
}
void Index::connectVersionList(const int row, const VersionListPtr &list)
{
- connect(list.get(), &VersionList::nameChanged, this, [this, row]()
- {
- emit dataChanged(index(row), index(row), QVector<int>() << Qt::DisplayRole);
- });
+ connect(list.get(), &VersionList::nameChanged, this, [this, row]()
+ {
+ emit dataChanged(index(row), index(row), QVector<int>() << Qt::DisplayRole);
+ });
}
}
diff --git a/api/logic/meta/Index.h b/api/logic/meta/Index.h
index 0ec43f96..7a84ff38 100644
--- a/api/logic/meta/Index.h
+++ b/api/logic/meta/Index.h
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,41 +31,41 @@ using VersionPtr = std::shared_ptr<class Version>;
class MULTIMC_LOGIC_EXPORT Index : public QAbstractListModel, public BaseEntity
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit Index(QObject *parent = nullptr);
- explicit Index(const QVector<VersionListPtr> &lists, QObject *parent = nullptr);
+ explicit Index(QObject *parent = nullptr);
+ explicit Index(const QVector<VersionListPtr> &lists, QObject *parent = nullptr);
- enum
- {
- UidRole = Qt::UserRole,
- NameRole,
- ListPtrRole
- };
+ enum
+ {
+ UidRole = Qt::UserRole,
+ NameRole,
+ ListPtrRole
+ };
- QVariant data(const QModelIndex &index, int role) const override;
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
- QString localFilename() const override { return "index.json"; }
+ QString localFilename() const override { return "index.json"; }
- // queries
- VersionListPtr get(const QString &uid);
- VersionPtr get(const QString &uid, const QString &version);
- bool hasUid(const QString &uid) const;
+ // queries
+ VersionListPtr get(const QString &uid);
+ VersionPtr get(const QString &uid, const QString &version);
+ bool hasUid(const QString &uid) const;
- QVector<VersionListPtr> lists() const { return m_lists; }
+ QVector<VersionListPtr> lists() const { return m_lists; }
public: // for usage by parsers only
- void merge(const std::shared_ptr<Index> &other);
- void parse(const QJsonObject &obj) override;
+ void merge(const std::shared_ptr<Index> &other);
+ void parse(const QJsonObject &obj) override;
private:
- QVector<VersionListPtr> m_lists;
- QHash<QString, VersionListPtr> m_uids;
+ QVector<VersionListPtr> m_lists;
+ QHash<QString, VersionListPtr> m_uids;
- void connectVersionList(const int row, const VersionListPtr &list);
+ void connectVersionList(const int row, const VersionListPtr &list);
};
}
diff --git a/api/logic/meta/Index_test.cpp b/api/logic/meta/Index_test.cpp
index 1c5face2..b0892070 100644
--- a/api/logic/meta/Index_test.cpp
+++ b/api/logic/meta/Index_test.cpp
@@ -7,36 +7,36 @@
class IndexTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
private
slots:
- void test_isProvidedByEnv()
- {
- QVERIFY(ENV.metadataIndex());
- QCOMPARE(ENV.metadataIndex(), ENV.metadataIndex());
- }
+ void test_isProvidedByEnv()
+ {
+ QVERIFY(ENV.metadataIndex());
+ QCOMPARE(ENV.metadataIndex(), ENV.metadataIndex());
+ }
- void test_hasUid_and_getList()
- {
- Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
- QVERIFY(windex.hasUid("list1"));
- QVERIFY(!windex.hasUid("asdf"));
- QVERIFY(windex.get("list2") != nullptr);
- QCOMPARE(windex.get("list2")->uid(), QString("list2"));
- QVERIFY(windex.get("adsf") != nullptr);
- }
+ void test_hasUid_and_getList()
+ {
+ Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
+ QVERIFY(windex.hasUid("list1"));
+ QVERIFY(!windex.hasUid("asdf"));
+ QVERIFY(windex.get("list2") != nullptr);
+ QCOMPARE(windex.get("list2")->uid(), QString("list2"));
+ QVERIFY(windex.get("adsf") != nullptr);
+ }
- void test_merge()
- {
- Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
- QCOMPARE(windex.lists().size(), 3);
- windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")})));
- QCOMPARE(windex.lists().size(), 3);
- windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list4"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list5")})));
- QCOMPARE(windex.lists().size(), 5);
- windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list6")})));
- QCOMPARE(windex.lists().size(), 6);
- }
+ void test_merge()
+ {
+ Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
+ QCOMPARE(windex.lists().size(), 3);
+ windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")})));
+ QCOMPARE(windex.lists().size(), 3);
+ windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list4"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list5")})));
+ QCOMPARE(windex.lists().size(), 5);
+ windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list6")})));
+ QCOMPARE(windex.lists().size(), 6);
+ }
};
QTEST_GUILESS_MAIN(IndexTest)
diff --git a/api/logic/meta/JsonFormat.cpp b/api/logic/meta/JsonFormat.cpp
index 2183e579..41abb8e9 100644
--- a/api/logic/meta/JsonFormat.cpp
+++ b/api/logic/meta/JsonFormat.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,141 +30,141 @@ namespace Meta
MetadataVersion currentFormatVersion()
{
- return MetadataVersion::InitialRelease;
+ return MetadataVersion::InitialRelease;
}
// Index
static std::shared_ptr<Index> parseIndexInternal(const QJsonObject &obj)
{
- const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages");
- QVector<VersionListPtr> lists;
- lists.reserve(objects.size());
- std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj)
- {
- VersionListPtr list = std::make_shared<VersionList>(requireString(obj, "uid"));
- list->setName(ensureString(obj, "name", QString()));
- return list;
- });
- return std::make_shared<Index>(lists);
+ const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages");
+ QVector<VersionListPtr> lists;
+ lists.reserve(objects.size());
+ std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj)
+ {
+ VersionListPtr list = std::make_shared<VersionList>(requireString(obj, "uid"));
+ list->setName(ensureString(obj, "name", QString()));
+ return list;
+ });
+ return std::make_shared<Index>(lists);
}
// Version
static VersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj)
{
- VersionPtr version = std::make_shared<Version>(uid, requireString(obj, "version"));
- version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
- version->setType(ensureString(obj, "type", QString()));
- version->setRecommended(ensureBoolean(obj, QString("recommended"), false));
- version->setVolatile(ensureBoolean(obj, QString("volatile"), false));
- RequireSet requires, conflicts;
- parseRequires(obj, &requires, "requires");
- parseRequires(obj, &conflicts, "conflicts");
- version->setRequires(requires, conflicts);
- return version;
+ VersionPtr version = std::make_shared<Version>(uid, requireString(obj, "version"));
+ version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
+ version->setType(ensureString(obj, "type", QString()));
+ version->setRecommended(ensureBoolean(obj, QString("recommended"), false));
+ version->setVolatile(ensureBoolean(obj, QString("volatile"), false));
+ RequireSet requires, conflicts;
+ parseRequires(obj, &requires, "requires");
+ parseRequires(obj, &conflicts, "conflicts");
+ version->setRequires(requires, conflicts);
+ return version;
}
static std::shared_ptr<Version> parseVersionInternal(const QJsonObject &obj)
{
- VersionPtr version = parseCommonVersion(requireString(obj, "uid"), obj);
+ VersionPtr version = parseCommonVersion(requireString(obj, "uid"), obj);
- version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj),
- QString("%1/%2.json").arg(version->uid(), version->version()),
- obj.contains("order")));
- return version;
+ version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj),
+ QString("%1/%2.json").arg(version->uid(), version->version()),
+ obj.contains("order")));
+ return version;
}
// Version list / package
static std::shared_ptr<VersionList> parseVersionListInternal(const QJsonObject &obj)
{
- const QString uid = requireString(obj, "uid");
-
- const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions");
- QVector<VersionPtr> versions;
- versions.reserve(versionsRaw.size());
- std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject &vObj)
- {
- auto version = parseCommonVersion(uid, vObj);
- version->setProvidesRecommendations();
- return version;
- });
-
- VersionListPtr list = std::make_shared<VersionList>(uid);
- list->setName(ensureString(obj, "name", QString()));
- list->setVersions(versions);
- return list;
+ const QString uid = requireString(obj, "uid");
+
+ const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions");
+ QVector<VersionPtr> versions;
+ versions.reserve(versionsRaw.size());
+ std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject &vObj)
+ {
+ auto version = parseCommonVersion(uid, vObj);
+ version->setProvidesRecommendations();
+ return version;
+ });
+
+ VersionListPtr list = std::make_shared<VersionList>(uid);
+ list->setName(ensureString(obj, "name", QString()));
+ list->setVersions(versions);
+ return list;
}
MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required)
{
- if (!obj.contains("formatVersion"))
- {
- if(required)
- {
- return MetadataVersion::Invalid;
- }
- return MetadataVersion::InitialRelease;
- }
- if (!obj.value("formatVersion").isDouble())
- {
- return MetadataVersion::Invalid;
- }
- switch(obj.value("formatVersion").toInt())
- {
- case 0:
- case 1:
- return MetadataVersion::InitialRelease;
- default:
- return MetadataVersion::Invalid;
- }
+ if (!obj.contains("formatVersion"))
+ {
+ if(required)
+ {
+ return MetadataVersion::Invalid;
+ }
+ return MetadataVersion::InitialRelease;
+ }
+ if (!obj.value("formatVersion").isDouble())
+ {
+ return MetadataVersion::Invalid;
+ }
+ switch(obj.value("formatVersion").toInt())
+ {
+ case 0:
+ case 1:
+ return MetadataVersion::InitialRelease;
+ default:
+ return MetadataVersion::Invalid;
+ }
}
void serializeFormatVersion(QJsonObject& obj, Meta::MetadataVersion version)
{
- if(version == MetadataVersion::Invalid)
- {
- return;
- }
- obj.insert("formatVersion", int(version));
+ if(version == MetadataVersion::Invalid)
+ {
+ return;
+ }
+ obj.insert("formatVersion", int(version));
}
void parseIndex(const QJsonObject &obj, Index *ptr)
{
- const MetadataVersion version = parseFormatVersion(obj);
- switch (version)
- {
- case MetadataVersion::InitialRelease:
- ptr->merge(parseIndexInternal(obj));
- break;
- case MetadataVersion::Invalid:
- throw ParseException(QObject::tr("Unknown format version!"));
- }
+ const MetadataVersion version = parseFormatVersion(obj);
+ switch (version)
+ {
+ case MetadataVersion::InitialRelease:
+ ptr->merge(parseIndexInternal(obj));
+ break;
+ case MetadataVersion::Invalid:
+ throw ParseException(QObject::tr("Unknown format version!"));
+ }
}
void parseVersionList(const QJsonObject &obj, VersionList *ptr)
{
- const MetadataVersion version = parseFormatVersion(obj);
- switch (version)
- {
- case MetadataVersion::InitialRelease:
- ptr->merge(parseVersionListInternal(obj));
- break;
- case MetadataVersion::Invalid:
- throw ParseException(QObject::tr("Unknown format version!"));
- }
+ const MetadataVersion version = parseFormatVersion(obj);
+ switch (version)
+ {
+ case MetadataVersion::InitialRelease:
+ ptr->merge(parseVersionListInternal(obj));
+ break;
+ case MetadataVersion::Invalid:
+ throw ParseException(QObject::tr("Unknown format version!"));
+ }
}
void parseVersion(const QJsonObject &obj, Version *ptr)
{
- const MetadataVersion version = parseFormatVersion(obj);
- switch (version)
- {
- case MetadataVersion::InitialRelease:
- ptr->merge(parseVersionInternal(obj));
- break;
- case MetadataVersion::Invalid:
- throw ParseException(QObject::tr("Unknown format version!"));
- }
+ const MetadataVersion version = parseFormatVersion(obj);
+ switch (version)
+ {
+ case MetadataVersion::InitialRelease:
+ ptr->merge(parseVersionInternal(obj));
+ break;
+ case MetadataVersion::Invalid:
+ throw ParseException(QObject::tr("Unknown format version!"));
+ }
}
/*
@@ -174,44 +174,44 @@ void parseVersion(const QJsonObject &obj, Version *ptr)
*/
void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char * keyName)
{
- if(obj.contains(keyName))
- {
- QSet<QString> requires;
- auto reqArray = requireArray(obj, keyName);
- auto iter = reqArray.begin();
- while(iter != reqArray.end())
- {
- auto reqObject = requireObject(*iter);
- auto uid = requireString(reqObject, "uid");
- auto equals = ensureString(reqObject, "equals", QString());
- auto suggests = ensureString(reqObject, "suggests", QString());
- ptr->insert({uid, equals, suggests});
- iter++;
- }
- }
+ if(obj.contains(keyName))
+ {
+ QSet<QString> requires;
+ auto reqArray = requireArray(obj, keyName);
+ auto iter = reqArray.begin();
+ while(iter != reqArray.end())
+ {
+ auto reqObject = requireObject(*iter);
+ auto uid = requireString(reqObject, "uid");
+ auto equals = ensureString(reqObject, "equals", QString());
+ auto suggests = ensureString(reqObject, "suggests", QString());
+ ptr->insert({uid, equals, suggests});
+ iter++;
+ }
+ }
}
void serializeRequires(QJsonObject& obj, RequireSet* ptr, const char * keyName)
{
- if(!ptr || ptr->empty())
- {
- return;
- }
- QJsonArray arrOut;
- for(auto &iter: *ptr)
- {
- QJsonObject reqOut;
- reqOut.insert("uid", iter.uid);
- if(!iter.equalsVersion.isEmpty())
- {
- reqOut.insert("equals", iter.equalsVersion);
- }
- if(!iter.suggests.isEmpty())
- {
- reqOut.insert("suggests", iter.suggests);
- }
- arrOut.append(reqOut);
- }
- obj.insert(keyName, arrOut);
+ if(!ptr || ptr->empty())
+ {
+ return;
+ }
+ QJsonArray arrOut;
+ for(auto &iter: *ptr)
+ {
+ QJsonObject reqOut;
+ reqOut.insert("uid", iter.uid);
+ if(!iter.equalsVersion.isEmpty())
+ {
+ reqOut.insert("equals", iter.equalsVersion);
+ }
+ if(!iter.suggests.isEmpty())
+ {
+ reqOut.insert("suggests", iter.suggests);
+ }
+ arrOut.append(reqOut);
+ }
+ obj.insert(keyName, arrOut);
}
}
diff --git a/api/logic/meta/JsonFormat.h b/api/logic/meta/JsonFormat.h
index 762a36f6..06732ffd 100644
--- a/api/logic/meta/JsonFormat.h
+++ b/api/logic/meta/JsonFormat.h
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,39 +30,39 @@ class VersionList;
enum class MetadataVersion
{
- Invalid = -1,
- InitialRelease = 1
+ Invalid = -1,
+ InitialRelease = 1
};
class ParseException : public Exception
{
public:
- using Exception::Exception;
+ using Exception::Exception;
};
struct Require
{
- bool operator==(const Require & rhs) const
- {
- return uid == rhs.uid;
- }
- bool operator<(const Require & rhs) const
- {
- return uid < rhs.uid;
- }
- bool deepEquals(const Require & rhs) const
- {
- return uid == rhs.uid
- && equalsVersion == rhs.equalsVersion
- && suggests == rhs.suggests;
- }
- QString uid;
- QString equalsVersion;
- QString suggests;
+ bool operator==(const Require & rhs) const
+ {
+ return uid == rhs.uid;
+ }
+ bool operator<(const Require & rhs) const
+ {
+ return uid < rhs.uid;
+ }
+ bool deepEquals(const Require & rhs) const
+ {
+ return uid == rhs.uid
+ && equalsVersion == rhs.equalsVersion
+ && suggests == rhs.suggests;
+ }
+ QString uid;
+ QString equalsVersion;
+ QString suggests;
};
inline Q_DECL_PURE_FUNCTION uint qHash(const Require &key, uint seed = 0) Q_DECL_NOTHROW
{
- return qHash(key.uid, seed);
+ return qHash(key.uid, seed);
}
using RequireSet = std::set<Require>;
@@ -80,4 +80,4 @@ void serializeRequires(QJsonObject & objOut, RequireSet* ptr, const char * keyNa
MetadataVersion currentFormatVersion();
}
-Q_DECLARE_METATYPE(std::set<Meta::Require>); \ No newline at end of file
+Q_DECLARE_METATYPE(std::set<Meta::Require>)
diff --git a/api/logic/meta/Version.cpp b/api/logic/meta/Version.cpp
index edc70f33..ca889550 100644
--- a/api/logic/meta/Version.cpp
+++ b/api/logic/meta/Version.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@
#include "minecraft/ComponentList.h"
Meta::Version::Version(const QString &uid, const QString &version)
- : BaseVersion(), m_uid(uid), m_version(version)
+ : BaseVersion(), m_uid(uid), m_version(version)
{
}
@@ -31,110 +31,110 @@ Meta::Version::~Version()
QString Meta::Version::descriptor()
{
- return m_version;
+ return m_version;
}
QString Meta::Version::name()
{
- if(m_data)
- return m_data->name;
- return m_uid;
+ if(m_data)
+ return m_data->name;
+ return m_uid;
}
QString Meta::Version::typeString() const
{
- return m_type;
+ return m_type;
}
QDateTime Meta::Version::time() const
{
- return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC);
+ return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC);
}
void Meta::Version::parse(const QJsonObject& obj)
{
- parseVersion(obj, this);
+ parseVersion(obj, this);
}
void Meta::Version::mergeFromList(const Meta::VersionPtr& other)
{
- if(other->m_providesRecommendations)
- {
- if(m_recommended != other->m_recommended)
- {
- setRecommended(other->m_recommended);
- }
- }
- if (m_type != other->m_type)
- {
- setType(other->m_type);
- }
- if (m_time != other->m_time)
- {
- setTime(other->m_time);
- }
- if (m_requires != other->m_requires)
- {
- m_requires = other->m_requires;
- }
- if (m_conflicts != other->m_conflicts)
- {
- m_conflicts = other->m_conflicts;
- }
- if(m_volatile != other->m_volatile)
- {
- setVolatile(other->m_volatile);
- }
+ if(other->m_providesRecommendations)
+ {
+ if(m_recommended != other->m_recommended)
+ {
+ setRecommended(other->m_recommended);
+ }
+ }
+ if (m_type != other->m_type)
+ {
+ setType(other->m_type);
+ }
+ if (m_time != other->m_time)
+ {
+ setTime(other->m_time);
+ }
+ if (m_requires != other->m_requires)
+ {
+ m_requires = other->m_requires;
+ }
+ if (m_conflicts != other->m_conflicts)
+ {
+ m_conflicts = other->m_conflicts;
+ }
+ if(m_volatile != other->m_volatile)
+ {
+ setVolatile(other->m_volatile);
+ }
}
void Meta::Version::merge(const VersionPtr &other)
{
- mergeFromList(other);
- if(other->m_data)
- {
- setData(other->m_data);
- }
+ mergeFromList(other);
+ if(other->m_data)
+ {
+ setData(other->m_data);
+ }
}
QString Meta::Version::localFilename() const
{
- return m_uid + '/' + m_version + ".json";
+ return m_uid + '/' + m_version + ".json";
}
void Meta::Version::setType(const QString &type)
{
- m_type = type;
- emit typeChanged();
+ m_type = type;
+ emit typeChanged();
}
void Meta::Version::setTime(const qint64 time)
{
- m_time = time;
- emit timeChanged();
+ m_time = time;
+ emit timeChanged();
}
void Meta::Version::setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts)
{
- m_requires = requires;
- m_conflicts = conflicts;
- emit requiresChanged();
+ m_requires = requires;
+ m_conflicts = conflicts;
+ emit requiresChanged();
}
void Meta::Version::setVolatile(bool volatile_)
{
- m_volatile = volatile_;
+ m_volatile = volatile_;
}
void Meta::Version::setData(const VersionFilePtr &data)
{
- m_data = data;
+ m_data = data;
}
void Meta::Version::setProvidesRecommendations()
{
- m_providesRecommendations = true;
+ m_providesRecommendations = true;
}
void Meta::Version::setRecommended(bool recommended)
{
- m_recommended = recommended;
+ m_recommended = recommended;
}
diff --git a/api/logic/meta/Version.h b/api/logic/meta/Version.h
index 33bd5b35..d902049a 100644
--- a/api/logic/meta/Version.h
+++ b/api/logic/meta/Version.h
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,82 +36,82 @@ using VersionPtr = std::shared_ptr<class Version>;
class MULTIMC_LOGIC_EXPORT Version : public QObject, public BaseVersion, public BaseEntity
{
- Q_OBJECT
+ Q_OBJECT
public: /* con/des */
- explicit Version(const QString &uid, const QString &version);
- virtual ~Version();
-
- QString descriptor() override;
- QString name() override;
- QString typeString() const override;
-
- QString uid() const
- {
- return m_uid;
- }
- QString version() const
- {
- return m_version;
- }
- QString type() const
- {
- return m_type;
- }
- QDateTime time() const;
- qint64 rawTime() const
- {
- return m_time;
- }
- const Meta::RequireSet &requires() const
- {
- return m_requires;
- }
- VersionFilePtr data() const
- {
- return m_data;
- }
- bool isRecommended() const
- {
- return m_recommended;
- }
- bool isLoaded() const
- {
- return m_data != nullptr;
- }
-
- void merge(const VersionPtr &other);
- void mergeFromList(const VersionPtr &other);
- void parse(const QJsonObject &obj) override;
-
- QString localFilename() const override;
+ explicit Version(const QString &uid, const QString &version);
+ virtual ~Version();
+
+ QString descriptor() override;
+ QString name() override;
+ QString typeString() const override;
+
+ QString uid() const
+ {
+ return m_uid;
+ }
+ QString version() const
+ {
+ return m_version;
+ }
+ QString type() const
+ {
+ return m_type;
+ }
+ QDateTime time() const;
+ qint64 rawTime() const
+ {
+ return m_time;
+ }
+ const Meta::RequireSet &requires() const
+ {
+ return m_requires;
+ }
+ VersionFilePtr data() const
+ {
+ return m_data;
+ }
+ bool isRecommended() const
+ {
+ return m_recommended;
+ }
+ bool isLoaded() const
+ {
+ return m_data != nullptr;
+ }
+
+ void merge(const VersionPtr &other);
+ void mergeFromList(const VersionPtr &other);
+ void parse(const QJsonObject &obj) override;
+
+ QString localFilename() const override;
public: // for usage by format parsers only
- void setType(const QString &type);
- void setTime(const qint64 time);
- void setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts);
- void setVolatile(bool volatile_);
- void setRecommended(bool recommended);
- void setProvidesRecommendations();
- void setData(const VersionFilePtr &data);
+ void setType(const QString &type);
+ void setTime(const qint64 time);
+ void setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts);
+ void setVolatile(bool volatile_);
+ void setRecommended(bool recommended);
+ void setProvidesRecommendations();
+ void setData(const VersionFilePtr &data);
signals:
- void typeChanged();
- void timeChanged();
- void requiresChanged();
+ void typeChanged();
+ void timeChanged();
+ void requiresChanged();
private:
- bool m_providesRecommendations = false;
- bool m_recommended = false;
- QString m_name;
- QString m_uid;
- QString m_version;
- QString m_type;
- qint64 m_time = 0;
- Meta::RequireSet m_requires;
- Meta::RequireSet m_conflicts;
- bool m_volatile = false;
- VersionFilePtr m_data;
+ bool m_providesRecommendations = false;
+ bool m_recommended = false;
+ QString m_name;
+ QString m_uid;
+ QString m_version;
+ QString m_type;
+ qint64 m_time = 0;
+ Meta::RequireSet m_requires;
+ Meta::RequireSet m_conflicts;
+ bool m_volatile = false;
+ VersionFilePtr m_data;
};
}
diff --git a/api/logic/meta/VersionList.cpp b/api/logic/meta/VersionList.cpp
index 9ae02301..cc15d0e4 100644
--- a/api/logic/meta/VersionList.cpp
+++ b/api/logic/meta/VersionList.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,222 +24,222 @@
namespace Meta
{
VersionList::VersionList(const QString &uid, QObject *parent)
- : BaseVersionList(parent), m_uid(uid)
+ : BaseVersionList(parent), m_uid(uid)
{
- setObjectName("Version list: " + uid);
+ setObjectName("Version list: " + uid);
}
shared_qobject_ptr<Task> VersionList::getLoadTask()
{
- load(Net::Mode::Online);
- return getCurrentTask();
+ load(Net::Mode::Online);
+ return getCurrentTask();
}
bool VersionList::isLoaded()
{
- return BaseEntity::isLoaded();
+ return BaseEntity::isLoaded();
}
const BaseVersionPtr VersionList::at(int i) const
{
- return m_versions.at(i);
+ return m_versions.at(i);
}
int VersionList::count() const
{
- return m_versions.size();
+ return m_versions.size();
}
void VersionList::sortVersions()
{
- beginResetModel();
- std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
- {
- return *a.get() < *b.get();
- });
- endResetModel();
+ beginResetModel();
+ std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
+ {
+ return *a.get() < *b.get();
+ });
+ endResetModel();
}
QVariant VersionList::data(const QModelIndex &index, int role) const
{
- if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid())
- {
- return QVariant();
- }
-
- VersionPtr version = m_versions.at(index.row());
-
- switch (role)
- {
- case VersionPointerRole: return QVariant::fromValue(std::dynamic_pointer_cast<BaseVersion>(version));
- case VersionRole:
- case VersionIdRole:
- return version->version();
- case ParentVersionRole:
- {
- // FIXME: HACK: this should be generic and be replaced by something else. Anything that is a hard 'equals' dep is a 'parent uid'.
- auto & reqs = version->requires();
- auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Require & req)
- {
- return req.uid == "net.minecraft";
- });
- if (iter != reqs.end())
- {
- return (*iter).equalsVersion;
- }
- return QVariant();
- }
- case TypeRole: return version->type();
-
- case UidRole: return version->uid();
- case TimeRole: return version->time();
- case RequiresRole: return QVariant::fromValue(version->requires());
- case SortRole: return version->rawTime();
- case VersionPtrRole: return QVariant::fromValue(version);
- case RecommendedRole: return version->isRecommended();
- // FIXME: this should be determined in whatever view/proxy is used...
- // case LatestRole: return version == getLatestStable();
- default: return QVariant();
- }
+ if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid())
+ {
+ return QVariant();
+ }
+
+ VersionPtr version = m_versions.at(index.row());
+
+ switch (role)
+ {
+ case VersionPointerRole: return QVariant::fromValue(std::dynamic_pointer_cast<BaseVersion>(version));
+ case VersionRole:
+ case VersionIdRole:
+ return version->version();
+ case ParentVersionRole:
+ {
+ // FIXME: HACK: this should be generic and be replaced by something else. Anything that is a hard 'equals' dep is a 'parent uid'.
+ auto & reqs = version->requires();
+ auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Require & req)
+ {
+ return req.uid == "net.minecraft";
+ });
+ if (iter != reqs.end())
+ {
+ return (*iter).equalsVersion;
+ }
+ return QVariant();
+ }
+ case TypeRole: return version->type();
+
+ case UidRole: return version->uid();
+ case TimeRole: return version->time();
+ case RequiresRole: return QVariant::fromValue(version->requires());
+ case SortRole: return version->rawTime();
+ case VersionPtrRole: return QVariant::fromValue(version);
+ case RecommendedRole: return version->isRecommended();
+ // FIXME: this should be determined in whatever view/proxy is used...
+ // case LatestRole: return version == getLatestStable();
+ default: return QVariant();
+ }
}
BaseVersionList::RoleList VersionList::providesRoles() const
{
- return {VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole,
- TypeRole, UidRole, TimeRole, RequiresRole, SortRole,
- RecommendedRole, LatestRole, VersionPtrRole};
+ return {VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole,
+ TypeRole, UidRole, TimeRole, RequiresRole, SortRole,
+ RecommendedRole, LatestRole, VersionPtrRole};
}
QHash<int, QByteArray> VersionList::roleNames() const
{
- QHash<int, QByteArray> roles = BaseVersionList::roleNames();
- roles.insert(UidRole, "uid");
- roles.insert(TimeRole, "time");
- roles.insert(SortRole, "sort");
- roles.insert(RequiresRole, "requires");
- return roles;
+ QHash<int, QByteArray> roles = BaseVersionList::roleNames();
+ roles.insert(UidRole, "uid");
+ roles.insert(TimeRole, "time");
+ roles.insert(SortRole, "sort");
+ roles.insert(RequiresRole, "requires");
+ return roles;
}
QString VersionList::localFilename() const
{
- return m_uid + "/index.json";
+ return m_uid + "/index.json";
}
QString VersionList::humanReadable() const
{
- return m_name.isEmpty() ? m_uid : m_name;
+ return m_name.isEmpty() ? m_uid : m_name;
}
VersionPtr VersionList::getVersion(const QString &version)
{
- VersionPtr out = m_lookup.value(version, nullptr);
- if(!out)
- {
- out = std::make_shared<Version>(m_uid, version);
- m_lookup[version] = out;
- }
- return out;
+ VersionPtr out = m_lookup.value(version, nullptr);
+ if(!out)
+ {
+ out = std::make_shared<Version>(m_uid, version);
+ m_lookup[version] = out;
+ }
+ return out;
}
void VersionList::setName(const QString &name)
{
- m_name = name;
- emit nameChanged(name);
+ m_name = name;
+ emit nameChanged(name);
}
void VersionList::setVersions(const QVector<VersionPtr> &versions)
{
- beginResetModel();
- m_versions = versions;
- std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
- {
- return a->rawTime() > b->rawTime();
- });
- for (int i = 0; i < m_versions.size(); ++i)
- {
- m_lookup.insert(m_versions.at(i)->version(), m_versions.at(i));
- setupAddedVersion(i, m_versions.at(i));
- }
+ beginResetModel();
+ m_versions = versions;
+ std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
+ {
+ return a->rawTime() > b->rawTime();
+ });
+ for (int i = 0; i < m_versions.size(); ++i)
+ {
+ m_lookup.insert(m_versions.at(i)->version(), m_versions.at(i));
+ setupAddedVersion(i, m_versions.at(i));
+ }
- // FIXME: this is dumb, we have 'recommended' as part of the metadata already...
- auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const VersionPtr &ptr) { return ptr->type() == "release"; });
- m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt;
- endResetModel();
+ // FIXME: this is dumb, we have 'recommended' as part of the metadata already...
+ auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const VersionPtr &ptr) { return ptr->type() == "release"; });
+ m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt;
+ endResetModel();
}
void VersionList::parse(const QJsonObject& obj)
{
- parseVersionList(obj, this);
+ parseVersionList(obj, this);
}
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
static const Meta::VersionPtr &getBetterVersion(const Meta::VersionPtr &a, const Meta::VersionPtr &b)
{
- if(!a)
- return b;
- if(!b)
- return a;
- if(a->type() == b->type())
- {
- // newer of same type wins
- return (a->rawTime() > b->rawTime() ? a : b);
- }
- // 'release' type wins
- return (a->type() == "release" ? a : b);
+ if(!a)
+ return b;
+ if(!b)
+ return a;
+ if(a->type() == b->type())
+ {
+ // newer of same type wins
+ return (a->rawTime() > b->rawTime() ? a : b);
+ }
+ // 'release' type wins
+ return (a->type() == "release" ? a : b);
}
void VersionList::mergeFromIndex(const VersionListPtr &other)
{
- if (m_name != other->m_name)
- {
- setName(other->m_name);
- }
+ if (m_name != other->m_name)
+ {
+ setName(other->m_name);
+ }
}
void VersionList::merge(const VersionListPtr &other)
{
- if (m_name != other->m_name)
- {
- setName(other->m_name);
- }
-
- // TODO: do not reset the whole model. maybe?
- beginResetModel();
- m_versions.clear();
- if(other->m_versions.isEmpty())
- {
- qWarning() << "Empty list loaded ...";
- }
- for (const VersionPtr &version : other->m_versions)
- {
- // we already have the version. merge the contents
- if (m_lookup.contains(version->version()))
- {
- m_lookup.value(version->version())->mergeFromList(version);
- }
- else
- {
- m_lookup.insert(version->uid(), version);
- }
- // connect it.
- setupAddedVersion(m_versions.size(), version);
- m_versions.append(version);
- m_recommended = getBetterVersion(m_recommended, version);
- }
- endResetModel();
+ if (m_name != other->m_name)
+ {
+ setName(other->m_name);
+ }
+
+ // TODO: do not reset the whole model. maybe?
+ beginResetModel();
+ m_versions.clear();
+ if(other->m_versions.isEmpty())
+ {
+ qWarning() << "Empty list loaded ...";
+ }
+ for (const VersionPtr &version : other->m_versions)
+ {
+ // we already have the version. merge the contents
+ if (m_lookup.contains(version->version()))
+ {
+ m_lookup.value(version->version())->mergeFromList(version);
+ }
+ else
+ {
+ m_lookup.insert(version->uid(), version);
+ }
+ // connect it.
+ setupAddedVersion(m_versions.size(), version);
+ m_versions.append(version);
+ m_recommended = getBetterVersion(m_recommended, version);
+ }
+ endResetModel();
}
void VersionList::setupAddedVersion(const int row, const VersionPtr &version)
{
- // FIXME: do not disconnect from everythin, disconnect only the lambdas here
- version->disconnect();
- connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << RequiresRole); });
- connect(version.get(), &Version::timeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TimeRole << SortRole); });
- connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });
+ // FIXME: do not disconnect from everythin, disconnect only the lambdas here
+ version->disconnect();
+ connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << RequiresRole); });
+ connect(version.get(), &Version::timeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TimeRole << SortRole); });
+ connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });
}
BaseVersionPtr VersionList::getRecommended() const
{
- return m_recommended;
+ return m_recommended;
}
}
diff --git a/api/logic/meta/VersionList.h b/api/logic/meta/VersionList.h
index ad8733cc..24b01d08 100644
--- a/api/logic/meta/VersionList.h
+++ b/api/logic/meta/VersionList.h
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,75 +27,75 @@ using VersionListPtr = std::shared_ptr<class VersionList>;
class MULTIMC_LOGIC_EXPORT VersionList : public BaseVersionList, public BaseEntity
{
- Q_OBJECT
- Q_PROPERTY(QString uid READ uid CONSTANT)
- Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+ Q_OBJECT
+ Q_PROPERTY(QString uid READ uid CONSTANT)
+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
public:
- explicit VersionList(const QString &uid, QObject *parent = nullptr);
-
- enum Roles
- {
- UidRole = Qt::UserRole + 100,
- TimeRole,
- RequiresRole,
- VersionPtrRole
- };
-
- shared_qobject_ptr<Task> getLoadTask() override;
- bool isLoaded() override;
- const BaseVersionPtr at(int i) const override;
- int count() const override;
- void sortVersions() override;
-
- BaseVersionPtr getRecommended() const override;
-
- QVariant data(const QModelIndex &index, int role) const override;
- RoleList providesRoles() const override;
- QHash<int, QByteArray> roleNames() const override;
-
- QString localFilename() const override;
-
- QString uid() const
- {
- return m_uid;
- }
- QString name() const
- {
- return m_name;
- }
- QString humanReadable() const;
-
- VersionPtr getVersion(const QString &version);
-
- QVector<VersionPtr> versions() const
- {
- return m_versions;
- }
+ explicit VersionList(const QString &uid, QObject *parent = nullptr);
+
+ enum Roles
+ {
+ UidRole = Qt::UserRole + 100,
+ TimeRole,
+ RequiresRole,
+ VersionPtrRole
+ };
+
+ shared_qobject_ptr<Task> getLoadTask() override;
+ bool isLoaded() override;
+ const BaseVersionPtr at(int i) const override;
+ int count() const override;
+ void sortVersions() override;
+
+ BaseVersionPtr getRecommended() const override;
+
+ QVariant data(const QModelIndex &index, int role) const override;
+ RoleList providesRoles() const override;
+ QHash<int, QByteArray> roleNames() const override;
+
+ QString localFilename() const override;
+
+ QString uid() const
+ {
+ return m_uid;
+ }
+ QString name() const
+ {
+ return m_name;
+ }
+ QString humanReadable() const;
+
+ VersionPtr getVersion(const QString &version);
+
+ QVector<VersionPtr> versions() const
+ {
+ return m_versions;
+ }
public: // for usage only by parsers
- void setName(const QString &name);
- void setVersions(const QVector<VersionPtr> &versions);
- void merge(const VersionListPtr &other);
- void mergeFromIndex(const VersionListPtr &other);
- void parse(const QJsonObject &obj) override;
+ void setName(const QString &name);
+ void setVersions(const QVector<VersionPtr> &versions);
+ void merge(const VersionListPtr &other);
+ void mergeFromIndex(const VersionListPtr &other);
+ void parse(const QJsonObject &obj) override;
signals:
- void nameChanged(const QString &name);
+ void nameChanged(const QString &name);
protected slots:
- void updateListData(QList<BaseVersionPtr>) override
- {
- }
+ void updateListData(QList<BaseVersionPtr>) override
+ {
+ }
private:
- QVector<VersionPtr> m_versions;
- QHash<QString, VersionPtr> m_lookup;
- QString m_uid;
- QString m_name;
+ QVector<VersionPtr> m_versions;
+ QHash<QString, VersionPtr> m_lookup;
+ QString m_uid;
+ QString m_name;
- VersionPtr m_recommended;
+ VersionPtr m_recommended;
- void setupAddedVersion(const int row, const VersionPtr &version);
+ void setupAddedVersion(const int row, const VersionPtr &version);
};
}
Q_DECLARE_METATYPE(Meta::VersionListPtr)
diff --git a/api/logic/minecraft/AssetsUtils.cpp b/api/logic/minecraft/AssetsUtils.cpp
index 5b25bede..f94f68b1 100644
--- a/api/logic/minecraft/AssetsUtils.cpp
+++ b/api/logic/minecraft/AssetsUtils.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,34 @@
#include "FileSystem.h"
#include "net/Download.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
@@ -36,202 +64,270 @@ namespace AssetsUtils
* Returns true on success, with index populated
* index is undefined otherwise
*/
-bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index)
+bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsIndex& index)
+{
+ /*
+ {
+ "objects": {
+ "icons/icon_16x16.png": {
+ "hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a",
+ "size": 3665
+ },
+ ...
+ }
+ }
+ }
+ */
+
+ QFile file(path);
+
+ // Try to open the file and fail if we can't.
+ // TODO: We should probably report this error to the user.
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ qCritical() << "Failed to read assets index file" << path;
+ return false;
+ }
+ index.id = assetsId;
+
+ // Read the file and close it.
+ QByteArray jsonData = file.readAll();
+ file.close();
+
+ QJsonParseError parseError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
+
+ // Fail if the JSON is invalid.
+ if (parseError.error != QJsonParseError::NoError)
+ {
+ qCritical() << "Failed to parse assets index file:" << parseError.errorString()
+ << "at offset " << QString::number(parseError.offset);
+ return false;
+ }
+
+ // Make sure the root is an object.
+ if (!jsonDoc.isObject())
+ {
+ qCritical() << "Invalid assets index JSON: Root should be an array.";
+ return false;
+ }
+
+ QJsonObject root = jsonDoc.object();
+
+ QJsonValue isVirtual = root.value("virtual");
+ if (!isVirtual.isUndefined())
+ {
+ 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");
+ QVariantMap map = objects.toVariant().toMap();
+
+ for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
+ {
+ // qDebug() << iter.key();
+
+ QVariant variant = iter.value();
+ QVariantMap nested_objects = variant.toMap();
+
+ AssetObject object;
+
+ for (QVariantMap::const_iterator nested_iter = nested_objects.begin();
+ nested_iter != nested_objects.end(); ++nested_iter)
+ {
+ // qDebug() << nested_iter.key() << nested_iter.value().toString();
+ QString key = nested_iter.key();
+ QVariant value = nested_iter.value();
+
+ if (key == "hash")
+ {
+ object.hash = value.toString();
+ }
+ else if (key == "size")
+ {
+ object.size = value.toDouble();
+ }
+ }
+
+ index.objects.insert(iter.key(), object);
+ }
+
+ return true;
+}
+
+// FIXME: ugly code duplication
+QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder)
{
- /*
- {
- "objects": {
- "icons/icon_16x16.png": {
- "hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a",
- "size": 3665
- },
- ...
- }
- }
- }
- */
-
- QFile file(path);
-
- // Try to open the file and fail if we can't.
- // TODO: We should probably report this error to the user.
- if (!file.open(QIODevice::ReadOnly))
- {
- qCritical() << "Failed to read assets index file" << path;
- return false;
- }
- index->id = assetsId;
-
- // Read the file and close it.
- QByteArray jsonData = file.readAll();
- file.close();
-
- QJsonParseError parseError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
-
- // Fail if the JSON is invalid.
- if (parseError.error != QJsonParseError::NoError)
- {
- qCritical() << "Failed to parse assets index file:" << parseError.errorString()
- << "at offset " << QString::number(parseError.offset);
- return false;
- }
-
- // Make sure the root is an object.
- if (!jsonDoc.isObject())
- {
- qCritical() << "Invalid assets index JSON: Root should be an array.";
- return false;
- }
-
- QJsonObject root = jsonDoc.object();
-
- QJsonValue isVirtual = root.value("virtual");
- if (!isVirtual.isUndefined())
- {
- index->isVirtual = isVirtual.toBool(false);
- }
-
- QJsonValue objects = root.value("objects");
- QVariantMap map = objects.toVariant().toMap();
-
- for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
- {
- // qDebug() << iter.key();
-
- QVariant variant = iter.value();
- QVariantMap nested_objects = variant.toMap();
-
- AssetObject object;
-
- for (QVariantMap::const_iterator nested_iter = nested_objects.begin();
- nested_iter != nested_objects.end(); ++nested_iter)
- {
- // qDebug() << nested_iter.key() << nested_iter.value().toString();
- QString key = nested_iter.key();
- QVariant value = nested_iter.value();
-
- if (key == "hash")
- {
- object.hash = value.toString();
- }
- else if (key == "size")
- {
- object.size = value.toDouble();
- }
- }
-
- index->objects.insert(iter.key(), object);
- }
-
- return true;
+ 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 determine assets path!";
+ return virtualRoot;
+ }
+
+ AssetsIndex index;
+ 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;
}
-QDir reconstructAssets(QString assetsId)
+// 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 virtualRoot;
- }
-
- qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path()
- << objectDir.path() << virtualDir.path() << virtualRoot.path();
-
- AssetsIndex index;
- bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, &index);
-
- if (loadAssetsIndex && index.isVirtual)
- {
- qDebug() << "Reconstructing virtual assets folder at" << virtualRoot.path();
-
- for (QString map : index.objects.keys())
- {
- AssetObject asset_object = index.objects.value(map);
- QString target_path = FS::PathCombine(virtualRoot.path(), map);
- QFile target(target_path);
-
- QString tlk = asset_object.hash.left(2);
-
- QString original_path = FS::PathCombine(objectDir.path(), tlk, asset_object.hash);
- QFile original(original_path);
- if (!original.exists())
- continue;
- if (!target.exists())
- {
- QFileInfo info(target_path);
- QDir target_dir = info.dir();
- // qDebug() << target_dir;
- if (!target_dir.exists())
- QDir("").mkpath(target_dir.path());
-
- bool couldCopy = original.copy(target_path);
- qDebug() << " Copying" << original_path << "to" << target_path
- << QString::number(couldCopy); // << original.errorString();
- }
- }
-
- // TODO: Write last used time to virtualRoot/.lastused
- }
-
- return virtualRoot;
+ 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;
+ if(!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index))
+ {
+ 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())
+ {
+ AssetObject asset_object = index.objects.value(map);
+ QString target_path = FS::PathCombine(targetPath, map);
+ QFile target(target_path);
+
+ QString tlk = asset_object.hash.left(2);
+
+ QString original_path = FS::PathCombine(objectDir.path(), tlk, asset_object.hash);
+ QFile original(original_path);
+ if (!original.exists())
+ continue;
+
+ presentFiles.remove(target_path);
+
+ if (!target.exists())
+ {
+ QFileInfo info(target_path);
+ QDir target_dir = info.dir();
+
+ qDebug() << target_dir.path();
+ FS::ensureFolderPathExists(target_dir.path());
+
+ bool couldCopy = original.copy(target_path);
+ qDebug() << " Copying" << original_path << "to" << target_path << QString::number(couldCopy);
+ }
+ }
+
+ // TODO: Write last used time to virtualRoot/.lastused
+ if(removeLeftovers)
+ {
+ for(auto & file: presentFiles)
+ {
+ qDebug() << "Would remove" << file;
+ }
+ }
+ }
+ return true;
}
}
NetActionPtr AssetObject::getDownloadAction()
{
- QFileInfo objectFile(getLocalPath());
- if ((!objectFile.isFile()) || (objectFile.size() != size))
- {
- auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath());
- if(hash.size())
- {
- auto rawHash = QByteArray::fromHex(hash.toLatin1());
- objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
- }
- objectDL->m_total_progress = size;
- return objectDL;
- }
- return nullptr;
+ QFileInfo objectFile(getLocalPath());
+ if ((!objectFile.isFile()) || (objectFile.size() != size))
+ {
+ auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath());
+ if(hash.size())
+ {
+ auto rawHash = QByteArray::fromHex(hash.toLatin1());
+ objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
+ }
+ objectDL->m_total_progress = size;
+ return objectDL;
+ }
+ return nullptr;
}
QString AssetObject::getLocalPath()
{
- return "assets/objects/" + getRelPath();
+ return "assets/objects/" + getRelPath();
}
QUrl AssetObject::getUrl()
{
- return QUrl("http://resources.download.minecraft.net/" + getRelPath());
+ return URLConstants::RESOURCE_BASE + getRelPath();
}
QString AssetObject::getRelPath()
{
- return hash.left(2) + "/" + hash;
+ return hash.left(2) + "/" + hash;
}
NetJobPtr AssetsIndex::getDownloadJob()
{
- auto job = new NetJob(QObject::tr("Assets for %1").arg(id));
- for (auto &object : objects.values())
- {
- auto dl = object.getDownloadAction();
- if(dl)
- {
- job->addNetAction(dl);
- }
- }
- if(job->size())
- return job;
- return nullptr;
+ auto job = new NetJob(QObject::tr("Assets for %1").arg(id));
+ for (auto &object : objects.values())
+ {
+ auto dl = object.getDownloadAction();
+ if(dl)
+ {
+ job->addNetAction(dl);
+ }
+ }
+ if(job->size())
+ return job;
+ return nullptr;
}
diff --git a/api/logic/minecraft/AssetsUtils.h b/api/logic/minecraft/AssetsUtils.h
index b7ea9cc1..356b8c8a 100644
--- a/api/logic/minecraft/AssetsUtils.h
+++ b/api/logic/minecraft/AssetsUtils.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,27 +22,32 @@
struct AssetObject
{
- QString getRelPath();
- QUrl getUrl();
- QString getLocalPath();
- NetActionPtr getDownloadAction();
+ QString getRelPath();
+ QUrl getUrl();
+ QString getLocalPath();
+ NetActionPtr getDownloadAction();
- QString hash;
- qint64 size;
+ QString hash;
+ qint64 size;
};
struct AssetsIndex
{
- NetJobPtr getDownloadJob();
+ NetJobPtr getDownloadJob();
- QString id;
- QMap<QString, AssetObject> objects;
- bool isVirtual = false;
+ QString id;
+ QMap<QString, AssetObject> objects;
+ bool isVirtual = false;
+ bool mapToResources = false;
};
+/// FIXME: this is absolutely horrendous. REDO!!!!
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
-QDir reconstructAssets(QString assetsId);
+bool reconstructAssets(QString assetsId, QString resourcesFolder);
}
diff --git a/api/logic/minecraft/Component.cpp b/api/logic/minecraft/Component.cpp
index 50a2ae16..51957d17 100644
--- a/api/logic/minecraft/Component.cpp
+++ b/api/logic/minecraft/Component.cpp
@@ -13,356 +13,356 @@
Component::Component(ComponentList * parent, const QString& uid)
{
- assert(parent);
- m_parent = parent;
+ assert(parent);
+ m_parent = parent;
- m_uid = uid;
+ m_uid = uid;
}
Component::Component(ComponentList * parent, std::shared_ptr<Meta::Version> version)
{
- assert(parent);
- m_parent = parent;
+ assert(parent);
+ m_parent = parent;
- m_metaVersion = version;
- m_uid = version->uid();
- m_version = m_cachedVersion = version->version();
- m_cachedName = version->name();
- m_loaded = version->isLoaded();
+ m_metaVersion = version;
+ m_uid = version->uid();
+ m_version = m_cachedVersion = version->version();
+ m_cachedName = version->name();
+ m_loaded = version->isLoaded();
}
Component::Component(ComponentList * parent, const QString& uid, std::shared_ptr<VersionFile> file)
{
- assert(parent);
- m_parent = parent;
+ assert(parent);
+ m_parent = parent;
- m_file = file;
- m_uid = uid;
- m_cachedVersion = m_file->version;
- m_cachedName = m_file->name;
- m_loaded = true;
+ m_file = file;
+ m_uid = uid;
+ m_cachedVersion = m_file->version;
+ m_cachedName = m_file->name;
+ m_loaded = true;
}
std::shared_ptr<Meta::Version> Component::getMeta()
{
- return m_metaVersion;
+ return m_metaVersion;
}
void Component::applyTo(LaunchProfile* profile)
{
- // do not apply disabled components
- if(!isEnabled())
- {
- return;
- }
- auto vfile = getVersionFile();
- if(vfile)
- {
- vfile->applyTo(profile);
- }
- else
- {
- profile->applyProblemSeverity(getProblemSeverity());
- }
+ // do not apply disabled components
+ if(!isEnabled())
+ {
+ return;
+ }
+ auto vfile = getVersionFile();
+ if(vfile)
+ {
+ vfile->applyTo(profile);
+ }
+ else
+ {
+ profile->applyProblemSeverity(getProblemSeverity());
+ }
}
std::shared_ptr<class VersionFile> Component::getVersionFile() const
{
- if(m_metaVersion)
- {
- if(!m_metaVersion->isLoaded())
- {
- m_metaVersion->load(Net::Mode::Online);
- }
- return m_metaVersion->data();
- }
- else
- {
- return m_file;
- }
+ if(m_metaVersion)
+ {
+ if(!m_metaVersion->isLoaded())
+ {
+ m_metaVersion->load(Net::Mode::Online);
+ }
+ return m_metaVersion->data();
+ }
+ else
+ {
+ return m_file;
+ }
}
std::shared_ptr<class Meta::VersionList> Component::getVersionList() const
{
- // FIXME: what if the metadata index isn't loaded yet?
- if(ENV.metadataIndex()->hasUid(m_uid))
- {
- return ENV.metadataIndex()->get(m_uid);
- }
- return nullptr;
+ // FIXME: what if the metadata index isn't loaded yet?
+ if(ENV.metadataIndex()->hasUid(m_uid))
+ {
+ return ENV.metadataIndex()->get(m_uid);
+ }
+ return nullptr;
}
int Component::getOrder()
{
- if(m_orderOverride)
- return m_order;
+ if(m_orderOverride)
+ return m_order;
- auto vfile = getVersionFile();
- if(vfile)
- {
- return vfile->order;
- }
- return 0;
+ auto vfile = getVersionFile();
+ if(vfile)
+ {
+ return vfile->order;
+ }
+ return 0;
}
void Component::setOrder(int order)
{
- m_orderOverride = true;
- m_order = order;
+ m_orderOverride = true;
+ m_order = order;
}
QString Component::getID()
{
- return m_uid;
+ return m_uid;
}
QString Component::getName()
{
- if (!m_cachedName.isEmpty())
- return m_cachedName;
- return m_uid;
+ if (!m_cachedName.isEmpty())
+ return m_cachedName;
+ return m_uid;
}
QString Component::getVersion()
{
- return m_cachedVersion;
+ return m_cachedVersion;
}
QString Component::getFilename()
{
- return m_parent->patchFilePathForUid(m_uid);
+ return m_parent->patchFilePathForUid(m_uid);
}
QDateTime Component::getReleaseDateTime()
{
- if(m_metaVersion)
- {
- return m_metaVersion->time();
- }
- auto vfile = getVersionFile();
- if(vfile)
- {
- return vfile->releaseTime;
- }
- // FIXME: fake
- return QDateTime::currentDateTime();
+ if(m_metaVersion)
+ {
+ return m_metaVersion->time();
+ }
+ auto vfile = getVersionFile();
+ if(vfile)
+ {
+ return vfile->releaseTime;
+ }
+ // FIXME: fake
+ return QDateTime::currentDateTime();
}
bool Component::isEnabled()
{
- return !canBeDisabled() || !m_disabled;
-};
+ return !canBeDisabled() || !m_disabled;
+}
bool Component::canBeDisabled()
{
- return isRemovable() && !m_dependencyOnly;
+ return isRemovable() && !m_dependencyOnly;
}
bool Component::setEnabled(bool state)
{
- bool intendedDisabled = !state;
- if (!canBeDisabled())
- {
- intendedDisabled = false;
- }
- if(intendedDisabled != m_disabled)
- {
- m_disabled = intendedDisabled;
- emit dataChanged();
- return true;
- }
- return false;
+ bool intendedDisabled = !state;
+ if (!canBeDisabled())
+ {
+ intendedDisabled = false;
+ }
+ if(intendedDisabled != m_disabled)
+ {
+ m_disabled = intendedDisabled;
+ emit dataChanged();
+ return true;
+ }
+ return false;
}
bool Component::isCustom()
{
- return m_file != nullptr;
-};
+ return m_file != nullptr;
+}
bool Component::isCustomizable()
{
- if(m_metaVersion)
- {
- if(getVersionFile())
- {
- return true;
- }
- }
- return false;
+ if(m_metaVersion)
+ {
+ if(getVersionFile())
+ {
+ return true;
+ }
+ }
+ return false;
}
bool Component::isRemovable()
{
- return !m_important;
+ return !m_important;
}
bool Component::isRevertible()
{
- if (isCustom())
- {
- if(ENV.metadataIndex()->hasUid(m_uid))
- {
- return true;
- }
- }
- return false;
+ if (isCustom())
+ {
+ if(ENV.metadataIndex()->hasUid(m_uid))
+ {
+ return true;
+ }
+ }
+ return false;
}
bool Component::isMoveable()
{
- // HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
- return true;
+ // HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
+ return true;
}
bool Component::isVersionChangeable()
{
- auto list = getVersionList();
- if(list)
- {
- if(!list->isLoaded())
- {
- list->load(Net::Mode::Online);
- }
- return list->count() != 0;
- }
- return false;
+ auto list = getVersionList();
+ if(list)
+ {
+ if(!list->isLoaded())
+ {
+ list->load(Net::Mode::Online);
+ }
+ return list->count() != 0;
+ }
+ return false;
}
void Component::setImportant(bool state)
{
- if(m_important != state)
- {
- m_important = state;
- emit dataChanged();
- }
+ if(m_important != state)
+ {
+ m_important = state;
+ emit dataChanged();
+ }
}
ProblemSeverity Component::getProblemSeverity() const
{
- auto file = getVersionFile();
- if(file)
- {
- return file->getProblemSeverity();
- }
- return ProblemSeverity::Error;
+ auto file = getVersionFile();
+ if(file)
+ {
+ return file->getProblemSeverity();
+ }
+ return ProblemSeverity::Error;
}
const QList<PatchProblem> Component::getProblems() const
{
- auto file = getVersionFile();
- if(file)
- {
- return file->getProblems();
- }
- return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}};
+ auto file = getVersionFile();
+ if(file)
+ {
+ return file->getProblems();
+ }
+ return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}};
}
void Component::setVersion(const QString& version)
{
- if(version == m_version)
- {
- return;
- }
- m_version = version;
- if(m_loaded)
- {
- // we are loaded and potentially have state to invalidate
- if(m_file)
- {
- // we have a file... explicit version has been changed and there is nothing else to do.
- }
- else
- {
- // we don't have a file, therefore we are loaded with metadata
- m_cachedVersion = version;
- // see if the meta version is loaded
- auto metaVersion = ENV.metadataIndex()->get(m_uid, version);
- if(metaVersion->isLoaded())
- {
- // if yes, we can continue with that.
- m_metaVersion = metaVersion;
- }
- else
- {
- // if not, we need loading
- m_metaVersion.reset();
- m_loaded = false;
- }
- updateCachedData();
- }
- }
- else
- {
- // not loaded... assume it will be sorted out later by the update task
- }
- emit dataChanged();
+ if(version == m_version)
+ {
+ return;
+ }
+ m_version = version;
+ if(m_loaded)
+ {
+ // we are loaded and potentially have state to invalidate
+ if(m_file)
+ {
+ // we have a file... explicit version has been changed and there is nothing else to do.
+ }
+ else
+ {
+ // we don't have a file, therefore we are loaded with metadata
+ m_cachedVersion = version;
+ // see if the meta version is loaded
+ auto metaVersion = ENV.metadataIndex()->get(m_uid, version);
+ if(metaVersion->isLoaded())
+ {
+ // if yes, we can continue with that.
+ m_metaVersion = metaVersion;
+ }
+ else
+ {
+ // if not, we need loading
+ m_metaVersion.reset();
+ m_loaded = false;
+ }
+ updateCachedData();
+ }
+ }
+ else
+ {
+ // not loaded... assume it will be sorted out later by the update task
+ }
+ emit dataChanged();
}
bool Component::customize()
{
- if(isCustom())
- {
- return false;
- }
+ if(isCustom())
+ {
+ return false;
+ }
- auto filename = getFilename();
- if(!FS::ensureFilePathExists(filename))
- {
- return false;
- }
- // FIXME: get rid of this try-catch.
- try
- {
- QSaveFile jsonFile(filename);
- if(!jsonFile.open(QIODevice::WriteOnly))
- {
- return false;
- }
- auto vfile = getVersionFile();
- if(!vfile)
- {
- return false;
- }
- auto document = OneSixVersionFormat::versionFileToJson(vfile);
- jsonFile.write(document.toJson());
- if(!jsonFile.commit())
- {
- return false;
- }
- m_file = vfile;
- m_metaVersion.reset();
- emit dataChanged();
- }
- catch (Exception &error)
- {
- qWarning() << "Version could not be loaded:" << error.cause();
- }
- return true;
+ auto filename = getFilename();
+ if(!FS::ensureFilePathExists(filename))
+ {
+ return false;
+ }
+ // FIXME: get rid of this try-catch.
+ try
+ {
+ QSaveFile jsonFile(filename);
+ if(!jsonFile.open(QIODevice::WriteOnly))
+ {
+ return false;
+ }
+ auto vfile = getVersionFile();
+ if(!vfile)
+ {
+ return false;
+ }
+ auto document = OneSixVersionFormat::versionFileToJson(vfile);
+ jsonFile.write(document.toJson());
+ if(!jsonFile.commit())
+ {
+ return false;
+ }
+ m_file = vfile;
+ m_metaVersion.reset();
+ emit dataChanged();
+ }
+ catch (const Exception &error)
+ {
+ qWarning() << "Version could not be loaded:" << error.cause();
+ }
+ return true;
}
bool Component::revert()
{
- if(!isCustom())
- {
- // already not custom
- return true;
- }
- auto filename = getFilename();
- bool result = true;
- // just kill the file and reload
- if(QFile::exists(filename))
- {
- result = QFile::remove(filename);
- }
- if(result)
- {
- // file gone...
- m_file.reset();
+ if(!isCustom())
+ {
+ // already not custom
+ return true;
+ }
+ auto filename = getFilename();
+ bool result = true;
+ // just kill the file and reload
+ if(QFile::exists(filename))
+ {
+ result = QFile::remove(filename);
+ }
+ if(result)
+ {
+ // file gone...
+ m_file.reset();
- // check local cache for metadata...
- auto version = ENV.metadataIndex()->get(m_uid, m_version);
- if(version->isLoaded())
- {
- m_metaVersion = version;
- }
- else
- {
- m_metaVersion.reset();
- m_loaded = false;
- }
- emit dataChanged();
- }
- return result;
+ // check local cache for metadata...
+ auto version = ENV.metadataIndex()->get(m_uid, m_version);
+ if(version->isLoaded())
+ {
+ m_metaVersion = version;
+ }
+ else
+ {
+ m_metaVersion.reset();
+ m_loaded = false;
+ }
+ emit dataChanged();
+ }
+ return result;
}
/**
@@ -372,68 +372,68 @@ bool Component::revert()
*/
static bool deepCompare(const std::set<Meta::Require> & a, const std::set<Meta::Require> & b)
{
- // NOTE: this needs to be rewritten if the type of Meta::RequireSet changes
- if(a.size() != b.size())
- {
- return false;
- }
- for(const auto & reqA :a)
- {
- const auto &iter2 = b.find(reqA);
- if(iter2 == b.cend())
- {
- return false;
- }
- const auto & reqB = *iter2;
- if(!reqA.deepEquals(reqB))
- {
- return false;
- }
- }
- return true;
+ // NOTE: this needs to be rewritten if the type of Meta::RequireSet changes
+ if(a.size() != b.size())
+ {
+ return false;
+ }
+ for(const auto & reqA :a)
+ {
+ const auto &iter2 = b.find(reqA);
+ if(iter2 == b.cend())
+ {
+ return false;
+ }
+ const auto & reqB = *iter2;
+ if(!reqA.deepEquals(reqB))
+ {
+ return false;
+ }
+ }
+ return true;
}
void Component::updateCachedData()
{
- auto file = getVersionFile();
- if(file)
- {
- bool changed = false;
- if(m_cachedName != file->name)
- {
- m_cachedName = file->name;
- changed = true;
- }
- if(m_cachedVersion != file->version)
- {
- m_cachedVersion = file->version;
- changed = true;
- }
- if(m_cachedVolatile != file->m_volatile)
- {
- m_cachedVolatile = file->m_volatile;
- changed = true;
- }
- if(!deepCompare(m_cachedRequires, file->requires))
- {
- m_cachedRequires = file->requires;
- changed = true;
- }
- if(!deepCompare(m_cachedConflicts, file->conflicts))
- {
- m_cachedConflicts = file->conflicts;
- changed = true;
- }
- if(changed)
- {
- emit dataChanged();
- }
- }
- else
- {
- // in case we removed all the metadata
- m_cachedRequires.clear();
- m_cachedConflicts.clear();
- emit dataChanged();
- }
+ auto file = getVersionFile();
+ if(file)
+ {
+ bool changed = false;
+ if(m_cachedName != file->name)
+ {
+ m_cachedName = file->name;
+ changed = true;
+ }
+ if(m_cachedVersion != file->version)
+ {
+ m_cachedVersion = file->version;
+ changed = true;
+ }
+ if(m_cachedVolatile != file->m_volatile)
+ {
+ m_cachedVolatile = file->m_volatile;
+ changed = true;
+ }
+ if(!deepCompare(m_cachedRequires, file->requires))
+ {
+ m_cachedRequires = file->requires;
+ changed = true;
+ }
+ if(!deepCompare(m_cachedConflicts, file->conflicts))
+ {
+ m_cachedConflicts = file->conflicts;
+ changed = true;
+ }
+ if(changed)
+ {
+ emit dataChanged();
+ }
+ }
+ else
+ {
+ // in case we removed all the metadata
+ m_cachedRequires.clear();
+ m_cachedConflicts.clear();
+ emit dataChanged();
+ }
}
diff --git a/api/logic/minecraft/Component.h b/api/logic/minecraft/Component.h
index 778fbb18..6a0f86c8 100644
--- a/api/logic/minecraft/Component.h
+++ b/api/logic/minecraft/Component.h
@@ -13,8 +13,8 @@ class ComponentList;
class LaunchProfile;
namespace Meta
{
- class Version;
- class VersionList;
+ class Version;
+ class VersionList;
}
class VersionFile;
@@ -22,90 +22,90 @@ class MULTIMC_LOGIC_EXPORT Component : public QObject, public ProblemProvider
{
Q_OBJECT
public:
- Component(ComponentList * parent, const QString &uid);
+ Component(ComponentList * parent, const QString &uid);
- // DEPRECATED: remove these constructors?
- Component(ComponentList * parent, std::shared_ptr<Meta::Version> version);
- Component(ComponentList * parent, const QString & uid, std::shared_ptr<VersionFile> file);
+ // DEPRECATED: remove these constructors?
+ Component(ComponentList * parent, std::shared_ptr<Meta::Version> version);
+ Component(ComponentList * parent, const QString & uid, std::shared_ptr<VersionFile> file);
- virtual ~Component(){};
- void applyTo(LaunchProfile *profile);
+ virtual ~Component(){};
+ void applyTo(LaunchProfile *profile);
- bool isEnabled();
- bool setEnabled (bool state);
- bool canBeDisabled();
+ bool isEnabled();
+ bool setEnabled (bool state);
+ bool canBeDisabled();
- bool isMoveable();
- bool isCustomizable();
- bool isRevertible();
- bool isRemovable();
- bool isCustom();
- bool isVersionChangeable();
+ bool isMoveable();
+ bool isCustomizable();
+ bool isRevertible();
+ bool isRemovable();
+ bool isCustom();
+ bool isVersionChangeable();
- // DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
- void setOrder(int order);
- int getOrder();
+ // DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
+ void setOrder(int order);
+ int getOrder();
- QString getID();
- QString getName();
- QString getVersion();
- std::shared_ptr<Meta::Version> getMeta();
- QDateTime getReleaseDateTime();
+ QString getID();
+ QString getName();
+ QString getVersion();
+ std::shared_ptr<Meta::Version> getMeta();
+ QDateTime getReleaseDateTime();
- QString getFilename();
+ QString getFilename();
- std::shared_ptr<class VersionFile> getVersionFile() const;
- std::shared_ptr<class Meta::VersionList> getVersionList() const;
+ std::shared_ptr<class VersionFile> getVersionFile() const;
+ std::shared_ptr<class Meta::VersionList> getVersionList() const;
- void setImportant (bool state);
+ void setImportant (bool state);
- const QList<PatchProblem> getProblems() const override;
- ProblemSeverity getProblemSeverity() const override;
+ const QList<PatchProblem> getProblems() const override;
+ ProblemSeverity getProblemSeverity() const override;
- void setVersion(const QString & version);
- bool customize();
- bool revert();
+ void setVersion(const QString & version);
+ bool customize();
+ bool revert();
- void updateCachedData();
+ void updateCachedData();
signals:
- void dataChanged();
+ void dataChanged();
public: /* data */
- ComponentList * m_parent;
-
- // BEGIN: persistent component list properties
- /// ID of the component
- QString m_uid;
- /// version of the component - when there's a custom json override, this is also the version the component reverts to
- QString m_version;
- /// if true, this has been added automatically to satisfy dependencies and may be automatically removed
- bool m_dependencyOnly = false;
- /// if true, the component is either the main component of the instance, or otherwise important and cannot be removed.
- bool m_important = false;
- /// if true, the component is disabled
- bool m_disabled = false;
-
- /// cached name for display purposes, taken from the version file (meta or local override)
- QString m_cachedName;
- /// cached version for display AND other purposes, taken from the version file (meta or local override)
- QString m_cachedVersion;
- /// cached set of requirements, taken from the version file (meta or local override)
- Meta::RequireSet m_cachedRequires;
- Meta::RequireSet m_cachedConflicts;
- /// if true, the component is volatile and may be automatically removed when no longer needed
- bool m_cachedVolatile = false;
- // END: persistent component list properties
-
- // DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
- bool m_orderOverride = false;
- int m_order = 0;
-
- // load state
- std::shared_ptr<Meta::Version> m_metaVersion;
- std::shared_ptr<VersionFile> m_file;
- bool m_loaded = false;
+ ComponentList * m_parent;
+
+ // BEGIN: persistent component list properties
+ /// ID of the component
+ QString m_uid;
+ /// version of the component - when there's a custom json override, this is also the version the component reverts to
+ QString m_version;
+ /// if true, this has been added automatically to satisfy dependencies and may be automatically removed
+ bool m_dependencyOnly = false;
+ /// if true, the component is either the main component of the instance, or otherwise important and cannot be removed.
+ bool m_important = false;
+ /// if true, the component is disabled
+ bool m_disabled = false;
+
+ /// cached name for display purposes, taken from the version file (meta or local override)
+ QString m_cachedName;
+ /// cached version for display AND other purposes, taken from the version file (meta or local override)
+ QString m_cachedVersion;
+ /// cached set of requirements, taken from the version file (meta or local override)
+ Meta::RequireSet m_cachedRequires;
+ Meta::RequireSet m_cachedConflicts;
+ /// if true, the component is volatile and may be automatically removed when no longer needed
+ bool m_cachedVolatile = false;
+ // END: persistent component list properties
+
+ // DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
+ bool m_orderOverride = false;
+ int m_order = 0;
+
+ // load state
+ std::shared_ptr<Meta::Version> m_metaVersion;
+ std::shared_ptr<VersionFile> m_file;
+ bool m_loaded = false;
};
typedef shared_qobject_ptr<Component> ComponentPtr;
diff --git a/api/logic/minecraft/ComponentList.cpp b/api/logic/minecraft/ComponentList.cpp
index a207e987..51fe214d 100644
--- a/api/logic/minecraft/ComponentList.cpp
+++ b/api/logic/minecraft/ComponentList.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,18 +37,20 @@
#include "ComponentUpdateTask.h"
ComponentList::ComponentList(MinecraftInstance * instance)
- : QAbstractListModel()
+ : QAbstractListModel()
{
- d.reset(new ComponentListData);
- d->m_instance = instance;
- d->m_saveTimer.setSingleShot(true);
- d->m_saveTimer.setInterval(5000);
- connect(&d->m_saveTimer, &QTimer::timeout, this, &ComponentList::save_internal);
+ d.reset(new ComponentListData);
+ d->m_instance = instance;
+ d->m_saveTimer.setSingleShot(true);
+ d->m_saveTimer.setInterval(5000);
+ d->interactionDisabled = instance->isRunning();
+ connect(d->m_instance, &BaseInstance::runningStatusChanged, this, &ComponentList::disableInteraction);
+ connect(&d->m_saveTimer, &QTimer::timeout, this, &ComponentList::save_internal);
}
ComponentList::~ComponentList()
{
- saveNow();
+ saveNow();
}
// BEGIN: component file format
@@ -57,151 +59,151 @@ static const int currentComponentsFileVersion = 1;
static QJsonObject componentToJsonV1(ComponentPtr component)
{
- QJsonObject obj;
- // critical
- obj.insert("uid", component->m_uid);
- if(!component->m_version.isEmpty())
- {
- obj.insert("version", component->m_version);
- }
- if(component->m_dependencyOnly)
- {
- obj.insert("dependencyOnly", true);
- }
- if(component->m_important)
- {
- obj.insert("important", true);
- }
- if(component->m_disabled)
- {
- obj.insert("disabled", true);
- }
-
- // cached
- if(!component->m_cachedVersion.isEmpty())
- {
- obj.insert("cachedVersion", component->m_cachedVersion);
- }
- if(!component->m_cachedName.isEmpty())
- {
- obj.insert("cachedName", component->m_cachedName);
- }
- Meta::serializeRequires(obj, &component->m_cachedRequires, "cachedRequires");
- Meta::serializeRequires(obj, &component->m_cachedConflicts, "cachedConflicts");
- if(component->m_cachedVolatile)
- {
- obj.insert("cachedVolatile", true);
- }
- return obj;
+ QJsonObject obj;
+ // critical
+ obj.insert("uid", component->m_uid);
+ if(!component->m_version.isEmpty())
+ {
+ obj.insert("version", component->m_version);
+ }
+ if(component->m_dependencyOnly)
+ {
+ obj.insert("dependencyOnly", true);
+ }
+ if(component->m_important)
+ {
+ obj.insert("important", true);
+ }
+ if(component->m_disabled)
+ {
+ obj.insert("disabled", true);
+ }
+
+ // cached
+ if(!component->m_cachedVersion.isEmpty())
+ {
+ obj.insert("cachedVersion", component->m_cachedVersion);
+ }
+ if(!component->m_cachedName.isEmpty())
+ {
+ obj.insert("cachedName", component->m_cachedName);
+ }
+ Meta::serializeRequires(obj, &component->m_cachedRequires, "cachedRequires");
+ Meta::serializeRequires(obj, &component->m_cachedConflicts, "cachedConflicts");
+ if(component->m_cachedVolatile)
+ {
+ obj.insert("cachedVolatile", true);
+ }
+ return obj;
}
static ComponentPtr componentFromJsonV1(ComponentList * parent, const QString & componentJsonPattern, const QJsonObject &obj)
{
- // critical
- auto uid = Json::requireString(obj.value("uid"));
- auto filePath = componentJsonPattern.arg(uid);
- auto component = new Component(parent, uid);
- component->m_version = Json::ensureString(obj.value("version"));
- component->m_dependencyOnly = Json::ensureBoolean(obj.value("dependencyOnly"), false);
- component->m_important = Json::ensureBoolean(obj.value("important"), false);
-
- // cached
- // TODO @RESILIENCE: ignore invalid values/structure here?
- component->m_cachedVersion = Json::ensureString(obj.value("cachedVersion"));
- component->m_cachedName = Json::ensureString(obj.value("cachedName"));
- Meta::parseRequires(obj, &component->m_cachedRequires, "cachedRequires");
- Meta::parseRequires(obj, &component->m_cachedConflicts, "cachedConflicts");
- component->m_cachedVolatile = Json::ensureBoolean(obj.value("volatile"), false);
- bool disabled = Json::ensureBoolean(obj.value("disabled"), false);
- component->setEnabled(!disabled);
- return component;
+ // critical
+ auto uid = Json::requireString(obj.value("uid"));
+ auto filePath = componentJsonPattern.arg(uid);
+ auto component = new Component(parent, uid);
+ component->m_version = Json::ensureString(obj.value("version"));
+ component->m_dependencyOnly = Json::ensureBoolean(obj.value("dependencyOnly"), false);
+ component->m_important = Json::ensureBoolean(obj.value("important"), false);
+
+ // cached
+ // TODO @RESILIENCE: ignore invalid values/structure here?
+ component->m_cachedVersion = Json::ensureString(obj.value("cachedVersion"));
+ component->m_cachedName = Json::ensureString(obj.value("cachedName"));
+ Meta::parseRequires(obj, &component->m_cachedRequires, "cachedRequires");
+ Meta::parseRequires(obj, &component->m_cachedConflicts, "cachedConflicts");
+ component->m_cachedVolatile = Json::ensureBoolean(obj.value("volatile"), false);
+ bool disabled = Json::ensureBoolean(obj.value("disabled"), false);
+ component->setEnabled(!disabled);
+ return component;
}
// Save the given component container data to a file
static bool saveComponentList(const QString & filename, const ComponentContainer & container)
{
- QJsonObject obj;
- obj.insert("formatVersion", currentComponentsFileVersion);
- QJsonArray orderArray;
- for(auto component: container)
- {
- orderArray.append(componentToJsonV1(component));
- }
- obj.insert("components", orderArray);
- QSaveFile outFile(filename);
- if (!outFile.open(QFile::WriteOnly))
- {
- qCritical() << "Couldn't open" << outFile.fileName()
- << "for writing:" << outFile.errorString();
- return false;
- }
- auto data = QJsonDocument(obj).toJson(QJsonDocument::Indented);
- if(outFile.write(data) != data.size())
- {
- qCritical() << "Couldn't write all the data into" << outFile.fileName()
- << "because:" << outFile.errorString();
- return false;
- }
- if(!outFile.commit())
- {
- qCritical() << "Couldn't save" << outFile.fileName()
- << "because:" << outFile.errorString();
- }
- return true;
+ QJsonObject obj;
+ obj.insert("formatVersion", currentComponentsFileVersion);
+ QJsonArray orderArray;
+ for(auto component: container)
+ {
+ orderArray.append(componentToJsonV1(component));
+ }
+ obj.insert("components", orderArray);
+ QSaveFile outFile(filename);
+ if (!outFile.open(QFile::WriteOnly))
+ {
+ qCritical() << "Couldn't open" << outFile.fileName()
+ << "for writing:" << outFile.errorString();
+ return false;
+ }
+ auto data = QJsonDocument(obj).toJson(QJsonDocument::Indented);
+ if(outFile.write(data) != data.size())
+ {
+ qCritical() << "Couldn't write all the data into" << outFile.fileName()
+ << "because:" << outFile.errorString();
+ return false;
+ }
+ if(!outFile.commit())
+ {
+ qCritical() << "Couldn't save" << outFile.fileName()
+ << "because:" << outFile.errorString();
+ }
+ return true;
}
// Read the given file into component containers
static bool loadComponentList(ComponentList * parent, const QString & filename, const QString & componentJsonPattern, ComponentContainer & container)
{
- QFile componentsFile(filename);
- if (!componentsFile.exists())
- {
- qWarning() << "Components file doesn't exist. This should never happen.";
- return false;
- }
- if (!componentsFile.open(QFile::ReadOnly))
- {
- qCritical() << "Couldn't open" << componentsFile.fileName()
- << " for reading:" << componentsFile.errorString();
- qWarning() << "Ignoring overriden order";
- return false;
- }
-
- // and it's valid JSON
- QJsonParseError error;
- QJsonDocument doc = QJsonDocument::fromJson(componentsFile.readAll(), &error);
- if (error.error != QJsonParseError::NoError)
- {
- qCritical() << "Couldn't parse" << componentsFile.fileName() << ":" << error.errorString();
- qWarning() << "Ignoring overriden order";
- return false;
- }
-
- // and then read it and process it if all above is true.
- try
- {
- auto obj = Json::requireObject(doc);
- // check order file version.
- auto version = Json::requireInteger(obj.value("formatVersion"));
- if (version != currentComponentsFileVersion)
- {
- throw JSONValidationError(QObject::tr("Invalid component file version, expected %1")
- .arg(currentComponentsFileVersion));
- }
- auto orderArray = Json::requireArray(obj.value("components"));
- for(auto item: orderArray)
- {
- auto obj = Json::requireObject(item, "Component must be an object.");
- container.append(componentFromJsonV1(parent, componentJsonPattern, obj));
- }
- }
- catch (JSONValidationError &err)
- {
- qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
- container.clear();
- return false;
- }
- return true;
+ QFile componentsFile(filename);
+ if (!componentsFile.exists())
+ {
+ qWarning() << "Components file doesn't exist. This should never happen.";
+ return false;
+ }
+ if (!componentsFile.open(QFile::ReadOnly))
+ {
+ qCritical() << "Couldn't open" << componentsFile.fileName()
+ << " for reading:" << componentsFile.errorString();
+ qWarning() << "Ignoring overriden order";
+ return false;
+ }
+
+ // and it's valid JSON
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(componentsFile.readAll(), &error);
+ if (error.error != QJsonParseError::NoError)
+ {
+ qCritical() << "Couldn't parse" << componentsFile.fileName() << ":" << error.errorString();
+ qWarning() << "Ignoring overriden order";
+ return false;
+ }
+
+ // and then read it and process it if all above is true.
+ try
+ {
+ auto obj = Json::requireObject(doc);
+ // check order file version.
+ auto version = Json::requireInteger(obj.value("formatVersion"));
+ if (version != currentComponentsFileVersion)
+ {
+ throw JSONValidationError(QObject::tr("Invalid component file version, expected %1")
+ .arg(currentComponentsFileVersion));
+ }
+ auto orderArray = Json::requireArray(obj.value("components"));
+ for(auto item: orderArray)
+ {
+ auto obj = Json::requireObject(item, "Component must be an object.");
+ container.append(componentFromJsonV1(parent, componentJsonPattern, obj));
+ }
+ }
+ catch (const JSONValidationError &err)
+ {
+ qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
+ container.clear();
+ return false;
+ }
+ return true;
}
// END: component file format
@@ -210,217 +212,217 @@ static bool loadComponentList(ComponentList * parent, const QString & filename,
void ComponentList::saveNow()
{
- if(saveIsScheduled())
- {
- d->m_saveTimer.stop();
- save_internal();
- }
+ if(saveIsScheduled())
+ {
+ d->m_saveTimer.stop();
+ save_internal();
+ }
}
bool ComponentList::saveIsScheduled() const
{
- return d->dirty;
+ return d->dirty;
}
void ComponentList::buildingFromScratch()
{
- d->loaded = true;
- d->dirty = true;
+ d->loaded = true;
+ d->dirty = true;
}
void ComponentList::scheduleSave()
{
- if(!d->loaded)
- {
- qDebug() << "Component list should never save if it didn't successfully load, instance:" << d->m_instance->name();
- return;
- }
- if(!d->dirty)
- {
- d->dirty = true;
- qDebug() << "Component list save is scheduled for" << d->m_instance->name();
- }
- d->m_saveTimer.start();
+ if(!d->loaded)
+ {
+ qDebug() << "Component list should never save if it didn't successfully load, instance:" << d->m_instance->name();
+ return;
+ }
+ if(!d->dirty)
+ {
+ d->dirty = true;
+ qDebug() << "Component list save is scheduled for" << d->m_instance->name();
+ }
+ d->m_saveTimer.start();
}
QString ComponentList::componentsFilePath() const
{
- return FS::PathCombine(d->m_instance->instanceRoot(), "mmc-pack.json");
+ return FS::PathCombine(d->m_instance->instanceRoot(), "mmc-pack.json");
}
QString ComponentList::patchesPattern() const
{
- return FS::PathCombine(d->m_instance->instanceRoot(), "patches", "%1.json");
+ return FS::PathCombine(d->m_instance->instanceRoot(), "patches", "%1.json");
}
QString ComponentList::patchFilePathForUid(const QString& uid) const
{
- return patchesPattern().arg(uid);
+ return patchesPattern().arg(uid);
}
void ComponentList::save_internal()
{
- qDebug() << "Component list save performed now for" << d->m_instance->name();
- auto filename = componentsFilePath();
- saveComponentList(filename, d->components);
- d->dirty = false;
+ qDebug() << "Component list save performed now for" << d->m_instance->name();
+ auto filename = componentsFilePath();
+ saveComponentList(filename, d->components);
+ d->dirty = false;
}
bool ComponentList::load()
{
- auto filename = componentsFilePath();
- QFile componentsFile(filename);
-
- // migrate old config to new one, if needed
- if(!componentsFile.exists())
- {
- if(!migratePreComponentConfig())
- {
- // FIXME: the user should be notified...
- qCritical() << "Failed to convert old pre-component config for instance" << d->m_instance->name();
- return false;
- }
- }
-
- // load the new component list and swap it with the current one...
- ComponentContainer newComponents;
- if(!loadComponentList(this, filename, patchesPattern(), newComponents))
- {
- qCritical() << "Failed to load the component config for instance" << d->m_instance->name();
- return false;
- }
- else
- {
- // FIXME: actually use fine-grained updates, not this...
- beginResetModel();
- // disconnect all the old components
- for(auto component: d->components)
- {
- disconnect(component.get(), &Component::dataChanged, this, &ComponentList::componentDataChanged);
- }
- d->components.clear();
- d->componentIndex.clear();
- for(auto component: newComponents)
- {
- if(d->componentIndex.contains(component->m_uid))
- {
- qWarning() << "Ignoring duplicate component entry" << component->m_uid;
- continue;
- }
- connect(component.get(), &Component::dataChanged, this, &ComponentList::componentDataChanged);
- d->components.append(component);
- d->componentIndex[component->m_uid] = component;
- }
- endResetModel();
- d->loaded = true;
- return true;
- }
+ auto filename = componentsFilePath();
+ QFile componentsFile(filename);
+
+ // migrate old config to new one, if needed
+ if(!componentsFile.exists())
+ {
+ if(!migratePreComponentConfig())
+ {
+ // FIXME: the user should be notified...
+ qCritical() << "Failed to convert old pre-component config for instance" << d->m_instance->name();
+ return false;
+ }
+ }
+
+ // load the new component list and swap it with the current one...
+ ComponentContainer newComponents;
+ if(!loadComponentList(this, filename, patchesPattern(), newComponents))
+ {
+ qCritical() << "Failed to load the component config for instance" << d->m_instance->name();
+ return false;
+ }
+ else
+ {
+ // FIXME: actually use fine-grained updates, not this...
+ beginResetModel();
+ // disconnect all the old components
+ for(auto component: d->components)
+ {
+ disconnect(component.get(), &Component::dataChanged, this, &ComponentList::componentDataChanged);
+ }
+ d->components.clear();
+ d->componentIndex.clear();
+ for(auto component: newComponents)
+ {
+ if(d->componentIndex.contains(component->m_uid))
+ {
+ qWarning() << "Ignoring duplicate component entry" << component->m_uid;
+ continue;
+ }
+ connect(component.get(), &Component::dataChanged, this, &ComponentList::componentDataChanged);
+ d->components.append(component);
+ d->componentIndex[component->m_uid] = component;
+ }
+ endResetModel();
+ d->loaded = true;
+ return true;
+ }
}
void ComponentList::reload(Net::Mode netmode)
{
- // Do not reload when the update/resolve task is running. It is in control.
- if(d->m_updateTask)
- {
- return;
- }
-
- // flush any scheduled saves to not lose state
- saveNow();
-
- // FIXME: differentiate when a reapply is required by propagating state from components
- invalidateLaunchProfile();
-
- if(load())
- {
- resolve(netmode);
- }
+ // Do not reload when the update/resolve task is running. It is in control.
+ if(d->m_updateTask)
+ {
+ return;
+ }
+
+ // flush any scheduled saves to not lose state
+ saveNow();
+
+ // FIXME: differentiate when a reapply is required by propagating state from components
+ invalidateLaunchProfile();
+
+ if(load())
+ {
+ resolve(netmode);
+ }
}
shared_qobject_ptr<Task> ComponentList::getCurrentTask()
{
- return d->m_updateTask;
+ return d->m_updateTask;
}
void ComponentList::resolve(Net::Mode netmode)
{
- auto updateTask = new ComponentUpdateTask(ComponentUpdateTask::Mode::Resolution, netmode, this);
- d->m_updateTask.reset(updateTask);
- connect(updateTask, &ComponentUpdateTask::succeeded, this, &ComponentList::updateSucceeded);
- connect(updateTask, &ComponentUpdateTask::failed, this, &ComponentList::updateFailed);
- d->m_updateTask->start();
+ auto updateTask = new ComponentUpdateTask(ComponentUpdateTask::Mode::Resolution, netmode, this);
+ d->m_updateTask.reset(updateTask);
+ connect(updateTask, &ComponentUpdateTask::succeeded, this, &ComponentList::updateSucceeded);
+ connect(updateTask, &ComponentUpdateTask::failed, this, &ComponentList::updateFailed);
+ d->m_updateTask->start();
}
void ComponentList::updateSucceeded()
{
- qDebug() << "Component list update/resolve task succeeded for" << d->m_instance->name();
- d->m_updateTask.reset();
- invalidateLaunchProfile();
+ qDebug() << "Component list update/resolve task succeeded for" << d->m_instance->name();
+ d->m_updateTask.reset();
+ invalidateLaunchProfile();
}
void ComponentList::updateFailed(const QString& error)
{
- qDebug() << "Component list update/resolve task failed for" << d->m_instance->name() << "Reason:" << error;
- d->m_updateTask.reset();
- invalidateLaunchProfile();
+ qDebug() << "Component list update/resolve task failed for" << d->m_instance->name() << "Reason:" << error;
+ d->m_updateTask.reset();
+ invalidateLaunchProfile();
}
// NOTE this is really old stuff, and only needs to be used when loading the old hardcoded component-unaware format (loadPreComponentConfig).
static void upgradeDeprecatedFiles(QString root, QString instanceName)
{
- auto versionJsonPath = FS::PathCombine(root, "version.json");
- auto customJsonPath = FS::PathCombine(root, "custom.json");
- auto mcJson = FS::PathCombine(root, "patches" , "net.minecraft.json");
-
- QString sourceFile;
- QString renameFile;
-
- // convert old crap.
- if(QFile::exists(customJsonPath))
- {
- sourceFile = customJsonPath;
- renameFile = versionJsonPath;
- }
- else if(QFile::exists(versionJsonPath))
- {
- sourceFile = versionJsonPath;
- }
- if(!sourceFile.isEmpty() && !QFile::exists(mcJson))
- {
- if(!FS::ensureFilePathExists(mcJson))
- {
- qWarning() << "Couldn't create patches folder for" << instanceName;
- return;
- }
- if(!renameFile.isEmpty() && QFile::exists(renameFile))
- {
- if(!QFile::rename(renameFile, renameFile + ".old"))
- {
- qWarning() << "Couldn't rename" << renameFile << "to" << renameFile + ".old" << "in" << instanceName;
- return;
- }
- }
- auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false);
- ProfileUtils::removeLwjglFromPatch(file);
- file->uid = "net.minecraft";
- file->version = file->minecraftVersion;
- file->name = "Minecraft";
-
- Meta::Require needsLwjgl;
- needsLwjgl.uid = "org.lwjgl";
- file->requires.insert(needsLwjgl);
-
- if(!ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), mcJson))
- {
- return;
- }
- if(!QFile::rename(sourceFile, sourceFile + ".old"))
- {
- qWarning() << "Couldn't rename" << sourceFile << "to" << sourceFile + ".old" << "in" << instanceName;
- return;
- }
- }
+ auto versionJsonPath = FS::PathCombine(root, "version.json");
+ auto customJsonPath = FS::PathCombine(root, "custom.json");
+ auto mcJson = FS::PathCombine(root, "patches" , "net.minecraft.json");
+
+ QString sourceFile;
+ QString renameFile;
+
+ // convert old crap.
+ if(QFile::exists(customJsonPath))
+ {
+ sourceFile = customJsonPath;
+ renameFile = versionJsonPath;
+ }
+ else if(QFile::exists(versionJsonPath))
+ {
+ sourceFile = versionJsonPath;
+ }
+ if(!sourceFile.isEmpty() && !QFile::exists(mcJson))
+ {
+ if(!FS::ensureFilePathExists(mcJson))
+ {
+ qWarning() << "Couldn't create patches folder for" << instanceName;
+ return;
+ }
+ if(!renameFile.isEmpty() && QFile::exists(renameFile))
+ {
+ if(!QFile::rename(renameFile, renameFile + ".old"))
+ {
+ qWarning() << "Couldn't rename" << renameFile << "to" << renameFile + ".old" << "in" << instanceName;
+ return;
+ }
+ }
+ auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false);
+ ProfileUtils::removeLwjglFromPatch(file);
+ file->uid = "net.minecraft";
+ file->version = file->minecraftVersion;
+ file->name = "Minecraft";
+
+ Meta::Require needsLwjgl;
+ needsLwjgl.uid = "org.lwjgl";
+ file->requires.insert(needsLwjgl);
+
+ if(!ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), mcJson))
+ {
+ return;
+ }
+ if(!QFile::rename(sourceFile, sourceFile + ".old"))
+ {
+ qWarning() << "Couldn't rename" << sourceFile << "to" << sourceFile + ".old" << "in" << instanceName;
+ return;
+ }
+ }
}
/*
@@ -431,774 +433,793 @@ static void upgradeDeprecatedFiles(QString root, QString instanceName)
*/
bool ComponentList::migratePreComponentConfig()
{
- // upgrade the very old files from the beginnings of MultiMC 5
- upgradeDeprecatedFiles(d->m_instance->instanceRoot(), d->m_instance->name());
-
- QList<ComponentPtr> components;
- QSet<QString> loaded;
-
- auto addBuiltinPatch = [&](const QString &uid, bool asDependency, const QString & emptyVersion, const Meta::Require & req, const Meta::Require & conflict)
- {
- auto jsonFilePath = FS::PathCombine(d->m_instance->instanceRoot(), "patches" , uid + ".json");
- auto intendedVersion = d->getOldConfigVersion(uid);
- // load up the base minecraft patch
- ComponentPtr component;
- if(QFile::exists(jsonFilePath))
- {
- if(intendedVersion.isEmpty())
- {
- intendedVersion = emptyVersion;
- }
- auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false);
- // fix uid
- file->uid = uid;
- // if version is missing, add it from the outside.
- if(file->version.isEmpty())
- {
- file->version = intendedVersion;
- }
- // if this is a dependency (LWJGL), mark it also as volatile
- if(asDependency)
- {
- file->m_volatile = true;
- }
- // insert requirements if needed
- if(!req.uid.isEmpty())
- {
- file->requires.insert(req);
- }
- // insert conflicts if needed
- if(!conflict.uid.isEmpty())
- {
- file->conflicts.insert(conflict);
- }
- // FIXME: @QUALITY do not ignore return value
- ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), jsonFilePath);
- component = new Component(this, uid, file);
- component->m_version = intendedVersion;
- }
- else if(!intendedVersion.isEmpty())
- {
- auto metaVersion = ENV.metadataIndex()->get(uid, intendedVersion);
- component = new Component(this, metaVersion);
- }
- else
- {
- return;
- }
- component->m_dependencyOnly = asDependency;
- component->m_important = !asDependency;
- components.append(component);
- };
- // TODO: insert depends and conflicts here if these are customized files...
- Meta::Require reqLwjgl;
- reqLwjgl.uid = "org.lwjgl";
- reqLwjgl.suggests = "2.9.1";
- Meta::Require conflictLwjgl3;
- conflictLwjgl3.uid = "org.lwjgl3";
- Meta::Require nullReq;
- addBuiltinPatch("org.lwjgl", true, "2.9.1", nullReq, conflictLwjgl3);
- addBuiltinPatch("net.minecraft", false, QString(), reqLwjgl, nullReq);
-
- // first, collect all other file-based patches and load them
- QMap<QString, ComponentPtr> loadedComponents;
- QDir patchesDir(FS::PathCombine(d->m_instance->instanceRoot(),"patches"));
- for (auto info : patchesDir.entryInfoList(QStringList() << "*.json", QDir::Files))
- {
- // parse the file
- qDebug() << "Reading" << info.fileName();
- auto file = ProfileUtils::parseJsonFile(info, true);
-
- // correct missing or wrong uid based on the file name
- QString uid = info.completeBaseName();
-
- // ignore builtins, they've been handled already
- if (uid == "net.minecraft")
- continue;
- if (uid == "org.lwjgl")
- continue;
-
- // handle horrible corner cases
- if(uid.isEmpty())
- {
- // if you have a file named '.json', make it just go away.
- // FIXME: @QUALITY do not ignore return value
- QFile::remove(info.absoluteFilePath());
- continue;
- }
- file->uid = uid;
- // FIXME: @QUALITY do not ignore return value
- ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), info.absoluteFilePath());
-
- auto component = new Component(this, file->uid, file);
- auto version = d->getOldConfigVersion(file->uid);
- if(!version.isEmpty())
- {
- component->m_version = version;
- }
- loadedComponents[file->uid] = component;
- }
- // try to load the other 'hardcoded' patches (forge, liteloader), if they weren't loaded from files
- auto loadSpecial = [&](const QString & uid, int order)
- {
- auto patchVersion = d->getOldConfigVersion(uid);
- if(!patchVersion.isEmpty() && !loadedComponents.contains(uid))
- {
- auto patch = new Component(this, ENV.metadataIndex()->get(uid, patchVersion));
- patch->setOrder(order);
- loadedComponents[uid] = patch;
- }
- };
- loadSpecial("net.minecraftforge", 5);
- loadSpecial("com.mumfrey.liteloader", 10);
-
- // load the old order.json file, if present
- ProfileUtils::PatchOrder userOrder;
- ProfileUtils::readOverrideOrders(FS::PathCombine(d->m_instance->instanceRoot(), "order.json"), userOrder);
-
- // now add all the patches by user sort order
- for (auto uid : userOrder)
- {
- // ignore builtins
- if (uid == "net.minecraft")
- continue;
- if (uid == "org.lwjgl")
- continue;
- // ordering has a patch that is gone?
- if(!loadedComponents.contains(uid))
- {
- continue;
- }
- components.append(loadedComponents.take(uid));
- }
-
- // is there anything left to sort? - this is used when there are leftover components that aren't part of the order.json
- if(!loadedComponents.isEmpty())
- {
- // inserting into multimap by order number as key sorts the patches and detects duplicates
- QMultiMap<int, ComponentPtr> files;
- auto iter = loadedComponents.begin();
- while(iter != loadedComponents.end())
- {
- files.insert((*iter)->getOrder(), *iter);
- iter++;
- }
-
- // then just extract the patches and put them in the list
- for (auto order : files.keys())
- {
- const auto &values = files.values(order);
- for(auto &value: values)
- {
- // TODO: put back the insertion of problem messages here, so the user knows about the id duplication
- components.append(value);
- }
- }
- }
- // new we have a complete list of components...
- return saveComponentList(componentsFilePath(), components);
+ // upgrade the very old files from the beginnings of MultiMC 5
+ upgradeDeprecatedFiles(d->m_instance->instanceRoot(), d->m_instance->name());
+
+ QList<ComponentPtr> components;
+ QSet<QString> loaded;
+
+ auto addBuiltinPatch = [&](const QString &uid, bool asDependency, const QString & emptyVersion, const Meta::Require & req, const Meta::Require & conflict)
+ {
+ auto jsonFilePath = FS::PathCombine(d->m_instance->instanceRoot(), "patches" , uid + ".json");
+ auto intendedVersion = d->getOldConfigVersion(uid);
+ // load up the base minecraft patch
+ ComponentPtr component;
+ if(QFile::exists(jsonFilePath))
+ {
+ if(intendedVersion.isEmpty())
+ {
+ intendedVersion = emptyVersion;
+ }
+ auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false);
+ // fix uid
+ file->uid = uid;
+ // if version is missing, add it from the outside.
+ if(file->version.isEmpty())
+ {
+ file->version = intendedVersion;
+ }
+ // if this is a dependency (LWJGL), mark it also as volatile
+ if(asDependency)
+ {
+ file->m_volatile = true;
+ }
+ // insert requirements if needed
+ if(!req.uid.isEmpty())
+ {
+ file->requires.insert(req);
+ }
+ // insert conflicts if needed
+ if(!conflict.uid.isEmpty())
+ {
+ file->conflicts.insert(conflict);
+ }
+ // FIXME: @QUALITY do not ignore return value
+ ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), jsonFilePath);
+ component = new Component(this, uid, file);
+ component->m_version = intendedVersion;
+ }
+ else if(!intendedVersion.isEmpty())
+ {
+ auto metaVersion = ENV.metadataIndex()->get(uid, intendedVersion);
+ component = new Component(this, metaVersion);
+ }
+ else
+ {
+ return;
+ }
+ component->m_dependencyOnly = asDependency;
+ component->m_important = !asDependency;
+ components.append(component);
+ };
+ // TODO: insert depends and conflicts here if these are customized files...
+ Meta::Require reqLwjgl;
+ reqLwjgl.uid = "org.lwjgl";
+ reqLwjgl.suggests = "2.9.1";
+ Meta::Require conflictLwjgl3;
+ conflictLwjgl3.uid = "org.lwjgl3";
+ Meta::Require nullReq;
+ addBuiltinPatch("org.lwjgl", true, "2.9.1", nullReq, conflictLwjgl3);
+ addBuiltinPatch("net.minecraft", false, QString(), reqLwjgl, nullReq);
+
+ // first, collect all other file-based patches and load them
+ QMap<QString, ComponentPtr> loadedComponents;
+ QDir patchesDir(FS::PathCombine(d->m_instance->instanceRoot(),"patches"));
+ for (auto info : patchesDir.entryInfoList(QStringList() << "*.json", QDir::Files))
+ {
+ // parse the file
+ qDebug() << "Reading" << info.fileName();
+ auto file = ProfileUtils::parseJsonFile(info, true);
+
+ // correct missing or wrong uid based on the file name
+ QString uid = info.completeBaseName();
+
+ // ignore builtins, they've been handled already
+ if (uid == "net.minecraft")
+ continue;
+ if (uid == "org.lwjgl")
+ continue;
+
+ // handle horrible corner cases
+ if(uid.isEmpty())
+ {
+ // if you have a file named '.json', make it just go away.
+ // FIXME: @QUALITY do not ignore return value
+ QFile::remove(info.absoluteFilePath());
+ continue;
+ }
+ file->uid = uid;
+ // FIXME: @QUALITY do not ignore return value
+ ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), info.absoluteFilePath());
+
+ auto component = new Component(this, file->uid, file);
+ auto version = d->getOldConfigVersion(file->uid);
+ if(!version.isEmpty())
+ {
+ component->m_version = version;
+ }
+ loadedComponents[file->uid] = component;
+ }
+ // try to load the other 'hardcoded' patches (forge, liteloader), if they weren't loaded from files
+ auto loadSpecial = [&](const QString & uid, int order)
+ {
+ auto patchVersion = d->getOldConfigVersion(uid);
+ if(!patchVersion.isEmpty() && !loadedComponents.contains(uid))
+ {
+ auto patch = new Component(this, ENV.metadataIndex()->get(uid, patchVersion));
+ patch->setOrder(order);
+ loadedComponents[uid] = patch;
+ }
+ };
+ loadSpecial("net.minecraftforge", 5);
+ loadSpecial("com.mumfrey.liteloader", 10);
+
+ // load the old order.json file, if present
+ ProfileUtils::PatchOrder userOrder;
+ ProfileUtils::readOverrideOrders(FS::PathCombine(d->m_instance->instanceRoot(), "order.json"), userOrder);
+
+ // now add all the patches by user sort order
+ for (auto uid : userOrder)
+ {
+ // ignore builtins
+ if (uid == "net.minecraft")
+ continue;
+ if (uid == "org.lwjgl")
+ continue;
+ // ordering has a patch that is gone?
+ if(!loadedComponents.contains(uid))
+ {
+ continue;
+ }
+ components.append(loadedComponents.take(uid));
+ }
+
+ // is there anything left to sort? - this is used when there are leftover components that aren't part of the order.json
+ if(!loadedComponents.isEmpty())
+ {
+ // inserting into multimap by order number as key sorts the patches and detects duplicates
+ QMultiMap<int, ComponentPtr> files;
+ auto iter = loadedComponents.begin();
+ while(iter != loadedComponents.end())
+ {
+ files.insert((*iter)->getOrder(), *iter);
+ iter++;
+ }
+
+ // then just extract the patches and put them in the list
+ for (auto order : files.keys())
+ {
+ const auto &values = files.values(order);
+ for(auto &value: values)
+ {
+ // TODO: put back the insertion of problem messages here, so the user knows about the id duplication
+ components.append(value);
+ }
+ }
+ }
+ // new we have a complete list of components...
+ return saveComponentList(componentsFilePath(), components);
}
// END: save/load
void ComponentList::appendComponent(ComponentPtr component)
{
- insertComponent(d->components.size(), component);
+ insertComponent(d->components.size(), component);
}
void ComponentList::insertComponent(size_t index, ComponentPtr component)
{
- auto id = component->getID();
- if(id.isEmpty())
- {
- qWarning() << "Attempt to add a component with empty ID!";
- return;
- }
- if(d->componentIndex.contains(id))
- {
- qWarning() << "Attempt to add a component that is already present!";
- return;
- }
- beginInsertRows(QModelIndex(), index, index);
- d->components.insert(index, component);
- d->componentIndex[id] = component;
- endInsertRows();
- connect(component.get(), &Component::dataChanged, this, &ComponentList::componentDataChanged);
- scheduleSave();
+ auto id = component->getID();
+ if(id.isEmpty())
+ {
+ qWarning() << "Attempt to add a component with empty ID!";
+ return;
+ }
+ if(d->componentIndex.contains(id))
+ {
+ qWarning() << "Attempt to add a component that is already present!";
+ return;
+ }
+ beginInsertRows(QModelIndex(), index, index);
+ d->components.insert(index, component);
+ d->componentIndex[id] = component;
+ endInsertRows();
+ connect(component.get(), &Component::dataChanged, this, &ComponentList::componentDataChanged);
+ scheduleSave();
}
void ComponentList::componentDataChanged()
{
- auto objPtr = qobject_cast<Component *>(sender());
- if(!objPtr)
- {
- qWarning() << "ComponentList got dataChenged signal from a non-Component!";
- return;
- }
- // figure out which one is it... in a seriously dumb way.
- int index = 0;
- for (auto component: d->components)
- {
- if(component.get() == objPtr)
- {
- emit dataChanged(createIndex(index, 0), createIndex(index, columnCount(QModelIndex()) - 1));
- scheduleSave();
- return;
- }
- index++;
- }
- qWarning() << "ComponentList got dataChenged signal from a Component which does not belong to it!";
+ auto objPtr = qobject_cast<Component *>(sender());
+ if(!objPtr)
+ {
+ qWarning() << "ComponentList got dataChenged signal from a non-Component!";
+ return;
+ }
+ if(objPtr->getID() == "net.minecraft") {
+ emit minecraftChanged();
+ }
+ // figure out which one is it... in a seriously dumb way.
+ int index = 0;
+ for (auto component: d->components)
+ {
+ if(component.get() == objPtr)
+ {
+ emit dataChanged(createIndex(index, 0), createIndex(index, columnCount(QModelIndex()) - 1));
+ scheduleSave();
+ return;
+ }
+ index++;
+ }
+ qWarning() << "ComponentList got dataChenged signal from a Component which does not belong to it!";
}
bool ComponentList::remove(const int index)
{
- auto patch = getComponent(index);
- if (!patch->isRemovable())
- {
- qWarning() << "Patch" << patch->getID() << "is non-removable";
- return false;
- }
-
- if(!removeComponent_internal(patch))
- {
- qCritical() << "Patch" << patch->getID() << "could not be removed";
- return false;
- }
-
- beginRemoveRows(QModelIndex(), index, index);
- d->components.removeAt(index);
- d->componentIndex.remove(patch->getID());
- endRemoveRows();
- invalidateLaunchProfile();
- scheduleSave();
- return true;
+ auto patch = getComponent(index);
+ if (!patch->isRemovable())
+ {
+ qWarning() << "Patch" << patch->getID() << "is non-removable";
+ return false;
+ }
+
+ if(!removeComponent_internal(patch))
+ {
+ qCritical() << "Patch" << patch->getID() << "could not be removed";
+ return false;
+ }
+
+ beginRemoveRows(QModelIndex(), index, index);
+ d->components.removeAt(index);
+ d->componentIndex.remove(patch->getID());
+ endRemoveRows();
+ invalidateLaunchProfile();
+ scheduleSave();
+ return true;
}
bool ComponentList::remove(const QString id)
{
- int i = 0;
- for (auto patch : d->components)
- {
- if (patch->getID() == id)
- {
- return remove(i);
- }
- i++;
- }
- return false;
+ int i = 0;
+ for (auto patch : d->components)
+ {
+ if (patch->getID() == id)
+ {
+ return remove(i);
+ }
+ i++;
+ }
+ return false;
}
bool ComponentList::customize(int index)
{
- auto patch = getComponent(index);
- if (!patch->isCustomizable())
- {
- qDebug() << "Patch" << patch->getID() << "is not customizable";
- return false;
- }
- if(!patch->customize())
- {
- qCritical() << "Patch" << patch->getID() << "could not be customized";
- return false;
- }
- invalidateLaunchProfile();
- scheduleSave();
- return true;
+ auto patch = getComponent(index);
+ if (!patch->isCustomizable())
+ {
+ qDebug() << "Patch" << patch->getID() << "is not customizable";
+ return false;
+ }
+ if(!patch->customize())
+ {
+ qCritical() << "Patch" << patch->getID() << "could not be customized";
+ return false;
+ }
+ invalidateLaunchProfile();
+ scheduleSave();
+ return true;
}
bool ComponentList::revertToBase(int index)
{
- auto patch = getComponent(index);
- if (!patch->isRevertible())
- {
- qDebug() << "Patch" << patch->getID() << "is not revertible";
- return false;
- }
- if(!patch->revert())
- {
- qCritical() << "Patch" << patch->getID() << "could not be reverted";
- return false;
- }
- invalidateLaunchProfile();
- scheduleSave();
- return true;
+ auto patch = getComponent(index);
+ if (!patch->isRevertible())
+ {
+ qDebug() << "Patch" << patch->getID() << "is not revertible";
+ return false;
+ }
+ if(!patch->revert())
+ {
+ qCritical() << "Patch" << patch->getID() << "could not be reverted";
+ return false;
+ }
+ invalidateLaunchProfile();
+ scheduleSave();
+ return true;
}
Component * ComponentList::getComponent(const QString &id)
{
- auto iter = d->componentIndex.find(id);
- if (iter == d->componentIndex.end())
- {
- return nullptr;
- }
- return (*iter).get();
+ auto iter = d->componentIndex.find(id);
+ if (iter == d->componentIndex.end())
+ {
+ return nullptr;
+ }
+ return (*iter).get();
}
Component * ComponentList::getComponent(int index)
{
- if(index < 0 || index >= d->components.size())
- {
- return nullptr;
- }
- return d->components[index].get();
+ if(index < 0 || index >= d->components.size())
+ {
+ return nullptr;
+ }
+ return d->components[index].get();
}
QVariant ComponentList::data(const QModelIndex &index, int role) const
{
- if (!index.isValid())
- return QVariant();
-
- int row = index.row();
- int column = index.column();
-
- if (row < 0 || row >= d->components.size())
- return QVariant();
-
- auto patch = d->components.at(row);
-
- switch (role)
- {
- case Qt::CheckStateRole:
- {
- switch (column)
- {
- case NameColumn:
- return d->components.at(row)->isEnabled() ? Qt::Checked : Qt::Unchecked;
- default:
- return QVariant();
- }
- }
- case Qt::DisplayRole:
- {
- switch (column)
- {
- case NameColumn:
- return d->components.at(row)->getName();
- case VersionColumn:
- {
- if(patch->isCustom())
- {
- return QString("%1 (Custom)").arg(patch->getVersion());
- }
- else
- {
- return patch->getVersion();
- }
- }
- default:
- return QVariant();
- }
- }
- case Qt::DecorationRole:
- {
- switch(column)
- {
- case NameColumn:
- {
- auto severity = patch->getProblemSeverity();
- switch (severity)
- {
- case ProblemSeverity::Warning:
- return "warning";
- case ProblemSeverity::Error:
- return "error";
- default:
- return QVariant();
- }
- }
- default:
- {
- return QVariant();
- }
- }
- }
- }
- return QVariant();
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+ int column = index.column();
+
+ if (row < 0 || row >= d->components.size())
+ return QVariant();
+
+ auto patch = d->components.at(row);
+
+ switch (role)
+ {
+ case Qt::CheckStateRole:
+ {
+ switch (column)
+ {
+ case NameColumn: {
+ return patch->isEnabled() ? Qt::Checked : Qt::Unchecked;
+ }
+ default:
+ return QVariant();
+ }
+ }
+ case Qt::DisplayRole:
+ {
+ switch (column)
+ {
+ case NameColumn:
+ return patch->getName();
+ case VersionColumn:
+ {
+ if(patch->isCustom())
+ {
+ return QString("%1 (Custom)").arg(patch->getVersion());
+ }
+ else
+ {
+ return patch->getVersion();
+ }
+ }
+ default:
+ return QVariant();
+ }
+ }
+ case Qt::DecorationRole:
+ {
+ switch(column)
+ {
+ case NameColumn:
+ {
+ auto severity = patch->getProblemSeverity();
+ switch (severity)
+ {
+ case ProblemSeverity::Warning:
+ return "warning";
+ case ProblemSeverity::Error:
+ return "error";
+ default:
+ return QVariant();
+ }
+ }
+ default:
+ {
+ return QVariant();
+ }
+ }
+ }
+ }
+ return QVariant();
}
bool ComponentList::setData(const QModelIndex& index, const QVariant& value, int role)
{
- if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index))
- {
- return false;
- }
-
- if (role == Qt::CheckStateRole)
- {
- auto component = d->components[index.row()];
- if (component->setEnabled(!component->isEnabled()))
- {
- return true;
- }
- }
- return false;
+ if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index))
+ {
+ return false;
+ }
+
+ if (role == Qt::CheckStateRole)
+ {
+ auto component = d->components[index.row()];
+ if (component->setEnabled(!component->isEnabled()))
+ {
+ return true;
+ }
+ }
+ return false;
}
QVariant ComponentList::headerData(int section, Qt::Orientation orientation, int role) const
{
- if (orientation == Qt::Horizontal)
- {
- if (role == Qt::DisplayRole)
- {
- switch (section)
- {
- case NameColumn:
- return tr("Name");
- case VersionColumn:
- return tr("Version");
- default:
- return QVariant();
- }
- }
- }
- return QVariant();
+ if (orientation == Qt::Horizontal)
+ {
+ if (role == Qt::DisplayRole)
+ {
+ switch (section)
+ {
+ case NameColumn:
+ return tr("Name");
+ case VersionColumn:
+ return tr("Version");
+ default:
+ return QVariant();
+ }
+ }
+ }
+ return QVariant();
}
+
+// FIXME: zero precision mess
Qt::ItemFlags ComponentList::flags(const QModelIndex &index) const
{
- if (!index.isValid())
- return Qt::NoItemFlags;
+ if (!index.isValid()) {
+ return Qt::NoItemFlags;
+ }
- Qt::ItemFlags outFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+ Qt::ItemFlags outFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
- int row = index.row();
+ int row = index.row();
- if (row < 0 || row >= d->components.size())
- return Qt::NoItemFlags;
+ if (row < 0 || row >= d->components.size()) {
+ return Qt::NoItemFlags;
+ }
- auto patch = d->components.at(row);
- // TODO: this will need fine-tuning later...
- if(patch->canBeDisabled())
- {
- outFlags |= Qt::ItemIsUserCheckable;
- }
- return outFlags;
+ auto patch = d->components.at(row);
+ // TODO: this will need fine-tuning later...
+ if(patch->canBeDisabled() && !d->interactionDisabled)
+ {
+ outFlags |= Qt::ItemIsUserCheckable;
+ }
+ return outFlags;
}
int ComponentList::rowCount(const QModelIndex &parent) const
{
- return d->components.size();
+ return d->components.size();
}
int ComponentList::columnCount(const QModelIndex &parent) const
{
- return NUM_COLUMNS;
+ return NUM_COLUMNS;
}
void ComponentList::move(const int index, const MoveDirection direction)
{
- int theirIndex;
- if (direction == MoveUp)
- {
- theirIndex = index - 1;
- }
- else
- {
- theirIndex = index + 1;
- }
-
- if (index < 0 || index >= d->components.size())
- return;
- if (theirIndex >= rowCount())
- theirIndex = rowCount() - 1;
- if (theirIndex == -1)
- theirIndex = rowCount() - 1;
- if (index == theirIndex)
- return;
- int togap = theirIndex > index ? theirIndex + 1 : theirIndex;
-
- auto from = getComponent(index);
- auto to = getComponent(theirIndex);
-
- if (!from || !to || !to->isMoveable() || !from->isMoveable())
- {
- return;
- }
- beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap);
- d->components.swap(index, theirIndex);
- endMoveRows();
- invalidateLaunchProfile();
- scheduleSave();
+ int theirIndex;
+ if (direction == MoveUp)
+ {
+ theirIndex = index - 1;
+ }
+ else
+ {
+ theirIndex = index + 1;
+ }
+
+ if (index < 0 || index >= d->components.size())
+ return;
+ if (theirIndex >= rowCount())
+ theirIndex = rowCount() - 1;
+ if (theirIndex == -1)
+ theirIndex = rowCount() - 1;
+ if (index == theirIndex)
+ return;
+ int togap = theirIndex > index ? theirIndex + 1 : theirIndex;
+
+ auto from = getComponent(index);
+ auto to = getComponent(theirIndex);
+
+ if (!from || !to || !to->isMoveable() || !from->isMoveable())
+ {
+ return;
+ }
+ beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap);
+ d->components.swap(index, theirIndex);
+ endMoveRows();
+ invalidateLaunchProfile();
+ scheduleSave();
}
void ComponentList::invalidateLaunchProfile()
{
- d->m_profile.reset();
+ d->m_profile.reset();
}
void ComponentList::installJarMods(QStringList selectedFiles)
{
- installJarMods_internal(selectedFiles);
+ installJarMods_internal(selectedFiles);
}
void ComponentList::installCustomJar(QString selectedFile)
{
- installCustomJar_internal(selectedFile);
+ installCustomJar_internal(selectedFile);
}
bool ComponentList::installEmpty(const QString& uid, const QString& name)
{
- QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
- if(!FS::ensureFolderPathExists(patchDir))
- {
- return false;
- }
- auto f = std::make_shared<VersionFile>();
- f->name = name;
- f->uid = uid;
- f->version = "1";
- QString patchFileName = FS::PathCombine(patchDir, uid + ".json");
- QFile file(patchFileName);
- if (!file.open(QFile::WriteOnly))
- {
- qCritical() << "Error opening" << file.fileName()
- << "for reading:" << file.errorString();
- return false;
- }
- file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
- file.close();
-
- appendComponent(new Component(this, f->uid, f));
- scheduleSave();
- invalidateLaunchProfile();
- return true;
+ QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
+ if(!FS::ensureFolderPathExists(patchDir))
+ {
+ return false;
+ }
+ auto f = std::make_shared<VersionFile>();
+ f->name = name;
+ f->uid = uid;
+ f->version = "1";
+ QString patchFileName = FS::PathCombine(patchDir, uid + ".json");
+ QFile file(patchFileName);
+ if (!file.open(QFile::WriteOnly))
+ {
+ qCritical() << "Error opening" << file.fileName()
+ << "for reading:" << file.errorString();
+ return false;
+ }
+ file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
+ file.close();
+
+ appendComponent(new Component(this, f->uid, f));
+ scheduleSave();
+ invalidateLaunchProfile();
+ return true;
}
bool ComponentList::removeComponent_internal(ComponentPtr patch)
{
- bool ok = true;
- // first, remove the patch file. this ensures it's not used anymore
- auto fileName = patch->getFilename();
- if(fileName.size())
- {
- QFile patchFile(fileName);
- if(patchFile.exists() && !patchFile.remove())
- {
- qCritical() << "File" << fileName << "could not be removed because:" << patchFile.errorString();
- return false;
- }
- }
-
- // FIXME: we need a generic way of removing local resources, not just jar mods...
- auto preRemoveJarMod = [&](LibraryPtr jarMod) -> bool
- {
- if (!jarMod->isLocal())
- {
- return true;
- }
- QStringList jar, temp1, temp2, temp3;
- jarMod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, d->m_instance->jarmodsPath().absolutePath());
- QFileInfo finfo (jar[0]);
- if(finfo.exists())
- {
- QFile jarModFile(jar[0]);
- if(!jarModFile.remove())
- {
- qCritical() << "File" << jar[0] << "could not be removed because:" << jarModFile.errorString();
- return false;
- }
- return true;
- }
- return true;
- };
-
- auto vFile = patch->getVersionFile();
- if(vFile)
- {
- auto &jarMods = vFile->jarMods;
- for(auto &jarmod: jarMods)
- {
- ok &= preRemoveJarMod(jarmod);
- }
- }
- return ok;
+ bool ok = true;
+ // first, remove the patch file. this ensures it's not used anymore
+ auto fileName = patch->getFilename();
+ if(fileName.size())
+ {
+ QFile patchFile(fileName);
+ if(patchFile.exists() && !patchFile.remove())
+ {
+ qCritical() << "File" << fileName << "could not be removed because:" << patchFile.errorString();
+ return false;
+ }
+ }
+
+ // FIXME: we need a generic way of removing local resources, not just jar mods...
+ auto preRemoveJarMod = [&](LibraryPtr jarMod) -> bool
+ {
+ if (!jarMod->isLocal())
+ {
+ return true;
+ }
+ QStringList jar, temp1, temp2, temp3;
+ jarMod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, d->m_instance->jarmodsPath().absolutePath());
+ QFileInfo finfo (jar[0]);
+ if(finfo.exists())
+ {
+ QFile jarModFile(jar[0]);
+ if(!jarModFile.remove())
+ {
+ qCritical() << "File" << jar[0] << "could not be removed because:" << jarModFile.errorString();
+ return false;
+ }
+ return true;
+ }
+ return true;
+ };
+
+ auto vFile = patch->getVersionFile();
+ if(vFile)
+ {
+ auto &jarMods = vFile->jarMods;
+ for(auto &jarmod: jarMods)
+ {
+ ok &= preRemoveJarMod(jarmod);
+ }
+ }
+ return ok;
}
bool ComponentList::installJarMods_internal(QStringList filepaths)
{
- QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
- if(!FS::ensureFolderPathExists(patchDir))
- {
- return false;
- }
-
- if (!FS::ensureFolderPathExists(d->m_instance->jarModsDir()))
- {
- return false;
- }
-
- for(auto filepath:filepaths)
- {
- QFileInfo sourceInfo(filepath);
- auto uuid = QUuid::createUuid();
- QString id = uuid.toString().remove('{').remove('}');
- QString target_filename = id + ".jar";
- QString target_id = "org.multimc.jarmod." + id;
- QString target_name = sourceInfo.completeBaseName() + " (jar mod)";
- QString finalPath = FS::PathCombine(d->m_instance->jarModsDir(), target_filename);
-
- QFileInfo targetInfo(finalPath);
- if(targetInfo.exists())
- {
- return false;
- }
-
- if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath()))
- {
- return false;
- }
-
- auto f = std::make_shared<VersionFile>();
- auto jarMod = std::make_shared<Library>();
- jarMod->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1"));
- jarMod->setFilename(target_filename);
- jarMod->setDisplayName(sourceInfo.completeBaseName());
- jarMod->setHint("local");
- f->jarMods.append(jarMod);
- f->name = target_name;
- f->uid = target_id;
- QString patchFileName = FS::PathCombine(patchDir, target_id + ".json");
-
- QFile file(patchFileName);
- if (!file.open(QFile::WriteOnly))
- {
- qCritical() << "Error opening" << file.fileName()
- << "for reading:" << file.errorString();
- return false;
- }
- file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
- file.close();
-
- appendComponent(new Component(this, f->uid, f));
- }
- scheduleSave();
- invalidateLaunchProfile();
- return true;
+ QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
+ if(!FS::ensureFolderPathExists(patchDir))
+ {
+ return false;
+ }
+
+ if (!FS::ensureFolderPathExists(d->m_instance->jarModsDir()))
+ {
+ return false;
+ }
+
+ for(auto filepath:filepaths)
+ {
+ QFileInfo sourceInfo(filepath);
+ auto uuid = QUuid::createUuid();
+ QString id = uuid.toString().remove('{').remove('}');
+ QString target_filename = id + ".jar";
+ QString target_id = "org.multimc.jarmod." + id;
+ QString target_name = sourceInfo.completeBaseName() + " (jar mod)";
+ QString finalPath = FS::PathCombine(d->m_instance->jarModsDir(), target_filename);
+
+ QFileInfo targetInfo(finalPath);
+ if(targetInfo.exists())
+ {
+ return false;
+ }
+
+ if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath()))
+ {
+ return false;
+ }
+
+ auto f = std::make_shared<VersionFile>();
+ auto jarMod = std::make_shared<Library>();
+ jarMod->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1"));
+ jarMod->setFilename(target_filename);
+ jarMod->setDisplayName(sourceInfo.completeBaseName());
+ jarMod->setHint("local");
+ f->jarMods.append(jarMod);
+ f->name = target_name;
+ f->uid = target_id;
+ QString patchFileName = FS::PathCombine(patchDir, target_id + ".json");
+
+ QFile file(patchFileName);
+ if (!file.open(QFile::WriteOnly))
+ {
+ qCritical() << "Error opening" << file.fileName()
+ << "for reading:" << file.errorString();
+ return false;
+ }
+ file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
+ file.close();
+
+ appendComponent(new Component(this, f->uid, f));
+ }
+ scheduleSave();
+ invalidateLaunchProfile();
+ return true;
}
bool ComponentList::installCustomJar_internal(QString filepath)
{
- QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
- if(!FS::ensureFolderPathExists(patchDir))
- {
- return false;
- }
-
- QString libDir = d->m_instance->getLocalLibraryPath();
- if (!FS::ensureFolderPathExists(libDir))
- {
- return false;
- }
-
- auto specifier = GradleSpecifier("org.multimc:customjar:1");
- QFileInfo sourceInfo(filepath);
- QString target_filename = specifier.getFileName();
- QString target_id = specifier.artifactId();
- QString target_name = sourceInfo.completeBaseName() + " (custom jar)";
- QString finalPath = FS::PathCombine(libDir, target_filename);
-
- QFileInfo jarInfo(finalPath);
- if (jarInfo.exists())
- {
- if(!QFile::remove(finalPath))
- {
- return false;
- }
- }
- if (!QFile::copy(filepath, finalPath))
- {
- return false;
- }
-
- auto f = std::make_shared<VersionFile>();
- auto jarMod = std::make_shared<Library>();
- jarMod->setRawName(specifier);
- jarMod->setDisplayName(sourceInfo.completeBaseName());
- jarMod->setHint("local");
- f->mainJar = jarMod;
- f->name = target_name;
- f->uid = target_id;
- QString patchFileName = FS::PathCombine(patchDir, target_id + ".json");
-
- QFile file(patchFileName);
- if (!file.open(QFile::WriteOnly))
- {
- qCritical() << "Error opening" << file.fileName()
- << "for reading:" << file.errorString();
- return false;
- }
- file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
- file.close();
-
- appendComponent(new Component(this, f->uid, f));
-
- scheduleSave();
- invalidateLaunchProfile();
- return true;
+ QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
+ if(!FS::ensureFolderPathExists(patchDir))
+ {
+ return false;
+ }
+
+ QString libDir = d->m_instance->getLocalLibraryPath();
+ if (!FS::ensureFolderPathExists(libDir))
+ {
+ return false;
+ }
+
+ auto specifier = GradleSpecifier("org.multimc:customjar:1");
+ QFileInfo sourceInfo(filepath);
+ QString target_filename = specifier.getFileName();
+ QString target_id = specifier.artifactId();
+ QString target_name = sourceInfo.completeBaseName() + " (custom jar)";
+ QString finalPath = FS::PathCombine(libDir, target_filename);
+
+ QFileInfo jarInfo(finalPath);
+ if (jarInfo.exists())
+ {
+ if(!QFile::remove(finalPath))
+ {
+ return false;
+ }
+ }
+ if (!QFile::copy(filepath, finalPath))
+ {
+ return false;
+ }
+
+ auto f = std::make_shared<VersionFile>();
+ auto jarMod = std::make_shared<Library>();
+ jarMod->setRawName(specifier);
+ jarMod->setDisplayName(sourceInfo.completeBaseName());
+ jarMod->setHint("local");
+ f->mainJar = jarMod;
+ f->name = target_name;
+ f->uid = target_id;
+ QString patchFileName = FS::PathCombine(patchDir, target_id + ".json");
+
+ QFile file(patchFileName);
+ if (!file.open(QFile::WriteOnly))
+ {
+ qCritical() << "Error opening" << file.fileName()
+ << "for reading:" << file.errorString();
+ return false;
+ }
+ file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
+ file.close();
+
+ appendComponent(new Component(this, f->uid, f));
+
+ scheduleSave();
+ invalidateLaunchProfile();
+ return true;
}
std::shared_ptr<LaunchProfile> ComponentList::getProfile() const
{
- if(!d->m_profile)
- {
- try
- {
- auto profile = std::make_shared<LaunchProfile>();
- for(auto file: d->components)
- {
- qDebug() << "Applying" << file->getID() << (file->getProblemSeverity() == ProblemSeverity::Error ? "ERROR" : "GOOD");
- file->applyTo(profile.get());
- }
- d->m_profile = profile;
- }
- catch (Exception & error)
- {
- qWarning() << "Couldn't apply profile patches because: " << error.cause();
- }
- }
- return d->m_profile;
+ if(!d->m_profile)
+ {
+ try
+ {
+ auto profile = std::make_shared<LaunchProfile>();
+ for(auto file: d->components)
+ {
+ qDebug() << "Applying" << file->getID() << (file->getProblemSeverity() == ProblemSeverity::Error ? "ERROR" : "GOOD");
+ file->applyTo(profile.get());
+ }
+ d->m_profile = profile;
+ }
+ catch (const Exception &error)
+ {
+ qWarning() << "Couldn't apply profile patches because: " << error.cause();
+ }
+ }
+ return d->m_profile;
}
void ComponentList::setOldConfigVersion(const QString& uid, const QString& version)
{
- if(version.isEmpty())
- {
- return;
- }
- d->m_oldConfigVersions[uid] = version;
+ if(version.isEmpty())
+ {
+ return;
+ }
+ d->m_oldConfigVersions[uid] = version;
}
bool ComponentList::setComponentVersion(const QString& uid, const QString& version, bool important)
{
- auto iter = d->componentIndex.find(uid);
- if(iter != d->componentIndex.end())
- {
- ComponentPtr component = *iter;
- // set existing
- if(component->revert())
- {
- component->setVersion(version);
- component->setImportant(important);
- return true;
- }
- return false;
- }
- else
- {
- // add new
- auto component = new Component(this, uid);
- component->m_version = version;
- component->m_important = important;
- appendComponent(component);
- return true;
- }
+ auto iter = d->componentIndex.find(uid);
+ if(iter != d->componentIndex.end())
+ {
+ ComponentPtr component = *iter;
+ // set existing
+ if(component->revert())
+ {
+ component->setVersion(version);
+ component->setImportant(important);
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ // add new
+ auto component = new Component(this, uid);
+ component->m_version = version;
+ component->m_important = important;
+ appendComponent(component);
+ return true;
+ }
}
QString ComponentList::getComponentVersion(const QString& uid) const
{
- const auto iter = d->componentIndex.find(uid);
- if (iter != d->componentIndex.end())
- {
- return (*iter)->getVersion();
- }
- return QString();
+ const auto iter = d->componentIndex.find(uid);
+ if (iter != d->componentIndex.end())
+ {
+ return (*iter)->getVersion();
+ }
+ return QString();
+}
+
+void ComponentList::disableInteraction(bool disable)
+{
+ if(d->interactionDisabled != disable) {
+ d->interactionDisabled = disable;
+ auto size = d->components.size();
+ if(size) {
+ emit dataChanged(index(0), index(size - 1));
+ }
+ }
}
diff --git a/api/logic/minecraft/ComponentList.h b/api/logic/minecraft/ComponentList.h
index cf4d9975..7b5e1385 100644
--- a/api/logic/minecraft/ComponentList.h
+++ b/api/logic/minecraft/ComponentList.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,111 +36,115 @@ class ComponentUpdateTask;
class MULTIMC_LOGIC_EXPORT ComponentList : public QAbstractListModel
{
- Q_OBJECT
- friend ComponentUpdateTask;
+ Q_OBJECT
+ friend ComponentUpdateTask;
public:
- enum Columns
- {
- NameColumn = 0,
- VersionColumn,
- NUM_COLUMNS
- };
+ enum Columns
+ {
+ NameColumn = 0,
+ VersionColumn,
+ NUM_COLUMNS
+ };
- explicit ComponentList(MinecraftInstance * instance);
- virtual ~ComponentList();
+ explicit ComponentList(MinecraftInstance * instance);
+ virtual ~ComponentList();
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
- virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- virtual int columnCount(const QModelIndex &parent) const override;
- virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ virtual int columnCount(const QModelIndex &parent) const override;
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
- /// call this to explicitly mark the component list as loaded - this is used to build a new component list from scratch.
- void buildingFromScratch();
+ /// call this to explicitly mark the component list as loaded - this is used to build a new component list from scratch.
+ void buildingFromScratch();
- /// install more jar mods
- void installJarMods(QStringList selectedFiles);
+ /// install more jar mods
+ void installJarMods(QStringList selectedFiles);
- /// install a jar/zip as a replacement for the main jar
- void installCustomJar(QString selectedFile);
+ /// install a jar/zip as a replacement for the main jar
+ void installCustomJar(QString selectedFile);
- enum MoveDirection { MoveUp, MoveDown };
- /// move component file # up or down the list
- void move(const int index, const MoveDirection direction);
+ enum MoveDirection { MoveUp, MoveDown };
+ /// move component file # up or down the list
+ void move(const int index, const MoveDirection direction);
- /// remove component file # - including files/records
- bool remove(const int index);
+ /// remove component file # - including files/records
+ bool remove(const int index);
- /// remove component file by id - including files/records
- bool remove(const QString id);
+ /// remove component file by id - including files/records
+ bool remove(const QString id);
- bool customize(int index);
+ bool customize(int index);
- bool revertToBase(int index);
+ bool revertToBase(int index);
- /// reload the list, reload all components, resolve dependencies
- void reload(Net::Mode netmode);
+ /// reload the list, reload all components, resolve dependencies
+ void reload(Net::Mode netmode);
- // reload all components, resolve dependencies
- void resolve(Net::Mode netmode);
+ // reload all components, resolve dependencies
+ void resolve(Net::Mode netmode);
- /// get current running task...
- shared_qobject_ptr<Task> getCurrentTask();
+ /// get current running task...
+ shared_qobject_ptr<Task> getCurrentTask();
- std::shared_ptr<LaunchProfile> getProfile() const;
+ std::shared_ptr<LaunchProfile> getProfile() const;
- // NOTE: used ONLY by MinecraftInstance to provide legacy version mappings from instance config
- void setOldConfigVersion(const QString &uid, const QString &version);
+ // NOTE: used ONLY by MinecraftInstance to provide legacy version mappings from instance config
+ void setOldConfigVersion(const QString &uid, const QString &version);
- QString getComponentVersion(const QString &uid) const;
+ QString getComponentVersion(const QString &uid) const;
- bool setComponentVersion(const QString &uid, const QString &version, bool important = false);
+ bool setComponentVersion(const QString &uid, const QString &version, bool important = false);
- bool installEmpty(const QString &uid, const QString &name);
+ bool installEmpty(const QString &uid, const QString &name);
- QString patchFilePathForUid(const QString &uid) const;
+ QString patchFilePathForUid(const QString &uid) const;
- /// if there is a save scheduled, do it now.
- void saveNow();
+ /// if there is a save scheduled, do it now.
+ void saveNow();
+
+signals:
+ void minecraftChanged();
public:
- /// get the profile component by id
- Component * getComponent(const QString &id);
+ /// get the profile component by id
+ Component * getComponent(const QString &id);
- /// get the profile component by index
- Component * getComponent(int index);
+ /// get the profile component by index
+ Component * getComponent(int index);
private:
- void scheduleSave();
- bool saveIsScheduled() const;
+ void scheduleSave();
+ bool saveIsScheduled() const;
- /// apply the component patches. Catches all the errors and returns true/false for success/failure
- void invalidateLaunchProfile();
+ /// apply the component patches. Catches all the errors and returns true/false for success/failure
+ void invalidateLaunchProfile();
- /// Add the component to the internal list of patches
- void appendComponent(ComponentPtr component);
- /// insert component so that its index is ideally the specified one (returns real index)
- void insertComponent(size_t index, ComponentPtr component);
+ /// Add the component to the internal list of patches
+ void appendComponent(ComponentPtr component);
+ /// insert component so that its index is ideally the specified one (returns real index)
+ void insertComponent(size_t index, ComponentPtr component);
- QString componentsFilePath() const;
- QString patchesPattern() const;
+ QString componentsFilePath() const;
+ QString patchesPattern() const;
private slots:
- void save_internal();
- void updateSucceeded();
- void updateFailed(const QString & error);
- void componentDataChanged();
+ void save_internal();
+ void updateSucceeded();
+ void updateFailed(const QString & error);
+ void componentDataChanged();
+ void disableInteraction(bool disable);
private:
- bool load();
- bool installJarMods_internal(QStringList filepaths);
+ bool load();
+ bool installJarMods_internal(QStringList filepaths);
bool installCustomJar_internal(QString filepath);
- bool removeComponent_internal(ComponentPtr patch);
+ bool removeComponent_internal(ComponentPtr patch);
- bool migratePreComponentConfig();
+ bool migratePreComponentConfig();
private: /* data */
- std::unique_ptr<ComponentListData> d;
+ std::unique_ptr<ComponentListData> d;
};
diff --git a/api/logic/minecraft/ComponentList_p.h b/api/logic/minecraft/ComponentList_p.h
index 26ca5049..7a3d498b 100644
--- a/api/logic/minecraft/ComponentList_p.h
+++ b/api/logic/minecraft/ComponentList_p.h
@@ -13,30 +13,31 @@ using ConnectionList = QList<QMetaObject::Connection>;
struct ComponentListData
{
- // the instance this belongs to
- MinecraftInstance *m_instance;
+ // the instance this belongs to
+ MinecraftInstance *m_instance;
- // the launch profile (volatile, temporary thing created on demand)
- std::shared_ptr<LaunchProfile> m_profile;
+ // the launch profile (volatile, temporary thing created on demand)
+ std::shared_ptr<LaunchProfile> m_profile;
- // version information migrated from instance.cfg file. Single use on migration!
- std::map<QString, QString> m_oldConfigVersions;
- QString getOldConfigVersion(const QString& uid) const
- {
- const auto iter = m_oldConfigVersions.find(uid);
- if(iter != m_oldConfigVersions.cend())
- {
- return (*iter).second;
- }
- return QString();
- }
+ // version information migrated from instance.cfg file. Single use on migration!
+ std::map<QString, QString> m_oldConfigVersions;
+ QString getOldConfigVersion(const QString& uid) const
+ {
+ const auto iter = m_oldConfigVersions.find(uid);
+ if(iter != m_oldConfigVersions.cend())
+ {
+ return (*iter).second;
+ }
+ return QString();
+ }
- // persistent list of components and related machinery
- ComponentContainer components;
- ComponentIndex componentIndex;
- bool dirty = false;
- QTimer m_saveTimer;
- shared_qobject_ptr<Task> m_updateTask;
- bool loaded = false;
+ // persistent list of components and related machinery
+ ComponentContainer components;
+ ComponentIndex componentIndex;
+ bool dirty = false;
+ QTimer m_saveTimer;
+ shared_qobject_ptr<Task> m_updateTask;
+ bool loaded = false;
+ bool interactionDisabled = true;
};
diff --git a/api/logic/minecraft/ComponentUpdateTask.cpp b/api/logic/minecraft/ComponentUpdateTask.cpp
index 2d6ceb91..15003160 100644
--- a/api/logic/minecraft/ComponentUpdateTask.cpp
+++ b/api/logic/minecraft/ComponentUpdateTask.cpp
@@ -32,12 +32,12 @@
*/
ComponentUpdateTask::ComponentUpdateTask(Mode mode, Net::Mode netmode, ComponentList* list, QObject* parent)
- : Task(parent)
+ : Task(parent)
{
- d.reset(new ComponentUpdateTaskData);
- d->m_list = list;
- d->mode = mode;
- d->netmode = netmode;
+ d.reset(new ComponentUpdateTaskData);
+ d->m_list = list;
+ d->mode = mode;
+ d->netmode = netmode;
}
ComponentUpdateTask::~ComponentUpdateTask()
@@ -46,356 +46,356 @@ ComponentUpdateTask::~ComponentUpdateTask()
void ComponentUpdateTask::executeTask()
{
- qDebug() << "Loading components";
- loadComponents();
+ qDebug() << "Loading components";
+ loadComponents();
}
namespace
{
enum class LoadResult
{
- LoadedLocal,
- RequiresRemote,
- Failed
+ LoadedLocal,
+ RequiresRemote,
+ Failed
};
LoadResult composeLoadResult(LoadResult a, LoadResult b)
{
- if (a < b)
- {
- return b;
- }
- return a;
+ if (a < b)
+ {
+ return b;
+ }
+ return a;
}
static LoadResult loadComponent(ComponentPtr component, shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
{
- if(component->m_loaded)
- {
- qDebug() << component->getName() << "is already loaded";
- return LoadResult::LoadedLocal;
- }
-
- LoadResult result = LoadResult::Failed;
- auto customPatchFilename = component->getFilename();
- if(QFile::exists(customPatchFilename))
- {
- // if local file exists...
-
- // check for uid problems inside...
- bool fileChanged = false;
- auto file = ProfileUtils::parseJsonFile(QFileInfo(customPatchFilename), false);
- if(file->uid != component->m_uid)
- {
- file->uid = component->m_uid;
- fileChanged = true;
- }
- if(fileChanged)
- {
- // FIXME: @QUALITY do not ignore return value
- ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), customPatchFilename);
- }
-
- component->m_file = file;
- component->m_loaded = true;
- result = LoadResult::LoadedLocal;
- }
- else
- {
- auto metaVersion = ENV.metadataIndex()->get(component->m_uid, component->m_version);
- component->m_metaVersion = metaVersion;
- if(metaVersion->isLoaded())
- {
- component->m_loaded = true;
- result = LoadResult::LoadedLocal;
- }
- else
- {
- metaVersion->load(netmode);
- loadTask = metaVersion->getCurrentTask();
- if(loadTask)
- result = LoadResult::RequiresRemote;
- else if (metaVersion->isLoaded())
- result = LoadResult::LoadedLocal;
- else
- result = LoadResult::Failed;
- }
- }
- return result;
+ if(component->m_loaded)
+ {
+ qDebug() << component->getName() << "is already loaded";
+ return LoadResult::LoadedLocal;
+ }
+
+ LoadResult result = LoadResult::Failed;
+ auto customPatchFilename = component->getFilename();
+ if(QFile::exists(customPatchFilename))
+ {
+ // if local file exists...
+
+ // check for uid problems inside...
+ bool fileChanged = false;
+ auto file = ProfileUtils::parseJsonFile(QFileInfo(customPatchFilename), false);
+ if(file->uid != component->m_uid)
+ {
+ file->uid = component->m_uid;
+ fileChanged = true;
+ }
+ if(fileChanged)
+ {
+ // FIXME: @QUALITY do not ignore return value
+ ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), customPatchFilename);
+ }
+
+ component->m_file = file;
+ component->m_loaded = true;
+ result = LoadResult::LoadedLocal;
+ }
+ else
+ {
+ auto metaVersion = ENV.metadataIndex()->get(component->m_uid, component->m_version);
+ component->m_metaVersion = metaVersion;
+ if(metaVersion->isLoaded())
+ {
+ component->m_loaded = true;
+ result = LoadResult::LoadedLocal;
+ }
+ else
+ {
+ metaVersion->load(netmode);
+ loadTask = metaVersion->getCurrentTask();
+ if(loadTask)
+ result = LoadResult::RequiresRemote;
+ else if (metaVersion->isLoaded())
+ result = LoadResult::LoadedLocal;
+ else
+ result = LoadResult::Failed;
+ }
+ }
+ 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)
{
- if(component->m_loaded)
- {
- qDebug() << component->getName() << "is already loaded";
- return LoadResult::LoadedLocal;
- }
-
- LoadResult result = LoadResult::Failed;
- auto metaList = ENV.metadataIndex()->get(component->m_uid);
- if(metaList->isLoaded())
- {
- component->m_loaded = true;
- result = LoadResult::LoadedLocal;
- }
- else
- {
- metaList->load(netmode);
- loadTask = metaList->getCurrentTask();
- result = LoadResult::RequiresRemote;
- }
- return result;
+ if(component->m_loaded)
+ {
+ qDebug() << component->getName() << "is already loaded";
+ return LoadResult::LoadedLocal;
+ }
+
+ LoadResult result = LoadResult::Failed;
+ auto metaList = ENV.metadataIndex()->get(component->m_uid);
+ if(metaList->isLoaded())
+ {
+ component->m_loaded = true;
+ result = LoadResult::LoadedLocal;
+ }
+ else
+ {
+ metaList->load(netmode);
+ loadTask = metaList->getCurrentTask();
+ result = LoadResult::RequiresRemote;
+ }
+ return result;
}
*/
static LoadResult loadIndex(shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
{
- // FIXME: DECIDE. do we want to run the update task anyway?
- if(ENV.metadataIndex()->isLoaded())
- {
- qDebug() << "Index is already loaded";
- return LoadResult::LoadedLocal;
- }
- ENV.metadataIndex()->load(netmode);
- loadTask = ENV.metadataIndex()->getCurrentTask();
- if(loadTask)
- {
- return LoadResult::RequiresRemote;
- }
- // FIXME: this is assuming the load succeeded... did it really?
- return LoadResult::LoadedLocal;
+ // FIXME: DECIDE. do we want to run the update task anyway?
+ if(ENV.metadataIndex()->isLoaded())
+ {
+ qDebug() << "Index is already loaded";
+ return LoadResult::LoadedLocal;
+ }
+ ENV.metadataIndex()->load(netmode);
+ loadTask = ENV.metadataIndex()->getCurrentTask();
+ if(loadTask)
+ {
+ return LoadResult::RequiresRemote;
+ }
+ // FIXME: this is assuming the load succeeded... did it really?
+ return LoadResult::LoadedLocal;
}
}
void ComponentUpdateTask::loadComponents()
{
- LoadResult result = LoadResult::LoadedLocal;
- size_t taskIndex = 0;
- size_t componentIndex = 0;
- d->remoteLoadSuccessful = true;
- // load the main index (it is needed to determine if components can revert)
- {
- // FIXME: tear out as a method? or lambda?
- shared_qobject_ptr<Task> indexLoadTask;
- auto singleResult = loadIndex(indexLoadTask, d->netmode);
- result = composeLoadResult(result, singleResult);
- if(indexLoadTask)
- {
- qDebug() << "Remote loading is being run for metadata index";
- RemoteLoadStatus status;
- status.type = RemoteLoadStatus::Type::Index;
- d->remoteLoadStatusList.append(status);
- connect(indexLoadTask.get(), &Task::succeeded, [=]()
- {
- remoteLoadSucceeded(taskIndex);
- });
- connect(indexLoadTask.get(), &Task::failed, [=](const QString & error)
- {
- remoteLoadFailed(taskIndex, error);
- });
- taskIndex++;
- }
- }
- // load all the components OR their lists...
- for (auto component: d->m_list->d->components)
- {
- shared_qobject_ptr<Task> loadTask;
- LoadResult singleResult;
- RemoteLoadStatus::Type loadType;
- // FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now, ignore all that...
+ LoadResult result = LoadResult::LoadedLocal;
+ size_t taskIndex = 0;
+ size_t componentIndex = 0;
+ d->remoteLoadSuccessful = true;
+ // load the main index (it is needed to determine if components can revert)
+ {
+ // FIXME: tear out as a method? or lambda?
+ shared_qobject_ptr<Task> indexLoadTask;
+ auto singleResult = loadIndex(indexLoadTask, d->netmode);
+ result = composeLoadResult(result, singleResult);
+ if(indexLoadTask)
+ {
+ qDebug() << "Remote loading is being run for metadata index";
+ RemoteLoadStatus status;
+ status.type = RemoteLoadStatus::Type::Index;
+ d->remoteLoadStatusList.append(status);
+ connect(indexLoadTask.get(), &Task::succeeded, [=]()
+ {
+ remoteLoadSucceeded(taskIndex);
+ });
+ connect(indexLoadTask.get(), &Task::failed, [=](const QString & error)
+ {
+ remoteLoadFailed(taskIndex, error);
+ });
+ taskIndex++;
+ }
+ }
+ // load all the components OR their lists...
+ for (auto component: d->m_list->d->components)
+ {
+ shared_qobject_ptr<Task> loadTask;
+ LoadResult singleResult;
+ RemoteLoadStatus::Type loadType;
+ // FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now, ignore all that...
#if 0
- switch(d->mode)
- {
- case Mode::Launch:
- {
- singleResult = loadComponent(component, loadTask, d->netmode);
- loadType = RemoteLoadStatus::Type::Version;
- break;
- }
- case Mode::Resolution:
- {
- singleResult = loadComponentList(component, loadTask, d->netmode);
- loadType = RemoteLoadStatus::Type::List;
- break;
- }
- }
+ switch(d->mode)
+ {
+ case Mode::Launch:
+ {
+ singleResult = loadComponent(component, loadTask, d->netmode);
+ loadType = RemoteLoadStatus::Type::Version;
+ break;
+ }
+ case Mode::Resolution:
+ {
+ singleResult = loadComponentList(component, loadTask, d->netmode);
+ loadType = RemoteLoadStatus::Type::List;
+ break;
+ }
+ }
#else
- singleResult = loadComponent(component, loadTask, d->netmode);
- loadType = RemoteLoadStatus::Type::Version;
+ singleResult = loadComponent(component, loadTask, d->netmode);
+ loadType = RemoteLoadStatus::Type::Version;
#endif
- if(singleResult == LoadResult::LoadedLocal)
- {
- component->updateCachedData();
- }
- result = composeLoadResult(result, singleResult);
- if (loadTask)
- {
- qDebug() << "Remote loading is being run for" << component->getName();
- connect(loadTask.get(), &Task::succeeded, [=]()
- {
- remoteLoadSucceeded(taskIndex);
- });
- connect(loadTask.get(), &Task::failed, [=](const QString & error)
- {
- remoteLoadFailed(taskIndex, error);
- });
- RemoteLoadStatus status;
- status.type = loadType;
- status.componentListIndex = componentIndex;
- d->remoteLoadStatusList.append(status);
- taskIndex++;
- }
- componentIndex++;
- }
- d->remoteTasksInProgress = taskIndex;
- switch(result)
- {
- case LoadResult::LoadedLocal:
- {
- // Everything got loaded. Advance to dependency resolution.
- resolveDependencies(d->mode == Mode::Launch || d->netmode == Net::Mode::Offline);
- break;
- }
- case LoadResult::RequiresRemote:
- {
- // we wait for signals.
- break;
- }
- case LoadResult::Failed:
- {
- emitFailed(tr("Some component metadata load tasks failed."));
- break;
- }
- }
+ if(singleResult == LoadResult::LoadedLocal)
+ {
+ component->updateCachedData();
+ }
+ result = composeLoadResult(result, singleResult);
+ if (loadTask)
+ {
+ qDebug() << "Remote loading is being run for" << component->getName();
+ connect(loadTask.get(), &Task::succeeded, [=]()
+ {
+ remoteLoadSucceeded(taskIndex);
+ });
+ connect(loadTask.get(), &Task::failed, [=](const QString & error)
+ {
+ remoteLoadFailed(taskIndex, error);
+ });
+ RemoteLoadStatus status;
+ status.type = loadType;
+ status.componentListIndex = componentIndex;
+ d->remoteLoadStatusList.append(status);
+ taskIndex++;
+ }
+ componentIndex++;
+ }
+ d->remoteTasksInProgress = taskIndex;
+ switch(result)
+ {
+ case LoadResult::LoadedLocal:
+ {
+ // Everything got loaded. Advance to dependency resolution.
+ resolveDependencies(d->mode == Mode::Launch || d->netmode == Net::Mode::Offline);
+ break;
+ }
+ case LoadResult::RequiresRemote:
+ {
+ // we wait for signals.
+ break;
+ }
+ case LoadResult::Failed:
+ {
+ emitFailed(tr("Some component metadata load tasks failed."));
+ break;
+ }
+ }
}
namespace
{
- struct RequireEx : public Meta::Require
- {
- size_t indexOfFirstDependee = 0;
- };
- struct RequireCompositionResult
- {
- bool ok;
- RequireEx outcome;
- };
- using RequireExSet = std::set<RequireEx>;
+ struct RequireEx : public Meta::Require
+ {
+ size_t indexOfFirstDependee = 0;
+ };
+ struct RequireCompositionResult
+ {
+ bool ok;
+ RequireEx outcome;
+ };
+ using RequireExSet = std::set<RequireEx>;
}
static RequireCompositionResult composeRequirement(const RequireEx & a, const RequireEx & b)
{
- assert(a.uid == b.uid);
- RequireEx out;
- out.uid = a.uid;
- out.indexOfFirstDependee = std::min(a.indexOfFirstDependee, b.indexOfFirstDependee);
- if(a.equalsVersion.isEmpty())
- {
- out.equalsVersion = b.equalsVersion;
- }
- else if (b.equalsVersion.isEmpty())
- {
- out.equalsVersion = a.equalsVersion;
- }
- else if (a.equalsVersion == b.equalsVersion)
- {
- out.equalsVersion = a.equalsVersion;
- }
- else
- {
- // FIXME: mark error as explicit version conflict
- return {false, out};
- }
-
- if(a.suggests.isEmpty())
- {
- out.suggests = b.suggests;
- }
- else if (b.suggests.isEmpty())
- {
- out.suggests = a.suggests;
- }
- else
- {
- Version aVer(a.suggests);
- Version bVer(b.suggests);
- out.suggests = (aVer < bVer ? b.suggests : a.suggests);
- }
- return {true, out};
+ assert(a.uid == b.uid);
+ RequireEx out;
+ out.uid = a.uid;
+ out.indexOfFirstDependee = std::min(a.indexOfFirstDependee, b.indexOfFirstDependee);
+ if(a.equalsVersion.isEmpty())
+ {
+ out.equalsVersion = b.equalsVersion;
+ }
+ else if (b.equalsVersion.isEmpty())
+ {
+ out.equalsVersion = a.equalsVersion;
+ }
+ else if (a.equalsVersion == b.equalsVersion)
+ {
+ out.equalsVersion = a.equalsVersion;
+ }
+ else
+ {
+ // FIXME: mark error as explicit version conflict
+ return {false, out};
+ }
+
+ if(a.suggests.isEmpty())
+ {
+ out.suggests = b.suggests;
+ }
+ else if (b.suggests.isEmpty())
+ {
+ out.suggests = a.suggests;
+ }
+ else
+ {
+ Version aVer(a.suggests);
+ Version bVer(b.suggests);
+ out.suggests = (aVer < bVer ? b.suggests : a.suggests);
+ }
+ return {true, out};
}
// gather the requirements from all components, finding any obvious conflicts
static bool gatherRequirementsFromComponents(const ComponentContainer & input, RequireExSet & output)
{
- bool succeeded = true;
- size_t componentNum = 0;
- for(auto component: input)
- {
- auto &componentRequires = component->m_cachedRequires;
- for(const auto & componentRequire: componentRequires)
- {
- auto found = std::find_if(output.cbegin(), output.cend(), [componentRequire](const Meta::Require & req){
- return req.uid == componentRequire.uid;
- });
-
- RequireEx componenRequireEx;
- componenRequireEx.uid = componentRequire.uid;
- componenRequireEx.suggests = componentRequire.suggests;
- componenRequireEx.equalsVersion = componentRequire.equalsVersion;
- componenRequireEx.indexOfFirstDependee = componentNum;
-
- if(found != output.cend())
- {
- // found... process it further
- auto result = composeRequirement(componenRequireEx, *found);
- if(result.ok)
- {
- output.erase(componenRequireEx);
- output.insert(result.outcome);
- }
- else
- {
- qCritical()
- << "Conflicting requirements:"
- << componentRequire.uid
- << "versions:"
- << componentRequire.equalsVersion
- << ";"
- << (*found).equalsVersion;
- }
- succeeded &= result.ok;
- }
- else
- {
- // not found, accumulate
- output.insert(componenRequireEx);
- }
- }
- componentNum++;
- }
- return succeeded;
+ bool succeeded = true;
+ size_t componentNum = 0;
+ for(auto component: input)
+ {
+ auto &componentRequires = component->m_cachedRequires;
+ for(const auto & componentRequire: componentRequires)
+ {
+ auto found = std::find_if(output.cbegin(), output.cend(), [componentRequire](const Meta::Require & req){
+ return req.uid == componentRequire.uid;
+ });
+
+ RequireEx componenRequireEx;
+ componenRequireEx.uid = componentRequire.uid;
+ componenRequireEx.suggests = componentRequire.suggests;
+ componenRequireEx.equalsVersion = componentRequire.equalsVersion;
+ componenRequireEx.indexOfFirstDependee = componentNum;
+
+ if(found != output.cend())
+ {
+ // found... process it further
+ auto result = composeRequirement(componenRequireEx, *found);
+ if(result.ok)
+ {
+ output.erase(componenRequireEx);
+ output.insert(result.outcome);
+ }
+ else
+ {
+ qCritical()
+ << "Conflicting requirements:"
+ << componentRequire.uid
+ << "versions:"
+ << componentRequire.equalsVersion
+ << ";"
+ << (*found).equalsVersion;
+ }
+ succeeded &= result.ok;
+ }
+ else
+ {
+ // not found, accumulate
+ output.insert(componenRequireEx);
+ }
+ }
+ componentNum++;
+ }
+ return succeeded;
}
/// Get list of uids that can be trivially removed because nothing is depending on them anymore (and they are installed as deps)
static void getTrivialRemovals(const ComponentContainer & components, const RequireExSet & reqs, QStringList &toRemove)
{
- for(const auto & component: components)
- {
- if(!component->m_dependencyOnly)
- continue;
- if(!component->m_cachedVolatile)
- continue;
- RequireEx reqNeedle;
- reqNeedle.uid = component->m_uid;
- const auto iter = reqs.find(reqNeedle);
- if(iter == reqs.cend())
- {
- toRemove.append(component->m_uid);
- }
- }
+ for(const auto & component: components)
+ {
+ if(!component->m_dependencyOnly)
+ continue;
+ if(!component->m_cachedVolatile)
+ continue;
+ RequireEx reqNeedle;
+ reqNeedle.uid = component->m_uid;
+ const auto iter = reqs.find(reqNeedle);
+ if(iter == reqs.cend())
+ {
+ toRemove.append(component->m_uid);
+ }
+ }
}
/**
@@ -408,86 +408,86 @@ static void getTrivialRemovals(const ComponentContainer & components, const Requ
*/
static bool getTrivialComponentChanges(const ComponentIndex & index, const RequireExSet & input, RequireExSet & toAdd, RequireExSet & toChange)
{
- enum class Decision
- {
- Undetermined,
- Met,
- Missing,
- VersionNotSame,
- LockedVersionNotSame
- } decision = Decision::Undetermined;
-
- QString reqStr;
- bool succeeded = true;
- // list the composed requirements and say if they are met or unmet
- for(auto & req: input)
- {
- do
- {
- if(req.equalsVersion.isEmpty())
- {
- reqStr = QString("Req: %1").arg(req.uid);
- if(index.contains(req.uid))
- {
- decision = Decision::Met;
- }
- else
- {
- toAdd.insert(req);
- decision = Decision::Missing;
- }
- break;
- }
- else
- {
- reqStr = QString("Req: %1 == %2").arg(req.uid, req.equalsVersion);
- const auto & compIter = index.find(req.uid);
- if(compIter == index.cend())
- {
- toAdd.insert(req);
- decision = Decision::Missing;
- break;
- }
- auto & comp = (*compIter);
- if(comp->getVersion() != req.equalsVersion)
- {
- if(comp->m_dependencyOnly)
- {
- decision = Decision::VersionNotSame;
- }
- else
- {
- decision = Decision::LockedVersionNotSame;
- }
- break;
- }
- decision = Decision::Met;
- }
- } while(false);
- switch(decision)
- {
- case Decision::Undetermined:
- qCritical() << "No decision for" << reqStr;
- succeeded = false;
- break;
- case Decision::Met:
- qDebug() << reqStr << "Is met.";
- break;
- case Decision::Missing:
- qDebug() << reqStr << "Is missing and should be added at" << req.indexOfFirstDependee;
- toAdd.insert(req);
- break;
- case Decision::VersionNotSame:
- qDebug() << reqStr << "already has different version that can be changed.";
- toChange.insert(req);
- break;
- case Decision::LockedVersionNotSame:
- qDebug() << reqStr << "already has different version that cannot be changed.";
- succeeded = false;
- break;
- }
- }
- return succeeded;
+ enum class Decision
+ {
+ Undetermined,
+ Met,
+ Missing,
+ VersionNotSame,
+ LockedVersionNotSame
+ } decision = Decision::Undetermined;
+
+ QString reqStr;
+ bool succeeded = true;
+ // list the composed requirements and say if they are met or unmet
+ for(auto & req: input)
+ {
+ do
+ {
+ if(req.equalsVersion.isEmpty())
+ {
+ reqStr = QString("Req: %1").arg(req.uid);
+ if(index.contains(req.uid))
+ {
+ decision = Decision::Met;
+ }
+ else
+ {
+ toAdd.insert(req);
+ decision = Decision::Missing;
+ }
+ break;
+ }
+ else
+ {
+ reqStr = QString("Req: %1 == %2").arg(req.uid, req.equalsVersion);
+ const auto & compIter = index.find(req.uid);
+ if(compIter == index.cend())
+ {
+ toAdd.insert(req);
+ decision = Decision::Missing;
+ break;
+ }
+ auto & comp = (*compIter);
+ if(comp->getVersion() != req.equalsVersion)
+ {
+ if(comp->m_dependencyOnly)
+ {
+ decision = Decision::VersionNotSame;
+ }
+ else
+ {
+ decision = Decision::LockedVersionNotSame;
+ }
+ break;
+ }
+ decision = Decision::Met;
+ }
+ } while(false);
+ switch(decision)
+ {
+ case Decision::Undetermined:
+ qCritical() << "No decision for" << reqStr;
+ succeeded = false;
+ break;
+ case Decision::Met:
+ qDebug() << reqStr << "Is met.";
+ break;
+ case Decision::Missing:
+ qDebug() << reqStr << "Is missing and should be added at" << req.indexOfFirstDependee;
+ toAdd.insert(req);
+ break;
+ case Decision::VersionNotSame:
+ qDebug() << reqStr << "already has different version that can be changed.";
+ toChange.insert(req);
+ break;
+ case Decision::LockedVersionNotSame:
+ qDebug() << reqStr << "already has different version that cannot be changed.";
+ succeeded = false;
+ break;
+ }
+ }
+ return succeeded;
}
// FIXME, TODO: decouple dependency resolution from loading
@@ -495,197 +495,206 @@ static bool getTrivialComponentChanges(const ComponentIndex & index, const Requi
// FIXME: throw all this away and use a graph
void ComponentUpdateTask::resolveDependencies(bool checkOnly)
{
- qDebug() << "Resolving dependencies";
- /*
- * this is a naive dependency resolving algorithm. all it does is check for following conditions and react in simple ways:
- * 1. There are conflicting dependencies on the same uid with different exact version numbers
- * -> hard error
- * 2. A dependency has non-matching exact version number
- * -> hard error
- * 3. A dependency is entirely missing and needs to be injected before the dependee(s)
- * -> requirements are injected
- *
- * NOTE: this is a placeholder and should eventually be replaced with something 'serious'
- */
- auto & components = d->m_list->d->components;
- auto & componentIndex = d->m_list->d->componentIndex;
-
- RequireExSet allRequires;
- QStringList toRemove;
- do
- {
- allRequires.clear();
- toRemove.clear();
- if(!gatherRequirementsFromComponents(components, allRequires))
- {
- emitFailed(tr("Conflicting requirements detected during dependency checking!"));
- return;
- }
- getTrivialRemovals(components, allRequires, toRemove);
- if(!toRemove.isEmpty())
- {
- qDebug() << "Removing obsolete components...";
- for(auto & remove : toRemove)
- {
- qDebug() << "Removing" << remove;
- d->m_list->remove(remove);
- }
- }
- } while (!toRemove.isEmpty());
- RequireExSet toAdd;
- RequireExSet toChange;
- bool succeeded = getTrivialComponentChanges(componentIndex, allRequires, toAdd, toChange);
- if(!succeeded)
- {
- emitFailed(tr("Instance has conflicting dependencies."));
- return;
- }
- if(checkOnly)
- {
- if(toAdd.size() || toChange.size())
- {
- emitFailed(tr("Instance has unresolved dependencies while loading/checking for launch."));
- }
- else
- {
- emitSucceeded();
- }
- return;
- }
-
- bool recursionNeeded = false;
- if(toAdd.size())
- {
- // add stuff...
- for(auto &add: toAdd)
- {
- ComponentPtr component = new Component(d->m_list, add.uid);
- if(!add.equalsVersion.isEmpty())
- {
- // exact version
- qDebug() << "Adding" << add.uid << "version" << add.equalsVersion << "at position" << add.indexOfFirstDependee;
- component->m_version = add.equalsVersion;
- }
- else
- {
- // version needs to be decided
- qDebug() << "Adding" << add.uid << "at position" << add.indexOfFirstDependee;
+ qDebug() << "Resolving dependencies";
+ /*
+ * this is a naive dependency resolving algorithm. all it does is check for following conditions and react in simple ways:
+ * 1. There are conflicting dependencies on the same uid with different exact version numbers
+ * -> hard error
+ * 2. A dependency has non-matching exact version number
+ * -> hard error
+ * 3. A dependency is entirely missing and needs to be injected before the dependee(s)
+ * -> requirements are injected
+ *
+ * NOTE: this is a placeholder and should eventually be replaced with something 'serious'
+ */
+ auto & components = d->m_list->d->components;
+ auto & componentIndex = d->m_list->d->componentIndex;
+
+ RequireExSet allRequires;
+ QStringList toRemove;
+ do
+ {
+ allRequires.clear();
+ toRemove.clear();
+ if(!gatherRequirementsFromComponents(components, allRequires))
+ {
+ emitFailed(tr("Conflicting requirements detected during dependency checking!"));
+ return;
+ }
+ getTrivialRemovals(components, allRequires, toRemove);
+ if(!toRemove.isEmpty())
+ {
+ qDebug() << "Removing obsolete components...";
+ for(auto & remove : toRemove)
+ {
+ qDebug() << "Removing" << remove;
+ d->m_list->remove(remove);
+ }
+ }
+ } while (!toRemove.isEmpty());
+ RequireExSet toAdd;
+ RequireExSet toChange;
+ bool succeeded = getTrivialComponentChanges(componentIndex, allRequires, toAdd, toChange);
+ if(!succeeded)
+ {
+ emitFailed(tr("Instance has conflicting dependencies."));
+ return;
+ }
+ if(checkOnly)
+ {
+ if(toAdd.size() || toChange.size())
+ {
+ emitFailed(tr("Instance has unresolved dependencies while loading/checking for launch."));
+ }
+ else
+ {
+ emitSucceeded();
+ }
+ return;
+ }
+
+ bool recursionNeeded = false;
+ if(toAdd.size())
+ {
+ // add stuff...
+ for(auto &add: toAdd)
+ {
+ ComponentPtr component = new Component(d->m_list, add.uid);
+ if(!add.equalsVersion.isEmpty())
+ {
+ // exact version
+ qDebug() << "Adding" << add.uid << "version" << add.equalsVersion << "at position" << add.indexOfFirstDependee;
+ component->m_version = add.equalsVersion;
+ }
+ else
+ {
+ // version needs to be decided
+ qDebug() << "Adding" << add.uid << "at position" << add.indexOfFirstDependee;
// ############################################################################################################
// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded.
- if(!add.suggests.isEmpty())
- {
- component->m_version = add.suggests;
- }
- else
- {
- if(add.uid == "org.lwjgl")
- {
- component->m_version = "2.9.1";
- }
- else if (add.uid == "org.lwjgl3")
- {
- component->m_version = "3.1.2";
- }
- }
+ if(!add.suggests.isEmpty())
+ {
+ component->m_version = add.suggests;
+ }
+ else
+ {
+ if(add.uid == "org.lwjgl")
+ {
+ component->m_version = "2.9.1";
+ }
+ else if (add.uid == "org.lwjgl3")
+ {
+ 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.
// ############################################################################################################
- }
- component->m_dependencyOnly = true;
- // FIXME: this should not work directly with the component list
- d->m_list->insertComponent(add.indexOfFirstDependee, component);
- componentIndex[add.uid] = component;
- }
- recursionNeeded = true;
- }
- if(toChange.size())
- {
- // change a version of something that exists
- for(auto &change: toChange)
- {
- // FIXME: this should not work directly with the component list
- qDebug() << "Setting version of " << change.uid << "to" << change.equalsVersion;
- auto component = componentIndex[change.uid];
- component->setVersion(change.equalsVersion);
- }
- recursionNeeded = true;
- }
-
- if(recursionNeeded)
- {
- loadComponents();
- }
- else
- {
- emitSucceeded();
- }
+ }
+ component->m_dependencyOnly = true;
+ // FIXME: this should not work directly with the component list
+ d->m_list->insertComponent(add.indexOfFirstDependee, component);
+ componentIndex[add.uid] = component;
+ }
+ recursionNeeded = true;
+ }
+ if(toChange.size())
+ {
+ // change a version of something that exists
+ for(auto &change: toChange)
+ {
+ // FIXME: this should not work directly with the component list
+ qDebug() << "Setting version of " << change.uid << "to" << change.equalsVersion;
+ auto component = componentIndex[change.uid];
+ component->setVersion(change.equalsVersion);
+ }
+ recursionNeeded = true;
+ }
+
+ if(recursionNeeded)
+ {
+ loadComponents();
+ }
+ else
+ {
+ emitSucceeded();
+ }
}
void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
{
- auto &taskSlot = d->remoteLoadStatusList[taskIndex];
- if(taskSlot.finished)
- {
- qWarning() << "Got multiple results from remote load task" << taskIndex;
- return;
- }
- qDebug() << "Remote task" << taskIndex << "succeeded";
- taskSlot.succeeded = false;
- taskSlot.finished = true;
- d->remoteTasksInProgress --;
- // update the cached data of the component from the downloaded version file.
- if (taskSlot.type == RemoteLoadStatus::Type::Version)
- {
- auto component = d->m_list->getComponent(taskSlot.componentListIndex);
- component->m_loaded = true;
- component->updateCachedData();
- }
- checkIfAllFinished();
+ auto &taskSlot = d->remoteLoadStatusList[taskIndex];
+ if(taskSlot.finished)
+ {
+ qWarning() << "Got multiple results from remote load task" << taskIndex;
+ return;
+ }
+ qDebug() << "Remote task" << taskIndex << "succeeded";
+ taskSlot.succeeded = false;
+ taskSlot.finished = true;
+ d->remoteTasksInProgress --;
+ // update the cached data of the component from the downloaded version file.
+ if (taskSlot.type == RemoteLoadStatus::Type::Version)
+ {
+ auto component = d->m_list->getComponent(taskSlot.componentListIndex);
+ component->m_loaded = true;
+ component->updateCachedData();
+ }
+ checkIfAllFinished();
}
void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg)
{
- auto &taskSlot = d->remoteLoadStatusList[taskIndex];
- if(taskSlot.finished)
- {
- qWarning() << "Got multiple results from remote load task" << taskIndex;
- return;
- }
- qDebug() << "Remote task" << taskIndex << "failed: " << msg;
- d->remoteLoadSuccessful = false;
- taskSlot.succeeded = false;
- taskSlot.finished = true;
- taskSlot.error = msg;
- d->remoteTasksInProgress --;
- checkIfAllFinished();
+ auto &taskSlot = d->remoteLoadStatusList[taskIndex];
+ if(taskSlot.finished)
+ {
+ qWarning() << "Got multiple results from remote load task" << taskIndex;
+ return;
+ }
+ qDebug() << "Remote task" << taskIndex << "failed: " << msg;
+ d->remoteLoadSuccessful = false;
+ taskSlot.succeeded = false;
+ taskSlot.finished = true;
+ taskSlot.error = msg;
+ d->remoteTasksInProgress --;
+ checkIfAllFinished();
}
void ComponentUpdateTask::checkIfAllFinished()
{
- if(d->remoteTasksInProgress)
- {
- // not yet...
- return;
- }
- if(d->remoteLoadSuccessful)
- {
- // nothing bad happened... clear the temp load status and proceed with looking at dependencies
- d->remoteLoadStatusList.clear();
- resolveDependencies(d->mode == Mode::Launch);
- }
- else
- {
- // remote load failed... report error and bail
- QStringList allErrorsList;
- for(auto & item: d->remoteLoadStatusList)
- {
- if(!item.succeeded)
- {
- allErrorsList.append(item.error);
- }
- }
- auto allErrors = allErrorsList.join("\n");
- emitFailed(tr("Component metadata update task failed while downloading from remote server:\n%1").arg(allErrors));
- d->remoteLoadStatusList.clear();
- }
+ if(d->remoteTasksInProgress)
+ {
+ // not yet...
+ return;
+ }
+ if(d->remoteLoadSuccessful)
+ {
+ // nothing bad happened... clear the temp load status and proceed with looking at dependencies
+ d->remoteLoadStatusList.clear();
+ resolveDependencies(d->mode == Mode::Launch);
+ }
+ else
+ {
+ // remote load failed... report error and bail
+ QStringList allErrorsList;
+ for(auto & item: d->remoteLoadStatusList)
+ {
+ if(!item.succeeded)
+ {
+ allErrorsList.append(item.error);
+ }
+ }
+ auto allErrors = allErrorsList.join("\n");
+ emitFailed(tr("Component metadata update task failed while downloading from remote server:\n%1").arg(allErrors));
+ d->remoteLoadStatusList.clear();
+ }
}
diff --git a/api/logic/minecraft/ComponentUpdateTask.h b/api/logic/minecraft/ComponentUpdateTask.h
index 11d122b6..4cb29a89 100644
--- a/api/logic/minecraft/ComponentUpdateTask.h
+++ b/api/logic/minecraft/ComponentUpdateTask.h
@@ -9,29 +9,29 @@ struct ComponentUpdateTaskData;
class ComponentUpdateTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- enum class Mode
- {
- Launch,
- Resolution
- };
+ enum class Mode
+ {
+ Launch,
+ Resolution
+ };
public:
- explicit ComponentUpdateTask(Mode mode, Net::Mode netmode, ComponentList * list, QObject *parent = 0);
- virtual ~ComponentUpdateTask();
+ explicit ComponentUpdateTask(Mode mode, Net::Mode netmode, ComponentList * list, QObject *parent = 0);
+ virtual ~ComponentUpdateTask();
protected:
- void executeTask();
+ void executeTask();
private:
- void loadComponents();
- void resolveDependencies(bool checkOnly);
+ void loadComponents();
+ void resolveDependencies(bool checkOnly);
- void remoteLoadSucceeded(size_t index);
- void remoteLoadFailed(size_t index, const QString &msg);
- void checkIfAllFinished();
+ void remoteLoadSucceeded(size_t index);
+ void remoteLoadFailed(size_t index, const QString &msg);
+ void checkIfAllFinished();
private:
- std::unique_ptr<ComponentUpdateTaskData> d;
+ std::unique_ptr<ComponentUpdateTaskData> d;
};
diff --git a/api/logic/minecraft/ComponentUpdateTask_p.h b/api/logic/minecraft/ComponentUpdateTask_p.h
index a5216506..5989cf07 100644
--- a/api/logic/minecraft/ComponentUpdateTask_p.h
+++ b/api/logic/minecraft/ComponentUpdateTask_p.h
@@ -9,24 +9,24 @@ class ComponentList;
struct RemoteLoadStatus
{
- enum class Type
- {
- Index,
- List,
- Version
- } type = Type::Version;
- size_t componentListIndex = 0;
- bool finished = false;
- bool succeeded = false;
- QString error;
+ enum class Type
+ {
+ Index,
+ List,
+ Version
+ } type = Type::Version;
+ size_t componentListIndex = 0;
+ bool finished = false;
+ bool succeeded = false;
+ QString error;
};
struct ComponentUpdateTaskData
{
- ComponentList * m_list = nullptr;
- QList<RemoteLoadStatus> remoteLoadStatusList;
- bool remoteLoadSuccessful = true;
- size_t remoteTasksInProgress = 0;
- ComponentUpdateTask::Mode mode;
- Net::Mode netmode;
+ ComponentList * m_list = nullptr;
+ QList<RemoteLoadStatus> remoteLoadStatusList;
+ bool remoteLoadSuccessful = true;
+ size_t remoteTasksInProgress = 0;
+ ComponentUpdateTask::Mode mode;
+ Net::Mode netmode;
};
diff --git a/api/logic/minecraft/GradleSpecifier.h b/api/logic/minecraft/GradleSpecifier.h
index f842008c..959325c6 100644
--- a/api/logic/minecraft/GradleSpecifier.h
+++ b/api/logic/minecraft/GradleSpecifier.h
@@ -6,138 +6,138 @@
struct GradleSpecifier
{
- GradleSpecifier()
- {
- m_valid = false;
- }
- GradleSpecifier(QString value)
- {
- operator=(value);
- }
- GradleSpecifier & operator =(const QString & value)
- {
- /*
- org.gradle.test.classifiers : service : 1.0 : jdk15 @ jar
- DEBUG 0 "org.gradle.test.classifiers:service:1.0:jdk15@jar"
- DEBUG 1 "org.gradle.test.classifiers"
- DEBUG 2 "service"
- DEBUG 3 "1.0"
- DEBUG 4 ":jdk15"
- DEBUG 5 "jdk15"
- DEBUG 6 "@jar"
- DEBUG 7 "jar"
- */
- QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(:([^:@]+))?" "(@([^:@]+))?");
- m_valid = matcher.exactMatch(value);
- auto elements = matcher.capturedTexts();
- m_groupId = elements[1];
- m_artifactId = elements[2];
- m_version = elements[3];
- m_classifier = elements[5];
- if(!elements[7].isEmpty())
- {
- m_extension = elements[7];
- }
- return *this;
- }
- operator QString() const
- {
- if(!m_valid)
- return "INVALID";
- QString retval = m_groupId + ":" + m_artifactId + ":" + m_version;
- if(!m_classifier.isEmpty())
- {
- retval += ":" + m_classifier;
- }
- if(m_extension.isExplicit())
- {
- retval += "@" + m_extension;
- }
- return retval;
- }
- QString getFileName() const
- {
- QString filename = m_artifactId + '-' + m_version;
- if(!m_classifier.isEmpty())
- {
- filename += "-" + m_classifier;
- }
- filename += "." + m_extension;
- return filename;
- }
- QString toPath(const QString & filenameOverride = QString()) const
- {
- if(!m_valid)
- return "INVALID";
- QString filename;
- if(filenameOverride.isEmpty())
- {
- filename = getFileName();
- }
- else
- {
- filename = filenameOverride;
- }
- QString path = m_groupId;
- path.replace('.', '/');
- path += '/' + m_artifactId + '/' + m_version + '/' + filename;
- return path;
- }
- inline bool valid() const
- {
- return m_valid;
- }
- inline QString version() const
- {
- return m_version;
- }
- inline QString groupId() const
- {
- return m_groupId;
- }
- inline QString artifactId() const
- {
- return m_artifactId;
- }
- inline void setClassifier(const QString & classifier)
- {
- m_classifier = classifier;
- }
- inline QString classifier() const
- {
- return m_classifier;
- }
- inline QString extension() const
- {
- return m_extension;
- }
- inline QString artifactPrefix() const
- {
- return m_groupId + ":" + m_artifactId;
- }
- bool matchName(const GradleSpecifier & other) const
- {
- return other.artifactId() == artifactId() && other.groupId() == groupId();
- }
- bool operator==(const GradleSpecifier & other) const
- {
- if(m_groupId != other.m_groupId)
- return false;
- if(m_artifactId != other.m_artifactId)
- return false;
- if(m_version != other.m_version)
- return false;
- if(m_classifier != other.m_classifier)
- return false;
- if(m_extension != other.m_extension)
- return false;
- return true;
- }
+ GradleSpecifier()
+ {
+ m_valid = false;
+ }
+ GradleSpecifier(QString value)
+ {
+ operator=(value);
+ }
+ GradleSpecifier & operator =(const QString & value)
+ {
+ /*
+ org.gradle.test.classifiers : service : 1.0 : jdk15 @ jar
+ DEBUG 0 "org.gradle.test.classifiers:service:1.0:jdk15@jar"
+ DEBUG 1 "org.gradle.test.classifiers"
+ DEBUG 2 "service"
+ DEBUG 3 "1.0"
+ DEBUG 4 ":jdk15"
+ DEBUG 5 "jdk15"
+ DEBUG 6 "@jar"
+ DEBUG 7 "jar"
+ */
+ QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(:([^:@]+))?" "(@([^:@]+))?");
+ m_valid = matcher.exactMatch(value);
+ auto elements = matcher.capturedTexts();
+ m_groupId = elements[1];
+ m_artifactId = elements[2];
+ m_version = elements[3];
+ m_classifier = elements[5];
+ if(!elements[7].isEmpty())
+ {
+ m_extension = elements[7];
+ }
+ return *this;
+ }
+ operator QString() const
+ {
+ if(!m_valid)
+ return "INVALID";
+ QString retval = m_groupId + ":" + m_artifactId + ":" + m_version;
+ if(!m_classifier.isEmpty())
+ {
+ retval += ":" + m_classifier;
+ }
+ if(m_extension.isExplicit())
+ {
+ retval += "@" + m_extension;
+ }
+ return retval;
+ }
+ QString getFileName() const
+ {
+ QString filename = m_artifactId + '-' + m_version;
+ if(!m_classifier.isEmpty())
+ {
+ filename += "-" + m_classifier;
+ }
+ filename += "." + m_extension;
+ return filename;
+ }
+ QString toPath(const QString & filenameOverride = QString()) const
+ {
+ if(!m_valid)
+ return "INVALID";
+ QString filename;
+ if(filenameOverride.isEmpty())
+ {
+ filename = getFileName();
+ }
+ else
+ {
+ filename = filenameOverride;
+ }
+ QString path = m_groupId;
+ path.replace('.', '/');
+ path += '/' + m_artifactId + '/' + m_version + '/' + filename;
+ return path;
+ }
+ inline bool valid() const
+ {
+ return m_valid;
+ }
+ inline QString version() const
+ {
+ return m_version;
+ }
+ inline QString groupId() const
+ {
+ return m_groupId;
+ }
+ inline QString artifactId() const
+ {
+ return m_artifactId;
+ }
+ inline void setClassifier(const QString & classifier)
+ {
+ m_classifier = classifier;
+ }
+ inline QString classifier() const
+ {
+ return m_classifier;
+ }
+ inline QString extension() const
+ {
+ return m_extension;
+ }
+ inline QString artifactPrefix() const
+ {
+ return m_groupId + ":" + m_artifactId;
+ }
+ bool matchName(const GradleSpecifier & other) const
+ {
+ return other.artifactId() == artifactId() && other.groupId() == groupId();
+ }
+ bool operator==(const GradleSpecifier & other) const
+ {
+ if(m_groupId != other.m_groupId)
+ return false;
+ if(m_artifactId != other.m_artifactId)
+ return false;
+ if(m_version != other.m_version)
+ return false;
+ if(m_classifier != other.m_classifier)
+ return false;
+ if(m_extension != other.m_extension)
+ return false;
+ return true;
+ }
private:
- QString m_groupId;
- QString m_artifactId;
- QString m_version;
- QString m_classifier;
- DefaultVariable<QString> m_extension = DefaultVariable<QString>("jar");
- bool m_valid = false;
+ QString m_groupId;
+ QString m_artifactId;
+ QString m_version;
+ QString m_classifier;
+ DefaultVariable<QString> m_extension = DefaultVariable<QString>("jar");
+ bool m_valid = false;
};
diff --git a/api/logic/minecraft/GradleSpecifier_test.cpp b/api/logic/minecraft/GradleSpecifier_test.cpp
index 155522cd..f49ec718 100644
--- a/api/logic/minecraft/GradleSpecifier_test.cpp
+++ b/api/logic/minecraft/GradleSpecifier_test.cpp
@@ -5,71 +5,71 @@
class GradleSpecifierTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
private
slots:
- void initTestCase()
- {
-
- }
- void cleanupTestCase()
- {
-
- }
-
- void test_Positive_data()
- {
- QTest::addColumn<QString>("through");
-
- QTest::newRow("3 parter") << "org.gradle.test.classifiers:service:1.0";
- QTest::newRow("classifier") << "org.gradle.test.classifiers:service:1.0:jdk15";
- QTest::newRow("jarextension") << "org.gradle.test.classifiers:service:1.0@jar";
- QTest::newRow("jarboth") << "org.gradle.test.classifiers:service:1.0:jdk15@jar";
- QTest::newRow("packxz") << "org.gradle.test.classifiers:service:1.0:jdk15@jar.pack.xz";
- }
- void test_Positive()
- {
- QFETCH(QString, through);
-
- QString converted = GradleSpecifier(through);
-
- QCOMPARE(converted, through);
- }
-
- void test_Path_data()
- {
- QTest::addColumn<QString>("spec");
- QTest::addColumn<QString>("expected");
-
- QTest::newRow("3 parter") << "group.id:artifact:1.0" << "group/id/artifact/1.0/artifact-1.0.jar";
- QTest::newRow("doom") << "id.software:doom:1.666:demons@wad" << "id/software/doom/1.666/doom-1.666-demons.wad";
- }
- void test_Path()
- {
- QFETCH(QString, spec);
- QFETCH(QString, expected);
-
- QString converted = GradleSpecifier(spec).toPath();
-
- QCOMPARE(converted, expected);
- }
- void test_Negative_data()
- {
- QTest::addColumn<QString>("input");
-
- QTest::newRow("too many :") << "org:gradle.test:class:::ifiers:service:1.0::";
- QTest::newRow("nonsense") << "I like turtles";
- QTest::newRow("empty string") << "";
- QTest::newRow("missing version") << "herp.derp:artifact";
- }
- void test_Negative()
- {
- QFETCH(QString, input);
-
- GradleSpecifier spec(input);
- QVERIFY(!spec.valid());
- QCOMPARE(spec.operator QString(), QString("INVALID"));
- }
+ void initTestCase()
+ {
+
+ }
+ void cleanupTestCase()
+ {
+
+ }
+
+ void test_Positive_data()
+ {
+ QTest::addColumn<QString>("through");
+
+ QTest::newRow("3 parter") << "org.gradle.test.classifiers:service:1.0";
+ QTest::newRow("classifier") << "org.gradle.test.classifiers:service:1.0:jdk15";
+ QTest::newRow("jarextension") << "org.gradle.test.classifiers:service:1.0@jar";
+ QTest::newRow("jarboth") << "org.gradle.test.classifiers:service:1.0:jdk15@jar";
+ QTest::newRow("packxz") << "org.gradle.test.classifiers:service:1.0:jdk15@jar.pack.xz";
+ }
+ void test_Positive()
+ {
+ QFETCH(QString, through);
+
+ QString converted = GradleSpecifier(through);
+
+ QCOMPARE(converted, through);
+ }
+
+ void test_Path_data()
+ {
+ QTest::addColumn<QString>("spec");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("3 parter") << "group.id:artifact:1.0" << "group/id/artifact/1.0/artifact-1.0.jar";
+ QTest::newRow("doom") << "id.software:doom:1.666:demons@wad" << "id/software/doom/1.666/doom-1.666-demons.wad";
+ }
+ void test_Path()
+ {
+ QFETCH(QString, spec);
+ QFETCH(QString, expected);
+
+ QString converted = GradleSpecifier(spec).toPath();
+
+ QCOMPARE(converted, expected);
+ }
+ void test_Negative_data()
+ {
+ QTest::addColumn<QString>("input");
+
+ QTest::newRow("too many :") << "org:gradle.test:class:::ifiers:service:1.0::";
+ QTest::newRow("nonsense") << "I like turtles";
+ QTest::newRow("empty string") << "";
+ QTest::newRow("missing version") << "herp.derp:artifact";
+ }
+ void test_Negative()
+ {
+ QFETCH(QString, input);
+
+ GradleSpecifier spec(input);
+ QVERIFY(!spec.valid());
+ QCOMPARE(spec.operator QString(), QString("INVALID"));
+ }
};
QTEST_GUILESS_MAIN(GradleSpecifierTest)
diff --git a/api/logic/minecraft/LaunchProfile.cpp b/api/logic/minecraft/LaunchProfile.cpp
index 436a39d9..c39bdf04 100644
--- a/api/logic/minecraft/LaunchProfile.cpp
+++ b/api/logic/minecraft/LaunchProfile.cpp
@@ -3,295 +3,295 @@
void LaunchProfile::clear()
{
- m_minecraftVersion.clear();
- m_minecraftVersionType.clear();
- m_minecraftAssets.reset();
- m_minecraftArguments.clear();
- m_tweakers.clear();
- m_mainClass.clear();
- m_appletClass.clear();
- m_libraries.clear();
- m_traits.clear();
- m_jarMods.clear();
- m_mainJar.reset();
- m_problemSeverity = ProblemSeverity::None;
+ m_minecraftVersion.clear();
+ m_minecraftVersionType.clear();
+ m_minecraftAssets.reset();
+ m_minecraftArguments.clear();
+ m_tweakers.clear();
+ m_mainClass.clear();
+ m_appletClass.clear();
+ m_libraries.clear();
+ m_traits.clear();
+ m_jarMods.clear();
+ m_mainJar.reset();
+ m_problemSeverity = ProblemSeverity::None;
}
static void applyString(const QString & from, QString & to)
{
- if(from.isEmpty())
- return;
- to = from;
+ if(from.isEmpty())
+ return;
+ to = from;
}
void LaunchProfile::applyMinecraftVersion(const QString& id)
{
- applyString(id, this->m_minecraftVersion);
+ applyString(id, this->m_minecraftVersion);
}
void LaunchProfile::applyAppletClass(const QString& appletClass)
{
- applyString(appletClass, this->m_appletClass);
+ applyString(appletClass, this->m_appletClass);
}
void LaunchProfile::applyMainClass(const QString& mainClass)
{
- applyString(mainClass, this->m_mainClass);
+ applyString(mainClass, this->m_mainClass);
}
void LaunchProfile::applyMinecraftArguments(const QString& minecraftArguments)
{
- applyString(minecraftArguments, this->m_minecraftArguments);
+ applyString(minecraftArguments, this->m_minecraftArguments);
}
void LaunchProfile::applyMinecraftVersionType(const QString& type)
{
- applyString(type, this->m_minecraftVersionType);
+ applyString(type, this->m_minecraftVersionType);
}
void LaunchProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets)
{
- if(assets)
- {
- m_minecraftAssets = assets;
- }
+ if(assets)
+ {
+ m_minecraftAssets = assets;
+ }
}
void LaunchProfile::applyTraits(const QSet<QString>& traits)
{
- this->m_traits.unite(traits);
+ this->m_traits.unite(traits);
}
void LaunchProfile::applyTweakers(const QStringList& tweakers)
{
- // if the applied tweakers override an existing one, skip it. this effectively moves it later in the sequence
- QStringList newTweakers;
- for(auto & tweaker: m_tweakers)
- {
- if (tweakers.contains(tweaker))
- {
- continue;
- }
- newTweakers.append(tweaker);
- }
- // then just append the new tweakers (or moved original ones)
- newTweakers += tweakers;
- m_tweakers = newTweakers;
+ // if the applied tweakers override an existing one, skip it. this effectively moves it later in the sequence
+ QStringList newTweakers;
+ for(auto & tweaker: m_tweakers)
+ {
+ if (tweakers.contains(tweaker))
+ {
+ continue;
+ }
+ newTweakers.append(tweaker);
+ }
+ // then just append the new tweakers (or moved original ones)
+ newTweakers += tweakers;
+ m_tweakers = newTweakers;
}
void LaunchProfile::applyJarMods(const QList<LibraryPtr>& jarMods)
{
- this->m_jarMods.append(jarMods);
+ this->m_jarMods.append(jarMods);
}
static int findLibraryByName(QList<LibraryPtr> *haystack, const GradleSpecifier &needle)
{
- int retval = -1;
- for (int i = 0; i < haystack->size(); ++i)
- {
- if (haystack->at(i)->rawName().matchName(needle))
- {
- // only one is allowed.
- if (retval != -1)
- return -1;
- retval = i;
- }
- }
- return retval;
+ int retval = -1;
+ for (int i = 0; i < haystack->size(); ++i)
+ {
+ if (haystack->at(i)->rawName().matchName(needle))
+ {
+ // only one is allowed.
+ if (retval != -1)
+ return -1;
+ retval = i;
+ }
+ }
+ return retval;
}
void LaunchProfile::applyMods(const QList<LibraryPtr>& mods)
{
- QList<LibraryPtr> * list = &m_mods;
- for(auto & mod: mods)
- {
- auto modCopy = Library::limitedCopy(mod);
-
- // find the mod by name.
- const int index = findLibraryByName(list, mod->rawName());
- // mod not found? just add it.
- if (index < 0)
- {
- list->append(modCopy);
- return;
- }
-
- auto existingLibrary = list->at(index);
- // if we are higher it means we should update
- if (Version(mod->version()) > Version(existingLibrary->version()))
- {
- list->replace(index, modCopy);
- }
- }
+ QList<LibraryPtr> * list = &m_mods;
+ for(auto & mod: mods)
+ {
+ auto modCopy = Library::limitedCopy(mod);
+
+ // find the mod by name.
+ const int index = findLibraryByName(list, mod->rawName());
+ // mod not found? just add it.
+ if (index < 0)
+ {
+ list->append(modCopy);
+ return;
+ }
+
+ auto existingLibrary = list->at(index);
+ // if we are higher it means we should update
+ if (Version(mod->version()) > Version(existingLibrary->version()))
+ {
+ list->replace(index, modCopy);
+ }
+ }
}
void LaunchProfile::applyLibrary(LibraryPtr library)
{
- if(!library->isActive())
- {
- return;
- }
+ if(!library->isActive())
+ {
+ return;
+ }
- QList<LibraryPtr> * list = &m_libraries;
- if(library->isNative())
- {
- list = &m_nativeLibraries;
- }
+ QList<LibraryPtr> * list = &m_libraries;
+ if(library->isNative())
+ {
+ list = &m_nativeLibraries;
+ }
- auto libraryCopy = Library::limitedCopy(library);
+ auto libraryCopy = Library::limitedCopy(library);
- // find the library by name.
- const int index = findLibraryByName(list, library->rawName());
- // library not found? just add it.
- if (index < 0)
- {
- list->append(libraryCopy);
- return;
- }
+ // find the library by name.
+ const int index = findLibraryByName(list, library->rawName());
+ // library not found? just add it.
+ if (index < 0)
+ {
+ list->append(libraryCopy);
+ return;
+ }
- auto existingLibrary = list->at(index);
- // if we are higher it means we should update
- if (Version(library->version()) > Version(existingLibrary->version()))
- {
- list->replace(index, libraryCopy);
- }
+ auto existingLibrary = list->at(index);
+ // if we are higher it means we should update
+ if (Version(library->version()) > Version(existingLibrary->version()))
+ {
+ list->replace(index, libraryCopy);
+ }
}
const LibraryPtr LaunchProfile::getMainJar() const
{
- return m_mainJar;
+ return m_mainJar;
}
void LaunchProfile::applyMainJar(LibraryPtr jar)
{
- if(jar)
- {
- m_mainJar = jar;
- }
+ if(jar)
+ {
+ m_mainJar = jar;
+ }
}
void LaunchProfile::applyProblemSeverity(ProblemSeverity severity)
{
- if (m_problemSeverity < severity)
- {
- m_problemSeverity = severity;
- }
+ if (m_problemSeverity < severity)
+ {
+ m_problemSeverity = severity;
+ }
}
const QList<PatchProblem> LaunchProfile::getProblems() const
{
- // FIXME: implement something that actually makes sense here
- return {};
+ // FIXME: implement something that actually makes sense here
+ return {};
}
QString LaunchProfile::getMinecraftVersion() const
{
- return m_minecraftVersion;
+ return m_minecraftVersion;
}
QString LaunchProfile::getAppletClass() const
{
- return m_appletClass;
+ return m_appletClass;
}
QString LaunchProfile::getMainClass() const
{
- return m_mainClass;
+ return m_mainClass;
}
const QSet<QString> &LaunchProfile::getTraits() const
{
- return m_traits;
+ return m_traits;
}
const QStringList & LaunchProfile::getTweakers() const
{
- return m_tweakers;
+ return m_tweakers;
}
bool LaunchProfile::hasTrait(const QString& trait) const
{
- return m_traits.contains(trait);
+ return m_traits.contains(trait);
}
ProblemSeverity LaunchProfile::getProblemSeverity() const
{
- return m_problemSeverity;
+ return m_problemSeverity;
}
QString LaunchProfile::getMinecraftVersionType() const
{
- return m_minecraftVersionType;
+ return m_minecraftVersionType;
}
std::shared_ptr<MojangAssetIndexInfo> LaunchProfile::getMinecraftAssets() const
{
- if(!m_minecraftAssets)
- {
- return std::make_shared<MojangAssetIndexInfo>("legacy");
- }
- return m_minecraftAssets;
+ if(!m_minecraftAssets)
+ {
+ return std::make_shared<MojangAssetIndexInfo>("legacy");
+ }
+ return m_minecraftAssets;
}
QString LaunchProfile::getMinecraftArguments() const
{
- return m_minecraftArguments;
+ return m_minecraftArguments;
}
const QList<LibraryPtr> & LaunchProfile::getJarMods() const
{
- return m_jarMods;
+ return m_jarMods;
}
const QList<LibraryPtr> & LaunchProfile::getLibraries() const
{
- return m_libraries;
+ return m_libraries;
}
const QList<LibraryPtr> & LaunchProfile::getNativeLibraries() const
{
- return m_nativeLibraries;
+ return m_nativeLibraries;
}
void LaunchProfile::getLibraryFiles(
- const QString& architecture,
- QStringList& jars,
- QStringList& nativeJars,
- const QString& overridePath,
- const QString& tempPath
+ const QString& architecture,
+ QStringList& jars,
+ QStringList& nativeJars,
+ const QString& overridePath,
+ const QString& tempPath
) const
{
- QStringList native32, native64;
- jars.clear();
- nativeJars.clear();
- for (auto lib : getLibraries())
- {
- lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
- }
- // NOTE: order is important here, add main jar last to the lists
- if(m_mainJar)
- {
- // FIXME: HACK!! jar modding is weird and unsystematic!
- if(m_jarMods.size())
- {
- QDir tempDir(tempPath);
- jars.append(tempDir.absoluteFilePath("minecraft.jar"));
- }
- else
- {
- m_mainJar->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
- }
- }
- for (auto lib : getNativeLibraries())
- {
- lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
- }
- if(architecture == "32")
- {
- nativeJars.append(native32);
- }
- else if(architecture == "64")
- {
- nativeJars.append(native64);
- }
+ QStringList native32, native64;
+ jars.clear();
+ nativeJars.clear();
+ for (auto lib : getLibraries())
+ {
+ lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
+ }
+ // NOTE: order is important here, add main jar last to the lists
+ if(m_mainJar)
+ {
+ // FIXME: HACK!! jar modding is weird and unsystematic!
+ if(m_jarMods.size())
+ {
+ QDir tempDir(tempPath);
+ jars.append(tempDir.absoluteFilePath("minecraft.jar"));
+ }
+ else
+ {
+ m_mainJar->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
+ }
+ }
+ for (auto lib : getNativeLibraries())
+ {
+ lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
+ }
+ if(architecture == "32")
+ {
+ nativeJars.append(native32);
+ }
+ else if(architecture == "64")
+ {
+ nativeJars.append(native64);
+ }
}
diff --git a/api/logic/minecraft/LaunchProfile.h b/api/logic/minecraft/LaunchProfile.h
index e7f5f4af..77174079 100644
--- a/api/logic/minecraft/LaunchProfile.h
+++ b/api/logic/minecraft/LaunchProfile.h
@@ -6,94 +6,94 @@
class LaunchProfile: public ProblemProvider
{
public:
- virtual ~LaunchProfile() {};
+ virtual ~LaunchProfile() {};
public: /* application of profile variables from patches */
- void applyMinecraftVersion(const QString& id);
- void applyMainClass(const QString& mainClass);
- void applyAppletClass(const QString& appletClass);
- void applyMinecraftArguments(const QString& minecraftArguments);
- void applyMinecraftVersionType(const QString& type);
- void applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets);
- void applyTraits(const QSet<QString> &traits);
- void applyTweakers(const QStringList &tweakers);
- void applyJarMods(const QList<LibraryPtr> &jarMods);
- void applyMods(const QList<LibraryPtr> &jarMods);
- void applyLibrary(LibraryPtr library);
- void applyMainJar(LibraryPtr jar);
- void applyProblemSeverity(ProblemSeverity severity);
- /// clear the profile
- void clear();
+ void applyMinecraftVersion(const QString& id);
+ void applyMainClass(const QString& mainClass);
+ void applyAppletClass(const QString& appletClass);
+ void applyMinecraftArguments(const QString& minecraftArguments);
+ void applyMinecraftVersionType(const QString& type);
+ void applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets);
+ void applyTraits(const QSet<QString> &traits);
+ void applyTweakers(const QStringList &tweakers);
+ void applyJarMods(const QList<LibraryPtr> &jarMods);
+ void applyMods(const QList<LibraryPtr> &jarMods);
+ void applyLibrary(LibraryPtr library);
+ void applyMainJar(LibraryPtr jar);
+ void applyProblemSeverity(ProblemSeverity severity);
+ /// clear the profile
+ void clear();
public: /* getters for profile variables */
- QString getMinecraftVersion() const;
- QString getMainClass() const;
- QString getAppletClass() const;
- QString getMinecraftVersionType() const;
- MojangAssetIndexInfo::Ptr getMinecraftAssets() const;
- QString getMinecraftArguments() const;
- const QSet<QString> & getTraits() const;
- const QStringList & getTweakers() const;
- const QList<LibraryPtr> & getJarMods() const;
- const QList<LibraryPtr> & getLibraries() const;
- const QList<LibraryPtr> & getNativeLibraries() const;
- const LibraryPtr getMainJar() const;
- void getLibraryFiles(
- const QString & architecture,
- QStringList & jars,
- QStringList & nativeJars,
- const QString & overridePath,
- const QString & tempPath
- ) const;
- bool hasTrait(const QString & trait) const;
- ProblemSeverity getProblemSeverity() const override;
- const QList<PatchProblem> getProblems() const override;
+ QString getMinecraftVersion() const;
+ QString getMainClass() const;
+ QString getAppletClass() const;
+ QString getMinecraftVersionType() const;
+ MojangAssetIndexInfo::Ptr getMinecraftAssets() const;
+ QString getMinecraftArguments() const;
+ const QSet<QString> & getTraits() const;
+ const QStringList & getTweakers() const;
+ const QList<LibraryPtr> & getJarMods() const;
+ const QList<LibraryPtr> & getLibraries() const;
+ const QList<LibraryPtr> & getNativeLibraries() const;
+ const LibraryPtr getMainJar() const;
+ void getLibraryFiles(
+ const QString & architecture,
+ QStringList & jars,
+ QStringList & nativeJars,
+ const QString & overridePath,
+ const QString & tempPath
+ ) const;
+ bool hasTrait(const QString & trait) const;
+ ProblemSeverity getProblemSeverity() const override;
+ const QList<PatchProblem> getProblems() const override;
private:
- /// the version of Minecraft - jar to use
- QString m_minecraftVersion;
+ /// the version of Minecraft - jar to use
+ QString m_minecraftVersion;
- /// Release type - "release" or "snapshot"
- QString m_minecraftVersionType;
+ /// Release type - "release" or "snapshot"
+ QString m_minecraftVersionType;
- /// Assets type - "legacy" or a version ID
- MojangAssetIndexInfo::Ptr m_minecraftAssets;
+ /// Assets type - "legacy" or a version ID
+ MojangAssetIndexInfo::Ptr m_minecraftAssets;
- /**
- * arguments that should be used for launching minecraft
- *
- * ex: "--username ${auth_player_name} --session ${auth_session}
- * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}"
- */
- QString m_minecraftArguments;
+ /**
+ * arguments that should be used for launching minecraft
+ *
+ * ex: "--username ${auth_player_name} --session ${auth_session}
+ * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}"
+ */
+ QString m_minecraftArguments;
- /// A list of all tweaker classes
- QStringList m_tweakers;
+ /// A list of all tweaker classes
+ QStringList m_tweakers;
- /// The main class to load first
- QString m_mainClass;
+ /// The main class to load first
+ QString m_mainClass;
- /// The applet class, for some very old minecraft releases
- QString m_appletClass;
+ /// The applet class, for some very old minecraft releases
+ QString m_appletClass;
- /// the list of libraries
- QList<LibraryPtr> m_libraries;
+ /// the list of libraries
+ QList<LibraryPtr> m_libraries;
- /// the main jar
- LibraryPtr m_mainJar;
+ /// the main jar
+ LibraryPtr m_mainJar;
- /// the list of libraries
- QList<LibraryPtr> m_nativeLibraries;
+ /// the list of libraries
+ QList<LibraryPtr> m_nativeLibraries;
- /// traits, collected from all the version files (version files can only add)
- QSet<QString> m_traits;
+ /// traits, collected from all the version files (version files can only add)
+ QSet<QString> m_traits;
- /// A list of jar mods. version files can add those.
- QList<LibraryPtr> m_jarMods;
+ /// A list of jar mods. version files can add those.
+ QList<LibraryPtr> m_jarMods;
- /// the list of mods
- QList<LibraryPtr> m_mods;
+ /// the list of mods
+ QList<LibraryPtr> m_mods;
- ProblemSeverity m_problemSeverity = ProblemSeverity::None;
+ ProblemSeverity m_problemSeverity = ProblemSeverity::None;
};
diff --git a/api/logic/minecraft/Library.cpp b/api/logic/minecraft/Library.cpp
index cd1afde4..a56e8110 100644
--- a/api/logic/minecraft/Library.cpp
+++ b/api/logic/minecraft/Library.cpp
@@ -9,316 +9,313 @@
void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& native, QStringList& native32,
- QStringList& native64, const QString &overridePath) const
+ QStringList& native64, const QString &overridePath) const
{
- bool local = isLocal();
- auto actualPath = [&](QString relPath)
- {
- QFileInfo out(FS::PathCombine(storagePrefix(), relPath));
- if(local && !overridePath.isEmpty())
- {
- QString fileName = out.fileName();
- auto fullPath = FS::PathCombine(overridePath, fileName);
- qDebug() << fullPath;
- QFileInfo fileinfo(fullPath);
- if(fileinfo.exists())
- {
- return fileinfo.absoluteFilePath();
- }
- }
- return out.absoluteFilePath();
- };
- QString raw_storage = storageSuffix(system);
- if(isNative())
- {
- if (raw_storage.contains("${arch}"))
- {
- auto nat32Storage = raw_storage;
- nat32Storage.replace("${arch}", "32");
- auto nat64Storage = raw_storage;
- nat64Storage.replace("${arch}", "64");
- native32 += actualPath(nat32Storage);
- native64 += actualPath(nat64Storage);
- }
- else
- {
- native += actualPath(raw_storage);
- }
- }
- else
- {
- jar += actualPath(raw_storage);
- }
+ bool local = isLocal();
+ auto actualPath = [&](QString relPath)
+ {
+ QFileInfo out(FS::PathCombine(storagePrefix(), relPath));
+ if(local && !overridePath.isEmpty())
+ {
+ QString fileName = out.fileName();
+ return QFileInfo(FS::PathCombine(overridePath, fileName)).absoluteFilePath();
+ }
+ return out.absoluteFilePath();
+ };
+ QString raw_storage = storageSuffix(system);
+ if(isNative())
+ {
+ if (raw_storage.contains("${arch}"))
+ {
+ auto nat32Storage = raw_storage;
+ nat32Storage.replace("${arch}", "32");
+ auto nat64Storage = raw_storage;
+ nat64Storage.replace("${arch}", "64");
+ native32 += actualPath(nat32Storage);
+ native64 += actualPath(nat64Storage);
+ }
+ else
+ {
+ native += actualPath(raw_storage);
+ }
+ }
+ else
+ {
+ jar += actualPath(raw_storage);
+ }
}
-QList< std::shared_ptr< NetAction > > Library::getDownloads(OpSys system, class HttpMetaCache* cache,
- QStringList& failedFiles, const QString & overridePath) const
+QList< std::shared_ptr< NetAction > > Library::getDownloads(
+ OpSys system,
+ class HttpMetaCache* cache,
+ QStringList& failedLocalFiles,
+ const QString & overridePath
+) const
{
- QList<NetActionPtr> out;
- bool isAlwaysStale = (hint() == "always-stale");
- bool local = isLocal();
- bool isForge = (hint() == "forge-pack-xz");
+ QList<NetActionPtr> out;
+ bool stale = isAlwaysStale();
+ bool local = isLocal();
- auto add_download = [&](QString storage, QString url, QString sha1 = QString())
- {
- auto entry = cache->resolveEntry("libraries", storage);
- if(isAlwaysStale)
- {
- entry->setStale(true);
- }
- if (!entry->isStale())
- 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;
- if(isAlwaysStale)
- {
- options |= Net::Download::Option::AcceptLocalFiles;
- }
- if (isForge)
- {
- qDebug() << "XzDownload for:" << rawName() << "storage:" << storage << "url:" << url;
- out.append(ForgeXzDownload::make(storage, entry));
- }
- else
- {
- if(sha1.size())
- {
- auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
- auto dl = Net::Download::makeCached(url, entry, options);
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
- qDebug() << "Checksummed Download for:" << rawName() << "storage:" << storage << "url:" << url;
- out.append(dl);
- }
- else
- {
- out.append(Net::Download::makeCached(url, entry, options));
- qDebug() << "Download for:" << rawName() << "storage:" << storage << "url:" << url;
- }
- }
- return true;
- };
+ 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;
+ };
- QString raw_storage = storageSuffix(system);
- if(m_mojangDownloads)
- {
- if(isNative())
- {
- if(m_nativeClassifiers.contains(system))
- {
- auto nativeClassifier = m_nativeClassifiers[system];
- if(nativeClassifier.contains("${arch}"))
- {
- auto nat32Classifier = nativeClassifier;
- nat32Classifier.replace("${arch}", "32");
- auto nat64Classifier = nativeClassifier;
- nat64Classifier.replace("${arch}", "64");
- auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier);
- if(nat32info)
- {
- auto cooked_storage = raw_storage;
- cooked_storage.replace("${arch}", "32");
- add_download(cooked_storage, nat32info->url, nat32info->sha1);
- }
- auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier);
- if(nat64info)
- {
- auto cooked_storage = raw_storage;
- cooked_storage.replace("${arch}", "64");
- add_download(cooked_storage, nat64info->url, nat64info->sha1);
- }
- }
- else
- {
- auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier);
- if(info)
- {
- add_download(raw_storage, info->url, info->sha1);
- }
- }
- }
- else
- {
- qDebug() << "Ignoring native library" << m_name << "because it has no classifier for current OS";
- }
- }
- else
- {
- if(m_mojangDownloads->artifact)
- {
- auto artifact = m_mojangDownloads->artifact;
- add_download(raw_storage, artifact->url, artifact->sha1);
- }
- else
- {
- qDebug() << "Ignoring java library" << m_name << "because it has no artifact";
- }
- }
- }
- else
- {
- auto raw_dl = [&](){
- if (!m_absoluteURL.isEmpty())
- {
- return m_absoluteURL;
- }
+ auto add_download = [&](QString storage, QString url, QString sha1)
+ {
+ if(local)
+ {
+ return check_local_file(storage);
+ }
+ auto entry = cache->resolveEntry("libraries", storage);
+ if(stale)
+ {
+ entry->setStale(true);
+ }
+ if (!entry->isStale())
+ return true;
+ Net::Download::Options options;
+ if(stale)
+ {
+ options |= Net::Download::Option::AcceptLocalFiles;
+ }
+ if (isForge())
+ {
+ qDebug() << "XzDownload for:" << rawName() << "storage:" << storage << "url:" << url;
+ out.append(ForgeXzDownload::make(url, storage, entry));
+ }
+ else
+ {
+ if(sha1.size())
+ {
+ auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
+ auto dl = Net::Download::makeCached(url, entry, options);
+ dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
+ qDebug() << "Checksummed Download for:" << rawName() << "storage:" << storage << "url:" << url;
+ out.append(dl);
+ }
+ else
+ {
+ out.append(Net::Download::makeCached(url, entry, options));
+ qDebug() << "Download for:" << rawName() << "storage:" << storage << "url:" << url;
+ }
+ }
+ return true;
+ };
- if (m_repositoryURL.isEmpty())
- {
- return QString("https://" + URLConstants::LIBRARY_BASE) + raw_storage;
- }
+ QString raw_storage = storageSuffix(system);
+ if(m_mojangDownloads)
+ {
+ if(isNative())
+ {
+ if(m_nativeClassifiers.contains(system))
+ {
+ auto nativeClassifier = m_nativeClassifiers[system];
+ if(nativeClassifier.contains("${arch}"))
+ {
+ auto nat32Classifier = nativeClassifier;
+ nat32Classifier.replace("${arch}", "32");
+ auto nat64Classifier = nativeClassifier;
+ nat64Classifier.replace("${arch}", "64");
+ auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier);
+ if(nat32info)
+ {
+ auto cooked_storage = raw_storage;
+ cooked_storage.replace("${arch}", "32");
+ add_download(cooked_storage, nat32info->url, nat32info->sha1);
+ }
+ auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier);
+ if(nat64info)
+ {
+ auto cooked_storage = raw_storage;
+ cooked_storage.replace("${arch}", "64");
+ add_download(cooked_storage, nat64info->url, nat64info->sha1);
+ }
+ }
+ else
+ {
+ auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier);
+ if(info)
+ {
+ add_download(raw_storage, info->url, info->sha1);
+ }
+ }
+ }
+ else
+ {
+ qDebug() << "Ignoring native library" << m_name << "because it has no classifier for current OS";
+ }
+ }
+ else
+ {
+ if(m_mojangDownloads->artifact)
+ {
+ auto artifact = m_mojangDownloads->artifact;
+ add_download(raw_storage, artifact->url, artifact->sha1);
+ }
+ else
+ {
+ qDebug() << "Ignoring java library" << m_name << "because it has no artifact";
+ }
+ }
+ }
+ else
+ {
+ auto raw_dl = [&]()
+ {
+ if (!m_absoluteURL.isEmpty())
+ {
+ return m_absoluteURL;
+ }
- if(m_repositoryURL.endsWith('/'))
- {
- return m_repositoryURL + raw_storage;
- }
- else
- {
- return m_repositoryURL + QChar('/') + raw_storage;
- }
- }();
- if (raw_storage.contains("${arch}"))
- {
- QString cooked_storage = raw_storage;
- QString cooked_dl = raw_dl;
- add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32"));
- cooked_storage = raw_storage;
- cooked_dl = raw_dl;
- add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64"));
- }
- else
- {
- add_download(raw_storage, raw_dl);
- }
- }
- return out;
+ if (m_repositoryURL.isEmpty())
+ {
+ return URLConstants::LIBRARY_BASE + raw_storage;
+ }
+
+ if(m_repositoryURL.endsWith('/'))
+ {
+ return m_repositoryURL + raw_storage;
+ }
+ else
+ {
+ return m_repositoryURL + QChar('/') + raw_storage;
+ }
+ }();
+ if (raw_storage.contains("${arch}"))
+ {
+ QString cooked_storage = raw_storage;
+ QString cooked_dl = raw_dl;
+ add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32"), QString());
+ cooked_storage = raw_storage;
+ cooked_dl = raw_dl;
+ add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64"), QString());
+ }
+ else
+ {
+ add_download(raw_storage, raw_dl, QString());
+ }
+ }
+ return out;
}
bool Library::isActive() const
{
- bool result = true;
- if (m_rules.empty())
- {
- result = true;
- }
- else
- {
- RuleAction ruleResult = Disallow;
- for (auto rule : m_rules)
- {
- RuleAction temp = rule->apply(this);
- if (temp != Defer)
- ruleResult = temp;
- }
- result = result && (ruleResult == Allow);
- }
- if (isNative())
- {
- result = result && m_nativeClassifiers.contains(currentSystem);
- }
- return result;
+ bool result = true;
+ if (m_rules.empty())
+ {
+ result = true;
+ }
+ else
+ {
+ RuleAction ruleResult = Disallow;
+ for (auto rule : m_rules)
+ {
+ RuleAction temp = rule->apply(this);
+ if (temp != Defer)
+ ruleResult = temp;
+ }
+ result = result && (ruleResult == Allow);
+ }
+ if (isNative())
+ {
+ result = result && m_nativeClassifiers.contains(currentSystem);
+ }
+ return result;
}
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)
{
- m_storagePrefix = prefix;
+ m_storagePrefix = prefix;
}
QString Library::defaultStoragePrefix()
{
- return "libraries/";
+ return "libraries/";
}
QString Library::storagePrefix() const
{
- if(m_storagePrefix.isEmpty())
- {
- return defaultStoragePrefix();
- }
- return m_storagePrefix;
+ if(m_storagePrefix.isEmpty())
+ {
+ return defaultStoragePrefix();
+ }
+ return m_storagePrefix;
}
QString Library::filename(OpSys system) const
{
- if(!m_filename.isEmpty())
- {
- return m_filename;
- }
- // non-native? use only the gradle specifier
- if (!isNative())
- {
- return m_name.getFileName();
- }
+ if(!m_filename.isEmpty())
+ {
+ return m_filename;
+ }
+ // non-native? use only the gradle specifier
+ if (!isNative())
+ {
+ return m_name.getFileName();
+ }
- // otherwise native, override classifiers. Mojang HACK!
- GradleSpecifier nativeSpec = m_name;
- if (m_nativeClassifiers.contains(system))
- {
- nativeSpec.setClassifier(m_nativeClassifiers[system]);
- }
- else
- {
- nativeSpec.setClassifier("INVALID");
- }
- return nativeSpec.getFileName();
+ // otherwise native, override classifiers. Mojang HACK!
+ GradleSpecifier nativeSpec = m_name;
+ if (m_nativeClassifiers.contains(system))
+ {
+ nativeSpec.setClassifier(m_nativeClassifiers[system]);
+ }
+ else
+ {
+ nativeSpec.setClassifier("INVALID");
+ }
+ return nativeSpec.getFileName();
}
QString Library::displayName(OpSys system) const
{
- if(!m_displayname.isEmpty())
- return m_displayname;
- return filename(system);
+ if(!m_displayname.isEmpty())
+ return m_displayname;
+ return filename(system);
}
QString Library::storageSuffix(OpSys system) const
{
- // non-native? use only the gradle specifier
- if (!isNative())
- {
- return m_name.toPath(m_filename);
- }
+ // non-native? use only the gradle specifier
+ if (!isNative())
+ {
+ return m_name.toPath(m_filename);
+ }
- // otherwise native, override classifiers. Mojang HACK!
- GradleSpecifier nativeSpec = m_name;
- if (m_nativeClassifiers.contains(system))
- {
- nativeSpec.setClassifier(m_nativeClassifiers[system]);
- }
- else
- {
- nativeSpec.setClassifier("INVALID");
- }
- return nativeSpec.toPath(m_filename);
+ // otherwise native, override classifiers. Mojang HACK!
+ GradleSpecifier nativeSpec = m_name;
+ if (m_nativeClassifiers.contains(system))
+ {
+ nativeSpec.setClassifier(m_nativeClassifiers[system]);
+ }
+ else
+ {
+ nativeSpec.setClassifier("INVALID");
+ }
+ return nativeSpec.toPath(m_filename);
}
diff --git a/api/logic/minecraft/Library.h b/api/logic/minecraft/Library.h
index 9577de33..014f6745 100644
--- a/api/logic/minecraft/Library.h
+++ b/api/logic/minecraft/Library.h
@@ -24,191 +24,197 @@ typedef std::shared_ptr<Library> LibraryPtr;
class MULTIMC_LOGIC_EXPORT Library
{
- friend class OneSixVersionFormat;
- friend class MojangVersionFormat;
- friend class LibraryTest;
+ friend class OneSixVersionFormat;
+ friend class MojangVersionFormat;
+ friend class LibraryTest;
public:
- Library()
- {
- }
- Library(const QString &name)
- {
- m_name = name;
- }
- /// limited copy without some data. TODO: why?
- static LibraryPtr limitedCopy(LibraryPtr base)
- {
- auto newlib = std::make_shared<Library>();
- newlib->m_name = base->m_name;
- newlib->m_repositoryURL = base->m_repositoryURL;
- newlib->m_hint = base->m_hint;
- newlib->m_absoluteURL = base->m_absoluteURL;
- newlib->m_extractExcludes = base->m_extractExcludes;
- newlib->m_nativeClassifiers = base->m_nativeClassifiers;
- newlib->m_rules = base->m_rules;
- newlib->m_storagePrefix = base->m_storagePrefix;
- newlib->m_mojangDownloads = base->m_mojangDownloads;
- newlib->m_filename = base->m_filename;
- return newlib;
- }
+ Library()
+ {
+ }
+ Library(const QString &name)
+ {
+ m_name = name;
+ }
+ /// limited copy without some data. TODO: why?
+ static LibraryPtr limitedCopy(LibraryPtr base)
+ {
+ auto newlib = std::make_shared<Library>();
+ newlib->m_name = base->m_name;
+ newlib->m_repositoryURL = base->m_repositoryURL;
+ newlib->m_hint = base->m_hint;
+ newlib->m_absoluteURL = base->m_absoluteURL;
+ newlib->m_extractExcludes = base->m_extractExcludes;
+ newlib->m_nativeClassifiers = base->m_nativeClassifiers;
+ newlib->m_rules = base->m_rules;
+ newlib->m_storagePrefix = base->m_storagePrefix;
+ newlib->m_mojangDownloads = base->m_mojangDownloads;
+ newlib->m_filename = base->m_filename;
+ return newlib;
+ }
public: /* methods */
- /// Returns the raw name field
- const GradleSpecifier & rawName() const
- {
- return m_name;
- }
-
- void setRawName(const GradleSpecifier & spec)
- {
- m_name = spec;
- }
-
- void setClassifier(const QString & spec)
- {
- m_name.setClassifier(spec);
- }
-
- /// returns the full group and artifact prefix
- QString artifactPrefix() const
- {
- return m_name.artifactPrefix();
- }
-
- /// get the artifact ID
- QString artifactId() const
- {
- return m_name.artifactId();
- }
-
- /// get the artifact version
- QString version() const
- {
- return m_name.version();
- }
-
- /// Returns true if the library is native
- bool isNative() const
- {
- return m_nativeClassifiers.size() != 0;
- }
-
- void setStoragePrefix(QString prefix = QString());
-
- /// Set the url base for downloads
- void setRepositoryURL(const QString &base_url)
- {
- m_repositoryURL = base_url;
- }
-
- void getApplicableFiles(OpSys system, QStringList & jar, QStringList & native,
- QStringList & native32, QStringList & native64, const QString & overridePath) const;
-
- void setAbsoluteUrl(const QString &absolute_url)
- {
- m_absoluteURL = absolute_url;
- }
-
- void setFilename(const QString &filename)
- {
- m_filename = filename;
- }
-
- /// Get the file name of the library
- QString filename(OpSys system) const;
-
- // DEPRECATED: set a display name, used by jar mods only
- void setDisplayName(const QString & displayName)
- {
- m_displayname = displayName;
- }
-
- /// Get the file name of the library
- QString displayName(OpSys system) const;
-
- void setMojangDownloadInfo(MojangLibraryDownloadInfo::Ptr info)
- {
- m_mojangDownloads = info;
- }
-
- void setHint(const QString &hint)
- {
- m_hint = hint;
- }
-
- /// Set the load rules
- void setRules(QList<std::shared_ptr<Rule>> rules)
- {
- m_rules = rules;
- }
-
- /// Returns true if the library should be loaded (or extracted, in case of natives)
- bool isActive() const;
-
- /// Returns true if the library is contained in an instance and false if it is shared
- bool isLocal() const;
-
- // Get a list of downloads for this library
- QList<NetActionPtr> getDownloads(OpSys system, class HttpMetaCache * cache,
- QStringList & failedFiles, const QString & overridePath) const;
+ /// Returns the raw name field
+ const GradleSpecifier & rawName() const
+ {
+ return m_name;
+ }
+
+ void setRawName(const GradleSpecifier & spec)
+ {
+ m_name = spec;
+ }
+
+ void setClassifier(const QString & spec)
+ {
+ m_name.setClassifier(spec);
+ }
+
+ /// returns the full group and artifact prefix
+ QString artifactPrefix() const
+ {
+ return m_name.artifactPrefix();
+ }
+
+ /// get the artifact ID
+ QString artifactId() const
+ {
+ return m_name.artifactId();
+ }
+
+ /// get the artifact version
+ QString version() const
+ {
+ return m_name.version();
+ }
+
+ /// Returns true if the library is native
+ bool isNative() const
+ {
+ return m_nativeClassifiers.size() != 0;
+ }
+
+ void setStoragePrefix(QString prefix = QString());
+
+ /// Set the url base for downloads
+ void setRepositoryURL(const QString &base_url)
+ {
+ m_repositoryURL = base_url;
+ }
+
+ void getApplicableFiles(OpSys system, QStringList & jar, QStringList & native,
+ QStringList & native32, QStringList & native64, const QString & overridePath) const;
+
+ void setAbsoluteUrl(const QString &absolute_url)
+ {
+ m_absoluteURL = absolute_url;
+ }
+
+ void setFilename(const QString &filename)
+ {
+ m_filename = filename;
+ }
+
+ /// Get the file name of the library
+ QString filename(OpSys system) const;
+
+ // DEPRECATED: set a display name, used by jar mods only
+ void setDisplayName(const QString & displayName)
+ {
+ m_displayname = displayName;
+ }
+
+ /// Get the file name of the library
+ QString displayName(OpSys system) const;
+
+ void setMojangDownloadInfo(MojangLibraryDownloadInfo::Ptr info)
+ {
+ m_mojangDownloads = info;
+ }
+
+ void setHint(const QString &hint)
+ {
+ m_hint = hint;
+ }
+
+ /// Set the load rules
+ void setRules(QList<std::shared_ptr<Rule>> rules)
+ {
+ m_rules = rules;
+ }
+
+ /// Returns true if the library should be loaded (or extracted, in case of natives)
+ bool isActive() const;
+
+ /// Returns true if the library is contained in an instance and false if it is shared
+ 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
+ QList<NetActionPtr> getDownloads(OpSys system, class HttpMetaCache * cache,
+ QStringList & failedLocalFiles, const QString & overridePath) const;
private: /* methods */
- /// the default storage prefix used by MultiMC
- static QString defaultStoragePrefix();
+ /// the default storage prefix used by MultiMC
+ static QString defaultStoragePrefix();
- /// Get the prefix - root of the storage to be used
- QString storagePrefix() const;
+ /// Get the prefix - root of the storage to be used
+ QString storagePrefix() const;
- /// Get the relative file path where the library should be saved
- QString storageSuffix(OpSys system) const;
+ /// Get the relative file path where the library should be saved
+ QString storageSuffix(OpSys system) const;
- QString hint() const
- {
- return m_hint;
- }
+ QString hint() const
+ {
+ return m_hint;
+ }
protected: /* data */
- /// the basic gradle dependency specifier.
- GradleSpecifier m_name;
+ /// the basic gradle dependency specifier.
+ GradleSpecifier m_name;
- /// DEPRECATED URL prefix of the maven repo where the file can be downloaded
- QString m_repositoryURL;
+ /// DEPRECATED URL prefix of the maven repo where the file can be downloaded
+ QString m_repositoryURL;
- /// DEPRECATED: MultiMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined
- QString m_absoluteURL;
+ /// DEPRECATED: MultiMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined
+ QString m_absoluteURL;
- /// MultiMC extension - filename override
- QString m_filename;
+ /// MultiMC extension - filename override
+ QString m_filename;
- /// DEPRECATED MultiMC extension - display name
- QString m_displayname;
+ /// DEPRECATED MultiMC extension - display name
+ QString m_displayname;
- /**
- * MultiMC-specific type hint - modifies how the library is treated
- */
- QString m_hint;
+ /**
+ * MultiMC-specific type hint - modifies how the library is treated
+ */
+ QString m_hint;
- /**
- * storage - by default the local libraries folder in multimc, but could be elsewhere
- * MultiMC specific, because of FTB.
- */
- QString m_storagePrefix;
+ /**
+ * storage - by default the local libraries folder in multimc, but could be elsewhere
+ * MultiMC specific, because of FTB.
+ */
+ QString m_storagePrefix;
- /// true if the library had an extract/excludes section (even empty)
- bool m_hasExcludes = false;
+ /// true if the library had an extract/excludes section (even empty)
+ bool m_hasExcludes = false;
- /// a list of files that shouldn't be extracted from the library
- QStringList m_extractExcludes;
+ /// a list of files that shouldn't be extracted from the library
+ QStringList m_extractExcludes;
- /// native suffixes per OS
- QMap<OpSys, QString> m_nativeClassifiers;
+ /// native suffixes per OS
+ QMap<OpSys, QString> m_nativeClassifiers;
- /// true if the library had a rules section (even empty)
- bool applyRules = false;
+ /// true if the library had a rules section (even empty)
+ bool applyRules = false;
- /// rules associated with the library
- QList<std::shared_ptr<Rule>> m_rules;
+ /// rules associated with the library
+ QList<std::shared_ptr<Rule>> m_rules;
- /// MOJANG: container with Mojang style download info
- MojangLibraryDownloadInfo::Ptr m_mojangDownloads;
+ /// MOJANG: container with Mojang style download info
+ MojangLibraryDownloadInfo::Ptr m_mojangDownloads;
};
diff --git a/api/logic/minecraft/Library_test.cpp b/api/logic/minecraft/Library_test.cpp
index 1aac951b..c3d6150d 100644
--- a/api/logic/minecraft/Library_test.cpp
+++ b/api/logic/minecraft/Library_test.cpp
@@ -9,261 +9,261 @@
class LibraryTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
private:
- LibraryPtr readMojangJson(const char *file)
- {
- auto path = QFINDTESTDATA(file);
- QFile jsonFile(path);
- jsonFile.open(QIODevice::ReadOnly);
- auto data = jsonFile.readAll();
- jsonFile.close();
- return MojangVersionFormat::libraryFromJson(QJsonDocument::fromJson(data).object(), file);
- }
- // get absolute path to expected storage, assuming default cache prefix
- QStringList getStorage(QString relative)
- {
- return {FS::PathCombine(cache->getBasePath("libraries"), relative)};
- }
+ LibraryPtr readMojangJson(const char *file)
+ {
+ auto path = QFINDTESTDATA(file);
+ QFile jsonFile(path);
+ jsonFile.open(QIODevice::ReadOnly);
+ auto data = jsonFile.readAll();
+ jsonFile.close();
+ return MojangVersionFormat::libraryFromJson(QJsonDocument::fromJson(data).object(), file);
+ }
+ // get absolute path to expected storage, assuming default cache prefix
+ QStringList getStorage(QString relative)
+ {
+ return {FS::PathCombine(cache->getBasePath("libraries"), relative)};
+ }
private
slots:
- void initTestCase()
- {
- cache.reset(new HttpMetaCache());
- cache->addBase("libraries", QDir("libraries").absolutePath());
- dataDir = QDir("data").absolutePath();
- }
- void test_legacy()
- {
- Library test("test.package:testname:testversion");
- QCOMPARE(test.artifactPrefix(), QString("test.package:testname"));
- QCOMPARE(test.isNative(), false);
+ void initTestCase()
+ {
+ cache.reset(new HttpMetaCache());
+ cache->addBase("libraries", QDir("libraries").absolutePath());
+ dataDir = QDir("data").absolutePath();
+ }
+ void test_legacy()
+ {
+ Library test("test.package:testname:testversion");
+ QCOMPARE(test.artifactPrefix(), QString("test.package:testname"));
+ QCOMPARE(test.isNative(), false);
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString());
- QCOMPARE(jar, getStorage("test/package/testname/testversion/testname-testversion.jar"));
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- void test_legacy_url()
- {
- QStringList failedFiles;
- Library test("test.package:testname:testversion");
- test.setRepositoryURL("file://foo/bar");
- auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
- QCOMPARE(downloads.size(), 1);
- QCOMPARE(failedFiles, {});
- NetActionPtr dl = downloads[0];
- QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion.jar"));
- }
- void test_legacy_url_local_broken()
- {
- Library test("test.package:testname:testversion");
- QCOMPARE(test.isNative(), false);
- QStringList failedFiles;
- test.setHint("local");
- auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
- QCOMPARE(downloads.size(), 0);
- QCOMPARE(failedFiles, getStorage("test/package/testname/testversion/testname-testversion.jar"));
- }
- void test_legacy_url_local_override()
- {
- Library test("com.paulscode:codecwav:20101023");
- QCOMPARE(test.isNative(), false);
- QStringList failedFiles;
- test.setHint("local");
- auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString("data"));
- QCOMPARE(downloads.size(), 0);
- qDebug() << failedFiles;
- QCOMPARE(failedFiles.size(), 0);
+ QStringList jar, native, native32, native64;
+ test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString());
+ QCOMPARE(jar, getStorage("test/package/testname/testversion/testname-testversion.jar"));
+ QCOMPARE(native, {});
+ QCOMPARE(native32, {});
+ QCOMPARE(native64, {});
+ }
+ void test_legacy_url()
+ {
+ QStringList failedFiles;
+ Library test("test.package:testname:testversion");
+ test.setRepositoryURL("file://foo/bar");
+ auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
+ QCOMPARE(downloads.size(), 1);
+ QCOMPARE(failedFiles, {});
+ NetActionPtr dl = downloads[0];
+ QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion.jar"));
+ }
+ void test_legacy_url_local_broken()
+ {
+ Library test("test.package:testname:testversion");
+ QCOMPARE(test.isNative(), false);
+ QStringList failedFiles;
+ test.setHint("local");
+ auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
+ QCOMPARE(downloads.size(), 0);
+ QCOMPARE(failedFiles, {"testname-testversion.jar"});
+ }
+ void test_legacy_url_local_override()
+ {
+ Library test("com.paulscode:codecwav:20101023");
+ QCOMPARE(test.isNative(), false);
+ QStringList failedFiles;
+ test.setHint("local");
+ auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString("data"));
+ QCOMPARE(downloads.size(), 0);
+ qDebug() << failedFiles;
+ QCOMPARE(failedFiles.size(), 0);
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString("data"));
- QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()});
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- void test_legacy_native()
- {
- Library test("test.package:testname:testversion");
- test.m_nativeClassifiers[OpSys::Os_Linux]="linux";
- QCOMPARE(test.isNative(), true);
- test.setRepositoryURL("file://foo/bar");
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, getStorage("test/package/testname/testversion/testname-testversion-linux.jar"));
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 1);
- QCOMPARE(failedFiles, {});
- auto dl = dls[0];
- QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux.jar"));
- }
- }
- void test_legacy_native_arch()
- {
- Library test("test.package:testname:testversion");
- test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}";
- test.m_nativeClassifiers[OpSys::Os_OSX]="osx-${arch}";
- test.m_nativeClassifiers[OpSys::Os_Windows]="windows-${arch}";
- QCOMPARE(test.isNative(), true);
- test.setRepositoryURL("file://foo/bar");
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-linux-32.jar"));
- QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar"));
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 2);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-32.jar"));
- QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-64.jar"));
- }
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_Windows, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-windows-32.jar"));
- QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-windows-64.jar"));
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_Windows, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 2);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-32.jar"));
- QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-64.jar"));
- }
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-osx-32.jar"));
- QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-osx-64.jar"));
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_OSX, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 2);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-32.jar"));
- QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-64.jar"));
- }
- }
- void test_legacy_native_arch_local_override()
- {
- Library test("test.package:testname:testversion");
- test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}";
- test.setHint("local");
- QCOMPARE(test.isNative(), true);
- test.setRepositoryURL("file://foo/bar");
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString("data"));
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, {QFileInfo("data/testname-testversion-linux-32.jar").absoluteFilePath()});
- QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar"));
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
- QCOMPARE(dls.size(), 0);
- QCOMPARE(failedFiles, {getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar")});
- }
- }
- void test_onenine()
- {
- auto test = readMojangJson("data/lib-simple.json");
- {
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
- QCOMPARE(jar, getStorage("com/paulscode/codecwav/20101023/codecwav-20101023.jar"));
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- {
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 1);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar"));
- }
- test->setHint("local");
- {
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data"));
- QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()});
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- {
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
- QCOMPARE(dls.size(), 0);
- QCOMPARE(failedFiles, {});
- }
- }
- void test_onenine_local_override()
- {
- auto test = readMojangJson("data/lib-simple.json");
- test->setHint("local");
- {
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data"));
- QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()});
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- {
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
- QCOMPARE(dls.size(), 0);
- QCOMPARE(failedFiles, {});
- }
- }
- void test_onenine_native()
- {
- auto test = readMojangJson("data/lib-native.json");
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
- QCOMPARE(jar, QStringList());
- QCOMPARE(native, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"));
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_OSX, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 1);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"));
- }
- void test_onenine_native_arch()
- {
- auto test = readMojangJson("data/lib-native-arch.json");
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_Windows, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"));
- QCOMPARE(native64, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"));
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_Windows, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 2);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"));
- QCOMPARE(dls[1]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"));
- }
+ QStringList jar, native, native32, native64;
+ test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString("data"));
+ QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()});
+ QCOMPARE(native, {});
+ QCOMPARE(native32, {});
+ QCOMPARE(native64, {});
+ }
+ void test_legacy_native()
+ {
+ Library test("test.package:testname:testversion");
+ test.m_nativeClassifiers[OpSys::Os_Linux]="linux";
+ QCOMPARE(test.isNative(), true);
+ test.setRepositoryURL("file://foo/bar");
+ {
+ QStringList jar, native, native32, native64;
+ test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString());
+ QCOMPARE(jar, {});
+ QCOMPARE(native, getStorage("test/package/testname/testversion/testname-testversion-linux.jar"));
+ QCOMPARE(native32, {});
+ QCOMPARE(native64, {});
+ QStringList failedFiles;
+ auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString());
+ QCOMPARE(dls.size(), 1);
+ QCOMPARE(failedFiles, {});
+ auto dl = dls[0];
+ QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux.jar"));
+ }
+ }
+ void test_legacy_native_arch()
+ {
+ Library test("test.package:testname:testversion");
+ test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}";
+ test.m_nativeClassifiers[OpSys::Os_OSX]="osx-${arch}";
+ test.m_nativeClassifiers[OpSys::Os_Windows]="windows-${arch}";
+ QCOMPARE(test.isNative(), true);
+ test.setRepositoryURL("file://foo/bar");
+ {
+ QStringList jar, native, native32, native64;
+ test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString());
+ QCOMPARE(jar, {});
+ QCOMPARE(native, {});
+ QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-linux-32.jar"));
+ QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar"));
+ QStringList failedFiles;
+ auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString());
+ QCOMPARE(dls.size(), 2);
+ QCOMPARE(failedFiles, {});
+ QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-32.jar"));
+ QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-64.jar"));
+ }
+ {
+ QStringList jar, native, native32, native64;
+ test.getApplicableFiles(Os_Windows, jar, native, native32, native64, QString());
+ QCOMPARE(jar, {});
+ QCOMPARE(native, {});
+ QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-windows-32.jar"));
+ QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-windows-64.jar"));
+ QStringList failedFiles;
+ auto dls = test.getDownloads(Os_Windows, cache.get(), failedFiles, QString());
+ QCOMPARE(dls.size(), 2);
+ QCOMPARE(failedFiles, {});
+ QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-32.jar"));
+ QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-64.jar"));
+ }
+ {
+ QStringList jar, native, native32, native64;
+ test.getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
+ QCOMPARE(jar, {});
+ QCOMPARE(native, {});
+ QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-osx-32.jar"));
+ QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-osx-64.jar"));
+ QStringList failedFiles;
+ auto dls = test.getDownloads(Os_OSX, cache.get(), failedFiles, QString());
+ QCOMPARE(dls.size(), 2);
+ QCOMPARE(failedFiles, {});
+ QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-32.jar"));
+ QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-64.jar"));
+ }
+ }
+ void test_legacy_native_arch_local_override()
+ {
+ Library test("test.package:testname:testversion");
+ test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}";
+ test.setHint("local");
+ QCOMPARE(test.isNative(), true);
+ test.setRepositoryURL("file://foo/bar");
+ {
+ QStringList jar, native, native32, native64;
+ test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString("data"));
+ QCOMPARE(jar, {});
+ QCOMPARE(native, {});
+ QCOMPARE(native32, {QFileInfo("data/testname-testversion-linux-32.jar").absoluteFilePath()});
+ QCOMPARE(native64, {QFileInfo("data/testname-testversion-linux-64.jar").absoluteFilePath()});
+ QStringList failedFiles;
+ auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
+ QCOMPARE(dls.size(), 0);
+ QCOMPARE(failedFiles, {"data/testname-testversion-linux-64.jar"});
+ }
+ }
+ void test_onenine()
+ {
+ auto test = readMojangJson("data/lib-simple.json");
+ {
+ QStringList jar, native, native32, native64;
+ test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
+ QCOMPARE(jar, getStorage("com/paulscode/codecwav/20101023/codecwav-20101023.jar"));
+ QCOMPARE(native, {});
+ QCOMPARE(native32, {});
+ QCOMPARE(native64, {});
+ }
+ {
+ QStringList failedFiles;
+ auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString());
+ QCOMPARE(dls.size(), 1);
+ QCOMPARE(failedFiles, {});
+ QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar"));
+ }
+ test->setHint("local");
+ {
+ QStringList jar, native, native32, native64;
+ test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data"));
+ QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()});
+ QCOMPARE(native, {});
+ QCOMPARE(native32, {});
+ QCOMPARE(native64, {});
+ }
+ {
+ QStringList failedFiles;
+ auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
+ QCOMPARE(dls.size(), 0);
+ QCOMPARE(failedFiles, {});
+ }
+ }
+ void test_onenine_local_override()
+ {
+ auto test = readMojangJson("data/lib-simple.json");
+ test->setHint("local");
+ {
+ QStringList jar, native, native32, native64;
+ test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data"));
+ QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()});
+ QCOMPARE(native, {});
+ QCOMPARE(native32, {});
+ QCOMPARE(native64, {});
+ }
+ {
+ QStringList failedFiles;
+ auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
+ QCOMPARE(dls.size(), 0);
+ QCOMPARE(failedFiles, {});
+ }
+ }
+ void test_onenine_native()
+ {
+ auto test = readMojangJson("data/lib-native.json");
+ QStringList jar, native, native32, native64;
+ test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
+ QCOMPARE(jar, QStringList());
+ QCOMPARE(native, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"));
+ QCOMPARE(native32, {});
+ QCOMPARE(native64, {});
+ QStringList failedFiles;
+ auto dls = test->getDownloads(Os_OSX, cache.get(), failedFiles, QString());
+ QCOMPARE(dls.size(), 1);
+ QCOMPARE(failedFiles, {});
+ QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"));
+ }
+ void test_onenine_native_arch()
+ {
+ auto test = readMojangJson("data/lib-native-arch.json");
+ QStringList jar, native, native32, native64;
+ test->getApplicableFiles(Os_Windows, jar, native, native32, native64, QString());
+ QCOMPARE(jar, {});
+ QCOMPARE(native, {});
+ QCOMPARE(native32, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"));
+ QCOMPARE(native64, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"));
+ QStringList failedFiles;
+ auto dls = test->getDownloads(Os_Windows, cache.get(), failedFiles, QString());
+ QCOMPARE(dls.size(), 2);
+ QCOMPARE(failedFiles, {});
+ QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"));
+ QCOMPARE(dls[1]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"));
+ }
private:
- std::unique_ptr<HttpMetaCache> cache;
- QString dataDir;
+ std::unique_ptr<HttpMetaCache> cache;
+ QString dataDir;
};
QTEST_GUILESS_MAIN(LibraryTest)
diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp
index 0fffc99f..28073edf 100644
--- a/api/logic/minecraft/MinecraftInstance.cpp
+++ b/api/logic/minecraft/MinecraftInstance.cpp
@@ -20,12 +20,14 @@
#include "minecraft/launch/DirectJavaLaunch.h"
#include "minecraft/launch/ModMinecraftJar.h"
#include "minecraft/launch/ClaimAccount.h"
+#include "minecraft/launch/ReconstructAssets.h"
+#include "minecraft/launch/ScanModFolders.h"
#include "java/launch/CheckJava.h"
#include "java/JavaUtils.h"
#include "meta/Index.h"
#include "meta/VersionList.h"
-#include "ModList.h"
+#include "mod/ModFolderModel.h"
#include "WorldList.h"
#include "icons/IIconList.h"
@@ -35,6 +37,7 @@
#include "AssetsUtils.h"
#include "MinecraftUpdate.h"
#include "MinecraftLoadAndCheck.h"
+#include <minecraft/gameoptions/GameOptions.h>
#define IBUS "@im=ibus"
@@ -42,909 +45,934 @@
// if either of the settings {a, b} is true, this also resolves to true
class OrSetting : public Setting
{
- Q_OBJECT
+ Q_OBJECT
public:
- OrSetting(QString id, std::shared_ptr<Setting> a, std::shared_ptr<Setting> b)
- :Setting({id}, false), m_a(a), m_b(b)
- {
- }
- virtual QVariant get() const
- {
- bool a = m_a->get().toBool();
- bool b = m_b->get().toBool();
- return a || b;
- }
- virtual void reset() {}
- virtual void set(QVariant value) {}
+ OrSetting(QString id, std::shared_ptr<Setting> a, std::shared_ptr<Setting> b)
+ :Setting({id}, false), m_a(a), m_b(b)
+ {
+ }
+ virtual QVariant get() const
+ {
+ bool a = m_a->get().toBool();
+ bool b = m_b->get().toBool();
+ return a || b;
+ }
+ virtual void reset() {}
+ virtual void set(QVariant value) {}
private:
- std::shared_ptr<Setting> m_a;
- std::shared_ptr<Setting> m_b;
+ std::shared_ptr<Setting> m_a;
+ std::shared_ptr<Setting> m_b;
};
MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
- : BaseInstance(globalSettings, settings, rootDir)
-{
- // Java Settings
- auto javaOverride = m_settings->registerSetting("OverrideJava", false);
- auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false);
- auto argsOverride = m_settings->registerSetting("OverrideJavaArgs", false);
-
- // combinations
- auto javaOrLocation = std::make_shared<OrSetting>("JavaOrLocationOverride", javaOverride, locationOverride);
- auto javaOrArgs = std::make_shared<OrSetting>("JavaOrArgsOverride", javaOverride, argsOverride);
-
- m_settings->registerOverride(globalSettings->getSetting("JavaPath"), javaOrLocation);
- m_settings->registerOverride(globalSettings->getSetting("JvmArgs"), javaOrArgs);
-
- // special!
- m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation);
- m_settings->registerPassthrough(globalSettings->getSetting("JavaVersion"), javaOrLocation);
- m_settings->registerPassthrough(globalSettings->getSetting("JavaArchitecture"), javaOrLocation);
-
- // Window Size
- auto windowSetting = m_settings->registerSetting("OverrideWindow", false);
- m_settings->registerOverride(globalSettings->getSetting("LaunchMaximized"), windowSetting);
- m_settings->registerOverride(globalSettings->getSetting("MinecraftWinWidth"), windowSetting);
- m_settings->registerOverride(globalSettings->getSetting("MinecraftWinHeight"), windowSetting);
-
- // Memory
- auto memorySetting = m_settings->registerSetting("OverrideMemory", false);
- m_settings->registerOverride(globalSettings->getSetting("MinMemAlloc"), memorySetting);
- m_settings->registerOverride(globalSettings->getSetting("MaxMemAlloc"), memorySetting);
- m_settings->registerOverride(globalSettings->getSetting("PermGen"), memorySetting);
-
- // Minecraft launch method
- auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
- m_settings->registerOverride(globalSettings->getSetting("MCLaunchMethod"), launchMethodOverride);
-
- // DEPRECATED: Read what versions the user configuration thinks should be used
- m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
- m_settings->registerSetting("LWJGLVersion", "");
- m_settings->registerSetting("ForgeVersion", "");
- m_settings->registerSetting("LiteloaderVersion", "");
-
- m_components.reset(new ComponentList(this));
- m_components->setOldConfigVersion("net.minecraft", m_settings->get("IntendedVersion").toString());
- auto setting = m_settings->getSetting("LWJGLVersion");
- m_components->setOldConfigVersion("org.lwjgl", m_settings->get("LWJGLVersion").toString());
- m_components->setOldConfigVersion("net.minecraftforge", m_settings->get("ForgeVersion").toString());
- m_components->setOldConfigVersion("com.mumfrey.liteloader", m_settings->get("LiteloaderVersion").toString());
-}
-
-void MinecraftInstance::init()
-{
+ : BaseInstance(globalSettings, settings, rootDir)
+{
+ // Java Settings
+ auto javaOverride = m_settings->registerSetting("OverrideJava", false);
+ auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false);
+ auto argsOverride = m_settings->registerSetting("OverrideJavaArgs", false);
+
+ // combinations
+ auto javaOrLocation = std::make_shared<OrSetting>("JavaOrLocationOverride", javaOverride, locationOverride);
+ auto javaOrArgs = std::make_shared<OrSetting>("JavaOrArgsOverride", javaOverride, argsOverride);
+
+ m_settings->registerOverride(globalSettings->getSetting("JavaPath"), javaOrLocation);
+ m_settings->registerOverride(globalSettings->getSetting("JvmArgs"), javaOrArgs);
+
+ // special!
+ m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation);
+ m_settings->registerPassthrough(globalSettings->getSetting("JavaVersion"), javaOrLocation);
+ m_settings->registerPassthrough(globalSettings->getSetting("JavaArchitecture"), javaOrLocation);
+
+ // Window Size
+ auto windowSetting = m_settings->registerSetting("OverrideWindow", false);
+ m_settings->registerOverride(globalSettings->getSetting("LaunchMaximized"), windowSetting);
+ m_settings->registerOverride(globalSettings->getSetting("MinecraftWinWidth"), windowSetting);
+ m_settings->registerOverride(globalSettings->getSetting("MinecraftWinHeight"), windowSetting);
+
+ // Memory
+ auto memorySetting = m_settings->registerSetting("OverrideMemory", false);
+ m_settings->registerOverride(globalSettings->getSetting("MinMemAlloc"), memorySetting);
+ m_settings->registerOverride(globalSettings->getSetting("MaxMemAlloc"), memorySetting);
+ m_settings->registerOverride(globalSettings->getSetting("PermGen"), memorySetting);
+
+ // Minecraft launch method
+ auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
+ m_settings->registerOverride(globalSettings->getSetting("MCLaunchMethod"), launchMethodOverride);
+
+ // DEPRECATED: Read what versions the user configuration thinks should be used
+ m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
+ m_settings->registerSetting("LWJGLVersion", "");
+ m_settings->registerSetting("ForgeVersion", "");
+ m_settings->registerSetting("LiteloaderVersion", "");
+
+ m_components.reset(new ComponentList(this));
+ m_components->setOldConfigVersion("net.minecraft", m_settings->get("IntendedVersion").toString());
+ auto setting = m_settings->getSetting("LWJGLVersion");
+ m_components->setOldConfigVersion("org.lwjgl", m_settings->get("LWJGLVersion").toString());
+ m_components->setOldConfigVersion("net.minecraftforge", m_settings->get("ForgeVersion").toString());
+ m_components->setOldConfigVersion("com.mumfrey.liteloader", m_settings->get("LiteloaderVersion").toString());
}
void MinecraftInstance::saveNow()
{
- m_components->saveNow();
+ m_components->saveNow();
}
QString MinecraftInstance::typeName() const
{
- return "Minecraft";
+ return "Minecraft";
}
std::shared_ptr<ComponentList> MinecraftInstance::getComponentList() const
{
- return m_components;
+ return m_components;
}
QSet<QString> MinecraftInstance::traits() const
{
- auto components = getComponentList();
- if (!components)
- {
- return {"version-incomplete"};
- }
- auto profile = components->getProfile();
- if (!profile)
- {
- return {"version-incomplete"};
- }
- return profile->getTraits();
+ auto components = getComponentList();
+ if (!components)
+ {
+ return {"version-incomplete"};
+ }
+ auto profile = components->getProfile();
+ if (!profile)
+ {
+ return {"version-incomplete"};
+ }
+ return profile->getTraits();
}
-QString MinecraftInstance::minecraftRoot() const
+QString MinecraftInstance::gameRoot() const
{
- QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
- QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
+ QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
+ QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
- if (mcDir.exists() && !dotMCDir.exists())
- return mcDir.filePath();
- else
- return dotMCDir.filePath();
+ if (mcDir.exists() && !dotMCDir.exists())
+ return mcDir.filePath();
+ else
+ return dotMCDir.filePath();
}
QString MinecraftInstance::binRoot() const
{
- return FS::PathCombine(minecraftRoot(), "bin");
+ return FS::PathCombine(gameRoot(), "bin");
}
QString MinecraftInstance::getNativePath() const
{
- QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/"));
- return natives_dir.absolutePath();
+ QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/"));
+ return natives_dir.absolutePath();
}
QString MinecraftInstance::getLocalLibraryPath() const
{
- QDir libraries_dir(FS::PathCombine(instanceRoot(), "libraries/"));
- return libraries_dir.absolutePath();
+ QDir libraries_dir(FS::PathCombine(instanceRoot(), "libraries/"));
+ return libraries_dir.absolutePath();
+}
+
+QString MinecraftInstance::jarModsDir() const
+{
+ QDir jarmods_dir(FS::PathCombine(instanceRoot(), "jarmods/"));
+ return jarmods_dir.absolutePath();
}
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
{
- return FS::PathCombine(minecraftRoot(), "coremods");
+ return FS::PathCombine(gameRoot(), "coremods");
}
QString MinecraftInstance::resourcePacksDir() const
{
- return FS::PathCombine(minecraftRoot(), "resourcepacks");
+ return FS::PathCombine(gameRoot(), "resourcepacks");
}
QString MinecraftInstance::texturePacksDir() const
{
- return FS::PathCombine(minecraftRoot(), "texturepacks");
+ return FS::PathCombine(gameRoot(), "texturepacks");
}
QString MinecraftInstance::instanceConfigFolder() const
{
- return FS::PathCombine(minecraftRoot(), "config");
+ return FS::PathCombine(gameRoot(), "config");
}
-QString MinecraftInstance::jarModsDir() const
+QString MinecraftInstance::libDir() const
{
- return FS::PathCombine(instanceRoot(), "jarmods");
+ return FS::PathCombine(gameRoot(), "lib");
}
-QString MinecraftInstance::libDir() const
+QString MinecraftInstance::worldDir() const
{
- return FS::PathCombine(minecraftRoot(), "lib");
+ return FS::PathCombine(gameRoot(), "saves");
}
-QString MinecraftInstance::worldDir() const
+QString MinecraftInstance::resourcesDir() const
{
- return FS::PathCombine(minecraftRoot(), "saves");
+ return FS::PathCombine(gameRoot(), "resources");
}
QDir MinecraftInstance::librariesPath() const
{
- return QDir::current().absoluteFilePath("libraries");
+ return QDir::current().absoluteFilePath("libraries");
}
QDir MinecraftInstance::jarmodsPath() const
{
- return QDir(jarModsDir());
+ return QDir(jarModsDir());
}
QDir MinecraftInstance::versionsPath() const
{
- return QDir::current().absoluteFilePath("versions");
+ return QDir::current().absoluteFilePath("versions");
}
QStringList MinecraftInstance::getClassPath() const
{
- QStringList jars, nativeJars;
- auto javaArchitecture = settings()->get("JavaArchitecture").toString();
- auto profile = m_components->getProfile();
- profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
- return jars;
+ QStringList jars, nativeJars;
+ auto javaArchitecture = settings()->get("JavaArchitecture").toString();
+ auto profile = m_components->getProfile();
+ profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
+ return jars;
}
QString MinecraftInstance::getMainClass() const
{
- auto profile = m_components->getProfile();
- return profile->getMainClass();
+ auto profile = m_components->getProfile();
+ return profile->getMainClass();
}
QStringList MinecraftInstance::getNativeJars() const
{
- QStringList jars, nativeJars;
- auto javaArchitecture = settings()->get("JavaArchitecture").toString();
- auto profile = m_components->getProfile();
- profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
- return nativeJars;
+ QStringList jars, nativeJars;
+ auto javaArchitecture = settings()->get("JavaArchitecture").toString();
+ auto profile = m_components->getProfile();
+ profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
+ return nativeJars;
}
QStringList MinecraftInstance::extraArguments() const
{
- auto list = BaseInstance::extraArguments();
- auto version = getComponentList();
- if (!version)
- return list;
- auto jarMods = getJarMods();
- if (!jarMods.isEmpty())
- {
- list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true",
- "-Dfml.ignorePatchDiscrepancies=true"});
- }
- return list;
+ auto list = BaseInstance::extraArguments();
+ auto version = getComponentList();
+ if (!version)
+ return list;
+ auto jarMods = getJarMods();
+ if (!jarMods.isEmpty())
+ {
+ list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true",
+ "-Dfml.ignorePatchDiscrepancies=true"});
+ }
+ return list;
}
QStringList MinecraftInstance::javaArguments() const
{
- QStringList args;
+ QStringList args;
- // custom args go first. we want to override them if we have our own here.
- args.append(extraArguments());
+ // custom args go first. we want to override them if we have our own here.
+ args.append(extraArguments());
- // OSX dock icon and name
+ // OSX dock icon and name
#ifdef Q_OS_MAC
- args << "-Xdock:icon=icon.png";
- args << QString("-Xdock:name=\"%1\"").arg(windowTitle());
+ args << "-Xdock:icon=icon.png";
+ args << QString("-Xdock:name=\"%1\"").arg(windowTitle());
#endif
- auto traits_ = traits();
- // HACK: fix issues on macOS with 1.13 snapshots
- // NOTE: Oracle Java option. if there are alternate jvm implementations, this would be the place to customize this for them
+ auto traits_ = traits();
+ // HACK: fix issues on macOS with 1.13 snapshots
+ // NOTE: Oracle Java option. if there are alternate jvm implementations, this would be the place to customize this for them
#ifdef Q_OS_MAC
- if(traits_.contains("FirstThreadOnMacOS"))
- {
- args << QString("-XstartOnFirstThread");
- }
+ if(traits_.contains("FirstThreadOnMacOS"))
+ {
+ args << QString("-XstartOnFirstThread");
+ }
#endif
- // HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767
+ // HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767
#ifdef Q_OS_WIN32
- args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
- "minecraft.exe.heapdump");
+ args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
+ "minecraft.exe.heapdump");
#endif
- int min = settings()->get("MinMemAlloc").toInt();
- int max = settings()->get("MaxMemAlloc").toInt();
- if(min < max)
- {
- args << QString("-Xms%1m").arg(min);
- args << QString("-Xmx%1m").arg(max);
- }
- else
- {
- args << QString("-Xms%1m").arg(max);
- args << QString("-Xmx%1m").arg(min);
- }
-
- // No PermGen in newer java.
- JavaVersion javaVersion = getJavaVersion();
- if(javaVersion.requiresPermGen())
- {
- auto permgen = settings()->get("PermGen").toInt();
- if (permgen != 64)
- {
- args << QString("-XX:PermSize=%1m").arg(permgen);
- }
- }
-
- args << "-Duser.language=en";
-
- return args;
+ int min = settings()->get("MinMemAlloc").toInt();
+ int max = settings()->get("MaxMemAlloc").toInt();
+ if(min < max)
+ {
+ args << QString("-Xms%1m").arg(min);
+ args << QString("-Xmx%1m").arg(max);
+ }
+ else
+ {
+ args << QString("-Xms%1m").arg(max);
+ args << QString("-Xmx%1m").arg(min);
+ }
+
+ // No PermGen in newer java.
+ JavaVersion javaVersion = getJavaVersion();
+ if(javaVersion.requiresPermGen())
+ {
+ auto permgen = settings()->get("PermGen").toInt();
+ if (permgen != 64)
+ {
+ args << QString("-XX:PermSize=%1m").arg(permgen);
+ }
+ }
+
+ args << "-Duser.language=en";
+
+ return args;
}
QMap<QString, QString> MinecraftInstance::getVariables() const
{
- QMap<QString, QString> out;
- out.insert("INST_NAME", name());
- out.insert("INST_ID", id());
- out.insert("INST_DIR", QDir(instanceRoot()).absolutePath());
- out.insert("INST_MC_DIR", QDir(minecraftRoot()).absolutePath());
- out.insert("INST_JAVA", settings()->get("JavaPath").toString());
- out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
- return out;
+ QMap<QString, QString> out;
+ out.insert("INST_NAME", name());
+ out.insert("INST_ID", id());
+ out.insert("INST_DIR", QDir(instanceRoot()).absolutePath());
+ out.insert("INST_MC_DIR", QDir(gameRoot()).absolutePath());
+ out.insert("INST_JAVA", settings()->get("JavaPath").toString());
+ out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
+ return out;
}
QProcessEnvironment MinecraftInstance::createEnvironment()
{
- // prepare the process environment
- QProcessEnvironment env = CleanEnviroment();
+ // prepare the process environment
+ QProcessEnvironment env = CleanEnviroment();
- // export some infos
- auto variables = getVariables();
- for (auto it = variables.begin(); it != variables.end(); ++it)
- {
- env.insert(it.key(), it.value());
- }
- return env;
+ // export some infos
+ auto variables = getVariables();
+ for (auto it = variables.begin(); it != variables.end(); ++it)
+ {
+ env.insert(it.key(), it.value());
+ }
+ return env;
}
static QString replaceTokensIn(QString text, QMap<QString, QString> with)
{
- QString result;
- QRegExp token_regexp("\\$\\{(.+)\\}");
- token_regexp.setMinimal(true);
- QStringList list;
- int tail = 0;
- int head = 0;
- while ((head = token_regexp.indexIn(text, head)) != -1)
- {
- result.append(text.mid(tail, head - tail));
- QString key = token_regexp.cap(1);
- auto iter = with.find(key);
- if (iter != with.end())
- {
- result.append(*iter);
- }
- head += token_regexp.matchedLength();
- tail = head;
- }
- result.append(text.mid(tail));
- return result;
+ QString result;
+ QRegExp token_regexp("\\$\\{(.+)\\}");
+ token_regexp.setMinimal(true);
+ QStringList list;
+ int tail = 0;
+ int head = 0;
+ while ((head = token_regexp.indexIn(text, head)) != -1)
+ {
+ result.append(text.mid(tail, head - tail));
+ QString key = token_regexp.cap(1);
+ auto iter = with.find(key);
+ if (iter != with.end())
+ {
+ result.append(*iter);
+ }
+ head += token_regexp.matchedLength();
+ tail = head;
+ }
+ result.append(text.mid(tail));
+ return result;
}
QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) const
{
- auto profile = m_components->getProfile();
- QString args_pattern = profile->getMinecraftArguments();
- for (auto tweaker : profile->getTweakers())
- {
- args_pattern += " --tweakClass " + tweaker;
- }
-
- QMap<QString, QString> token_mapping;
- // yggdrasil!
- if(session)
- {
- token_mapping["auth_username"] = session->username;
- token_mapping["auth_session"] = session->session;
- token_mapping["auth_access_token"] = session->access_token;
- token_mapping["auth_player_name"] = session->player_name;
- token_mapping["auth_uuid"] = session->uuid;
- token_mapping["user_properties"] = session->serializeUserProperties();
- token_mapping["user_type"] = session->user_type;
- }
-
- // blatant self-promotion.
- token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5";
-
- token_mapping["version_type"] = profile->getMinecraftVersionType();
-
- QString absRootDir = QDir(minecraftRoot()).absolutePath();
- token_mapping["game_directory"] = absRootDir;
- QString absAssetsDir = QDir("assets/").absolutePath();
- auto assets = profile->getMinecraftAssets();
- // FIXME: this is wrong and should be run as an async task
- token_mapping["game_assets"] = AssetsUtils::reconstructAssets(assets->id).absolutePath();
-
- // 1.7.3+ assets tokens
- token_mapping["assets_root"] = absAssetsDir;
- token_mapping["assets_index_name"] = assets->id;
-
- QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
- for (int i = 0; i < parts.length(); i++)
- {
- parts[i] = replaceTokensIn(parts[i], token_mapping);
- }
- return parts;
+ auto profile = m_components->getProfile();
+ QString args_pattern = profile->getMinecraftArguments();
+ for (auto tweaker : profile->getTweakers())
+ {
+ args_pattern += " --tweakClass " + tweaker;
+ }
+
+ QMap<QString, QString> token_mapping;
+ // yggdrasil!
+ if(session)
+ {
+ token_mapping["auth_username"] = session->username;
+ token_mapping["auth_session"] = session->session;
+ token_mapping["auth_access_token"] = session->access_token;
+ token_mapping["auth_player_name"] = session->player_name;
+ token_mapping["auth_uuid"] = session->uuid;
+ token_mapping["user_properties"] = session->serializeUserProperties();
+ token_mapping["user_type"] = session->user_type;
+ }
+
+ // blatant self-promotion.
+ token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5";
+
+ token_mapping["version_type"] = profile->getMinecraftVersionType();
+
+ QString absRootDir = QDir(gameRoot()).absolutePath();
+ token_mapping["game_directory"] = absRootDir;
+ QString absAssetsDir = QDir("assets/").absolutePath();
+ auto assets = profile->getMinecraftAssets();
+ token_mapping["game_assets"] = AssetsUtils::getAssetsDir(assets->id, resourcesDir()).absolutePath();
+
+ // 1.7.3+ assets tokens
+ token_mapping["assets_root"] = absAssetsDir;
+ token_mapping["assets_index_name"] = assets->id;
+
+ QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
+ for (int i = 0; i < parts.length(); i++)
+ {
+ parts[i] = replaceTokensIn(parts[i], token_mapping);
+ }
+ return parts;
}
QString MinecraftInstance::createLaunchScript(AuthSessionPtr session)
{
- QString launchScript;
-
- if (!m_components)
- return QString();
- auto profile = m_components->getProfile();
- if(!profile)
- return QString();
-
- auto mainClass = getMainClass();
- if (!mainClass.isEmpty())
- {
- launchScript += "mainClass " + mainClass + "\n";
- }
- auto appletClass = profile->getAppletClass();
- if (!appletClass.isEmpty())
- {
- launchScript += "appletClass " + appletClass + "\n";
- }
-
- // generic minecraft params
- for (auto param : processMinecraftArgs(session))
- {
- launchScript += "param " + param + "\n";
- }
-
- // window size, title and state, legacy
- {
- QString windowParams;
- if (settings()->get("LaunchMaximized").toBool())
- windowParams = "max";
- else
- windowParams = QString("%1x%2")
- .arg(settings()->get("MinecraftWinWidth").toInt())
- .arg(settings()->get("MinecraftWinHeight").toInt());
- launchScript += "windowTitle " + windowTitle() + "\n";
- launchScript += "windowParams " + windowParams + "\n";
- }
-
- // legacy auth
- if(session)
- {
- launchScript += "userName " + session->player_name + "\n";
- launchScript += "sessionId " + session->session + "\n";
- }
-
- // libraries and class path.
- {
- QStringList jars, nativeJars;
- auto javaArchitecture = settings()->get("JavaArchitecture").toString();
- profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
- for(auto file: jars)
- {
- launchScript += "cp " + file + "\n";
- }
- for(auto file: nativeJars)
- {
- launchScript += "ext " + file + "\n";
- }
- launchScript += "natives " + getNativePath() + "\n";
- }
-
- for (auto trait : profile->getTraits())
- {
- launchScript += "traits " + trait + "\n";
- }
- launchScript += "launcher onesix\n";
- // qDebug() << "Generated launch script:" << launchScript;
- return launchScript;
+ QString launchScript;
+
+ if (!m_components)
+ return QString();
+ auto profile = m_components->getProfile();
+ if(!profile)
+ return QString();
+
+ auto mainClass = getMainClass();
+ if (!mainClass.isEmpty())
+ {
+ launchScript += "mainClass " + mainClass + "\n";
+ }
+ auto appletClass = profile->getAppletClass();
+ if (!appletClass.isEmpty())
+ {
+ launchScript += "appletClass " + appletClass + "\n";
+ }
+
+ // generic minecraft params
+ for (auto param : processMinecraftArgs(session))
+ {
+ launchScript += "param " + param + "\n";
+ }
+
+ // window size, title and state, legacy
+ {
+ QString windowParams;
+ if (settings()->get("LaunchMaximized").toBool())
+ windowParams = "max";
+ else
+ windowParams = QString("%1x%2")
+ .arg(settings()->get("MinecraftWinWidth").toInt())
+ .arg(settings()->get("MinecraftWinHeight").toInt());
+ launchScript += "windowTitle " + windowTitle() + "\n";
+ launchScript += "windowParams " + windowParams + "\n";
+ }
+
+ // legacy auth
+ if(session)
+ {
+ launchScript += "userName " + session->player_name + "\n";
+ launchScript += "sessionId " + session->session + "\n";
+ }
+
+ // libraries and class path.
+ {
+ QStringList jars, nativeJars;
+ auto javaArchitecture = settings()->get("JavaArchitecture").toString();
+ profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
+ for(auto file: jars)
+ {
+ launchScript += "cp " + file + "\n";
+ }
+ for(auto file: nativeJars)
+ {
+ launchScript += "ext " + file + "\n";
+ }
+ launchScript += "natives " + getNativePath() + "\n";
+ }
+
+ for (auto trait : profile->getTraits())
+ {
+ launchScript += "traits " + trait + "\n";
+ }
+ launchScript += "launcher onesix\n";
+ // qDebug() << "Generated launch script:" << launchScript;
+ return launchScript;
}
QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session)
{
- QStringList out;
- out << "Main Class:" << " " + getMainClass() << "";
- out << "Native path:" << " " + getNativePath() << "";
-
- auto profile = m_components->getProfile();
-
- auto alltraits = traits();
- if(alltraits.size())
- {
- out << "Traits:";
- for (auto trait : alltraits)
- {
- out << "traits " + trait;
- }
- out << "";
- }
-
- // libraries and class path.
- {
- out << "Libraries:";
- QStringList jars, nativeJars;
- auto javaArchitecture = settings()->get("JavaArchitecture").toString();
- profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
- auto printLibFile = [&](const QString & path)
- {
- QFileInfo info(path);
- if(info.exists())
- {
- out << " " + path;
- }
- else
- {
- out << " " + path + " (missing)";
- }
- };
- for(auto file: jars)
- {
- printLibFile(file);
- }
- out << "";
- out << "Native libraries:";
- for(auto file: nativeJars)
- {
- printLibFile(file);
- }
- out << "";
- }
-
- if(loaderModList()->size())
- {
- out << "Mods:";
- for(auto & mod: loaderModList()->allMods())
- {
- if(!mod.enabled())
- continue;
- if(mod.type() == Mod::MOD_FOLDER)
- continue;
- // TODO: proper implementation would need to descend into folders.
-
- out << " " + mod.filename().completeBaseName();
- }
- out << "";
- }
-
- if(coreModList()->size())
- {
- out << "Core Mods:";
- for(auto & coremod: coreModList()->allMods())
- {
- if(!coremod.enabled())
- continue;
- if(coremod.type() == Mod::MOD_FOLDER)
- continue;
- // TODO: proper implementation would need to descend into folders.
-
- out << " " + coremod.filename().completeBaseName();
- }
- out << "";
- }
-
- auto & jarMods = profile->getJarMods();
- if(jarMods.size())
- {
- out << "Jar Mods:";
- for(auto & jarmod: jarMods)
- {
- auto displayname = jarmod->displayName(currentSystem);
- auto realname = jarmod->filename(currentSystem);
- if(displayname != realname)
- {
- out << " " + displayname + " (" + realname + ")";
- }
- else
- {
- out << " " + realname;
- }
- }
- out << "";
- }
-
- auto params = processMinecraftArgs(nullptr);
- out << "Params:";
- out << " " + params.join(' ');
- out << "";
-
- QString windowParams;
- if (settings()->get("LaunchMaximized").toBool())
- {
- out << "Window size: max (if available)";
- }
- else
- {
- auto width = settings()->get("MinecraftWinWidth").toInt();
- auto height = settings()->get("MinecraftWinHeight").toInt();
- out << "Window size: " + QString::number(width) + " x " + QString::number(height);
- }
- out << "";
- return out;
+ QStringList out;
+ out << "Main Class:" << " " + getMainClass() << "";
+ out << "Native path:" << " " + getNativePath() << "";
+
+ auto profile = m_components->getProfile();
+
+ auto alltraits = traits();
+ if(alltraits.size())
+ {
+ out << "Traits:";
+ for (auto trait : alltraits)
+ {
+ out << "traits " + trait;
+ }
+ out << "";
+ }
+
+ // libraries and class path.
+ {
+ out << "Libraries:";
+ QStringList jars, nativeJars;
+ auto javaArchitecture = settings()->get("JavaArchitecture").toString();
+ profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
+ auto printLibFile = [&](const QString & path)
+ {
+ QFileInfo info(path);
+ if(info.exists())
+ {
+ out << " " + path;
+ }
+ else
+ {
+ out << " " + path + " (missing)";
+ }
+ };
+ for(auto file: jars)
+ {
+ printLibFile(file);
+ }
+ out << "";
+ out << "Native libraries:";
+ for(auto file: nativeJars)
+ {
+ printLibFile(file);
+ }
+ out << "";
+ }
+
+ auto printModList = [&](const QString & label, ModFolderModel & model) {
+ if(model.size())
+ {
+ out << QString("%1:").arg(label);
+ auto modList = model.allMods();
+ std::sort(modList.begin(), modList.end(), [](Mod &a, Mod &b) {
+ auto aName = a.filename().completeBaseName();
+ auto bName = b.filename().completeBaseName();
+ return aName.localeAwareCompare(bName) < 0;
+ });
+ for(auto & mod: modList)
+ {
+ if(mod.type() == Mod::MOD_FOLDER)
+ {
+ out << u8" [📁] " + mod.filename().completeBaseName() + " (folder)";
+ continue;
+ }
+
+ if(mod.enabled()) {
+ out << u8" [✔️] " + mod.filename().completeBaseName();
+ }
+ else {
+ out << u8" [❌] " + mod.filename().completeBaseName() + " (disabled)";
+ }
+
+ }
+ out << "";
+ }
+ };
+
+ printModList("Mods", *(loaderModList().get()));
+ printModList("Core Mods", *(coreModList().get()));
+
+ auto & jarMods = profile->getJarMods();
+ if(jarMods.size())
+ {
+ out << "Jar Mods:";
+ for(auto & jarmod: jarMods)
+ {
+ auto displayname = jarmod->displayName(currentSystem);
+ auto realname = jarmod->filename(currentSystem);
+ if(displayname != realname)
+ {
+ out << " " + displayname + " (" + realname + ")";
+ }
+ else
+ {
+ out << " " + realname;
+ }
+ }
+ out << "";
+ }
+
+ auto params = processMinecraftArgs(nullptr);
+ out << "Params:";
+ out << " " + params.join(' ');
+ out << "";
+
+ QString windowParams;
+ if (settings()->get("LaunchMaximized").toBool())
+ {
+ out << "Window size: max (if available)";
+ }
+ else
+ {
+ auto width = settings()->get("MinecraftWinWidth").toInt();
+ auto height = settings()->get("MinecraftWinHeight").toInt();
+ out << "Window size: " + QString::number(width) + " x " + QString::number(height);
+ }
+ out << "";
+ return out;
}
QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSessionPtr session)
{
- if(!session)
- {
- return QMap<QString, QString>();
- }
- auto & sessionRef = *session.get();
- QMap<QString, QString> filter;
- auto addToFilter = [&filter](QString key, QString value)
- {
- if(key.trimmed().size())
- {
- filter[key] = value;
- }
- };
- if (sessionRef.session != "-")
- {
- addToFilter(sessionRef.session, tr("<SESSION ID>"));
- }
- addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
- addToFilter(sessionRef.client_token, tr("<CLIENT TOKEN>"));
- addToFilter(sessionRef.uuid, tr("<PROFILE ID>"));
-
- auto i = sessionRef.u.properties.begin();
- while (i != sessionRef.u.properties.end())
- {
- if(i.key() == "preferredLanguage")
- {
- ++i;
- continue;
- }
- addToFilter(i.value(), "<" + i.key().toUpper() + ">");
- ++i;
- }
- return filter;
+ if(!session)
+ {
+ return QMap<QString, QString>();
+ }
+ auto & sessionRef = *session.get();
+ QMap<QString, QString> filter;
+ auto addToFilter = [&filter](QString key, QString value)
+ {
+ if(key.trimmed().size())
+ {
+ filter[key] = value;
+ }
+ };
+ if (sessionRef.session != "-")
+ {
+ addToFilter(sessionRef.session, tr("<SESSION ID>"));
+ }
+ addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
+ addToFilter(sessionRef.client_token, tr("<CLIENT TOKEN>"));
+ addToFilter(sessionRef.uuid, tr("<PROFILE ID>"));
+
+ auto i = sessionRef.u.properties.begin();
+ while (i != sessionRef.u.properties.end())
+ {
+ if(i.value().length() <= 3) {
+ ++i;
+ continue;
+ }
+ addToFilter(i.value(), "<" + i.key().toUpper() + ">");
+ ++i;
+ }
+ return filter;
}
MessageLevel::Enum MinecraftInstance::guessLevel(const QString &line, MessageLevel::Enum level)
{
- QRegularExpression re("\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\]");
- auto match = re.match(line);
- if(match.hasMatch())
- {
- // New style logs from log4j
- QString timestamp = match.captured("timestamp");
- QString levelStr = match.captured("level");
- if(levelStr == "INFO")
- level = MessageLevel::Message;
- if(levelStr == "WARN")
- level = MessageLevel::Warning;
- if(levelStr == "ERROR")
- level = MessageLevel::Error;
- if(levelStr == "FATAL")
- level = MessageLevel::Fatal;
- if(levelStr == "TRACE" || levelStr == "DEBUG")
- level = MessageLevel::Debug;
- }
- else
- {
- // Old style forge logs
- if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") ||
- line.contains("[FINER]") || line.contains("[FINEST]"))
- level = MessageLevel::Message;
- if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
- level = MessageLevel::Error;
- if (line.contains("[WARNING]"))
- level = MessageLevel::Warning;
- if (line.contains("[DEBUG]"))
- level = MessageLevel::Debug;
- }
- if (line.contains("overwriting existing"))
- return MessageLevel::Fatal;
- //NOTE: this diverges from the real regexp. no unicode, the first section is + instead of *
- static const QString javaSymbol = "([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$][a-zA-Z\\d_$]*";
- if (line.contains("Exception in thread")
- || line.contains(QRegularExpression("\\s+at " + javaSymbol))
- || line.contains(QRegularExpression("Caused by: " + javaSymbol))
- || line.contains(QRegularExpression("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$]?[a-zA-Z\\d_$]*(Exception|Error|Throwable)"))
- || line.contains(QRegularExpression("... \\d+ more$"))
- )
- return MessageLevel::Error;
- return level;
+ QRegularExpression re("\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\]");
+ auto match = re.match(line);
+ if(match.hasMatch())
+ {
+ // New style logs from log4j
+ QString timestamp = match.captured("timestamp");
+ QString levelStr = match.captured("level");
+ if(levelStr == "INFO")
+ level = MessageLevel::Message;
+ if(levelStr == "WARN")
+ level = MessageLevel::Warning;
+ if(levelStr == "ERROR")
+ level = MessageLevel::Error;
+ if(levelStr == "FATAL")
+ level = MessageLevel::Fatal;
+ if(levelStr == "TRACE" || levelStr == "DEBUG")
+ level = MessageLevel::Debug;
+ }
+ else
+ {
+ // Old style forge logs
+ if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") ||
+ line.contains("[FINER]") || line.contains("[FINEST]"))
+ level = MessageLevel::Message;
+ if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
+ level = MessageLevel::Error;
+ if (line.contains("[WARNING]"))
+ level = MessageLevel::Warning;
+ if (line.contains("[DEBUG]"))
+ level = MessageLevel::Debug;
+ }
+ if (line.contains("overwriting existing"))
+ return MessageLevel::Fatal;
+ //NOTE: this diverges from the real regexp. no unicode, the first section is + instead of *
+ static const QString javaSymbol = "([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$][a-zA-Z\\d_$]*";
+ if (line.contains("Exception in thread")
+ || line.contains(QRegularExpression("\\s+at " + javaSymbol))
+ || line.contains(QRegularExpression("Caused by: " + javaSymbol))
+ || line.contains(QRegularExpression("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$]?[a-zA-Z\\d_$]*(Exception|Error|Throwable)"))
+ || line.contains(QRegularExpression("... \\d+ more$"))
+ )
+ return MessageLevel::Error;
+ return level;
}
IPathMatcher::Ptr MinecraftInstance::getLogFileMatcher()
{
- auto combined = std::make_shared<MultiMatcher>();
- combined->add(std::make_shared<RegexpMatcher>(".*\\.log(\\.[0-9]*)?(\\.gz)?$"));
- combined->add(std::make_shared<RegexpMatcher>("crash-.*\\.txt"));
- combined->add(std::make_shared<RegexpMatcher>("IDMap dump.*\\.txt$"));
- combined->add(std::make_shared<RegexpMatcher>("ModLoader\\.txt(\\..*)?$"));
- return combined;
+ auto combined = std::make_shared<MultiMatcher>();
+ combined->add(std::make_shared<RegexpMatcher>(".*\\.log(\\.[0-9]*)?(\\.gz)?$"));
+ combined->add(std::make_shared<RegexpMatcher>("crash-.*\\.txt"));
+ combined->add(std::make_shared<RegexpMatcher>("IDMap dump.*\\.txt$"));
+ combined->add(std::make_shared<RegexpMatcher>("ModLoader\\.txt(\\..*)?$"));
+ return combined;
}
QString MinecraftInstance::getLogFileRoot()
{
- return minecraftRoot();
+ return gameRoot();
}
QString MinecraftInstance::prettifyTimeDuration(int64_t duration)
{
- int seconds = (int) (duration % 60);
- duration /= 60;
- int minutes = (int) (duration % 60);
- duration /= 60;
- int hours = (int) (duration % 24);
- int days = (int) (duration / 24);
- if((hours == 0)&&(days == 0))
- {
- return tr("%1m %2s").arg(minutes).arg(seconds);
- }
- if (days == 0)
- {
- return tr("%1h %2m").arg(hours).arg(minutes);
- }
- return tr("%1d %2h %3m").arg(days).arg(hours).arg(minutes);
+ int seconds = (int) (duration % 60);
+ duration /= 60;
+ int minutes = (int) (duration % 60);
+ duration /= 60;
+ int hours = (int) (duration % 24);
+ int days = (int) (duration / 24);
+ if((hours == 0)&&(days == 0))
+ {
+ return tr("%1m %2s").arg(minutes).arg(seconds);
+ }
+ if (days == 0)
+ {
+ return tr("%1h %2m").arg(hours).arg(minutes);
+ }
+ return tr("%1d %2h %3m").arg(days).arg(hours).arg(minutes);
}
QString MinecraftInstance::getStatusbarDescription()
{
- QStringList traits;
- if (hasVersionBroken())
- {
- traits.append(tr("broken"));
- }
-
- QString description;
- description.append(tr("Minecraft %1 (%2)").arg(m_components->getComponentVersion("net.minecraft")).arg(typeName()));
- if(totalTimePlayed() > 0)
- {
- description.append(tr(", played for %1").arg(prettifyTimeDuration(totalTimePlayed())));
- }
- if(hasCrashed())
- {
- description.append(tr(", has crashed."));
- }
- return description;
+ QStringList traits;
+ if (hasVersionBroken())
+ {
+ traits.append(tr("broken"));
+ }
+
+ QString description;
+ description.append(tr("Minecraft %1 (%2)").arg(m_components->getComponentVersion("net.minecraft")).arg(typeName()));
+ if(totalTimePlayed() > 0)
+ {
+ description.append(tr(", played for %1").arg(prettifyTimeDuration(totalTimePlayed())));
+ }
+ if(hasCrashed())
+ {
+ description.append(tr(", has crashed."));
+ }
+ return description;
}
shared_qobject_ptr<Task> MinecraftInstance::createUpdateTask(Net::Mode mode)
{
- switch (mode)
- {
- case Net::Mode::Offline:
- {
- return shared_qobject_ptr<Task>(new MinecraftLoadAndCheck(this));
- }
- case Net::Mode::Online:
- {
- return shared_qobject_ptr<Task>(new OneSixUpdate(this));
- }
- }
- return nullptr;
-}
-
-std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session)
-{
- auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr()));
- auto pptr = process.get();
-
- ENV.icons()->saveIcon(iconKey(), FS::PathCombine(minecraftRoot(), "icon.png"), "PNG");
-
- // print a header
- {
- process->appendStep(std::make_shared<TextPrint>(pptr, "Minecraft folder is:\n" + minecraftRoot() + "\n\n", MessageLevel::MultiMC));
- }
-
- // check java
- {
- auto step = std::make_shared<CheckJava>(pptr);
- process->appendStep(step);
- }
-
- // check launch method
- QStringList validMethods = {"LauncherPart", "DirectJava"};
- QString method = launchMethod();
- if(!validMethods.contains(method))
- {
- process->appendStep(std::make_shared<TextPrint>(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal));
- return process;
- }
-
- // run pre-launch command if that's needed
- if(getPreLaunchCommand().size())
- {
- auto step = std::make_shared<PreLaunchCommand>(pptr);
- step->setWorkingDirectory(minecraftRoot());
- process->appendStep(step);
- }
-
- // if we aren't in offline mode,.
- if(session->status != AuthSession::PlayableOffline)
- {
- process->appendStep(std::make_shared<ClaimAccount>(pptr, session));
- process->appendStep(std::make_shared<Update>(pptr, Net::Mode::Online));
- }
- else
- {
- process->appendStep(std::make_shared<Update>(pptr, Net::Mode::Offline));
- }
-
- // if there are any jar mods
- {
- auto step = std::make_shared<ModMinecraftJar>(pptr);
- process->appendStep(step);
- }
-
- // print some instance info here...
- {
- auto step = std::make_shared<PrintInstanceInfo>(pptr, session);
- process->appendStep(step);
- }
-
- // create the server-resource-packs folder (workaround for Minecraft bug MCL-3732)
- {
- auto step = std::make_shared<CreateServerResourcePacksFolder>(pptr);
- process->appendStep(step);
- }
-
- // extract native jars if needed
- {
- auto step = std::make_shared<ExtractNatives>(pptr);
- process->appendStep(step);
- }
-
- {
- // actually launch the game
- auto method = launchMethod();
- if(method == "LauncherPart")
- {
- auto step = std::make_shared<LauncherPartLaunch>(pptr);
- step->setWorkingDirectory(minecraftRoot());
- step->setAuthSession(session);
- process->appendStep(step);
- }
- else if (method == "DirectJava")
- {
- auto step = std::make_shared<DirectJavaLaunch>(pptr);
- step->setWorkingDirectory(minecraftRoot());
- step->setAuthSession(session);
- process->appendStep(step);
- }
- }
-
- // run post-exit command if that's needed
- if(getPostExitCommand().size())
- {
- auto step = std::make_shared<PostLaunchCommand>(pptr);
- step->setWorkingDirectory(minecraftRoot());
- process->appendStep(step);
- }
- if (session)
- {
- process->setCensorFilter(createCensorFilterFromSession(session));
- }
- m_launchProcess = process;
- emit launchTaskChanged(m_launchProcess);
- return m_launchProcess;
+ switch (mode)
+ {
+ case Net::Mode::Offline:
+ {
+ return shared_qobject_ptr<Task>(new MinecraftLoadAndCheck(this));
+ }
+ case Net::Mode::Online:
+ {
+ return shared_qobject_ptr<Task>(new MinecraftUpdate(this));
+ }
+ }
+ return nullptr;
+}
+
+shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session)
+{
+ // FIXME: get rid of shared_from_this ...
+ auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(shared_from_this()));
+ auto pptr = process.get();
+
+ ENV.icons()->saveIcon(iconKey(), FS::PathCombine(gameRoot(), "icon.png"), "PNG");
+
+ // print a header
+ {
+ process->appendStep(new TextPrint(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::MultiMC));
+ }
+
+ // check java
+ {
+ process->appendStep(new CheckJava(pptr));
+ }
+
+ // check launch method
+ QStringList validMethods = {"LauncherPart", "DirectJava"};
+ QString method = launchMethod();
+ if(!validMethods.contains(method))
+ {
+ process->appendStep(new TextPrint(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal));
+ return process;
+ }
+
+ // run pre-launch command if that's needed
+ if(getPreLaunchCommand().size())
+ {
+ auto step = new PreLaunchCommand(pptr);
+ step->setWorkingDirectory(gameRoot());
+ process->appendStep(step);
+ }
+
+ // if we aren't in offline mode,.
+ if(session->status != AuthSession::PlayableOffline)
+ {
+ process->appendStep(new ClaimAccount(pptr, session));
+ process->appendStep(new Update(pptr, Net::Mode::Online));
+ }
+ else
+ {
+ process->appendStep(new Update(pptr, Net::Mode::Offline));
+ }
+
+ // if there are any jar mods
+ {
+ process->appendStep(new ModMinecraftJar(pptr));
+ }
+
+ // if there are any jar mods
+ {
+ process->appendStep(new ScanModFolders(pptr));
+ }
+
+ // print some instance info here...
+ {
+ process->appendStep(new PrintInstanceInfo(pptr, session));
+ }
+
+ // create the server-resource-packs folder (workaround for Minecraft bug MCL-3732)
+ {
+ process->appendStep(new CreateServerResourcePacksFolder(pptr));
+ }
+
+ // extract native jars if needed
+ {
+ process->appendStep(new ExtractNatives(pptr));
+ }
+
+ // reconstruct assets if needed
+ {
+ process->appendStep(new ReconstructAssets(pptr));
+ }
+
+ {
+ // actually launch the game
+ auto method = launchMethod();
+ if(method == "LauncherPart")
+ {
+ auto step = new LauncherPartLaunch(pptr);
+ step->setWorkingDirectory(gameRoot());
+ step->setAuthSession(session);
+ process->appendStep(step);
+ }
+ else if (method == "DirectJava")
+ {
+ auto step = new DirectJavaLaunch(pptr);
+ step->setWorkingDirectory(gameRoot());
+ step->setAuthSession(session);
+ process->appendStep(step);
+ }
+ }
+
+ // run post-exit command if that's needed
+ if(getPostExitCommand().size())
+ {
+ auto step = new PostLaunchCommand(pptr);
+ step->setWorkingDirectory(gameRoot());
+ process->appendStep(step);
+ }
+ if (session)
+ {
+ process->setCensorFilter(createCensorFilterFromSession(session));
+ }
+ m_launchProcess = process;
+ emit launchTaskChanged(m_launchProcess);
+ return m_launchProcess;
}
QString MinecraftInstance::launchMethod()
{
- return m_settings->get("MCLaunchMethod").toString();
+ return m_settings->get("MCLaunchMethod").toString();
}
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<ModFolderModel> MinecraftInstance::loaderModList() const
{
- if (!m_loader_mod_list)
- {
- m_loader_mod_list.reset(new ModList(loaderModsDir()));
- }
- m_loader_mod_list->update();
- return m_loader_mod_list;
+ if (!m_loader_mod_list)
+ {
+ m_loader_mod_list.reset(new ModFolderModel(loaderModsDir()));
+ m_loader_mod_list->disableInteraction(isRunning());
+ connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction);
+ }
+ return m_loader_mod_list;
}
-std::shared_ptr<ModList> MinecraftInstance::coreModList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList() const
{
- if (!m_core_mod_list)
- {
- m_core_mod_list.reset(new ModList(coreModsDir()));
- }
- m_core_mod_list->update();
- return m_core_mod_list;
+ if (!m_core_mod_list)
+ {
+ m_core_mod_list.reset(new ModFolderModel(coreModsDir()));
+ m_core_mod_list->disableInteraction(isRunning());
+ connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction);
+ }
+ return m_core_mod_list;
}
-std::shared_ptr<ModList> MinecraftInstance::resourcePackList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::resourcePackList() const
{
- if (!m_resource_pack_list)
- {
- m_resource_pack_list.reset(new ModList(resourcePacksDir()));
- }
- m_resource_pack_list->update();
- return m_resource_pack_list;
+ if (!m_resource_pack_list)
+ {
+ m_resource_pack_list.reset(new ModFolderModel(resourcePacksDir()));
+ m_resource_pack_list->disableInteraction(isRunning());
+ connect(this, &BaseInstance::runningStatusChanged, m_resource_pack_list.get(), &ModFolderModel::disableInteraction);
+ }
+ return m_resource_pack_list;
}
-std::shared_ptr<ModList> MinecraftInstance::texturePackList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::texturePackList() const
{
- if (!m_texture_pack_list)
- {
- m_texture_pack_list.reset(new ModList(texturePacksDir()));
- }
- m_texture_pack_list->update();
- return m_texture_pack_list;
+ if (!m_texture_pack_list)
+ {
+ m_texture_pack_list.reset(new ModFolderModel(texturePacksDir()));
+ m_texture_pack_list->disableInteraction(isRunning());
+ connect(this, &BaseInstance::runningStatusChanged, m_texture_pack_list.get(), &ModFolderModel::disableInteraction);
+ }
+ return m_texture_pack_list;
}
std::shared_ptr<WorldList> MinecraftInstance::worldList() const
{
- if (!m_world_list)
- {
- m_world_list.reset(new WorldList(worldDir()));
- }
- return m_world_list;
+ if (!m_world_list)
+ {
+ m_world_list.reset(new WorldList(worldDir()));
+ }
+ 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
{
- auto profile = m_components->getProfile();
- QList<Mod> mods;
- for (auto jarmod : profile->getJarMods())
- {
- QStringList jar, temp1, temp2, temp3;
- jarmod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, jarmodsPath().absolutePath());
- // QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem));
- mods.push_back(Mod(QFileInfo(jar[0])));
- }
- return mods;
+ auto profile = m_components->getProfile();
+ QList<Mod> mods;
+ for (auto jarmod : profile->getJarMods())
+ {
+ QStringList jar, temp1, temp2, temp3;
+ jarmod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, jarmodsPath().absolutePath());
+ // QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem));
+ mods.push_back(Mod(QFileInfo(jar[0])));
+ }
+ return mods;
}
diff --git a/api/logic/minecraft/MinecraftInstance.h b/api/logic/minecraft/MinecraftInstance.h
index 446a39d5..a8f64109 100644
--- a/api/logic/minecraft/MinecraftInstance.h
+++ b/api/logic/minecraft/MinecraftInstance.h
@@ -1,124 +1,132 @@
#pragma once
#include "BaseInstance.h"
#include <java/JavaVersion.h>
-#include "minecraft/Mod.h"
+#include "minecraft/mod/Mod.h"
#include <QProcess>
#include <QDir>
#include "multimc_logic_export.h"
-class ModList;
+class ModFolderModel;
class WorldList;
+class GameOptions;
class LaunchStep;
class ComponentList;
class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance
{
- Q_OBJECT
+ Q_OBJECT
public:
- MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
- virtual ~MinecraftInstance() {};
- virtual void init() override;
- virtual void saveNow();
-
- // FIXME: remove
- QString typeName() const override;
- // FIXME: remove
- QSet<QString> traits() const override;
-
- bool canEdit() const override
- {
- return true;
- }
-
- bool canExport() const override
- {
- return true;
- }
-
- ////// Directories and files //////
- QString jarModsDir() const;
- QString resourcePacksDir() const;
- QString texturePacksDir() const;
- QString loaderModsDir() const;
- QString coreModsDir() const;
- QString libDir() const;
- QString worldDir() const;
- QDir jarmodsPath() const;
- QDir librariesPath() const;
- QDir versionsPath() const;
- 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.
- QString getNativePath() const; // where to put the natives during/before launch
- QString getLocalLibraryPath() const; // where the instance-local libraries should be
-
-
- ////// Profile management //////
- std::shared_ptr<ComponentList> getComponentList() const;
-
- ////// Mod Lists //////
- std::shared_ptr<ModList> loaderModList() const;
- std::shared_ptr<ModList> coreModList() const;
- std::shared_ptr<ModList> resourcePackList() const;
- std::shared_ptr<ModList> texturePackList() const;
- std::shared_ptr<WorldList> worldList() const;
-
-
- ////// Launch stuff //////
- shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
- std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override;
- QStringList extraArguments() const override;
- QStringList verboseDescription(AuthSessionPtr session) override;
- QList<Mod> getJarMods() const;
- QString createLaunchScript(AuthSessionPtr session);
- /// get arguments passed to java
- QStringList javaArguments() const;
-
- /// get variables for launch command variable substitution/environment
- QMap<QString, QString> getVariables() const override;
-
- /// create an environment for launching processes
- QProcessEnvironment createEnvironment() override;
-
- /// guess log level from a line of minecraft log
- MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) override;
-
- IPathMatcher::Ptr getLogFileMatcher() override;
-
- QString getLogFileRoot() override;
-
- QString getStatusbarDescription() override;
-
- // FIXME: remove
- virtual QStringList getClassPath() const;
- // FIXME: remove
- virtual QStringList getNativeJars() const;
- // FIXME: remove
- virtual QString getMainClass() const;
-
- // FIXME: remove
- virtual QStringList processMinecraftArgs(AuthSessionPtr account) const;
-
- virtual JavaVersion getJavaVersion() const;
-
-signals:
- void versionReloaded();
+ MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
+ virtual ~MinecraftInstance() {};
+ virtual void saveNow() override;
+
+ // FIXME: remove
+ QString typeName() const override;
+ // FIXME: remove
+ QSet<QString> traits() const override;
+
+ bool canEdit() const override
+ {
+ return true;
+ }
+
+ bool canExport() const override
+ {
+ return true;
+ }
+
+ ////// Directories and files //////
+ QString jarModsDir() const;
+ QString resourcePacksDir() const;
+ QString texturePacksDir() const;
+ QString loaderModsDir() const;
+ QString coreModsDir() const;
+ QString modsCacheLocation() const;
+ QString libDir() const;
+ QString worldDir() const;
+ QString resourcesDir() const;
+ QDir jarmodsPath() const;
+ QDir librariesPath() const;
+ QDir versionsPath() const;
+ QString instanceConfigFolder() const override;
+
+ // Path to the instance's minecraft directory.
+ QString gameRoot() const override;
+
+ // 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 //////
+ std::shared_ptr<ComponentList> getComponentList() const;
+
+ ////// Mod Lists //////
+ std::shared_ptr<ModFolderModel> loaderModList() const;
+ std::shared_ptr<ModFolderModel> coreModList() const;
+ std::shared_ptr<ModFolderModel> resourcePackList() const;
+ std::shared_ptr<ModFolderModel> texturePackList() const;
+ std::shared_ptr<WorldList> worldList() const;
+ std::shared_ptr<GameOptions> gameOptionsModel() const;
+
+ ////// Launch stuff //////
+ shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
+ shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override;
+ QStringList extraArguments() const override;
+ QStringList verboseDescription(AuthSessionPtr session) override;
+ QList<Mod> getJarMods() const;
+ QString createLaunchScript(AuthSessionPtr session);
+ /// get arguments passed to java
+ QStringList javaArguments() const;
+
+ /// get variables for launch command variable substitution/environment
+ QMap<QString, QString> getVariables() const override;
+
+ /// create an environment for launching processes
+ QProcessEnvironment createEnvironment() override;
+
+ /// guess log level from a line of minecraft log
+ MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) override;
+
+ IPathMatcher::Ptr getLogFileMatcher() override;
+
+ QString getLogFileRoot() override;
+
+ QString getStatusbarDescription() override;
+
+ // FIXME: remove
+ virtual QStringList getClassPath() const;
+ // FIXME: remove
+ virtual QStringList getNativeJars() const;
+ // FIXME: remove
+ virtual QString getMainClass() const;
+
+ // FIXME: remove
+ virtual QStringList processMinecraftArgs(AuthSessionPtr account) const;
+
+ virtual JavaVersion getJavaVersion() const;
protected:
- QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
- QStringList validLaunchMethods();
- QString launchMethod();
+ QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
+ QStringList validLaunchMethods();
+ QString launchMethod();
private:
- QString prettifyTimeDuration(int64_t duration);
+ QString prettifyTimeDuration(int64_t duration);
protected: // data
- std::shared_ptr<ComponentList> m_components;
- mutable std::shared_ptr<ModList> m_loader_mod_list;
- mutable std::shared_ptr<ModList> m_core_mod_list;
- mutable std::shared_ptr<ModList> m_resource_pack_list;
- mutable std::shared_ptr<ModList> m_texture_pack_list;
- mutable std::shared_ptr<WorldList> m_world_list;
+ std::shared_ptr<ComponentList> m_components;
+ mutable std::shared_ptr<ModFolderModel> m_loader_mod_list;
+ mutable std::shared_ptr<ModFolderModel> m_core_mod_list;
+ mutable std::shared_ptr<ModFolderModel> m_resource_pack_list;
+ mutable std::shared_ptr<ModFolderModel> m_texture_pack_list;
+ mutable std::shared_ptr<WorldList> m_world_list;
+ mutable std::shared_ptr<GameOptions> m_game_options;
};
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr;
diff --git a/api/logic/minecraft/MinecraftLoadAndCheck.cpp b/api/logic/minecraft/MinecraftLoadAndCheck.cpp
index c64bbddf..593e2fc4 100644
--- a/api/logic/minecraft/MinecraftLoadAndCheck.cpp
+++ b/api/logic/minecraft/MinecraftLoadAndCheck.cpp
@@ -8,38 +8,38 @@ MinecraftLoadAndCheck::MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *p
void MinecraftLoadAndCheck::executeTask()
{
- // add offline metadata load task
- auto components = m_inst->getComponentList();
- components->reload(Net::Mode::Offline);
- m_task = components->getCurrentTask();
+ // add offline metadata load task
+ auto components = m_inst->getComponentList();
+ components->reload(Net::Mode::Offline);
+ m_task = components->getCurrentTask();
- if(!m_task)
- {
- emitSucceeded();
- return;
- }
- connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::subtaskSucceeded);
- connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed);
- connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress);
- connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus);
+ if(!m_task)
+ {
+ emitSucceeded();
+ return;
+ }
+ connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::subtaskSucceeded);
+ connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed);
+ connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress);
+ connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus);
}
void MinecraftLoadAndCheck::subtaskSucceeded()
{
- if(isFinished())
- {
- qCritical() << "OneSixUpdate: Subtask" << sender() << "succeeded, but work was already done!";
- return;
- }
- emitSucceeded();
+ if(isFinished())
+ {
+ qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
+ return;
+ }
+ emitSucceeded();
}
void MinecraftLoadAndCheck::subtaskFailed(QString error)
{
- if(isFinished())
- {
- qCritical() << "OneSixUpdate: Subtask" << sender() << "failed, but work was already done!";
- return;
- }
- emitFailed(error);
+ if(isFinished())
+ {
+ qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
+ return;
+ }
+ emitFailed(error);
}
diff --git a/api/logic/minecraft/MinecraftLoadAndCheck.h b/api/logic/minecraft/MinecraftLoadAndCheck.h
index 00515f2d..6924ca2d 100644
--- a/api/logic/minecraft/MinecraftLoadAndCheck.h
+++ b/api/logic/minecraft/MinecraftLoadAndCheck.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,19 +29,20 @@ class MinecraftInstance;
class MinecraftLoadAndCheck : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent = 0);
- void executeTask() override;
+ explicit MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent = 0);
+ virtual ~MinecraftLoadAndCheck() {};
+ void executeTask() override;
private slots:
- void subtaskSucceeded();
- void subtaskFailed(QString error);
+ void subtaskSucceeded();
+ void subtaskFailed(QString error);
private:
- MinecraftInstance *m_inst = nullptr;
- shared_qobject_ptr<Task> m_task;
- QString m_preFailure;
- QString m_fail_reason;
+ MinecraftInstance *m_inst = nullptr;
+ shared_qobject_ptr<Task> m_task;
+ QString m_preFailure;
+ QString m_fail_reason;
};
diff --git a/api/logic/minecraft/MinecraftUpdate.cpp b/api/logic/minecraft/MinecraftUpdate.cpp
index 86835fa4..00558e37 100644
--- a/api/logic/minecraft/MinecraftUpdate.cpp
+++ b/api/logic/minecraft/MinecraftUpdate.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,148 +37,148 @@
#include <meta/Index.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();
- // create folders
- {
- m_tasks.append(std::make_shared<FoldersTask>(m_inst));
- }
-
- // add metadata update task if necessary
- {
- auto components = m_inst->getComponentList();
- components->reload(Net::Mode::Online);
- auto task = components->getCurrentTask();
- if(task)
- {
- m_tasks.append(task.unwrap());
- }
- }
-
- // libraries download
- {
- m_tasks.append(std::make_shared<LibrariesTask>(m_inst));
- }
-
- // FML libraries download and copy into the instance
- {
- m_tasks.append(std::make_shared<FMLLibrariesTask>(m_inst));
- }
-
- // assets update
- {
- m_tasks.append(std::make_shared<AssetUpdateTask>(m_inst));
- }
-
- if(!m_preFailure.isEmpty())
- {
- emitFailed(m_preFailure);
- return;
- }
- next();
+ m_tasks.clear();
+ // create folders
+ {
+ m_tasks.append(std::make_shared<FoldersTask>(m_inst));
+ }
+
+ // add metadata update task if necessary
+ {
+ auto components = m_inst->getComponentList();
+ components->reload(Net::Mode::Online);
+ auto task = components->getCurrentTask();
+ if(task)
+ {
+ m_tasks.append(task.unwrap());
+ }
+ }
+
+ // libraries download
+ {
+ m_tasks.append(std::make_shared<LibrariesTask>(m_inst));
+ }
+
+ // FML libraries download and copy into the instance
+ {
+ m_tasks.append(std::make_shared<FMLLibrariesTask>(m_inst));
+ }
+
+ // assets update
+ {
+ m_tasks.append(std::make_shared<AssetUpdateTask>(m_inst));
+ }
+
+ if(!m_preFailure.isEmpty())
+ {
+ emitFailed(m_preFailure);
+ return;
+ }
+ next();
}
-void OneSixUpdate::next()
+void MinecraftUpdate::next()
{
- if(m_abort)
- {
- emitFailed(tr("Aborted by user."));
- return;
- }
- if(m_failed_out_of_order)
- {
- emitFailed(m_fail_reason);
- return;
- }
- m_currentTask ++;
- if(m_currentTask > 0)
- {
- auto task = m_tasks[m_currentTask - 1];
- disconnect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded);
- disconnect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed);
- disconnect(task.get(), &Task::progress, this, &OneSixUpdate::progress);
- disconnect(task.get(), &Task::status, this, &OneSixUpdate::setStatus);
- }
- if(m_currentTask == m_tasks.size())
- {
- emitSucceeded();
- return;
- }
- auto task = m_tasks[m_currentTask];
- // if the task is already finished by the time we look at it, skip it
- if(task->isFinished())
- {
- qCritical() << "OneSixUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get();
- next();
- }
- connect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded);
- connect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed);
- connect(task.get(), &Task::progress, this, &OneSixUpdate::progress);
- connect(task.get(), &Task::status, this, &OneSixUpdate::setStatus);
- // if the task is already running, do not start it again
- if(!task->isRunning())
- {
- task->start();
- }
+ if(m_abort)
+ {
+ emitFailed(tr("Aborted by user."));
+ return;
+ }
+ if(m_failed_out_of_order)
+ {
+ emitFailed(m_fail_reason);
+ return;
+ }
+ m_currentTask ++;
+ if(m_currentTask > 0)
+ {
+ auto task = m_tasks[m_currentTask - 1];
+ disconnect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
+ disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
+ disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
+ disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
+ }
+ if(m_currentTask == m_tasks.size())
+ {
+ emitSucceeded();
+ return;
+ }
+ auto task = m_tasks[m_currentTask];
+ // if the task is already finished by the time we look at it, skip it
+ if(task->isFinished())
+ {
+ qCritical() << "MinecraftUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get();
+ next();
+ }
+ connect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
+ connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
+ connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
+ connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
+ // if the task is already running, do not start it again
+ if(!task->isRunning())
+ {
+ task->start();
+ }
}
-void OneSixUpdate::subtaskSucceeded()
+void MinecraftUpdate::subtaskSucceeded()
{
- if(isFinished())
- {
- qCritical() << "OneSixUpdate: Subtask" << sender() << "succeeded, but work was already done!";
- return;
- }
- auto senderTask = QObject::sender();
- auto currentTask = m_tasks[m_currentTask].get();
- if(senderTask != currentTask)
- {
- qDebug() << "OneSixUpdate: Subtask" << sender() << "succeeded out of order.";
- return;
- }
- next();
+ if(isFinished())
+ {
+ qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
+ return;
+ }
+ auto senderTask = QObject::sender();
+ auto currentTask = m_tasks[m_currentTask].get();
+ if(senderTask != currentTask)
+ {
+ qDebug() << "MinecraftUpdate: Subtask" << sender() << "succeeded out of order.";
+ return;
+ }
+ next();
}
-void OneSixUpdate::subtaskFailed(QString error)
+void MinecraftUpdate::subtaskFailed(QString error)
{
- if(isFinished())
- {
- qCritical() << "OneSixUpdate: Subtask" << sender() << "failed, but work was already done!";
- return;
- }
- auto senderTask = QObject::sender();
- auto currentTask = m_tasks[m_currentTask].get();
- if(senderTask != currentTask)
- {
- qDebug() << "OneSixUpdate: Subtask" << sender() << "failed out of order.";
- m_failed_out_of_order = true;
- m_fail_reason = error;
- return;
- }
- emitFailed(error);
+ if(isFinished())
+ {
+ qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
+ return;
+ }
+ auto senderTask = QObject::sender();
+ auto currentTask = m_tasks[m_currentTask].get();
+ if(senderTask != currentTask)
+ {
+ qDebug() << "MinecraftUpdate: Subtask" << sender() << "failed out of order.";
+ m_failed_out_of_order = true;
+ m_fail_reason = error;
+ return;
+ }
+ emitFailed(error);
}
-bool OneSixUpdate::abort()
+bool MinecraftUpdate::abort()
{
- if(!m_abort)
- {
- m_abort = true;
- auto task = m_tasks[m_currentTask];
- if(task->canAbort())
- {
- return task->abort();
- }
- }
- return true;
+ if(!m_abort)
+ {
+ m_abort = true;
+ auto task = m_tasks[m_currentTask];
+ if(task->canAbort())
+ {
+ return task->abort();
+ }
+ }
+ return true;
}
-bool OneSixUpdate::canAbort() const
+bool MinecraftUpdate::canAbort() const
{
- return true;
+ return true;
}
diff --git a/api/logic/minecraft/MinecraftUpdate.h b/api/logic/minecraft/MinecraftUpdate.h
index 78c02049..eb8e74db 100644
--- a/api/logic/minecraft/MinecraftUpdate.h
+++ b/api/logic/minecraft/MinecraftUpdate.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,29 +27,31 @@
class MinecraftVersion;
class MinecraftInstance;
-class OneSixUpdate : public Task
+class MinecraftUpdate : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit OneSixUpdate(MinecraftInstance *inst, QObject *parent = 0);
- void executeTask() override;
- bool canAbort() const override;
+ explicit MinecraftUpdate(MinecraftInstance *inst, QObject *parent = 0);
+ virtual ~MinecraftUpdate() {};
+
+ void executeTask() override;
+ bool canAbort() const override;
private
slots:
- bool abort() override;
- void subtaskSucceeded();
- void subtaskFailed(QString error);
+ bool abort() override;
+ void subtaskSucceeded();
+ void subtaskFailed(QString error);
private:
- void next();
+ void next();
private:
- MinecraftInstance *m_inst = nullptr;
- QList<std::shared_ptr<Task>> m_tasks;
- QString m_preFailure;
- int m_currentTask = -1;
- bool m_abort = false;
- bool m_failed_out_of_order = false;
- QString m_fail_reason;
+ MinecraftInstance *m_inst = nullptr;
+ QList<std::shared_ptr<Task>> m_tasks;
+ QString m_preFailure;
+ int m_currentTask = -1;
+ bool m_abort = false;
+ bool m_failed_out_of_order = false;
+ QString m_fail_reason;
};
diff --git a/api/logic/minecraft/Mod.cpp b/api/logic/minecraft/Mod.cpp
deleted file mode 100644
index 03e04b2b..00000000
--- a/api/logic/minecraft/Mod.cpp
+++ /dev/null
@@ -1,378 +0,0 @@
-/* Copyright 2013-2018 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QDir>
-#include <QString>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonValue>
-#include <quazip.h>
-#include <quazipfile.h>
-
-#include "Mod.h"
-#include "settings/INIFile.h"
-#include <FileSystem.h>
-#include <QDebug>
-
-Mod::Mod(const QFileInfo &file)
-{
- repath(file);
- m_changedDateTime = file.lastModified();
-}
-
-void Mod::repath(const QFileInfo &file)
-{
- m_file = file;
- QString name_base = file.fileName();
-
- m_type = Mod::MOD_UNKNOWN;
-
- if (m_file.isDir())
- {
- m_type = MOD_FOLDER;
- m_name = name_base;
- m_mmc_id = name_base;
- }
- else if (m_file.isFile())
- {
- if (name_base.endsWith(".disabled"))
- {
- m_enabled = false;
- name_base.chop(9);
- }
- else
- {
- m_enabled = true;
- }
- m_mmc_id = name_base;
- if (name_base.endsWith(".zip") || name_base.endsWith(".jar"))
- {
- m_type = MOD_ZIPFILE;
- name_base.chop(4);
- }
- else if (name_base.endsWith(".litemod"))
- {
- m_type = MOD_LITEMOD;
- name_base.chop(8);
- }
- else
- {
- m_type = MOD_SINGLEFILE;
- }
- m_name = name_base;
- }
-
- if (m_type == MOD_ZIPFILE)
- {
- QuaZip zip(m_file.filePath());
- if (!zip.open(QuaZip::mdUnzip))
- return;
-
- QuaZipFile file(&zip);
-
- if (zip.setCurrentFile("mcmod.info"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- ReadMCModInfo(file.readAll());
- file.close();
- zip.close();
- return;
- }
- else if (zip.setCurrentFile("forgeversion.properties"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- ReadForgeInfo(file.readAll());
- file.close();
- zip.close();
- return;
- }
-
- zip.close();
- }
- else if (m_type == MOD_FOLDER)
- {
- QFileInfo mcmod_info(FS::PathCombine(m_file.filePath(), "mcmod.info"));
- if (mcmod_info.isFile())
- {
- QFile mcmod(mcmod_info.filePath());
- if (!mcmod.open(QIODevice::ReadOnly))
- return;
- auto data = mcmod.readAll();
- if (data.isEmpty() || data.isNull())
- return;
- ReadMCModInfo(data);
- }
- }
- else if (m_type == MOD_LITEMOD)
- {
- QuaZip zip(m_file.filePath());
- if (!zip.open(QuaZip::mdUnzip))
- return;
-
- QuaZipFile file(&zip);
-
- if (zip.setCurrentFile("litemod.json"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- ReadLiteModInfo(file.readAll());
- file.close();
- }
- zip.close();
- }
-}
-
-// NEW format
-// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3
-
-// OLD format:
-// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
-void Mod::ReadMCModInfo(QByteArray contents)
-{
- auto getInfoFromArray = [&](QJsonArray arr)->void
- {
- if (!arr.at(0).isObject())
- return;
- auto firstObj = arr.at(0).toObject();
- m_mod_id = firstObj.value("modid").toString();
- m_name = firstObj.value("name").toString();
- m_version = firstObj.value("version").toString();
- m_homeurl = firstObj.value("url").toString();
- m_updateurl = firstObj.value("updateUrl").toString();
- m_homeurl = m_homeurl.trimmed();
- if(!m_homeurl.isEmpty())
- {
- // fix up url.
- if (!m_homeurl.startsWith("http://") && !m_homeurl.startsWith("https://") &&
- !m_homeurl.startsWith("ftp://"))
- {
- m_homeurl.prepend("http://");
- }
- }
- m_description = firstObj.value("description").toString();
- QJsonArray authors = firstObj.value("authorList").toArray();
- if (authors.size() == 0)
- authors = firstObj.value("authors").toArray();
-
- if (authors.size() == 0)
- m_authors = "";
- else if (authors.size() >= 1)
- {
- m_authors = authors.at(0).toString();
- for (int i = 1; i < authors.size(); i++)
- {
- m_authors += ", " + authors.at(i).toString();
- }
- }
- m_credits = firstObj.value("credits").toString();
- return;
- }
- ;
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
- // this is the very old format that had just the array
- if (jsonDoc.isArray())
- {
- getInfoFromArray(jsonDoc.array());
- }
- else if (jsonDoc.isObject())
- {
- auto val = jsonDoc.object().value("modinfoversion");
- if(val.isUndefined())
- val = jsonDoc.object().value("modListVersion");
- int version = val.toDouble();
- if (version != 2)
- {
- qCritical() << "BAD stuff happened to mod json:";
- qCritical() << contents;
- return;
- }
- auto arrVal = jsonDoc.object().value("modlist");
- if(arrVal.isUndefined())
- arrVal = jsonDoc.object().value("modList");
- if (arrVal.isArray())
- {
- getInfoFromArray(arrVal.toArray());
- }
- }
-}
-
-void Mod::ReadForgeInfo(QByteArray contents)
-{
- // Read the data
- m_name = "Minecraft Forge";
- m_mod_id = "Forge";
- m_homeurl = "http://www.minecraftforge.net/forum/";
- INIFile ini;
- if (!ini.loadFile(contents))
- return;
-
- QString major = ini.get("forge.major.number", "0").toString();
- QString minor = ini.get("forge.minor.number", "0").toString();
- QString revision = ini.get("forge.revision.number", "0").toString();
- QString build = ini.get("forge.build.number", "0").toString();
-
- m_version = major + "." + minor + "." + revision + "." + build;
-}
-
-void Mod::ReadLiteModInfo(QByteArray contents)
-{
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
- auto object = jsonDoc.object();
- if (object.contains("name"))
- {
- m_mod_id = m_name = object.value("name").toString();
- }
- if (object.contains("version"))
- {
- m_version = object.value("version").toString("");
- }
- else
- {
- m_version = object.value("revision").toString("");
- }
- m_mcversion = object.value("mcversion").toString();
- m_authors = object.value("author").toString();
- m_description = object.value("description").toString();
- m_homeurl = object.value("url").toString();
-}
-
-bool Mod::replace(Mod &with)
-{
- if (!destroy())
- return false;
- bool success = false;
- auto t = with.type();
-
- if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE || t == MOD_LITEMOD)
- {
- qDebug() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath();
- success = QFile::copy(with.m_file.filePath(), m_file.filePath());
- }
- if (t == MOD_FOLDER)
- {
- success = FS::copy(with.m_file.filePath(), m_file.path())();
- }
- if (success)
- {
- m_name = with.m_name;
- m_mmc_id = with.m_mmc_id;
- m_mod_id = with.m_mod_id;
- m_version = with.m_version;
- m_mcversion = with.m_mcversion;
- m_description = with.m_description;
- m_authors = with.m_authors;
- m_credits = with.m_credits;
- m_homeurl = with.m_homeurl;
- m_type = with.m_type;
- m_file.refresh();
- }
- return success;
-}
-
-bool Mod::destroy()
-{
- if (m_type == MOD_FOLDER)
- {
- QDir d(m_file.filePath());
- if (d.removeRecursively())
- {
- m_type = MOD_UNKNOWN;
- return true;
- }
- return false;
- }
- else if (m_type == MOD_SINGLEFILE || m_type == MOD_ZIPFILE || m_type == MOD_LITEMOD)
- {
- QFile f(m_file.filePath());
- if (f.remove())
- {
- m_type = MOD_UNKNOWN;
- return true;
- }
- return false;
- }
- return true;
-}
-
-QString Mod::version() const
-{
- switch (type())
- {
- case MOD_ZIPFILE:
- case MOD_LITEMOD:
- return m_version;
- case MOD_FOLDER:
- return "Folder";
- case MOD_SINGLEFILE:
- return "File";
- default:
- return "VOID";
- }
-}
-
-bool Mod::enable(bool value)
-{
- if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER)
- return false;
-
- if (m_enabled == value)
- return false;
-
- QString path = m_file.absoluteFilePath();
- if (value)
- {
- QFile foo(path);
- if (!path.endsWith(".disabled"))
- return false;
- path.chop(9);
- if (!foo.rename(path))
- return false;
- }
- else
- {
- QFile foo(path);
- path += ".disabled";
- if (!foo.rename(path))
- return false;
- }
- m_file = QFileInfo(path);
- m_enabled = value;
- return true;
-}
-bool Mod::operator==(const Mod &other) const
-{
- return mmc_id() == other.mmc_id();
-}
-bool Mod::strongCompare(const Mod &other) const
-{
- return mmc_id() == other.mmc_id() && version() == other.version() && type() == other.type();
-}
diff --git a/api/logic/minecraft/Mod.h b/api/logic/minecraft/Mod.h
deleted file mode 100644
index ccab1867..00000000
--- a/api/logic/minecraft/Mod.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/* Copyright 2013-2018 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <QFileInfo>
-#include <QDateTime>
-
-class Mod
-{
-public:
- enum ModType
- {
- MOD_UNKNOWN, //!< Indicates an unspecified mod type.
- MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files.
- MOD_SINGLEFILE, //!< The mod is a single file (not a zip file).
- MOD_FOLDER, //!< The mod is in a folder on the filesystem.
- MOD_LITEMOD, //!< The mod is a litemod
- };
-
- Mod(const QFileInfo &file);
-
- QFileInfo filename() const
- {
- return m_file;
- }
- QString mmc_id() const
- {
- return m_mmc_id;
- }
- QString mod_id() const
- {
- return m_mod_id;
- }
- ModType type() const
- {
- return m_type;
- }
- QString mcversion() const
- {
- return m_mcversion;
- }
- ;
- bool valid()
- {
- return m_type != MOD_UNKNOWN;
- }
- QString name() const
- {
- QString name = m_name.trimmed();
- if(name.isEmpty() || name == "Example Mod")
- {
- return m_mmc_id;
- }
- return m_name;
- }
-
- QString version() const;
-
- QString homeurl() const
- {
- return m_homeurl;
- }
-
- QString description() const
- {
- return m_description;
- }
-
- QString authors() const
- {
- return m_authors;
- }
-
- QString credits() const
- {
- return m_credits;
- }
-
- QDateTime dateTimeChanged() const
- {
- return m_changedDateTime;
- }
-
- bool enabled() const
- {
- return m_enabled;
- }
-
- bool enable(bool value);
-
- // delete all the files of this mod
- bool destroy();
- // replace this mod with a copy of the other
- bool replace(Mod &with);
- // change the mod's filesystem path (used by mod lists for *MAGIC* purposes)
- void repath(const QFileInfo &file);
-
- // WEAK compare operator - used for replacing mods
- bool operator==(const Mod &other) const;
- bool strongCompare(const Mod &other) const;
-
-private:
- void ReadMCModInfo(QByteArray contents);
- void ReadForgeInfo(QByteArray contents);
- void ReadLiteModInfo(QByteArray contents);
-
-protected:
-
- // FIXME: what do do with those? HMM...
- /*
- void ReadModInfoData(QString info);
- void ReadForgeInfoData(QString infoFileData);
- */
-
- QFileInfo m_file;
- QDateTime m_changedDateTime;
- QString m_mmc_id;
- QString m_mod_id;
- bool m_enabled = true;
- QString m_name;
- QString m_version;
- QString m_mcversion;
- QString m_homeurl;
- QString m_updateurl;
- QString m_description;
- QString m_authors;
- QString m_credits;
-
- ModType m_type;
-};
diff --git a/api/logic/minecraft/ModList.cpp b/api/logic/minecraft/ModList.cpp
deleted file mode 100644
index 6ccf20e2..00000000
--- a/api/logic/minecraft/ModList.cpp
+++ /dev/null
@@ -1,367 +0,0 @@
-/* Copyright 2013-2018 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ModList.h"
-#include <FileSystem.h>
-#include <QMimeData>
-#include <QUrl>
-#include <QUuid>
-#include <QString>
-#include <QFileSystemWatcher>
-#include <QDebug>
-
-ModList::ModList(const QString &dir) : QAbstractListModel(), m_dir(dir)
-{
- FS::ensureFolderPathExists(m_dir.absolutePath());
- m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs |
- QDir::NoSymLinks);
- m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
- m_watcher = new QFileSystemWatcher(this);
- connect(m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString)));
-}
-
-void ModList::startWatching()
-{
- if(is_watching)
- return;
-
- update();
-
- is_watching = m_watcher->addPath(m_dir.absolutePath());
- if (is_watching)
- {
- qDebug() << "Started watching " << m_dir.absolutePath();
- }
- else
- {
- qDebug() << "Failed to start watching " << m_dir.absolutePath();
- }
-}
-
-void ModList::stopWatching()
-{
- if(!is_watching)
- return;
-
- is_watching = !m_watcher->removePath(m_dir.absolutePath());
- if (!is_watching)
- {
- qDebug() << "Stopped watching " << m_dir.absolutePath();
- }
- else
- {
- qDebug() << "Failed to stop watching " << m_dir.absolutePath();
- }
-}
-
-bool ModList::update()
-{
- if (!isValid())
- return false;
-
- QList<Mod> orderedMods;
- QList<Mod> newMods;
- m_dir.refresh();
- auto folderContents = m_dir.entryInfoList();
- bool orderOrStateChanged = false;
-
- // if there are any untracked files...
- if (folderContents.size())
- {
- // the order surely changed!
- for (auto entry : folderContents)
- {
- newMods.append(Mod(entry));
- }
- orderedMods.append(newMods);
- orderOrStateChanged = true;
- }
- // otherwise, if we were already tracking some mods
- else if (mods.size())
- {
- // if the number doesn't match, order changed.
- if (mods.size() != orderedMods.size())
- orderOrStateChanged = true;
- // if it does match, compare the mods themselves
- else
- for (int i = 0; i < mods.size(); i++)
- {
- if (!mods[i].strongCompare(orderedMods[i]))
- {
- orderOrStateChanged = true;
- break;
- }
- }
- }
- beginResetModel();
- mods.swap(orderedMods);
- endResetModel();
- if (orderOrStateChanged)
- {
- emit changed();
- }
- return true;
-}
-
-void ModList::directoryChanged(QString path)
-{
- update();
-}
-
-bool ModList::isValid()
-{
- return m_dir.exists() && m_dir.isReadable();
-}
-
-bool ModList::installMod(const QString &filename)
-{
- // NOTE: fix for GH-1178: remove trailing slash to avoid issues with using the empty result of QFileInfo::fileName
- QFileInfo fileinfo(FS::NormalizePath(filename));
-
- qDebug() << "installing: " << fileinfo.absoluteFilePath();
-
- if (!fileinfo.exists() || !fileinfo.isReadable())
- {
- return false;
- }
- Mod m(fileinfo);
- if (!m.valid())
- return false;
-
- auto type = m.type();
- if (type == Mod::MOD_UNKNOWN)
- return false;
- if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE || type == Mod::MOD_LITEMOD)
- {
- QString newpath = FS::PathCombine(m_dir.path(), fileinfo.fileName());
- if (!QFile::copy(fileinfo.filePath(), newpath))
- return false;
- FS::updateTimestamp(newpath);
- m.repath(newpath);
- update();
- return true;
- }
- else if (type == Mod::MOD_FOLDER)
- {
- QString from = fileinfo.filePath();
- QString to = FS::PathCombine(m_dir.path(), fileinfo.fileName());
- if (!FS::copy(from, to)())
- return false;
- m.repath(to);
- update();
- return true;
- }
- return false;
-}
-
-bool ModList::enableMods(const QModelIndexList& indexes, bool enable)
-{
- if(indexes.isEmpty())
- return true;
-
- for (auto i: indexes)
- {
- Mod &m = mods[i.row()];
- m.enable(enable);
- emit dataChanged(i, i);
- }
- emit changed();
- return true;
-}
-
-bool ModList::deleteMods(const QModelIndexList& indexes)
-{
- if(indexes.isEmpty())
- return true;
-
- for (auto i: indexes)
- {
- Mod &m = mods[i.row()];
- m.destroy();
- }
- emit changed();
- return true;
-}
-
-int ModList::columnCount(const QModelIndex &parent) const
-{
- return NUM_COLUMNS;
-}
-
-QVariant ModList::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return QVariant();
-
- int row = index.row();
- int column = index.column();
-
- if (row < 0 || row >= mods.size())
- return QVariant();
-
- switch (role)
- {
- case Qt::DisplayRole:
- switch (column)
- {
- case NameColumn:
- return mods[row].name();
- case VersionColumn:
- return mods[row].version();
- case DateColumn:
- return mods[row].dateTimeChanged();
-
- default:
- return QVariant();
- }
-
- case Qt::ToolTipRole:
- return mods[row].mmc_id();
-
- case Qt::CheckStateRole:
- switch (column)
- {
- case ActiveColumn:
- return mods[row].enabled() ? Qt::Checked : Qt::Unchecked;
- default:
- return QVariant();
- }
- default:
- return QVariant();
- }
-}
-
-bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
- {
- return false;
- }
-
- if (role == Qt::CheckStateRole)
- {
- auto &mod = mods[index.row()];
- if (mod.enable(!mod.enabled()))
- {
- emit dataChanged(index, index);
- return true;
- }
- }
- return false;
-}
-
-QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const
-{
- switch (role)
- {
- case Qt::DisplayRole:
- switch (section)
- {
- case ActiveColumn:
- return QString();
- case NameColumn:
- return tr("Name");
- case VersionColumn:
- return tr("Version");
- case DateColumn:
- return tr("Last changed");
- default:
- return QVariant();
- }
-
- case Qt::ToolTipRole:
- switch (section)
- {
- case ActiveColumn:
- return tr("Is the mod enabled?");
- case NameColumn:
- return tr("The name of the mod.");
- case VersionColumn:
- return tr("The version of the mod.");
- case DateColumn:
- return tr("The date and time this mod was last changed (or added).");
- default:
- return QVariant();
- }
- default:
- return QVariant();
- }
- return QVariant();
-}
-
-Qt::ItemFlags ModList::flags(const QModelIndex &index) const
-{
- Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
- if (index.isValid())
- return Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled |
- defaultFlags;
- else
- return Qt::ItemIsDropEnabled | defaultFlags;
-}
-
-Qt::DropActions ModList::supportedDropActions() const
-{
- // copy from outside, move from within and other mod lists
- return Qt::CopyAction | Qt::MoveAction;
-}
-
-QStringList ModList::mimeTypes() const
-{
- QStringList types;
- types << "text/uri-list";
- return types;
-}
-
-bool ModList::dropMimeData(const QMimeData* data, Qt::DropAction action, int, int, const QModelIndex&)
-{
- if (action == Qt::IgnoreAction)
- {
- return true;
- }
-
- // check if the action is supported
- if (!data || !(action & supportedDropActions()))
- {
- return false;
- }
-
- // files dropped from outside?
- if (data->hasUrls())
- {
- bool was_watching = is_watching;
- if (was_watching)
- {
- stopWatching();
- }
- auto urls = data->urls();
- for (auto url : urls)
- {
- // only local files may be dropped...
- if (!url.isLocalFile())
- {
- continue;
- }
- // TODO: implement not only copy, but also move
- // FIXME: handle errors here
- installMod(url.toLocalFile());
- }
- if (was_watching)
- {
- startWatching();
- }
- return true;
- }
- return false;
-}
diff --git a/api/logic/minecraft/ModList.h b/api/logic/minecraft/ModList.h
deleted file mode 100644
index 72f50edb..00000000
--- a/api/logic/minecraft/ModList.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/* Copyright 2013-2018 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QList>
-#include <QString>
-#include <QDir>
-#include <QAbstractListModel>
-
-#include "minecraft/Mod.h"
-
-#include "multimc_logic_export.h"
-
-class LegacyInstance;
-class BaseInstance;
-class QFileSystemWatcher;
-
-/**
- * A legacy mod list.
- * Backed by a folder.
- */
-class MULTIMC_LOGIC_EXPORT ModList : public QAbstractListModel
-{
- Q_OBJECT
-public:
- enum Columns
- {
- ActiveColumn = 0,
- NameColumn,
- DateColumn,
- VersionColumn,
- NUM_COLUMNS
- };
- ModList(const QString &dir);
-
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
- Qt::DropActions supportedDropActions() const override;
-
- /// flags, mostly to support drag&drop
- virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
- QStringList mimeTypes() const override;
- bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override;
-
- virtual int rowCount(const QModelIndex &) const override
- {
- return size();
- }
- ;
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
- virtual int columnCount(const QModelIndex &parent) const override;
-
- size_t size() const
- {
- return mods.size();
- }
- ;
- bool empty() const
- {
- return size() == 0;
- }
- Mod &operator[](size_t index)
- {
- return mods[index];
- }
-
- /// Reloads the mod list and returns true if the list changed.
- virtual bool update();
-
- /**
- * Adds the given mod to the list at the given index - if the list supports custom ordering
- */
- bool installMod(const QString& filename);
-
- /// Deletes all the selected mods
- virtual bool deleteMods(const QModelIndexList &indexes);
-
- /// Enable or disable listed mods
- virtual bool enableMods(const QModelIndexList &indexes, bool enable = true);
-
- void startWatching();
- void stopWatching();
-
- virtual bool isValid();
-
- QDir dir()
- {
- return m_dir;
- }
-
- const QList<Mod> & allMods()
- {
- return mods;
- }
-
-private
-slots:
- void directoryChanged(QString path);
-
-signals:
- void changed();
-
-protected:
- QFileSystemWatcher *m_watcher;
- bool is_watching = false;
- QDir m_dir;
- QList<Mod> mods;
-};
diff --git a/api/logic/minecraft/ModList_test.cpp b/api/logic/minecraft/ModList_test.cpp
deleted file mode 100644
index 155c238a..00000000
--- a/api/logic/minecraft/ModList_test.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-
-#include <QTest>
-#include <QTemporaryDir>
-#include "TestUtil.h"
-
-#include "FileSystem.h"
-#include "minecraft/ModList.h"
-
-class ModListTest : public QObject
-{
- Q_OBJECT
-
-private
-slots:
- // test for GH-1178 - install a folder with files to a mod list
- void test_1178()
- {
- // source
- QString source = QFINDTESTDATA("data/test_folder");
-
- // sanity check
- QVERIFY(!source.endsWith('/'));
-
- auto verify = [](QString path)
- {
- QDir target_dir(FS::PathCombine(path, "test_folder"));
- QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
- QVERIFY(target_dir.entryList().contains("assets"));
- };
-
- // 1. test with no trailing /
- {
- QString folder = source;
- QTemporaryDir tempDir;
- ModList m(tempDir.path());
- m.installMod(folder);
- verify(tempDir.path());
- }
-
- // 2. test with trailing /
- {
- QString folder = source + '/';
- QTemporaryDir tempDir;
- ModList m(tempDir.path());
- m.installMod(folder);
- verify(tempDir.path());
- }
- }
-};
-
-QTEST_GUILESS_MAIN(ModListTest)
-
-#include "ModList_test.moc"
diff --git a/api/logic/minecraft/MojangDownloadInfo.h b/api/logic/minecraft/MojangDownloadInfo.h
index 7399a56b..88f87287 100644
--- a/api/logic/minecraft/MojangDownloadInfo.h
+++ b/api/logic/minecraft/MojangDownloadInfo.h
@@ -5,78 +5,78 @@
struct MojangDownloadInfo
{
- // types
- typedef std::shared_ptr<MojangDownloadInfo> Ptr;
+ // types
+ typedef std::shared_ptr<MojangDownloadInfo> Ptr;
- // data
- /// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested!
- QString path;
- /// absolute URL of this file
- QString url;
- /// sha-1 checksum of the file
- QString sha1;
- /// size of the file in bytes
- int size;
+ // data
+ /// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested!
+ QString path;
+ /// absolute URL of this file
+ QString url;
+ /// sha-1 checksum of the file
+ QString sha1;
+ /// size of the file in bytes
+ int size;
};
struct MojangLibraryDownloadInfo
{
- MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact): artifact(artifact) {};
- MojangLibraryDownloadInfo() {};
+ MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact): artifact(artifact) {};
+ MojangLibraryDownloadInfo() {};
- // types
- typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr;
+ // types
+ typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr;
- // methods
- MojangDownloadInfo *getDownloadInfo(QString classifier)
- {
- if (classifier.isNull())
- {
- return artifact.get();
- }
-
- return classifiers[classifier].get();
- }
+ // methods
+ MojangDownloadInfo *getDownloadInfo(QString classifier)
+ {
+ if (classifier.isNull())
+ {
+ return artifact.get();
+ }
+
+ return classifiers[classifier].get();
+ }
- // data
- MojangDownloadInfo::Ptr artifact;
- QMap<QString, MojangDownloadInfo::Ptr> classifiers;
+ // data
+ MojangDownloadInfo::Ptr artifact;
+ QMap<QString, MojangDownloadInfo::Ptr> classifiers;
};
struct MojangAssetIndexInfo : public MojangDownloadInfo
{
- // types
- typedef std::shared_ptr<MojangAssetIndexInfo> Ptr;
+ // types
+ typedef std::shared_ptr<MojangAssetIndexInfo> Ptr;
- // methods
- MojangAssetIndexInfo()
- {
- }
+ // methods
+ MojangAssetIndexInfo()
+ {
+ }
- MojangAssetIndexInfo(QString id)
- {
- this->id = id;
- // HACK: ignore assets from other version files than Minecraft
- // workaround for stupid assets issue caused by amazon:
- // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
- if(id == "legacy")
- {
- url = "https://launchermeta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json";
- }
- // HACK
- else
- {
- url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id + ".json";
- }
- known = false;
- }
+ MojangAssetIndexInfo(QString id)
+ {
+ this->id = id;
+ // HACK: ignore assets from other version files than Minecraft
+ // workaround for stupid assets issue caused by amazon:
+ // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
+ if(id == "legacy")
+ {
+ url = "https://launchermeta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json";
+ }
+ // HACK
+ else
+ {
+ url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id + ".json";
+ }
+ known = false;
+ }
- // data
- int totalSize;
- QString id;
- bool known = true;
+ // data
+ int totalSize;
+ QString id;
+ bool known = true;
};
diff --git a/api/logic/minecraft/MojangVersionFormat.cpp b/api/logic/minecraft/MojangVersionFormat.cpp
index a0aa2894..33d3c54c 100644
--- a/api/logic/minecraft/MojangVersionFormat.cpp
+++ b/api/logic/minecraft/MojangVersionFormat.cpp
@@ -19,361 +19,361 @@ namespace Bits
{
static void readString(const QJsonObject &root, const QString &key, QString &variable)
{
- if (root.contains(key))
- {
- variable = requireString(root.value(key));
- }
+ if (root.contains(key))
+ {
+ variable = requireString(root.value(key));
+ }
}
static void readDownloadInfo(MojangDownloadInfo::Ptr out, const QJsonObject &obj)
{
- // optional, not used
- readString(obj, "path", out->path);
- // required!
- out->sha1 = requireString(obj, "sha1");
- out->url = requireString(obj, "url");
- out->size = requireInteger(obj, "size");
+ // optional, not used
+ readString(obj, "path", out->path);
+ // required!
+ out->sha1 = requireString(obj, "sha1");
+ out->url = requireString(obj, "url");
+ out->size = requireInteger(obj, "size");
}
static void readAssetIndex(MojangAssetIndexInfo::Ptr out, const QJsonObject &obj)
{
- out->totalSize = requireInteger(obj, "totalSize");
- out->id = requireString(obj, "id");
- // out->known = true;
+ out->totalSize = requireInteger(obj, "totalSize");
+ out->id = requireString(obj, "id");
+ // out->known = true;
}
}
MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject &obj)
{
- auto out = std::make_shared<MojangDownloadInfo>();
- Bits::readDownloadInfo(out, obj);
- return out;
+ auto out = std::make_shared<MojangDownloadInfo>();
+ Bits::readDownloadInfo(out, obj);
+ return out;
}
MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject &obj)
{
- auto out = std::make_shared<MojangAssetIndexInfo>();
- Bits::readDownloadInfo(out, obj);
- Bits::readAssetIndex(out, obj);
- return out;
+ auto out = std::make_shared<MojangAssetIndexInfo>();
+ Bits::readDownloadInfo(out, obj);
+ Bits::readAssetIndex(out, obj);
+ return out;
}
QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info)
{
- QJsonObject out;
- if(!info->path.isNull())
- {
- out.insert("path", info->path);
- }
- out.insert("sha1", info->sha1);
- out.insert("size", info->size);
- out.insert("url", info->url);
- return out;
+ QJsonObject out;
+ if(!info->path.isNull())
+ {
+ out.insert("path", info->path);
+ }
+ out.insert("sha1", info->sha1);
+ out.insert("size", info->size);
+ out.insert("url", info->url);
+ return out;
}
MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject &libObj)
{
- auto out = std::make_shared<MojangLibraryDownloadInfo>();
- auto dlObj = requireObject(libObj.value("downloads"));
- if(dlObj.contains("artifact"))
- {
- out->artifact = downloadInfoFromJson(requireObject(dlObj, "artifact"));
- }
- if(dlObj.contains("classifiers"))
- {
- auto classifiersObj = requireObject(dlObj, "classifiers");
- for(auto iter = classifiersObj.begin(); iter != classifiersObj.end(); iter++)
- {
- auto classifier = iter.key();
- auto classifierObj = requireObject(iter.value());
- out->classifiers[classifier] = downloadInfoFromJson(classifierObj);
- }
- }
- return out;
+ auto out = std::make_shared<MojangLibraryDownloadInfo>();
+ auto dlObj = requireObject(libObj.value("downloads"));
+ if(dlObj.contains("artifact"))
+ {
+ out->artifact = downloadInfoFromJson(requireObject(dlObj, "artifact"));
+ }
+ if(dlObj.contains("classifiers"))
+ {
+ auto classifiersObj = requireObject(dlObj, "classifiers");
+ for(auto iter = classifiersObj.begin(); iter != classifiersObj.end(); iter++)
+ {
+ auto classifier = iter.key();
+ auto classifierObj = requireObject(iter.value());
+ out->classifiers[classifier] = downloadInfoFromJson(classifierObj);
+ }
+ }
+ return out;
}
QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo)
{
- QJsonObject out;
- if(libinfo->artifact)
- {
- out.insert("artifact", downloadInfoToJson(libinfo->artifact));
- }
- if(libinfo->classifiers.size())
- {
- QJsonObject classifiersOut;
- for(auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++)
- {
- classifiersOut.insert(iter.key(), downloadInfoToJson(iter.value()));
- }
- out.insert("classifiers", classifiersOut);
- }
- return out;
+ QJsonObject out;
+ if(libinfo->artifact)
+ {
+ out.insert("artifact", downloadInfoToJson(libinfo->artifact));
+ }
+ if(libinfo->classifiers.size())
+ {
+ QJsonObject classifiersOut;
+ for(auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++)
+ {
+ classifiersOut.insert(iter.key(), downloadInfoToJson(iter.value()));
+ }
+ out.insert("classifiers", classifiersOut);
+ }
+ return out;
}
QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr info)
{
- QJsonObject out;
- if(!info->path.isNull())
- {
- out.insert("path", info->path);
- }
- out.insert("sha1", info->sha1);
- out.insert("size", info->size);
- out.insert("url", info->url);
- out.insert("totalSize", info->totalSize);
- out.insert("id", info->id);
- return out;
+ QJsonObject out;
+ if(!info->path.isNull())
+ {
+ out.insert("path", info->path);
+ }
+ out.insert("sha1", info->sha1);
+ out.insert("size", info->size);
+ out.insert("url", info->url);
+ out.insert("totalSize", info->totalSize);
+ out.insert("id", info->id);
+ return out;
}
void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFile *out)
{
- Bits::readString(in, "id", out->minecraftVersion);
- Bits::readString(in, "mainClass", out->mainClass);
- Bits::readString(in, "minecraftArguments", out->minecraftArguments);
- if(out->minecraftArguments.isEmpty())
- {
- QString processArguments;
- Bits::readString(in, "processArguments", processArguments);
- QString toCompare = processArguments.toLower();
- if (toCompare == "legacy")
- {
- out->minecraftArguments = " ${auth_player_name} ${auth_session}";
- }
- else if (toCompare == "username_session")
- {
- out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
- }
- else if (toCompare == "username_session_version")
- {
- out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}";
- }
- else if (!toCompare.isEmpty())
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments));
- }
- }
- Bits::readString(in, "type", out->type);
+ Bits::readString(in, "id", out->minecraftVersion);
+ Bits::readString(in, "mainClass", out->mainClass);
+ Bits::readString(in, "minecraftArguments", out->minecraftArguments);
+ if(out->minecraftArguments.isEmpty())
+ {
+ QString processArguments;
+ Bits::readString(in, "processArguments", processArguments);
+ QString toCompare = processArguments.toLower();
+ if (toCompare == "legacy")
+ {
+ out->minecraftArguments = " ${auth_player_name} ${auth_session}";
+ }
+ else if (toCompare == "username_session")
+ {
+ out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
+ }
+ else if (toCompare == "username_session_version")
+ {
+ out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}";
+ }
+ else if (!toCompare.isEmpty())
+ {
+ out->addProblem(ProblemSeverity::Error, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments));
+ }
+ }
+ Bits::readString(in, "type", out->type);
- Bits::readString(in, "assets", out->assets);
- if(in.contains("assetIndex"))
- {
- out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex"));
- }
- else if (!out->assets.isNull())
- {
- out->mojangAssetIndex = std::make_shared<MojangAssetIndexInfo>(out->assets);
- }
+ Bits::readString(in, "assets", out->assets);
+ if(in.contains("assetIndex"))
+ {
+ out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex"));
+ }
+ else if (!out->assets.isNull())
+ {
+ out->mojangAssetIndex = std::make_shared<MojangAssetIndexInfo>(out->assets);
+ }
- out->releaseTime = timeFromS3Time(in.value("releaseTime").toString(""));
- out->updateTime = timeFromS3Time(in.value("time").toString(""));
+ out->releaseTime = timeFromS3Time(in.value("releaseTime").toString(""));
+ out->updateTime = timeFromS3Time(in.value("time").toString(""));
- if (in.contains("minimumLauncherVersion"))
- {
- out->minimumLauncherVersion = requireInteger(in.value("minimumLauncherVersion"));
- if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
- {
- out->addProblem(
- ProblemSeverity::Warning,
- QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by MultiMC (%2). It might not work properly!")
- .arg(out->minimumLauncherVersion)
- .arg(CURRENT_MINIMUM_LAUNCHER_VERSION));
- }
- }
- if(in.contains("downloads"))
- {
- auto downloadsObj = requireObject(in, "downloads");
- for(auto iter = downloadsObj.begin(); iter != downloadsObj.end(); iter++)
- {
- auto classifier = iter.key();
- auto classifierObj = requireObject(iter.value());
- out->mojangDownloads[classifier] = downloadInfoFromJson(classifierObj);
- }
- }
+ if (in.contains("minimumLauncherVersion"))
+ {
+ out->minimumLauncherVersion = requireInteger(in.value("minimumLauncherVersion"));
+ if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
+ {
+ out->addProblem(
+ ProblemSeverity::Warning,
+ QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by MultiMC (%2). It might not work properly!")
+ .arg(out->minimumLauncherVersion)
+ .arg(CURRENT_MINIMUM_LAUNCHER_VERSION));
+ }
+ }
+ if(in.contains("downloads"))
+ {
+ auto downloadsObj = requireObject(in, "downloads");
+ for(auto iter = downloadsObj.begin(); iter != downloadsObj.end(); iter++)
+ {
+ auto classifier = iter.key();
+ auto classifierObj = requireObject(iter.value());
+ out->mojangDownloads[classifier] = downloadInfoFromJson(classifierObj);
+ }
+ }
}
VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename)
{
- VersionFilePtr out(new VersionFile());
- if (doc.isEmpty() || doc.isNull())
- {
- throw JSONValidationError(filename + " is empty or null");
- }
- if (!doc.isObject())
- {
- throw JSONValidationError(filename + " is not an object");
- }
+ VersionFilePtr out(new VersionFile());
+ if (doc.isEmpty() || doc.isNull())
+ {
+ throw JSONValidationError(filename + " is empty or null");
+ }
+ if (!doc.isObject())
+ {
+ throw JSONValidationError(filename + " is not an object");
+ }
- QJsonObject root = doc.object();
+ QJsonObject root = doc.object();
- readVersionProperties(root, out.get());
+ readVersionProperties(root, out.get());
- out->name = "Minecraft";
- out->uid = "net.minecraft";
- out->version = out->minecraftVersion;
- // out->filename = filename;
+ out->name = "Minecraft";
+ out->uid = "net.minecraft";
+ out->version = out->minecraftVersion;
+ // out->filename = filename;
- if (root.contains("libraries"))
- {
- for (auto libVal : requireArray(root.value("libraries")))
- {
- auto libObj = requireObject(libVal);
+ if (root.contains("libraries"))
+ {
+ for (auto libVal : requireArray(root.value("libraries")))
+ {
+ auto libObj = requireObject(libVal);
- auto lib = MojangVersionFormat::libraryFromJson(libObj, filename);
- out->libraries.append(lib);
- }
- }
- return out;
+ auto lib = MojangVersionFormat::libraryFromJson(libObj, filename);
+ out->libraries.append(lib);
+ }
+ }
+ return out;
}
void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObject& out)
{
- writeString(out, "id", in->minecraftVersion);
- writeString(out, "mainClass", in->mainClass);
- writeString(out, "minecraftArguments", in->minecraftArguments);
- writeString(out, "type", in->type);
- if(!in->releaseTime.isNull())
- {
- writeString(out, "releaseTime", timeToS3Time(in->releaseTime));
- }
- if(!in->updateTime.isNull())
- {
- writeString(out, "time", timeToS3Time(in->updateTime));
- }
- if(in->minimumLauncherVersion != -1)
- {
- out.insert("minimumLauncherVersion", in->minimumLauncherVersion);
- }
- writeString(out, "assets", in->assets);
- if(in->mojangAssetIndex && in->mojangAssetIndex->known)
- {
- out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex));
- }
- if(in->mojangDownloads.size())
- {
- QJsonObject downloadsOut;
- for(auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++)
- {
- downloadsOut.insert(iter.key(), downloadInfoToJson(iter.value()));
- }
- out.insert("downloads", downloadsOut);
- }
+ writeString(out, "id", in->minecraftVersion);
+ writeString(out, "mainClass", in->mainClass);
+ writeString(out, "minecraftArguments", in->minecraftArguments);
+ writeString(out, "type", in->type);
+ if(!in->releaseTime.isNull())
+ {
+ writeString(out, "releaseTime", timeToS3Time(in->releaseTime));
+ }
+ if(!in->updateTime.isNull())
+ {
+ writeString(out, "time", timeToS3Time(in->updateTime));
+ }
+ if(in->minimumLauncherVersion != -1)
+ {
+ out.insert("minimumLauncherVersion", in->minimumLauncherVersion);
+ }
+ writeString(out, "assets", in->assets);
+ if(in->mojangAssetIndex && in->mojangAssetIndex->known)
+ {
+ out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex));
+ }
+ if(in->mojangDownloads.size())
+ {
+ QJsonObject downloadsOut;
+ for(auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++)
+ {
+ downloadsOut.insert(iter.key(), downloadInfoToJson(iter.value()));
+ }
+ out.insert("downloads", downloadsOut);
+ }
}
QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr &patch)
{
- QJsonObject root;
- writeVersionProperties(patch.get(), root);
- if (!patch->libraries.isEmpty())
- {
- QJsonArray array;
- for (auto value: patch->libraries)
- {
- array.append(MojangVersionFormat::libraryToJson(value.get()));
- }
- root.insert("libraries", array);
- }
+ QJsonObject root;
+ writeVersionProperties(patch.get(), root);
+ if (!patch->libraries.isEmpty())
+ {
+ QJsonArray array;
+ for (auto value: patch->libraries)
+ {
+ array.append(MojangVersionFormat::libraryToJson(value.get()));
+ }
+ root.insert("libraries", array);
+ }
- // write the contents to a json document.
- {
- QJsonDocument out;
- out.setObject(root);
- return out;
- }
+ // write the contents to a json document.
+ {
+ QJsonDocument out;
+ out.setObject(root);
+ return out;
+ }
}
LibraryPtr MojangVersionFormat::libraryFromJson(const QJsonObject &libObj, const QString &filename)
{
- LibraryPtr out(new Library());
- if (!libObj.contains("name"))
- {
- throw JSONValidationError(filename + "contains a library that doesn't have a 'name' field");
- }
- out->m_name = libObj.value("name").toString();
+ LibraryPtr out(new Library());
+ if (!libObj.contains("name"))
+ {
+ throw JSONValidationError(filename + "contains a library that doesn't have a 'name' field");
+ }
+ out->m_name = libObj.value("name").toString();
- Bits::readString(libObj, "url", out->m_repositoryURL);
- if (libObj.contains("extract"))
- {
- out->m_hasExcludes = true;
- auto extractObj = requireObject(libObj.value("extract"));
- for (auto excludeVal : requireArray(extractObj.value("exclude")))
- {
- out->m_extractExcludes.append(requireString(excludeVal));
- }
- }
- if (libObj.contains("natives"))
- {
- QJsonObject nativesObj = requireObject(libObj.value("natives"));
- for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
- {
- if (!it.value().isString())
- {
- qWarning() << filename << "contains an invalid native (skipping)";
- }
- OpSys opSys = OpSys_fromString(it.key());
- if (opSys != Os_Other)
- {
- out->m_nativeClassifiers[opSys] = it.value().toString();
- }
- }
- }
- if (libObj.contains("rules"))
- {
- out->applyRules = true;
- out->m_rules = rulesFromJsonV4(libObj);
- }
- if (libObj.contains("downloads"))
- {
- out->m_mojangDownloads = libDownloadInfoFromJson(libObj);
- }
- return out;
+ Bits::readString(libObj, "url", out->m_repositoryURL);
+ if (libObj.contains("extract"))
+ {
+ out->m_hasExcludes = true;
+ auto extractObj = requireObject(libObj.value("extract"));
+ for (auto excludeVal : requireArray(extractObj.value("exclude")))
+ {
+ out->m_extractExcludes.append(requireString(excludeVal));
+ }
+ }
+ if (libObj.contains("natives"))
+ {
+ QJsonObject nativesObj = requireObject(libObj.value("natives"));
+ for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
+ {
+ if (!it.value().isString())
+ {
+ qWarning() << filename << "contains an invalid native (skipping)";
+ }
+ OpSys opSys = OpSys_fromString(it.key());
+ if (opSys != Os_Other)
+ {
+ out->m_nativeClassifiers[opSys] = it.value().toString();
+ }
+ }
+ }
+ if (libObj.contains("rules"))
+ {
+ out->applyRules = true;
+ out->m_rules = rulesFromJsonV4(libObj);
+ }
+ if (libObj.contains("downloads"))
+ {
+ out->m_mojangDownloads = libDownloadInfoFromJson(libObj);
+ }
+ return out;
}
QJsonObject MojangVersionFormat::libraryToJson(Library *library)
{
- QJsonObject libRoot;
- libRoot.insert("name", (QString)library->m_name);
- if (!library->m_repositoryURL.isEmpty())
- {
- libRoot.insert("url", library->m_repositoryURL);
- }
- if (library->isNative())
- {
- QJsonObject nativeList;
- auto iter = library->m_nativeClassifiers.begin();
- while (iter != library->m_nativeClassifiers.end())
- {
- nativeList.insert(OpSys_toString(iter.key()), iter.value());
- iter++;
- }
- libRoot.insert("natives", nativeList);
- if (library->m_extractExcludes.size())
- {
- QJsonArray excludes;
- QJsonObject extract;
- for (auto exclude : library->m_extractExcludes)
- {
- excludes.append(exclude);
- }
- extract.insert("exclude", excludes);
- libRoot.insert("extract", extract);
- }
- }
- if (library->m_rules.size())
- {
- QJsonArray allRules;
- for (auto &rule : library->m_rules)
- {
- QJsonObject ruleObj = rule->toJson();
- allRules.append(ruleObj);
- }
- libRoot.insert("rules", allRules);
- }
- if(library->m_mojangDownloads)
- {
- auto downloadsObj = libDownloadInfoToJson(library->m_mojangDownloads);
- libRoot.insert("downloads", downloadsObj);
- }
- return libRoot;
+ QJsonObject libRoot;
+ libRoot.insert("name", (QString)library->m_name);
+ if (!library->m_repositoryURL.isEmpty())
+ {
+ libRoot.insert("url", library->m_repositoryURL);
+ }
+ if (library->isNative())
+ {
+ QJsonObject nativeList;
+ auto iter = library->m_nativeClassifiers.begin();
+ while (iter != library->m_nativeClassifiers.end())
+ {
+ nativeList.insert(OpSys_toString(iter.key()), iter.value());
+ iter++;
+ }
+ libRoot.insert("natives", nativeList);
+ if (library->m_extractExcludes.size())
+ {
+ QJsonArray excludes;
+ QJsonObject extract;
+ for (auto exclude : library->m_extractExcludes)
+ {
+ excludes.append(exclude);
+ }
+ extract.insert("exclude", excludes);
+ libRoot.insert("extract", extract);
+ }
+ }
+ if (library->m_rules.size())
+ {
+ QJsonArray allRules;
+ for (auto &rule : library->m_rules)
+ {
+ QJsonObject ruleObj = rule->toJson();
+ allRules.append(ruleObj);
+ }
+ libRoot.insert("rules", allRules);
+ }
+ if(library->m_mojangDownloads)
+ {
+ auto downloadsObj = libDownloadInfoToJson(library->m_mojangDownloads);
+ libRoot.insert("downloads", downloadsObj);
+ }
+ return libRoot;
}
diff --git a/api/logic/minecraft/MojangVersionFormat.h b/api/logic/minecraft/MojangVersionFormat.h
index 4e141088..76c529e9 100644
--- a/api/logic/minecraft/MojangVersionFormat.h
+++ b/api/logic/minecraft/MojangVersionFormat.h
@@ -10,16 +10,16 @@ class MULTIMC_LOGIC_EXPORT MojangVersionFormat
{
friend class OneSixVersionFormat;
protected:
- // does not include libraries
- static void readVersionProperties(const QJsonObject& in, VersionFile* out);
- // does not include libraries
- static void writeVersionProperties(const VersionFile* in, QJsonObject& out);
+ // does not include libraries
+ static void readVersionProperties(const QJsonObject& in, VersionFile* out);
+ // does not include libraries
+ static void writeVersionProperties(const VersionFile* in, QJsonObject& out);
public:
- // version files / profile patches
- static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename);
- static QJsonDocument versionFileToJson(const VersionFilePtr &patch);
+ // version files / profile patches
+ static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename);
+ static QJsonDocument versionFileToJson(const VersionFilePtr &patch);
- // libraries
- static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename);
- static QJsonObject libraryToJson(Library *library);
+ // libraries
+ static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename);
+ static QJsonObject libraryToJson(Library *library);
};
diff --git a/api/logic/minecraft/MojangVersionFormat_test.cpp b/api/logic/minecraft/MojangVersionFormat_test.cpp
index 9c8482ce..9d095340 100644
--- a/api/logic/minecraft/MojangVersionFormat_test.cpp
+++ b/api/logic/minecraft/MojangVersionFormat_test.cpp
@@ -6,47 +6,47 @@
class MojangVersionFormatTest : public QObject
{
- Q_OBJECT
-
- static QJsonDocument readJson(const char *file)
- {
- auto path = QFINDTESTDATA(file);
- QFile jsonFile(path);
- jsonFile.open(QIODevice::ReadOnly);
- auto data = jsonFile.readAll();
- jsonFile.close();
- return QJsonDocument::fromJson(data);
- }
- static void writeJson(const char *file, QJsonDocument doc)
- {
- QFile jsonFile(file);
- jsonFile.open(QIODevice::WriteOnly | QIODevice::Text);
- auto data = doc.toJson(QJsonDocument::Indented);
- qDebug() << QString::fromUtf8(data);
- jsonFile.write(data);
- jsonFile.close();
- }
+ Q_OBJECT
+
+ static QJsonDocument readJson(const char *file)
+ {
+ auto path = QFINDTESTDATA(file);
+ QFile jsonFile(path);
+ jsonFile.open(QIODevice::ReadOnly);
+ auto data = jsonFile.readAll();
+ jsonFile.close();
+ return QJsonDocument::fromJson(data);
+ }
+ static void writeJson(const char *file, QJsonDocument doc)
+ {
+ QFile jsonFile(file);
+ jsonFile.open(QIODevice::WriteOnly | QIODevice::Text);
+ auto data = doc.toJson(QJsonDocument::Indented);
+ qDebug() << QString::fromUtf8(data);
+ jsonFile.write(data);
+ jsonFile.close();
+ }
private
slots:
- void test_Through_Simple()
- {
- QJsonDocument doc = readJson("data/1.9-simple.json");
- auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9-simple.json");
- auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
- writeJson("1.9-simple-passthorugh.json", doc2);
-
- QCOMPARE(doc.toJson(), doc2.toJson());
- }
-
- void test_Through()
- {
- QJsonDocument doc = readJson("data/1.9.json");
- auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9.json");
- auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
- writeJson("1.9-passthorugh.json", doc2);
- QCOMPARE(doc.toJson(), doc2.toJson());
- }
+ void test_Through_Simple()
+ {
+ QJsonDocument doc = readJson("data/1.9-simple.json");
+ auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9-simple.json");
+ auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
+ writeJson("1.9-simple-passthorugh.json", doc2);
+
+ QCOMPARE(doc.toJson(), doc2.toJson());
+ }
+
+ void test_Through()
+ {
+ QJsonDocument doc = readJson("data/1.9.json");
+ auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9.json");
+ auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
+ writeJson("1.9-passthorugh.json", doc2);
+ QCOMPARE(doc.toJson(), doc2.toJson());
+ }
};
QTEST_GUILESS_MAIN(MojangVersionFormatTest)
diff --git a/api/logic/minecraft/OneSixVersionFormat.cpp b/api/logic/minecraft/OneSixVersionFormat.cpp
index f7ab25b3..6f3b926b 100644
--- a/api/logic/minecraft/OneSixVersionFormat.cpp
+++ b/api/logic/minecraft/OneSixVersionFormat.cpp
@@ -7,366 +7,366 @@ using namespace Json;
static void readString(const QJsonObject &root, const QString &key, QString &variable)
{
- if (root.contains(key))
- {
- variable = requireString(root.value(key));
- }
+ if (root.contains(key))
+ {
+ variable = requireString(root.value(key));
+ }
}
LibraryPtr OneSixVersionFormat::libraryFromJson(const QJsonObject &libObj, const QString &filename)
{
- LibraryPtr out = MojangVersionFormat::libraryFromJson(libObj, filename);
- readString(libObj, "MMC-hint", out->m_hint);
- readString(libObj, "MMC-absulute_url", out->m_absoluteURL);
- readString(libObj, "MMC-absoluteUrl", out->m_absoluteURL);
- readString(libObj, "MMC-filename", out->m_filename);
- readString(libObj, "MMC-displayname", out->m_displayname);
- return out;
+ LibraryPtr out = MojangVersionFormat::libraryFromJson(libObj, filename);
+ readString(libObj, "MMC-hint", out->m_hint);
+ readString(libObj, "MMC-absulute_url", out->m_absoluteURL);
+ readString(libObj, "MMC-absoluteUrl", out->m_absoluteURL);
+ readString(libObj, "MMC-filename", out->m_filename);
+ readString(libObj, "MMC-displayname", out->m_displayname);
+ return out;
}
QJsonObject OneSixVersionFormat::libraryToJson(Library *library)
{
- QJsonObject libRoot = MojangVersionFormat::libraryToJson(library);
- if (library->m_absoluteURL.size())
- libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL);
- if (library->m_hint.size())
- libRoot.insert("MMC-hint", library->m_hint);
- if (library->m_filename.size())
- libRoot.insert("MMC-filename", library->m_filename);
- if (library->m_displayname.size())
- libRoot.insert("MMC-displayname", library->m_displayname);
- return libRoot;
+ QJsonObject libRoot = MojangVersionFormat::libraryToJson(library);
+ if (library->m_absoluteURL.size())
+ libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL);
+ if (library->m_hint.size())
+ libRoot.insert("MMC-hint", library->m_hint);
+ if (library->m_filename.size())
+ libRoot.insert("MMC-filename", library->m_filename);
+ if (library->m_displayname.size())
+ libRoot.insert("MMC-displayname", library->m_displayname);
+ return libRoot;
}
VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder)
{
- VersionFilePtr out(new VersionFile());
- if (doc.isEmpty() || doc.isNull())
- {
- throw JSONValidationError(filename + " is empty or null");
- }
- if (!doc.isObject())
- {
- throw JSONValidationError(filename + " is not an object");
- }
-
- QJsonObject root = doc.object();
-
- Meta::MetadataVersion formatVersion = Meta::parseFormatVersion(root, false);
- switch(formatVersion)
- {
- case Meta::MetadataVersion::InitialRelease:
- break;
- case Meta::MetadataVersion::Invalid:
- throw JSONValidationError(filename + " does not contain a recognizable version of the metadata format.");
- }
-
- if (requireOrder)
- {
- if (root.contains("order"))
- {
- out->order = requireInteger(root.value("order"));
- }
- else
- {
- // FIXME: evaluate if we don't want to throw exceptions here instead
- qCritical() << filename << "doesn't contain an order field";
- }
- }
-
- out->name = root.value("name").toString();
-
- if(root.contains("uid"))
- {
- out->uid = root.value("uid").toString();
- }
- else
- {
- out->uid = root.value("fileId").toString();
- }
-
- out->version = root.value("version").toString();
-
- MojangVersionFormat::readVersionProperties(root, out.get());
-
- // added for legacy Minecraft window embedding, TODO: remove
- readString(root, "appletClass", out->appletClass);
-
- if (root.contains("+tweakers"))
- {
- for (auto tweakerVal : requireArray(root.value("+tweakers")))
- {
- out->addTweakers.append(requireString(tweakerVal));
- }
- }
-
- if (root.contains("+traits"))
- {
- for (auto tweakerVal : requireArray(root.value("+traits")))
- {
- out->traits.insert(requireString(tweakerVal));
- }
- }
-
-
- if (root.contains("jarMods"))
- {
- for (auto libVal : requireArray(root.value("jarMods")))
- {
- QJsonObject libObj = requireObject(libVal);
- // parse the jarmod
- auto lib = OneSixVersionFormat::jarModFromJson(libObj, filename);
- // and add to jar mods
- out->jarMods.append(lib);
- }
- }
- else if (root.contains("+jarMods")) // DEPRECATED: old style '+jarMods' are only here for backwards compatibility
- {
- for (auto libVal : requireArray(root.value("+jarMods")))
- {
- QJsonObject libObj = requireObject(libVal);
- // parse the jarmod
- auto lib = OneSixVersionFormat::plusJarModFromJson(libObj, filename, out->name);
- // and add to jar mods
- out->jarMods.append(lib);
- }
- }
-
- if (root.contains("mods"))
- {
- for (auto libVal : requireArray(root.value("mods")))
- {
- QJsonObject libObj = requireObject(libVal);
- // parse the jarmod
- auto lib = OneSixVersionFormat::modFromJson(libObj, filename);
- // and add to jar mods
- out->mods.append(lib);
- }
- }
-
- auto readLibs = [&](const char * which)
- {
- for (auto libVal : requireArray(root.value(which)))
- {
- QJsonObject libObj = requireObject(libVal);
- // parse the library
- auto lib = libraryFromJson(libObj, filename);
- out->libraries.append(lib);
- }
- };
- bool hasPlusLibs = root.contains("+libraries");
- bool hasLibs = root.contains("libraries");
- if (hasPlusLibs && hasLibs)
- {
- out->addProblem(ProblemSeverity::Warning,
- QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported."));
- readLibs("libraries");
- readLibs("+libraries");
- }
- else if (hasLibs)
- {
- readLibs("libraries");
- }
- else if(hasPlusLibs)
- {
- readLibs("+libraries");
- }
-
- // if we have mainJar, just use it
- if(root.contains("mainJar"))
- {
- QJsonObject libObj = requireObject(root, "mainJar");
- out->mainJar = libraryFromJson(libObj, filename);
- }
- // else reconstruct it from downloads and id ... if that's available
- else if(!out->minecraftVersion.isEmpty())
- {
- auto lib = std::make_shared<Library>();
- lib->setRawName(GradleSpecifier(QString("com.mojang:minecraft:%1:client").arg(out->minecraftVersion)));
- // we have a reliable client download, use it.
- if(out->mojangDownloads.contains("client"))
- {
- auto LibDLInfo = std::make_shared<MojangLibraryDownloadInfo>();
- LibDLInfo->artifact = out->mojangDownloads["client"];
- lib->setMojangDownloadInfo(LibDLInfo);
- }
- // we got nothing... guess based on ancient hardcoded Mojang behaviour
- // FIXME: this will eventually break...
- else
- {
- lib->setAbsoluteUrl(URLConstants::getLegacyJarUrl(out->minecraftVersion));
- }
- out->mainJar = lib;
- }
-
- if (root.contains("requires"))
- {
- Meta::parseRequires(root, &out->requires);
- }
- QString dependsOnMinecraftVersion = root.value("mcVersion").toString();
- if(!dependsOnMinecraftVersion.isEmpty())
- {
- Meta::Require mcReq;
- mcReq.uid = "net.minecraft";
- mcReq.equalsVersion = dependsOnMinecraftVersion;
- if (out->requires.count(mcReq) == 0)
- {
- out->requires.insert(mcReq);
- }
- }
- if (root.contains("conflicts"))
- {
- Meta::parseRequires(root, &out->conflicts);
- }
- if (root.contains("volatile"))
- {
- out->m_volatile = requireBoolean(root, "volatile");
- }
-
- /* removed features that shouldn't be used */
- if (root.contains("tweakers"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'"));
- }
- if (root.contains("-libraries"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-libraries'"));
- }
- if (root.contains("-tweakers"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-tweakers'"));
- }
- if (root.contains("-minecraftArguments"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-minecraftArguments'"));
- }
- if (root.contains("+minecraftArguments"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '+minecraftArguments'"));
- }
- return out;
+ VersionFilePtr out(new VersionFile());
+ if (doc.isEmpty() || doc.isNull())
+ {
+ throw JSONValidationError(filename + " is empty or null");
+ }
+ if (!doc.isObject())
+ {
+ throw JSONValidationError(filename + " is not an object");
+ }
+
+ QJsonObject root = doc.object();
+
+ Meta::MetadataVersion formatVersion = Meta::parseFormatVersion(root, false);
+ switch(formatVersion)
+ {
+ case Meta::MetadataVersion::InitialRelease:
+ break;
+ case Meta::MetadataVersion::Invalid:
+ throw JSONValidationError(filename + " does not contain a recognizable version of the metadata format.");
+ }
+
+ if (requireOrder)
+ {
+ if (root.contains("order"))
+ {
+ out->order = requireInteger(root.value("order"));
+ }
+ else
+ {
+ // FIXME: evaluate if we don't want to throw exceptions here instead
+ qCritical() << filename << "doesn't contain an order field";
+ }
+ }
+
+ out->name = root.value("name").toString();
+
+ if(root.contains("uid"))
+ {
+ out->uid = root.value("uid").toString();
+ }
+ else
+ {
+ out->uid = root.value("fileId").toString();
+ }
+
+ out->version = root.value("version").toString();
+
+ MojangVersionFormat::readVersionProperties(root, out.get());
+
+ // added for legacy Minecraft window embedding, TODO: remove
+ readString(root, "appletClass", out->appletClass);
+
+ if (root.contains("+tweakers"))
+ {
+ for (auto tweakerVal : requireArray(root.value("+tweakers")))
+ {
+ out->addTweakers.append(requireString(tweakerVal));
+ }
+ }
+
+ if (root.contains("+traits"))
+ {
+ for (auto tweakerVal : requireArray(root.value("+traits")))
+ {
+ out->traits.insert(requireString(tweakerVal));
+ }
+ }
+
+
+ if (root.contains("jarMods"))
+ {
+ for (auto libVal : requireArray(root.value("jarMods")))
+ {
+ QJsonObject libObj = requireObject(libVal);
+ // parse the jarmod
+ auto lib = OneSixVersionFormat::jarModFromJson(libObj, filename);
+ // and add to jar mods
+ out->jarMods.append(lib);
+ }
+ }
+ else if (root.contains("+jarMods")) // DEPRECATED: old style '+jarMods' are only here for backwards compatibility
+ {
+ for (auto libVal : requireArray(root.value("+jarMods")))
+ {
+ QJsonObject libObj = requireObject(libVal);
+ // parse the jarmod
+ auto lib = OneSixVersionFormat::plusJarModFromJson(libObj, filename, out->name);
+ // and add to jar mods
+ out->jarMods.append(lib);
+ }
+ }
+
+ if (root.contains("mods"))
+ {
+ for (auto libVal : requireArray(root.value("mods")))
+ {
+ QJsonObject libObj = requireObject(libVal);
+ // parse the jarmod
+ auto lib = OneSixVersionFormat::modFromJson(libObj, filename);
+ // and add to jar mods
+ out->mods.append(lib);
+ }
+ }
+
+ auto readLibs = [&](const char * which)
+ {
+ for (auto libVal : requireArray(root.value(which)))
+ {
+ QJsonObject libObj = requireObject(libVal);
+ // parse the library
+ auto lib = libraryFromJson(libObj, filename);
+ out->libraries.append(lib);
+ }
+ };
+ bool hasPlusLibs = root.contains("+libraries");
+ bool hasLibs = root.contains("libraries");
+ if (hasPlusLibs && hasLibs)
+ {
+ out->addProblem(ProblemSeverity::Warning,
+ QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported."));
+ readLibs("libraries");
+ readLibs("+libraries");
+ }
+ else if (hasLibs)
+ {
+ readLibs("libraries");
+ }
+ else if(hasPlusLibs)
+ {
+ readLibs("+libraries");
+ }
+
+ // if we have mainJar, just use it
+ if(root.contains("mainJar"))
+ {
+ QJsonObject libObj = requireObject(root, "mainJar");
+ out->mainJar = libraryFromJson(libObj, filename);
+ }
+ // else reconstruct it from downloads and id ... if that's available
+ else if(!out->minecraftVersion.isEmpty())
+ {
+ auto lib = std::make_shared<Library>();
+ lib->setRawName(GradleSpecifier(QString("com.mojang:minecraft:%1:client").arg(out->minecraftVersion)));
+ // we have a reliable client download, use it.
+ if(out->mojangDownloads.contains("client"))
+ {
+ auto LibDLInfo = std::make_shared<MojangLibraryDownloadInfo>();
+ LibDLInfo->artifact = out->mojangDownloads["client"];
+ lib->setMojangDownloadInfo(LibDLInfo);
+ }
+ // we got nothing... guess based on ancient hardcoded Mojang behaviour
+ // FIXME: this will eventually break...
+ else
+ {
+ lib->setAbsoluteUrl(URLConstants::getLegacyJarUrl(out->minecraftVersion));
+ }
+ out->mainJar = lib;
+ }
+
+ if (root.contains("requires"))
+ {
+ Meta::parseRequires(root, &out->requires);
+ }
+ QString dependsOnMinecraftVersion = root.value("mcVersion").toString();
+ if(!dependsOnMinecraftVersion.isEmpty())
+ {
+ Meta::Require mcReq;
+ mcReq.uid = "net.minecraft";
+ mcReq.equalsVersion = dependsOnMinecraftVersion;
+ if (out->requires.count(mcReq) == 0)
+ {
+ out->requires.insert(mcReq);
+ }
+ }
+ if (root.contains("conflicts"))
+ {
+ Meta::parseRequires(root, &out->conflicts);
+ }
+ if (root.contains("volatile"))
+ {
+ out->m_volatile = requireBoolean(root, "volatile");
+ }
+
+ /* removed features that shouldn't be used */
+ if (root.contains("tweakers"))
+ {
+ out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'"));
+ }
+ if (root.contains("-libraries"))
+ {
+ out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-libraries'"));
+ }
+ if (root.contains("-tweakers"))
+ {
+ out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-tweakers'"));
+ }
+ if (root.contains("-minecraftArguments"))
+ {
+ out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-minecraftArguments'"));
+ }
+ if (root.contains("+minecraftArguments"))
+ {
+ out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '+minecraftArguments'"));
+ }
+ return out;
}
QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch)
{
- QJsonObject root;
- writeString(root, "name", patch->name);
-
- writeString(root, "uid", patch->uid);
-
- writeString(root, "version", patch->version);
-
- Meta::serializeFormatVersion(root, Meta::MetadataVersion::InitialRelease);
-
- MojangVersionFormat::writeVersionProperties(patch.get(), root);
-
- if(patch->mainJar)
- {
- root.insert("mainJar", libraryToJson(patch->mainJar.get()));
- }
- writeString(root, "appletClass", patch->appletClass);
- writeStringList(root, "+tweakers", patch->addTweakers);
- writeStringList(root, "+traits", patch->traits.toList());
- if (!patch->libraries.isEmpty())
- {
- QJsonArray array;
- for (auto value: patch->libraries)
- {
- array.append(OneSixVersionFormat::libraryToJson(value.get()));
- }
- root.insert("libraries", array);
- }
- if (!patch->jarMods.isEmpty())
- {
- QJsonArray array;
- for (auto value: patch->jarMods)
- {
- array.append(OneSixVersionFormat::jarModtoJson(value.get()));
- }
- root.insert("jarMods", array);
- }
- if (!patch->mods.isEmpty())
- {
- QJsonArray array;
- for (auto value: patch->jarMods)
- {
- array.append(OneSixVersionFormat::modtoJson(value.get()));
- }
- root.insert("mods", array);
- }
- if(!patch->requires.empty())
- {
- Meta::serializeRequires(root, &patch->requires, "requires");
- }
- if(!patch->conflicts.empty())
- {
- Meta::serializeRequires(root, &patch->conflicts, "conflicts");
- }
- if(patch->m_volatile)
- {
- root.insert("volatile", true);
- }
- // write the contents to a json document.
- {
- QJsonDocument out;
- out.setObject(root);
- return out;
- }
+ QJsonObject root;
+ writeString(root, "name", patch->name);
+
+ writeString(root, "uid", patch->uid);
+
+ writeString(root, "version", patch->version);
+
+ Meta::serializeFormatVersion(root, Meta::MetadataVersion::InitialRelease);
+
+ MojangVersionFormat::writeVersionProperties(patch.get(), root);
+
+ if(patch->mainJar)
+ {
+ root.insert("mainJar", libraryToJson(patch->mainJar.get()));
+ }
+ writeString(root, "appletClass", patch->appletClass);
+ writeStringList(root, "+tweakers", patch->addTweakers);
+ writeStringList(root, "+traits", patch->traits.toList());
+ if (!patch->libraries.isEmpty())
+ {
+ QJsonArray array;
+ for (auto value: patch->libraries)
+ {
+ array.append(OneSixVersionFormat::libraryToJson(value.get()));
+ }
+ root.insert("libraries", array);
+ }
+ if (!patch->jarMods.isEmpty())
+ {
+ QJsonArray array;
+ for (auto value: patch->jarMods)
+ {
+ array.append(OneSixVersionFormat::jarModtoJson(value.get()));
+ }
+ root.insert("jarMods", array);
+ }
+ if (!patch->mods.isEmpty())
+ {
+ QJsonArray array;
+ for (auto value: patch->jarMods)
+ {
+ array.append(OneSixVersionFormat::modtoJson(value.get()));
+ }
+ root.insert("mods", array);
+ }
+ if(!patch->requires.empty())
+ {
+ Meta::serializeRequires(root, &patch->requires, "requires");
+ }
+ if(!patch->conflicts.empty())
+ {
+ Meta::serializeRequires(root, &patch->conflicts, "conflicts");
+ }
+ if(patch->m_volatile)
+ {
+ root.insert("volatile", true);
+ }
+ // write the contents to a json document.
+ {
+ QJsonDocument out;
+ out.setObject(root);
+ return out;
+ }
}
LibraryPtr OneSixVersionFormat::plusJarModFromJson(const QJsonObject &libObj, const QString &filename, const QString &originalName)
{
- LibraryPtr out(new Library());
- if (!libObj.contains("name"))
- {
- throw JSONValidationError(filename +
- "contains a jarmod that doesn't have a 'name' field");
- }
-
- // just make up something unique on the spot for the library name.
- auto uuid = QUuid::createUuid();
- QString id = uuid.toString().remove('{').remove('}');
- out->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1"));
-
- // filename override is the old name
- out->setFilename(libObj.value("name").toString());
-
- // it needs to be local, it is stored in the instance jarmods folder
- out->setHint("local");
-
- // read the original name if present - some versions did not set it
- // it is the original jar mod filename before it got renamed at the point of addition
- auto displayName = libObj.value("originalName").toString();
- if(displayName.isEmpty())
- {
- auto fixed = originalName;
- fixed.remove(" (jar mod)");
- out->setDisplayName(fixed);
- }
- else
- {
- out->setDisplayName(displayName);
- }
- return out;
+ LibraryPtr out(new Library());
+ if (!libObj.contains("name"))
+ {
+ throw JSONValidationError(filename +
+ "contains a jarmod that doesn't have a 'name' field");
+ }
+
+ // just make up something unique on the spot for the library name.
+ auto uuid = QUuid::createUuid();
+ QString id = uuid.toString().remove('{').remove('}');
+ out->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1"));
+
+ // filename override is the old name
+ out->setFilename(libObj.value("name").toString());
+
+ // it needs to be local, it is stored in the instance jarmods folder
+ out->setHint("local");
+
+ // read the original name if present - some versions did not set it
+ // it is the original jar mod filename before it got renamed at the point of addition
+ auto displayName = libObj.value("originalName").toString();
+ if(displayName.isEmpty())
+ {
+ auto fixed = originalName;
+ fixed.remove(" (jar mod)");
+ out->setDisplayName(fixed);
+ }
+ else
+ {
+ out->setDisplayName(displayName);
+ }
+ return out;
}
LibraryPtr OneSixVersionFormat::jarModFromJson(const QJsonObject& libObj, const QString& filename)
{
- return libraryFromJson(libObj, filename);
+ return libraryFromJson(libObj, filename);
}
QJsonObject OneSixVersionFormat::jarModtoJson(Library *jarmod)
{
- return libraryToJson(jarmod);
+ return libraryToJson(jarmod);
}
LibraryPtr OneSixVersionFormat::modFromJson(const QJsonObject& libObj, const QString& filename)
{
- return libraryFromJson(libObj, filename);
+ return libraryFromJson(libObj, filename);
}
QJsonObject OneSixVersionFormat::modtoJson(Library *jarmod)
{
- return libraryToJson(jarmod);
+ return libraryToJson(jarmod);
}
diff --git a/api/logic/minecraft/OneSixVersionFormat.h b/api/logic/minecraft/OneSixVersionFormat.h
index 8b782db0..6bbebca0 100644
--- a/api/logic/minecraft/OneSixVersionFormat.h
+++ b/api/logic/minecraft/OneSixVersionFormat.h
@@ -8,22 +8,22 @@
class OneSixVersionFormat
{
public:
- // version files / profile patches
- static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder);
- static QJsonDocument versionFileToJson(const VersionFilePtr &patch);
+ // version files / profile patches
+ static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder);
+ static QJsonDocument versionFileToJson(const VersionFilePtr &patch);
- // libraries
- static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename);
- static QJsonObject libraryToJson(Library *library);
+ // libraries
+ static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename);
+ static QJsonObject libraryToJson(Library *library);
- // DEPRECATED: old 'plus' jar mods generated by the application
- static LibraryPtr plusJarModFromJson(const QJsonObject &libObj, const QString &filename, const QString &originalName);
+ // DEPRECATED: old 'plus' jar mods generated by the application
+ static LibraryPtr plusJarModFromJson(const QJsonObject &libObj, const QString &filename, const QString &originalName);
- // new jar mods derived from libraries
- static LibraryPtr jarModFromJson(const QJsonObject &libObj, const QString &filename);
- static QJsonObject jarModtoJson(Library * jarmod);
+ // new jar mods derived from libraries
+ static LibraryPtr jarModFromJson(const QJsonObject &libObj, const QString &filename);
+ static QJsonObject jarModtoJson(Library * jarmod);
- // mods, also derived from libraries
- static LibraryPtr modFromJson(const QJsonObject &libObj, const QString &filename);
- static QJsonObject modtoJson(Library * jarmod);
+ // mods, also derived from libraries
+ static LibraryPtr modFromJson(const QJsonObject &libObj, const QString &filename);
+ static QJsonObject modtoJson(Library * jarmod);
};
diff --git a/api/logic/minecraft/OpSys.cpp b/api/logic/minecraft/OpSys.cpp
index 2165fa7f..5e1e6c2b 100644
--- a/api/logic/minecraft/OpSys.cpp
+++ b/api/logic/minecraft/OpSys.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,26 +17,26 @@
OpSys OpSys_fromString(QString name)
{
- if (name == "linux")
- return Os_Linux;
- if (name == "windows")
- return Os_Windows;
- if (name == "osx")
- return Os_OSX;
- return Os_Other;
+ if (name == "linux")
+ return Os_Linux;
+ if (name == "windows")
+ return Os_Windows;
+ if (name == "osx")
+ return Os_OSX;
+ return Os_Other;
}
QString OpSys_toString(OpSys name)
{
- switch (name)
- {
- case Os_Linux:
- return "linux";
- case Os_OSX:
- return "osx";
- case Os_Windows:
- return "windows";
- default:
- return "other";
- }
+ switch (name)
+ {
+ case Os_Linux:
+ return "linux";
+ case Os_OSX:
+ return "osx";
+ case Os_Windows:
+ return "windows";
+ default:
+ return "other";
+ }
} \ No newline at end of file
diff --git a/api/logic/minecraft/OpSys.h b/api/logic/minecraft/OpSys.h
index e44b1e07..58852acb 100644
--- a/api/logic/minecraft/OpSys.h
+++ b/api/logic/minecraft/OpSys.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,10 +17,10 @@
#include <QString>
enum OpSys
{
- Os_Windows,
- Os_Linux,
- Os_OSX,
- Os_Other
+ Os_Windows,
+ Os_Linux,
+ Os_OSX,
+ Os_Other
};
OpSys OpSys_fromString(QString);
diff --git a/api/logic/minecraft/ParseUtils.cpp b/api/logic/minecraft/ParseUtils.cpp
index ca188432..c9640e77 100644
--- a/api/logic/minecraft/ParseUtils.cpp
+++ b/api/logic/minecraft/ParseUtils.cpp
@@ -6,29 +6,29 @@
QDateTime timeFromS3Time(QString str)
{
- return QDateTime::fromString(str, Qt::ISODate);
+ return QDateTime::fromString(str, Qt::ISODate);
}
QString timeToS3Time(QDateTime time)
{
- // this all because Qt can't format timestamps right.
- int offsetRaw = time.offsetFromUtc();
- bool negative = offsetRaw < 0;
- int offsetAbs = std::abs(offsetRaw);
+ // this all because Qt can't format timestamps right.
+ int offsetRaw = time.offsetFromUtc();
+ bool negative = offsetRaw < 0;
+ int offsetAbs = std::abs(offsetRaw);
- int offsetSeconds = offsetAbs % 60;
- offsetAbs -= offsetSeconds;
+ int offsetSeconds = offsetAbs % 60;
+ offsetAbs -= offsetSeconds;
- int offsetMinutes = offsetAbs % 3600;
- offsetAbs -= offsetMinutes;
- offsetMinutes /= 60;
-
- int offsetHours = offsetAbs / 3600;
+ int offsetMinutes = offsetAbs % 3600;
+ offsetAbs -= offsetMinutes;
+ offsetMinutes /= 60;
+
+ int offsetHours = offsetAbs / 3600;
- QString raw = time.toString("yyyy-MM-ddTHH:mm:ss");
- raw += (negative ? QChar('-') : QChar('+'));
- raw += QString("%1").arg(offsetHours, 2, 10, QChar('0'));
- raw += ":";
- raw += QString("%1").arg(offsetMinutes, 2, 10, QChar('0'));
- return raw;
+ QString raw = time.toString("yyyy-MM-ddTHH:mm:ss");
+ raw += (negative ? QChar('-') : QChar('+'));
+ raw += QString("%1").arg(offsetHours, 2, 10, QChar('0'));
+ raw += ":";
+ raw += QString("%1").arg(offsetMinutes, 2, 10, QChar('0'));
+ return raw;
}
diff --git a/api/logic/minecraft/ParseUtils_test.cpp b/api/logic/minecraft/ParseUtils_test.cpp
index 1ce96248..fcc137e5 100644
--- a/api/logic/minecraft/ParseUtils_test.cpp
+++ b/api/logic/minecraft/ParseUtils_test.cpp
@@ -5,37 +5,37 @@
class ParseUtilsTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
private
slots:
- void test_Through_data()
- {
- QTest::addColumn<QString>("timestamp");
- const char * timestamps[] =
- {
- "2016-02-29T13:49:54+01:00",
- "2016-02-26T15:21:11+00:01",
- "2016-02-24T15:52:36+01:13",
- "2016-02-18T17:41:00+00:00",
- "2016-02-17T15:23:19+00:00",
- "2016-02-16T15:22:39+09:22",
- "2016-02-10T15:06:41+00:00",
- "2016-02-04T15:28:02-05:33"
- };
- for(unsigned i = 0; i < (sizeof(timestamps) / sizeof(const char *)); i++)
- {
- QTest::newRow(timestamps[i]) << QString(timestamps[i]);
- }
- }
- void test_Through()
- {
- QFETCH(QString, timestamp);
-
- auto time_parsed = timeFromS3Time(timestamp);
- auto time_serialized = timeToS3Time(time_parsed);
-
- QCOMPARE(time_serialized, timestamp);
- }
+ void test_Through_data()
+ {
+ QTest::addColumn<QString>("timestamp");
+ const char * timestamps[] =
+ {
+ "2016-02-29T13:49:54+01:00",
+ "2016-02-26T15:21:11+00:01",
+ "2016-02-24T15:52:36+01:13",
+ "2016-02-18T17:41:00+00:00",
+ "2016-02-17T15:23:19+00:00",
+ "2016-02-16T15:22:39+09:22",
+ "2016-02-10T15:06:41+00:00",
+ "2016-02-04T15:28:02-05:33"
+ };
+ for(unsigned i = 0; i < (sizeof(timestamps) / sizeof(const char *)); i++)
+ {
+ QTest::newRow(timestamps[i]) << QString(timestamps[i]);
+ }
+ }
+ void test_Through()
+ {
+ QFETCH(QString, timestamp);
+
+ auto time_parsed = timeFromS3Time(timestamp);
+ auto time_serialized = timeToS3Time(time_parsed);
+
+ QCOMPARE(time_serialized, timestamp);
+ }
};
diff --git a/api/logic/minecraft/ProfileUtils.cpp b/api/logic/minecraft/ProfileUtils.cpp
index a6d2028d..8ca24cc8 100644
--- a/api/logic/minecraft/ProfileUtils.cpp
+++ b/api/logic/minecraft/ProfileUtils.cpp
@@ -16,163 +16,163 @@ static const int currentOrderFileVersion = 1;
bool readOverrideOrders(QString path, PatchOrder &order)
{
- QFile orderFile(path);
- if (!orderFile.exists())
- {
- qWarning() << "Order file doesn't exist. Ignoring.";
- return false;
- }
- if (!orderFile.open(QFile::ReadOnly))
- {
- qCritical() << "Couldn't open" << orderFile.fileName()
- << " for reading:" << orderFile.errorString();
- qWarning() << "Ignoring overriden order";
- return false;
- }
+ QFile orderFile(path);
+ if (!orderFile.exists())
+ {
+ qWarning() << "Order file doesn't exist. Ignoring.";
+ return false;
+ }
+ if (!orderFile.open(QFile::ReadOnly))
+ {
+ qCritical() << "Couldn't open" << orderFile.fileName()
+ << " for reading:" << orderFile.errorString();
+ qWarning() << "Ignoring overriden order";
+ return false;
+ }
- // and it's valid JSON
- QJsonParseError error;
- QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
- if (error.error != QJsonParseError::NoError)
- {
- qCritical() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString();
- qWarning() << "Ignoring overriden order";
- return false;
- }
+ // and it's valid JSON
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
+ if (error.error != QJsonParseError::NoError)
+ {
+ qCritical() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString();
+ qWarning() << "Ignoring overriden order";
+ return false;
+ }
- // and then read it and process it if all above is true.
- try
- {
- auto obj = Json::requireObject(doc);
- // check order file version.
- auto version = Json::requireInteger(obj.value("version"));
- if (version != currentOrderFileVersion)
- {
- throw JSONValidationError(QObject::tr("Invalid order file version, expected %1")
- .arg(currentOrderFileVersion));
- }
- auto orderArray = Json::requireArray(obj.value("order"));
- for(auto item: orderArray)
- {
- order.append(Json::requireString(item));
- }
- }
- catch (JSONValidationError &err)
- {
- qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
- qWarning() << "Ignoring overriden order";
- order.clear();
- return false;
- }
- return true;
+ // and then read it and process it if all above is true.
+ try
+ {
+ auto obj = Json::requireObject(doc);
+ // check order file version.
+ auto version = Json::requireInteger(obj.value("version"));
+ if (version != currentOrderFileVersion)
+ {
+ throw JSONValidationError(QObject::tr("Invalid order file version, expected %1")
+ .arg(currentOrderFileVersion));
+ }
+ auto orderArray = Json::requireArray(obj.value("order"));
+ for(auto item: orderArray)
+ {
+ order.append(Json::requireString(item));
+ }
+ }
+ catch (const JSONValidationError &err)
+ {
+ qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
+ qWarning() << "Ignoring overriden order";
+ order.clear();
+ return false;
+ }
+ return true;
}
static VersionFilePtr createErrorVersionFile(QString fileId, QString filepath, QString error)
{
- auto outError = std::make_shared<VersionFile>();
- outError->uid = outError->name = fileId;
- // outError->filename = filepath;
- outError->addProblem(ProblemSeverity::Error, error);
- return outError;
+ auto outError = std::make_shared<VersionFile>();
+ outError->uid = outError->name = fileId;
+ // outError->filename = filepath;
+ outError->addProblem(ProblemSeverity::Error, error);
+ return outError;
}
static VersionFilePtr guardedParseJson(const QJsonDocument & doc,const QString &fileId,const QString &filepath,const bool &requireOrder)
{
- try
- {
- return OneSixVersionFormat::versionFileFromJson(doc, filepath, requireOrder);
- }
- catch (Exception & e)
- {
- return createErrorVersionFile(fileId, filepath, e.cause());
- }
+ try
+ {
+ return OneSixVersionFormat::versionFileFromJson(doc, filepath, requireOrder);
+ }
+ catch (const Exception &e)
+ {
+ return createErrorVersionFile(fileId, filepath, e.cause());
+ }
}
VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder)
{
- QFile file(fileInfo.absoluteFilePath());
- if (!file.open(QFile::ReadOnly))
- {
- auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString());
- return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr);
- }
- QJsonParseError error;
- auto data = file.readAll();
- QJsonDocument doc = QJsonDocument::fromJson(data, &error);
- file.close();
- if (error.error != QJsonParseError::NoError)
- {
- int line = 1;
- int column = 0;
- for(int i = 0; i < error.offset; i++)
- {
- if(data[i] == '\n')
- {
- line++;
- column = 0;
- continue;
- }
- column++;
- }
- auto errorStr = QObject::tr("Unable to process the version file %1: %2 at line %3 column %4.")
- .arg(fileInfo.fileName(), error.errorString())
- .arg(line).arg(column);
- return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr);
- }
- return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder);
+ QFile file(fileInfo.absoluteFilePath());
+ if (!file.open(QFile::ReadOnly))
+ {
+ auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString());
+ return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr);
+ }
+ QJsonParseError error;
+ auto data = file.readAll();
+ QJsonDocument doc = QJsonDocument::fromJson(data, &error);
+ file.close();
+ if (error.error != QJsonParseError::NoError)
+ {
+ int line = 1;
+ int column = 0;
+ for(int i = 0; i < error.offset; i++)
+ {
+ if(data[i] == '\n')
+ {
+ line++;
+ column = 0;
+ continue;
+ }
+ column++;
+ }
+ auto errorStr = QObject::tr("Unable to process the version file %1: %2 at line %3 column %4.")
+ .arg(fileInfo.fileName(), error.errorString())
+ .arg(line).arg(column);
+ return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr);
+ }
+ return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder);
}
bool saveJsonFile(const QJsonDocument doc, const QString & filename)
{
- auto data = doc.toJson();
- QSaveFile jsonFile(filename);
- if(!jsonFile.open(QIODevice::WriteOnly))
- {
- jsonFile.cancelWriting();
- qWarning() << "Couldn't open" << filename << "for writing";
- return false;
- }
- jsonFile.write(data);
- if(!jsonFile.commit())
- {
- qWarning() << "Couldn't save" << filename;
- return false;
- }
- return true;
+ auto data = doc.toJson();
+ QSaveFile jsonFile(filename);
+ if(!jsonFile.open(QIODevice::WriteOnly))
+ {
+ jsonFile.cancelWriting();
+ qWarning() << "Couldn't open" << filename << "for writing";
+ return false;
+ }
+ jsonFile.write(data);
+ if(!jsonFile.commit())
+ {
+ qWarning() << "Couldn't save" << filename;
+ return false;
+ }
+ return true;
}
VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo)
{
- QFile file(fileInfo.absoluteFilePath());
- if (!file.open(QFile::ReadOnly))
- {
- auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString());
- return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr);
- }
- QJsonDocument doc = QJsonDocument::fromBinaryData(file.readAll());
- file.close();
- if (doc.isNull())
- {
- file.remove();
- throw JSONValidationError(QObject::tr("Unable to process the version file %1.").arg(fileInfo.fileName()));
- }
- return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), false);
+ QFile file(fileInfo.absoluteFilePath());
+ if (!file.open(QFile::ReadOnly))
+ {
+ auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString());
+ return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr);
+ }
+ QJsonDocument doc = QJsonDocument::fromBinaryData(file.readAll());
+ file.close();
+ if (doc.isNull())
+ {
+ file.remove();
+ throw JSONValidationError(QObject::tr("Unable to process the version file %1.").arg(fileInfo.fileName()));
+ }
+ return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), false);
}
void removeLwjglFromPatch(VersionFilePtr patch)
{
- auto filter = [](QList<LibraryPtr>& libs)
- {
- QList<LibraryPtr> filteredLibs;
- for (auto lib : libs)
- {
- if (!g_VersionFilterData.lwjglWhitelist.contains(lib->artifactPrefix()))
- {
- filteredLibs.append(lib);
- }
- }
- libs = filteredLibs;
- };
- filter(patch->libraries);
+ auto filter = [](QList<LibraryPtr>& libs)
+ {
+ QList<LibraryPtr> filteredLibs;
+ for (auto lib : libs)
+ {
+ if (!g_VersionFilterData.lwjglWhitelist.contains(lib->artifactPrefix()))
+ {
+ filteredLibs.append(lib);
+ }
+ }
+ libs = filteredLibs;
+ };
+ filter(patch->libraries);
}
}
diff --git a/api/logic/minecraft/Rule.cpp b/api/logic/minecraft/Rule.cpp
index 43d673c2..cd10504f 100644
--- a/api/logic/minecraft/Rule.cpp
+++ b/api/logic/minecraft/Rule.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,74 +20,74 @@
RuleAction RuleAction_fromString(QString name)
{
- if (name == "allow")
- return Allow;
- if (name == "disallow")
- return Disallow;
- return Defer;
+ if (name == "allow")
+ return Allow;
+ if (name == "disallow")
+ return Disallow;
+ return Defer;
}
QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules)
{
- QList<std::shared_ptr<Rule>> rules;
- auto rulesVal = objectWithRules.value("rules");
- if (!rulesVal.isArray())
- return rules;
+ QList<std::shared_ptr<Rule>> rules;
+ auto rulesVal = objectWithRules.value("rules");
+ if (!rulesVal.isArray())
+ return rules;
- QJsonArray ruleList = rulesVal.toArray();
- for (auto ruleVal : ruleList)
- {
- std::shared_ptr<Rule> rule;
- if (!ruleVal.isObject())
- continue;
- auto ruleObj = ruleVal.toObject();
- auto actionVal = ruleObj.value("action");
- if (!actionVal.isString())
- continue;
- auto action = RuleAction_fromString(actionVal.toString());
- if (action == Defer)
- continue;
+ QJsonArray ruleList = rulesVal.toArray();
+ for (auto ruleVal : ruleList)
+ {
+ std::shared_ptr<Rule> rule;
+ if (!ruleVal.isObject())
+ continue;
+ auto ruleObj = ruleVal.toObject();
+ auto actionVal = ruleObj.value("action");
+ if (!actionVal.isString())
+ continue;
+ auto action = RuleAction_fromString(actionVal.toString());
+ if (action == Defer)
+ continue;
- auto osVal = ruleObj.value("os");
- if (!osVal.isObject())
- {
- // add a new implicit action rule
- rules.append(ImplicitRule::create(action));
- continue;
- }
+ auto osVal = ruleObj.value("os");
+ if (!osVal.isObject())
+ {
+ // add a new implicit action rule
+ rules.append(ImplicitRule::create(action));
+ continue;
+ }
- auto osObj = osVal.toObject();
- auto osNameVal = osObj.value("name");
- if (!osNameVal.isString())
- continue;
- OpSys requiredOs = OpSys_fromString(osNameVal.toString());
- QString versionRegex = osObj.value("version").toString();
- // add a new OS rule
- rules.append(OsRule::create(action, requiredOs, versionRegex));
- }
+ auto osObj = osVal.toObject();
+ auto osNameVal = osObj.value("name");
+ if (!osNameVal.isString())
+ continue;
+ OpSys requiredOs = OpSys_fromString(osNameVal.toString());
+ QString versionRegex = osObj.value("version").toString();
+ // add a new OS rule
+ rules.append(OsRule::create(action, requiredOs, versionRegex));
+ }
return rules;
}
QJsonObject ImplicitRule::toJson()
{
- QJsonObject ruleObj;
- ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow"));
- return ruleObj;
+ QJsonObject ruleObj;
+ ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow"));
+ return ruleObj;
}
QJsonObject OsRule::toJson()
{
- QJsonObject ruleObj;
- ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow"));
- QJsonObject osObj;
- {
- osObj.insert("name", OpSys_toString(m_system));
- if(!m_version_regexp.isEmpty())
- {
- osObj.insert("version", m_version_regexp);
- }
- }
- ruleObj.insert("os", osObj);
- return ruleObj;
+ QJsonObject ruleObj;
+ ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow"));
+ QJsonObject osObj;
+ {
+ osObj.insert("name", OpSys_toString(m_system));
+ if(!m_version_regexp.isEmpty())
+ {
+ osObj.insert("version", m_version_regexp);
+ }
+ }
+ ruleObj.insert("os", osObj);
+ return ruleObj;
}
diff --git a/api/logic/minecraft/Rule.h b/api/logic/minecraft/Rule.h
index dc1eee0b..ef10a6c3 100644
--- a/api/logic/minecraft/Rule.h
+++ b/api/logic/minecraft/Rule.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,9 +26,9 @@ class Rule;
enum RuleAction
{
- Allow,
- Disallow,
- Defer
+ Allow,
+ Disallow,
+ Defer
};
QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules);
@@ -36,66 +36,66 @@ QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules)
class Rule
{
protected:
- RuleAction m_result;
- virtual bool applies(const Library *parent) = 0;
+ RuleAction m_result;
+ virtual bool applies(const Library *parent) = 0;
public:
- Rule(RuleAction result) : m_result(result)
- {
- }
- virtual ~Rule() {};
- virtual QJsonObject toJson() = 0;
- RuleAction apply(const Library *parent)
- {
- if (applies(parent))
- return m_result;
- else
- return Defer;
- }
+ Rule(RuleAction result) : m_result(result)
+ {
+ }
+ virtual ~Rule() {};
+ virtual QJsonObject toJson() = 0;
+ RuleAction apply(const Library *parent)
+ {
+ if (applies(parent))
+ return m_result;
+ else
+ return Defer;
+ }
};
class OsRule : public Rule
{
private:
- // the OS
- OpSys m_system;
- // the OS version regexp
- QString m_version_regexp;
+ // the OS
+ OpSys m_system;
+ // the OS version regexp
+ QString m_version_regexp;
protected:
- virtual bool applies(const Library *)
- {
- return (m_system == currentSystem);
- }
- OsRule(RuleAction result, OpSys system, QString version_regexp)
- : Rule(result), m_system(system), m_version_regexp(version_regexp)
- {
- }
+ virtual bool applies(const Library *)
+ {
+ return (m_system == currentSystem);
+ }
+ OsRule(RuleAction result, OpSys system, QString version_regexp)
+ : Rule(result), m_system(system), m_version_regexp(version_regexp)
+ {
+ }
public:
- virtual QJsonObject toJson();
- static std::shared_ptr<OsRule> create(RuleAction result, OpSys system,
- QString version_regexp)
- {
- return std::shared_ptr<OsRule>(new OsRule(result, system, version_regexp));
- }
+ virtual QJsonObject toJson();
+ static std::shared_ptr<OsRule> create(RuleAction result, OpSys system,
+ QString version_regexp)
+ {
+ return std::shared_ptr<OsRule>(new OsRule(result, system, version_regexp));
+ }
};
class ImplicitRule : public Rule
{
protected:
- virtual bool applies(const Library *)
- {
- return true;
- }
- ImplicitRule(RuleAction result) : Rule(result)
- {
- }
+ virtual bool applies(const Library *)
+ {
+ return true;
+ }
+ ImplicitRule(RuleAction result) : Rule(result)
+ {
+ }
public:
- virtual QJsonObject toJson();
- static std::shared_ptr<ImplicitRule> create(RuleAction result)
- {
- return std::shared_ptr<ImplicitRule>(new ImplicitRule(result));
- }
+ virtual QJsonObject toJson();
+ static std::shared_ptr<ImplicitRule> create(RuleAction result)
+ {
+ return std::shared_ptr<ImplicitRule>(new ImplicitRule(result));
+ }
};
diff --git a/api/logic/minecraft/SkinUpload.cpp b/api/logic/minecraft/SkinUpload.cpp
index bd246139..83bdf592 100644
--- a/api/logic/minecraft/SkinUpload.cpp
+++ b/api/logic/minecraft/SkinUpload.cpp
@@ -4,66 +4,66 @@
#include <Env.h>
QByteArray getModelString(SkinUpload::Model model) {
- switch (model) {
- case SkinUpload::STEVE:
- return "";
- case SkinUpload::ALEX:
- return "slim";
- default:
- qDebug() << "Unknown skin type!";
- return "";
- }
+ switch (model) {
+ case SkinUpload::STEVE:
+ return "";
+ case SkinUpload::ALEX:
+ return "slim";
+ default:
+ qDebug() << "Unknown skin type!";
+ return "";
+ }
}
SkinUpload::SkinUpload(QObject *parent, AuthSessionPtr session, QByteArray skin, SkinUpload::Model model)
- : Task(parent), m_model(model), m_skin(skin), m_session(session)
+ : Task(parent), m_model(model), m_skin(skin), m_session(session)
{
}
void SkinUpload::executeTask()
{
- QNetworkRequest request(QUrl(QString("https://api.mojang.com/user/profile/%1/skin").arg(m_session->uuid)));
- request.setRawHeader("Authorization", QString("Bearer: %1").arg(m_session->access_token).toLocal8Bit());
+ QNetworkRequest request(QUrl(QString("https://api.mojang.com/user/profile/%1/skin").arg(m_session->uuid)));
+ request.setRawHeader("Authorization", QString("Bearer: %1").arg(m_session->access_token).toLocal8Bit());
- QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
+ QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
- QHttpPart model;
- model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"model\""));
- model.setBody(getModelString(m_model));
+ QHttpPart model;
+ model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"model\""));
+ model.setBody(getModelString(m_model));
- QHttpPart skin;
- skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png"));
- skin.setHeader(QNetworkRequest::ContentDispositionHeader,
- QVariant("form-data; name=\"file\"; filename=\"skin.png\""));
- skin.setBody(m_skin);
+ QHttpPart skin;
+ skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png"));
+ skin.setHeader(QNetworkRequest::ContentDispositionHeader,
+ QVariant("form-data; name=\"file\"; filename=\"skin.png\""));
+ skin.setBody(m_skin);
- multiPart->append(model);
- multiPart->append(skin);
+ multiPart->append(model);
+ multiPart->append(skin);
- QNetworkReply *rep = ENV.qnam().put(request, multiPart);
- m_reply = std::shared_ptr<QNetworkReply>(rep);
+ QNetworkReply *rep = ENV.qnam().put(request, multiPart);
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
- setStatus(tr("Uploading skin"));
- connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
- connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
- connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
+ setStatus(tr("Uploading skin"));
+ connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
}
void SkinUpload::downloadError(QNetworkReply::NetworkError error)
{
- // error happened during download.
- qCritical() << "Network error: " << error;
- emitFailed(m_reply->errorString());
+ // error happened during download.
+ qCritical() << "Network error: " << error;
+ emitFailed(m_reply->errorString());
}
void SkinUpload::downloadFinished()
{
- // if the download failed
- if (m_reply->error() != QNetworkReply::NetworkError::NoError)
- {
- emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
- m_reply.reset();
- return;
- }
- emitSucceeded();
+ // if the download failed
+ if (m_reply->error() != QNetworkReply::NetworkError::NoError)
+ {
+ emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
+ m_reply.reset();
+ return;
+ }
+ emitSucceeded();
}
diff --git a/api/logic/minecraft/SkinUpload.h b/api/logic/minecraft/SkinUpload.h
index 5b331fa9..c77abb03 100644
--- a/api/logic/minecraft/SkinUpload.h
+++ b/api/logic/minecraft/SkinUpload.h
@@ -11,30 +11,29 @@ typedef std::shared_ptr<class SkinUpload> SkinUploadPtr;
class MULTIMC_LOGIC_EXPORT SkinUpload : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- enum Model
- {
- STEVE,
- ALEX
- };
+ enum Model
+ {
+ STEVE,
+ ALEX
+ };
- // Note this class takes ownership of the file.
- SkinUpload(QObject *parent, AuthSessionPtr session, QByteArray skin, Model model = STEVE);
-
- virtual ~SkinUpload() {}
+ // Note this class takes ownership of the file.
+ SkinUpload(QObject *parent, AuthSessionPtr session, QByteArray skin, Model model = STEVE);
+ virtual ~SkinUpload() {}
private:
- Model m_model;
- QByteArray m_skin;
- AuthSessionPtr m_session;
- std::shared_ptr<QNetworkReply> m_reply;
+ Model m_model;
+ QByteArray m_skin;
+ AuthSessionPtr m_session;
+ std::shared_ptr<QNetworkReply> m_reply;
protected:
- virtual void executeTask();
+ virtual void executeTask();
public slots:
- void downloadError(QNetworkReply::NetworkError);
+ void downloadError(QNetworkReply::NetworkError);
- void downloadFinished();
+ void downloadFinished();
};
diff --git a/api/logic/minecraft/VersionFile.cpp b/api/logic/minecraft/VersionFile.cpp
index 9f485c55..cfb9e504 100644
--- a/api/logic/minecraft/VersionFile.cpp
+++ b/api/logic/minecraft/VersionFile.cpp
@@ -12,45 +12,45 @@
static bool isMinecraftVersion(const QString &uid)
{
- return uid == "net.minecraft";
+ return uid == "net.minecraft";
}
void VersionFile::applyTo(LaunchProfile *profile)
{
- // Only real Minecraft can set those. Don't let anything override them.
- if (isMinecraftVersion(uid))
- {
- profile->applyMinecraftVersion(minecraftVersion);
- profile->applyMinecraftVersionType(type);
- // HACK: ignore assets from other version files than Minecraft
- // workaround for stupid assets issue caused by amazon:
- // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
- profile->applyMinecraftAssets(mojangAssetIndex);
- }
-
- profile->applyMainJar(mainJar);
- profile->applyMainClass(mainClass);
- profile->applyAppletClass(appletClass);
- profile->applyMinecraftArguments(minecraftArguments);
- profile->applyTweakers(addTweakers);
- profile->applyJarMods(jarMods);
- profile->applyMods(mods);
- profile->applyTraits(traits);
-
- for (auto library : libraries)
- {
- profile->applyLibrary(library);
- }
- profile->applyProblemSeverity(getProblemSeverity());
+ // Only real Minecraft can set those. Don't let anything override them.
+ if (isMinecraftVersion(uid))
+ {
+ profile->applyMinecraftVersion(minecraftVersion);
+ profile->applyMinecraftVersionType(type);
+ // HACK: ignore assets from other version files than Minecraft
+ // workaround for stupid assets issue caused by amazon:
+ // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
+ profile->applyMinecraftAssets(mojangAssetIndex);
+ }
+
+ profile->applyMainJar(mainJar);
+ profile->applyMainClass(mainClass);
+ profile->applyAppletClass(appletClass);
+ profile->applyMinecraftArguments(minecraftArguments);
+ profile->applyTweakers(addTweakers);
+ profile->applyJarMods(jarMods);
+ profile->applyMods(mods);
+ profile->applyTraits(traits);
+
+ for (auto library : libraries)
+ {
+ profile->applyLibrary(library);
+ }
+ profile->applyProblemSeverity(getProblemSeverity());
}
/*
- auto theirVersion = profile->getMinecraftVersion();
- if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull())
- {
- if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1)
- {
- throw MinecraftVersionMismatch(uid, dependsOnMinecraftVersion, theirVersion);
- }
- }
+ auto theirVersion = profile->getMinecraftVersion();
+ if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull())
+ {
+ if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1)
+ {
+ throw MinecraftVersionMismatch(uid, dependsOnMinecraftVersion, theirVersion);
+ }
+ }
*/ \ No newline at end of file
diff --git a/api/logic/minecraft/VersionFile.h b/api/logic/minecraft/VersionFile.h
index 5aea7a7a..3dc9db96 100644
--- a/api/logic/minecraft/VersionFile.h
+++ b/api/logic/minecraft/VersionFile.h
@@ -21,91 +21,91 @@ struct MojangAssetIndexInfo;
using VersionFilePtr = std::shared_ptr<VersionFile>;
class VersionFile : public ProblemContainer
{
- friend class MojangVersionFormat;
- friend class OneSixVersionFormat;
+ friend class MojangVersionFormat;
+ friend class OneSixVersionFormat;
public: /* methods */
- void applyTo(LaunchProfile* profile);
+ void applyTo(LaunchProfile* profile);
public: /* data */
- /// MultiMC: order hint for this version file if no explicit order is set
- int order = 0;
+ /// MultiMC: order hint for this version file if no explicit order is set
+ int order = 0;
- /// MultiMC: human readable name of this package
- QString name;
+ /// MultiMC: human readable name of this package
+ QString name;
- /// MultiMC: package ID of this package
- QString uid;
+ /// MultiMC: package ID of this package
+ QString uid;
- /// MultiMC: version of this package
- QString version;
+ /// MultiMC: version of this package
+ QString version;
- /// MultiMC: DEPRECATED dependency on a Minecraft version
- QString dependsOnMinecraftVersion;
+ /// MultiMC: DEPRECATED dependency on a Minecraft version
+ QString dependsOnMinecraftVersion;
- /// Mojang: DEPRECATED used to version the Mojang version format
- int minimumLauncherVersion = -1;
+ /// Mojang: DEPRECATED used to version the Mojang version format
+ int minimumLauncherVersion = -1;
- /// Mojang: DEPRECATED version of Minecraft this is
- QString minecraftVersion;
+ /// Mojang: DEPRECATED version of Minecraft this is
+ QString minecraftVersion;
- /// Mojang: class to launch Minecraft with
- QString mainClass;
+ /// Mojang: class to launch Minecraft with
+ QString mainClass;
- /// MultiMC: class to launch legacy Minecraft with (embed in a custom window)
- QString appletClass;
+ /// MultiMC: class to launch legacy Minecraft with (embed in a custom window)
+ QString appletClass;
- /// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution)
- QString minecraftArguments;
+ /// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution)
+ QString minecraftArguments;
- /// Mojang: type of the Minecraft version
- QString type;
+ /// Mojang: type of the Minecraft version
+ QString type;
- /// Mojang: the time this version was actually released by Mojang
- QDateTime releaseTime;
+ /// Mojang: the time this version was actually released by Mojang
+ QDateTime releaseTime;
- /// Mojang: DEPRECATED the time this version was last updated by Mojang
- QDateTime updateTime;
+ /// Mojang: DEPRECATED the time this version was last updated by Mojang
+ QDateTime updateTime;
- /// Mojang: DEPRECATED asset group to be used with Minecraft
- QString assets;
+ /// Mojang: DEPRECATED asset group to be used with Minecraft
+ QString assets;
- /// MultiMC: list of tweaker mod arguments for launchwrapper
- QStringList addTweakers;
+ /// MultiMC: list of tweaker mod arguments for launchwrapper
+ QStringList addTweakers;
- /// Mojang: list of libraries to add to the version
- QList<LibraryPtr> libraries;
+ /// Mojang: list of libraries to add to the version
+ QList<LibraryPtr> libraries;
- /// The main jar (Minecraft version library, normally)
- LibraryPtr mainJar;
+ /// The main jar (Minecraft version library, normally)
+ LibraryPtr mainJar;
- /// MultiMC: list of attached traits of this version file - used to enable features
- QSet<QString> traits;
+ /// MultiMC: list of attached traits of this version file - used to enable features
+ QSet<QString> traits;
- /// MultiMC: list of jar mods added to this version
- QList<LibraryPtr> jarMods;
+ /// MultiMC: list of jar mods added to this version
+ QList<LibraryPtr> jarMods;
- /// MultiMC: list of mods added to this version
- QList<LibraryPtr> mods;
+ /// MultiMC: list of mods added to this version
+ QList<LibraryPtr> mods;
- /**
- * MultiMC: set of packages this depends on
- * NOTE: this is shared with the meta format!!!
- */
- Meta::RequireSet requires;
+ /**
+ * MultiMC: set of packages this depends on
+ * NOTE: this is shared with the meta format!!!
+ */
+ Meta::RequireSet requires;
- /**
- * MultiMC: set of packages this conflicts with
- * NOTE: this is shared with the meta format!!!
- */
- Meta::RequireSet conflicts;
+ /**
+ * MultiMC: set of packages this conflicts with
+ * NOTE: this is shared with the meta format!!!
+ */
+ Meta::RequireSet conflicts;
- /// is volatile -- may be removed as soon as it is no longer needed by something else
- bool m_volatile = false;
+ /// is volatile -- may be removed as soon as it is no longer needed by something else
+ bool m_volatile = false;
public:
- // Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more.
- QMap <QString, std::shared_ptr<MojangDownloadInfo>> mojangDownloads;
+ // Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more.
+ QMap <QString, std::shared_ptr<MojangDownloadInfo>> mojangDownloads;
- // Mojang: extended asset index download information
- std::shared_ptr<MojangAssetIndexInfo> mojangAssetIndex;
+ // Mojang: extended asset index download information
+ std::shared_ptr<MojangAssetIndexInfo> mojangAssetIndex;
};
diff --git a/api/logic/minecraft/VersionFilterData.cpp b/api/logic/minecraft/VersionFilterData.cpp
index 5f4ceee6..11f7eea9 100644
--- a/api/logic/minecraft/VersionFilterData.cpp
+++ b/api/logic/minecraft/VersionFilterData.cpp
@@ -5,64 +5,64 @@ VersionFilterData g_VersionFilterData = VersionFilterData();
VersionFilterData::VersionFilterData()
{
- // 1.3.*
- auto libs13 =
- QList<FMLlib>{{"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}};
+ // 1.3.*
+ auto libs13 =
+ QList<FMLlib>{{"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
+ {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
+ {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}};
- fmlLibsMapping["1.3.2"] = libs13;
+ fmlLibsMapping["1.3.2"] = libs13;
- // 1.4.*
- auto libs14 = QList<FMLlib>{
- {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false},
- {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb", false}};
+ // 1.4.*
+ auto libs14 = QList<FMLlib>{
+ {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
+ {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
+ {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false},
+ {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb", false}};
- fmlLibsMapping["1.4"] = libs14;
- fmlLibsMapping["1.4.1"] = libs14;
- fmlLibsMapping["1.4.2"] = libs14;
- fmlLibsMapping["1.4.3"] = libs14;
- fmlLibsMapping["1.4.4"] = libs14;
- fmlLibsMapping["1.4.5"] = libs14;
- fmlLibsMapping["1.4.6"] = libs14;
- fmlLibsMapping["1.4.7"] = libs14;
+ fmlLibsMapping["1.4"] = libs14;
+ fmlLibsMapping["1.4.1"] = libs14;
+ fmlLibsMapping["1.4.2"] = libs14;
+ fmlLibsMapping["1.4.3"] = libs14;
+ fmlLibsMapping["1.4.4"] = libs14;
+ fmlLibsMapping["1.4.5"] = libs14;
+ fmlLibsMapping["1.4.6"] = libs14;
+ fmlLibsMapping["1.4.7"] = libs14;
- // 1.5
- fmlLibsMapping["1.5"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
- {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8", false},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
+ // 1.5
+ fmlLibsMapping["1.5"] = QList<FMLlib>{
+ {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
+ {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
+ {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
+ {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
+ {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8", false},
+ {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
- // 1.5.1
- fmlLibsMapping["1.5.1"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
- {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6", false},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
+ // 1.5.1
+ fmlLibsMapping["1.5.1"] = QList<FMLlib>{
+ {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
+ {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
+ {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
+ {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
+ {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6", false},
+ {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
- // 1.5.2
- fmlLibsMapping["1.5.2"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
- {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9", false},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
+ // 1.5.2
+ fmlLibsMapping["1.5.2"] = QList<FMLlib>{
+ {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
+ {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
+ {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
+ {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
+ {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9", false},
+ {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
- // don't use installers for those.
- forgeInstallerBlacklist = QSet<QString>({"1.5.2"});
+ // don't use installers for those.
+ forgeInstallerBlacklist = QSet<QString>({"1.5.2"});
- // FIXME: remove, used for deciding when core mods should display
- legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00");
- lwjglWhitelist =
- QSet<QString>{"net.java.jinput:jinput", "net.java.jinput:jinput-platform",
- "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl",
- "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"};
+ // FIXME: remove, used for deciding when core mods should display
+ legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00");
+ lwjglWhitelist =
+ QSet<QString>{"net.java.jinput:jinput", "net.java.jinput:jinput-platform",
+ "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl",
+ "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"};
}
diff --git a/api/logic/minecraft/VersionFilterData.h b/api/logic/minecraft/VersionFilterData.h
index 2408e704..88e91f11 100644
--- a/api/logic/minecraft/VersionFilterData.h
+++ b/api/logic/minecraft/VersionFilterData.h
@@ -8,21 +8,21 @@
struct FMLlib
{
- QString filename;
- QString checksum;
- bool ours;
+ QString filename;
+ QString checksum;
+ bool ours;
};
struct VersionFilterData
{
- VersionFilterData();
- // mapping between minecraft versions and FML libraries required
- QMap<QString, QList<FMLlib>> fmlLibsMapping;
- // set of minecraft versions for which using forge installers is blacklisted
- QSet<QString> forgeInstallerBlacklist;
- // no new versions below this date will be accepted from Mojang servers
- QDateTime legacyCutoffDate;
- // Libraries that belong to LWJGL
- QSet<QString> lwjglWhitelist;
+ VersionFilterData();
+ // mapping between minecraft versions and FML libraries required
+ QMap<QString, QList<FMLlib>> fmlLibsMapping;
+ // set of minecraft versions for which using forge installers is blacklisted
+ QSet<QString> forgeInstallerBlacklist;
+ // no new versions below this date will be accepted from Mojang servers
+ QDateTime legacyCutoffDate;
+ // Libraries that belong to LWJGL
+ QSet<QString> lwjglWhitelist;
};
extern VersionFilterData MULTIMC_LOGIC_EXPORT g_VersionFilterData;
diff --git a/api/logic/minecraft/World.cpp b/api/logic/minecraft/World.cpp
index 68c0a5cc..17dbf4ae 100644
--- a/api/logic/minecraft/World.cpp
+++ b/api/logic/minecraft/World.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,356 +30,402 @@
#include <quazipfile.h>
#include <quazipdir.h>
+#include <QCoreApplication>
+
+QString gameTypeToString(GameType type)
+{
+ switch (type)
+ {
+ case GameType::Survival:
+ return QCoreApplication::translate("GameType", "Survival");
+ case GameType::Creative:
+ return QCoreApplication::translate("GameType", "Creative");
+ case GameType::Adventure:
+ return QCoreApplication::translate("GameType", "Adventure");
+ case GameType::Spectator:
+ return QCoreApplication::translate("GameType", "Spectator");
+ default:
+ break;
+ }
+ return QObject::tr("Unknown");
+}
+
std::unique_ptr <nbt::tag_compound> parseLevelDat(QByteArray data)
{
- QByteArray output;
- if(!GZip::unzip(data, output))
- {
- return nullptr;
- }
- std::istringstream foo(std::string(output.constData(), output.size()));
- auto pair = nbt::io::read_compound(foo);
+ QByteArray output;
+ if(!GZip::unzip(data, output))
+ {
+ return nullptr;
+ }
+ std::istringstream foo(std::string(output.constData(), output.size()));
+ auto pair = nbt::io::read_compound(foo);
- if(pair.first != "")
- return nullptr;
+ if(pair.first != "")
+ return nullptr;
- if(pair.second == nullptr)
- return nullptr;
+ if(pair.second == nullptr)
+ return nullptr;
- return std::move(pair.second);
+ return std::move(pair.second);
}
QByteArray serializeLevelDat(nbt::tag_compound * levelInfo)
{
- std::ostringstream s;
- nbt::io::write_tag("", *levelInfo, s);
- QByteArray val( s.str().data(), (int) s.str().size() );
- return val;
+ std::ostringstream s;
+ nbt::io::write_tag("", *levelInfo, s);
+ QByteArray val( s.str().data(), (int) s.str().size() );
+ return val;
}
QString getLevelDatFromFS(const QFileInfo &file)
{
- QDir worldDir(file.filePath());
- if(!file.isDir() || !worldDir.exists("level.dat"))
- {
- return QString();
- }
- return worldDir.absoluteFilePath("level.dat");
+ QDir worldDir(file.filePath());
+ if(!file.isDir() || !worldDir.exists("level.dat"))
+ {
+ return QString();
+ }
+ return worldDir.absoluteFilePath("level.dat");
}
QByteArray getLevelDatDataFromFS(const QFileInfo &file)
{
- auto fullFilePath = getLevelDatFromFS(file);
- if(fullFilePath.isNull())
- {
- return QByteArray();
- }
- QFile f(fullFilePath);
- if(!f.open(QIODevice::ReadOnly))
- {
- return QByteArray();
- }
- return f.readAll();
+ auto fullFilePath = getLevelDatFromFS(file);
+ if(fullFilePath.isNull())
+ {
+ return QByteArray();
+ }
+ QFile f(fullFilePath);
+ if(!f.open(QIODevice::ReadOnly))
+ {
+ return QByteArray();
+ }
+ return f.readAll();
}
bool putLevelDatDataToFS(const QFileInfo &file, QByteArray & data)
{
- auto fullFilePath = getLevelDatFromFS(file);
- if(fullFilePath.isNull())
- {
- return false;
- }
- QSaveFile f(fullFilePath);
- if(!f.open(QIODevice::WriteOnly))
- {
- return false;
- }
- QByteArray compressed;
- if(!GZip::zip(data, compressed))
- {
- return false;
- }
- if(f.write(compressed) != compressed.size())
- {
- f.cancelWriting();
- return false;
- }
- return f.commit();
+ auto fullFilePath = getLevelDatFromFS(file);
+ if(fullFilePath.isNull())
+ {
+ return false;
+ }
+ QSaveFile f(fullFilePath);
+ if(!f.open(QIODevice::WriteOnly))
+ {
+ return false;
+ }
+ QByteArray compressed;
+ if(!GZip::zip(data, compressed))
+ {
+ return false;
+ }
+ if(f.write(compressed) != compressed.size())
+ {
+ f.cancelWriting();
+ return false;
+ }
+ return f.commit();
}
World::World(const QFileInfo &file)
{
- repath(file);
+ repath(file);
}
void World::repath(const QFileInfo &file)
{
- m_containerFile = file;
- m_folderName = file.fileName();
- if(file.isFile() && file.suffix() == "zip")
- {
- readFromZip(file);
- }
- else if(file.isDir())
- {
- readFromFS(file);
- }
+ m_containerFile = file;
+ m_folderName = file.fileName();
+ if(file.isFile() && file.suffix() == "zip")
+ {
+ readFromZip(file);
+ }
+ else if(file.isDir())
+ {
+ readFromFS(file);
+ }
}
void World::readFromFS(const QFileInfo &file)
{
- auto bytes = getLevelDatDataFromFS(file);
- if(bytes.isEmpty())
- {
- is_valid = false;
- return;
- }
- loadFromLevelDat(bytes);
- levelDatTime = file.lastModified();
+ auto bytes = getLevelDatDataFromFS(file);
+ if(bytes.isEmpty())
+ {
+ is_valid = false;
+ return;
+ }
+ loadFromLevelDat(bytes);
+ levelDatTime = file.lastModified();
}
void World::readFromZip(const QFileInfo &file)
{
- QuaZip zip(file.absoluteFilePath());
- is_valid = zip.open(QuaZip::mdUnzip);
- if (!is_valid)
- {
- return;
- }
- auto location = MMCZip::findFolderOfFileInZip(&zip, "level.dat");
- is_valid = !location.isEmpty();
- if (!is_valid)
- {
- return;
- }
- m_containerOffsetPath = location;
- QuaZipFile zippedFile(&zip);
- // read the install profile
- is_valid = zip.setCurrentFile(location + "level.dat");
- if (!is_valid)
- {
- return;
- }
- is_valid = zippedFile.open(QIODevice::ReadOnly);
- QuaZipFileInfo64 levelDatInfo;
- zippedFile.getFileInfo(&levelDatInfo);
- auto modTime = levelDatInfo.getNTFSmTime();
- if(!modTime.isValid())
- {
- modTime = levelDatInfo.dateTime;
- }
- levelDatTime = modTime;
- if (!is_valid)
- {
- return;
- }
- loadFromLevelDat(zippedFile.readAll());
- zippedFile.close();
+ QuaZip zip(file.absoluteFilePath());
+ is_valid = zip.open(QuaZip::mdUnzip);
+ if (!is_valid)
+ {
+ return;
+ }
+ auto location = MMCZip::findFolderOfFileInZip(&zip, "level.dat");
+ is_valid = !location.isEmpty();
+ if (!is_valid)
+ {
+ return;
+ }
+ m_containerOffsetPath = location;
+ QuaZipFile zippedFile(&zip);
+ // read the install profile
+ is_valid = zip.setCurrentFile(location + "level.dat");
+ if (!is_valid)
+ {
+ return;
+ }
+ is_valid = zippedFile.open(QIODevice::ReadOnly);
+ QuaZipFileInfo64 levelDatInfo;
+ zippedFile.getFileInfo(&levelDatInfo);
+ auto modTime = levelDatInfo.getNTFSmTime();
+ if(!modTime.isValid())
+ {
+ modTime = levelDatInfo.dateTime;
+ }
+ levelDatTime = modTime;
+ if (!is_valid)
+ {
+ return;
+ }
+ loadFromLevelDat(zippedFile.readAll());
+ zippedFile.close();
}
bool World::install(const QString &to, const QString &name)
{
- auto finalPath = FS::PathCombine(to, FS::DirNameFromString(m_actualName, to));
- if(!FS::ensureFolderPathExists(finalPath))
- {
- return false;
- }
- bool ok = false;
- if(m_containerFile.isFile())
- {
- QuaZip zip(m_containerFile.absoluteFilePath());
- if (!zip.open(QuaZip::mdUnzip))
- {
- return false;
- }
- ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath).isEmpty();
- }
- else if(m_containerFile.isDir())
- {
- QString from = m_containerFile.filePath();
- ok = FS::copy(from, finalPath)();
- }
-
- if(ok && !name.isEmpty() && m_actualName != name)
- {
- World newWorld(finalPath);
- if(newWorld.isValid())
- {
- newWorld.rename(name);
- }
- }
- return ok;
+ auto finalPath = FS::PathCombine(to, FS::DirNameFromString(m_actualName, to));
+ if(!FS::ensureFolderPathExists(finalPath))
+ {
+ return false;
+ }
+ bool ok = false;
+ if(m_containerFile.isFile())
+ {
+ QuaZip zip(m_containerFile.absoluteFilePath());
+ if (!zip.open(QuaZip::mdUnzip))
+ {
+ return false;
+ }
+ ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath).isEmpty();
+ }
+ else if(m_containerFile.isDir())
+ {
+ QString from = m_containerFile.filePath();
+ ok = FS::copy(from, finalPath)();
+ }
+
+ if(ok && !name.isEmpty() && m_actualName != name)
+ {
+ World newWorld(finalPath);
+ if(newWorld.isValid())
+ {
+ newWorld.rename(name);
+ }
+ }
+ return ok;
}
bool World::rename(const QString &newName)
{
- if(m_containerFile.isFile())
- {
- return false;
- }
-
- auto data = getLevelDatDataFromFS(m_containerFile);
- if(data.isEmpty())
- {
- return false;
- }
-
- auto worldData = parseLevelDat(data);
- if(!worldData)
- {
- return false;
- }
- auto &val = worldData->at("Data");
- if(val.get_type() != nbt::tag_type::Compound)
- {
- return false;
- }
- auto &dataCompound = val.as<nbt::tag_compound>();
- dataCompound.put("LevelName", nbt::value_initializer(newName.toUtf8().data()));
- data = serializeLevelDat(worldData.get());
-
- putLevelDatDataToFS(m_containerFile, data);
-
- m_actualName = newName;
-
- QDir parentDir(m_containerFile.absoluteFilePath());
- parentDir.cdUp();
- QFile container(m_containerFile.absoluteFilePath());
- auto dirName = FS::DirNameFromString(m_actualName, parentDir.absolutePath());
- container.rename(parentDir.absoluteFilePath(dirName));
-
- return true;
+ if(m_containerFile.isFile())
+ {
+ return false;
+ }
+
+ auto data = getLevelDatDataFromFS(m_containerFile);
+ if(data.isEmpty())
+ {
+ return false;
+ }
+
+ auto worldData = parseLevelDat(data);
+ if(!worldData)
+ {
+ return false;
+ }
+ auto &val = worldData->at("Data");
+ if(val.get_type() != nbt::tag_type::Compound)
+ {
+ return false;
+ }
+ auto &dataCompound = val.as<nbt::tag_compound>();
+ dataCompound.put("LevelName", nbt::value_initializer(newName.toUtf8().data()));
+ data = serializeLevelDat(worldData.get());
+
+ putLevelDatDataToFS(m_containerFile, data);
+
+ m_actualName = newName;
+
+ QDir parentDir(m_containerFile.absoluteFilePath());
+ parentDir.cdUp();
+ QFile container(m_containerFile.absoluteFilePath());
+ auto dirName = FS::DirNameFromString(m_actualName, parentDir.absolutePath());
+ container.rename(parentDir.absoluteFilePath(dirName));
+
+ return true;
}
static QString read_string (nbt::value& parent, const char * name, const QString & fallback = QString())
{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::String)
- {
- return fallback;
- }
- auto & tag_str = namedValue.as<nbt::tag_string>();
- return QString::fromStdString(tag_str.get());
- }
- catch(std::out_of_range e)
- {
- // fallback for old world formats
- qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback;
- return fallback;
- }
- catch(std::bad_cast e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback;
- return fallback;
- }
-};
+ try
+ {
+ auto &namedValue = parent.at(name);
+ if(namedValue.get_type() != nbt::tag_type::String)
+ {
+ return fallback;
+ }
+ auto & tag_str = namedValue.as<nbt::tag_string>();
+ return QString::fromStdString(tag_str.get());
+ }
+ catch (const std::out_of_range &e)
+ {
+ // fallback for old world formats
+ qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback;
+ return fallback;
+ }
+ catch (const std::bad_cast &e)
+ {
+ // type mismatch
+ qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback;
+ return fallback;
+ }
+}
static int64_t read_long (nbt::value& parent, const char * name, const int64_t & fallback = 0)
{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::Long)
- {
- return fallback;
- }
- auto & tag_str = namedValue.as<nbt::tag_long>();
- return tag_str.get();
- }
- catch(std::out_of_range e)
- {
- // fallback for old world formats
- qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback;
- return fallback;
- }
- catch(std::bad_cast e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback;
- return fallback;
- }
-};
+ try
+ {
+ auto &namedValue = parent.at(name);
+ if(namedValue.get_type() != nbt::tag_type::Long)
+ {
+ return fallback;
+ }
+ auto & tag_str = namedValue.as<nbt::tag_long>();
+ return tag_str.get();
+ }
+ catch (const std::out_of_range &e)
+ {
+ // fallback for old world formats
+ qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback;
+ return fallback;
+ }
+ catch (const std::bad_cast &e)
+ {
+ // type mismatch
+ qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback;
+ return fallback;
+ }
+}
+
+static int read_int (nbt::value& parent, const char * name, const int & fallback = 0)
+{
+ try
+ {
+ auto &namedValue = parent.at(name);
+ if(namedValue.get_type() != nbt::tag_type::Int)
+ {
+ return fallback;
+ }
+ auto & tag_str = namedValue.as<nbt::tag_int>();
+ return tag_str.get();
+ }
+ catch (const std::out_of_range &e)
+ {
+ // fallback for old world formats
+ qWarning() << "Int NBT tag" << name << "could not be found. Defaulting to" << fallback;
+ return fallback;
+ }
+ catch (const std::bad_cast &e)
+ {
+ // type mismatch
+ qWarning() << "NBT tag" << name << "could not be converted to int. Defaulting to" << fallback;
+ return fallback;
+ }
+}
void World::loadFromLevelDat(QByteArray data)
{
- try
- {
- auto levelData = parseLevelDat(data);
- if(!levelData)
- {
- is_valid = false;
- return;
- }
-
- auto &val = levelData->at("Data");
- is_valid = val.get_type() == nbt::tag_type::Compound;
- if(!is_valid)
- return;
-
- m_actualName = read_string(val, "LevelName", m_folderName);
-
-
- int64_t temp = read_long(val, "LastPlayed", 0);
- if(temp == 0)
- {
- m_lastPlayed = levelDatTime;
- }
- else
- {
- m_lastPlayed = QDateTime::fromMSecsSinceEpoch(temp);
- }
-
- m_randomSeed = read_long(val, "RandomSeed", 0);
-
- qDebug() << "World Name:" << m_actualName;
- qDebug() << "Last Played:" << m_lastPlayed.toString();
- qDebug() << "Seed:" << m_randomSeed;
- }
- catch (nbt::io::input_error e)
- {
- qWarning() << "Unable to load" << m_folderName << ":" << e.what();
- is_valid = false;
- return;
- }
+ try
+ {
+ auto levelData = parseLevelDat(data);
+ if(!levelData)
+ {
+ is_valid = false;
+ return;
+ }
+
+ auto &val = levelData->at("Data");
+ is_valid = val.get_type() == nbt::tag_type::Compound;
+ if(!is_valid)
+ return;
+
+ m_actualName = read_string(val, "LevelName", m_folderName);
+
+
+ int64_t temp = read_long(val, "LastPlayed", 0);
+ if(temp == 0)
+ {
+ m_lastPlayed = levelDatTime;
+ }
+ else
+ {
+ m_lastPlayed = QDateTime::fromMSecsSinceEpoch(temp);
+ }
+
+ int GameType_val = read_int(val, "GameType", 0);
+ m_gameType = (GameType) GameType_val;
+
+ m_randomSeed = read_long(val, "RandomSeed", 0);
+
+ qDebug() << "World Name:" << m_actualName;
+ qDebug() << "Last Played:" << m_lastPlayed.toString();
+ qDebug() << "Seed:" << m_randomSeed;
+ qDebug() << "GameMode:" << GameType_val;
+ }
+ catch (const nbt::io::input_error &e)
+ {
+ qWarning() << "Unable to load" << m_folderName << ":" << e.what();
+ is_valid = false;
+ return;
+ }
}
bool World::replace(World &with)
{
- if (!destroy())
- return false;
- bool success = FS::copy(with.m_containerFile.filePath(), m_containerFile.path())();
- if (success)
- {
- m_folderName = with.m_folderName;
- m_containerFile.refresh();
- }
- return success;
+ if (!destroy())
+ return false;
+ bool success = FS::copy(with.m_containerFile.filePath(), m_containerFile.path())();
+ if (success)
+ {
+ m_folderName = with.m_folderName;
+ m_containerFile.refresh();
+ }
+ return success;
}
bool World::destroy()
{
- if(!is_valid) return false;
- if (m_containerFile.isDir())
- {
- QDir d(m_containerFile.filePath());
- return d.removeRecursively();
- }
- else if(m_containerFile.isFile())
- {
- QFile file(m_containerFile.absoluteFilePath());
- return file.remove();
- }
- return true;
+ if(!is_valid) return false;
+ if (m_containerFile.isDir())
+ {
+ QDir d(m_containerFile.filePath());
+ return d.removeRecursively();
+ }
+ else if(m_containerFile.isFile())
+ {
+ QFile file(m_containerFile.absoluteFilePath());
+ return file.remove();
+ }
+ return true;
}
bool World::operator==(const World &other) const
{
- return is_valid == other.is_valid && folderName() == other.folderName();
-}
-bool World::strongCompare(const World &other) const
-{
- return is_valid == other.is_valid && folderName() == other.folderName();
+ return is_valid == other.is_valid && folderName() == other.folderName();
}
diff --git a/api/logic/minecraft/World.h b/api/logic/minecraft/World.h
index 7087bf48..818701fa 100644
--- a/api/logic/minecraft/World.h
+++ b/api/logic/minecraft/World.h
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,65 +19,78 @@
#include "multimc_logic_export.h"
+enum class GameType
+{
+ Survival,
+ Creative,
+ Adventure,
+ Spectator
+};
+QString MULTIMC_LOGIC_EXPORT gameTypeToString(GameType type);
+
class MULTIMC_LOGIC_EXPORT World
{
public:
- World(const QFileInfo &file);
- QString folderName() const
- {
- return m_folderName;
- }
- QString name() const
- {
- return m_actualName;
- }
- QDateTime lastPlayed() const
- {
- return m_lastPlayed;
- }
- int64_t seed() const
- {
- return m_randomSeed;
- }
- bool isValid() const
- {
- return is_valid;
- }
- bool isOnFS() const
- {
- return m_containerFile.isDir();
- }
- QFileInfo container() const
- {
- return m_containerFile;
- }
- // delete all the files of this world
- bool destroy();
- // replace this world with a copy of the other
- bool replace(World &with);
- // change the world's filesystem path (used by world lists for *MAGIC* purposes)
- void repath(const QFileInfo &file);
+ World(const QFileInfo &file);
+ QString folderName() const
+ {
+ return m_folderName;
+ }
+ QString name() const
+ {
+ return m_actualName;
+ }
+ QDateTime lastPlayed() const
+ {
+ return m_lastPlayed;
+ }
+ GameType gameType() const
+ {
+ return m_gameType;
+ }
+ int64_t seed() const
+ {
+ return m_randomSeed;
+ }
+ bool isValid() const
+ {
+ return is_valid;
+ }
+ bool isOnFS() const
+ {
+ return m_containerFile.isDir();
+ }
+ QFileInfo container() const
+ {
+ return m_containerFile;
+ }
+ // delete all the files of this world
+ bool destroy();
+ // replace this world with a copy of the other
+ bool replace(World &with);
+ // change the world's filesystem path (used by world lists for *MAGIC* purposes)
+ void repath(const QFileInfo &file);
- bool rename(const QString &to);
- bool install(const QString &to, const QString &name= QString());
+ bool rename(const QString &to);
+ bool install(const QString &to, const QString &name= QString());
- // WEAK compare operator - used for replacing worlds
- bool operator==(const World &other) const;
- bool strongCompare(const World &other) const;
+ // WEAK compare operator - used for replacing worlds
+ bool operator==(const World &other) const;
private:
- void readFromZip(const QFileInfo &file);
- void readFromFS(const QFileInfo &file);
- void loadFromLevelDat(QByteArray data);
+ void readFromZip(const QFileInfo &file);
+ void readFromFS(const QFileInfo &file);
+ void loadFromLevelDat(QByteArray data);
protected:
- QFileInfo m_containerFile;
- QString m_containerOffsetPath;
- QString m_folderName;
- QString m_actualName;
- QDateTime levelDatTime;
- QDateTime m_lastPlayed;
- int64_t m_randomSeed = 0;
- bool is_valid = false;
+ QFileInfo m_containerFile;
+ QString m_containerOffsetPath;
+ QString m_folderName;
+ QString m_actualName;
+ QDateTime levelDatTime;
+ QDateTime m_lastPlayed;
+ int64_t m_randomSeed = 0;
+ GameType m_gameType = GameType::Survival;
+ bool is_valid = false;
};
diff --git a/api/logic/minecraft/WorldList.cpp b/api/logic/minecraft/WorldList.cpp
index 4278c2f7..b7a24434 100644
--- a/api/logic/minecraft/WorldList.cpp
+++ b/api/logic/minecraft/WorldList.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,216 +23,223 @@
#include <QDebug>
WorldList::WorldList(const QString &dir)
- : QAbstractListModel(), m_dir(dir)
+ : QAbstractListModel(), m_dir(dir)
{
- FS::ensureFolderPathExists(m_dir.absolutePath());
- m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs |
- QDir::NoSymLinks);
- m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
- m_watcher = new QFileSystemWatcher(this);
- is_watching = false;
- connect(m_watcher, SIGNAL(directoryChanged(QString)), this,
- SLOT(directoryChanged(QString)));
+ FS::ensureFolderPathExists(m_dir.absolutePath());
+ m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs |
+ QDir::NoSymLinks);
+ m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
+ m_watcher = new QFileSystemWatcher(this);
+ is_watching = false;
+ connect(m_watcher, SIGNAL(directoryChanged(QString)), this,
+ SLOT(directoryChanged(QString)));
}
void WorldList::startWatching()
{
- if(is_watching)
- {
- return;
- }
- update();
- is_watching = m_watcher->addPath(m_dir.absolutePath());
- if (is_watching)
- {
- qDebug() << "Started watching " << m_dir.absolutePath();
- }
- else
- {
- qDebug() << "Failed to start watching " << m_dir.absolutePath();
- }
+ if(is_watching)
+ {
+ return;
+ }
+ update();
+ is_watching = m_watcher->addPath(m_dir.absolutePath());
+ if (is_watching)
+ {
+ qDebug() << "Started watching " << m_dir.absolutePath();
+ }
+ else
+ {
+ qDebug() << "Failed to start watching " << m_dir.absolutePath();
+ }
}
void WorldList::stopWatching()
{
- if(!is_watching)
- {
- return;
- }
- is_watching = !m_watcher->removePath(m_dir.absolutePath());
- if (!is_watching)
- {
- qDebug() << "Stopped watching " << m_dir.absolutePath();
- }
- else
- {
- qDebug() << "Failed to stop watching " << m_dir.absolutePath();
- }
+ if(!is_watching)
+ {
+ return;
+ }
+ is_watching = !m_watcher->removePath(m_dir.absolutePath());
+ if (!is_watching)
+ {
+ qDebug() << "Stopped watching " << m_dir.absolutePath();
+ }
+ else
+ {
+ qDebug() << "Failed to stop watching " << m_dir.absolutePath();
+ }
}
bool WorldList::update()
{
- if (!isValid())
- return false;
-
- QList<World> newWorlds;
- m_dir.refresh();
- auto folderContents = m_dir.entryInfoList();
- // if there are any untracked files...
- for (QFileInfo entry : folderContents)
- {
- if(!entry.isDir())
- continue;
-
- World w(entry);
- if(w.isValid())
- {
- newWorlds.append(w);
- }
- }
- beginResetModel();
- worlds.swap(newWorlds);
- endResetModel();
- return true;
+ if (!isValid())
+ return false;
+
+ QList<World> newWorlds;
+ m_dir.refresh();
+ auto folderContents = m_dir.entryInfoList();
+ // if there are any untracked files...
+ for (QFileInfo entry : folderContents)
+ {
+ if(!entry.isDir())
+ continue;
+
+ World w(entry);
+ if(w.isValid())
+ {
+ newWorlds.append(w);
+ }
+ }
+ beginResetModel();
+ worlds.swap(newWorlds);
+ endResetModel();
+ return true;
}
void WorldList::directoryChanged(QString path)
{
- update();
+ update();
}
bool WorldList::isValid()
{
- return m_dir.exists() && m_dir.isReadable();
+ return m_dir.exists() && m_dir.isReadable();
}
bool WorldList::deleteWorld(int index)
{
- if (index >= worlds.size() || index < 0)
- return false;
- World &m = worlds[index];
- if (m.destroy())
- {
- beginRemoveRows(QModelIndex(), index, index);
- worlds.removeAt(index);
- endRemoveRows();
- emit changed();
- return true;
- }
- return false;
+ if (index >= worlds.size() || index < 0)
+ return false;
+ World &m = worlds[index];
+ if (m.destroy())
+ {
+ beginRemoveRows(QModelIndex(), index, index);
+ worlds.removeAt(index);
+ endRemoveRows();
+ emit changed();
+ return true;
+ }
+ return false;
}
bool WorldList::deleteWorlds(int first, int last)
{
- for (int i = first; i <= last; i++)
- {
- World &m = worlds[i];
- m.destroy();
- }
- beginRemoveRows(QModelIndex(), first, last);
- worlds.erase(worlds.begin() + first, worlds.begin() + last + 1);
- endRemoveRows();
- emit changed();
- return true;
+ for (int i = first; i <= last; i++)
+ {
+ World &m = worlds[i];
+ m.destroy();
+ }
+ beginRemoveRows(QModelIndex(), first, last);
+ worlds.erase(worlds.begin() + first, worlds.begin() + last + 1);
+ endRemoveRows();
+ emit changed();
+ return true;
}
int WorldList::columnCount(const QModelIndex &parent) const
{
- return 2;
+ return 3;
}
QVariant WorldList::data(const QModelIndex &index, int role) const
{
- if (!index.isValid())
- return QVariant();
-
- int row = index.row();
- int column = index.column();
-
- if (row < 0 || row >= worlds.size())
- return QVariant();
-
- auto & world = worlds[row];
- switch (role)
- {
- case Qt::DisplayRole:
- switch (column)
- {
- case NameColumn:
- return world.name();
-
- case LastPlayedColumn:
- return world.lastPlayed();
-
- default:
- return QVariant();
- }
-
- case Qt::ToolTipRole:
- {
- return world.folderName();
- }
- case ObjectRole:
- {
- return QVariant::fromValue<void *>((void *)&world);
- }
- case FolderRole:
- {
- return QDir::toNativeSeparators(dir().absoluteFilePath(world.folderName()));
- }
- case SeedRole:
- {
- return qVariantFromValue<qlonglong>(world.seed());
- }
- case NameRole:
- {
- return world.name();
- }
- case LastPlayedRole:
- {
- return world.lastPlayed();
- }
- default:
- return QVariant();
- }
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+ int column = index.column();
+
+ if (row < 0 || row >= worlds.size())
+ return QVariant();
+
+ auto & world = worlds[row];
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (column)
+ {
+ case NameColumn:
+ return world.name();
+
+ case GameModeColumn:
+ return gameTypeToString(world.gameType());
+
+ case LastPlayedColumn:
+ return world.lastPlayed();
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ {
+ return world.folderName();
+ }
+ case ObjectRole:
+ {
+ return QVariant::fromValue<void *>((void *)&world);
+ }
+ case FolderRole:
+ {
+ return QDir::toNativeSeparators(dir().absoluteFilePath(world.folderName()));
+ }
+ case SeedRole:
+ {
+ return qVariantFromValue<qlonglong>(world.seed());
+ }
+ case NameRole:
+ {
+ return world.name();
+ }
+ case LastPlayedRole:
+ {
+ return world.lastPlayed();
+ }
+ default:
+ return QVariant();
+ }
}
QVariant WorldList::headerData(int section, Qt::Orientation orientation, int role) const
{
- switch (role)
- {
- case Qt::DisplayRole:
- switch (section)
- {
- case NameColumn:
- return tr("Name");
- case LastPlayedColumn:
- return tr("Last Played");
- default:
- return QVariant();
- }
-
- case Qt::ToolTipRole:
- switch (section)
- {
- case NameColumn:
- return tr("The name of the world.");
- case LastPlayedColumn:
- return tr("Date and time the world was last played.");
- default:
- return QVariant();
- }
- default:
- return QVariant();
- }
- return QVariant();
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case NameColumn:
+ return tr("Name");
+ case GameModeColumn:
+ return tr("Game Mode");
+ case LastPlayedColumn:
+ return tr("Last Played");
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case NameColumn:
+ return tr("The name of the world.");
+ case GameModeColumn:
+ return tr("Game mode of the world.");
+ case LastPlayedColumn:
+ return tr("Date and time the world was last played.");
+ default:
+ return QVariant();
+ }
+ default:
+ return QVariant();
+ }
+ return QVariant();
}
QStringList WorldList::mimeTypes() const
{
- QStringList types;
- types << "text/uri-list";
- return types;
+ QStringList types;
+ types << "text/uri-list";
+ return types;
}
class WorldMimeData : public QMimeData
@@ -240,124 +247,124 @@ class WorldMimeData : public QMimeData
Q_OBJECT
public:
- WorldMimeData(QList<World> worlds)
- {
- m_worlds = worlds;
+ WorldMimeData(QList<World> worlds)
+ {
+ m_worlds = worlds;
- }
- QStringList formats() const
- {
- return QMimeData::formats() << "text/uri-list";
- }
+ }
+ QStringList formats() const
+ {
+ return QMimeData::formats() << "text/uri-list";
+ }
protected:
- QVariant retrieveData(const QString &mimetype, QVariant::Type type) const
- {
- QList<QUrl> urls;
- for(auto &world: m_worlds)
- {
- if(!world.isValid() || !world.isOnFS())
- continue;
- QString worldPath = world.container().absoluteFilePath();
- qDebug() << worldPath;
- urls.append(QUrl::fromLocalFile(worldPath));
- }
- const_cast<WorldMimeData*>(this)->setUrls(urls);
- return QMimeData::retrieveData(mimetype, type);
- }
+ QVariant retrieveData(const QString &mimetype, QVariant::Type type) const
+ {
+ QList<QUrl> urls;
+ for(auto &world: m_worlds)
+ {
+ if(!world.isValid() || !world.isOnFS())
+ continue;
+ QString worldPath = world.container().absoluteFilePath();
+ qDebug() << worldPath;
+ urls.append(QUrl::fromLocalFile(worldPath));
+ }
+ const_cast<WorldMimeData*>(this)->setUrls(urls);
+ return QMimeData::retrieveData(mimetype, type);
+ }
private:
- QList<World> m_worlds;
+ QList<World> m_worlds;
};
QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const
{
- if (indexes.size() == 0)
- return new QMimeData();
-
- QList<World> worlds;
- for(auto idx : indexes)
- {
- if(idx.column() != 0)
- continue;
- int row = idx.row();
- if (row < 0 || row >= this->worlds.size())
- continue;
- worlds.append(this->worlds[row]);
- }
- if(!worlds.size())
- {
- return new QMimeData();
- }
- return new WorldMimeData(worlds);
+ if (indexes.size() == 0)
+ return new QMimeData();
+
+ QList<World> worlds;
+ for(auto idx : indexes)
+ {
+ if(idx.column() != 0)
+ continue;
+ int row = idx.row();
+ if (row < 0 || row >= this->worlds.size())
+ continue;
+ worlds.append(this->worlds[row]);
+ }
+ if(!worlds.size())
+ {
+ return new QMimeData();
+ }
+ return new WorldMimeData(worlds);
}
Qt::ItemFlags WorldList::flags(const QModelIndex &index) const
{
- Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
- if (index.isValid())
- return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled |
- defaultFlags;
- else
- return Qt::ItemIsDropEnabled | defaultFlags;
+ Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
+ if (index.isValid())
+ return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled |
+ defaultFlags;
+ else
+ return Qt::ItemIsDropEnabled | defaultFlags;
}
Qt::DropActions WorldList::supportedDragActions() const
{
- // move to other mod lists or VOID
- return Qt::MoveAction;
+ // move to other mod lists or VOID
+ return Qt::MoveAction;
}
Qt::DropActions WorldList::supportedDropActions() const
{
- // copy from outside, move from within and other mod lists
- return Qt::CopyAction | Qt::MoveAction;
+ // copy from outside, move from within and other mod lists
+ return Qt::CopyAction | Qt::MoveAction;
}
void WorldList::installWorld(QFileInfo filename)
{
- qDebug() << "installing: " << filename.absoluteFilePath();
- World w(filename);
- if(!w.isValid())
- {
- return;
- }
- w.install(m_dir.absolutePath());
+ qDebug() << "installing: " << filename.absoluteFilePath();
+ World w(filename);
+ if(!w.isValid())
+ {
+ return;
+ }
+ w.install(m_dir.absolutePath());
}
bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
- const QModelIndex &parent)
+ const QModelIndex &parent)
{
- if (action == Qt::IgnoreAction)
- return true;
- // check if the action is supported
- if (!data || !(action & supportedDropActions()))
- return false;
- // files dropped from outside?
- if (data->hasUrls())
- {
- bool was_watching = is_watching;
- if (was_watching)
- stopWatching();
- auto urls = data->urls();
- for (auto url : urls)
- {
- // only local files may be dropped...
- if (!url.isLocalFile())
- continue;
- QString filename = url.toLocalFile();
-
- QFileInfo worldInfo(filename);
-
- if(!m_dir.entryInfoList().contains(worldInfo))
- {
- installWorld(worldInfo);
- }
- }
- if (was_watching)
- startWatching();
- return true;
- }
- return false;
+ if (action == Qt::IgnoreAction)
+ return true;
+ // check if the action is supported
+ if (!data || !(action & supportedDropActions()))
+ return false;
+ // files dropped from outside?
+ if (data->hasUrls())
+ {
+ bool was_watching = is_watching;
+ if (was_watching)
+ stopWatching();
+ auto urls = data->urls();
+ for (auto url : urls)
+ {
+ // only local files may be dropped...
+ if (!url.isLocalFile())
+ continue;
+ QString filename = url.toLocalFile();
+
+ QFileInfo worldInfo(filename);
+
+ if(!m_dir.entryInfoList().contains(worldInfo))
+ {
+ installWorld(worldInfo);
+ }
+ }
+ if (was_watching)
+ startWatching();
+ return true;
+ }
+ return false;
}
#include "WorldList.moc"
diff --git a/api/logic/minecraft/WorldList.h b/api/logic/minecraft/WorldList.h
index 811393cd..37ad330a 100644
--- a/api/logic/minecraft/WorldList.h
+++ b/api/logic/minecraft/WorldList.h
@@ -1,4 +1,4 @@
-/* Copyright 2015-2018 MultiMC Contributors
+/* Copyright 2015-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,98 +28,100 @@ class QFileSystemWatcher;
class MULTIMC_LOGIC_EXPORT WorldList : public QAbstractListModel
{
- Q_OBJECT
+ Q_OBJECT
public:
- enum Columns
- {
- NameColumn,
- LastPlayedColumn
- };
-
- enum Roles
- {
- ObjectRole = Qt::UserRole + 1,
- FolderRole,
- SeedRole,
- NameRole,
- LastPlayedRole
- };
-
- WorldList(const QString &dir);
-
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
-
- virtual int rowCount(const QModelIndex &parent = QModelIndex()) const
- {
- return size();
- };
- virtual QVariant headerData(int section, Qt::Orientation orientation,
- int role = Qt::DisplayRole) const;
- virtual int columnCount(const QModelIndex &parent) const;
-
- size_t size() const
- {
- return worlds.size();
- };
- bool empty() const
- {
- return size() == 0;
- }
- World &operator[](size_t index)
- {
- return worlds[index];
- }
-
- /// Reloads the mod list and returns true if the list changed.
- virtual bool update();
-
- /// Install a world from location
- void installWorld(QFileInfo filename);
-
- /// Deletes the mod at the given index.
- virtual bool deleteWorld(int index);
-
- /// Deletes all the selected mods
- virtual bool deleteWorlds(int first, int last);
-
- /// flags, mostly to support drag&drop
- virtual Qt::ItemFlags flags(const QModelIndex &index) const;
- /// get data for drag action
- virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
- /// get the supported mime types
- virtual QStringList mimeTypes() const;
- /// process data from drop action
- virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
- /// what drag actions do we support?
- virtual Qt::DropActions supportedDragActions() const;
-
- /// what drop actions do we support?
- virtual Qt::DropActions supportedDropActions() const;
-
- void startWatching();
- void stopWatching();
-
- virtual bool isValid();
-
- QDir dir() const
- {
- return m_dir;
- }
-
- const QList<World> &allWorlds() const
- {
- return worlds;
- }
+ enum Columns
+ {
+ NameColumn,
+ GameModeColumn,
+ LastPlayedColumn
+ };
+
+ enum Roles
+ {
+ ObjectRole = Qt::UserRole + 1,
+ FolderRole,
+ SeedRole,
+ NameRole,
+ GameModeRole,
+ LastPlayedRole
+ };
+
+ WorldList(const QString &dir);
+
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const
+ {
+ return size();
+ };
+ virtual QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+
+ size_t size() const
+ {
+ return worlds.size();
+ };
+ bool empty() const
+ {
+ return size() == 0;
+ }
+ World &operator[](size_t index)
+ {
+ return worlds[index];
+ }
+
+ /// Reloads the mod list and returns true if the list changed.
+ virtual bool update();
+
+ /// Install a world from location
+ void installWorld(QFileInfo filename);
+
+ /// Deletes the mod at the given index.
+ virtual bool deleteWorld(int index);
+
+ /// Deletes all the selected mods
+ virtual bool deleteWorlds(int first, int last);
+
+ /// flags, mostly to support drag&drop
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+ /// get data for drag action
+ virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
+ /// get the supported mime types
+ virtual QStringList mimeTypes() const;
+ /// process data from drop action
+ virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
+ /// what drag actions do we support?
+ virtual Qt::DropActions supportedDragActions() const;
+
+ /// what drop actions do we support?
+ virtual Qt::DropActions supportedDropActions() const;
+
+ void startWatching();
+ void stopWatching();
+
+ virtual bool isValid();
+
+ QDir dir() const
+ {
+ return m_dir;
+ }
+
+ const QList<World> &allWorlds() const
+ {
+ return worlds;
+ }
private slots:
- void directoryChanged(QString path);
+ void directoryChanged(QString path);
signals:
- void changed();
+ void changed();
protected:
- QFileSystemWatcher *m_watcher;
- bool is_watching;
- QDir m_dir;
- QList<World> worlds;
+ QFileSystemWatcher *m_watcher;
+ bool is_watching;
+ QDir m_dir;
+ QList<World> worlds;
};
diff --git a/api/logic/minecraft/auth/AuthSession.cpp b/api/logic/minecraft/auth/AuthSession.cpp
index 8758bfbd..4e858796 100644
--- a/api/logic/minecraft/auth/AuthSession.cpp
+++ b/api/logic/minecraft/auth/AuthSession.cpp
@@ -6,25 +6,25 @@
QString AuthSession::serializeUserProperties()
{
- QJsonObject userAttrs;
- for (auto key : u.properties.keys())
- {
- auto array = QJsonArray::fromStringList(u.properties.values(key));
- userAttrs.insert(key, array);
- }
- QJsonDocument value(userAttrs);
- return value.toJson(QJsonDocument::Compact);
+ QJsonObject userAttrs;
+ for (auto key : u.properties.keys())
+ {
+ auto array = QJsonArray::fromStringList(u.properties.values(key));
+ userAttrs.insert(key, array);
+ }
+ QJsonDocument value(userAttrs);
+ return value.toJson(QJsonDocument::Compact);
}
bool AuthSession::MakeOffline(QString offline_playername)
{
- if (status != PlayableOffline && status != PlayableOnline)
- {
- return false;
- }
- session = "-";
- player_name = offline_playername;
- status = PlayableOffline;
- return true;
+ if (status != PlayableOffline && status != PlayableOnline)
+ {
+ return false;
+ }
+ session = "-";
+ player_name = offline_playername;
+ status = PlayableOffline;
+ return true;
}
diff --git a/api/logic/minecraft/auth/AuthSession.h b/api/logic/minecraft/auth/AuthSession.h
index d2f66db8..b397d9a1 100644
--- a/api/logic/minecraft/auth/AuthSession.h
+++ b/api/logic/minecraft/auth/AuthSession.h
@@ -10,45 +10,45 @@ class MojangAccount;
struct User
{
- QString id;
- QMultiMap<QString, QString> properties;
+ QString id;
+ QMultiMap<QString, QString> properties;
};
struct MULTIMC_LOGIC_EXPORT AuthSession
{
- bool MakeOffline(QString offline_playername);
-
- QString serializeUserProperties();
-
- enum Status
- {
- Undetermined,
- RequiresPassword,
- PlayableOffline,
- PlayableOnline
- } status = Undetermined;
-
- User u;
-
- // client token
- QString client_token;
- // account user name
- QString username;
- // combined session ID
- QString session;
- // volatile auth token
- QString access_token;
- // profile name
- QString player_name;
- // profile ID
- QString uuid;
- // 'legacy' or 'mojang', depending on account type
- QString user_type;
- // Did the auth server reply?
- bool auth_server_online = false;
- // Did the user request online mode?
- bool wants_online = true;
- std::shared_ptr<MojangAccount> m_accountPtr;
+ bool MakeOffline(QString offline_playername);
+
+ QString serializeUserProperties();
+
+ enum Status
+ {
+ Undetermined,
+ RequiresPassword,
+ PlayableOffline,
+ PlayableOnline
+ } status = Undetermined;
+
+ User u;
+
+ // client token
+ QString client_token;
+ // account user name
+ QString username;
+ // combined session ID
+ QString session;
+ // volatile auth token
+ QString access_token;
+ // profile name
+ QString player_name;
+ // profile ID
+ QString uuid;
+ // 'legacy' or 'mojang', depending on account type
+ QString user_type;
+ // Did the auth server reply?
+ bool auth_server_online = false;
+ // Did the user request online mode?
+ bool wants_online = true;
+ std::shared_ptr<MojangAccount> m_accountPtr;
};
typedef std::shared_ptr<AuthSession> AuthSessionPtr;
diff --git a/api/logic/minecraft/auth/MojangAccount.cpp b/api/logic/minecraft/auth/MojangAccount.cpp
index edad4344..657e0009 100644
--- a/api/logic/minecraft/auth/MojangAccount.cpp
+++ b/api/logic/minecraft/auth/MojangAccount.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
@@ -30,286 +30,286 @@
MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
{
- // The JSON object must at least have a username for it to be valid.
- if (!object.value("username").isString())
- {
- qCritical() << "Can't load Mojang account info from JSON object. Username field is "
- "missing or of the wrong type.";
- return nullptr;
- }
+ // The JSON object must at least have a username for it to be valid.
+ if (!object.value("username").isString())
+ {
+ qCritical() << "Can't load Mojang account info from JSON object. Username field is "
+ "missing or of the wrong type.";
+ return nullptr;
+ }
- QString username = object.value("username").toString("");
- QString clientToken = object.value("clientToken").toString("");
- QString accessToken = object.value("accessToken").toString("");
+ QString username = object.value("username").toString("");
+ QString clientToken = object.value("clientToken").toString("");
+ QString accessToken = object.value("accessToken").toString("");
- QJsonArray profileArray = object.value("profiles").toArray();
- if (profileArray.size() < 1)
- {
- qCritical() << "Can't load Mojang account with username \"" << username
- << "\". No profiles found.";
- return nullptr;
- }
+ QJsonArray profileArray = object.value("profiles").toArray();
+ if (profileArray.size() < 1)
+ {
+ qCritical() << "Can't load Mojang account with username \"" << username
+ << "\". No profiles found.";
+ return nullptr;
+ }
- QList<AccountProfile> profiles;
- for (QJsonValue profileVal : profileArray)
- {
- QJsonObject profileObject = profileVal.toObject();
- QString id = profileObject.value("id").toString("");
- QString name = profileObject.value("name").toString("");
- bool legacy = profileObject.value("legacy").toBool(false);
- if (id.isEmpty() || name.isEmpty())
- {
- qWarning() << "Unable to load a profile because it was missing an ID or a name.";
- continue;
- }
- profiles.append({id, name, legacy});
- }
+ QList<AccountProfile> profiles;
+ for (QJsonValue profileVal : profileArray)
+ {
+ QJsonObject profileObject = profileVal.toObject();
+ QString id = profileObject.value("id").toString("");
+ QString name = profileObject.value("name").toString("");
+ bool legacy = profileObject.value("legacy").toBool(false);
+ if (id.isEmpty() || name.isEmpty())
+ {
+ qWarning() << "Unable to load a profile because it was missing an ID or a name.";
+ continue;
+ }
+ profiles.append({id, name, legacy});
+ }
- MojangAccountPtr account(new MojangAccount());
- if (object.value("user").isObject())
- {
- User u;
- QJsonObject userStructure = object.value("user").toObject();
- u.id = userStructure.value("id").toString();
- /*
- QJsonObject propMap = userStructure.value("properties").toObject();
- for(auto key: propMap.keys())
- {
- auto values = propMap.operator[](key).toArray();
- for(auto value: values)
- u.properties.insert(key, value.toString());
- }
- */
- account->m_user = u;
- }
- account->m_username = username;
- account->m_clientToken = clientToken;
- account->m_accessToken = accessToken;
- account->m_profiles = profiles;
+ MojangAccountPtr account(new MojangAccount());
+ if (object.value("user").isObject())
+ {
+ User u;
+ QJsonObject userStructure = object.value("user").toObject();
+ u.id = userStructure.value("id").toString();
+ /*
+ QJsonObject propMap = userStructure.value("properties").toObject();
+ for(auto key: propMap.keys())
+ {
+ auto values = propMap.operator[](key).toArray();
+ for(auto value: values)
+ u.properties.insert(key, value.toString());
+ }
+ */
+ account->m_user = u;
+ }
+ account->m_username = username;
+ account->m_clientToken = clientToken;
+ account->m_accessToken = accessToken;
+ account->m_profiles = profiles;
- // Get the currently selected profile.
- QString currentProfile = object.value("activeProfile").toString("");
- if (!currentProfile.isEmpty())
- account->setCurrentProfile(currentProfile);
+ // Get the currently selected profile.
+ QString currentProfile = object.value("activeProfile").toString("");
+ if (!currentProfile.isEmpty())
+ account->setCurrentProfile(currentProfile);
- return account;
+ return account;
}
MojangAccountPtr MojangAccount::createFromUsername(const QString &username)
{
- MojangAccountPtr account(new MojangAccount());
- account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
- account->m_username = username;
- return account;
+ MojangAccountPtr account(new MojangAccount());
+ account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ account->m_username = username;
+ return account;
}
QJsonObject MojangAccount::saveToJson() const
{
- QJsonObject json;
- json.insert("username", m_username);
- json.insert("clientToken", m_clientToken);
- json.insert("accessToken", m_accessToken);
+ QJsonObject json;
+ json.insert("username", m_username);
+ json.insert("clientToken", m_clientToken);
+ json.insert("accessToken", m_accessToken);
- QJsonArray profileArray;
- for (AccountProfile profile : m_profiles)
- {
- QJsonObject profileObj;
- profileObj.insert("id", profile.id);
- profileObj.insert("name", profile.name);
- profileObj.insert("legacy", profile.legacy);
- profileArray.append(profileObj);
- }
- json.insert("profiles", profileArray);
+ QJsonArray profileArray;
+ for (AccountProfile profile : m_profiles)
+ {
+ QJsonObject profileObj;
+ profileObj.insert("id", profile.id);
+ profileObj.insert("name", profile.name);
+ profileObj.insert("legacy", profile.legacy);
+ profileArray.append(profileObj);
+ }
+ json.insert("profiles", profileArray);
- QJsonObject userStructure;
- {
- userStructure.insert("id", m_user.id);
- /*
- QJsonObject userAttrs;
- for(auto key: m_user.properties.keys())
- {
- auto array = QJsonArray::fromStringList(m_user.properties.values(key));
- userAttrs.insert(key, array);
- }
- userStructure.insert("properties", userAttrs);
- */
- }
- json.insert("user", userStructure);
+ QJsonObject userStructure;
+ {
+ userStructure.insert("id", m_user.id);
+ /*
+ QJsonObject userAttrs;
+ for(auto key: m_user.properties.keys())
+ {
+ auto array = QJsonArray::fromStringList(m_user.properties.values(key));
+ userAttrs.insert(key, array);
+ }
+ userStructure.insert("properties", userAttrs);
+ */
+ }
+ json.insert("user", userStructure);
- if (m_currentProfile != -1)
- json.insert("activeProfile", currentProfile()->id);
+ if (m_currentProfile != -1)
+ json.insert("activeProfile", currentProfile()->id);
- return json;
+ return json;
}
bool MojangAccount::setCurrentProfile(const QString &profileId)
{
- for (int i = 0; i < m_profiles.length(); i++)
- {
- if (m_profiles[i].id == profileId)
- {
- m_currentProfile = i;
- return true;
- }
- }
- return false;
+ for (int i = 0; i < m_profiles.length(); i++)
+ {
+ if (m_profiles[i].id == profileId)
+ {
+ m_currentProfile = i;
+ return true;
+ }
+ }
+ return false;
}
const AccountProfile *MojangAccount::currentProfile() const
{
- if (m_currentProfile == -1)
- return nullptr;
- return &m_profiles[m_currentProfile];
+ if (m_currentProfile == -1)
+ return nullptr;
+ return &m_profiles[m_currentProfile];
}
AccountStatus MojangAccount::accountStatus() const
{
- if (m_accessToken.isEmpty())
- return NotVerified;
- else
- return Verified;
+ if (m_accessToken.isEmpty())
+ return NotVerified;
+ else
+ return Verified;
}
std::shared_ptr<YggdrasilTask> MojangAccount::login(AuthSessionPtr session, QString password)
{
- Q_ASSERT(m_currentTask.get() == nullptr);
+ Q_ASSERT(m_currentTask.get() == nullptr);
- // take care of the true offline status
- if (accountStatus() == NotVerified && password.isEmpty())
- {
- if (session)
- {
- session->status = AuthSession::RequiresPassword;
- fillSession(session);
- }
- return nullptr;
- }
+ // take care of the true offline status
+ if (accountStatus() == NotVerified && password.isEmpty())
+ {
+ if (session)
+ {
+ session->status = AuthSession::RequiresPassword;
+ fillSession(session);
+ }
+ return nullptr;
+ }
- if(accountStatus() == Verified && !session->wants_online)
- {
- session->status = AuthSession::PlayableOffline;
- session->auth_server_online = false;
- fillSession(session);
- return nullptr;
- }
- else
- {
- if (password.isEmpty())
- {
- m_currentTask.reset(new RefreshTask(this));
- }
- else
- {
- m_currentTask.reset(new AuthenticateTask(this, password));
- }
- m_currentTask->assignSession(session);
+ if(accountStatus() == Verified && !session->wants_online)
+ {
+ session->status = AuthSession::PlayableOffline;
+ session->auth_server_online = false;
+ fillSession(session);
+ return nullptr;
+ }
+ else
+ {
+ if (password.isEmpty())
+ {
+ m_currentTask.reset(new RefreshTask(this));
+ }
+ else
+ {
+ m_currentTask.reset(new AuthenticateTask(this, password));
+ }
+ m_currentTask->assignSession(session);
- connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
- connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
- }
- return m_currentTask;
+ connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
+ connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
+ }
+ return m_currentTask;
}
void MojangAccount::authSucceeded()
{
- auto session = m_currentTask->getAssignedSession();
- if (session)
- {
- session->status =
- session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline;
- fillSession(session);
- session->auth_server_online = true;
- }
- m_currentTask.reset();
- emit changed();
+ auto session = m_currentTask->getAssignedSession();
+ if (session)
+ {
+ session->status =
+ session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline;
+ fillSession(session);
+ session->auth_server_online = true;
+ }
+ m_currentTask.reset();
+ emit changed();
}
void MojangAccount::authFailed(QString reason)
{
- auto session = m_currentTask->getAssignedSession();
- // This is emitted when the yggdrasil tasks time out or are cancelled.
- // -> we treat the error as no-op
- if (m_currentTask->state() == YggdrasilTask::STATE_FAILED_SOFT)
- {
- if (session)
- {
- session->status = accountStatus() == Verified ? AuthSession::PlayableOffline
- : AuthSession::RequiresPassword;
- session->auth_server_online = false;
- fillSession(session);
- }
- }
- else
- {
- m_accessToken = QString();
- emit changed();
- if (session)
- {
- session->status = AuthSession::RequiresPassword;
- session->auth_server_online = true;
- fillSession(session);
- }
- }
- m_currentTask.reset();
+ auto session = m_currentTask->getAssignedSession();
+ // This is emitted when the yggdrasil tasks time out or are cancelled.
+ // -> we treat the error as no-op
+ if (m_currentTask->state() == YggdrasilTask::STATE_FAILED_SOFT)
+ {
+ if (session)
+ {
+ session->status = accountStatus() == Verified ? AuthSession::PlayableOffline
+ : AuthSession::RequiresPassword;
+ session->auth_server_online = false;
+ fillSession(session);
+ }
+ }
+ else
+ {
+ m_accessToken = QString();
+ emit changed();
+ if (session)
+ {
+ session->status = AuthSession::RequiresPassword;
+ session->auth_server_online = true;
+ fillSession(session);
+ }
+ }
+ m_currentTask.reset();
}
void MojangAccount::fillSession(AuthSessionPtr session)
{
- // the user name. you have to have an user name
- session->username = m_username;
- // volatile auth token
- session->access_token = m_accessToken;
- // the semi-permanent client token
- session->client_token = m_clientToken;
- if (currentProfile())
- {
- // profile name
- session->player_name = currentProfile()->name;
- // profile ID
- session->uuid = currentProfile()->id;
- // 'legacy' or 'mojang', depending on account type
- session->user_type = currentProfile()->legacy ? "legacy" : "mojang";
- if (!session->access_token.isEmpty())
- {
- session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id;
- }
- else
- {
- session->session = "-";
- }
- }
- else
- {
- session->player_name = "Player";
- session->session = "-";
- }
- session->u = user();
- session->m_accountPtr = shared_from_this();
+ // the user name. you have to have an user name
+ session->username = m_username;
+ // volatile auth token
+ session->access_token = m_accessToken;
+ // the semi-permanent client token
+ session->client_token = m_clientToken;
+ if (currentProfile())
+ {
+ // profile name
+ session->player_name = currentProfile()->name;
+ // profile ID
+ session->uuid = currentProfile()->id;
+ // 'legacy' or 'mojang', depending on account type
+ session->user_type = currentProfile()->legacy ? "legacy" : "mojang";
+ if (!session->access_token.isEmpty())
+ {
+ session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id;
+ }
+ else
+ {
+ session->session = "-";
+ }
+ }
+ else
+ {
+ session->player_name = "Player";
+ session->session = "-";
+ }
+ session->u = user();
+ session->m_accountPtr = shared_from_this();
}
void MojangAccount::decrementUses()
{
- Usable::decrementUses();
- if(!isInUse())
- {
- emit changed();
- qWarning() << "Account" << m_username << "is no longer in use.";
- }
+ Usable::decrementUses();
+ if(!isInUse())
+ {
+ emit changed();
+ qWarning() << "Account" << m_username << "is no longer in use.";
+ }
}
void MojangAccount::incrementUses()
{
- bool wasInUse = isInUse();
- Usable::incrementUses();
- if(!wasInUse)
- {
- emit changed();
- qWarning() << "Account" << m_username << "is now in use.";
- }
+ bool wasInUse = isInUse();
+ Usable::incrementUses();
+ if(!wasInUse)
+ {
+ emit changed();
+ qWarning() << "Account" << m_username << "is now in use.";
+ }
}
void MojangAccount::invalidateClientToken()
{
- m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
- emit changed();
+ m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ emit changed();
}
diff --git a/api/logic/minecraft/auth/MojangAccount.h b/api/logic/minecraft/auth/MojangAccount.h
index b2bbc357..7006435e 100644
--- a/api/logic/minecraft/auth/MojangAccount.h
+++ b/api/logic/minecraft/auth/MojangAccount.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,15 +44,15 @@ Q_DECLARE_METATYPE(MojangAccountPtr)
*/
struct AccountProfile
{
- QString id;
- QString name;
- bool legacy;
+ QString id;
+ QString name;
+ bool legacy;
};
enum AccountStatus
{
- NotVerified,
- Verified
+ NotVerified,
+ Verified
};
/**
@@ -62,121 +62,121 @@ enum AccountStatus
* token if the user chose to stay logged in.
*/
class MULTIMC_LOGIC_EXPORT MojangAccount :
- public QObject,
- public Usable,
- public std::enable_shared_from_this<MojangAccount>
+ public QObject,
+ public Usable,
+ public std::enable_shared_from_this<MojangAccount>
{
- Q_OBJECT
+ Q_OBJECT
public: /* construction */
- //! Do not copy accounts. ever.
- explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete;
+ //! Do not copy accounts. ever.
+ explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete;
- //! Default constructor
- explicit MojangAccount(QObject *parent = 0) : QObject(parent) {};
+ //! Default constructor
+ explicit MojangAccount(QObject *parent = 0) : QObject(parent) {};
- //! Creates an empty account for the specified user name.
- static MojangAccountPtr createFromUsername(const QString &username);
+ //! Creates an empty account for the specified user name.
+ static MojangAccountPtr createFromUsername(const QString &username);
- //! Loads a MojangAccount from the given JSON object.
- static MojangAccountPtr loadFromJson(const QJsonObject &json);
+ //! Loads a MojangAccount from the given JSON object.
+ static MojangAccountPtr loadFromJson(const QJsonObject &json);
- //! Saves a MojangAccount to a JSON object and returns it.
- QJsonObject saveToJson() const;
+ //! Saves a MojangAccount to a JSON object and returns it.
+ QJsonObject saveToJson() const;
public: /* manipulation */
- /**
- * Sets the currently selected profile to the profile with the given ID string.
- * If profileId is not in the list of available profiles, the function will simply return
- * false.
- */
- bool setCurrentProfile(const QString &profileId);
-
- /**
- * Attempt to login. Empty password means we use the token.
- * If the attempt fails because we already are performing some task, it returns false.
- */
- std::shared_ptr<YggdrasilTask> login(AuthSessionPtr session, QString password = QString());
- void invalidateClientToken();
+ /**
+ * Sets the currently selected profile to the profile with the given ID string.
+ * If profileId is not in the list of available profiles, the function will simply return
+ * false.
+ */
+ bool setCurrentProfile(const QString &profileId);
+
+ /**
+ * Attempt to login. Empty password means we use the token.
+ * If the attempt fails because we already are performing some task, it returns false.
+ */
+ std::shared_ptr<YggdrasilTask> login(AuthSessionPtr session, QString password = QString());
+ void invalidateClientToken();
public: /* queries */
- const QString &username() const
- {
- return m_username;
- }
+ const QString &username() const
+ {
+ return m_username;
+ }
- const QString &clientToken() const
- {
- return m_clientToken;
- }
+ const QString &clientToken() const
+ {
+ return m_clientToken;
+ }
- const QString &accessToken() const
- {
- return m_accessToken;
- }
+ const QString &accessToken() const
+ {
+ return m_accessToken;
+ }
- const QList<AccountProfile> &profiles() const
- {
- return m_profiles;
- }
+ const QList<AccountProfile> &profiles() const
+ {
+ return m_profiles;
+ }
- const User &user()
- {
- return m_user;
- }
+ const User &user()
+ {
+ return m_user;
+ }
- //! Returns the currently selected profile (if none, returns nullptr)
- const AccountProfile *currentProfile() const;
+ //! Returns the currently selected profile (if none, returns nullptr)
+ const AccountProfile *currentProfile() const;
- //! Returns whether the account is NotVerified, Verified or Online
- AccountStatus accountStatus() const;
+ //! Returns whether the account is NotVerified, Verified or Online
+ AccountStatus accountStatus() const;
signals:
- /**
- * This signal is emitted when the account changes
- */
- void changed();
+ /**
+ * This signal is emitted when the account changes
+ */
+ void changed();
- // TODO: better signalling for the various possible state changes - especially errors
+ // TODO: better signalling for the various possible state changes - especially errors
protected: /* variables */
- QString m_username;
+ QString m_username;
- // Used to identify the client - the user can have multiple clients for the same account
- // Think: different launchers, all connecting to the same account/profile
- QString m_clientToken;
+ // Used to identify the client - the user can have multiple clients for the same account
+ // Think: different launchers, all connecting to the same account/profile
+ QString m_clientToken;
- // Blank if not logged in.
- QString m_accessToken;
+ // Blank if not logged in.
+ QString m_accessToken;
- // Index of the selected profile within the list of available
- // profiles. -1 if nothing is selected.
- int m_currentProfile = -1;
+ // Index of the selected profile within the list of available
+ // profiles. -1 if nothing is selected.
+ int m_currentProfile = -1;
- // List of available profiles.
- QList<AccountProfile> m_profiles;
+ // List of available profiles.
+ QList<AccountProfile> m_profiles;
- // the user structure, whatever it is.
- User m_user;
+ // the user structure, whatever it is.
+ User m_user;
- // current task we are executing here
- std::shared_ptr<YggdrasilTask> m_currentTask;
+ // current task we are executing here
+ std::shared_ptr<YggdrasilTask> m_currentTask;
protected: /* methods */
- void incrementUses() override;
- void decrementUses() override;
+ void incrementUses() override;
+ void decrementUses() override;
private
slots:
- void authSucceeded();
- void authFailed(QString reason);
+ void authSucceeded();
+ void authFailed(QString reason);
private:
- void fillSession(AuthSessionPtr session);
+ void fillSession(AuthSessionPtr session);
public:
- friend class YggdrasilTask;
- friend class AuthenticateTask;
- friend class ValidateTask;
- friend class RefreshTask;
+ friend class YggdrasilTask;
+ friend class AuthenticateTask;
+ friend class ValidateTask;
+ friend class RefreshTask;
};
diff --git a/api/logic/minecraft/auth/MojangAccountList.cpp b/api/logic/minecraft/auth/MojangAccountList.cpp
index 21ae188a..b594c652 100644
--- a/api/logic/minecraft/auth/MojangAccountList.cpp
+++ b/api/logic/minecraft/auth/MojangAccountList.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,432 +37,432 @@ MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(paren
MojangAccountPtr MojangAccountList::findAccount(const QString &username) const
{
- for (int i = 0; i < count(); i++)
- {
- MojangAccountPtr account = at(i);
- if (account->username() == username)
- return account;
- }
- return nullptr;
+ for (int i = 0; i < count(); i++)
+ {
+ MojangAccountPtr account = at(i);
+ if (account->username() == username)
+ return account;
+ }
+ return nullptr;
}
const MojangAccountPtr MojangAccountList::at(int i) const
{
- return MojangAccountPtr(m_accounts.at(i));
+ return MojangAccountPtr(m_accounts.at(i));
}
void MojangAccountList::addAccount(const MojangAccountPtr account)
{
- int row = m_accounts.count();
- beginInsertRows(QModelIndex(), row, row);
- connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
- m_accounts.append(account);
- endInsertRows();
- onListChanged();
+ int row = m_accounts.count();
+ beginInsertRows(QModelIndex(), row, row);
+ connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
+ m_accounts.append(account);
+ endInsertRows();
+ onListChanged();
}
void MojangAccountList::removeAccount(const QString &username)
{
- int idx = 0;
- for (auto account : m_accounts)
- {
- if (account->username() == username)
- {
- beginRemoveRows(QModelIndex(), idx, idx);
- m_accounts.removeOne(account);
- endRemoveRows();
- return;
- }
- idx++;
- }
- onListChanged();
+ int idx = 0;
+ for (auto account : m_accounts)
+ {
+ if (account->username() == username)
+ {
+ beginRemoveRows(QModelIndex(), idx, idx);
+ m_accounts.removeOne(account);
+ endRemoveRows();
+ return;
+ }
+ idx++;
+ }
+ onListChanged();
}
void MojangAccountList::removeAccount(QModelIndex index)
{
- int row = index.row();
- if(index.isValid() && row >= 0 && row < m_accounts.size())
- {
- auto & account = m_accounts[row];
- if(account == m_activeAccount)
- {
- m_activeAccount = nullptr;
- onActiveChanged();
- }
- beginRemoveRows(QModelIndex(), row, row);
- m_accounts.removeAt(index.row());
- endRemoveRows();
- onListChanged();
- }
+ int row = index.row();
+ if(index.isValid() && row >= 0 && row < m_accounts.size())
+ {
+ auto & account = m_accounts[row];
+ if(account == m_activeAccount)
+ {
+ m_activeAccount = nullptr;
+ onActiveChanged();
+ }
+ beginRemoveRows(QModelIndex(), row, row);
+ m_accounts.removeAt(index.row());
+ endRemoveRows();
+ onListChanged();
+ }
}
MojangAccountPtr MojangAccountList::activeAccount() const
{
- return m_activeAccount;
+ return m_activeAccount;
}
void MojangAccountList::setActiveAccount(const QString &username)
{
- if (username.isEmpty() && m_activeAccount)
- {
- int idx = 0;
- auto prevActiveAcc = m_activeAccount;
- m_activeAccount = nullptr;
- for (MojangAccountPtr account : m_accounts)
- {
- if (account == prevActiveAcc)
- {
- emit dataChanged(index(idx), index(idx));
- }
- idx ++;
- }
- onActiveChanged();
- }
- else
- {
- auto currentActiveAccount = m_activeAccount;
- int currentActiveAccountIdx = -1;
- auto newActiveAccount = m_activeAccount;
- int newActiveAccountIdx = -1;
- int idx = 0;
- for (MojangAccountPtr account : m_accounts)
- {
- if (account->username() == username)
- {
- newActiveAccount = account;
- newActiveAccountIdx = idx;
- }
- if(currentActiveAccount == account)
- {
- currentActiveAccountIdx = idx;
- }
- idx++;
- }
- if(currentActiveAccount != newActiveAccount)
- {
- emit dataChanged(index(currentActiveAccountIdx), index(currentActiveAccountIdx));
- emit dataChanged(index(newActiveAccountIdx), index(newActiveAccountIdx));
- m_activeAccount = newActiveAccount;
- onActiveChanged();
- }
- }
+ if (username.isEmpty() && m_activeAccount)
+ {
+ int idx = 0;
+ auto prevActiveAcc = m_activeAccount;
+ m_activeAccount = nullptr;
+ for (MojangAccountPtr account : m_accounts)
+ {
+ if (account == prevActiveAcc)
+ {
+ emit dataChanged(index(idx), index(idx));
+ }
+ idx ++;
+ }
+ onActiveChanged();
+ }
+ else
+ {
+ auto currentActiveAccount = m_activeAccount;
+ int currentActiveAccountIdx = -1;
+ auto newActiveAccount = m_activeAccount;
+ int newActiveAccountIdx = -1;
+ int idx = 0;
+ for (MojangAccountPtr account : m_accounts)
+ {
+ if (account->username() == username)
+ {
+ newActiveAccount = account;
+ newActiveAccountIdx = idx;
+ }
+ if(currentActiveAccount == account)
+ {
+ currentActiveAccountIdx = idx;
+ }
+ idx++;
+ }
+ if(currentActiveAccount != newActiveAccount)
+ {
+ emit dataChanged(index(currentActiveAccountIdx), index(currentActiveAccountIdx));
+ emit dataChanged(index(newActiveAccountIdx), index(newActiveAccountIdx));
+ m_activeAccount = newActiveAccount;
+ onActiveChanged();
+ }
+ }
}
void MojangAccountList::accountChanged()
{
- // the list changed. there is no doubt.
- onListChanged();
+ // the list changed. there is no doubt.
+ onListChanged();
}
void MojangAccountList::onListChanged()
{
- if (m_autosave)
- // TODO: Alert the user if this fails.
- saveList();
+ if (m_autosave)
+ // TODO: Alert the user if this fails.
+ saveList();
- emit listChanged();
+ emit listChanged();
}
void MojangAccountList::onActiveChanged()
{
- if (m_autosave)
- saveList();
+ if (m_autosave)
+ saveList();
- emit activeAccountChanged();
+ emit activeAccountChanged();
}
int MojangAccountList::count() const
{
- return m_accounts.count();
+ return m_accounts.count();
}
QVariant MojangAccountList::data(const QModelIndex &index, int role) const
{
- if (!index.isValid())
- return QVariant();
-
- if (index.row() > count())
- return QVariant();
-
- MojangAccountPtr account = at(index.row());
-
- switch (role)
- {
- case Qt::DisplayRole:
- switch (index.column())
- {
- case NameColumn:
- return account->username();
-
- default:
- return QVariant();
- }
-
- case Qt::ToolTipRole:
- return account->username();
-
- case PointerRole:
- return qVariantFromValue(account);
-
- case Qt::CheckStateRole:
- switch (index.column())
- {
- case ActiveColumn:
- return account == m_activeAccount ? Qt::Checked : Qt::Unchecked;
- }
-
- default:
- return QVariant();
- }
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.row() > count())
+ return QVariant();
+
+ MojangAccountPtr account = at(index.row());
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (index.column())
+ {
+ case NameColumn:
+ return account->username();
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ return account->username();
+
+ case PointerRole:
+ return qVariantFromValue(account);
+
+ case Qt::CheckStateRole:
+ switch (index.column())
+ {
+ case ActiveColumn:
+ return account == m_activeAccount ? Qt::Checked : Qt::Unchecked;
+ }
+
+ default:
+ return QVariant();
+ }
}
QVariant MojangAccountList::headerData(int section, Qt::Orientation orientation, int role) const
{
- switch (role)
- {
- case Qt::DisplayRole:
- switch (section)
- {
- case ActiveColumn:
- return tr("Active?");
-
- case NameColumn:
- return tr("Name");
-
- default:
- return QVariant();
- }
-
- case Qt::ToolTipRole:
- switch (section)
- {
- case NameColumn:
- return tr("The name of the version.");
-
- default:
- return QVariant();
- }
-
- default:
- return QVariant();
- }
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case ActiveColumn:
+ return tr("Active?");
+
+ case NameColumn:
+ return tr("Name");
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case NameColumn:
+ return tr("The name of the version.");
+
+ default:
+ return QVariant();
+ }
+
+ default:
+ return QVariant();
+ }
}
int MojangAccountList::rowCount(const QModelIndex &) const
{
- // Return count
- return count();
+ // Return count
+ return count();
}
int MojangAccountList::columnCount(const QModelIndex &) const
{
- return 2;
+ return 2;
}
Qt::ItemFlags MojangAccountList::flags(const QModelIndex &index) const
{
- if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
- {
- return Qt::NoItemFlags;
- }
+ if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
+ {
+ return Qt::NoItemFlags;
+ }
- return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
bool MojangAccountList::setData(const QModelIndex &index, const QVariant &value, int role)
{
- if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
- {
- return false;
- }
-
- if(role == Qt::CheckStateRole)
- {
- if(value == Qt::Checked)
- {
- MojangAccountPtr account = this->at(index.row());
- this->setActiveAccount(account->username());
- }
- }
-
- emit dataChanged(index, index);
- return true;
+ if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
+ {
+ return false;
+ }
+
+ if(role == Qt::CheckStateRole)
+ {
+ if(value == Qt::Checked)
+ {
+ MojangAccountPtr account = this->at(index.row());
+ this->setActiveAccount(account->username());
+ }
+ }
+
+ emit dataChanged(index, index);
+ return true;
}
void MojangAccountList::updateListData(QList<MojangAccountPtr> versions)
{
- beginResetModel();
- m_accounts = versions;
- endResetModel();
+ beginResetModel();
+ m_accounts = versions;
+ endResetModel();
}
bool MojangAccountList::loadList(const QString &filePath)
{
- QString path = filePath;
- if (path.isEmpty())
- path = m_listFilePath;
- if (path.isEmpty())
- {
- qCritical() << "Can't load Mojang account list. No file path given and no default set.";
- return false;
- }
-
- QFile file(path);
-
- // Try to open the file and fail if we can't.
- // TODO: We should probably report this error to the user.
- if (!file.open(QIODevice::ReadOnly))
- {
- qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
- return false;
- }
-
- // Read the file and close it.
- QByteArray jsonData = file.readAll();
- file.close();
-
- QJsonParseError parseError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
-
- // Fail if the JSON is invalid.
- if (parseError.error != QJsonParseError::NoError)
- {
- qCritical() << QString("Failed to parse account list file: %1 at offset %2")
- .arg(parseError.errorString(), QString::number(parseError.offset))
- .toUtf8();
- return false;
- }
-
- // Make sure the root is an object.
- if (!jsonDoc.isObject())
- {
- qCritical() << "Invalid account list JSON: Root should be an array.";
- return false;
- }
-
- QJsonObject root = jsonDoc.object();
-
- // Make sure the format version matches.
- if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION)
- {
- QString newName = "accounts-old.json";
- qWarning() << "Format version mismatch when loading account list. Existing one will be renamed to"
- << newName;
-
- // Attempt to rename the old version.
- file.rename(newName);
- return false;
- }
-
- // Now, load the accounts array.
- beginResetModel();
- QJsonArray accounts = root.value("accounts").toArray();
- for (QJsonValue accountVal : accounts)
- {
- QJsonObject accountObj = accountVal.toObject();
- MojangAccountPtr account = MojangAccount::loadFromJson(accountObj);
- if (account.get() != nullptr)
- {
- connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
- m_accounts.append(account);
- }
- else
- {
- qWarning() << "Failed to load an account.";
- }
- }
- // Load the active account.
- m_activeAccount = findAccount(root.value("activeAccount").toString(""));
- endResetModel();
- return true;
+ QString path = filePath;
+ if (path.isEmpty())
+ path = m_listFilePath;
+ if (path.isEmpty())
+ {
+ qCritical() << "Can't load Mojang account list. No file path given and no default set.";
+ return false;
+ }
+
+ QFile file(path);
+
+ // Try to open the file and fail if we can't.
+ // TODO: We should probably report this error to the user.
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
+ return false;
+ }
+
+ // Read the file and close it.
+ QByteArray jsonData = file.readAll();
+ file.close();
+
+ QJsonParseError parseError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
+
+ // Fail if the JSON is invalid.
+ if (parseError.error != QJsonParseError::NoError)
+ {
+ qCritical() << QString("Failed to parse account list file: %1 at offset %2")
+ .arg(parseError.errorString(), QString::number(parseError.offset))
+ .toUtf8();
+ return false;
+ }
+
+ // Make sure the root is an object.
+ if (!jsonDoc.isObject())
+ {
+ qCritical() << "Invalid account list JSON: Root should be an array.";
+ return false;
+ }
+
+ QJsonObject root = jsonDoc.object();
+
+ // Make sure the format version matches.
+ if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION)
+ {
+ QString newName = "accounts-old.json";
+ qWarning() << "Format version mismatch when loading account list. Existing one will be renamed to"
+ << newName;
+
+ // Attempt to rename the old version.
+ file.rename(newName);
+ return false;
+ }
+
+ // Now, load the accounts array.
+ beginResetModel();
+ QJsonArray accounts = root.value("accounts").toArray();
+ for (QJsonValue accountVal : accounts)
+ {
+ QJsonObject accountObj = accountVal.toObject();
+ MojangAccountPtr account = MojangAccount::loadFromJson(accountObj);
+ if (account.get() != nullptr)
+ {
+ connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
+ m_accounts.append(account);
+ }
+ else
+ {
+ qWarning() << "Failed to load an account.";
+ }
+ }
+ // Load the active account.
+ m_activeAccount = findAccount(root.value("activeAccount").toString(""));
+ endResetModel();
+ return true;
}
bool MojangAccountList::saveList(const QString &filePath)
{
- QString path(filePath);
- if (path.isEmpty())
- path = m_listFilePath;
- if (path.isEmpty())
- {
- qCritical() << "Can't save Mojang account list. No file path given and no default set.";
- return false;
- }
-
- // make sure the parent folder exists
- if(!FS::ensureFilePathExists(path))
- return false;
-
- // make sure the file wasn't overwritten with a folder before (fixes a bug)
- QFileInfo finfo(path);
- if(finfo.isDir())
- {
- QDir badDir(path);
- badDir.removeRecursively();
- }
-
- qDebug() << "Writing account list to" << path;
-
- qDebug() << "Building JSON data structure.";
- // Build the JSON document to write to the list file.
- QJsonObject root;
-
- root.insert("formatVersion", ACCOUNT_LIST_FORMAT_VERSION);
-
- // Build a list of accounts.
- qDebug() << "Building account array.";
- QJsonArray accounts;
- for (MojangAccountPtr account : m_accounts)
- {
- QJsonObject accountObj = account->saveToJson();
- accounts.append(accountObj);
- }
-
- // Insert the account list into the root object.
- root.insert("accounts", accounts);
-
- if(m_activeAccount)
- {
- // Save the active account.
- root.insert("activeAccount", m_activeAccount->username());
- }
-
- // Create a JSON document object to convert our JSON to bytes.
- QJsonDocument doc(root);
-
- // Now that we're done building the JSON object, we can write it to the file.
- qDebug() << "Writing account list to file.";
- QFile file(path);
-
- // Try to open the file and fail if we can't.
- // TODO: We should probably report this error to the user.
- if (!file.open(QIODevice::WriteOnly))
- {
- qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
- return false;
- }
-
- // Write the JSON to the file.
- file.write(doc.toJson());
- file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser);
- file.close();
-
- qDebug() << "Saved account list to" << path;
-
- return true;
+ QString path(filePath);
+ if (path.isEmpty())
+ path = m_listFilePath;
+ if (path.isEmpty())
+ {
+ qCritical() << "Can't save Mojang account list. No file path given and no default set.";
+ return false;
+ }
+
+ // make sure the parent folder exists
+ if(!FS::ensureFilePathExists(path))
+ return false;
+
+ // make sure the file wasn't overwritten with a folder before (fixes a bug)
+ QFileInfo finfo(path);
+ if(finfo.isDir())
+ {
+ QDir badDir(path);
+ badDir.removeRecursively();
+ }
+
+ qDebug() << "Writing account list to" << path;
+
+ qDebug() << "Building JSON data structure.";
+ // Build the JSON document to write to the list file.
+ QJsonObject root;
+
+ root.insert("formatVersion", ACCOUNT_LIST_FORMAT_VERSION);
+
+ // Build a list of accounts.
+ qDebug() << "Building account array.";
+ QJsonArray accounts;
+ for (MojangAccountPtr account : m_accounts)
+ {
+ QJsonObject accountObj = account->saveToJson();
+ accounts.append(accountObj);
+ }
+
+ // Insert the account list into the root object.
+ root.insert("accounts", accounts);
+
+ if(m_activeAccount)
+ {
+ // Save the active account.
+ root.insert("activeAccount", m_activeAccount->username());
+ }
+
+ // Create a JSON document object to convert our JSON to bytes.
+ QJsonDocument doc(root);
+
+ // Now that we're done building the JSON object, we can write it to the file.
+ qDebug() << "Writing account list to file.";
+ QFile file(path);
+
+ // Try to open the file and fail if we can't.
+ // TODO: We should probably report this error to the user.
+ if (!file.open(QIODevice::WriteOnly))
+ {
+ qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
+ return false;
+ }
+
+ // Write the JSON to the file.
+ file.write(doc.toJson());
+ file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser);
+ file.close();
+
+ qDebug() << "Saved account list to" << path;
+
+ return true;
}
void MojangAccountList::setListFilePath(QString path, bool autosave)
{
- m_listFilePath = path;
- m_autosave = autosave;
+ m_listFilePath = path;
+ m_autosave = autosave;
}
bool MojangAccountList::anyAccountIsValid()
{
- for(auto account:m_accounts)
- {
- if(account->accountStatus() != NotVerified)
- return true;
- }
- return false;
+ for(auto account:m_accounts)
+ {
+ if(account->accountStatus() != NotVerified)
+ return true;
+ }
+ return false;
}
diff --git a/api/logic/minecraft/auth/MojangAccountList.h b/api/logic/minecraft/auth/MojangAccountList.h
index dd6c07e8..7760b32f 100644
--- a/api/logic/minecraft/auth/MojangAccountList.h
+++ b/api/logic/minecraft/auth/MojangAccountList.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,167 +35,167 @@
*/
class MULTIMC_LOGIC_EXPORT MojangAccountList : public QAbstractListModel
{
- Q_OBJECT
+ Q_OBJECT
public:
- enum ModelRoles
- {
- PointerRole = 0x34B1CB48
- };
-
- enum VListColumns
- {
- // TODO: Add icon column.
-
- // First column - Active?
- ActiveColumn = 0,
-
- // Second column - Name
- NameColumn,
- };
-
- explicit MojangAccountList(QObject *parent = 0);
-
- //! Gets the account at the given index.
- virtual const MojangAccountPtr at(int i) const;
-
- //! Returns the number of accounts in the list.
- virtual int count() const;
-
- //////// List Model Functions ////////
- virtual QVariant data(const QModelIndex &index, int role) const;
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
- virtual int rowCount(const QModelIndex &parent) const;
- virtual int columnCount(const QModelIndex &parent) const;
- virtual Qt::ItemFlags flags(const QModelIndex &index) const;
- virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
-
- /*!
- * Adds a the given Mojang account to the account list.
- */
- virtual void addAccount(const MojangAccountPtr account);
-
- /*!
- * Removes the mojang account with the given username from the account list.
- */
- virtual void removeAccount(const QString &username);
-
- /*!
- * Removes the account at the given QModelIndex.
- */
- virtual void removeAccount(QModelIndex index);
-
- /*!
- * \brief Finds an account by its username.
- * \param The username of the account to find.
- * \return A const pointer to the account with the given username. NULL if
- * one doesn't exist.
- */
- virtual MojangAccountPtr findAccount(const QString &username) const;
-
- /*!
- * Sets the default path to save the list file to.
- * If autosave is true, this list will automatically save to the given path whenever it changes.
- * THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately
- * after calling this function to ensure an autosaved change doesn't overwrite the list you intended
- * to load.
- */
- virtual void setListFilePath(QString path, bool autosave = false);
-
- /*!
- * \brief Loads the account list from the given file path.
- * If the given file is an empty string (default), will load from the default account list file.
- * \return True if successful, otherwise false.
- */
- virtual bool loadList(const QString &file = "");
-
- /*!
- * \brief Saves the account list to the given file.
- * If the given file is an empty string (default), will save from the default account list file.
- * \return True if successful, otherwise false.
- */
- virtual bool saveList(const QString &file = "");
-
- /*!
- * \brief Gets a pointer to the account that the user has selected as their "active" account.
- * Which account is active can be overridden on a per-instance basis, but this will return the one that
- * is set as active globally.
- * \return The currently active MojangAccount. If there isn't an active account, returns a null pointer.
- */
- virtual MojangAccountPtr activeAccount() const;
-
- /*!
- * Sets the given account as the current active account.
- * If the username given is an empty string, sets the active account to nothing.
- */
- virtual void setActiveAccount(const QString &username);
-
- /*!
- * Returns true if any of the account is at least Validated
- */
- bool anyAccountIsValid();
+ enum ModelRoles
+ {
+ PointerRole = 0x34B1CB48
+ };
+
+ enum VListColumns
+ {
+ // TODO: Add icon column.
+
+ // First column - Active?
+ ActiveColumn = 0,
+
+ // Second column - Name
+ NameColumn,
+ };
+
+ explicit MojangAccountList(QObject *parent = 0);
+
+ //! Gets the account at the given index.
+ virtual const MojangAccountPtr at(int i) const;
+
+ //! Returns the number of accounts in the list.
+ virtual int count() const;
+
+ //////// List Model Functions ////////
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ virtual int rowCount(const QModelIndex &parent) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
+
+ /*!
+ * Adds a the given Mojang account to the account list.
+ */
+ virtual void addAccount(const MojangAccountPtr account);
+
+ /*!
+ * Removes the mojang account with the given username from the account list.
+ */
+ virtual void removeAccount(const QString &username);
+
+ /*!
+ * Removes the account at the given QModelIndex.
+ */
+ virtual void removeAccount(QModelIndex index);
+
+ /*!
+ * \brief Finds an account by its username.
+ * \param The username of the account to find.
+ * \return A const pointer to the account with the given username. NULL if
+ * one doesn't exist.
+ */
+ virtual MojangAccountPtr findAccount(const QString &username) const;
+
+ /*!
+ * Sets the default path to save the list file to.
+ * If autosave is true, this list will automatically save to the given path whenever it changes.
+ * THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately
+ * after calling this function to ensure an autosaved change doesn't overwrite the list you intended
+ * to load.
+ */
+ virtual void setListFilePath(QString path, bool autosave = false);
+
+ /*!
+ * \brief Loads the account list from the given file path.
+ * If the given file is an empty string (default), will load from the default account list file.
+ * \return True if successful, otherwise false.
+ */
+ virtual bool loadList(const QString &file = "");
+
+ /*!
+ * \brief Saves the account list to the given file.
+ * If the given file is an empty string (default), will save from the default account list file.
+ * \return True if successful, otherwise false.
+ */
+ virtual bool saveList(const QString &file = "");
+
+ /*!
+ * \brief Gets a pointer to the account that the user has selected as their "active" account.
+ * Which account is active can be overridden on a per-instance basis, but this will return the one that
+ * is set as active globally.
+ * \return The currently active MojangAccount. If there isn't an active account, returns a null pointer.
+ */
+ virtual MojangAccountPtr activeAccount() const;
+
+ /*!
+ * Sets the given account as the current active account.
+ * If the username given is an empty string, sets the active account to nothing.
+ */
+ virtual void setActiveAccount(const QString &username);
+
+ /*!
+ * Returns true if any of the account is at least Validated
+ */
+ bool anyAccountIsValid();
signals:
- /*!
- * Signal emitted to indicate that the account list has changed.
- * This will also fire if the value of an element in the list changes (will be implemented
- * later).
- */
- void listChanged();
-
- /*!
- * Signal emitted to indicate that the active account has changed.
- */
- void activeAccountChanged();
+ /*!
+ * Signal emitted to indicate that the account list has changed.
+ * This will also fire if the value of an element in the list changes (will be implemented
+ * later).
+ */
+ void listChanged();
+
+ /*!
+ * Signal emitted to indicate that the active account has changed.
+ */
+ void activeAccountChanged();
public
slots:
- /**
- * This is called when one of the accounts changes and the list needs to be updated
- */
- void accountChanged();
+ /**
+ * This is called when one of the accounts changes and the list needs to be updated
+ */
+ void accountChanged();
protected:
- /*!
- * Called whenever the list changes.
- * This emits the listChanged() signal and autosaves the list (if autosave is enabled).
- */
- void onListChanged();
-
- /*!
- * Called whenever the active account changes.
- * Emits the activeAccountChanged() signal and autosaves the list if enabled.
- */
- void onActiveChanged();
-
- QList<MojangAccountPtr> m_accounts;
-
- /*!
- * Account that is currently active.
- */
- MojangAccountPtr m_activeAccount;
-
- //! Path to the account list file. Empty string if there isn't one.
- QString m_listFilePath;
-
- /*!
- * If true, the account list will automatically save to the account list path when it changes.
- * Ignored if m_listFilePath is blank.
- */
- bool m_autosave = false;
+ /*!
+ * Called whenever the list changes.
+ * This emits the listChanged() signal and autosaves the list (if autosave is enabled).
+ */
+ void onListChanged();
+
+ /*!
+ * Called whenever the active account changes.
+ * Emits the activeAccountChanged() signal and autosaves the list if enabled.
+ */
+ void onActiveChanged();
+
+ QList<MojangAccountPtr> m_accounts;
+
+ /*!
+ * Account that is currently active.
+ */
+ MojangAccountPtr m_activeAccount;
+
+ //! Path to the account list file. Empty string if there isn't one.
+ QString m_listFilePath;
+
+ /*!
+ * If true, the account list will automatically save to the account list path when it changes.
+ * Ignored if m_listFilePath is blank.
+ */
+ bool m_autosave = false;
protected
slots:
- /*!
- * Updates this list with the given list of accounts.
- * This is done by copying each account in the given list and inserting it
- * into this one.
- * We need to do this so that we can set the parents of the accounts are set to this
- * account list. This can't be done in the load task, because the accounts the load
- * task creates are on the load task's thread and Qt won't allow their parents
- * to be set to something created on another thread.
- * To get around that problem, we invoke this method on the GUI thread, which
- * then copies the accounts and sets their parents correctly.
- * \param accounts List of accounts whose parents should be set.
- */
- virtual void updateListData(QList<MojangAccountPtr> versions);
+ /*!
+ * Updates this list with the given list of accounts.
+ * This is done by copying each account in the given list and inserting it
+ * into this one.
+ * We need to do this so that we can set the parents of the accounts are set to this
+ * account list. This can't be done in the load task, because the accounts the load
+ * task creates are on the load task's thread and Qt won't allow their parents
+ * to be set to something created on another thread.
+ * To get around that problem, we invoke this method on the GUI thread, which
+ * then copies the accounts and sets their parents correctly.
+ * \param accounts List of accounts whose parents should be set.
+ */
+ virtual void updateListData(QList<MojangAccountPtr> versions);
};
diff --git a/api/logic/minecraft/auth/YggdrasilTask.cpp b/api/logic/minecraft/auth/YggdrasilTask.cpp
index f17d803f..71f5d950 100644
--- a/api/logic/minecraft/auth/YggdrasilTask.cpp
+++ b/api/logic/minecraft/auth/YggdrasilTask.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,226 +30,226 @@
#include <QDebug>
YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent)
- : Task(parent), m_account(account)
+ : Task(parent), m_account(account)
{
- changeState(STATE_CREATED);
+ changeState(STATE_CREATED);
}
void YggdrasilTask::executeTask()
{
- changeState(STATE_SENDING_REQUEST);
-
- // Get the content of the request we're going to send to the server.
- QJsonDocument doc(getRequestContent());
-
- QUrl reqUrl("https://" + URLConstants::AUTH_BASE + getEndpoint());
- QNetworkRequest netRequest(reqUrl);
- netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-
- QByteArray requestData = doc.toJson();
- m_netReply = ENV.qnam().post(netRequest, requestData);
- connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply);
- connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers);
- connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers);
- connect(m_netReply, &QNetworkReply::sslErrors, this, &YggdrasilTask::sslErrors);
- timeout_keeper.setSingleShot(true);
- timeout_keeper.start(timeout_max);
- counter.setSingleShot(false);
- counter.start(time_step);
- progress(0, timeout_max);
- connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abortByTimeout);
- connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat);
+ changeState(STATE_SENDING_REQUEST);
+
+ // Get the content of the request we're going to send to the server.
+ QJsonDocument doc(getRequestContent());
+
+ QUrl reqUrl(URLConstants::AUTH_BASE + getEndpoint());
+ QNetworkRequest netRequest(reqUrl);
+ netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+ QByteArray requestData = doc.toJson();
+ m_netReply = ENV.qnam().post(netRequest, requestData);
+ connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply);
+ connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers);
+ connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers);
+ connect(m_netReply, &QNetworkReply::sslErrors, this, &YggdrasilTask::sslErrors);
+ timeout_keeper.setSingleShot(true);
+ timeout_keeper.start(timeout_max);
+ counter.setSingleShot(false);
+ counter.start(time_step);
+ progress(0, timeout_max);
+ connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abortByTimeout);
+ connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat);
}
void YggdrasilTask::refreshTimers(qint64, qint64)
{
- timeout_keeper.stop();
- timeout_keeper.start(timeout_max);
- progress(count = 0, timeout_max);
+ timeout_keeper.stop();
+ timeout_keeper.start(timeout_max);
+ progress(count = 0, timeout_max);
}
void YggdrasilTask::heartbeat()
{
- count += time_step;
- progress(count, timeout_max);
+ count += time_step;
+ progress(count, timeout_max);
}
bool YggdrasilTask::abort()
{
- progress(timeout_max, timeout_max);
- // TODO: actually use this in a meaningful way
- m_aborted = YggdrasilTask::BY_USER;
- m_netReply->abort();
- return true;
+ progress(timeout_max, timeout_max);
+ // TODO: actually use this in a meaningful way
+ m_aborted = YggdrasilTask::BY_USER;
+ m_netReply->abort();
+ return true;
}
void YggdrasilTask::abortByTimeout()
{
- progress(timeout_max, timeout_max);
- // TODO: actually use this in a meaningful way
- m_aborted = YggdrasilTask::BY_TIMEOUT;
- m_netReply->abort();
+ progress(timeout_max, timeout_max);
+ // TODO: actually use this in a meaningful way
+ m_aborted = YggdrasilTask::BY_TIMEOUT;
+ m_netReply->abort();
}
void YggdrasilTask::sslErrors(QList<QSslError> errors)
{
- int i = 1;
- for (auto error : errors)
- {
- qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString();
- auto cert = error.certificate();
- qCritical() << "Certificate in question:\n" << cert.toText();
- i++;
- }
+ int i = 1;
+ for (auto error : errors)
+ {
+ qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString();
+ auto cert = error.certificate();
+ qCritical() << "Certificate in question:\n" << cert.toText();
+ i++;
+ }
}
void YggdrasilTask::processReply()
{
- changeState(STATE_PROCESSING_RESPONSE);
-
- switch (m_netReply->error())
- {
- case QNetworkReply::NoError:
- break;
- case QNetworkReply::TimeoutError:
- changeState(STATE_FAILED_SOFT, tr("Authentication operation timed out."));
- return;
- case QNetworkReply::OperationCanceledError:
- changeState(STATE_FAILED_SOFT, tr("Authentication operation cancelled."));
- return;
- case QNetworkReply::SslHandshakeFailedError:
- changeState(
- STATE_FAILED_SOFT,
- tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>"
- "<ul>"
- "<li>You use Windows XP and need to <a "
- "href=\"http://www.microsoft.com/en-us/download/details.aspx?id=38918\">update "
- "your root certificates</a></li>"
- "<li>Some device on your network is interfering with SSL traffic. In that case, "
- "you have bigger worries than Minecraft not starting.</li>"
- "<li>Possibly something else. Check the MultiMC log file for details</li>"
- "</ul>"));
- return;
- // used for invalid credentials and similar errors. Fall through.
- case QNetworkReply::ContentAccessDenied:
- case QNetworkReply::ContentOperationNotPermittedError:
- break;
- default:
- changeState(STATE_FAILED_SOFT,
- tr("Authentication operation failed due to a network error: %1 (%2)")
- .arg(m_netReply->errorString()).arg(m_netReply->error()));
- return;
- }
-
- // Try to parse the response regardless of the response code.
- // Sometimes the auth server will give more information and an error code.
- QJsonParseError jsonError;
- QByteArray replyData = m_netReply->readAll();
- QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
- // Check the response code.
- int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
- if (responseCode == 200)
- {
- // If the response code was 200, then there shouldn't be an error. Make sure
- // anyways.
- // Also, sometimes an empty reply indicates success. If there was no data received,
- // pass an empty json object to the processResponse function.
- if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
- {
- processResponse(replyData.size() > 0 ? doc.object() : QJsonObject());
- return;
- }
- else
- {
- changeState(STATE_FAILED_SOFT, tr("Failed to parse authentication server response "
- "JSON response: %1 at offset %2.")
- .arg(jsonError.errorString())
- .arg(jsonError.offset));
- qCritical() << replyData;
- }
- return;
- }
-
- // If the response code was not 200, then Yggdrasil may have given us information
- // about the error.
- // If we can parse the response, then get information from it. Otherwise just say
- // there was an unknown error.
- if (jsonError.error == QJsonParseError::NoError)
- {
- // We were able to parse the server's response. Woo!
- // Call processError. If a subclass has overridden it then they'll handle their
- // stuff there.
- qDebug() << "The request failed, but the server gave us an error message. "
- "Processing error.";
- processError(doc.object());
- }
- else
- {
- // The server didn't say anything regarding the error. Give the user an unknown
- // error.
- qDebug()
- << "The request failed and the server gave no error message. Unknown error.";
- changeState(STATE_FAILED_SOFT,
- tr("An unknown error occurred when trying to communicate with the "
- "authentication server: %1").arg(m_netReply->errorString()));
- }
+ changeState(STATE_PROCESSING_RESPONSE);
+
+ switch (m_netReply->error())
+ {
+ case QNetworkReply::NoError:
+ break;
+ case QNetworkReply::TimeoutError:
+ changeState(STATE_FAILED_SOFT, tr("Authentication operation timed out."));
+ return;
+ case QNetworkReply::OperationCanceledError:
+ changeState(STATE_FAILED_SOFT, tr("Authentication operation cancelled."));
+ return;
+ case QNetworkReply::SslHandshakeFailedError:
+ changeState(
+ STATE_FAILED_SOFT,
+ tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>"
+ "<ul>"
+ "<li>You use Windows XP and need to <a "
+ "href=\"https://www.microsoft.com/en-us/download/details.aspx?id=38918\">update "
+ "your root certificates</a></li>"
+ "<li>Some device on your network is interfering with SSL traffic. In that case, "
+ "you have bigger worries than Minecraft not starting.</li>"
+ "<li>Possibly something else. Check the MultiMC log file for details</li>"
+ "</ul>"));
+ return;
+ // used for invalid credentials and similar errors. Fall through.
+ case QNetworkReply::ContentAccessDenied:
+ case QNetworkReply::ContentOperationNotPermittedError:
+ break;
+ default:
+ changeState(STATE_FAILED_SOFT,
+ tr("Authentication operation failed due to a network error: %1 (%2)")
+ .arg(m_netReply->errorString()).arg(m_netReply->error()));
+ return;
+ }
+
+ // Try to parse the response regardless of the response code.
+ // Sometimes the auth server will give more information and an error code.
+ QJsonParseError jsonError;
+ QByteArray replyData = m_netReply->readAll();
+ QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
+ // Check the response code.
+ int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (responseCode == 200)
+ {
+ // If the response code was 200, then there shouldn't be an error. Make sure
+ // anyways.
+ // Also, sometimes an empty reply indicates success. If there was no data received,
+ // pass an empty json object to the processResponse function.
+ if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
+ {
+ processResponse(replyData.size() > 0 ? doc.object() : QJsonObject());
+ return;
+ }
+ else
+ {
+ changeState(STATE_FAILED_SOFT, tr("Failed to parse authentication server response "
+ "JSON response: %1 at offset %2.")
+ .arg(jsonError.errorString())
+ .arg(jsonError.offset));
+ qCritical() << replyData;
+ }
+ return;
+ }
+
+ // If the response code was not 200, then Yggdrasil may have given us information
+ // about the error.
+ // If we can parse the response, then get information from it. Otherwise just say
+ // there was an unknown error.
+ if (jsonError.error == QJsonParseError::NoError)
+ {
+ // We were able to parse the server's response. Woo!
+ // Call processError. If a subclass has overridden it then they'll handle their
+ // stuff there.
+ qDebug() << "The request failed, but the server gave us an error message. "
+ "Processing error.";
+ processError(doc.object());
+ }
+ else
+ {
+ // The server didn't say anything regarding the error. Give the user an unknown
+ // error.
+ qDebug()
+ << "The request failed and the server gave no error message. Unknown error.";
+ changeState(STATE_FAILED_SOFT,
+ tr("An unknown error occurred when trying to communicate with the "
+ "authentication server: %1").arg(m_netReply->errorString()));
+ }
}
void YggdrasilTask::processError(QJsonObject responseData)
{
- QJsonValue errorVal = responseData.value("error");
- QJsonValue errorMessageValue = responseData.value("errorMessage");
- QJsonValue causeVal = responseData.value("cause");
-
- if (errorVal.isString() && errorMessageValue.isString())
- {
- m_error = std::shared_ptr<Error>(new Error{
- errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")});
- changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose);
- }
- else
- {
- // Error is not in standard format. Don't set m_error and return unknown error.
- changeState(STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred."));
- }
+ QJsonValue errorVal = responseData.value("error");
+ QJsonValue errorMessageValue = responseData.value("errorMessage");
+ QJsonValue causeVal = responseData.value("cause");
+
+ if (errorVal.isString() && errorMessageValue.isString())
+ {
+ m_error = std::shared_ptr<Error>(new Error{
+ errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")});
+ changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose);
+ }
+ else
+ {
+ // Error is not in standard format. Don't set m_error and return unknown error.
+ changeState(STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred."));
+ }
}
QString YggdrasilTask::getStateMessage() const
{
- switch (m_state)
- {
- case STATE_CREATED:
- return "Waiting...";
- case STATE_SENDING_REQUEST:
- return tr("Sending request to auth servers...");
- case STATE_PROCESSING_RESPONSE:
- return tr("Processing response from servers...");
- case STATE_SUCCEEDED:
- return tr("Authentication task succeeded.");
- case STATE_FAILED_SOFT:
- return tr("Failed to contact the authentication server.");
- case STATE_FAILED_HARD:
- return tr("Failed to authenticate.");
- default:
- return tr("...");
- }
+ switch (m_state)
+ {
+ case STATE_CREATED:
+ return "Waiting...";
+ case STATE_SENDING_REQUEST:
+ return tr("Sending request to auth servers...");
+ case STATE_PROCESSING_RESPONSE:
+ return tr("Processing response from servers...");
+ case STATE_SUCCEEDED:
+ return tr("Authentication task succeeded.");
+ case STATE_FAILED_SOFT:
+ return tr("Failed to contact the authentication server.");
+ case STATE_FAILED_HARD:
+ return tr("Failed to authenticate.");
+ default:
+ return tr("...");
+ }
}
void YggdrasilTask::changeState(YggdrasilTask::State newState, QString reason)
{
- m_state = newState;
- setStatus(getStateMessage());
- if (newState == STATE_SUCCEEDED)
- {
- emitSucceeded();
- }
- else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT)
- {
- emitFailed(reason);
- }
+ m_state = newState;
+ setStatus(getStateMessage());
+ if (newState == STATE_SUCCEEDED)
+ {
+ emitSucceeded();
+ }
+ else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT)
+ {
+ emitFailed(reason);
+ }
}
YggdrasilTask::State YggdrasilTask::state()
{
- return m_state;
+ return m_state;
}
diff --git a/api/logic/minecraft/auth/YggdrasilTask.h b/api/logic/minecraft/auth/YggdrasilTask.h
index b165d3e9..c5352386 100644
--- a/api/logic/minecraft/auth/YggdrasilTask.h
+++ b/api/logic/minecraft/auth/YggdrasilTask.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,120 +31,121 @@ class QNetworkReply;
*/
class YggdrasilTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0);
-
- /**
- * assign a session to this task. the session will be filled with required infomration
- * upon completion
- */
- void assignSession(AuthSessionPtr session)
- {
- m_session = session;
- }
-
- /// get the assigned session for filling with information.
- AuthSessionPtr getAssignedSession()
- {
- return m_session;
- }
-
- /**
- * Class describing a Yggdrasil error response.
- */
- struct Error
- {
- QString m_errorMessageShort;
- QString m_errorMessageVerbose;
- QString m_cause;
- };
-
- enum AbortedBy
- {
- BY_NOTHING,
- BY_USER,
- BY_TIMEOUT
- } m_aborted = BY_NOTHING;
-
- /**
- * Enum for describing the state of the current task.
- * Used by the getStateMessage function to determine what the status message should be.
- */
- enum State
- {
- STATE_CREATED,
- STATE_SENDING_REQUEST,
- STATE_PROCESSING_RESPONSE,
- STATE_FAILED_SOFT, //!< soft failure. this generally means the user auth details haven't been invalidated
- STATE_FAILED_HARD, //!< hard failure. auth is invalid
- STATE_SUCCEEDED
- } m_state = STATE_CREATED;
+ explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0);
+ virtual ~YggdrasilTask() {};
+
+ /**
+ * assign a session to this task. the session will be filled with required infomration
+ * upon completion
+ */
+ void assignSession(AuthSessionPtr session)
+ {
+ m_session = session;
+ }
+
+ /// get the assigned session for filling with information.
+ AuthSessionPtr getAssignedSession()
+ {
+ return m_session;
+ }
+
+ /**
+ * Class describing a Yggdrasil error response.
+ */
+ struct Error
+ {
+ QString m_errorMessageShort;
+ QString m_errorMessageVerbose;
+ QString m_cause;
+ };
+
+ enum AbortedBy
+ {
+ BY_NOTHING,
+ BY_USER,
+ BY_TIMEOUT
+ } m_aborted = BY_NOTHING;
+
+ /**
+ * Enum for describing the state of the current task.
+ * Used by the getStateMessage function to determine what the status message should be.
+ */
+ enum State
+ {
+ STATE_CREATED,
+ STATE_SENDING_REQUEST,
+ STATE_PROCESSING_RESPONSE,
+ STATE_FAILED_SOFT, //!< soft failure. this generally means the user auth details haven't been invalidated
+ STATE_FAILED_HARD, //!< hard failure. auth is invalid
+ STATE_SUCCEEDED
+ } m_state = STATE_CREATED;
protected:
- virtual void executeTask() override;
-
- /**
- * Gets the JSON object that will be sent to the authentication server.
- * Should be overridden by subclasses.
- */
- virtual QJsonObject getRequestContent() const = 0;
-
- /**
- * Gets the endpoint to POST to.
- * No leading slash.
- */
- virtual QString getEndpoint() const = 0;
-
- /**
- * Processes the response received from the server.
- * If an error occurred, this should emit a failed signal and return false.
- * If Yggdrasil gave an error response, it should call setError() first, and then return false.
- * Otherwise, it should return true.
- * Note: If the response from the server was blank, and the HTTP code was 200, this function is called with
- * an empty QJsonObject.
- */
- virtual void processResponse(QJsonObject responseData) = 0;
-
- /**
- * Processes an error response received from the server.
- * The default implementation will read data from Yggdrasil's standard error response format and set it as this task's Error.
- * \returns a QString error message that will be passed to emitFailed.
- */
- virtual void processError(QJsonObject responseData);
-
- /**
- * Returns the state message for the given state.
- * Used to set the status message for the task.
- * Should be overridden by subclasses that want to change messages for a given state.
- */
- virtual QString getStateMessage() const;
+ virtual void executeTask() override;
+
+ /**
+ * Gets the JSON object that will be sent to the authentication server.
+ * Should be overridden by subclasses.
+ */
+ virtual QJsonObject getRequestContent() const = 0;
+
+ /**
+ * Gets the endpoint to POST to.
+ * No leading slash.
+ */
+ virtual QString getEndpoint() const = 0;
+
+ /**
+ * Processes the response received from the server.
+ * If an error occurred, this should emit a failed signal and return false.
+ * If Yggdrasil gave an error response, it should call setError() first, and then return false.
+ * Otherwise, it should return true.
+ * Note: If the response from the server was blank, and the HTTP code was 200, this function is called with
+ * an empty QJsonObject.
+ */
+ virtual void processResponse(QJsonObject responseData) = 0;
+
+ /**
+ * Processes an error response received from the server.
+ * The default implementation will read data from Yggdrasil's standard error response format and set it as this task's Error.
+ * \returns a QString error message that will be passed to emitFailed.
+ */
+ virtual void processError(QJsonObject responseData);
+
+ /**
+ * Returns the state message for the given state.
+ * Used to set the status message for the task.
+ * Should be overridden by subclasses that want to change messages for a given state.
+ */
+ virtual QString getStateMessage() const;
protected
slots:
- void processReply();
- void refreshTimers(qint64, qint64);
- void heartbeat();
- void sslErrors(QList<QSslError>);
+ void processReply();
+ void refreshTimers(qint64, qint64);
+ void heartbeat();
+ void sslErrors(QList<QSslError>);
- void changeState(State newState, QString reason=QString());
+ void changeState(State newState, QString reason=QString());
public
slots:
- virtual bool abort() override;
- void abortByTimeout();
- State state();
+ virtual bool abort() override;
+ void abortByTimeout();
+ State state();
protected:
- // FIXME: segfault disaster waiting to happen
- MojangAccount *m_account = nullptr;
- QNetworkReply *m_netReply = nullptr;
- std::shared_ptr<Error> m_error;
- QTimer timeout_keeper;
- QTimer counter;
- int count = 0; // num msec since time reset
-
- const int timeout_max = 30000;
- const int time_step = 50;
-
- AuthSessionPtr m_session;
+ // FIXME: segfault disaster waiting to happen
+ MojangAccount *m_account = nullptr;
+ QNetworkReply *m_netReply = nullptr;
+ std::shared_ptr<Error> m_error;
+ QTimer timeout_keeper;
+ QTimer counter;
+ int count = 0; // num msec since time reset
+
+ const int timeout_max = 30000;
+ const int time_step = 50;
+
+ AuthSessionPtr m_session;
};
diff --git a/api/logic/minecraft/auth/flows/AuthenticateTask.cpp b/api/logic/minecraft/auth/flows/AuthenticateTask.cpp
index ff4e7d47..8286c7a4 100644
--- a/api/logic/minecraft/auth/flows/AuthenticateTask.cpp
+++ b/api/logic/minecraft/auth/flows/AuthenticateTask.cpp
@@ -1,5 +1,5 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,177 +26,177 @@
#include <QUuid>
AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password,
- QObject *parent)
- : YggdrasilTask(account, parent), m_password(password)
+ QObject *parent)
+ : YggdrasilTask(account, parent), m_password(password)
{
}
QJsonObject AuthenticateTask::getRequestContent() const
{
- /*
- * {
- * "agent": { // optional
- * "name": "Minecraft", // So far this is the only encountered value
- * "version": 1 // This number might be increased
- * // by the vanilla client in the future
- * },
- * "username": "mojang account name", // Can be an email address or player name for
- // unmigrated accounts
- * "password": "mojang account password",
- * "clientToken": "client identifier" // optional
- * "requestUser": true/false // request the user structure
- * }
- */
- QJsonObject req;
-
- {
- QJsonObject agent;
- // C++ makes string literals void* for some stupid reason, so we have to tell it
- // QString... Thanks Obama.
- agent.insert("name", QString("Minecraft"));
- agent.insert("version", 1);
- req.insert("agent", agent);
- }
-
- req.insert("username", m_account->username());
- req.insert("password", m_password);
- req.insert("requestUser", true);
-
- // If we already have a client token, give it to the server.
- // Otherwise, let the server give us one.
-
- if(m_account->m_clientToken.isEmpty())
- {
- auto uuid = QUuid::createUuid();
- auto uuidString = uuid.toString().remove('{').remove('-').remove('}');
- m_account->m_clientToken = uuidString;
- }
- req.insert("clientToken", m_account->m_clientToken);
-
- return req;
+ /*
+ * {
+ * "agent": { // optional
+ * "name": "Minecraft", // So far this is the only encountered value
+ * "version": 1 // This number might be increased
+ * // by the vanilla client in the future
+ * },
+ * "username": "mojang account name", // Can be an email address or player name for
+ // unmigrated accounts
+ * "password": "mojang account password",
+ * "clientToken": "client identifier" // optional
+ * "requestUser": true/false // request the user structure
+ * }
+ */
+ QJsonObject req;
+
+ {
+ QJsonObject agent;
+ // C++ makes string literals void* for some stupid reason, so we have to tell it
+ // QString... Thanks Obama.
+ agent.insert("name", QString("Minecraft"));
+ agent.insert("version", 1);
+ req.insert("agent", agent);
+ }
+
+ req.insert("username", m_account->username());
+ req.insert("password", m_password);
+ req.insert("requestUser", true);
+
+ // If we already have a client token, give it to the server.
+ // Otherwise, let the server give us one.
+
+ if(m_account->m_clientToken.isEmpty())
+ {
+ auto uuid = QUuid::createUuid();
+ auto uuidString = uuid.toString().remove('{').remove('-').remove('}');
+ m_account->m_clientToken = uuidString;
+ }
+ req.insert("clientToken", m_account->m_clientToken);
+
+ return req;
}
void AuthenticateTask::processResponse(QJsonObject responseData)
{
- // Read the response data. We need to get the client token, access token, and the selected
- // profile.
- qDebug() << "Processing authentication response.";
- // qDebug() << responseData;
- // If we already have a client token, make sure the one the server gave us matches our
- // existing one.
- qDebug() << "Getting client token.";
- QString clientToken = responseData.value("clientToken").toString("");
- if (clientToken.isEmpty())
- {
- // Fail if the server gave us an empty client token
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
- return;
- }
- if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
- return;
- }
- // Set the client token.
- m_account->m_clientToken = clientToken;
-
- // Now, we set the access token.
- qDebug() << "Getting access token.";
- QString accessToken = responseData.value("accessToken").toString("");
- if (accessToken.isEmpty())
- {
- // Fail if the server didn't give us an access token.
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
- return;
- }
- // Set the access token.
- m_account->m_accessToken = accessToken;
-
- // Now we load the list of available profiles.
- // Mojang hasn't yet implemented the profile system,
- // but we might as well support what's there so we
- // don't have trouble implementing it later.
- qDebug() << "Loading profile list.";
- QJsonArray availableProfiles = responseData.value("availableProfiles").toArray();
- QList<AccountProfile> loadedProfiles;
- for (auto iter : availableProfiles)
- {
- QJsonObject profile = iter.toObject();
- // Profiles are easy, we just need their ID and name.
- QString id = profile.value("id").toString("");
- QString name = profile.value("name").toString("");
- bool legacy = profile.value("legacy").toBool(false);
-
- if (id.isEmpty() || name.isEmpty())
- {
- // This should never happen, but we might as well
- // warn about it if it does so we can debug it easily.
- // You never know when Mojang might do something truly derpy.
- qWarning() << "Found entry in available profiles list with missing ID or name "
- "field. Ignoring it.";
- }
-
- // Now, add a new AccountProfile entry to the list.
- loadedProfiles.append({id, name, legacy});
- }
- // Put the list of profiles we loaded into the MojangAccount object.
- m_account->m_profiles = loadedProfiles;
-
- // Finally, we set the current profile to the correct value. This is pretty simple.
- // We do need to make sure that the current profile that the server gave us
- // is actually in the available profiles list.
- // If it isn't, we'll just fail horribly (*shouldn't* ever happen, but you never know).
- qDebug() << "Setting current profile.";
- QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
- QString currentProfileId = currentProfile.value("id").toString("");
- if (currentProfileId.isEmpty())
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify a currently selected profile. The account exists, but likely isn't premium."));
- return;
- }
- if (!m_account->setCurrentProfile(currentProfileId))
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server specified a selected profile that wasn't in the available profiles list."));
- return;
- }
-
- // this is what the vanilla launcher passes to the userProperties launch param
- if (responseData.contains("user"))
- {
- User u;
- auto obj = responseData.value("user").toObject();
- u.id = obj.value("id").toString();
- auto propArray = obj.value("properties").toArray();
- for (auto prop : propArray)
- {
- auto propTuple = prop.toObject();
- auto name = propTuple.value("name").toString();
- auto value = propTuple.value("value").toString();
- u.properties.insert(name, value);
- }
- m_account->m_user = u;
- }
-
- // We've made it through the minefield of possible errors. Return true to indicate that
- // we've succeeded.
- qDebug() << "Finished reading authentication response.";
- changeState(STATE_SUCCEEDED);
+ // Read the response data. We need to get the client token, access token, and the selected
+ // profile.
+ qDebug() << "Processing authentication response.";
+ // qDebug() << responseData;
+ // If we already have a client token, make sure the one the server gave us matches our
+ // existing one.
+ qDebug() << "Getting client token.";
+ QString clientToken = responseData.value("clientToken").toString("");
+ if (clientToken.isEmpty())
+ {
+ // Fail if the server gave us an empty client token
+ changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
+ return;
+ }
+ if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
+ {
+ changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
+ return;
+ }
+ // Set the client token.
+ m_account->m_clientToken = clientToken;
+
+ // Now, we set the access token.
+ qDebug() << "Getting access token.";
+ QString accessToken = responseData.value("accessToken").toString("");
+ if (accessToken.isEmpty())
+ {
+ // Fail if the server didn't give us an access token.
+ changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
+ return;
+ }
+ // Set the access token.
+ m_account->m_accessToken = accessToken;
+
+ // Now we load the list of available profiles.
+ // Mojang hasn't yet implemented the profile system,
+ // but we might as well support what's there so we
+ // don't have trouble implementing it later.
+ qDebug() << "Loading profile list.";
+ QJsonArray availableProfiles = responseData.value("availableProfiles").toArray();
+ QList<AccountProfile> loadedProfiles;
+ for (auto iter : availableProfiles)
+ {
+ QJsonObject profile = iter.toObject();
+ // Profiles are easy, we just need their ID and name.
+ QString id = profile.value("id").toString("");
+ QString name = profile.value("name").toString("");
+ bool legacy = profile.value("legacy").toBool(false);
+
+ if (id.isEmpty() || name.isEmpty())
+ {
+ // This should never happen, but we might as well
+ // warn about it if it does so we can debug it easily.
+ // You never know when Mojang might do something truly derpy.
+ qWarning() << "Found entry in available profiles list with missing ID or name "
+ "field. Ignoring it.";
+ }
+
+ // Now, add a new AccountProfile entry to the list.
+ loadedProfiles.append({id, name, legacy});
+ }
+ // Put the list of profiles we loaded into the MojangAccount object.
+ m_account->m_profiles = loadedProfiles;
+
+ // Finally, we set the current profile to the correct value. This is pretty simple.
+ // We do need to make sure that the current profile that the server gave us
+ // is actually in the available profiles list.
+ // If it isn't, we'll just fail horribly (*shouldn't* ever happen, but you never know).
+ qDebug() << "Setting current profile.";
+ QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
+ QString currentProfileId = currentProfile.value("id").toString("");
+ if (currentProfileId.isEmpty())
+ {
+ changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify a currently selected profile. The account exists, but likely isn't premium."));
+ return;
+ }
+ if (!m_account->setCurrentProfile(currentProfileId))
+ {
+ changeState(STATE_FAILED_HARD, tr("Authentication server specified a selected profile that wasn't in the available profiles list."));
+ return;
+ }
+
+ // this is what the vanilla launcher passes to the userProperties launch param
+ if (responseData.contains("user"))
+ {
+ User u;
+ auto obj = responseData.value("user").toObject();
+ u.id = obj.value("id").toString();
+ auto propArray = obj.value("properties").toArray();
+ for (auto prop : propArray)
+ {
+ auto propTuple = prop.toObject();
+ auto name = propTuple.value("name").toString();
+ auto value = propTuple.value("value").toString();
+ u.properties.insert(name, value);
+ }
+ m_account->m_user = u;
+ }
+
+ // We've made it through the minefield of possible errors. Return true to indicate that
+ // we've succeeded.
+ qDebug() << "Finished reading authentication response.";
+ changeState(STATE_SUCCEEDED);
}
QString AuthenticateTask::getEndpoint() const
{
- return "authenticate";
+ return "authenticate";
}
QString AuthenticateTask::getStateMessage() const
{
- switch (m_state)
- {
- case STATE_SENDING_REQUEST:
- return tr("Authenticating: Sending request...");
- case STATE_PROCESSING_RESPONSE:
- return tr("Authenticating: Processing response...");
- default:
- return YggdrasilTask::getStateMessage();
- }
+ switch (m_state)
+ {
+ case STATE_SENDING_REQUEST:
+ return tr("Authenticating: Sending request...");
+ case STATE_PROCESSING_RESPONSE:
+ return tr("Authenticating: Processing response...");
+ default:
+ return YggdrasilTask::getStateMessage();
+ }
}
diff --git a/api/logic/minecraft/auth/flows/AuthenticateTask.h b/api/logic/minecraft/auth/flows/AuthenticateTask.h
index ab7a6e64..e95837a9 100644
--- a/api/logic/minecraft/auth/flows/AuthenticateTask.h
+++ b/api/logic/minecraft/auth/flows/AuthenticateTask.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,19 +28,19 @@
*/
class AuthenticateTask : public YggdrasilTask
{
- Q_OBJECT
+ Q_OBJECT
public:
- AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0);
+ AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0);
protected:
- virtual QJsonObject getRequestContent() const override;
+ virtual QJsonObject getRequestContent() const override;
- virtual QString getEndpoint() const override;
+ virtual QString getEndpoint() const override;
- virtual void processResponse(QJsonObject responseData) override;
+ virtual void processResponse(QJsonObject responseData) override;
- virtual QString getStateMessage() const override;
+ virtual QString getStateMessage() const override;
private:
- QString m_password;
+ QString m_password;
};
diff --git a/api/logic/minecraft/auth/flows/RefreshTask.cpp b/api/logic/minecraft/auth/flows/RefreshTask.cpp
index 73050907..7a789f42 100644
--- a/api/logic/minecraft/auth/flows/RefreshTask.cpp
+++ b/api/logic/minecraft/auth/flows/RefreshTask.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,116 +29,116 @@ RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account)
QJsonObject RefreshTask::getRequestContent() const
{
- /*
- * {
- * "clientToken": "client identifier"
- * "accessToken": "current access token to be refreshed"
- * "selectedProfile": // specifying this causes errors
- * {
- * "id": "profile ID"
- * "name": "profile name"
- * }
- * "requestUser": true/false // request the user structure
- * }
- */
- QJsonObject req;
- req.insert("clientToken", m_account->m_clientToken);
- req.insert("accessToken", m_account->m_accessToken);
- /*
- {
- auto currentProfile = m_account->currentProfile();
- QJsonObject profile;
- profile.insert("id", currentProfile->id());
- profile.insert("name", currentProfile->name());
- req.insert("selectedProfile", profile);
- }
- */
- req.insert("requestUser", true);
+ /*
+ * {
+ * "clientToken": "client identifier"
+ * "accessToken": "current access token to be refreshed"
+ * "selectedProfile": // specifying this causes errors
+ * {
+ * "id": "profile ID"
+ * "name": "profile name"
+ * }
+ * "requestUser": true/false // request the user structure
+ * }
+ */
+ QJsonObject req;
+ req.insert("clientToken", m_account->m_clientToken);
+ req.insert("accessToken", m_account->m_accessToken);
+ /*
+ {
+ auto currentProfile = m_account->currentProfile();
+ QJsonObject profile;
+ profile.insert("id", currentProfile->id());
+ profile.insert("name", currentProfile->name());
+ req.insert("selectedProfile", profile);
+ }
+ */
+ req.insert("requestUser", true);
- return req;
+ return req;
}
void RefreshTask::processResponse(QJsonObject responseData)
{
- // Read the response data. We need to get the client token, access token, and the selected
- // profile.
- qDebug() << "Processing authentication response.";
+ // Read the response data. We need to get the client token, access token, and the selected
+ // profile.
+ qDebug() << "Processing authentication response.";
- // qDebug() << responseData;
- // If we already have a client token, make sure the one the server gave us matches our
- // existing one.
- QString clientToken = responseData.value("clientToken").toString("");
- if (clientToken.isEmpty())
- {
- // Fail if the server gave us an empty client token
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
- return;
- }
- if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
- return;
- }
+ // qDebug() << responseData;
+ // If we already have a client token, make sure the one the server gave us matches our
+ // existing one.
+ QString clientToken = responseData.value("clientToken").toString("");
+ if (clientToken.isEmpty())
+ {
+ // Fail if the server gave us an empty client token
+ changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
+ return;
+ }
+ if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
+ {
+ changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
+ return;
+ }
- // Now, we set the access token.
- qDebug() << "Getting new access token.";
- QString accessToken = responseData.value("accessToken").toString("");
- if (accessToken.isEmpty())
- {
- // Fail if the server didn't give us an access token.
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
- return;
- }
+ // Now, we set the access token.
+ qDebug() << "Getting new access token.";
+ QString accessToken = responseData.value("accessToken").toString("");
+ if (accessToken.isEmpty())
+ {
+ // Fail if the server didn't give us an access token.
+ changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
+ return;
+ }
- // we validate that the server responded right. (our current profile = returned current
- // profile)
- QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
- QString currentProfileId = currentProfile.value("id").toString("");
- if (m_account->currentProfile()->id != currentProfileId)
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify the same prefile as expected."));
- return;
- }
+ // we validate that the server responded right. (our current profile = returned current
+ // profile)
+ QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
+ QString currentProfileId = currentProfile.value("id").toString("");
+ if (m_account->currentProfile()->id != currentProfileId)
+ {
+ changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify the same prefile as expected."));
+ return;
+ }
- // this is what the vanilla launcher passes to the userProperties launch param
- if (responseData.contains("user"))
- {
- User u;
- auto obj = responseData.value("user").toObject();
- u.id = obj.value("id").toString();
- auto propArray = obj.value("properties").toArray();
- for (auto prop : propArray)
- {
- auto propTuple = prop.toObject();
- auto name = propTuple.value("name").toString();
- auto value = propTuple.value("value").toString();
- u.properties.insert(name, value);
- }
- m_account->m_user = u;
- }
+ // this is what the vanilla launcher passes to the userProperties launch param
+ if (responseData.contains("user"))
+ {
+ User u;
+ auto obj = responseData.value("user").toObject();
+ u.id = obj.value("id").toString();
+ auto propArray = obj.value("properties").toArray();
+ for (auto prop : propArray)
+ {
+ auto propTuple = prop.toObject();
+ auto name = propTuple.value("name").toString();
+ auto value = propTuple.value("value").toString();
+ u.properties.insert(name, value);
+ }
+ m_account->m_user = u;
+ }
- // We've made it through the minefield of possible errors. Return true to indicate that
- // we've succeeded.
- qDebug() << "Finished reading refresh response.";
- // Reset the access token.
- m_account->m_accessToken = accessToken;
- changeState(STATE_SUCCEEDED);
+ // We've made it through the minefield of possible errors. Return true to indicate that
+ // we've succeeded.
+ qDebug() << "Finished reading refresh response.";
+ // Reset the access token.
+ m_account->m_accessToken = accessToken;
+ changeState(STATE_SUCCEEDED);
}
QString RefreshTask::getEndpoint() const
{
- return "refresh";
+ return "refresh";
}
QString RefreshTask::getStateMessage() const
{
- switch (m_state)
- {
- case STATE_SENDING_REQUEST:
- return tr("Refreshing login token...");
- case STATE_PROCESSING_RESPONSE:
- return tr("Refreshing login token: Processing response...");
- default:
- return YggdrasilTask::getStateMessage();
- }
+ switch (m_state)
+ {
+ case STATE_SENDING_REQUEST:
+ return tr("Refreshing login token...");
+ case STATE_PROCESSING_RESPONSE:
+ return tr("Refreshing login token: Processing response...");
+ default:
+ return YggdrasilTask::getStateMessage();
+ }
}
diff --git a/api/logic/minecraft/auth/flows/RefreshTask.h b/api/logic/minecraft/auth/flows/RefreshTask.h
index a97b54e6..b2dfd261 100644
--- a/api/logic/minecraft/auth/flows/RefreshTask.h
+++ b/api/logic/minecraft/auth/flows/RefreshTask.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,17 +28,17 @@
*/
class RefreshTask : public YggdrasilTask
{
- Q_OBJECT
+ Q_OBJECT
public:
- RefreshTask(MojangAccount * account);
+ RefreshTask(MojangAccount * account);
protected:
- virtual QJsonObject getRequestContent() const override;
+ virtual QJsonObject getRequestContent() const override;
- virtual QString getEndpoint() const override;
+ virtual QString getEndpoint() const override;
- virtual void processResponse(QJsonObject responseData) override;
+ virtual void processResponse(QJsonObject responseData) override;
- virtual QString getStateMessage() const override;
+ virtual QString getStateMessage() const override;
};
diff --git a/api/logic/minecraft/auth/flows/ValidateTask.cpp b/api/logic/minecraft/auth/flows/ValidateTask.cpp
index b6d6c823..01f0f819 100644
--- a/api/logic/minecraft/auth/flows/ValidateTask.cpp
+++ b/api/logic/minecraft/auth/flows/ValidateTask.cpp
@@ -1,5 +1,5 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,37 +25,37 @@
#include <QDebug>
ValidateTask::ValidateTask(MojangAccount * account, QObject *parent)
- : YggdrasilTask(account, parent)
+ : YggdrasilTask(account, parent)
{
}
QJsonObject ValidateTask::getRequestContent() const
{
- QJsonObject req;
- req.insert("accessToken", m_account->m_accessToken);
- return req;
+ QJsonObject req;
+ req.insert("accessToken", m_account->m_accessToken);
+ return req;
}
void ValidateTask::processResponse(QJsonObject responseData)
{
- // Assume that if processError wasn't called, then the request was successful.
- changeState(YggdrasilTask::STATE_SUCCEEDED);
+ // Assume that if processError wasn't called, then the request was successful.
+ changeState(YggdrasilTask::STATE_SUCCEEDED);
}
QString ValidateTask::getEndpoint() const
{
- return "validate";
+ return "validate";
}
QString ValidateTask::getStateMessage() const
{
- switch (m_state)
- {
- case YggdrasilTask::STATE_SENDING_REQUEST:
- return tr("Validating access token: Sending request...");
- case YggdrasilTask::STATE_PROCESSING_RESPONSE:
- return tr("Validating access token: Processing response...");
- default:
- return YggdrasilTask::getStateMessage();
- }
+ switch (m_state)
+ {
+ case YggdrasilTask::STATE_SENDING_REQUEST:
+ return tr("Validating access token: Sending request...");
+ case YggdrasilTask::STATE_PROCESSING_RESPONSE:
+ return tr("Validating access token: Processing response...");
+ default:
+ return YggdrasilTask::getStateMessage();
+ }
}
diff --git a/api/logic/minecraft/auth/flows/ValidateTask.h b/api/logic/minecraft/auth/flows/ValidateTask.h
index 77de24c7..dcb76b2b 100644
--- a/api/logic/minecraft/auth/flows/ValidateTask.h
+++ b/api/logic/minecraft/auth/flows/ValidateTask.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,18 +30,18 @@
*/
class ValidateTask : public YggdrasilTask
{
- Q_OBJECT
+ Q_OBJECT
public:
- ValidateTask(MojangAccount *account, QObject *parent = 0);
+ ValidateTask(MojangAccount *account, QObject *parent = 0);
protected:
- virtual QJsonObject getRequestContent() const override;
+ virtual QJsonObject getRequestContent() const override;
- virtual QString getEndpoint() const override;
+ virtual QString getEndpoint() const override;
- virtual void processResponse(QJsonObject responseData) override;
+ virtual void processResponse(QJsonObject responseData) override;
- virtual QString getStateMessage() const override;
+ virtual QString getStateMessage() const override;
private:
};
diff --git a/api/logic/minecraft/forge/ForgeXzDownload.cpp b/api/logic/minecraft/forge/ForgeXzDownload.cpp
index a05d0f8d..c6465469 100644
--- a/api/logic/minecraft/forge/ForgeXzDownload.cpp
+++ b/api/logic/minecraft/forge/ForgeXzDownload.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,140 +23,140 @@
#include <QDir>
#include <QDebug>
-ForgeXzDownload::ForgeXzDownload(QString relative_path, MetaEntryPtr entry) : NetAction()
+ForgeXzDownload::ForgeXzDownload(QString url, QString relative_path, MetaEntryPtr entry) : NetAction()
{
- m_entry = entry;
- m_target_path = entry->getFullPath();
- m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX");
- m_status = Job_NotStarted;
- m_url_path = relative_path;
- m_url = "http://files.minecraftforge.net/maven/" + m_url_path + ".pack.xz";
+ m_entry = entry;
+ m_target_path = entry->getFullPath();
+ m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX");
+ m_status = Job_NotStarted;
+ m_url_path = relative_path;
+ m_url = url + ".pack.xz";
}
void ForgeXzDownload::start()
{
- if(m_status == Job_Aborted)
- {
- qWarning() << "Attempt to start an aborted Download:" << m_url.toString();
- emit aborted(m_index_within_job);
- return;
- }
- m_status = Job_InProgress;
- if (!m_entry->isStale())
- {
- m_status = Job_Finished;
- emit succeeded(m_index_within_job);
- return;
- }
- // can we actually create the real, final file?
- if (!FS::ensureFilePathExists(m_target_path))
- {
- m_status = Job_Failed;
- emit failed(m_index_within_job);
- return;
- }
-
- qDebug() << "Downloading " << m_url.toString();
- QNetworkRequest request(m_url);
- request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1());
- request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
-
- QNetworkReply *rep = ENV.qnam().get(request);
-
- m_reply.reset(rep);
- connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64)));
- connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
- connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
- connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+ if(m_status == Job_Aborted)
+ {
+ qWarning() << "Attempt to start an aborted Download:" << m_url.toString();
+ emit aborted(m_index_within_job);
+ return;
+ }
+ m_status = Job_InProgress;
+ if (!m_entry->isStale())
+ {
+ m_status = Job_Finished;
+ emit succeeded(m_index_within_job);
+ return;
+ }
+ // can we actually create the real, final file?
+ if (!FS::ensureFilePathExists(m_target_path))
+ {
+ m_status = Job_Failed;
+ emit failed(m_index_within_job);
+ return;
+ }
+
+ qDebug() << "Downloading " << m_url.toString();
+ QNetworkRequest request(m_url);
+ request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1());
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
+
+ QNetworkReply *rep = ENV.qnam().get(request);
+
+ m_reply.reset(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
}
void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
- m_total_progress = bytesTotal;
- m_progress = bytesReceived;
- emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
}
void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error)
{
- if(error == QNetworkReply::OperationCanceledError)
- {
- qCritical() << "Aborted " << m_url.toString();
- m_status = Job_Aborted;
- }
- else
- {
- // error happened during download.
- qCritical() << "Failed " << m_url.toString() << " with reason " << error;
- m_status = Job_Failed;
- }
+ if(error == QNetworkReply::OperationCanceledError)
+ {
+ qCritical() << "Aborted " << m_url.toString();
+ m_status = Job_Aborted;
+ }
+ else
+ {
+ // error happened during download.
+ qCritical() << "Failed " << m_url.toString() << " with reason " << error;
+ m_status = Job_Failed;
+ }
}
void ForgeXzDownload::failAndTryNextMirror()
{
- m_status = Job_Failed;
- emit failed(m_index_within_job);
+ m_status = Job_Failed;
+ emit failed(m_index_within_job);
}
void ForgeXzDownload::downloadFinished()
{
- // if the download succeeded
- if (m_status != Job_Failed && m_status != Job_Aborted)
- {
- // nothing went wrong...
- m_status = Job_Finished;
- if (m_pack200_xz_file.isOpen())
- {
- // we actually downloaded something! process and isntall it
- decompressAndInstall();
- return;
- }
- else
- {
- // something bad happened -- on the local machine!
- m_status = Job_Failed;
- m_pack200_xz_file.remove();
- m_reply.reset();
- emit failed(m_index_within_job);
- return;
- }
- }
- else if(m_status == Job_Aborted)
- {
- m_pack200_xz_file.remove();
- m_reply.reset();
- emit failed(m_index_within_job);
- emit aborted(m_index_within_job);
- return;
- }
- // else the download failed
- else
- {
- m_status = Job_Failed;
- m_pack200_xz_file.close();
- m_pack200_xz_file.remove();
- m_reply.reset();
- failAndTryNextMirror();
- return;
- }
+ // if the download succeeded
+ if (m_status != Job_Failed && m_status != Job_Aborted)
+ {
+ // nothing went wrong...
+ m_status = Job_Finished;
+ if (m_pack200_xz_file.isOpen())
+ {
+ // we actually downloaded something! process and isntall it
+ decompressAndInstall();
+ return;
+ }
+ else
+ {
+ // something bad happened -- on the local machine!
+ m_status = Job_Failed;
+ m_pack200_xz_file.remove();
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
+ }
+ else if(m_status == Job_Aborted)
+ {
+ m_pack200_xz_file.remove();
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ emit aborted(m_index_within_job);
+ return;
+ }
+ // else the download failed
+ else
+ {
+ m_status = Job_Failed;
+ m_pack200_xz_file.close();
+ m_pack200_xz_file.remove();
+ m_reply.reset();
+ failAndTryNextMirror();
+ return;
+ }
}
void ForgeXzDownload::downloadReadyRead()
{
- if (!m_pack200_xz_file.isOpen())
- {
- if (!m_pack200_xz_file.open())
- {
- /*
- * Can't open the file... the job failed
- */
- m_reply->abort();
- emit failed(m_index_within_job);
- return;
- }
- }
- m_pack200_xz_file.write(m_reply->readAll());
+ if (!m_pack200_xz_file.isOpen())
+ {
+ if (!m_pack200_xz_file.open())
+ {
+ /*
+ * Can't open the file... the job failed
+ */
+ m_reply->abort();
+ emit failed(m_index_within_job);
+ return;
+ }
+ }
+ m_pack200_xz_file.write(m_reply->readAll());
}
#include "xz.h"
@@ -169,225 +169,225 @@ const size_t buffer_size = 8196;
// NOTE: once this gets here, it can't be aborted anymore. we don't care.
void ForgeXzDownload::decompressAndInstall()
{
- // rewind the downloaded temp file
- m_pack200_xz_file.seek(0);
- // de-xz'd file
- QTemporaryFile pack200_file("./dl_temp.XXXXXX");
- pack200_file.open();
-
- bool xz_success = false;
- // first, de-xz
- {
- uint8_t in[buffer_size];
- uint8_t out[buffer_size];
- struct xz_buf b;
- struct xz_dec *s;
- enum xz_ret ret;
- xz_crc32_init();
- xz_crc64_init();
- s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
- if (s == nullptr)
- {
- xz_dec_end(s);
- failAndTryNextMirror();
- return;
- }
- b.in = in;
- b.in_pos = 0;
- b.in_size = 0;
- b.out = out;
- b.out_pos = 0;
- b.out_size = buffer_size;
- while (!xz_success)
- {
- if (b.in_pos == b.in_size)
- {
- b.in_size = m_pack200_xz_file.read((char *)in, sizeof(in));
- b.in_pos = 0;
- }
-
- ret = xz_dec_run(s, &b);
-
- if (b.out_pos == sizeof(out))
- {
- auto wresult = pack200_file.write((char *)out, b.out_pos);
- if (wresult < 0 || size_t(wresult) != b.out_pos)
- {
- // msg = "Write error\n";
- xz_dec_end(s);
- failAndTryNextMirror();
- return;
- }
-
- b.out_pos = 0;
- }
-
- if (ret == XZ_OK)
- continue;
-
- if (ret == XZ_UNSUPPORTED_CHECK)
- {
- // unsupported check. this is OK, but we should log this
- continue;
- }
-
- auto wresult = pack200_file.write((char *)out, b.out_pos);
- if (wresult < 0 || size_t(wresult) != b.out_pos)
- {
- // write error
- pack200_file.close();
- xz_dec_end(s);
- return;
- }
-
- switch (ret)
- {
- case XZ_STREAM_END:
- xz_dec_end(s);
- xz_success = true;
- break;
-
- case XZ_MEM_ERROR:
- qCritical() << "Memory allocation failed\n";
- xz_dec_end(s);
- failAndTryNextMirror();
- return;
-
- case XZ_MEMLIMIT_ERROR:
- qCritical() << "Memory usage limit reached\n";
- xz_dec_end(s);
- failAndTryNextMirror();
- return;
-
- case XZ_FORMAT_ERROR:
- qCritical() << "Not a .xz file\n";
- xz_dec_end(s);
- failAndTryNextMirror();
- return;
-
- case XZ_OPTIONS_ERROR:
- qCritical() << "Unsupported options in the .xz headers\n";
- xz_dec_end(s);
- failAndTryNextMirror();
- return;
-
- case XZ_DATA_ERROR:
- case XZ_BUF_ERROR:
- qCritical() << "File is corrupt\n";
- xz_dec_end(s);
- failAndTryNextMirror();
- return;
-
- default:
- qCritical() << "Bug!\n";
- xz_dec_end(s);
- failAndTryNextMirror();
- return;
- }
- }
- }
- m_pack200_xz_file.remove();
-
- // revert pack200
- pack200_file.seek(0);
- int handle_in = pack200_file.handle();
- // FIXME: dispose of file handles, pointers and the like. Ideally wrap in objects.
- if(handle_in == -1)
- {
- qCritical() << "Error reopening " << pack200_file.fileName();
- failAndTryNextMirror();
- return;
- }
- int handle_in_dup = dup (handle_in);
- if(handle_in_dup == -1)
- {
- qCritical() << "Error reopening " << pack200_file.fileName();
- failAndTryNextMirror();
- return;
- }
- FILE *file_in = fdopen (handle_in_dup, "rb");
- if(!file_in)
- {
- qCritical() << "Error reopening " << pack200_file.fileName();
- failAndTryNextMirror();
- return;
- }
- QFile qfile_out(m_target_path);
- if(!qfile_out.open(QIODevice::WriteOnly))
- {
- qCritical() << "Error opening " << qfile_out.fileName();
- failAndTryNextMirror();
- return;
- }
- int handle_out = qfile_out.handle();
- if(handle_out == -1)
- {
- qCritical() << "Error opening " << qfile_out.fileName();
- failAndTryNextMirror();
- return;
- }
- int handle_out_dup = dup (handle_out);
- if(handle_out_dup == -1)
- {
- qCritical() << "Error reopening " << qfile_out.fileName();
- failAndTryNextMirror();
- return;
- }
- FILE *file_out = fdopen (handle_out_dup, "wb");
- if(!file_out)
- {
- qCritical() << "Error opening " << qfile_out.fileName();
- failAndTryNextMirror();
- return;
- }
- try
- {
- // NOTE: this takes ownership of both FILE pointers. That's why we duplicate them above.
- unpack_200(file_in, file_out);
- }
- catch (std::runtime_error &err)
- {
- m_status = Job_Failed;
- qCritical() << "Error unpacking " << pack200_file.fileName() << " : " << err.what();
- QFile f(m_target_path);
- if (f.exists())
- f.remove();
- failAndTryNextMirror();
- return;
- }
- pack200_file.remove();
-
- QFile jar_file(m_target_path);
-
- if (!jar_file.open(QIODevice::ReadOnly))
- {
- jar_file.remove();
- failAndTryNextMirror();
- return;
- }
- auto hash = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5);
- m_entry->setMD5Sum(hash.toHex().constData());
- jar_file.close();
-
- QFileInfo output_file_info(m_target_path);
- m_entry->setETag(m_reply->rawHeader("ETag").constData());
- m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch());
- m_entry->setStale(false);
- ENV.metacache()->updateEntry(m_entry);
-
- m_reply.reset();
- emit succeeded(m_index_within_job);
+ // rewind the downloaded temp file
+ m_pack200_xz_file.seek(0);
+ // de-xz'd file
+ QTemporaryFile pack200_file("./dl_temp.XXXXXX");
+ pack200_file.open();
+
+ bool xz_success = false;
+ // first, de-xz
+ {
+ uint8_t in[buffer_size];
+ uint8_t out[buffer_size];
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+ xz_crc32_init();
+ xz_crc64_init();
+ s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+ if (s == nullptr)
+ {
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+ }
+ b.in = in;
+ b.in_pos = 0;
+ b.in_size = 0;
+ b.out = out;
+ b.out_pos = 0;
+ b.out_size = buffer_size;
+ while (!xz_success)
+ {
+ if (b.in_pos == b.in_size)
+ {
+ b.in_size = m_pack200_xz_file.read((char *)in, sizeof(in));
+ b.in_pos = 0;
+ }
+
+ ret = xz_dec_run(s, &b);
+
+ if (b.out_pos == sizeof(out))
+ {
+ auto wresult = pack200_file.write((char *)out, b.out_pos);
+ if (wresult < 0 || size_t(wresult) != b.out_pos)
+ {
+ // msg = "Write error\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+ }
+
+ b.out_pos = 0;
+ }
+
+ if (ret == XZ_OK)
+ continue;
+
+ if (ret == XZ_UNSUPPORTED_CHECK)
+ {
+ // unsupported check. this is OK, but we should log this
+ continue;
+ }
+
+ auto wresult = pack200_file.write((char *)out, b.out_pos);
+ if (wresult < 0 || size_t(wresult) != b.out_pos)
+ {
+ // write error
+ pack200_file.close();
+ xz_dec_end(s);
+ return;
+ }
+
+ switch (ret)
+ {
+ case XZ_STREAM_END:
+ xz_dec_end(s);
+ xz_success = true;
+ break;
+
+ case XZ_MEM_ERROR:
+ qCritical() << "Memory allocation failed\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ case XZ_MEMLIMIT_ERROR:
+ qCritical() << "Memory usage limit reached\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ case XZ_FORMAT_ERROR:
+ qCritical() << "Not a .xz file\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ case XZ_OPTIONS_ERROR:
+ qCritical() << "Unsupported options in the .xz headers\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ case XZ_DATA_ERROR:
+ case XZ_BUF_ERROR:
+ qCritical() << "File is corrupt\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ default:
+ qCritical() << "Bug!\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+ }
+ }
+ }
+ m_pack200_xz_file.remove();
+
+ // revert pack200
+ pack200_file.seek(0);
+ int handle_in = pack200_file.handle();
+ // FIXME: dispose of file handles, pointers and the like. Ideally wrap in objects.
+ if(handle_in == -1)
+ {
+ qCritical() << "Error reopening " << pack200_file.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ int handle_in_dup = dup (handle_in);
+ if(handle_in_dup == -1)
+ {
+ qCritical() << "Error reopening " << pack200_file.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ FILE *file_in = fdopen (handle_in_dup, "rb");
+ if(!file_in)
+ {
+ qCritical() << "Error reopening " << pack200_file.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ QFile qfile_out(m_target_path);
+ if(!qfile_out.open(QIODevice::WriteOnly))
+ {
+ qCritical() << "Error opening " << qfile_out.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ int handle_out = qfile_out.handle();
+ if(handle_out == -1)
+ {
+ qCritical() << "Error opening " << qfile_out.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ int handle_out_dup = dup (handle_out);
+ if(handle_out_dup == -1)
+ {
+ qCritical() << "Error reopening " << qfile_out.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ FILE *file_out = fdopen (handle_out_dup, "wb");
+ if(!file_out)
+ {
+ qCritical() << "Error opening " << qfile_out.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ try
+ {
+ // NOTE: this takes ownership of both FILE pointers. That's why we duplicate them above.
+ unpack_200(file_in, file_out);
+ }
+ catch (const std::runtime_error &err)
+ {
+ m_status = Job_Failed;
+ qCritical() << "Error unpacking " << pack200_file.fileName() << " : " << err.what();
+ QFile f(m_target_path);
+ if (f.exists())
+ f.remove();
+ failAndTryNextMirror();
+ return;
+ }
+ pack200_file.remove();
+
+ QFile jar_file(m_target_path);
+
+ if (!jar_file.open(QIODevice::ReadOnly))
+ {
+ jar_file.remove();
+ failAndTryNextMirror();
+ return;
+ }
+ auto hash = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5);
+ m_entry->setMD5Sum(hash.toHex().constData());
+ jar_file.close();
+
+ QFileInfo output_file_info(m_target_path);
+ m_entry->setETag(m_reply->rawHeader("ETag").constData());
+ m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch());
+ m_entry->setStale(false);
+ ENV.metacache()->updateEntry(m_entry);
+
+ m_reply.reset();
+ emit succeeded(m_index_within_job);
}
bool ForgeXzDownload::abort()
{
- if(m_reply)
- m_reply->abort();
- m_status = Job_Aborted;
- return true;
+ if(m_reply)
+ m_reply->abort();
+ m_status = Job_Aborted;
+ return true;
}
bool ForgeXzDownload::canAbort()
{
- return true;
+ return true;
}
diff --git a/api/logic/minecraft/forge/ForgeXzDownload.h b/api/logic/minecraft/forge/ForgeXzDownload.h
index cee402ef..63e75f71 100644
--- a/api/logic/minecraft/forge/ForgeXzDownload.h
+++ b/api/logic/minecraft/forge/ForgeXzDownload.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,38 +24,38 @@ typedef std::shared_ptr<class ForgeXzDownload> ForgeXzDownloadPtr;
class ForgeXzDownload : public NetAction
{
- Q_OBJECT
+ Q_OBJECT
public:
- MetaEntryPtr m_entry;
- /// if saving to file, use the one specified in this string
- QString m_target_path;
- /// this is the output file, if any
- QTemporaryFile m_pack200_xz_file;
- /// path relative to the mirror base
- QString m_url_path;
+ MetaEntryPtr m_entry;
+ /// if saving to file, use the one specified in this string
+ QString m_target_path;
+ /// this is the output file, if any
+ QTemporaryFile m_pack200_xz_file;
+ /// path relative to the mirror base
+ QString m_url_path;
public:
- explicit ForgeXzDownload(QString relative_path, MetaEntryPtr entry);
- static ForgeXzDownloadPtr make(QString relative_path, MetaEntryPtr entry)
- {
- return ForgeXzDownloadPtr(new ForgeXzDownload(relative_path, entry));
- }
- virtual ~ForgeXzDownload(){};
- bool canAbort() override;
+ explicit ForgeXzDownload(QString url, QString relative_path, MetaEntryPtr entry);
+ static ForgeXzDownloadPtr make(QString url, QString relative_path, MetaEntryPtr entry)
+ {
+ return ForgeXzDownloadPtr(new ForgeXzDownload(url, relative_path, entry));
+ }
+ virtual ~ForgeXzDownload(){};
+ bool canAbort() override;
protected
slots:
- void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
- void downloadError(QNetworkReply::NetworkError error) override;
- void downloadFinished() override;
- void downloadReadyRead() override;
+ void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
+ void downloadError(QNetworkReply::NetworkError error) override;
+ void downloadFinished() override;
+ void downloadReadyRead() override;
public
slots:
- void start() override;
- bool abort() override;
+ void start() override;
+ bool abort() override;
private:
- void decompressAndInstall();
- void failAndTryNextMirror();
+ void decompressAndInstall();
+ void failAndTryNextMirror();
};
diff --git a/api/logic/minecraft/gameoptions/GameOptions.cpp b/api/logic/minecraft/gameoptions/GameOptions.cpp
new file mode 100644
index 00000000..e547b32a
--- /dev/null
+++ b/api/logic/minecraft/gameoptions/GameOptions.cpp
@@ -0,0 +1,144 @@
+#include "GameOptions.h"
+#include "FileSystem.h"
+#include <QDebug>
+#include <QSaveFile>
+
+namespace {
+bool load(const QString& path, std::vector<GameOptionItem> &contents, int & version)
+{
+ contents.clear();
+ QFile file(path);
+ if (!file.open(QFile::ReadOnly))
+ {
+ qWarning() << "Failed to read options file.";
+ return false;
+ }
+ version = 0;
+ while(!file.atEnd())
+ {
+ auto line = file.readLine();
+ if(line.endsWith('\n'))
+ {
+ line.chop(1);
+ }
+ auto separatorIndex = line.indexOf(':');
+ if(separatorIndex == -1)
+ {
+ continue;
+ }
+ auto key = QString::fromUtf8(line.data(), separatorIndex);
+ auto value = QString::fromUtf8(line.data() + separatorIndex + 1, line.size() - 1 - separatorIndex);
+ qDebug() << "!!" << key << "!!";
+ if(key == "version")
+ {
+ version = value.toInt();
+ continue;
+ }
+ contents.emplace_back(GameOptionItem{key, value});
+ }
+ qDebug() << "Loaded" << path << "with version:" << version;
+ return true;
+}
+bool save(const QString& path, std::vector<GameOptionItem> &mapping, int version)
+{
+ QSaveFile out(path);
+ if(!out.open(QIODevice::WriteOnly))
+ {
+ return false;
+ }
+ if(version != 0)
+ {
+ QString versionLine = QString("version:%1\n").arg(version);
+ out.write(versionLine.toUtf8());
+ }
+ auto iter = mapping.begin();
+ while (iter != mapping.end())
+ {
+ out.write(iter->key.toUtf8());
+ out.write(":");
+ out.write(iter->value.toUtf8());
+ out.write("\n");
+ iter++;
+ }
+ return out.commit();
+}
+}
+
+GameOptions::GameOptions(const QString& path):
+ path(path)
+{
+ reload();
+}
+
+QVariant GameOptions::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if(role != Qt::DisplayRole)
+ {
+ return QAbstractListModel::headerData(section, orientation, role);
+ }
+ switch(section)
+ {
+ case 0:
+ return tr("Key");
+ case 1:
+ return tr("Value");
+ default:
+ return QVariant();
+ }
+}
+
+QVariant GameOptions::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+ int column = index.column();
+
+ if (row < 0 || row >= int(contents.size()))
+ return QVariant();
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ if(column == 0)
+ {
+ return contents[row].key;
+ }
+ else
+ {
+ return contents[row].value;
+ }
+ default:
+ return QVariant();
+ }
+ return QVariant();
+}
+
+int GameOptions::rowCount(const QModelIndex&) const
+{
+ return contents.size();
+}
+
+int GameOptions::columnCount(const QModelIndex&) const
+{
+ return 2;
+}
+
+bool GameOptions::isLoaded() const
+{
+ return loaded;
+}
+
+bool GameOptions::reload()
+{
+ beginResetModel();
+ loaded = load(path, contents, version);
+ endResetModel();
+ return loaded;
+}
+
+bool GameOptions::save()
+{
+ return ::save(path, contents, version);
+}
diff --git a/api/logic/minecraft/gameoptions/GameOptions.h b/api/logic/minecraft/gameoptions/GameOptions.h
new file mode 100644
index 00000000..c6d25492
--- /dev/null
+++ b/api/logic/minecraft/gameoptions/GameOptions.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <map>
+#include <QString>
+#include <QAbstractListModel>
+
+struct GameOptionItem
+{
+ QString key;
+ QString value;
+};
+
+class GameOptions : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit GameOptions(const QString& path);
+ virtual ~GameOptions() = default;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex & parent) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+
+ bool isLoaded() const;
+ bool reload();
+ bool save();
+
+private:
+ std::vector<GameOptionItem> contents;
+ bool loaded = false;
+ QString path;
+ int version = 0;
+};
diff --git a/api/logic/minecraft/launch/ClaimAccount.cpp b/api/logic/minecraft/launch/ClaimAccount.cpp
index 71670b4f..a1180f0a 100644
--- a/api/logic/minecraft/launch/ClaimAccount.cpp
+++ b/api/logic/minecraft/launch/ClaimAccount.cpp
@@ -3,22 +3,22 @@
ClaimAccount::ClaimAccount(LaunchTask* parent, AuthSessionPtr session): LaunchStep(parent)
{
- if(session->status == AuthSession::Status::PlayableOnline)
- {
- m_account = session->m_accountPtr;
- }
+ if(session->status == AuthSession::Status::PlayableOnline)
+ {
+ m_account = session->m_accountPtr;
+ }
}
void ClaimAccount::executeTask()
{
- if(m_account)
- {
- lock.reset(new UseLock(m_account));
- emitSucceeded();
- }
+ if(m_account)
+ {
+ lock.reset(new UseLock(m_account));
+ emitSucceeded();
+ }
}
void ClaimAccount::finalize()
{
- lock.reset();
+ lock.reset();
}
diff --git a/api/logic/minecraft/launch/ClaimAccount.h b/api/logic/minecraft/launch/ClaimAccount.h
index de9007d1..d0892981 100644
--- a/api/logic/minecraft/launch/ClaimAccount.h
+++ b/api/logic/minecraft/launch/ClaimAccount.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,16 +20,18 @@
class ClaimAccount: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit ClaimAccount(LaunchTask *parent, AuthSessionPtr session);
- void executeTask() override;
- void finalize() override;
- bool canAbort() const override
- {
- return false;
- }
+ explicit ClaimAccount(LaunchTask *parent, AuthSessionPtr session);
+ virtual ~ClaimAccount() {};
+
+ void executeTask() override;
+ void finalize() override;
+ bool canAbort() const override
+ {
+ return false;
+ }
private:
- std::unique_ptr<UseLock> lock;
- MojangAccountPtr m_account;
+ std::unique_ptr<UseLock> lock;
+ MojangAccountPtr m_account;
};
diff --git a/api/logic/minecraft/launch/CreateServerResourcePacksFolder.cpp b/api/logic/minecraft/launch/CreateServerResourcePacksFolder.cpp
index 5398f7d0..ae426e31 100644
--- a/api/logic/minecraft/launch/CreateServerResourcePacksFolder.cpp
+++ b/api/logic/minecraft/launch/CreateServerResourcePacksFolder.cpp
@@ -9,11 +9,11 @@ CreateServerResourcePacksFolder::CreateServerResourcePacksFolder(LaunchTask* par
void CreateServerResourcePacksFolder::executeTask()
{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
- if(!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->minecraftRoot(), "server-resource-packs")))
- {
- emit logLine(tr("Couldn't create the 'server-resource-packs' folder"), MessageLevel::Error);
- }
- emitSucceeded();
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
+ if(!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->gameRoot(), "server-resource-packs")))
+ {
+ emit logLine(tr("Couldn't create the 'server-resource-packs' folder"), MessageLevel::Error);
+ }
+ emitSucceeded();
}
diff --git a/api/logic/minecraft/launch/CreateServerResourcePacksFolder.h b/api/logic/minecraft/launch/CreateServerResourcePacksFolder.h
index 92026ecb..00ab2f2d 100644
--- a/api/logic/minecraft/launch/CreateServerResourcePacksFolder.h
+++ b/api/logic/minecraft/launch/CreateServerResourcePacksFolder.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,14 +22,16 @@
// HACK: this is a workaround for MCL-3732 - 'server-resource-packs' folder is created.
class CreateServerResourcePacksFolder: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit CreateServerResourcePacksFolder(LaunchTask *parent);
- virtual void executeTask();
- virtual bool canAbort() const
- {
- return false;
- }
+ explicit CreateServerResourcePacksFolder(LaunchTask *parent);
+ virtual ~CreateServerResourcePacksFolder() {};
+
+ virtual void executeTask();
+ virtual bool canAbort() const
+ {
+ return false;
+ }
};
diff --git a/api/logic/minecraft/launch/DirectJavaLaunch.cpp b/api/logic/minecraft/launch/DirectJavaLaunch.cpp
index 4ccc5c3c..5c2bb91e 100644
--- a/api/logic/minecraft/launch/DirectJavaLaunch.cpp
+++ b/api/logic/minecraft/launch/DirectJavaLaunch.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,128 +22,128 @@
DirectJavaLaunch::DirectJavaLaunch(LaunchTask *parent) : LaunchStep(parent)
{
- connect(&m_process, &LoggedProcess::log, this, &DirectJavaLaunch::logLines);
- connect(&m_process, &LoggedProcess::stateChanged, this, &DirectJavaLaunch::on_state);
+ connect(&m_process, &LoggedProcess::log, this, &DirectJavaLaunch::logLines);
+ connect(&m_process, &LoggedProcess::stateChanged, this, &DirectJavaLaunch::on_state);
}
void DirectJavaLaunch::executeTask()
{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
- QStringList args = minecraftInstance->javaArguments();
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
+ QStringList args = minecraftInstance->javaArguments();
- args.append("-Djava.library.path=" + minecraftInstance->getNativePath());
+ args.append("-Djava.library.path=" + minecraftInstance->getNativePath());
- auto classPathEntries = minecraftInstance->getClassPath();
- args.append("-cp");
- QString classpath;
+ auto classPathEntries = minecraftInstance->getClassPath();
+ args.append("-cp");
+ QString classpath;
#ifdef Q_OS_WIN32
- classpath = classPathEntries.join(';');
+ classpath = classPathEntries.join(';');
#else
- classpath = classPathEntries.join(':');
+ classpath = classPathEntries.join(':');
#endif
- args.append(classpath);
- args.append(minecraftInstance->getMainClass());
-
- QString allArgs = args.join(", ");
- emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC);
-
- auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
-
- m_process.setProcessEnvironment(instance->createEnvironment());
-
- // make detachable - this will keep the process running even if the object is destroyed
- m_process.setDetachable(true);
-
- auto mcArgs = minecraftInstance->processMinecraftArgs(m_session);
- args.append(mcArgs);
-
- QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
- if(!wrapperCommandStr.isEmpty())
- {
- auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
- auto wrapperCommand = wrapperArgs.takeFirst();
- auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
- if (realWrapperCommand.isEmpty())
- {
- QString reason = tr("The wrapper command \"%1\" couldn't be found.").arg(wrapperCommand);
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(reason);
- return;
- }
- emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::MultiMC);
- args.prepend(javaPath);
- m_process.start(wrapperCommand, wrapperArgs + args);
- }
- else
- {
- m_process.start(javaPath, args);
- }
+ args.append(classpath);
+ args.append(minecraftInstance->getMainClass());
+
+ QString allArgs = args.join(", ");
+ emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC);
+
+ auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
+
+ m_process.setProcessEnvironment(instance->createEnvironment());
+
+ // make detachable - this will keep the process running even if the object is destroyed
+ m_process.setDetachable(true);
+
+ auto mcArgs = minecraftInstance->processMinecraftArgs(m_session);
+ args.append(mcArgs);
+
+ QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
+ if(!wrapperCommandStr.isEmpty())
+ {
+ auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
+ auto wrapperCommand = wrapperArgs.takeFirst();
+ auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
+ if (realWrapperCommand.isEmpty())
+ {
+ QString reason = tr("The wrapper command \"%1\" couldn't be found.").arg(wrapperCommand);
+ emit logLine(reason, MessageLevel::Fatal);
+ emitFailed(reason);
+ return;
+ }
+ emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::MultiMC);
+ args.prepend(javaPath);
+ m_process.start(wrapperCommand, wrapperArgs + args);
+ }
+ else
+ {
+ m_process.start(javaPath, args);
+ }
}
void DirectJavaLaunch::on_state(LoggedProcess::State state)
{
- switch(state)
- {
- case LoggedProcess::FailedToStart:
- {
- //: Error message displayed if instace can't start
- QString reason = tr("Could not launch minecraft!");
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(reason);
- return;
- }
- case LoggedProcess::Aborted:
- case LoggedProcess::Crashed:
-
- {
- m_parent->setPid(-1);
- emitFailed("Game crashed.");
- return;
- }
- case LoggedProcess::Finished:
- {
- m_parent->setPid(-1);
- // if the exit code wasn't 0, report this as a crash
- auto exitCode = m_process.exitCode();
- if(exitCode != 0)
- {
- emitFailed("Game crashed.");
- return;
- }
- //FIXME: make this work again
- // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode));
- // run post-exit
- emitSucceeded();
- break;
- }
- case LoggedProcess::Running:
- emit logLine(tr("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC);
- m_parent->setPid(m_process.processId());
- m_parent->instance()->setLastLaunch();
- break;
- default:
- break;
- }
+ switch(state)
+ {
+ case LoggedProcess::FailedToStart:
+ {
+ //: Error message displayed if instace can't start
+ QString reason = tr("Could not launch minecraft!");
+ emit logLine(reason, MessageLevel::Fatal);
+ emitFailed(reason);
+ return;
+ }
+ case LoggedProcess::Aborted:
+ case LoggedProcess::Crashed:
+
+ {
+ m_parent->setPid(-1);
+ emitFailed("Game crashed.");
+ return;
+ }
+ case LoggedProcess::Finished:
+ {
+ m_parent->setPid(-1);
+ // if the exit code wasn't 0, report this as a crash
+ auto exitCode = m_process.exitCode();
+ if(exitCode != 0)
+ {
+ emitFailed("Game crashed.");
+ return;
+ }
+ //FIXME: make this work again
+ // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode));
+ // run post-exit
+ emitSucceeded();
+ break;
+ }
+ case LoggedProcess::Running:
+ emit logLine(tr("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC);
+ m_parent->setPid(m_process.processId());
+ m_parent->instance()->setLastLaunch();
+ break;
+ default:
+ break;
+ }
}
void DirectJavaLaunch::setWorkingDirectory(const QString &wd)
{
- m_process.setWorkingDirectory(wd);
+ m_process.setWorkingDirectory(wd);
}
void DirectJavaLaunch::proceed()
{
- // nil
+ // nil
}
bool DirectJavaLaunch::abort()
{
- auto state = m_process.state();
- if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
- {
- m_process.kill();
- }
- return true;
+ auto state = m_process.state();
+ if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
+ {
+ m_process.kill();
+ }
+ return true;
}
diff --git a/api/logic/minecraft/launch/DirectJavaLaunch.h b/api/logic/minecraft/launch/DirectJavaLaunch.h
index 19087b50..9f57272c 100644
--- a/api/logic/minecraft/launch/DirectJavaLaunch.h
+++ b/api/logic/minecraft/launch/DirectJavaLaunch.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,27 +21,29 @@
class DirectJavaLaunch: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit DirectJavaLaunch(LaunchTask *parent);
- virtual void executeTask();
- virtual bool abort();
- virtual void proceed();
- virtual bool canAbort() const
- {
- return true;
- }
- void setWorkingDirectory(const QString &wd);
- void setAuthSession(AuthSessionPtr session)
- {
- m_session = session;
- }
+ explicit DirectJavaLaunch(LaunchTask *parent);
+ virtual ~DirectJavaLaunch() {};
+
+ virtual void executeTask();
+ virtual bool abort();
+ virtual void proceed();
+ virtual bool canAbort() const
+ {
+ return true;
+ }
+ void setWorkingDirectory(const QString &wd);
+ void setAuthSession(AuthSessionPtr session)
+ {
+ m_session = session;
+ }
private slots:
- void on_state(LoggedProcess::State state);
+ void on_state(LoggedProcess::State state);
private:
- LoggedProcess m_process;
- QString m_command;
- AuthSessionPtr m_session;
+ LoggedProcess m_process;
+ QString m_command;
+ AuthSessionPtr m_session;
};
diff --git a/api/logic/minecraft/launch/ExtractNatives.cpp b/api/logic/minecraft/launch/ExtractNatives.cpp
index 7ddde374..253d13bc 100644
--- a/api/logic/minecraft/launch/ExtractNatives.cpp
+++ b/api/logic/minecraft/launch/ExtractNatives.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,76 +25,76 @@
static QString replaceSuffix (QString target, const QString &suffix, const QString &replacement)
{
- if (!target.endsWith(suffix))
- {
- return target;
- }
- target.resize(target.length() - suffix.length());
- return target + replacement;
+ if (!target.endsWith(suffix))
+ {
+ return target;
+ }
+ target.resize(target.length() - suffix.length());
+ return target + replacement;
}
static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack)
{
- QuaZip zip(source);
- if(!zip.open(QuaZip::mdUnzip))
- {
- return false;
- }
- QDir directory(targetFolder);
- if (!zip.goToFirstFile())
- {
- return false;
- }
- do
- {
- QString name = zip.getCurrentFileName();
- if(applyJnilibHack)
- {
- name = replaceSuffix(name, ".jnilib", ".dylib");
- }
- QString absFilePath = directory.absoluteFilePath(name);
- if (!JlCompress::extractFile(&zip, "", absFilePath))
- {
- return false;
- }
- } while (zip.goToNextFile());
- zip.close();
- if(zip.getZipError()!=0)
- {
- return false;
- }
- return true;
+ QuaZip zip(source);
+ if(!zip.open(QuaZip::mdUnzip))
+ {
+ return false;
+ }
+ QDir directory(targetFolder);
+ if (!zip.goToFirstFile())
+ {
+ return false;
+ }
+ do
+ {
+ QString name = zip.getCurrentFileName();
+ if(applyJnilibHack)
+ {
+ name = replaceSuffix(name, ".jnilib", ".dylib");
+ }
+ QString absFilePath = directory.absoluteFilePath(name);
+ if (!JlCompress::extractFile(&zip, "", absFilePath))
+ {
+ return false;
+ }
+ } while (zip.goToNextFile());
+ zip.close();
+ if(zip.getZipError()!=0)
+ {
+ return false;
+ }
+ return true;
}
void ExtractNatives::executeTask()
{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
- auto toExtract = minecraftInstance->getNativeJars();
- if(toExtract.isEmpty())
- {
- emitSucceeded();
- return;
- }
- auto outputPath = minecraftInstance->getNativePath();
- auto javaVersion = minecraftInstance->getJavaVersion();
- bool jniHackEnabled = javaVersion.major() >= 8;
- for(const auto &source: toExtract)
- {
- if(!unzipNatives(source, outputPath, jniHackEnabled))
- {
- auto reason = tr("Couldn't extract native jar '%1' to destination '%2'").arg(source, outputPath);
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(reason);
- }
- }
- emitSucceeded();
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
+ auto toExtract = minecraftInstance->getNativeJars();
+ if(toExtract.isEmpty())
+ {
+ emitSucceeded();
+ return;
+ }
+ auto outputPath = minecraftInstance->getNativePath();
+ auto javaVersion = minecraftInstance->getJavaVersion();
+ bool jniHackEnabled = javaVersion.major() >= 8;
+ for(const auto &source: toExtract)
+ {
+ if(!unzipNatives(source, outputPath, jniHackEnabled))
+ {
+ auto reason = tr("Couldn't extract native jar '%1' to destination '%2'").arg(source, outputPath);
+ emit logLine(reason, MessageLevel::Fatal);
+ emitFailed(reason);
+ }
+ }
+ emitSucceeded();
}
void ExtractNatives::finalize()
{
- auto instance = m_parent->instance();
- QString target_dir = FS::PathCombine(instance->instanceRoot(), "natives/");
- QDir dir(target_dir);
- dir.removeRecursively();
+ auto instance = m_parent->instance();
+ QString target_dir = FS::PathCombine(instance->instanceRoot(), "natives/");
+ QDir dir(target_dir);
+ dir.removeRecursively();
}
diff --git a/api/logic/minecraft/launch/ExtractNatives.h b/api/logic/minecraft/launch/ExtractNatives.h
index 6e1e7cd4..abb3bd7a 100644
--- a/api/logic/minecraft/launch/ExtractNatives.h
+++ b/api/logic/minecraft/launch/ExtractNatives.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,17 +22,17 @@
// FIXME: temporary wrapper for existing task.
class ExtractNatives: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit ExtractNatives(LaunchTask *parent) : LaunchStep(parent){};
- virtual ~ExtractNatives(){};
+ explicit ExtractNatives(LaunchTask *parent) : LaunchStep(parent){};
+ virtual ~ExtractNatives(){};
- void executeTask() override;
- bool canAbort() const override
- {
- return false;
- }
- void finalize() override;
+ void executeTask() override;
+ bool canAbort() const override
+ {
+ return false;
+ }
+ void finalize() override;
};
diff --git a/api/logic/minecraft/launch/LauncherPartLaunch.cpp b/api/logic/minecraft/launch/LauncherPartLaunch.cpp
index 1fe9c323..e854a3a6 100644
--- a/api/logic/minecraft/launch/LauncherPartLaunch.cpp
+++ b/api/logic/minecraft/launch/LauncherPartLaunch.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,8 @@
LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent)
{
- connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines);
- connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
+ connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines);
+ connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
}
#ifdef Q_OS_WIN
@@ -33,187 +33,187 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent)
#include <windows.h>
QString shortPathName(const QString & file)
{
- auto input = file.toStdWString();
- std::wstring output;
- long length = GetShortPathNameW(input.c_str(), NULL, 0);
- // NOTE: this resizing might seem weird...
- // when GetShortPathNameW fails, it returns length including null character
- // when it succeeds, it returns length excluding null character
- // See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
- output.resize(length);
- GetShortPathNameW(input.c_str(),(LPWSTR)output.c_str(),length);
- output.resize(length-1);
- QString ret = QString::fromStdWString(output);
- return ret;
+ auto input = file.toStdWString();
+ std::wstring output;
+ long length = GetShortPathNameW(input.c_str(), NULL, 0);
+ // NOTE: this resizing might seem weird...
+ // when GetShortPathNameW fails, it returns length including null character
+ // when it succeeds, it returns length excluding null character
+ // See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
+ output.resize(length);
+ GetShortPathNameW(input.c_str(),(LPWSTR)output.c_str(),length);
+ output.resize(length-1);
+ QString ret = QString::fromStdWString(output);
+ return ret;
}
#endif
// if the string survives roundtrip through local 8bit encoding...
bool fitsInLocal8bit(const QString & string)
{
- return string == QString::fromLocal8Bit(string.toLocal8Bit());
+ return string == QString::fromLocal8Bit(string.toLocal8Bit());
}
void LauncherPartLaunch::executeTask()
{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
- m_launchScript = minecraftInstance->createLaunchScript(m_session);
- QStringList args = minecraftInstance->javaArguments();
- QString allArgs = args.join(", ");
- emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC);
+ m_launchScript = minecraftInstance->createLaunchScript(m_session);
+ QStringList args = minecraftInstance->javaArguments();
+ QString allArgs = args.join(", ");
+ emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC);
- auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
+ auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
- m_process.setProcessEnvironment(instance->createEnvironment());
+ m_process.setProcessEnvironment(instance->createEnvironment());
- // make detachable - this will keep the process running even if the object is destroyed
- m_process.setDetachable(true);
+ // make detachable - this will keep the process running even if the object is destroyed
+ m_process.setDetachable(true);
- auto classPath = minecraftInstance->getClassPath();
- classPath.prepend(FS::PathCombine(ENV.getJarsPath(), "NewLaunch.jar"));
+ auto classPath = minecraftInstance->getClassPath();
+ classPath.prepend(FS::PathCombine(ENV.getJarsPath(), "NewLaunch.jar"));
- auto natPath = minecraftInstance->getNativePath();
+ auto natPath = minecraftInstance->getNativePath();
#ifdef Q_OS_WIN
- if (!fitsInLocal8bit(natPath))
- {
- args << "-Djava.library.path=" + shortPathName(natPath);
- }
- else
- {
- args << "-Djava.library.path=" + natPath;
- }
+ if (!fitsInLocal8bit(natPath))
+ {
+ args << "-Djava.library.path=" + shortPathName(natPath);
+ }
+ else
+ {
+ args << "-Djava.library.path=" + natPath;
+ }
#else
- args << "-Djava.library.path=" + natPath;
+ args << "-Djava.library.path=" + natPath;
#endif
- args << "-cp";
+ args << "-cp";
#ifdef Q_OS_WIN
- QStringList processed;
- for(auto & item: classPath)
- {
- if (!fitsInLocal8bit(item))
- {
- processed << shortPathName(item);
- }
- else
- {
- processed << item;
- }
- }
- args << processed.join(';');
+ QStringList processed;
+ for(auto & item: classPath)
+ {
+ if (!fitsInLocal8bit(item))
+ {
+ processed << shortPathName(item);
+ }
+ else
+ {
+ processed << item;
+ }
+ }
+ args << processed.join(';');
#else
- args << classPath.join(':');
+ args << classPath.join(':');
#endif
- args << "org.multimc.EntryPoint";
-
- qDebug() << args.join(' ');
-
- QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
- if(!wrapperCommandStr.isEmpty())
- {
- auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
- auto wrapperCommand = wrapperArgs.takeFirst();
- auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
- if (realWrapperCommand.isEmpty())
- {
- QString reason = tr("The wrapper command \"%1\" couldn't be found.").arg(wrapperCommand);
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(reason);
- return;
- }
- emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::MultiMC);
- args.prepend(javaPath);
- m_process.start(wrapperCommand, wrapperArgs + args);
- }
- else
- {
- m_process.start(javaPath, args);
- }
+ args << "org.multimc.EntryPoint";
+
+ qDebug() << args.join(' ');
+
+ QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
+ if(!wrapperCommandStr.isEmpty())
+ {
+ auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
+ auto wrapperCommand = wrapperArgs.takeFirst();
+ auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
+ if (realWrapperCommand.isEmpty())
+ {
+ QString reason = tr("The wrapper command \"%1\" couldn't be found.").arg(wrapperCommand);
+ emit logLine(reason, MessageLevel::Fatal);
+ emitFailed(reason);
+ return;
+ }
+ emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::MultiMC);
+ args.prepend(javaPath);
+ m_process.start(wrapperCommand, wrapperArgs + args);
+ }
+ else
+ {
+ m_process.start(javaPath, args);
+ }
}
void LauncherPartLaunch::on_state(LoggedProcess::State state)
{
- switch(state)
- {
- case LoggedProcess::FailedToStart:
- {
- //: Error message displayed if instace can't start
- QString reason = tr("Could not launch minecraft!");
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(reason);
- return;
- }
- case LoggedProcess::Aborted:
- case LoggedProcess::Crashed:
-
- {
- m_parent->setPid(-1);
- emitFailed("Game crashed.");
- return;
- }
- case LoggedProcess::Finished:
- {
- m_parent->setPid(-1);
- // if the exit code wasn't 0, report this as a crash
- auto exitCode = m_process.exitCode();
- if(exitCode != 0)
- {
- emitFailed("Game crashed.");
- return;
- }
- //FIXME: make this work again
- // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode));
- // run post-exit
- emitSucceeded();
- break;
- }
- case LoggedProcess::Running:
- emit logLine(tr("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC);
- m_parent->setPid(m_process.processId());
- m_parent->instance()->setLastLaunch();
- // send the launch script to the launcher part
- m_process.write(m_launchScript.toUtf8());
-
- mayProceed = true;
- emit readyForLaunch();
- break;
- default:
- break;
- }
+ switch(state)
+ {
+ case LoggedProcess::FailedToStart:
+ {
+ //: Error message displayed if instace can't start
+ QString reason = tr("Could not launch minecraft!");
+ emit logLine(reason, MessageLevel::Fatal);
+ emitFailed(reason);
+ return;
+ }
+ case LoggedProcess::Aborted:
+ case LoggedProcess::Crashed:
+
+ {
+ m_parent->setPid(-1);
+ emitFailed("Game crashed.");
+ return;
+ }
+ case LoggedProcess::Finished:
+ {
+ m_parent->setPid(-1);
+ // if the exit code wasn't 0, report this as a crash
+ auto exitCode = m_process.exitCode();
+ if(exitCode != 0)
+ {
+ emitFailed("Game crashed.");
+ return;
+ }
+ //FIXME: make this work again
+ // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode));
+ // run post-exit
+ emitSucceeded();
+ break;
+ }
+ case LoggedProcess::Running:
+ emit logLine(tr("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC);
+ m_parent->setPid(m_process.processId());
+ m_parent->instance()->setLastLaunch();
+ // send the launch script to the launcher part
+ m_process.write(m_launchScript.toUtf8());
+
+ mayProceed = true;
+ emit readyForLaunch();
+ break;
+ default:
+ break;
+ }
}
void LauncherPartLaunch::setWorkingDirectory(const QString &wd)
{
- m_process.setWorkingDirectory(wd);
+ m_process.setWorkingDirectory(wd);
}
void LauncherPartLaunch::proceed()
{
- if(mayProceed)
- {
- QString launchString("launch\n");
- m_process.write(launchString.toUtf8());
- mayProceed = false;
- }
+ if(mayProceed)
+ {
+ QString launchString("launch\n");
+ m_process.write(launchString.toUtf8());
+ mayProceed = false;
+ }
}
bool LauncherPartLaunch::abort()
{
- if(mayProceed)
- {
- mayProceed = false;
- QString launchString("abort\n");
- m_process.write(launchString.toUtf8());
- }
- else
- {
- auto state = m_process.state();
- if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
- {
- m_process.kill();
- }
- }
- return true;
+ if(mayProceed)
+ {
+ mayProceed = false;
+ QString launchString("abort\n");
+ m_process.write(launchString.toUtf8());
+ }
+ else
+ {
+ auto state = m_process.state();
+ if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
+ {
+ m_process.kill();
+ }
+ }
+ return true;
}
diff --git a/api/logic/minecraft/launch/LauncherPartLaunch.h b/api/logic/minecraft/launch/LauncherPartLaunch.h
index d384c2d1..1bc4a5f8 100644
--- a/api/logic/minecraft/launch/LauncherPartLaunch.h
+++ b/api/logic/minecraft/launch/LauncherPartLaunch.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,29 +21,31 @@
class LauncherPartLaunch: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit LauncherPartLaunch(LaunchTask *parent);
- virtual void executeTask();
- virtual bool abort();
- virtual void proceed();
- virtual bool canAbort() const
- {
- return true;
- }
- void setWorkingDirectory(const QString &wd);
- void setAuthSession(AuthSessionPtr session)
- {
- m_session = session;
- }
+ explicit LauncherPartLaunch(LaunchTask *parent);
+ virtual ~LauncherPartLaunch() {};
+
+ virtual void executeTask();
+ virtual bool abort();
+ virtual void proceed();
+ virtual bool canAbort() const
+ {
+ return true;
+ }
+ void setWorkingDirectory(const QString &wd);
+ void setAuthSession(AuthSessionPtr session)
+ {
+ m_session = session;
+ }
private slots:
- void on_state(LoggedProcess::State state);
+ void on_state(LoggedProcess::State state);
private:
- LoggedProcess m_process;
- QString m_command;
- AuthSessionPtr m_session;
- QString m_launchScript;
- bool mayProceed = false;
+ LoggedProcess m_process;
+ QString m_command;
+ AuthSessionPtr m_session;
+ QString m_launchScript;
+ bool mayProceed = false;
};
diff --git a/api/logic/minecraft/launch/ModMinecraftJar.cpp b/api/logic/minecraft/launch/ModMinecraftJar.cpp
index 34825b45..ceaad175 100644
--- a/api/logic/minecraft/launch/ModMinecraftJar.cpp
+++ b/api/logic/minecraft/launch/ModMinecraftJar.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,60 +23,60 @@
void ModMinecraftJar::executeTask()
{
- auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
+ auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
- if(!m_inst->getJarMods().size())
- {
- emitSucceeded();
- return;
- }
- // nuke obsolete stripped jar(s) if needed
- if(!FS::ensureFolderPathExists(m_inst->binRoot()))
- {
- emitFailed(tr("Couldn't create the bin folder for Minecraft.jar"));
- }
+ if(!m_inst->getJarMods().size())
+ {
+ emitSucceeded();
+ return;
+ }
+ // nuke obsolete stripped jar(s) if needed
+ if(!FS::ensureFolderPathExists(m_inst->binRoot()))
+ {
+ emitFailed(tr("Couldn't create the bin folder for Minecraft.jar"));
+ }
- auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar");
- if(!removeJar())
- {
- emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath));
- }
+ auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar");
+ if(!removeJar())
+ {
+ emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath));
+ }
- // create temporary modded jar, if needed
- auto components = m_inst->getComponentList();
- auto profile = components->getProfile();
- auto jarMods = m_inst->getJarMods();
- if(jarMods.size())
- {
- auto mainJar = profile->getMainJar();
- QStringList jars, temp1, temp2, temp3, temp4;
- mainJar->getApplicableFiles(currentSystem, jars, temp1, temp2, temp3, m_inst->getLocalLibraryPath());
- auto sourceJarPath = jars[0];
- if(!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods))
- {
- emitFailed(tr("Failed to create the custom Minecraft jar file."));
- return;
- }
- }
- emitSucceeded();
+ // create temporary modded jar, if needed
+ auto components = m_inst->getComponentList();
+ auto profile = components->getProfile();
+ auto jarMods = m_inst->getJarMods();
+ if(jarMods.size())
+ {
+ auto mainJar = profile->getMainJar();
+ QStringList jars, temp1, temp2, temp3, temp4;
+ mainJar->getApplicableFiles(currentSystem, jars, temp1, temp2, temp3, m_inst->getLocalLibraryPath());
+ auto sourceJarPath = jars[0];
+ if(!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods))
+ {
+ emitFailed(tr("Failed to create the custom Minecraft jar file."));
+ return;
+ }
+ }
+ emitSucceeded();
}
void ModMinecraftJar::finalize()
{
- removeJar();
+ removeJar();
}
bool ModMinecraftJar::removeJar()
{
- auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
- auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar");
- QFile finalJar(finalJarPath);
- if(finalJar.exists())
- {
- if(!finalJar.remove())
- {
- return false;
- }
- }
- return true;
+ auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
+ auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar");
+ QFile finalJar(finalJarPath);
+ if(finalJar.exists())
+ {
+ if(!finalJar.remove())
+ {
+ return false;
+ }
+ }
+ return true;
}
diff --git a/api/logic/minecraft/launch/ModMinecraftJar.h b/api/logic/minecraft/launch/ModMinecraftJar.h
index b9a2d35b..bed5079f 100644
--- a/api/logic/minecraft/launch/ModMinecraftJar.h
+++ b/api/logic/minecraft/launch/ModMinecraftJar.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,17 +20,17 @@
class ModMinecraftJar: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit ModMinecraftJar(LaunchTask *parent) : LaunchStep(parent) {};
- virtual ~ModMinecraftJar(){};
+ explicit ModMinecraftJar(LaunchTask *parent) : LaunchStep(parent) {};
+ virtual ~ModMinecraftJar(){};
- virtual void executeTask();
- virtual bool canAbort() const
- {
- return false;
- }
- void finalize() override;
+ virtual void executeTask() override;
+ virtual bool canAbort() const override
+ {
+ return false;
+ }
+ void finalize() override;
private:
- bool removeJar();
+ bool removeJar();
};
diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.cpp b/api/logic/minecraft/launch/PrintInstanceInfo.cpp
index 83bf584f..4cc180fd 100644
--- a/api/logic/minecraft/launch/PrintInstanceInfo.cpp
+++ b/api/logic/minecraft/launch/PrintInstanceInfo.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,67 +19,88 @@
#include "PrintInstanceInfo.h"
#include <launch/LaunchTask.h>
-void PrintInstanceInfo::executeTask()
-{
- auto instance = m_parent->instance();
- auto lines = instance->verboseDescription(m_session);
-
#ifdef Q_OS_LINUX
+namespace {
+void probeProcCpuinfo(QStringList &log)
+{
std::ifstream cpuin("/proc/cpuinfo");
for (std::string line; std::getline(cpuin, line);)
{
if (strncmp(line.c_str(), "model name", 10) == 0)
{
- QStringList clines = (QStringList() << QString::fromStdString(line.substr(13, std::string::npos)));
- logLines(clines, MessageLevel::MultiMC);
+ log << QString::fromStdString(line.substr(13, std::string::npos));
break;
}
}
+}
+void runLspci(QStringList &log)
+{
+ // FIXME: fixed size buffers...
char buff[512];
int gpuline = -1;
int cline = 0;
- FILE *fp = popen("lspci -k", "r");
- if (fp != NULL)
+ FILE * lspci = popen("lspci -k", "r");
+
+ if (!lspci)
+ return;
+
+ while (fgets(buff, 512, lspci) != NULL)
{
- while (fgets(buff, 512, fp) != NULL)
+ std::string str(buff);
+ if (str.length() < 9)
+ continue;
+ if (str.substr(8, 3) == "VGA")
{
- std::string str(buff);
- if (str.length() < 9)
- continue;
- if (str.substr(8, 3) == "VGA")
- {
- gpuline = cline;
- QStringList glines = (QStringList() << QString::fromStdString(str.substr(35, std::string::npos)));
- logLines(glines, MessageLevel::MultiMC);
- }
- if (gpuline > -1 && gpuline != cline)
+ gpuline = cline;
+ log << QString::fromStdString(str.substr(35, std::string::npos));
+ }
+ if (gpuline > -1 && gpuline != cline)
+ {
+ if (cline - gpuline < 3)
{
- if (cline - gpuline < 3)
- {
- QStringList alines = (QStringList() << QString::fromStdString(str.substr(1, std::string::npos)));
- logLines(alines, MessageLevel::MultiMC);
- }
+ log << QString::fromStdString(str.substr(1, std::string::npos));
}
- cline++;
}
+ cline++;
}
-
- FILE *fp2 = popen("glxinfo", "r");
- if (fp2 != NULL)
+ pclose(lspci);
+}
+
+void runGlxinfo(QStringList & log)
+{
+ // FIXME: fixed size buffers...
+ char buff[512];
+ FILE *glxinfo = popen("glxinfo", "r");
+ if (!glxinfo)
+ return;
+
+ while (fgets(buff, 512, glxinfo) != NULL)
{
- while (fgets(buff, 512, fp2) != NULL)
+ if (strncmp(buff, "OpenGL version string:", 22) == 0)
{
- if (strncmp(buff, "OpenGL version string:", 22) == 0)
- {
- QStringList drlines = (QStringList() << QString::fromUtf8(buff));
- logLines(drlines, MessageLevel::MultiMC);
- break;
- }
+ log << QString::fromUtf8(buff);
+ break;
}
}
+ pclose(glxinfo);
+}
+
+}
+#endif
+
+void PrintInstanceInfo::executeTask()
+{
+ auto instance = m_parent->instance();
+ QStringList log;
+
+#ifdef Q_OS_LINUX
+ ::probeProcCpuinfo(log);
+ ::runLspci(log);
+ ::runGlxinfo(log);
#endif
- logLines(lines, MessageLevel::MultiMC);
+ logLines(log, MessageLevel::MultiMC);
+ logLines(instance->verboseDescription(m_session), MessageLevel::MultiMC);
emitSucceeded();
}
diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.h b/api/logic/minecraft/launch/PrintInstanceInfo.h
index 61615ba1..1d081f3e 100644
--- a/api/logic/minecraft/launch/PrintInstanceInfo.h
+++ b/api/logic/minecraft/launch/PrintInstanceInfo.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,17 +22,17 @@
// FIXME: temporary wrapper for existing task.
class PrintInstanceInfo: public LaunchStep
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit PrintInstanceInfo(LaunchTask *parent, AuthSessionPtr session) : LaunchStep(parent), m_session(session) {};
- virtual ~PrintInstanceInfo(){};
+ explicit PrintInstanceInfo(LaunchTask *parent, AuthSessionPtr session) : LaunchStep(parent), m_session(session) {};
+ virtual ~PrintInstanceInfo(){};
- virtual void executeTask();
- virtual bool canAbort() const
- {
- return false;
- }
+ virtual void executeTask();
+ virtual bool canAbort() const
+ {
+ return false;
+ }
private:
- AuthSessionPtr m_session;
+ AuthSessionPtr m_session;
};
diff --git a/api/logic/minecraft/launch/ReconstructAssets.cpp b/api/logic/minecraft/launch/ReconstructAssets.cpp
new file mode 100644
index 00000000..af9af127
--- /dev/null
+++ b/api/logic/minecraft/launch/ReconstructAssets.cpp
@@ -0,0 +1,36 @@
+/* Copyright 2013-2019 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReconstructAssets.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/ComponentList.h"
+#include "minecraft/AssetsUtils.h"
+#include "launch/LaunchTask.h"
+
+void ReconstructAssets::executeTask()
+{
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
+ auto components = minecraftInstance->getComponentList();
+ auto profile = components->getProfile();
+ auto assets = profile->getMinecraftAssets();
+
+ if(!AssetsUtils::reconstructAssets(assets->id, minecraftInstance->resourcesDir()))
+ {
+ emit logLine("Failed to reconstruct Minecraft assets.", MessageLevel::Error);
+ }
+
+ emitSucceeded();
+}
diff --git a/api/logic/minecraft/launch/ReconstructAssets.h b/api/logic/minecraft/launch/ReconstructAssets.h
new file mode 100644
index 00000000..228fa083
--- /dev/null
+++ b/api/logic/minecraft/launch/ReconstructAssets.h
@@ -0,0 +1,33 @@
+/* Copyright 2013-2019 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <launch/LaunchStep.h>
+#include <memory>
+
+class ReconstructAssets: public LaunchStep
+{
+ Q_OBJECT
+public:
+ explicit ReconstructAssets(LaunchTask *parent) : LaunchStep(parent){};
+ virtual ~ReconstructAssets(){};
+
+ void executeTask() override;
+ bool canAbort() const override
+ {
+ return false;
+ }
+};
diff --git a/api/logic/minecraft/launch/ScanModFolders.cpp b/api/logic/minecraft/launch/ScanModFolders.cpp
new file mode 100644
index 00000000..485c3dc4
--- /dev/null
+++ b/api/logic/minecraft/launch/ScanModFolders.cpp
@@ -0,0 +1,54 @@
+/* Copyright 2013-2019 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ScanModFolders.h"
+#include "launch/LaunchTask.h"
+#include "MMCZip.h"
+#include "minecraft/OpSys.h"
+#include "FileSystem.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/mod/ModFolderModel.h"
+
+void ScanModFolders::executeTask()
+{
+ auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
+
+ auto loaders = m_inst->loaderModList();
+ connect(loaders.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::modsDone);
+ loaders->update();
+
+ auto cores = m_inst->coreModList();
+ connect(cores.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::coreModsDone);
+ cores->update();
+}
+
+void ScanModFolders::modsDone()
+{
+ m_modsDone = true;
+ checkDone();
+}
+
+void ScanModFolders::coreModsDone()
+{
+ m_coreModsDone = true;
+ checkDone();
+}
+
+void ScanModFolders::checkDone()
+{
+ if(m_modsDone && m_coreModsDone) {
+ emitSucceeded();
+ }
+}
diff --git a/api/logic/minecraft/launch/ScanModFolders.h b/api/logic/minecraft/launch/ScanModFolders.h
new file mode 100644
index 00000000..360df98d
--- /dev/null
+++ b/api/logic/minecraft/launch/ScanModFolders.h
@@ -0,0 +1,42 @@
+/* Copyright 2013-2019 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <launch/LaunchStep.h>
+#include <memory>
+
+class ScanModFolders: public LaunchStep
+{
+ Q_OBJECT
+public:
+ explicit ScanModFolders(LaunchTask *parent) : LaunchStep(parent) {};
+ virtual ~ScanModFolders(){};
+
+ virtual void executeTask() override;
+ virtual bool canAbort() const override
+ {
+ return false;
+ }
+private slots:
+ void coreModsDone();
+ void modsDone();
+private:
+ void checkDone();
+
+private: // DATA
+ bool m_modsDone = false;
+ bool m_coreModsDone = false;
+};
diff --git a/api/logic/minecraft/legacy/LegacyInstance.cpp b/api/logic/minecraft/legacy/LegacyInstance.cpp
index 5338763c..b9d68a9c 100644
--- a/api/logic/minecraft/legacy/LegacyInstance.cpp
+++ b/api/logic/minecraft/legacy/LegacyInstance.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,322 +21,236 @@
#include "LegacyInstance.h"
#include "minecraft/legacy/LegacyModList.h"
-#include "minecraft/ModList.h"
#include "minecraft/WorldList.h"
#include <MMCZip.h>
#include <FileSystem.h>
LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
- : BaseInstance(globalSettings, settings, rootDir)
+ : BaseInstance(globalSettings, settings, rootDir)
{
- settings->registerSetting("NeedsRebuild", true);
- settings->registerSetting("ShouldUpdate", false);
- settings->registerSetting("JarVersion", QString());
- settings->registerSetting("IntendedJarVersion", QString());
- /*
- * custom base jar has no default. it is determined in code... see the accessor methods for
- *it
- *
- * for instances that DO NOT have the CustomBaseJar setting (legacy instances),
- * [.]minecraft/bin/mcbackup.jar is the default base jar
- */
- settings->registerSetting("UseCustomBaseJar", true);
- settings->registerSetting("CustomBaseJar", "");
+ settings->registerSetting("NeedsRebuild", true);
+ settings->registerSetting("ShouldUpdate", false);
+ settings->registerSetting("JarVersion", QString());
+ settings->registerSetting("IntendedJarVersion", QString());
+ /*
+ * custom base jar has no default. it is determined in code... see the accessor methods for
+ *it
+ *
+ * for instances that DO NOT have the CustomBaseJar setting (legacy instances),
+ * [.]minecraft/bin/mcbackup.jar is the default base jar
+ */
+ settings->registerSetting("UseCustomBaseJar", true);
+ settings->registerSetting("CustomBaseJar", "");
}
QString LegacyInstance::mainJarToPreserve() const
{
- bool customJar = m_settings->get("UseCustomBaseJar").toBool();
- if(customJar)
- {
- auto base = baseJar();
- if(QFile::exists(base))
- {
- return base;
- }
- }
- auto runnable = runnableJar();
- if(QFile::exists(runnable))
- {
- return runnable;
- }
- return QString();
+ bool customJar = m_settings->get("UseCustomBaseJar").toBool();
+ if(customJar)
+ {
+ auto base = baseJar();
+ if(QFile::exists(base))
+ {
+ return base;
+ }
+ }
+ auto runnable = runnableJar();
+ if(QFile::exists(runnable))
+ {
+ return runnable;
+ }
+ return QString();
}
QString LegacyInstance::baseJar() const
{
- bool customJar = m_settings->get("UseCustomBaseJar").toBool();
- if (customJar)
- {
- return customBaseJar();
- }
- else
- return defaultBaseJar();
+ bool customJar = m_settings->get("UseCustomBaseJar").toBool();
+ if (customJar)
+ {
+ return customBaseJar();
+ }
+ else
+ return defaultBaseJar();
}
QString LegacyInstance::customBaseJar() const
{
- QString value = m_settings->get("CustomBaseJar").toString();
- if (value.isNull() || value.isEmpty())
- {
- return defaultCustomBaseJar();
- }
- return value;
+ QString value = m_settings->get("CustomBaseJar").toString();
+ if (value.isNull() || value.isEmpty())
+ {
+ return defaultCustomBaseJar();
+ }
+ return value;
}
bool LegacyInstance::shouldUseCustomBaseJar() const
{
- return m_settings->get("UseCustomBaseJar").toBool();
+ return m_settings->get("UseCustomBaseJar").toBool();
}
shared_qobject_ptr<Task> LegacyInstance::createUpdateTask(Net::Mode)
{
- return nullptr;
+ return nullptr;
}
-/*
-class LegacyJarModTask : public Task
-{
- //Q_OBJECT
-public:
- explicit LegacyJarModTask(std::shared_ptr<LegacyInstance> inst) : Task(nullptr), m_inst(inst)
- {
- }
- virtual void executeTask()
- {
- if (!m_inst->shouldRebuild())
- {
- emitSucceeded();
- return;
- }
-
- // Get the mod list
- auto modList = m_inst->getJarMods();
-
- QFileInfo runnableJar(m_inst->runnableJar());
- QFileInfo baseJar(m_inst->baseJar());
- bool base_is_custom = m_inst->shouldUseCustomBaseJar();
-
- // Nothing to do if there are no jar mods to install, no backup and just the mc jar
- if (base_is_custom)
- {
- // yes, this can happen if the instance only has the runnable jar and not the base jar
- // it *could* be assumed that such an instance is vanilla, but that wouldn't be safe
- // because that's not something mmc4 guarantees
- if (runnableJar.isFile() && !baseJar.exists() && modList.empty())
- {
- m_inst->setShouldRebuild(false);
- emitSucceeded();
- return;
- }
-
- setStatus(tr("Installing mods: Backing up minecraft.jar ..."));
- if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath()))
- {
- emitFailed("It seems both the active and base jar are gone. A fresh base jar will "
- "be used on next run.");
- m_inst->setShouldRebuild(true);
- m_inst->setShouldUpdate(true);
- m_inst->setShouldUseCustomBaseJar(false);
- return;
- }
- }
-
- if (!baseJar.exists())
- {
- emitFailed("The base jar " + baseJar.filePath() + " does not exist");
- return;
- }
-
- if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
- {
- emitFailed("Failed to delete old minecraft.jar");
- return;
- }
-
- setStatus(tr("Installing mods: Opening minecraft.jar ..."));
-
- QString outputJarPath = runnableJar.filePath();
- QString inputJarPath = baseJar.filePath();
-
- if(!MMCZip::createModdedJar(inputJarPath, outputJarPath, modList))
- {
- emitFailed(tr("Failed to create the custom Minecraft jar file."));
- return;
- }
- m_inst->setShouldRebuild(false);
- // inst->UpdateVersion(true);
- emitSucceeded();
- return;
-
- }
- std::shared_ptr<LegacyInstance> m_inst;
-};
-*/
-
std::shared_ptr<LegacyModList> LegacyInstance::jarModList() const
{
- if (!jar_mod_list)
- {
- auto list = new LegacyModList(jarModsDir(), modListFile());
- jar_mod_list.reset(list);
- }
- jar_mod_list->update();
- return jar_mod_list;
-}
-
-QList<Mod> LegacyInstance::getJarMods() const
-{
- return jarModList()->allMods();
+ if (!jar_mod_list)
+ {
+ auto list = new LegacyModList(jarModsDir(), modListFile());
+ jar_mod_list.reset(list);
+ }
+ jar_mod_list->update();
+ return jar_mod_list;
}
-QString LegacyInstance::minecraftRoot() const
+QString LegacyInstance::gameRoot() const
{
- QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
- QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
+ QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
+ QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
- if (mcDir.exists() && !dotMCDir.exists())
- return mcDir.filePath();
- else
- return dotMCDir.filePath();
+ if (mcDir.exists() && !dotMCDir.exists())
+ return mcDir.filePath();
+ else
+ return dotMCDir.filePath();
}
QString LegacyInstance::binRoot() const
{
- return FS::PathCombine(minecraftRoot(), "bin");
+ return FS::PathCombine(gameRoot(), "bin");
}
QString LegacyInstance::jarModsDir() const
{
- return FS::PathCombine(instanceRoot(), "instMods");
+ return FS::PathCombine(instanceRoot(), "instMods");
}
QString LegacyInstance::libDir() const
{
- return FS::PathCombine(minecraftRoot(), "lib");
+ return FS::PathCombine(gameRoot(), "lib");
}
QString LegacyInstance::savesDir() const
{
- return FS::PathCombine(minecraftRoot(), "saves");
+ return FS::PathCombine(gameRoot(), "saves");
}
QString LegacyInstance::loaderModsDir() const
{
- return FS::PathCombine(minecraftRoot(), "mods");
+ return FS::PathCombine(gameRoot(), "mods");
}
QString LegacyInstance::coreModsDir() const
{
- return FS::PathCombine(minecraftRoot(), "coremods");
+ return FS::PathCombine(gameRoot(), "coremods");
}
QString LegacyInstance::resourceDir() const
{
- return FS::PathCombine(minecraftRoot(), "resources");
+ return FS::PathCombine(gameRoot(), "resources");
}
QString LegacyInstance::texturePacksDir() const
{
- return FS::PathCombine(minecraftRoot(), "texturepacks");
+ return FS::PathCombine(gameRoot(), "texturepacks");
}
QString LegacyInstance::runnableJar() const
{
- return FS::PathCombine(binRoot(), "minecraft.jar");
+ return FS::PathCombine(binRoot(), "minecraft.jar");
}
QString LegacyInstance::modListFile() const
{
- return FS::PathCombine(instanceRoot(), "modlist");
+ return FS::PathCombine(instanceRoot(), "modlist");
}
QString LegacyInstance::instanceConfigFolder() const
{
- return FS::PathCombine(minecraftRoot(), "config");
+ return FS::PathCombine(gameRoot(), "config");
}
bool LegacyInstance::shouldRebuild() const
{
- return m_settings->get("NeedsRebuild").toBool();
+ return m_settings->get("NeedsRebuild").toBool();
}
QString LegacyInstance::currentVersionId() const
{
- return m_settings->get("JarVersion").toString();
+ return m_settings->get("JarVersion").toString();
}
QString LegacyInstance::intendedVersionId() const
{
- return m_settings->get("IntendedJarVersion").toString();
+ return m_settings->get("IntendedJarVersion").toString();
}
bool LegacyInstance::shouldUpdate() const
{
- QVariant var = settings()->get("ShouldUpdate");
- if (!var.isValid() || var.toBool() == false)
- {
- return intendedVersionId() != currentVersionId();
- }
- return true;
+ QVariant var = settings()->get("ShouldUpdate");
+ if (!var.isValid() || var.toBool() == false)
+ {
+ return intendedVersionId() != currentVersionId();
+ }
+ return true;
}
QString LegacyInstance::defaultBaseJar() const
{
- return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
+ return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
}
QString LegacyInstance::defaultCustomBaseJar() const
{
- return FS::PathCombine(binRoot(), "mcbackup.jar");
+ return FS::PathCombine(binRoot(), "mcbackup.jar");
}
std::shared_ptr<WorldList> LegacyInstance::worldList() const
{
- if (!m_world_list)
- {
- m_world_list.reset(new WorldList(savesDir()));
- }
- return m_world_list;
+ if (!m_world_list)
+ {
+ m_world_list.reset(new WorldList(savesDir()));
+ }
+ return m_world_list;
}
QString LegacyInstance::typeName() const
{
- return tr("Legacy");
+ return tr("Legacy");
}
QString LegacyInstance::getStatusbarDescription()
{
- return tr("Instance from previous versions.");
+ return tr("Instance from previous versions.");
}
QStringList LegacyInstance::verboseDescription(AuthSessionPtr session)
{
- QStringList out;
-
- auto alltraits = traits();
- if(alltraits.size())
- {
- out << "Traits:";
- for (auto trait : alltraits)
- {
- out << " " + trait;
- }
- out << "";
- }
-
- QString windowParams;
- if (settings()->get("LaunchMaximized").toBool())
- {
- out << "Window size: max (if available)";
- }
- else
- {
- auto width = settings()->get("MinecraftWinWidth").toInt();
- auto height = settings()->get("MinecraftWinHeight").toInt();
- out << "Window size: " + QString::number(width) + " x " + QString::number(height);
- }
- out << "";
- return out;
+ QStringList out;
+
+ auto alltraits = traits();
+ if(alltraits.size())
+ {
+ out << "Traits:";
+ for (auto trait : alltraits)
+ {
+ out << " " + trait;
+ }
+ out << "";
+ }
+
+ QString windowParams;
+ if (settings()->get("LaunchMaximized").toBool())
+ {
+ out << "Window size: max (if available)";
+ }
+ else
+ {
+ auto width = settings()->get("MinecraftWinWidth").toInt();
+ auto height = settings()->get("MinecraftWinHeight").toInt();
+ out << "Window size: " + QString::number(width) + " x " + QString::number(height);
+ }
+ out << "";
+ return out;
}
diff --git a/api/logic/minecraft/legacy/LegacyInstance.h b/api/logic/minecraft/legacy/LegacyInstance.h
index 7ab89509..7c0b94e8 100644
--- a/api/logic/minecraft/legacy/LegacyInstance.h
+++ b/api/logic/minecraft/legacy/LegacyInstance.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,11 @@
#pragma once
#include "BaseInstance.h"
-#include "minecraft/Mod.h"
+#include "launch/LaunchTask.h"
#include "multimc_logic_export.h"
-class ModList;
+class ModFolderModel;
class LegacyModList;
class WorldList;
class Task;
@@ -29,115 +29,113 @@ class Task;
*/
class MULTIMC_LOGIC_EXPORT LegacyInstance : public BaseInstance
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
-
- virtual void init() override {}
- virtual void saveNow() override {}
-
- /// Path to the instance's minecraft.jar
- QString runnableJar() const;
-
- //! Path to the instance's modlist file.
- QString modListFile() const;
-
- ////// Directories //////
- QString libDir() const;
- QString savesDir() const;
- QString texturePacksDir() const;
- QString jarModsDir() const;
- QString loaderModsDir() const;
- QString coreModsDir() const;
- QString resourceDir() const;
- virtual 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.
-
- /// Get the curent base jar of this instance. By default, it's the
- /// versions/$version/$version.jar
- QString baseJar() const;
-
- /// the default base jar of this instance
- QString defaultBaseJar() const;
- /// the default custom base jar of this instance
- QString defaultCustomBaseJar() const;
-
- // the main jar that we actually want to keep when migrating the instance
- QString mainJarToPreserve() const;
-
- /*!
- * Whether or not custom base jar is used
- */
- bool shouldUseCustomBaseJar() const;
-
- /*!
- * The value of the custom base jar
- */
- QString customBaseJar() const;
-
- std::shared_ptr<LegacyModList> jarModList() const;
- QList<Mod> getJarMods() const;
- std::shared_ptr<WorldList> worldList() const;
-
- /*!
- * Whether or not the instance's minecraft.jar needs to be rebuilt.
- * If this is true, when the instance launches, its jar mods will be
- * re-added to a fresh minecraft.jar file.
- */
- bool shouldRebuild() const;
-
- QString currentVersionId() const;
- QString intendedVersionId() const;
-
- QSet<QString> traits() const override
- {
- return {"legacy-instance", "texturepacks"};
- };
-
- virtual bool shouldUpdate() const;
- virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
-
- virtual QString typeName() const override;
-
- bool canLaunch() const override
- {
- return false;
- }
- bool canEdit() const override
- {
- return true;
- }
- bool canExport() const override
- {
- return false;
- }
- std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override
- {
- return nullptr;
- }
- IPathMatcher::Ptr getLogFileMatcher() override
- {
- return nullptr;
- }
- QString getLogFileRoot() override
- {
- return minecraftRoot();
- }
-
- QString getStatusbarDescription() override;
- QStringList verboseDescription(AuthSessionPtr session) override;
-
- QProcessEnvironment createEnvironment() override
- {
- return QProcessEnvironment();
- }
- QMap<QString, QString> getVariables() const override
- {
- return {};
- }
+ explicit LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
+
+ virtual void saveNow() override {}
+
+ /// Path to the instance's minecraft.jar
+ QString runnableJar() const;
+
+ //! Path to the instance's modlist file.
+ QString modListFile() const;
+
+ ////// Directories //////
+ QString libDir() const;
+ QString savesDir() const;
+ QString texturePacksDir() const;
+ QString jarModsDir() const;
+ QString loaderModsDir() const;
+ QString coreModsDir() const;
+ QString resourceDir() const;
+ virtual QString instanceConfigFolder() const override;
+ QString gameRoot() const override; // Path to the instance's minecraft directory.
+ QString binRoot() const; // Path to the instance's minecraft bin directory.
+
+ /// Get the curent base jar of this instance. By default, it's the
+ /// versions/$version/$version.jar
+ QString baseJar() const;
+
+ /// the default base jar of this instance
+ QString defaultBaseJar() const;
+ /// the default custom base jar of this instance
+ QString defaultCustomBaseJar() const;
+
+ // the main jar that we actually want to keep when migrating the instance
+ QString mainJarToPreserve() const;
+
+ /*!
+ * Whether or not custom base jar is used
+ */
+ bool shouldUseCustomBaseJar() const;
+
+ /*!
+ * The value of the custom base jar
+ */
+ QString customBaseJar() const;
+
+ std::shared_ptr<LegacyModList> jarModList() const;
+ std::shared_ptr<WorldList> worldList() const;
+
+ /*!
+ * Whether or not the instance's minecraft.jar needs to be rebuilt.
+ * If this is true, when the instance launches, its jar mods will be
+ * re-added to a fresh minecraft.jar file.
+ */
+ bool shouldRebuild() const;
+
+ QString currentVersionId() const;
+ QString intendedVersionId() const;
+
+ QSet<QString> traits() const override
+ {
+ return {"legacy-instance", "texturepacks"};
+ };
+
+ virtual bool shouldUpdate() const;
+ virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
+
+ virtual QString typeName() const override;
+
+ bool canLaunch() const override
+ {
+ return false;
+ }
+ bool canEdit() const override
+ {
+ return true;
+ }
+ bool canExport() const override
+ {
+ return false;
+ }
+ shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override
+ {
+ return nullptr;
+ }
+ IPathMatcher::Ptr getLogFileMatcher() override
+ {
+ return nullptr;
+ }
+ QString getLogFileRoot() override
+ {
+ return gameRoot();
+ }
+
+ QString getStatusbarDescription() override;
+ QStringList verboseDescription(AuthSessionPtr session) override;
+
+ QProcessEnvironment createEnvironment() override
+ {
+ return QProcessEnvironment();
+ }
+ QMap<QString, QString> getVariables() const override
+ {
+ return {};
+ }
protected:
- mutable std::shared_ptr<LegacyModList> jar_mod_list;
- mutable std::shared_ptr<WorldList> m_world_list;
+ mutable std::shared_ptr<LegacyModList> jar_mod_list;
+ mutable std::shared_ptr<WorldList> m_world_list;
};
diff --git a/api/logic/minecraft/legacy/LegacyModList.cpp b/api/logic/minecraft/legacy/LegacyModList.cpp
index 638b2e21..23b837c1 100644
--- a/api/logic/minecraft/legacy/LegacyModList.cpp
+++ b/api/logic/minecraft/legacy/LegacyModList.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,153 +19,118 @@
#include <QDebug>
LegacyModList::LegacyModList(const QString &dir, const QString &list_file)
- : m_dir(dir), m_list_file(list_file)
+ : m_dir(dir), m_list_file(list_file)
{
- FS::ensureFolderPathExists(m_dir.absolutePath());
- m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs |
- QDir::NoSymLinks);
- m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
+ FS::ensureFolderPathExists(m_dir.absolutePath());
+ m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::NoSymLinks);
+ m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
}
- struct OrderItem
- {
- QString id;
- bool enabled = false;
- };
- typedef QList<OrderItem> OrderList;
+ struct OrderItem
+ {
+ QString id;
+ bool enabled = false;
+ };
+ typedef QList<OrderItem> OrderList;
-static void internalSort(QList<Mod> &what)
+static void internalSort(QList<LegacyModList::Mod> &what)
{
- auto predicate = [](const Mod &left, const Mod &right)
- {
- if (left.name() == right.name())
- {
- return left.mmc_id().localeAwareCompare(right.mmc_id()) < 0;
- }
- return left.name().localeAwareCompare(right.name()) < 0;
- };
- std::sort(what.begin(), what.end(), predicate);
+ auto predicate = [](const LegacyModList::Mod &left, const LegacyModList::Mod &right)
+ {
+ return left.fileName().localeAwareCompare(right.fileName()) < 0;
+ };
+ std::sort(what.begin(), what.end(), predicate);
}
static OrderList readListFile(const QString &m_list_file)
{
- OrderList itemList;
- if (m_list_file.isNull() || m_list_file.isEmpty())
- return itemList;
+ OrderList itemList;
+ if (m_list_file.isNull() || m_list_file.isEmpty())
+ return itemList;
- QFile textFile(m_list_file);
- if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
- return OrderList();
+ QFile textFile(m_list_file);
+ if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ return OrderList();
- QTextStream textStream;
- textStream.setAutoDetectUnicode(true);
- textStream.setDevice(&textFile);
- while (true)
- {
- QString line = textStream.readLine();
- if (line.isNull() || line.isEmpty())
- break;
- else
- {
- OrderItem it;
- it.enabled = !line.endsWith(".disabled");
- if (!it.enabled)
- {
- line.chop(9);
- }
- it.id = line;
- itemList.append(it);
- }
- }
- textFile.close();
- return itemList;
+ QTextStream textStream;
+ textStream.setAutoDetectUnicode(true);
+ textStream.setDevice(&textFile);
+ while (true)
+ {
+ QString line = textStream.readLine();
+ if (line.isNull() || line.isEmpty())
+ break;
+ else
+ {
+ OrderItem it;
+ it.enabled = !line.endsWith(".disabled");
+ if (!it.enabled)
+ {
+ line.chop(9);
+ }
+ it.id = line;
+ itemList.append(it);
+ }
+ }
+ textFile.close();
+ return itemList;
}
bool LegacyModList::update()
{
- if (!m_dir.exists() || !m_dir.isReadable())
- return false;
+ if (!m_dir.exists() || !m_dir.isReadable())
+ return false;
- QList<Mod> orderedMods;
- QList<Mod> newMods;
- m_dir.refresh();
- auto folderContents = m_dir.entryInfoList();
- bool orderOrStateChanged = false;
+ QList<Mod> orderedMods;
+ QList<Mod> newMods;
+ m_dir.refresh();
+ auto folderContents = m_dir.entryInfoList();
- // first, process the ordered items (if any)
- OrderList listOrder = readListFile(m_list_file);
- for (auto item : listOrder)
- {
- QFileInfo infoEnabled(m_dir.filePath(item.id));
- QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled"));
- int idxEnabled = folderContents.indexOf(infoEnabled);
- int idxDisabled = folderContents.indexOf(infoDisabled);
- bool isEnabled;
- // if both enabled and disabled versions are present, it's a special case...
- if (idxEnabled >= 0 && idxDisabled >= 0)
- {
- // we only process the one we actually have in the order file.
- // and exactly as we have it.
- // THIS IS A CORNER CASE
- isEnabled = item.enabled;
- }
- else
- {
- // only one is present.
- // we pick the one that we found.
- // we assume the mod was enabled/disabled by external means
- isEnabled = idxEnabled >= 0;
- }
- int idx = isEnabled ? idxEnabled : idxDisabled;
- QFileInfo &info = isEnabled ? infoEnabled : infoDisabled;
- // if the file from the index file exists
- if (idx != -1)
- {
- // remove from the actual folder contents list
- folderContents.takeAt(idx);
- // append the new mod
- orderedMods.append(Mod(info));
- if (isEnabled != item.enabled)
- orderOrStateChanged = true;
- }
- else
- {
- orderOrStateChanged = true;
- }
- }
- // if there are any untracked files...
- if (folderContents.size())
- {
- // the order surely changed!
- for (auto entry : folderContents)
- {
- newMods.append(Mod(entry));
- }
- internalSort(newMods);
- orderedMods.append(newMods);
- orderOrStateChanged = true;
- }
- // otherwise, if we were already tracking some mods
- else if (mods.size())
- {
- // if the number doesn't match, order changed.
- if (mods.size() != orderedMods.size())
- orderOrStateChanged = true;
- // if it does match, compare the mods themselves
- else
- for (int i = 0; i < mods.size(); i++)
- {
- if (!mods[i].strongCompare(orderedMods[i]))
- {
- orderOrStateChanged = true;
- break;
- }
- }
- }
- mods.swap(orderedMods);
- if (orderOrStateChanged && !m_list_file.isEmpty())
- {
- qDebug() << "Mod list " << m_list_file << " changed!";
- }
- return true;
+ // first, process the ordered items (if any)
+ OrderList listOrder = readListFile(m_list_file);
+ for (auto item : listOrder)
+ {
+ QFileInfo infoEnabled(m_dir.filePath(item.id));
+ QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled"));
+ int idxEnabled = folderContents.indexOf(infoEnabled);
+ int idxDisabled = folderContents.indexOf(infoDisabled);
+ bool isEnabled;
+ // if both enabled and disabled versions are present, it's a special case...
+ if (idxEnabled >= 0 && idxDisabled >= 0)
+ {
+ // we only process the one we actually have in the order file.
+ // and exactly as we have it.
+ // THIS IS A CORNER CASE
+ isEnabled = item.enabled;
+ }
+ else
+ {
+ // only one is present.
+ // we pick the one that we found.
+ // we assume the mod was enabled/disabled by external means
+ isEnabled = idxEnabled >= 0;
+ }
+ int idx = isEnabled ? idxEnabled : idxDisabled;
+ QFileInfo &info = isEnabled ? infoEnabled : infoDisabled;
+ // if the file from the index file exists
+ if (idx != -1)
+ {
+ // remove from the actual folder contents list
+ folderContents.takeAt(idx);
+ // append the new mod
+ orderedMods.append(info);
+ }
+ }
+ // if there are any untracked files... append them sorted at the end
+ if (folderContents.size())
+ {
+ for (auto entry : folderContents)
+ {
+ newMods.append(entry);
+ }
+ internalSort(newMods);
+ orderedMods.append(newMods);
+ }
+ mods.swap(orderedMods);
+ return true;
}
diff --git a/api/logic/minecraft/legacy/LegacyModList.h b/api/logic/minecraft/legacy/LegacyModList.h
index 19b191a7..9a7bea50 100644
--- a/api/logic/minecraft/legacy/LegacyModList.h
+++ b/api/logic/minecraft/legacy/LegacyModList.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,38 +19,31 @@
#include <QString>
#include <QDir>
-#include "minecraft/Mod.h"
-
#include "multimc_logic_export.h"
-class LegacyInstance;
-class BaseInstance;
-
-/**
- * A legacy mod list.
- * Backed by a folder.
- */
class MULTIMC_LOGIC_EXPORT LegacyModList
{
public:
- LegacyModList(const QString &dir, const QString &list_file = QString());
+ using Mod = QFileInfo;
+
+ LegacyModList(const QString &dir, const QString &list_file = QString());
- /// Reloads the mod list and returns true if the list changed.
- bool update();
+ /// Reloads the mod list and returns true if the list changed.
+ bool update();
- QDir dir()
- {
- return m_dir;
- }
+ QDir dir()
+ {
+ return m_dir;
+ }
- const QList<Mod> & allMods()
- {
- return mods;
- }
+ const QList<Mod> & allMods()
+ {
+ return mods;
+ }
protected:
- QDir m_dir;
- QString m_list_file;
- QList<Mod> mods;
+ QDir m_dir;
+ QString m_list_file;
+ QList<Mod> mods;
};
diff --git a/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp b/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp
index 5cc3b5d9..9d86a7b5 100644
--- a/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp
+++ b/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp
@@ -1,5 +1,4 @@
#include "LegacyUpgradeTask.h"
-#include "BaseInstanceProvider.h"
#include "settings/INISettingsObject.h"
#include "FileSystem.h"
#include "NullInstance.h"
@@ -8,132 +7,132 @@
#include "LegacyInstance.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/ComponentList.h"
+#include "LegacyModList.h"
#include "classparser.h"
LegacyUpgradeTask::LegacyUpgradeTask(InstancePtr origInstance)
{
- m_origInstance = origInstance;
+ m_origInstance = origInstance;
}
void LegacyUpgradeTask::executeTask()
{
- setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
+ setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
- FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
- folderCopy.followSymlinks(true);
+ FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
+ folderCopy.followSymlinks(true);
- m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
- connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &LegacyUpgradeTask::copyFinished);
- connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &LegacyUpgradeTask::copyAborted);
- m_copyFutureWatcher.setFuture(m_copyFuture);
+ m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
+ connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &LegacyUpgradeTask::copyFinished);
+ connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &LegacyUpgradeTask::copyAborted);
+ m_copyFutureWatcher.setFuture(m_copyFuture);
}
static QString decideVersion(const QString& currentVersion, const QString& intendedVersion)
{
- if(intendedVersion != currentVersion)
- {
- if(!intendedVersion.isEmpty())
- {
- return intendedVersion;
- }
- else if(!currentVersion.isEmpty())
- {
- return currentVersion;
- }
- }
- else
- {
- if(!intendedVersion.isEmpty())
- {
- return intendedVersion;
- }
- }
- return QString();
+ if(intendedVersion != currentVersion)
+ {
+ if(!intendedVersion.isEmpty())
+ {
+ return intendedVersion;
+ }
+ else if(!currentVersion.isEmpty())
+ {
+ return currentVersion;
+ }
+ }
+ else
+ {
+ if(!intendedVersion.isEmpty())
+ {
+ return intendedVersion;
+ }
+ }
+ return QString();
}
void LegacyUpgradeTask::copyFinished()
{
- auto successful = m_copyFuture.result();
- if(!successful)
- {
- emitFailed(tr("Instance folder copy failed."));
- return;
- }
- auto legacyInst = std::dynamic_pointer_cast<LegacyInstance>(m_origInstance);
+ auto successful = m_copyFuture.result();
+ if(!successful)
+ {
+ emitFailed(tr("Instance folder copy failed."));
+ return;
+ }
+ auto legacyInst = std::dynamic_pointer_cast<LegacyInstance>(m_origInstance);
- auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
- // NOTE: this scope ensures the instance is fully saved before we emitSucceeded
- {
- MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
- inst.setName(m_instName);
- inst.init();
+ auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+ instanceSettings->set("InstanceType", "OneSix");
+ // NOTE: this scope ensures the instance is fully saved before we emitSucceeded
+ {
+ MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
+ inst.setName(m_instName);
- QString preferredVersionNumber = decideVersion(legacyInst->currentVersionId(), legacyInst->intendedVersionId());
- if(preferredVersionNumber.isNull())
- {
- // try to decide version based on the jar(s?)
- preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->baseJar());
- if(preferredVersionNumber.isNull())
- {
- preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->runnableJar());
- if(preferredVersionNumber.isNull())
- {
- emitFailed(tr("Could not decide Minecraft version."));
- return;
- }
- }
- }
- auto components = inst.getComponentList();
- components->buildingFromScratch();
- components->setComponentVersion("net.minecraft", preferredVersionNumber, true);
+ QString preferredVersionNumber = decideVersion(legacyInst->currentVersionId(), legacyInst->intendedVersionId());
+ if(preferredVersionNumber.isNull())
+ {
+ // try to decide version based on the jar(s?)
+ preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->baseJar());
+ if(preferredVersionNumber.isNull())
+ {
+ preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->runnableJar());
+ if(preferredVersionNumber.isNull())
+ {
+ emitFailed(tr("Could not decide Minecraft version."));
+ return;
+ }
+ }
+ }
+ auto components = inst.getComponentList();
+ components->buildingFromScratch();
+ components->setComponentVersion("net.minecraft", preferredVersionNumber, true);
- QString jarPath = legacyInst->mainJarToPreserve();
- if(!jarPath.isNull())
- {
- qDebug() << "Preserving base jar! : " << jarPath;
- // FIXME: handle case when the jar is unreadable?
- // TODO: check the hash, if it's the same as the upstream jar, do not do this
- components->installCustomJar(jarPath);
- }
+ QString jarPath = legacyInst->mainJarToPreserve();
+ if(!jarPath.isNull())
+ {
+ qDebug() << "Preserving base jar! : " << jarPath;
+ // FIXME: handle case when the jar is unreadable?
+ // TODO: check the hash, if it's the same as the upstream jar, do not do this
+ components->installCustomJar(jarPath);
+ }
- auto jarMods = legacyInst->getJarMods();
- for(auto & jarMod: jarMods)
- {
- QString modPath = jarMod.filename().absoluteFilePath();
- qDebug() << "jarMod: " << modPath;
- components->installJarMods({modPath});
- }
+ auto jarMods = legacyInst->jarModList()->allMods();
+ for(auto & jarMod: jarMods)
+ {
+ QString modPath = jarMod.absoluteFilePath();
+ qDebug() << "jarMod: " << modPath;
+ components->installJarMods({modPath});
+ }
- // remove all the extra garbage we no longer need
- auto removeAll = [&](const QString &root, const QStringList &things)
- {
- for(auto &thing : things)
- {
- auto removePath = FS::PathCombine(root, thing);
- QFileInfo stat(removePath);
- if(stat.isDir())
- {
- FS::deletePath(removePath);
- }
- else
- {
- QFile::remove(removePath);
- }
- }
- };
- QStringList rootRemovables = {"modlist", "version", "instMods"};
- QStringList mcRemovables = {"bin", "MultiMCLauncher.jar", "icon.png"};
- removeAll(inst.instanceRoot(), rootRemovables);
- removeAll(inst.minecraftRoot(), mcRemovables);
- }
- emitSucceeded();
+ // remove all the extra garbage we no longer need
+ auto removeAll = [&](const QString &root, const QStringList &things)
+ {
+ for(auto &thing : things)
+ {
+ auto removePath = FS::PathCombine(root, thing);
+ QFileInfo stat(removePath);
+ if(stat.isDir())
+ {
+ FS::deletePath(removePath);
+ }
+ else
+ {
+ QFile::remove(removePath);
+ }
+ }
+ };
+ QStringList rootRemovables = {"modlist", "version", "instMods"};
+ QStringList mcRemovables = {"bin", "MultiMCLauncher.jar", "icon.png"};
+ removeAll(inst.instanceRoot(), rootRemovables);
+ removeAll(inst.gameRoot(), mcRemovables);
+ }
+ emitSucceeded();
}
void LegacyUpgradeTask::copyAborted()
{
- emitFailed(tr("Instance folder copy has been aborted."));
- return;
+ emitFailed(tr("Instance folder copy has been aborted."));
+ return;
}
diff --git a/api/logic/minecraft/legacy/LegacyUpgradeTask.h b/api/logic/minecraft/legacy/LegacyUpgradeTask.h
index a93dd0d9..e35e43b7 100644
--- a/api/logic/minecraft/legacy/LegacyUpgradeTask.h
+++ b/api/logic/minecraft/legacy/LegacyUpgradeTask.h
@@ -11,22 +11,20 @@
#include "BaseInstance.h"
-class BaseInstanceProvider;
-
class MULTIMC_LOGIC_EXPORT LegacyUpgradeTask : public InstanceTask
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit LegacyUpgradeTask(InstancePtr origInstance);
+ explicit LegacyUpgradeTask(InstancePtr origInstance);
protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
- void copyFinished();
- void copyAborted();
+ //! Entry point for tasks.
+ virtual void executeTask() override;
+ void copyFinished();
+ void copyAborted();
private: /* data */
- InstancePtr m_origInstance;
- QFuture<bool> m_copyFuture;
- QFutureWatcher<bool> m_copyFutureWatcher;
+ InstancePtr m_origInstance;
+ QFuture<bool> m_copyFuture;
+ QFutureWatcher<bool> m_copyFutureWatcher;
};
diff --git a/api/logic/minecraft/mod/LocalModParseTask.cpp b/api/logic/minecraft/mod/LocalModParseTask.cpp
new file mode 100644
index 00000000..22ebd7d4
--- /dev/null
+++ b/api/logic/minecraft/mod/LocalModParseTask.cpp
@@ -0,0 +1,298 @@
+#include "LocalModParseTask.h"
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
+#include <quazip.h>
+#include <quazipfile.h>
+
+#include "settings/INIFile.h"
+#include "FileSystem.h"
+
+namespace {
+
+// NEW format
+// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3
+
+// OLD format:
+// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
+std::shared_ptr<ModDetails> ReadMCModInfo(QByteArray contents)
+{
+ auto getInfoFromArray = [&](QJsonArray arr)->std::shared_ptr<ModDetails>
+ {
+ if (!arr.at(0).isObject()) {
+ return nullptr;
+ }
+ std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
+ auto firstObj = arr.at(0).toObject();
+ details->mod_id = firstObj.value("modid").toString();
+ auto name = firstObj.value("name").toString();
+ // NOTE: ignore stupid example mods copies where the author didn't even bother to change the name
+ if(name != "Example Mod") {
+ details->name = name;
+ }
+ details->version = firstObj.value("version").toString();
+ details->updateurl = firstObj.value("updateUrl").toString();
+ auto homeurl = firstObj.value("url").toString().trimmed();
+ if(!homeurl.isEmpty())
+ {
+ // fix up url.
+ if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://"))
+ {
+ homeurl.prepend("http://");
+ }
+ }
+ details->homeurl = homeurl;
+ details->description = firstObj.value("description").toString();
+ QJsonArray authors = firstObj.value("authorList").toArray();
+ if (authors.size() == 0) {
+ // FIXME: what is the format of this? is there any?
+ authors = firstObj.value("authors").toArray();
+ }
+
+ for (auto author: authors)
+ {
+ details->authors.append(author.toString());
+ }
+ details->credits = firstObj.value("credits").toString();
+ return details;
+ };
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
+ // this is the very old format that had just the array
+ if (jsonDoc.isArray())
+ {
+ return getInfoFromArray(jsonDoc.array());
+ }
+ else if (jsonDoc.isObject())
+ {
+ auto val = jsonDoc.object().value("modinfoversion");
+ if(val.isUndefined()) {
+ val = jsonDoc.object().value("modListVersion");
+ }
+ int version = val.toDouble();
+ if (version != 2)
+ {
+ qCritical() << "BAD stuff happened to mod json:";
+ qCritical() << contents;
+ return nullptr;
+ }
+ auto arrVal = jsonDoc.object().value("modlist");
+ if(arrVal.isUndefined()) {
+ arrVal = jsonDoc.object().value("modList");
+ }
+ if (arrVal.isArray())
+ {
+ return getInfoFromArray(arrVal.toArray());
+ }
+ }
+ return nullptr;
+}
+
+// https://fabricmc.net/wiki/documentation:fabric_mod_json
+std::shared_ptr<ModDetails> ReadFabricModInfo(QByteArray contents)
+{
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
+ auto object = jsonDoc.object();
+ auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0;
+
+ std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
+
+ details->mod_id = object.value("id").toString();
+ details->version = object.value("version").toString();
+
+ details->name = object.contains("name") ? object.value("name").toString() : details->mod_id;
+ details->description = object.value("description").toString();
+
+ if (schemaVersion >= 1)
+ {
+ QJsonArray authors = object.value("authors").toArray();
+ for (auto author: authors)
+ {
+ if(author.isObject()) {
+ details->authors.append(author.toObject().value("name").toString());
+ }
+ else {
+ details->authors.append(author.toString());
+ }
+ }
+
+ if (object.contains("contact"))
+ {
+ QJsonObject contact = object.value("contact").toObject();
+
+ if (contact.contains("homepage"))
+ {
+ details->homeurl = contact.value("homepage").toString();
+ }
+ }
+ }
+ return details;
+}
+
+std::shared_ptr<ModDetails> ReadForgeInfo(QByteArray contents)
+{
+ std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
+ // Read the data
+ details->name = "Minecraft Forge";
+ details->mod_id = "Forge";
+ details->homeurl = "http://www.minecraftforge.net/forum/";
+ INIFile ini;
+ if (!ini.loadFile(contents))
+ return details;
+
+ QString major = ini.get("forge.major.number", "0").toString();
+ QString minor = ini.get("forge.minor.number", "0").toString();
+ QString revision = ini.get("forge.revision.number", "0").toString();
+ QString build = ini.get("forge.build.number", "0").toString();
+
+ details->version = major + "." + minor + "." + revision + "." + build;
+ return details;
+}
+
+std::shared_ptr<ModDetails> ReadLiteModInfo(QByteArray contents)
+{
+ std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
+ auto object = jsonDoc.object();
+ if (object.contains("name"))
+ {
+ details->mod_id = details->name = object.value("name").toString();
+ }
+ if (object.contains("version"))
+ {
+ details->version = object.value("version").toString("");
+ }
+ else
+ {
+ details->version = object.value("revision").toString("");
+ }
+ details->mcversion = object.value("mcversion").toString();
+ auto author = object.value("author").toString();
+ if(!author.isEmpty()) {
+ details->authors.append(author);
+ }
+ details->description = object.value("description").toString();
+ details->homeurl = object.value("url").toString();
+ return details;
+}
+
+}
+
+LocalModParseTask::LocalModParseTask(int token, Mod::ModType type, const QFileInfo& modFile):
+ m_token(token),
+ m_type(type),
+ m_modFile(modFile),
+ m_result(new Result())
+{
+}
+
+void LocalModParseTask::processAsZip()
+{
+ QuaZip zip(m_modFile.filePath());
+ if (!zip.open(QuaZip::mdUnzip))
+ return;
+
+ QuaZipFile file(&zip);
+
+ if (zip.setCurrentFile("mcmod.info"))
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ zip.close();
+ return;
+ }
+
+ m_result->details = ReadMCModInfo(file.readAll());
+ file.close();
+ zip.close();
+ return;
+ }
+ else if (zip.setCurrentFile("fabric.mod.json"))
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ zip.close();
+ return;
+ }
+
+ m_result->details = ReadFabricModInfo(file.readAll());
+ file.close();
+ zip.close();
+ return;
+ }
+ else if (zip.setCurrentFile("forgeversion.properties"))
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ zip.close();
+ return;
+ }
+
+ m_result->details = ReadForgeInfo(file.readAll());
+ file.close();
+ zip.close();
+ return;
+ }
+
+ zip.close();
+}
+
+void LocalModParseTask::processAsFolder()
+{
+ QFileInfo mcmod_info(FS::PathCombine(m_modFile.filePath(), "mcmod.info"));
+ if (mcmod_info.isFile())
+ {
+ QFile mcmod(mcmod_info.filePath());
+ if (!mcmod.open(QIODevice::ReadOnly))
+ return;
+ auto data = mcmod.readAll();
+ if (data.isEmpty() || data.isNull())
+ return;
+ m_result->details = ReadMCModInfo(data);
+ }
+}
+
+void LocalModParseTask::processAsLitemod()
+{
+ QuaZip zip(m_modFile.filePath());
+ if (!zip.open(QuaZip::mdUnzip))
+ return;
+
+ QuaZipFile file(&zip);
+
+ if (zip.setCurrentFile("litemod.json"))
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ zip.close();
+ return;
+ }
+
+ m_result->details = ReadLiteModInfo(file.readAll());
+ file.close();
+ }
+ zip.close();
+}
+
+void LocalModParseTask::run()
+{
+ switch(m_type)
+ {
+ case Mod::MOD_ZIPFILE:
+ processAsZip();
+ break;
+ case Mod::MOD_FOLDER:
+ processAsFolder();
+ break;
+ case Mod::MOD_LITEMOD:
+ processAsLitemod();
+ break;
+ default:
+ break;
+ }
+ emit finished(m_token);
+}
diff --git a/api/logic/minecraft/mod/LocalModParseTask.h b/api/logic/minecraft/mod/LocalModParseTask.h
new file mode 100644
index 00000000..0f119ba6
--- /dev/null
+++ b/api/logic/minecraft/mod/LocalModParseTask.h
@@ -0,0 +1,37 @@
+#pragma once
+#include <QRunnable>
+#include <QDebug>
+#include <QObject>
+#include "Mod.h"
+#include "ModDetails.h"
+
+class LocalModParseTask : public QObject, public QRunnable
+{
+ Q_OBJECT
+public:
+ struct Result {
+ QString id;
+ std::shared_ptr<ModDetails> details;
+ };
+ using ResultPtr = std::shared_ptr<Result>;
+ ResultPtr result() const {
+ return m_result;
+ }
+
+ LocalModParseTask(int token, Mod::ModType type, const QFileInfo & modFile);
+ void run();
+
+signals:
+ void finished(int token);
+
+private:
+ void processAsZip();
+ void processAsFolder();
+ void processAsLitemod();
+
+private:
+ int m_token;
+ Mod::ModType m_type;
+ QFileInfo m_modFile;
+ ResultPtr m_result;
+};
diff --git a/api/logic/minecraft/mod/Mod.cpp b/api/logic/minecraft/mod/Mod.cpp
new file mode 100644
index 00000000..df8b406d
--- /dev/null
+++ b/api/logic/minecraft/mod/Mod.cpp
@@ -0,0 +1,151 @@
+/* Copyright 2013-2019 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <QDir>
+#include <QString>
+
+#include "Mod.h"
+#include <QDebug>
+#include <FileSystem.h>
+
+namespace {
+
+ModDetails invalidDetails;
+
+}
+
+
+Mod::Mod(const QFileInfo &file)
+{
+ repath(file);
+ m_changedDateTime = file.lastModified();
+}
+
+void Mod::repath(const QFileInfo &file)
+{
+ m_file = file;
+ QString name_base = file.fileName();
+
+ m_type = Mod::MOD_UNKNOWN;
+
+ m_mmc_id = name_base;
+
+ if (m_file.isDir())
+ {
+ m_type = MOD_FOLDER;
+ m_name = name_base;
+ }
+ else if (m_file.isFile())
+ {
+ if (name_base.endsWith(".disabled"))
+ {
+ m_enabled = false;
+ name_base.chop(9);
+ }
+ else
+ {
+ m_enabled = true;
+ }
+ if (name_base.endsWith(".zip") || name_base.endsWith(".jar"))
+ {
+ m_type = MOD_ZIPFILE;
+ name_base.chop(4);
+ }
+ else if (name_base.endsWith(".litemod"))
+ {
+ m_type = MOD_LITEMOD;
+ name_base.chop(8);
+ }
+ else
+ {
+ m_type = MOD_SINGLEFILE;
+ }
+ m_name = name_base;
+ }
+}
+
+bool Mod::enable(bool value)
+{
+ if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER)
+ return false;
+
+ if (m_enabled == value)
+ return false;
+
+ QString path = m_file.absoluteFilePath();
+ if (value)
+ {
+ QFile foo(path);
+ if (!path.endsWith(".disabled"))
+ return false;
+ path.chop(9);
+ if (!foo.rename(path))
+ return false;
+ }
+ else
+ {
+ QFile foo(path);
+ path += ".disabled";
+ if (!foo.rename(path))
+ return false;
+ }
+ repath(QFileInfo(path));
+ m_enabled = value;
+ return true;
+}
+
+bool Mod::destroy()
+{
+ m_type = MOD_UNKNOWN;
+ return FS::deletePath(m_file.filePath());
+}
+
+
+const ModDetails & Mod::details() const
+{
+ if(!m_localDetails)
+ return invalidDetails;
+ return *m_localDetails;
+}
+
+
+QString Mod::version() const
+{
+ return details().version;
+}
+
+QString Mod::name() const
+{
+ auto & d = details();
+ if(!d.name.isEmpty()) {
+ return d.name;
+ }
+ return m_name;
+}
+
+QString Mod::homeurl() const
+{
+ return details().homeurl;
+}
+
+QString Mod::description() const
+{
+ return details().description;
+}
+
+QStringList Mod::authors() const
+{
+ return details().authors;
+}
diff --git a/api/logic/minecraft/mod/Mod.h b/api/logic/minecraft/mod/Mod.h
new file mode 100644
index 00000000..d787fb48
--- /dev/null
+++ b/api/logic/minecraft/mod/Mod.h
@@ -0,0 +1,117 @@
+/* Copyright 2013-2019 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <QFileInfo>
+#include <QDateTime>
+#include <QList>
+#include <memory>
+
+#include "multimc_logic_export.h"
+
+#include "ModDetails.h"
+
+
+
+class MULTIMC_LOGIC_EXPORT Mod
+{
+public:
+ enum ModType
+ {
+ MOD_UNKNOWN, //!< Indicates an unspecified mod type.
+ MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files.
+ MOD_SINGLEFILE, //!< The mod is a single file (not a zip file).
+ MOD_FOLDER, //!< The mod is in a folder on the filesystem.
+ MOD_LITEMOD, //!< The mod is a litemod
+ };
+
+ Mod() = default;
+ Mod(const QFileInfo &file);
+
+ QFileInfo filename() const
+ {
+ return m_file;
+ }
+ QString mmc_id() const
+ {
+ return m_mmc_id;
+ }
+ ModType type() const
+ {
+ return m_type;
+ }
+ bool valid()
+ {
+ return m_type != MOD_UNKNOWN;
+ }
+
+ QDateTime dateTimeChanged() const
+ {
+ return m_changedDateTime;
+ }
+
+ bool enabled() const
+ {
+ return m_enabled;
+ }
+
+ const ModDetails &details() const;
+
+ QString name() const;
+ QString version() const;
+ QString homeurl() const;
+ QString description() const;
+ QStringList authors() const;
+
+ bool enable(bool value);
+
+ // delete all the files of this mod
+ bool destroy();
+
+ // change the mod's filesystem path (used by mod lists for *MAGIC* purposes)
+ void repath(const QFileInfo &file);
+
+ bool shouldResolve() {
+ return !m_resolving && !m_resolved;
+ }
+ bool isResolving() {
+ return m_resolving;
+ }
+ int resolutionTicket()
+ {
+ return m_resolutionTicket;
+ }
+ void setResolving(bool resolving, int resolutionTicket) {
+ m_resolving = resolving;
+ m_resolutionTicket = resolutionTicket;
+ }
+ void finishResolvingWithDetails(std::shared_ptr<ModDetails> details){
+ m_resolving = false;
+ m_resolved = true;
+ m_localDetails = details;
+ }
+
+protected:
+ QFileInfo m_file;
+ QDateTime m_changedDateTime;
+ QString m_mmc_id;
+ QString m_name;
+ bool m_enabled = true;
+ bool m_resolving = false;
+ bool m_resolved = false;
+ int m_resolutionTicket = 0;
+ ModType m_type = MOD_UNKNOWN;
+ std::shared_ptr<ModDetails> m_localDetails;
+};
diff --git a/api/logic/minecraft/mod/ModDetails.h b/api/logic/minecraft/mod/ModDetails.h
new file mode 100644
index 00000000..6ab4aee7
--- /dev/null
+++ b/api/logic/minecraft/mod/ModDetails.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <QString>
+#include <QStringList>
+
+struct ModDetails
+{
+ QString mod_id;
+ QString name;
+ QString version;
+ QString mcversion;
+ QString homeurl;
+ QString updateurl;
+ QString description;
+ QStringList authors;
+ QString credits;
+};
diff --git a/api/logic/minecraft/mod/ModFolderLoadTask.cpp b/api/logic/minecraft/mod/ModFolderLoadTask.cpp
new file mode 100644
index 00000000..88349877
--- /dev/null
+++ b/api/logic/minecraft/mod/ModFolderLoadTask.cpp
@@ -0,0 +1,18 @@
+#include "ModFolderLoadTask.h"
+#include <QDebug>
+
+ModFolderLoadTask::ModFolderLoadTask(QDir dir) :
+ m_dir(dir), m_result(new Result())
+{
+}
+
+void ModFolderLoadTask::run()
+{
+ m_dir.refresh();
+ for (auto entry : m_dir.entryInfoList())
+ {
+ Mod m(entry);
+ m_result->mods[m.mmc_id()] = m;
+ }
+ emit succeeded();
+}
diff --git a/api/logic/minecraft/mod/ModFolderLoadTask.h b/api/logic/minecraft/mod/ModFolderLoadTask.h
new file mode 100644
index 00000000..8d720e65
--- /dev/null
+++ b/api/logic/minecraft/mod/ModFolderLoadTask.h
@@ -0,0 +1,29 @@
+#pragma once
+#include <QRunnable>
+#include <QObject>
+#include <QDir>
+#include <QMap>
+#include "Mod.h"
+#include <memory>
+
+class ModFolderLoadTask : public QObject, public QRunnable
+{
+ Q_OBJECT
+public:
+ struct Result {
+ QMap<QString, Mod> mods;
+ };
+ using ResultPtr = std::shared_ptr<Result>;
+ ResultPtr result() const {
+ return m_result;
+ }
+
+public:
+ ModFolderLoadTask(QDir dir);
+ void run();
+signals:
+ void succeeded();
+private:
+ QDir m_dir;
+ ResultPtr m_result;
+};
diff --git a/api/logic/minecraft/mod/ModFolderModel.cpp b/api/logic/minecraft/mod/ModFolderModel.cpp
new file mode 100644
index 00000000..59ccaaba
--- /dev/null
+++ b/api/logic/minecraft/mod/ModFolderModel.cpp
@@ -0,0 +1,554 @@
+/* Copyright 2013-2019 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ModFolderModel.h"
+#include <FileSystem.h>
+#include <QMimeData>
+#include <QUrl>
+#include <QUuid>
+#include <QString>
+#include <QFileSystemWatcher>
+#include <QDebug>
+#include "ModFolderLoadTask.h"
+#include <QThreadPool>
+#include <algorithm>
+#include "LocalModParseTask.h"
+
+ModFolderModel::ModFolderModel(const QString &dir) : QAbstractListModel(), m_dir(dir)
+{
+ FS::ensureFolderPathExists(m_dir.absolutePath());
+ m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::NoSymLinks);
+ m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
+ m_watcher = new QFileSystemWatcher(this);
+ connect(m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString)));
+}
+
+void ModFolderModel::startWatching()
+{
+ if(is_watching)
+ return;
+
+ update();
+
+ is_watching = m_watcher->addPath(m_dir.absolutePath());
+ if (is_watching)
+ {
+ qDebug() << "Started watching " << m_dir.absolutePath();
+ }
+ else
+ {
+ qDebug() << "Failed to start watching " << m_dir.absolutePath();
+ }
+}
+
+void ModFolderModel::stopWatching()
+{
+ if(!is_watching)
+ return;
+
+ is_watching = !m_watcher->removePath(m_dir.absolutePath());
+ if (!is_watching)
+ {
+ qDebug() << "Stopped watching " << m_dir.absolutePath();
+ }
+ else
+ {
+ qDebug() << "Failed to stop watching " << m_dir.absolutePath();
+ }
+}
+
+bool ModFolderModel::update()
+{
+ if (!isValid()) {
+ return false;
+ }
+ if(m_update) {
+ scheduled_update = true;
+ return true;
+ }
+
+ auto task = new ModFolderLoadTask(m_dir);
+ m_update = task->result();
+ QThreadPool *threadPool = QThreadPool::globalInstance();
+ connect(task, &ModFolderLoadTask::succeeded, this, &ModFolderModel::finishUpdate);
+ threadPool->start(task);
+ return true;
+}
+
+void ModFolderModel::finishUpdate()
+{
+ QSet<QString> currentSet = modsIndex.keys().toSet();
+ auto & newMods = m_update->mods;
+ QSet<QString> newSet = newMods.keys().toSet();
+
+ // see if the kept mods changed in some way
+ {
+ QSet<QString> kept = currentSet;
+ kept.intersect(newSet);
+ for(auto & keptMod: kept) {
+ auto & newMod = newMods[keptMod];
+ auto row = modsIndex[keptMod];
+ auto & currentMod = mods[row];
+ if(newMod.dateTimeChanged() == currentMod.dateTimeChanged()) {
+ // no significant change, ignore...
+ continue;
+ }
+ auto & oldMod = mods[row];
+ if(oldMod.isResolving()) {
+ activeTickets.remove(oldMod.resolutionTicket());
+ }
+ oldMod = newMod;
+ resolveMod(mods[row]);
+ emit dataChanged(index(row, 0), index(row, columnCount(QModelIndex()) - 1));
+ }
+ }
+
+ // remove mods no longer present
+ {
+ QSet<QString> removed = currentSet;
+ QList<int> removedRows;
+ removed.subtract(newSet);
+ for(auto & removedMod: removed) {
+ removedRows.append(modsIndex[removedMod]);
+ }
+ std::sort(removedRows.begin(), removedRows.end(), std::greater<int>());
+ for(auto iter = removedRows.begin(); iter != removedRows.end(); iter++) {
+ int removedIndex = *iter;
+ beginRemoveRows(QModelIndex(), removedIndex, removedIndex);
+ auto removedIter = mods.begin() + removedIndex;
+ if(removedIter->isResolving()) {
+ activeTickets.remove(removedIter->resolutionTicket());
+ }
+ mods.erase(removedIter);
+ endRemoveRows();
+ }
+ }
+
+ // add new mods to the end
+ {
+ QSet<QString> added = newSet;
+ added.subtract(currentSet);
+ beginInsertRows(QModelIndex(), mods.size(), mods.size() + added.size() - 1);
+ for(auto & addedMod: added) {
+ mods.append(newMods[addedMod]);
+ resolveMod(mods.last());
+ }
+ endInsertRows();
+ }
+
+ // update index
+ {
+ modsIndex.clear();
+ int idx = 0;
+ for(auto & mod: mods) {
+ modsIndex[mod.mmc_id()] = idx;
+ idx++;
+ }
+ }
+
+ m_update.reset();
+
+ emit updateFinished();
+
+ if(scheduled_update) {
+ scheduled_update = false;
+ update();
+ }
+}
+
+void ModFolderModel::resolveMod(Mod& m)
+{
+ if(!m.shouldResolve()) {
+ return;
+ }
+
+ auto task = new LocalModParseTask(nextResolutionTicket, m.type(), m.filename());
+ auto result = task->result();
+ result->id = m.mmc_id();
+ activeTickets.insert(nextResolutionTicket, result);
+ m.setResolving(true, nextResolutionTicket);
+ nextResolutionTicket++;
+ QThreadPool *threadPool = QThreadPool::globalInstance();
+ connect(task, &LocalModParseTask::finished, this, &ModFolderModel::finishModParse);
+ threadPool->start(task);
+}
+
+void ModFolderModel::finishModParse(int token)
+{
+ auto iter = activeTickets.find(token);
+ if(iter == activeTickets.end()) {
+ return;
+ }
+ auto result = *iter;
+ activeTickets.remove(token);
+ int row = modsIndex[result->id];
+ auto & mod = mods[row];
+ mod.finishResolvingWithDetails(result->details);
+ emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1));
+}
+
+void ModFolderModel::disableInteraction(bool disabled)
+{
+ if (interaction_disabled == disabled) {
+ return;
+ }
+ interaction_disabled = disabled;
+ if(size()) {
+ emit dataChanged(index(0), index(size() - 1));
+ }
+}
+
+void ModFolderModel::directoryChanged(QString path)
+{
+ update();
+}
+
+bool ModFolderModel::isValid()
+{
+ return m_dir.exists() && m_dir.isReadable();
+}
+
+// FIXME: this does not take disabled mod (with extra .disable extension) into account...
+bool ModFolderModel::installMod(const QString &filename)
+{
+ if(interaction_disabled) {
+ return false;
+ }
+
+ // NOTE: fix for GH-1178: remove trailing slash to avoid issues with using the empty result of QFileInfo::fileName
+ auto originalPath = FS::NormalizePath(filename);
+ QFileInfo fileinfo(originalPath);
+
+ if (!fileinfo.exists() || !fileinfo.isReadable())
+ {
+ qWarning() << "Caught attempt to install non-existing file or file-like object:" << originalPath;
+ return false;
+ }
+ qDebug() << "installing: " << fileinfo.absoluteFilePath();
+
+ Mod installedMod(fileinfo);
+ if (!installedMod.valid())
+ {
+ qDebug() << originalPath << "is not a valid mod. Ignoring it.";
+ return false;
+ }
+
+ auto type = installedMod.type();
+ if (type == Mod::MOD_UNKNOWN)
+ {
+ qDebug() << "Cannot recognize mod type of" << originalPath << ", ignoring it.";
+ return false;
+ }
+
+ auto newpath = FS::NormalizePath(FS::PathCombine(m_dir.path(), fileinfo.fileName()));
+ if(originalPath == newpath)
+ {
+ qDebug() << "Overwriting the mod (" << originalPath << ") with itself makes no sense...";
+ return false;
+ }
+
+ if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE || type == Mod::MOD_LITEMOD)
+ {
+ if(QFile::exists(newpath) || QFile::exists(newpath + QString(".disabled")))
+ {
+ if(!QFile::remove(newpath))
+ {
+ // FIXME: report error in a user-visible way
+ qWarning() << "Copy from" << originalPath << "to" << newpath << "has failed.";
+ return false;
+ }
+ qDebug() << newpath << "has been deleted.";
+ }
+ if (!QFile::copy(fileinfo.filePath(), newpath))
+ {
+ qWarning() << "Copy from" << originalPath << "to" << newpath << "has failed.";
+ // FIXME: report error in a user-visible way
+ return false;
+ }
+ FS::updateTimestamp(newpath);
+ installedMod.repath(newpath);
+ update();
+ return true;
+ }
+ else if (type == Mod::MOD_FOLDER)
+ {
+ QString from = fileinfo.filePath();
+ if(QFile::exists(newpath))
+ {
+ qDebug() << "Ignoring folder " << from << ", it would merge with " << newpath;
+ return false;
+ }
+
+ if (!FS::copy(from, newpath)())
+ {
+ qWarning() << "Copy of folder from" << originalPath << "to" << newpath << "has (potentially partially) failed.";
+ return false;
+ }
+ installedMod.repath(newpath);
+ update();
+ return true;
+ }
+ return false;
+}
+
+bool ModFolderModel::setModStatus(const QModelIndexList& indexes, ModStatusAction enable)
+{
+ if(interaction_disabled) {
+ return false;
+ }
+
+ if(indexes.isEmpty())
+ return true;
+
+ for (auto index: indexes)
+ {
+ if(index.column() != 0) {
+ continue;
+ }
+ setModStatus(index.row(), enable);
+ }
+ return true;
+}
+
+bool ModFolderModel::deleteMods(const QModelIndexList& indexes)
+{
+ if(interaction_disabled) {
+ return false;
+ }
+
+ if(indexes.isEmpty())
+ return true;
+
+ for (auto i: indexes)
+ {
+ Mod &m = mods[i.row()];
+ m.destroy();
+ }
+ return true;
+}
+
+int ModFolderModel::columnCount(const QModelIndex &parent) const
+{
+ return NUM_COLUMNS;
+}
+
+QVariant ModFolderModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+ int column = index.column();
+
+ if (row < 0 || row >= mods.size())
+ return QVariant();
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (column)
+ {
+ case NameColumn:
+ return mods[row].name();
+ case VersionColumn: {
+ switch(mods[row].type()) {
+ case Mod::MOD_FOLDER:
+ return tr("Folder");
+ case Mod::MOD_SINGLEFILE:
+ return tr("File");
+ default:
+ break;
+ }
+ return mods[row].version();
+ }
+ case DateColumn:
+ return mods[row].dateTimeChanged();
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ return mods[row].mmc_id();
+
+ case Qt::CheckStateRole:
+ switch (column)
+ {
+ case ActiveColumn:
+ return mods[row].enabled() ? Qt::Checked : Qt::Unchecked;
+ default:
+ return QVariant();
+ }
+ default:
+ return QVariant();
+ }
+}
+
+bool ModFolderModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
+ {
+ return false;
+ }
+
+ if (role == Qt::CheckStateRole)
+ {
+ return setModStatus(index.row(), Toggle);
+ }
+ return false;
+}
+
+bool ModFolderModel::setModStatus(int row, ModFolderModel::ModStatusAction action)
+{
+ if(row < 0 || row >= mods.size()) {
+ return false;
+ }
+
+ auto &mod = mods[row];
+ bool desiredStatus;
+ switch(action) {
+ case Enable:
+ desiredStatus = true;
+ break;
+ case Disable:
+ desiredStatus = false;
+ break;
+ case Toggle:
+ default:
+ desiredStatus = !mod.enabled();
+ break;
+ }
+
+ if(desiredStatus == mod.enabled()) {
+ return true;
+ }
+
+ // preserve the row, but change its ID
+ auto oldId = mod.mmc_id();
+ if(!mod.enable(!mod.enabled())) {
+ return false;
+ }
+ auto newId = mod.mmc_id();
+ if(modsIndex.contains(newId)) {
+ // NOTE: this could handle a corner case, where we are overwriting a file, because the same 'mod' exists both enabled and disabled
+ // But is it necessary?
+ }
+ modsIndex.remove(oldId);
+ modsIndex[newId] = row;
+ emit dataChanged(index(row, 0), index(row, columnCount(QModelIndex()) - 1));
+ return true;
+}
+
+QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case ActiveColumn:
+ return QString();
+ case NameColumn:
+ return tr("Name");
+ case VersionColumn:
+ return tr("Version");
+ case DateColumn:
+ return tr("Last changed");
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case ActiveColumn:
+ return tr("Is the mod enabled?");
+ case NameColumn:
+ return tr("The name of the mod.");
+ case VersionColumn:
+ return tr("The version of the mod.");
+ case DateColumn:
+ return tr("The date and time this mod was last changed (or added).");
+ default:
+ return QVariant();
+ }
+ default:
+ return QVariant();
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags ModFolderModel::flags(const QModelIndex &index) const
+{
+ Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
+ auto flags = defaultFlags;
+ if(interaction_disabled) {
+ flags &= ~Qt::ItemIsDropEnabled;
+ }
+ else
+ {
+ flags |= Qt::ItemIsDropEnabled;
+ if(index.isValid()) {
+ flags |= Qt::ItemIsUserCheckable;
+ }
+ }
+ return flags;
+}
+
+Qt::DropActions ModFolderModel::supportedDropActions() const
+{
+ // copy from outside, move from within and other mod lists
+ return Qt::CopyAction | Qt::MoveAction;
+}
+
+QStringList ModFolderModel::mimeTypes() const
+{
+ QStringList types;
+ types << "text/uri-list";
+ return types;
+}
+
+bool ModFolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int, int, const QModelIndex&)
+{
+ if (action == Qt::IgnoreAction)
+ {
+ return true;
+ }
+
+ // check if the action is supported
+ if (!data || !(action & supportedDropActions()))
+ {
+ return false;
+ }
+
+ // files dropped from outside?
+ if (data->hasUrls())
+ {
+ auto urls = data->urls();
+ for (auto url : urls)
+ {
+ // only local files may be dropped...
+ if (!url.isLocalFile())
+ {
+ continue;
+ }
+ // TODO: implement not only copy, but also move
+ // FIXME: handle errors here
+ installMod(url.toLocalFile());
+ }
+ return true;
+ }
+ return false;
+}
diff --git a/api/logic/minecraft/mod/ModFolderModel.h b/api/logic/minecraft/mod/ModFolderModel.h
new file mode 100644
index 00000000..8394e405
--- /dev/null
+++ b/api/logic/minecraft/mod/ModFolderModel.h
@@ -0,0 +1,145 @@
+/* Copyright 2013-2019 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <QList>
+#include <QMap>
+#include <QSet>
+#include <QString>
+#include <QDir>
+#include <QAbstractListModel>
+
+#include "Mod.h"
+
+#include "multimc_logic_export.h"
+#include "ModFolderLoadTask.h"
+#include "LocalModParseTask.h"
+
+class LegacyInstance;
+class BaseInstance;
+class QFileSystemWatcher;
+
+/**
+ * A legacy mod list.
+ * Backed by a folder.
+ */
+class MULTIMC_LOGIC_EXPORT ModFolderModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ enum Columns
+ {
+ ActiveColumn = 0,
+ NameColumn,
+ VersionColumn,
+ DateColumn,
+ NUM_COLUMNS
+ };
+ enum ModStatusAction {
+ Disable,
+ Enable,
+ Toggle
+ };
+ ModFolderModel(const QString &dir);
+
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+ Qt::DropActions supportedDropActions() const override;
+
+ /// flags, mostly to support drag&drop
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
+ QStringList mimeTypes() const override;
+ bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override;
+
+ virtual int rowCount(const QModelIndex &) const override
+ {
+ return size();
+ }
+
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+ virtual int columnCount(const QModelIndex &parent) const override;
+
+ size_t size() const
+ {
+ return mods.size();
+ }
+ ;
+ bool empty() const
+ {
+ return size() == 0;
+ }
+ Mod &operator[](size_t index)
+ {
+ return mods[index];
+ }
+
+ /// Reloads the mod list and returns true if the list changed.
+ bool update();
+
+ /**
+ * Adds the given mod to the list at the given index - if the list supports custom ordering
+ */
+ bool installMod(const QString& filename);
+
+ /// Deletes all the selected mods
+ bool deleteMods(const QModelIndexList &indexes);
+
+ /// Enable or disable listed mods
+ bool setModStatus(const QModelIndexList &indexes, ModStatusAction action);
+
+ void startWatching();
+ void stopWatching();
+
+ bool isValid();
+
+ QDir dir()
+ {
+ return m_dir;
+ }
+
+ const QList<Mod> & allMods()
+ {
+ return mods;
+ }
+
+public slots:
+ void disableInteraction(bool disabled);
+
+private
+slots:
+ void directoryChanged(QString path);
+ void finishUpdate();
+ void finishModParse(int token);
+
+signals:
+ void updateFinished();
+
+private:
+ void resolveMod(Mod& m);
+ bool setModStatus(int index, ModStatusAction action);
+
+protected:
+ QFileSystemWatcher *m_watcher;
+ bool is_watching = false;
+ ModFolderLoadTask::ResultPtr m_update;
+ bool scheduled_update = false;
+ bool interaction_disabled = false;
+ QDir m_dir;
+ QMap<QString, int> modsIndex;
+ QMap<int, LocalModParseTask::ResultPtr> activeTickets;
+ int nextResolutionTicket = 0;
+ QList<Mod> mods;
+};
diff --git a/api/logic/minecraft/mod/ModFolderModel_test.cpp b/api/logic/minecraft/mod/ModFolderModel_test.cpp
new file mode 100644
index 00000000..76f16ed5
--- /dev/null
+++ b/api/logic/minecraft/mod/ModFolderModel_test.cpp
@@ -0,0 +1,53 @@
+
+#include <QTest>
+#include <QTemporaryDir>
+#include "TestUtil.h"
+
+#include "FileSystem.h"
+#include "minecraft/mod/ModFolderModel.h"
+
+class ModFolderModelTest : public QObject
+{
+ Q_OBJECT
+
+private
+slots:
+ // test for GH-1178 - install a folder with files to a mod list
+ void test_1178()
+ {
+ // source
+ QString source = QFINDTESTDATA("data/test_folder");
+
+ // sanity check
+ QVERIFY(!source.endsWith('/'));
+
+ auto verify = [](QString path)
+ {
+ QDir target_dir(FS::PathCombine(path, "test_folder"));
+ QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
+ QVERIFY(target_dir.entryList().contains("assets"));
+ };
+
+ // 1. test with no trailing /
+ {
+ QString folder = source;
+ QTemporaryDir tempDir;
+ ModFolderModel m(tempDir.path());
+ m.installMod(folder);
+ verify(tempDir.path());
+ }
+
+ // 2. test with trailing /
+ {
+ QString folder = source + '/';
+ QTemporaryDir tempDir;
+ ModFolderModel m(tempDir.path());
+ m.installMod(folder);
+ verify(tempDir.path());
+ }
+ }
+};
+
+QTEST_GUILESS_MAIN(ModFolderModelTest)
+
+#include "ModFolderModel_test.moc"
diff --git a/api/logic/minecraft/testdata/lib-native-arch.json b/api/logic/minecraft/testdata/lib-native-arch.json
index a73aac54..501826ae 100644
--- a/api/logic/minecraft/testdata/lib-native-arch.json
+++ b/api/logic/minecraft/testdata/lib-native-arch.json
@@ -1,46 +1,46 @@
{
- "downloads": {
- "classifiers": {
- "natives-osx": {
- "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar",
- "sha1": "62503ee712766cf77f97252e5902786fd834b8c5",
- "size": 418331,
- "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar"
- },
- "natives-windows-32": {
- "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar",
- "sha1": "7c6affe439099806a4f552da14c42f9d643d8b23",
- "size": 386792,
- "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"
- },
- "natives-windows-64": {
- "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar",
- "sha1": "39d0c3d363735b4785598e0e7fbf8297c706a9f9",
- "size": 463390,
- "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"
- }
- }
- },
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "tv.twitch:twitch-platform:5.16",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows-${arch}"
- },
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "linux"
- }
- }
- ]
+ "downloads": {
+ "classifiers": {
+ "natives-osx": {
+ "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar",
+ "sha1": "62503ee712766cf77f97252e5902786fd834b8c5",
+ "size": 418331,
+ "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar"
+ },
+ "natives-windows-32": {
+ "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar",
+ "sha1": "7c6affe439099806a4f552da14c42f9d643d8b23",
+ "size": 386792,
+ "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"
+ },
+ "natives-windows-64": {
+ "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar",
+ "sha1": "39d0c3d363735b4785598e0e7fbf8297c706a9f9",
+ "size": 463390,
+ "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"
+ }
+ }
+ },
+ "extract": {
+ "exclude": [
+ "META-INF/"
+ ]
+ },
+ "name": "tv.twitch:twitch-platform:5.16",
+ "natives": {
+ "linux": "natives-linux",
+ "osx": "natives-osx",
+ "windows": "natives-windows-${arch}"
+ },
+ "rules": [
+ {
+ "action": "allow"
+ },
+ {
+ "action": "disallow",
+ "os": {
+ "name": "linux"
+ }
+ }
+ ]
}
diff --git a/api/logic/minecraft/testdata/lib-native.json b/api/logic/minecraft/testdata/lib-native.json
index 0a95b2b9..5b9f3b55 100644
--- a/api/logic/minecraft/testdata/lib-native.json
+++ b/api/logic/minecraft/testdata/lib-native.json
@@ -1,52 +1,52 @@
{
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar",
- "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33",
- "size": 22,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar"
- },
- "classifiers": {
- "natives-linux": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar",
- "sha1": "931074f46c795d2f7b30ed6395df5715cfd7675b",
- "size": 578680,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar"
- },
- "natives-osx": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar",
- "sha1": "bcab850f8f487c3f4c4dbabde778bb82bd1a40ed",
- "size": 426822,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"
- },
- "natives-windows": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar",
- "sha1": "b84d5102b9dbfabfeb5e43c7e2828d98a7fc80e0",
- "size": 613748,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar"
- }
- }
- },
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- },
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
+ "downloads": {
+ "artifact": {
+ "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar",
+ "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33",
+ "size": 22,
+ "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar"
+ },
+ "classifiers": {
+ "natives-linux": {
+ "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar",
+ "sha1": "931074f46c795d2f7b30ed6395df5715cfd7675b",
+ "size": 578680,
+ "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar"
+ },
+ "natives-osx": {
+ "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar",
+ "sha1": "bcab850f8f487c3f4c4dbabde778bb82bd1a40ed",
+ "size": 426822,
+ "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"
+ },
+ "natives-windows": {
+ "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar",
+ "sha1": "b84d5102b9dbfabfeb5e43c7e2828d98a7fc80e0",
+ "size": 613748,
+ "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar"
+ }
+ }
+ },
+ "extract": {
+ "exclude": [
+ "META-INF/"
+ ]
+ },
+ "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209",
+ "natives": {
+ "linux": "natives-linux",
+ "osx": "natives-osx",
+ "windows": "natives-windows"
+ },
+ "rules": [
+ {
+ "action": "allow"
+ },
+ {
+ "action": "disallow",
+ "os": {
+ "name": "osx"
+ }
+ }
+ ]
}
diff --git a/api/logic/minecraft/testdata/lib-simple.json b/api/logic/minecraft/testdata/lib-simple.json
index 3ef0f490..90bbff07 100644
--- a/api/logic/minecraft/testdata/lib-simple.json
+++ b/api/logic/minecraft/testdata/lib-simple.json
@@ -1,11 +1,11 @@
{
- "downloads": {
- "artifact": {
- "path": "com/paulscode/codecwav/20101023/codecwav-20101023.jar",
- "sha1": "12f031cfe88fef5c1dd36c563c0a3a69bd7261da",
- "size": 5618,
- "url": "https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar"
- }
- },
- "name": "com.paulscode:codecwav:20101023"
+ "downloads": {
+ "artifact": {
+ "path": "com/paulscode/codecwav/20101023/codecwav-20101023.jar",
+ "sha1": "12f031cfe88fef5c1dd36c563c0a3a69bd7261da",
+ "size": 5618,
+ "url": "https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar"
+ }
+ },
+ "name": "com.paulscode:codecwav:20101023"
}
diff --git a/api/logic/minecraft/update/AssetUpdateTask.cpp b/api/logic/minecraft/update/AssetUpdateTask.cpp
index 2ad2b5b2..051b1279 100644
--- a/api/logic/minecraft/update/AssetUpdateTask.cpp
+++ b/api/logic/minecraft/update/AssetUpdateTask.cpp
@@ -7,96 +7,101 @@
AssetUpdateTask::AssetUpdateTask(MinecraftInstance * inst)
{
- m_inst = inst;
+ m_inst = inst;
}
+
+AssetUpdateTask::~AssetUpdateTask()
+{
+}
+
void AssetUpdateTask::executeTask()
{
- setStatus(tr("Updating assets index..."));
- auto components = m_inst->getComponentList();
- auto profile = components->getProfile();
- auto assets = profile->getMinecraftAssets();
- QUrl indexUrl = assets->url;
- QString localPath = assets->id + ".json";
- auto job = new NetJob(tr("Asset index for %1").arg(m_inst->name()));
+ setStatus(tr("Updating assets index..."));
+ auto components = m_inst->getComponentList();
+ auto profile = components->getProfile();
+ auto assets = profile->getMinecraftAssets();
+ QUrl indexUrl = assets->url;
+ QString localPath = assets->id + ".json";
+ auto job = new NetJob(tr("Asset index for %1").arg(m_inst->name()));
- auto metacache = ENV.metacache();
- auto entry = metacache->resolveEntry("asset_indexes", localPath);
- entry->setStale(true);
- auto hexSha1 = assets->sha1.toLatin1();
- qDebug() << "Asset index SHA1:" << hexSha1;
- auto dl = Net::Download::makeCached(indexUrl, entry);
- auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1());
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
- job->addNetAction(dl);
+ auto metacache = ENV.metacache();
+ auto entry = metacache->resolveEntry("asset_indexes", localPath);
+ entry->setStale(true);
+ auto hexSha1 = assets->sha1.toLatin1();
+ qDebug() << "Asset index SHA1:" << hexSha1;
+ auto dl = Net::Download::makeCached(indexUrl, entry);
+ auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1());
+ dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
+ job->addNetAction(dl);
- downloadJob.reset(job);
+ downloadJob.reset(job);
- connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::assetIndexFinished);
- connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed);
- connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
+ connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::assetIndexFinished);
+ connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed);
+ connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
- qDebug() << m_inst->name() << ": Starting asset index download";
- downloadJob->start();
+ qDebug() << m_inst->name() << ": Starting asset index download";
+ downloadJob->start();
}
bool AssetUpdateTask::canAbort() const
{
- return true;
+ return true;
}
void AssetUpdateTask::assetIndexFinished()
{
- AssetsIndex index;
- qDebug() << m_inst->name() << ": Finished asset index download";
+ AssetsIndex index;
+ qDebug() << m_inst->name() << ": Finished asset index download";
- auto components = m_inst->getComponentList();
- auto profile = components->getProfile();
- auto assets = profile->getMinecraftAssets();
+ auto components = m_inst->getComponentList();
+ auto profile = components->getProfile();
+ auto assets = profile->getMinecraftAssets();
- QString asset_fname = "assets/indexes/" + assets->id + ".json";
- // FIXME: this looks like a job for a generic validator based on json schema?
- if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, &index))
- {
- auto metacache = ENV.metacache();
- auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json");
- metacache->evictEntry(entry);
- emitFailed(tr("Failed to read the assets index!"));
- }
+ QString asset_fname = "assets/indexes/" + assets->id + ".json";
+ // FIXME: this looks like a job for a generic validator based on json schema?
+ if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, index))
+ {
+ auto metacache = ENV.metacache();
+ auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json");
+ metacache->evictEntry(entry);
+ emitFailed(tr("Failed to read the assets index!"));
+ }
- auto job = index.getDownloadJob();
- if(job)
- {
- setStatus(tr("Getting the assets files from Mojang..."));
- downloadJob = job;
- connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded);
- connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed);
- connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
- downloadJob->start();
- return;
- }
- emitSucceeded();
+ auto job = index.getDownloadJob();
+ if(job)
+ {
+ setStatus(tr("Getting the assets files from Mojang..."));
+ downloadJob = job;
+ connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded);
+ connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed);
+ connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
+ downloadJob->start();
+ return;
+ }
+ emitSucceeded();
}
void AssetUpdateTask::assetIndexFailed(QString reason)
{
- qDebug() << m_inst->name() << ": Failed asset index download";
- emitFailed(tr("Failed to download the assets index:\n%1").arg(reason));
+ qDebug() << m_inst->name() << ": Failed asset index download";
+ emitFailed(tr("Failed to download the assets index:\n%1").arg(reason));
}
void AssetUpdateTask::assetsFailed(QString reason)
{
- emitFailed(tr("Failed to download assets:\n%1").arg(reason));
+ emitFailed(tr("Failed to download assets:\n%1").arg(reason));
}
bool AssetUpdateTask::abort()
{
- if(downloadJob)
- {
- return downloadJob->abort();
- }
- else
- {
- qWarning() << "Prematurely aborted FMLLibrariesTask";
- }
- return true;
+ if(downloadJob)
+ {
+ return downloadJob->abort();
+ }
+ else
+ {
+ qWarning() << "Prematurely aborted AssetUpdateTask";
+ }
+ return true;
}
diff --git a/api/logic/minecraft/update/AssetUpdateTask.h b/api/logic/minecraft/update/AssetUpdateTask.h
index c666faa6..fdfa8f1c 100644
--- a/api/logic/minecraft/update/AssetUpdateTask.h
+++ b/api/logic/minecraft/update/AssetUpdateTask.h
@@ -5,22 +5,24 @@ class MinecraftInstance;
class AssetUpdateTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- AssetUpdateTask(MinecraftInstance * inst);
- void executeTask() override;
+ AssetUpdateTask(MinecraftInstance * inst);
+ virtual ~AssetUpdateTask();
- bool canAbort() const override;
+ void executeTask() override;
+
+ bool canAbort() const override;
private slots:
- void assetIndexFinished();
- void assetIndexFailed(QString reason);
- void assetsFailed(QString reason);
+ void assetIndexFinished();
+ void assetIndexFailed(QString reason);
+ void assetsFailed(QString reason);
public slots:
- bool abort() override;
+ bool abort() override;
private:
- MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
+ MinecraftInstance *m_inst;
+ NetJobPtr downloadJob;
};
diff --git a/api/logic/minecraft/update/FMLLibrariesTask.cpp b/api/logic/minecraft/update/FMLLibrariesTask.cpp
index 1bd339e4..52a8375b 100644
--- a/api/logic/minecraft/update/FMLLibrariesTask.cpp
+++ b/api/logic/minecraft/update/FMLLibrariesTask.cpp
@@ -7,125 +7,124 @@
FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance * inst)
{
- m_inst = inst;
+ m_inst = inst;
}
void FMLLibrariesTask::executeTask()
{
- // Get the mod list
- MinecraftInstance *inst = (MinecraftInstance *)m_inst;
- auto components = inst->getComponentList();
- auto profile = components->getProfile();
+ // Get the mod list
+ MinecraftInstance *inst = (MinecraftInstance *)m_inst;
+ auto components = inst->getComponentList();
+ auto profile = components->getProfile();
- if (!profile->hasTrait("legacyFML"))
- {
- emitSucceeded();
- return;
- }
+ if (!profile->hasTrait("legacyFML"))
+ {
+ emitSucceeded();
+ return;
+ }
- QString version = components->getComponentVersion("net.minecraft");
- auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
- if (!fmlLibsMapping.contains(version))
- {
- emitSucceeded();
- return;
- }
+ QString version = components->getComponentVersion("net.minecraft");
+ auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
+ if (!fmlLibsMapping.contains(version))
+ {
+ emitSucceeded();
+ return;
+ }
- auto &libList = fmlLibsMapping[version];
+ auto &libList = fmlLibsMapping[version];
- // determine if we need some libs for FML or forge
- setStatus(tr("Checking for FML libraries..."));
- if(!components->getComponent("net.minecraftforge"))
- {
- emitSucceeded();
- return;
- }
+ // determine if we need some libs for FML or forge
+ setStatus(tr("Checking for FML libraries..."));
+ if(!components->getComponent("net.minecraftforge"))
+ {
+ emitSucceeded();
+ return;
+ }
- // now check the lib folder inside the instance for files.
- for (auto &lib : libList)
- {
- QFileInfo libInfo(FS::PathCombine(inst->libDir(), lib.filename));
- if (libInfo.exists())
- continue;
- fmlLibsToProcess.append(lib);
- }
+ // now check the lib folder inside the instance for files.
+ for (auto &lib : libList)
+ {
+ QFileInfo libInfo(FS::PathCombine(inst->libDir(), lib.filename));
+ if (libInfo.exists())
+ continue;
+ fmlLibsToProcess.append(lib);
+ }
- // if everything is in place, there's nothing to do here...
- if (fmlLibsToProcess.isEmpty())
- {
- emitSucceeded();
- return;
- }
+ // if everything is in place, there's nothing to do here...
+ if (fmlLibsToProcess.isEmpty())
+ {
+ emitSucceeded();
+ return;
+ }
- // download missing libs to our place
- setStatus(tr("Dowloading FML libraries..."));
- auto dljob = new NetJob("FML libraries");
- auto metacache = ENV.metacache();
- for (auto &lib : fmlLibsToProcess)
- {
- auto entry = metacache->resolveEntry("fmllibs", lib.filename);
- QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename
- : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename;
- dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry));
- }
+ // download missing libs to our place
+ setStatus(tr("Dowloading FML libraries..."));
+ auto dljob = new NetJob("FML libraries");
+ auto metacache = ENV.metacache();
+ for (auto &lib : fmlLibsToProcess)
+ {
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ QString urlString = (lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL : URLConstants::FMLLIBS_FORGE_BASE_URL) + lib.filename;
+ dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry));
+ }
- connect(dljob, &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished);
- connect(dljob, &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed);
- connect(dljob, &NetJob::progress, this, &FMLLibrariesTask::progress);
- downloadJob.reset(dljob);
- downloadJob->start();
+ connect(dljob, &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished);
+ connect(dljob, &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed);
+ connect(dljob, &NetJob::progress, this, &FMLLibrariesTask::progress);
+ downloadJob.reset(dljob);
+ downloadJob->start();
}
bool FMLLibrariesTask::canAbort() const
{
- return true;
+ return true;
}
void FMLLibrariesTask::fmllibsFinished()
{
- downloadJob.reset();
- if (!fmlLibsToProcess.isEmpty())
- {
- setStatus(tr("Copying FML libraries into the instance..."));
- MinecraftInstance *inst = (MinecraftInstance *)m_inst;
- auto metacache = ENV.metacache();
- int index = 0;
- for (auto &lib : fmlLibsToProcess)
- {
- progress(index, fmlLibsToProcess.size());
- auto entry = metacache->resolveEntry("fmllibs", lib.filename);
- auto path = FS::PathCombine(inst->libDir(), lib.filename);
- if (!FS::ensureFilePathExists(path))
- {
- emitFailed(tr("Failed creating FML library folder inside the instance."));
- return;
- }
- if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename)))
- {
- emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename));
- return;
- }
- index++;
- }
- progress(index, fmlLibsToProcess.size());
- }
- emitSucceeded();
+ downloadJob.reset();
+ if (!fmlLibsToProcess.isEmpty())
+ {
+ setStatus(tr("Copying FML libraries into the instance..."));
+ MinecraftInstance *inst = (MinecraftInstance *)m_inst;
+ auto metacache = ENV.metacache();
+ int index = 0;
+ for (auto &lib : fmlLibsToProcess)
+ {
+ progress(index, fmlLibsToProcess.size());
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ auto path = FS::PathCombine(inst->libDir(), lib.filename);
+ if (!FS::ensureFilePathExists(path))
+ {
+ emitFailed(tr("Failed creating FML library folder inside the instance."));
+ return;
+ }
+ if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename)))
+ {
+ emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename));
+ return;
+ }
+ index++;
+ }
+ progress(index, fmlLibsToProcess.size());
+ }
+ emitSucceeded();
}
void FMLLibrariesTask::fmllibsFailed(QString reason)
{
- QStringList failed = downloadJob->getFailedFiles();
- QString failed_all = failed.join("\n");
- emitFailed(tr("Failed to download the following files:\n%1\n\nReason:%2\nPlease try again.").arg(failed_all, reason));
+ QStringList failed = downloadJob->getFailedFiles();
+ QString failed_all = failed.join("\n");
+ emitFailed(tr("Failed to download the following files:\n%1\n\nReason:%2\nPlease try again.").arg(failed_all, reason));
}
bool FMLLibrariesTask::abort()
{
- if(downloadJob)
- {
- return downloadJob->abort();
- }
- else
- {
- qWarning() << "Prematurely aborted FMLLibrariesTask";
- }
- return true;
+ if(downloadJob)
+ {
+ return downloadJob->abort();
+ }
+ else
+ {
+ qWarning() << "Prematurely aborted FMLLibrariesTask";
+ }
+ return true;
}
diff --git a/api/logic/minecraft/update/FMLLibrariesTask.h b/api/logic/minecraft/update/FMLLibrariesTask.h
index 85f45468..a1e70ed4 100644
--- a/api/logic/minecraft/update/FMLLibrariesTask.h
+++ b/api/logic/minecraft/update/FMLLibrariesTask.h
@@ -7,24 +7,25 @@ class MinecraftInstance;
class FMLLibrariesTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- FMLLibrariesTask(MinecraftInstance * inst);
+ FMLLibrariesTask(MinecraftInstance * inst);
+ virtual ~FMLLibrariesTask() {};
- void executeTask() override;
+ void executeTask() override;
- bool canAbort() const override;
+ bool canAbort() const override;
private slots:
- void fmllibsFinished();
- void fmllibsFailed(QString reason);
+ void fmllibsFinished();
+ void fmllibsFailed(QString reason);
public slots:
- bool abort() override;
+ bool abort() override;
private:
- MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
- QList<FMLlib> fmlLibsToProcess;
+ MinecraftInstance *m_inst;
+ NetJobPtr downloadJob;
+ QList<FMLlib> fmlLibsToProcess;
};
diff --git a/api/logic/minecraft/update/FoldersTask.cpp b/api/logic/minecraft/update/FoldersTask.cpp
index 34e2292a..e2b1bb48 100644
--- a/api/logic/minecraft/update/FoldersTask.cpp
+++ b/api/logic/minecraft/update/FoldersTask.cpp
@@ -3,19 +3,19 @@
#include <QDir>
FoldersTask::FoldersTask(MinecraftInstance * inst)
- :Task()
+ :Task()
{
- m_inst = inst;
+ m_inst = inst;
}
void FoldersTask::executeTask()
{
- // Make directories
- QDir mcDir(m_inst->minecraftRoot());
- if (!mcDir.exists() && !mcDir.mkpath("."))
- {
- emitFailed(tr("Failed to create folder for minecraft binaries."));
- return;
- }
- emitSucceeded();
+ // Make directories
+ QDir mcDir(m_inst->gameRoot());
+ if (!mcDir.exists() && !mcDir.mkpath("."))
+ {
+ emitFailed(tr("Failed to create folder for minecraft binaries."));
+ return;
+ }
+ emitSucceeded();
}
diff --git a/api/logic/minecraft/update/FoldersTask.h b/api/logic/minecraft/update/FoldersTask.h
index 6e669b1e..f6ed5e6d 100644
--- a/api/logic/minecraft/update/FoldersTask.h
+++ b/api/logic/minecraft/update/FoldersTask.h
@@ -5,11 +5,13 @@
class MinecraftInstance;
class FoldersTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- FoldersTask(MinecraftInstance * inst);
- void executeTask() override;
+ FoldersTask(MinecraftInstance * inst);
+ virtual ~FoldersTask() {};
+
+ void executeTask() override;
private:
- MinecraftInstance *m_inst;
+ MinecraftInstance *m_inst;
};
diff --git a/api/logic/minecraft/update/LibrariesTask.cpp b/api/logic/minecraft/update/LibrariesTask.cpp
index 0bec61c1..912f492b 100644
--- a/api/logic/minecraft/update/LibrariesTask.cpp
+++ b/api/logic/minecraft/update/LibrariesTask.cpp
@@ -5,86 +5,85 @@
LibrariesTask::LibrariesTask(MinecraftInstance * inst)
{
- m_inst = inst;
+ m_inst = inst;
}
void LibrariesTask::executeTask()
{
- setStatus(tr("Getting the library files from Mojang..."));
- qDebug() << m_inst->name() << ": downloading libraries";
- MinecraftInstance *inst = (MinecraftInstance *)m_inst;
+ setStatus(tr("Getting the library files from Mojang..."));
+ qDebug() << m_inst->name() << ": downloading libraries";
+ MinecraftInstance *inst = (MinecraftInstance *)m_inst;
- // Build a list of URLs that will need to be downloaded.
- auto components = inst->getComponentList();
- auto profile = components->getProfile();
+ // Build a list of URLs that will need to be downloaded.
+ auto components = inst->getComponentList();
+ auto profile = components->getProfile();
- auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name()));
- downloadJob.reset(job);
+ auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name()));
+ downloadJob.reset(job);
- auto metacache = ENV.metacache();
- QList<LibraryPtr> brokenLocalLibs;
- QStringList failedFiles;
- auto createJob = [&](const LibraryPtr & lib)
- {
- if(!lib)
- {
- emitFailed(tr("Null jar is specified in the metadata, aborting."));
- return;
- }
- auto dls = lib->getDownloads(currentSystem, metacache.get(), failedFiles, inst->getLocalLibraryPath());
- for(auto dl : dls)
- {
- downloadJob->addNetAction(dl);
- }
- };
- auto createJobs = [&](const QList<LibraryPtr> & libs)
- {
- for (auto lib : libs)
- {
- createJob(lib);
- }
- };
- createJobs(profile->getLibraries());
- createJobs(profile->getNativeLibraries());
- createJobs(profile->getJarMods());
- createJob(profile->getMainJar());
+ auto metacache = ENV.metacache();
- // FIXME: this is never filled!!!!
- if (!brokenLocalLibs.empty())
- {
- downloadJob.reset();
- QString failed_all = failedFiles.join("\n");
- emitFailed(tr("Some libraries marked as 'local' are missing their jar "
- "files:\n%1\n\nYou'll have to correct this problem manually. If this is "
- "an externally tracked instance, make sure to run it at least once "
- "outside of MultiMC.").arg(failed_all));
- return;
- }
- connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded);
- connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed);
- connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress);
- downloadJob->start();
+ auto processArtifactPool = [&](const QList<LibraryPtr> & pool, QStringList & errors, const QString & localPath)
+ {
+ for (auto lib : pool)
+ {
+ if(!lib)
+ {
+ emitFailed(tr("Null jar is specified in the metadata, aborting."));
+ return false;
+ }
+ auto dls = lib->getDownloads(currentSystem, metacache.get(), errors, localPath);
+ for(auto dl : dls)
+ {
+ downloadJob->addNetAction(dl);
+ }
+ }
+ return true;
+ };
+
+ QStringList failedLocalLibraries;
+ QList<LibraryPtr> libArtifactPool;
+ libArtifactPool.append(profile->getLibraries());
+ libArtifactPool.append(profile->getNativeLibraries());
+ libArtifactPool.append(profile->getMainJar());
+ processArtifactPool(libArtifactPool, failedLocalLibraries, inst->getLocalLibraryPath());
+
+ QStringList failedLocalJarMods;
+ processArtifactPool(profile->getJarMods(), failedLocalJarMods, inst->jarModsDir());
+
+ if (!failedLocalJarMods.empty() || !failedLocalLibraries.empty())
+ {
+ downloadJob.reset();
+ QString failed_all = (failedLocalLibraries + failedLocalJarMods).join("\n");
+ emitFailed(tr("Some artifacts marked as 'local' are missing their files:\n%1\n\nYou need to either add the files, or removed the packages that require them.\nYou'll have to correct this problem manually.").arg(failed_all));
+ return;
+ }
+
+ connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded);
+ connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed);
+ connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress);
+ downloadJob->start();
}
bool LibrariesTask::canAbort() const
{
- return true;
+ return true;
}
void LibrariesTask::jarlibFailed(QString reason)
{
- emitFailed(tr("Game update failed: it was impossible to fetch the required libraries.\nReason:\n%1").arg(reason));
+ emitFailed(tr("Game update failed: it was impossible to fetch the required libraries.\nReason:\n%1").arg(reason));
}
bool LibrariesTask::abort()
{
- if(downloadJob)
- {
- return downloadJob->abort();
- }
- else
- {
- qWarning() << "Prematurely aborted LibrariesTask";
- }
- return true;
+ if(downloadJob)
+ {
+ return downloadJob->abort();
+ }
+ else
+ {
+ qWarning() << "Prematurely aborted LibrariesTask";
+ }
+ return true;
}
diff --git a/api/logic/minecraft/update/LibrariesTask.h b/api/logic/minecraft/update/LibrariesTask.h
index d06a5037..49f76932 100644
--- a/api/logic/minecraft/update/LibrariesTask.h
+++ b/api/logic/minecraft/update/LibrariesTask.h
@@ -5,21 +5,22 @@ class MinecraftInstance;
class LibrariesTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- LibrariesTask(MinecraftInstance * inst);
+ LibrariesTask(MinecraftInstance * inst);
+ virtual ~LibrariesTask() {};
- void executeTask() override;
+ void executeTask() override;
- bool canAbort() const override;
+ bool canAbort() const override;
private slots:
- void jarlibFailed(QString reason);
+ void jarlibFailed(QString reason);
public slots:
- bool abort() override;
+ bool abort() override;
private:
- MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
+ MinecraftInstance *m_inst;
+ NetJobPtr downloadJob;
};
diff --git a/api/logic/modplatform/flame/FileResolvingTask.cpp b/api/logic/modplatform/flame/FileResolvingTask.cpp
index efc73621..295574f0 100644
--- a/api/logic/modplatform/flame/FileResolvingTask.cpp
+++ b/api/logic/modplatform/flame/FileResolvingTask.cpp
@@ -1,117 +1,63 @@
#include "FileResolvingTask.h"
#include "Json.h"
-const char * metabase = "https://cursemeta.dries007.net";
+namespace {
+ const char * metabase = "https://cursemeta.dries007.net";
+}
Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess)
- : m_toProcess(toProcess)
+ : m_toProcess(toProcess)
{
}
void Flame::FileResolvingTask::executeTask()
{
- setStatus(tr("Resolving mod IDs..."));
- setProgress(0, m_toProcess.files.size());
- m_dljob.reset(new NetJob("Mod id resolver"));
- results.resize(m_toProcess.files.size());
- int index = 0;
- for(auto & file: m_toProcess.files)
- {
- auto projectIdStr = QString::number(file.projectId);
- auto fileIdStr = QString::number(file.fileId);
- QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
- auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results[index]);
- m_dljob->addNetAction(dl);
- index ++;
- }
- connect(m_dljob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::netJobFinished);
- m_dljob->start();
+ setStatus(tr("Resolving mod IDs..."));
+ setProgress(0, m_toProcess.files.size());
+ m_dljob.reset(new NetJob("Mod id resolver"));
+ results.resize(m_toProcess.files.size());
+ int index = 0;
+ for(auto & file: m_toProcess.files)
+ {
+ auto projectIdStr = QString::number(file.projectId);
+ auto fileIdStr = QString::number(file.fileId);
+ QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
+ auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results[index]);
+ m_dljob->addNetAction(dl);
+ index ++;
+ }
+ connect(m_dljob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::netJobFinished);
+ m_dljob->start();
}
void Flame::FileResolvingTask::netJobFinished()
{
- bool failed = false;
- int index = 0;
- for(auto & bytes: results)
- {
- try
- {
- auto doc = Json::requireDocument(bytes);
- auto obj = Json::requireObject(doc);
- auto & out = m_toProcess.files[index];
- // result code signifies true failure.
- if(obj.contains("code"))
- {
- qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a negative result:";
- qCritical() << bytes;
- failed = true;
- continue;
- }
- out.fileName = Json::requireString(obj, "FileNameOnDisk");
- QString rawUrl = Json::requireString(obj, "DownloadURL");
- out.url = QUrl(rawUrl, QUrl::TolerantMode);
- if(!out.url.isValid())
- {
- throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
- }
- // This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
- // It is also optional
- QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
- if(!projObj.isEmpty())
- {
- QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
- if(strType == "singlefile")
- {
- out.type = File::Type::SingleFile;
- }
- else if(strType == "ctoc")
- {
- out.type = File::Type::Ctoc;
- }
- else if(strType == "cmod2")
- {
- out.type = File::Type::Cmod2;
- }
- else if(strType == "mod")
- {
- out.type = File::Type::Mod;
- }
- else if(strType == "folder")
- {
- out.type = File::Type::Folder;
- }
- else if(strType == "modpack")
- {
- out.type = File::Type::Modpack;
- }
- else
- {
- qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of unknown file type:" << strType;
- out.type = File::Type::Unknown;
- failed = true;
- continue;
- }
- out.targetFolder = Json::ensureString(projObj, "Path", "mods");
- }
- out.resolved = true;
- }
- catch(JSONValidationError & e)
- {
- auto & out = m_toProcess.files[index];
- qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
- qCritical() << e.cause();
- qCritical() << "JSON:";
- qCritical() << bytes;
- failed = true;
- }
- index++;
- }
- if(!failed)
- {
- emitSucceeded();
- }
- else
- {
- emitFailed(tr("Some mod ID resolving tasks failed."));
- }
+ bool failed = false;
+ int index = 0;
+ for(auto & bytes: results)
+ {
+ auto & out = m_toProcess.files[index];
+ try
+ {
+ failed &= (!out.parseFromBytes(bytes));
+ }
+ catch (const JSONValidationError &e)
+ {
+
+ qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
+ qCritical() << e.cause();
+ qCritical() << "JSON:";
+ qCritical() << bytes;
+ failed = true;
+ }
+ index++;
+ }
+ if(!failed)
+ {
+ emitSucceeded();
+ }
+ else
+ {
+ emitFailed(tr("Some mod ID resolving tasks failed."));
+ }
}
diff --git a/api/logic/modplatform/flame/FileResolvingTask.h b/api/logic/modplatform/flame/FileResolvingTask.h
index 00658452..5679b907 100644
--- a/api/logic/modplatform/flame/FileResolvingTask.h
+++ b/api/logic/modplatform/flame/FileResolvingTask.h
@@ -10,23 +10,25 @@ namespace Flame
{
class MULTIMC_LOGIC_EXPORT FileResolvingTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit FileResolvingTask(Flame::Manifest &toProcess);
- const Flame::Manifest &getResults() const
- {
- return m_toProcess;
- }
+ explicit FileResolvingTask(Flame::Manifest &toProcess);
+ virtual ~FileResolvingTask() {};
+
+ const Flame::Manifest &getResults() const
+ {
+ return m_toProcess;
+ }
protected:
- virtual void executeTask() override;
+ virtual void executeTask() override;
protected slots:
- void netJobFinished();
+ void netJobFinished();
private: /* data */
- Flame::Manifest m_toProcess;
- QVector<QByteArray> results;
- NetJobPtr m_dljob;
+ Flame::Manifest m_toProcess;
+ QVector<QByteArray> results;
+ NetJobPtr m_dljob;
};
}
diff --git a/api/logic/modplatform/flame/PackManifest.cpp b/api/logic/modplatform/flame/PackManifest.cpp
index 6a9324fe..1db0a161 100644
--- a/api/logic/modplatform/flame/PackManifest.cpp
+++ b/api/logic/modplatform/flame/PackManifest.cpp
@@ -3,64 +3,124 @@
static void loadFileV1(Flame::File & f, QJsonObject & file)
{
- f.projectId = Json::requireInteger(file, "projectID");
- f.fileId = Json::requireInteger(file, "fileID");
- f.required = Json::ensureBoolean(file, QString("required"), true);
+ f.projectId = Json::requireInteger(file, "projectID");
+ f.fileId = Json::requireInteger(file, "fileID");
+ f.required = Json::ensureBoolean(file, QString("required"), true);
}
static void loadModloaderV1(Flame::Modloader & m, QJsonObject & modLoader)
{
- m.id = Json::requireString(modLoader, "id");
- m.primary = Json::ensureBoolean(modLoader, QString("primary"), false);
+ m.id = Json::requireString(modLoader, "id");
+ m.primary = Json::ensureBoolean(modLoader, QString("primary"), false);
}
static void loadMinecraftV1(Flame::Minecraft & m, QJsonObject & minecraft)
{
- m.version = Json::requireString(minecraft, "version");
- // extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack
- // intended use is likely hardcoded in the 'Flame' client, the manifest says nothing
- m.libraries = Json::ensureString(minecraft, QString("libraries"), QString());
- auto arr = Json::ensureArray(minecraft, "modLoaders", QJsonArray());
- for (const auto & item : arr)
- {
- auto obj = Json::requireObject(item);
- Flame::Modloader loader;
- loadModloaderV1(loader, obj);
- m.modLoaders.append(loader);
- }
+ m.version = Json::requireString(minecraft, "version");
+ // extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack
+ // intended use is likely hardcoded in the 'Flame' client, the manifest says nothing
+ m.libraries = Json::ensureString(minecraft, QString("libraries"), QString());
+ auto arr = Json::ensureArray(minecraft, "modLoaders", QJsonArray());
+ for (const auto & item : arr)
+ {
+ auto obj = Json::requireObject(item);
+ Flame::Modloader loader;
+ loadModloaderV1(loader, obj);
+ m.modLoaders.append(loader);
+ }
}
static void loadManifestV1(Flame::Manifest & m, QJsonObject & manifest)
{
- auto mc = Json::requireObject(manifest, "minecraft");
- loadMinecraftV1(m.minecraft, mc);
- m.name = Json::ensureString(manifest, QString("name"), "Unnamed");
- m.version = Json::ensureString(manifest, QString("version"), QString());
- m.author = Json::ensureString(manifest, QString("author"), "Anonymous Coward");
- auto arr = Json::ensureArray(manifest, "files", QJsonArray());
- for (const auto & item : arr)
- {
- auto obj = Json::requireObject(item);
- Flame::File file;
- loadFileV1(file, obj);
- m.files.append(file);
- }
- m.overrides = Json::ensureString(manifest, "overrides", "overrides");
+ auto mc = Json::requireObject(manifest, "minecraft");
+ loadMinecraftV1(m.minecraft, mc);
+ m.name = Json::ensureString(manifest, QString("name"), "Unnamed");
+ m.version = Json::ensureString(manifest, QString("version"), QString());
+ m.author = Json::ensureString(manifest, QString("author"), "Anonymous Coward");
+ auto arr = Json::ensureArray(manifest, "files", QJsonArray());
+ for (const auto & item : arr)
+ {
+ auto obj = Json::requireObject(item);
+ Flame::File file;
+ loadFileV1(file, obj);
+ m.files.append(file);
+ }
+ m.overrides = Json::ensureString(manifest, "overrides", "overrides");
}
void Flame::loadManifest(Flame::Manifest & m, const QString &filepath)
{
- auto doc = Json::requireDocument(filepath);
- auto obj = Json::requireObject(doc);
- m.manifestType = Json::requireString(obj, "manifestType");
- if(m.manifestType != "minecraftModpack")
- {
- throw JSONValidationError("Not a modpack manifest!");
- }
- m.manifestVersion = Json::requireInteger(obj, "manifestVersion");
- if(m.manifestVersion != 1)
- {
- throw JSONValidationError(QString("Unknown manifest version (%1)").arg(m.manifestVersion));
- }
- loadManifestV1(m, obj);
+ auto doc = Json::requireDocument(filepath);
+ auto obj = Json::requireObject(doc);
+ m.manifestType = Json::requireString(obj, "manifestType");
+ if(m.manifestType != "minecraftModpack")
+ {
+ throw JSONValidationError("Not a modpack manifest!");
+ }
+ m.manifestVersion = Json::requireInteger(obj, "manifestVersion");
+ if(m.manifestVersion != 1)
+ {
+ throw JSONValidationError(QString("Unknown manifest version (%1)").arg(m.manifestVersion));
+ }
+ loadManifestV1(m, obj);
+}
+
+bool Flame::File::parseFromBytes(const QByteArray& bytes)
+{
+ auto doc = Json::requireDocument(bytes);
+ auto obj = Json::requireObject(doc);
+ // result code signifies true failure.
+ if(obj.contains("code"))
+ {
+ qCritical() << "Resolving of" << projectId << fileId << "failed because of a negative result:";
+ qCritical() << bytes;
+ return false;
+ }
+ fileName = Json::requireString(obj, "FileNameOnDisk");
+ QString rawUrl = Json::requireString(obj, "DownloadURL");
+ url = QUrl(rawUrl, QUrl::TolerantMode);
+ if(!url.isValid())
+ {
+ throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
+ }
+ // This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
+ // It is also optional
+ QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
+ if(!projObj.isEmpty())
+ {
+ QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
+ if(strType == "singlefile")
+ {
+ type = File::Type::SingleFile;
+ }
+ else if(strType == "ctoc")
+ {
+ type = File::Type::Ctoc;
+ }
+ else if(strType == "cmod2")
+ {
+ type = File::Type::Cmod2;
+ }
+ else if(strType == "mod")
+ {
+ type = File::Type::Mod;
+ }
+ else if(strType == "folder")
+ {
+ type = File::Type::Folder;
+ }
+ else if(strType == "modpack")
+ {
+ type = File::Type::Modpack;
+ }
+ else
+ {
+ qCritical() << "Resolving of" << projectId << fileId << "failed because of unknown file type:" << strType;
+ type = File::Type::Unknown;
+ return false;
+ }
+ targetFolder = Json::ensureString(projObj, "Path", "mods");
+ }
+ resolved = true;
+ return true;
}
diff --git a/api/logic/modplatform/flame/PackManifest.h b/api/logic/modplatform/flame/PackManifest.h
index 1a5254a8..02f39f0e 100644
--- a/api/logic/modplatform/flame/PackManifest.h
+++ b/api/logic/modplatform/flame/PackManifest.h
@@ -8,51 +8,54 @@ namespace Flame
{
struct File
{
- int projectId = 0;
- int fileId = 0;
- // NOTE: the opposite to 'optional'. This is at the time of writing unused.
- bool required = true;
-
- // our
- bool resolved = false;
- QString fileName;
- QUrl url;
- QString targetFolder = QLatin1Literal("mods");
- enum class Type
- {
- Unknown,
- Folder,
- Ctoc,
- SingleFile,
- Cmod2,
- Modpack,
- Mod
- } type = Type::Mod;
+ // NOTE: throws JSONValidationError
+ bool parseFromBytes(const QByteArray &bytes);
+
+ int projectId = 0;
+ int fileId = 0;
+ // NOTE: the opposite to 'optional'. This is at the time of writing unused.
+ bool required = true;
+
+ // our
+ bool resolved = false;
+ QString fileName;
+ QUrl url;
+ QString targetFolder = QLatin1Literal("mods");
+ enum class Type
+ {
+ Unknown,
+ Folder,
+ Ctoc,
+ SingleFile,
+ Cmod2,
+ Modpack,
+ Mod
+ } type = Type::Mod;
};
struct Modloader
{
- QString id;
- bool primary = false;
+ QString id;
+ bool primary = false;
};
struct Minecraft
{
- QString version;
- QString libraries;
- QVector<Flame::Modloader> modLoaders;
+ QString version;
+ QString libraries;
+ QVector<Flame::Modloader> modLoaders;
};
struct Manifest
{
- QString manifestType;
- int manifestVersion = 0;
- Flame::Minecraft minecraft;
- QString name;
- QString version;
- QString author;
- QVector<Flame::File> files;
- QString overrides;
+ QString manifestType;
+ int manifestVersion = 0;
+ Flame::Minecraft minecraft;
+ QString name;
+ QString version;
+ QString author;
+ QVector<Flame::File> files;
+ QString overrides;
};
void loadManifest(Flame::Manifest & m, const QString &filepath);
diff --git a/api/logic/modplatform/flame/UrlResolvingTask.cpp b/api/logic/modplatform/flame/UrlResolvingTask.cpp
new file mode 100644
index 00000000..efea350c
--- /dev/null
+++ b/api/logic/modplatform/flame/UrlResolvingTask.cpp
@@ -0,0 +1,175 @@
+#include "UrlResolvingTask.h"
+#include <QtXml>
+#include <Json.h>
+
+
+namespace {
+ const char * metabase = "https://cursemeta.dries007.net";
+}
+
+Flame::UrlResolvingTask::UrlResolvingTask(const QString& toProcess)
+ : m_url(toProcess)
+{
+}
+
+void Flame::UrlResolvingTask::executeTask()
+{
+ resolveUrl();
+}
+
+void Flame::UrlResolvingTask::resolveUrl()
+{
+ setStatus(tr("Resolving URL..."));
+ setProgress(0, 1);
+ QUrl actualUrl(m_url);
+ if(actualUrl.host() != "www.curseforge.com") {
+ emitFailed(tr("Not a Twitch URL."));
+ return;
+ }
+ m_dljob.reset(new NetJob("URL resolver"));
+
+ bool weAreDigging = false;
+ needle = QString();
+
+ if(m_url.startsWith("https://")) {
+ if(m_url.endsWith("?client=y")) {
+ // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download?client=y
+ // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088?client=y
+ m_url.chop(9);
+ // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download
+ // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
+ }
+ if(m_url.endsWith("/download")) {
+ // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download -> need to dig inside html...
+ weAreDigging = true;
+ needle = m_url;
+ needle.replace("https://", "twitch://");
+ needle.replace("/download", "/download-client/");
+ m_url.append("?client=y");
+ } else if (m_url.contains("/download/")) {
+ // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
+ m_url.replace("/download/", "/download-client/");
+ }
+ }
+ else if(m_url.startsWith("twitch://")) {
+ // twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
+ m_url.replace(0, 9, "https://");
+ // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
+ }
+ auto dl = Net::Download::makeByteArray(QUrl(m_url), &results);
+ m_dljob->addNetAction(dl);
+ if(weAreDigging) {
+ connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processHTML);
+ } else {
+ connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCCIP);
+ }
+ m_dljob->start();
+}
+
+void Flame::UrlResolvingTask::processHTML()
+{
+ QString htmlDoc = QString::fromUtf8(results);
+ auto index = htmlDoc.indexOf(needle);
+ if(index < 0) {
+ emitFailed(tr("Couldn't find the needle in the haystack..."));
+ return;
+ }
+ auto indexStart = index;
+ int indexEnd = -1;
+ while((index + 1) < htmlDoc.size() && htmlDoc[index] != '"') {
+ index ++;
+ if(htmlDoc[index] == '"') {
+ indexEnd = index;
+ break;
+ }
+ }
+ if(indexEnd > 0) {
+ QString found = htmlDoc.mid(indexStart, indexEnd - indexStart);
+ qDebug() << "Found needle: " << found;
+ // twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
+ m_url = found;
+ resolveUrl();
+ return;
+ }
+ emitFailed(tr("Couldn't find the end of the needle in the haystack..."));
+ return;
+}
+
+void Flame::UrlResolvingTask::processCCIP()
+{
+ QDomDocument doc;
+ if (!doc.setContent(results)) {
+ qDebug() << results;
+ emitFailed(tr("Resolving failed."));
+ return;
+ }
+ auto packageNode = doc.namedItem("package");
+ if(!packageNode.isElement()) {
+ emitFailed(tr("Resolving failed: missing package root element."));
+ return;
+ }
+ auto projectNode = packageNode.namedItem("project");
+ if(!projectNode.isElement()) {
+ emitFailed(tr("Resolving failed: missing project element."));
+ return;
+ }
+ auto attribs = projectNode.attributes();
+
+ auto projectIdNode = attribs.namedItem("id");
+ if(!projectIdNode.isAttr()) {
+ emitFailed(tr("Resolving failed: missing id attribute."));
+ return;
+ }
+ auto fileIdNode = attribs.namedItem("file");
+ if(!fileIdNode.isAttr()) {
+ emitFailed(tr("Resolving failed: missing file attribute."));
+ return;
+ }
+
+ auto projectId = projectIdNode.nodeValue();
+ auto fileId = fileIdNode.nodeValue();
+ bool success = true;
+ m_result.projectId = projectId.toInt(&success);
+ if(!success) {
+ emitFailed(tr("Failed to resove projectId as a number."));
+ return;
+ }
+ m_result.fileId = fileId.toInt(&success);
+ if(!success) {
+ emitFailed(tr("Failed to resove fileId as a number."));
+ return;
+ }
+ qDebug() << "Resolved" << m_url << "as" << m_result.projectId << "/" << m_result.fileId;
+ resolveIDs();
+}
+
+void Flame::UrlResolvingTask::resolveIDs()
+{
+ setStatus(tr("Resolving mod IDs..."));
+ m_dljob.reset(new NetJob("Mod id resolver"));
+ auto projectIdStr = QString::number(m_result.projectId);
+ auto fileIdStr = QString::number(m_result.fileId);
+ QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
+ auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results);
+ m_dljob->addNetAction(dl);
+ connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCursemeta);
+ m_dljob->start();
+}
+
+void Flame::UrlResolvingTask::processCursemeta()
+{
+ try {
+ if(m_result.parseFromBytes(results)) {
+ emitSucceeded();
+ qDebug() << results;
+ return;
+ }
+ } catch (const JSONValidationError &e) {
+
+ qCritical() << "Resolving of" << m_result.projectId << m_result.fileId << "failed because of a parsing error:";
+ qCritical() << e.cause();
+ qCritical() << "JSON:";
+ qCritical() << results;
+ }
+ emitFailed(tr("Failed to resolve the modpack file."));
+}
diff --git a/api/logic/modplatform/flame/UrlResolvingTask.h b/api/logic/modplatform/flame/UrlResolvingTask.h
new file mode 100644
index 00000000..98b78f67
--- /dev/null
+++ b/api/logic/modplatform/flame/UrlResolvingTask.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "tasks/Task.h"
+#include "net/NetJob.h"
+#include "PackManifest.h"
+
+#include "multimc_logic_export.h"
+
+namespace Flame
+{
+class MULTIMC_LOGIC_EXPORT UrlResolvingTask : public Task
+{
+ Q_OBJECT
+public:
+ explicit UrlResolvingTask(const QString &toProcess);
+ virtual ~UrlResolvingTask() {};
+
+ const Flame::File &getResults() const
+ {
+ return m_result;
+ }
+
+protected:
+ virtual void executeTask() override;
+
+protected slots:
+ void processCCIP();
+ void processHTML();
+ void processCursemeta();
+
+private:
+ void resolveUrl();
+ void resolveIDs();
+
+private: /* data */
+ QString m_url;
+ QString needle;
+ Flame::File m_result;
+ QByteArray results;
+ NetJobPtr m_dljob;
+};
+}
+
diff --git a/api/logic/modplatform/ftb/FtbPackFetchTask.cpp b/api/logic/modplatform/ftb/FtbPackFetchTask.cpp
index 5588a7fd..fe3f3fac 100644
--- a/api/logic/modplatform/ftb/FtbPackFetchTask.cpp
+++ b/api/logic/modplatform/ftb/FtbPackFetchTask.cpp
@@ -1,117 +1,168 @@
#include "FtbPackFetchTask.h"
#include <QDomDocument>
+#include "FtbPrivatePackManager.h"
-FtbPackFetchTask::FtbPackFetchTask()
-{
-}
-
-FtbPackFetchTask::~FtbPackFetchTask()
-{
-}
+#include "net/URLConstants.h"
void FtbPackFetchTask::fetch()
{
- NetJob *netJob = new NetJob("FtbModpackFetch");
+ publicPacks.clear();
+ thirdPartyPacks.clear();
+
+ NetJob *netJob = new NetJob("FtbModpackFetch");
- QUrl publicPacksUrl = QUrl("https://ftb.cursecdn.com/FTB2/static/modpacks.xml");
- qDebug() << "Downloading public version info from" << publicPacksUrl.toString();
- netJob->addNetAction(Net::Download::makeByteArray(publicPacksUrl, &publicModpacksXmlFileData));
+ QUrl publicPacksUrl = QUrl(URLConstants::FTB_CDN_BASE_URL + "static/modpacks.xml");
+ qDebug() << "Downloading public version info from" << publicPacksUrl.toString();
+ netJob->addNetAction(Net::Download::makeByteArray(publicPacksUrl, &publicModpacksXmlFileData));
- QUrl thirdPartyUrl = QUrl("https://ftb.cursecdn.com/FTB2/static/thirdparty.xml");
- qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString();
- netJob->addNetAction(Net::Download::makeByteArray(thirdPartyUrl, &thirdPartyModpacksXmlFileData));
+ QUrl thirdPartyUrl = QUrl(URLConstants::FTB_CDN_BASE_URL + "static/thirdparty.xml");
+ qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString();
+ netJob->addNetAction(Net::Download::makeByteArray(thirdPartyUrl, &thirdPartyModpacksXmlFileData));
- QObject::connect(netJob, &NetJob::succeeded, this, &FtbPackFetchTask::fileDownloadFinished);
- QObject::connect(netJob, &NetJob::failed, this, &FtbPackFetchTask::fileDownloadFailed);
+ QObject::connect(netJob, &NetJob::succeeded, this, &FtbPackFetchTask::fileDownloadFinished);
+ QObject::connect(netJob, &NetJob::failed, this, &FtbPackFetchTask::fileDownloadFailed);
- jobPtr.reset(netJob);
- netJob->start();
+ jobPtr.reset(netJob);
+ netJob->start();
}
-void FtbPackFetchTask::fileDownloadFinished()
+void FtbPackFetchTask::fetchPrivate(const QStringList & toFetch)
{
- jobPtr.reset();
-
- QStringList failedLists;
-
- if(!parseAndAddPacks(publicModpacksXmlFileData, FtbPackType::Public, publicPacks)) {
- failedLists.append(tr("Public Packs"));
- }
-
- if(!parseAndAddPacks(thirdPartyModpacksXmlFileData, FtbPackType::ThirdParty, thirdPartyPacks)) {
- failedLists.append(tr("Third Party Packs"));
- }
+ QString privatePackBaseUrl = URLConstants::FTB_CDN_BASE_URL + "static/%1.xml";
+
+ for (auto &packCode: toFetch)
+ {
+ QByteArray *data = new QByteArray();
+ NetJob *job = new NetJob("Fetching private pack");
+ job->addNetAction(Net::Download::makeByteArray(privatePackBaseUrl.arg(packCode), data));
+
+ QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode]
+ {
+ FtbModpackList packs;
+ parseAndAddPacks(*data, FtbPackType::Private, packs);
+ foreach(FtbModpack currentPack, packs)
+ {
+ currentPack.packCode = packCode;
+ emit privateFileDownloadFinished(currentPack);
+ }
+
+ job->deleteLater();
+
+ data->clear();
+ delete data;
+ });
+
+ QObject::connect(job, &NetJob::failed, this, [this, job, packCode, data](QString reason)
+ {
+ emit privateFileDownloadFailed(reason, packCode);
+ job->deleteLater();
+
+ data->clear();
+ delete data;
+ });
+
+ job->start();
+ }
+}
- if(failedLists.size() > 0) {
- emit failed(QString("Failed to download some pack lists:%1").arg(failedLists.join("\n- ")));
- } else {
- emit finished(publicPacks, thirdPartyPacks);
- }
+void FtbPackFetchTask::fileDownloadFinished()
+{
+ jobPtr.reset();
+
+ QStringList failedLists;
+
+ if(!parseAndAddPacks(publicModpacksXmlFileData, FtbPackType::Public, publicPacks))
+ {
+ failedLists.append(tr("Public Packs"));
+ }
+
+ if(!parseAndAddPacks(thirdPartyModpacksXmlFileData, FtbPackType::ThirdParty, thirdPartyPacks))
+ {
+ failedLists.append(tr("Third Party Packs"));
+ }
+
+ if(failedLists.size() > 0)
+ {
+ emit failed(tr("Failed to download some pack lists: %1").arg(failedLists.join("\n- ")));
+ }
+ else
+ {
+ emit finished(publicPacks, thirdPartyPacks);
+ }
}
bool FtbPackFetchTask::parseAndAddPacks(QByteArray &data, FtbPackType packType, FtbModpackList &list)
{
- QDomDocument doc;
-
- QString errorMsg = "Unknown error.";
- int errorLine = -1;
- int errorCol = -1;
-
- if(!doc.setContent(data, false, &errorMsg, &errorLine, &errorCol)){
- auto fullErrMsg = QString("Failed to fetch modpack data: %s %d:%d!").arg(errorMsg, errorLine, errorCol);
- qWarning() << fullErrMsg;
- data.clear();
- return false;
- }
-
- QDomNodeList nodes = doc.elementsByTagName("modpack");
- for(int i = 0; i < nodes.length(); i++) {
- QDomElement element = nodes.at(i).toElement();
-
- FtbModpack modpack;
- modpack.name = element.attribute("name");
- modpack.currentVersion = element.attribute("version");
- modpack.mcVersion = element.attribute("mcVersion");
- modpack.description = element.attribute("description");
- modpack.mods = element.attribute("mods");
- modpack.logo = element.attribute("logo");
- modpack.oldVersions = element.attribute("oldVersions").split(";");
- modpack.broken = false;
- modpack.bugged = false;
-
- //remove empty if the xml is bugged
- for(QString curr : modpack.oldVersions) {
- if(curr.isNull() || curr.isEmpty()) {
- modpack.oldVersions.removeAll(curr);
- modpack.bugged = true;
- qWarning() << "Removed some empty versions from" << modpack.name;
- }
- }
-
- if(modpack.oldVersions.size() < 1) {
- if(!modpack.currentVersion.isNull() && !modpack.currentVersion.isEmpty()) {
- modpack.oldVersions.append(modpack.currentVersion);
- qWarning() << "Added current version to oldVersions because oldVersions was empty! (" + modpack.name + ")";
- } else {
- modpack.broken = true;
- qWarning() << "Broken pack:" << modpack.name << " => No valid version!";
- }
- }
-
- modpack.author = element.attribute("author");
-
- modpack.dir = element.attribute("dir");
- modpack.file = element.attribute("url");
-
- modpack.type = packType;
-
- list.append(modpack);
- }
-
- return true;
+ QDomDocument doc;
+
+ QString errorMsg = "Unknown error.";
+ int errorLine = -1;
+ int errorCol = -1;
+
+ if(!doc.setContent(data, false, &errorMsg, &errorLine, &errorCol))
+ {
+ auto fullErrMsg = QString("Failed to fetch modpack data: %1 %2:3d!").arg(errorMsg, errorLine, errorCol);
+ qWarning() << fullErrMsg;
+ data.clear();
+ return false;
+ }
+
+ QDomNodeList nodes = doc.elementsByTagName("modpack");
+ for(int i = 0; i < nodes.length(); i++)
+ {
+ QDomElement element = nodes.at(i).toElement();
+
+ FtbModpack modpack;
+ modpack.name = element.attribute("name");
+ modpack.currentVersion = element.attribute("version");
+ modpack.mcVersion = element.attribute("mcVersion");
+ modpack.description = element.attribute("description");
+ modpack.mods = element.attribute("mods");
+ modpack.logo = element.attribute("logo");
+ modpack.oldVersions = element.attribute("oldVersions").split(";");
+ modpack.broken = false;
+ modpack.bugged = false;
+
+ //remove empty if the xml is bugged
+ for(QString curr : modpack.oldVersions)
+ {
+ if(curr.isNull() || curr.isEmpty())
+ {
+ modpack.oldVersions.removeAll(curr);
+ modpack.bugged = true;
+ qWarning() << "Removed some empty versions from" << modpack.name;
+ }
+ }
+
+ if(modpack.oldVersions.size() < 1)
+ {
+ if(!modpack.currentVersion.isNull() && !modpack.currentVersion.isEmpty())
+ {
+ modpack.oldVersions.append(modpack.currentVersion);
+ qWarning() << "Added current version to oldVersions because oldVersions was empty! (" + modpack.name + ")";
+ }
+ else
+ {
+ modpack.broken = true;
+ qWarning() << "Broken pack:" << modpack.name << " => No valid version!";
+ }
+ }
+
+ modpack.author = element.attribute("author");
+
+ modpack.dir = element.attribute("dir");
+ modpack.file = element.attribute("url");
+
+ modpack.type = packType;
+
+ list.append(modpack);
+ }
+
+ return true;
}
-void FtbPackFetchTask::fileDownloadFailed(QString reason){
- qWarning() << "Fetching FtbPacks failed: " << reason;
- emit failed(reason);
+void FtbPackFetchTask::fileDownloadFailed(QString reason)
+{
+ qWarning() << "Fetching FtbPacks failed:" << reason;
+ emit failed(reason);
}
diff --git a/api/logic/modplatform/ftb/FtbPackFetchTask.h b/api/logic/modplatform/ftb/FtbPackFetchTask.h
index b5635b12..f955fe83 100644
--- a/api/logic/modplatform/ftb/FtbPackFetchTask.h
+++ b/api/logic/modplatform/ftb/FtbPackFetchTask.h
@@ -8,30 +8,33 @@
class MULTIMC_LOGIC_EXPORT FtbPackFetchTask : public QObject {
- Q_OBJECT
+ Q_OBJECT
public:
- FtbPackFetchTask();
- virtual ~FtbPackFetchTask();
+ FtbPackFetchTask() = default;
+ virtual ~FtbPackFetchTask() = default;
- void fetch();
+ void fetch();
+ void fetchPrivate(const QStringList &toFetch);
private:
- NetJobPtr jobPtr;
+ NetJobPtr jobPtr;
- QByteArray publicModpacksXmlFileData;
- QByteArray thirdPartyModpacksXmlFileData;
+ QByteArray publicModpacksXmlFileData;
+ QByteArray thirdPartyModpacksXmlFileData;
- bool parseAndAddPacks(QByteArray &data, FtbPackType packType, FtbModpackList &list);
- FtbModpackList publicPacks;
- FtbModpackList thirdPartyPacks;
+ bool parseAndAddPacks(QByteArray &data, FtbPackType packType, FtbModpackList &list);
+ FtbModpackList publicPacks;
+ FtbModpackList thirdPartyPacks;
protected slots:
- void fileDownloadFinished();
- void fileDownloadFailed(QString reason);
+ void fileDownloadFinished();
+ void fileDownloadFailed(QString reason);
signals:
- void finished(FtbModpackList publicPacks, FtbModpackList thirdPartyPacks);
- void failed(QString reason);
+ void finished(FtbModpackList publicPacks, FtbModpackList thirdPartyPacks);
+ void failed(QString reason);
+ void privateFileDownloadFinished(FtbModpack modpack);
+ void privateFileDownloadFailed(QString reason, QString packCode);
};
diff --git a/api/logic/modplatform/ftb/FtbPackInstallTask.cpp b/api/logic/modplatform/ftb/FtbPackInstallTask.cpp
index b57c2092..4962bcac 100644
--- a/api/logic/modplatform/ftb/FtbPackInstallTask.cpp
+++ b/api/logic/modplatform/ftb/FtbPackInstallTask.cpp
@@ -9,190 +9,199 @@
#include "minecraft/ComponentList.h"
#include "minecraft/GradleSpecifier.h"
+#include "net/URLConstants.h"
+
FtbPackInstallTask::FtbPackInstallTask(FtbModpack pack, QString version)
{
- m_pack = pack;
- m_version = version;
+ m_pack = pack;
+ m_version = version;
}
void FtbPackInstallTask::executeTask()
{
- downloadPack();
+ downloadPack();
}
void FtbPackInstallTask::downloadPack()
{
- setStatus(tr("Downloading zip for %1").arg(m_pack.name));
-
- auto packoffset = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
- auto entry = ENV.metacache()->resolveEntry("FTBPacks", packoffset);
- NetJob *job = new NetJob("Download FTB Pack");
-
- entry->setStale(true);
- QString url = QString("http://ftb.cursecdn.com/FTB2/modpacks/%1").arg(packoffset);
- job->addNetAction(Net::Download::makeCached(url, entry));
- archivePath = entry->getFullPath();
-
- netJobContainer.reset(job);
- connect(job, &NetJob::succeeded, this, &FtbPackInstallTask::onDownloadSucceeded);
- connect(job, &NetJob::failed, this, &FtbPackInstallTask::onDownloadFailed);
- connect(job, &NetJob::progress, this, &FtbPackInstallTask::onDownloadProgress);
- job->start();
-
- progress(1, 4);
+ setStatus(tr("Downloading zip for %1").arg(m_pack.name));
+
+ auto packoffset = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
+ auto entry = ENV.metacache()->resolveEntry("FTBPacks", packoffset);
+ NetJob *job = new NetJob("Download FTB Pack");
+
+ entry->setStale(true);
+ QString url;
+ if(m_pack.type == FtbPackType::Private)
+ {
+ url = QString(URLConstants::FTB_CDN_BASE_URL + "privatepacks/%1").arg(packoffset);
+ }
+ else
+ {
+ url = QString(URLConstants::FTB_CDN_BASE_URL + "modpacks/%1").arg(packoffset);
+ }
+ job->addNetAction(Net::Download::makeCached(url, entry));
+ archivePath = entry->getFullPath();
+
+ netJobContainer.reset(job);
+ connect(job, &NetJob::succeeded, this, &FtbPackInstallTask::onDownloadSucceeded);
+ connect(job, &NetJob::failed, this, &FtbPackInstallTask::onDownloadFailed);
+ connect(job, &NetJob::progress, this, &FtbPackInstallTask::onDownloadProgress);
+ job->start();
+
+ progress(1, 4);
}
void FtbPackInstallTask::onDownloadSucceeded()
{
- abortable = false;
- unzip();
+ abortable = false;
+ unzip();
}
void FtbPackInstallTask::onDownloadFailed(QString reason)
{
- emitFailed(reason);
+ abortable = false;
+ emitFailed(reason);
}
void FtbPackInstallTask::onDownloadProgress(qint64 current, qint64 total)
{
- abortable = true;
- progress(current, total * 4);
- setStatus(tr("Downloading zip for %1 (%2\%)").arg(m_pack.name).arg(current / 10));
+ abortable = true;
+ progress(current, total * 4);
+ setStatus(tr("Downloading zip for %1 (%2%)").arg(m_pack.name).arg(current / 10));
}
void FtbPackInstallTask::unzip()
{
- progress(2, 4);
- setStatus(tr("Extracting modpack"));
- QDir extractDir(m_stagingPath);
-
- m_packZip.reset(new QuaZip(archivePath));
- if(!m_packZip->open(QuaZip::mdUnzip))
- {
- emitFailed(tr("Failed to open modpack file %1!").arg(archivePath));
- return;
- }
-
- m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &FtbPackInstallTask::onUnzipFinished);
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &FtbPackInstallTask::onUnzipCanceled);
- m_extractFutureWatcher.setFuture(m_extractFuture);
+ progress(2, 4);
+ setStatus(tr("Extracting modpack"));
+ QDir extractDir(m_stagingPath);
+
+ m_packZip.reset(new QuaZip(archivePath));
+ if(!m_packZip->open(QuaZip::mdUnzip))
+ {
+ emitFailed(tr("Failed to open modpack file %1!").arg(archivePath));
+ return;
+ }
+
+ m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &FtbPackInstallTask::onUnzipFinished);
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &FtbPackInstallTask::onUnzipCanceled);
+ m_extractFutureWatcher.setFuture(m_extractFuture);
}
void FtbPackInstallTask::onUnzipFinished()
{
- install();
+ install();
}
void FtbPackInstallTask::onUnzipCanceled()
{
- emitAborted();
+ emitAborted();
}
void FtbPackInstallTask::install()
{
- progress(3, 4);
- setStatus(tr("Installing modpack"));
- QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
- if(unzipMcDir.exists())
- {
- //ok, found minecraft dir, move contents to instance dir
- if(!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft"))
- {
- emitFailed(tr("Failed to move unzipped minecraft!"));
- return;
- }
- }
-
- QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
-
- MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
- auto components = instance.getComponentList();
- components->buildingFromScratch();
- components->setComponentVersion("net.minecraft", m_pack.mcVersion, true);
-
- bool fallback = true;
-
- //handle different versions
- QFile packJson(m_stagingPath + "/.minecraft/pack.json");
- QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
- if(packJson.exists())
- {
- packJson.open(QIODevice::ReadOnly | QIODevice::Text);
- QJsonDocument doc = QJsonDocument::fromJson(packJson.readAll());
- packJson.close();
-
- //we only care about the libs
- QJsonArray libs = doc.object().value("libraries").toArray();
-
- foreach (const QJsonValue &value, libs)
- {
- QString nameValue = value.toObject().value("name").toString();
- if(!nameValue.startsWith("net.minecraftforge"))
- {
- continue;
- }
-
- GradleSpecifier forgeVersion(nameValue);
-
- components->setComponentVersion("net.minecraftforge", forgeVersion.version().replace(m_pack.mcVersion, "").replace("-", ""));
- packJson.remove();
- fallback = false;
- break;
- }
-
- }
-
- if(jarmodDir.exists())
- {
- qDebug() << "Found jarmods, installing...";
-
- QStringList jarmods;
- for (auto info: jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
- {
- qDebug() << "Jarmod:" << info.fileName();
- jarmods.push_back(info.absoluteFilePath());
- }
-
- components->installJarMods(jarmods);
- fallback = false;
- }
-
- //just nuke unzip directory, it s not needed anymore
- FS::deletePath(m_stagingPath + "/unzip");
-
- if(fallback)
- {
- //TODO: Some fallback mechanism... or just keep failing!
- emitFailed(tr("No installation method found!"));
- return;
- }
-
- components->saveNow();
-
- progress(4, 4);
-
- instance.init();
- instance.setName(m_instName);
- if(m_instIcon == "default")
- {
- m_instIcon = "ftb_logo";
- }
- instance.setIconKey(m_instIcon);
- instance.setGroupInitial(m_instGroup);
- instanceSettings->resumeSave();
-
- emitSucceeded();
+ progress(3, 4);
+ setStatus(tr("Installing modpack"));
+ QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
+ if(unzipMcDir.exists())
+ {
+ //ok, found minecraft dir, move contents to instance dir
+ if(!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft"))
+ {
+ emitFailed(tr("Failed to move unzipped minecraft!"));
+ return;
+ }
+ }
+
+ QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
+ auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+ instanceSettings->set("InstanceType", "OneSix");
+
+ MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
+ auto components = instance.getComponentList();
+ components->buildingFromScratch();
+ components->setComponentVersion("net.minecraft", m_pack.mcVersion, true);
+
+ bool fallback = true;
+
+ //handle different versions
+ QFile packJson(m_stagingPath + "/.minecraft/pack.json");
+ QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
+ if(packJson.exists())
+ {
+ packJson.open(QIODevice::ReadOnly | QIODevice::Text);
+ QJsonDocument doc = QJsonDocument::fromJson(packJson.readAll());
+ packJson.close();
+
+ //we only care about the libs
+ QJsonArray libs = doc.object().value("libraries").toArray();
+
+ foreach (const QJsonValue &value, libs)
+ {
+ QString nameValue = value.toObject().value("name").toString();
+ if(!nameValue.startsWith("net.minecraftforge"))
+ {
+ continue;
+ }
+
+ GradleSpecifier forgeVersion(nameValue);
+
+ components->setComponentVersion("net.minecraftforge", forgeVersion.version().replace(m_pack.mcVersion, "").replace("-", ""));
+ packJson.remove();
+ fallback = false;
+ break;
+ }
+
+ }
+
+ if(jarmodDir.exists())
+ {
+ qDebug() << "Found jarmods, installing...";
+
+ QStringList jarmods;
+ for (auto info: jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
+ {
+ qDebug() << "Jarmod:" << info.fileName();
+ jarmods.push_back(info.absoluteFilePath());
+ }
+
+ components->installJarMods(jarmods);
+ fallback = false;
+ }
+
+ //just nuke unzip directory, it s not needed anymore
+ FS::deletePath(m_stagingPath + "/unzip");
+
+ if(fallback)
+ {
+ //TODO: Some fallback mechanism... or just keep failing!
+ emitFailed(tr("No installation method found!"));
+ return;
+ }
+
+ components->saveNow();
+
+ progress(4, 4);
+
+ instance.setName(m_instName);
+ if(m_instIcon == "default")
+ {
+ m_instIcon = "ftb_logo";
+ }
+ instance.setIconKey(m_instIcon);
+ instanceSettings->resumeSave();
+
+ emitSucceeded();
}
bool FtbPackInstallTask::abort()
{
- if(abortable)
- {
- return netJobContainer->abort();
- }
- return false;
+ if(abortable)
+ {
+ return netJobContainer->abort();
+ }
+ return false;
}
diff --git a/api/logic/modplatform/ftb/FtbPackInstallTask.h b/api/logic/modplatform/ftb/FtbPackInstallTask.h
index 0bfba3d6..3319025e 100644
--- a/api/logic/modplatform/ftb/FtbPackInstallTask.h
+++ b/api/logic/modplatform/ftb/FtbPackInstallTask.h
@@ -1,6 +1,5 @@
#pragma once
#include "InstanceTask.h"
-#include "BaseInstanceProvider.h"
#include "net/NetJob.h"
#include "quazip.h"
#include "quazipdir.h"
@@ -9,41 +8,41 @@
#include "meta/VersionList.h"
#include "modplatform/ftb/PackHelpers.h"
-class MULTIMC_LOGIC_EXPORT FtbPackInstallTask : public InstanceTask {
-
- Q_OBJECT
+class MULTIMC_LOGIC_EXPORT FtbPackInstallTask : public InstanceTask
+{
+ Q_OBJECT
public:
- explicit FtbPackInstallTask(FtbModpack pack, QString version);
- virtual ~FtbPackInstallTask(){}
+ explicit FtbPackInstallTask(FtbModpack pack, QString version);
+ virtual ~FtbPackInstallTask(){}
- bool abort() override;
+ bool abort() override;
protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
+ //! Entry point for tasks.
+ virtual void executeTask() override;
private:
- void downloadPack();
- void unzip();
- void install();
+ void downloadPack();
+ void unzip();
+ void install();
private slots:
- void onDownloadSucceeded();
- void onDownloadFailed(QString reason);
- void onDownloadProgress(qint64 current, qint64 total);
+ void onDownloadSucceeded();
+ void onDownloadFailed(QString reason);
+ void onDownloadProgress(qint64 current, qint64 total);
- void onUnzipFinished();
- void onUnzipCanceled();
+ void onUnzipFinished();
+ void onUnzipCanceled();
private: /* data */
- bool abortable = false;
- std::unique_ptr<QuaZip> m_packZip;
- QFuture<QStringList> m_extractFuture;
- QFutureWatcher<QStringList> m_extractFutureWatcher;
- NetJobPtr netJobContainer;
- QString archivePath;
-
- FtbModpack m_pack;
- QString m_version;
+ bool abortable = false;
+ std::unique_ptr<QuaZip> m_packZip;
+ QFuture<QStringList> m_extractFuture;
+ QFutureWatcher<QStringList> m_extractFutureWatcher;
+ NetJobPtr netJobContainer;
+ QString archivePath;
+
+ FtbModpack m_pack;
+ QString m_version;
};
diff --git a/api/logic/modplatform/ftb/FtbPrivatePackManager.cpp b/api/logic/modplatform/ftb/FtbPrivatePackManager.cpp
new file mode 100644
index 00000000..c3477cec
--- /dev/null
+++ b/api/logic/modplatform/ftb/FtbPrivatePackManager.cpp
@@ -0,0 +1,37 @@
+#include "FtbPrivatePackManager.h"
+
+#include <QDebug>
+
+#include "FileSystem.h"
+
+void FtbPrivatePackManager::load()
+{
+ try
+ {
+ currentPacks = QString::fromUtf8(FS::read(m_filename)).split('\n', QString::SkipEmptyParts).toSet();
+ dirty = false;
+ }
+ catch(...)
+ {
+ currentPacks = {};
+ qWarning() << "Failed to read third party FTB pack codes from" << m_filename;
+ }
+}
+
+void FtbPrivatePackManager::save() const
+{
+ if(!dirty)
+ {
+ return;
+ }
+ try
+ {
+ QStringList list = currentPacks.toList();
+ FS::write(m_filename, list.join('\n').toUtf8());
+ dirty = false;
+ }
+ catch(...)
+ {
+ qWarning() << "Failed to write third party FTB pack codes to" << m_filename;
+ }
+}
diff --git a/api/logic/modplatform/ftb/FtbPrivatePackManager.h b/api/logic/modplatform/ftb/FtbPrivatePackManager.h
new file mode 100644
index 00000000..388224d6
--- /dev/null
+++ b/api/logic/modplatform/ftb/FtbPrivatePackManager.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <QSet>
+#include <QString>
+#include <QFile>
+#include "multimc_logic_export.h"
+
+class MULTIMC_LOGIC_EXPORT FtbPrivatePackManager
+{
+public:
+ ~FtbPrivatePackManager()
+ {
+ save();
+ }
+ void load();
+ void save() const;
+ bool empty() const
+ {
+ return currentPacks.empty();
+ }
+ const QSet<QString> &getCurrentPackCodes() const
+ {
+ return currentPacks;
+ }
+ void add(const QString &code)
+ {
+ currentPacks.insert(code);
+ dirty = true;
+ }
+ void remove(const QString &code)
+ {
+ currentPacks.remove(code);
+ dirty = true;
+ }
+
+private:
+ QSet<QString> currentPacks;
+ QString m_filename = "private_packs.txt";
+ mutable bool dirty = false;
+};
diff --git a/api/logic/modplatform/ftb/PackHelpers.h b/api/logic/modplatform/ftb/PackHelpers.h
index 39b65927..4306caee 100644
--- a/api/logic/modplatform/ftb/PackHelpers.h
+++ b/api/logic/modplatform/ftb/PackHelpers.h
@@ -6,30 +6,33 @@
#include <QMetaType>
//Header for structs etc...
-enum FtbPackType {
- Public,
- ThirdParty,
- Private
+enum class FtbPackType
+{
+ Public,
+ ThirdParty,
+ Private
};
-struct FtbModpack {
- QString name;
- QString description;
- QString author;
- QStringList oldVersions;
- QString currentVersion;
- QString mcVersion;
- QString mods;
- QString logo;
-
- //Technical data
- QString dir;
- QString file; //<- Url in the xml, but doesn't make much sense
-
- bool bugged = false;
- bool broken = false;
-
- FtbPackType type;
+struct FtbModpack
+{
+ QString name;
+ QString description;
+ QString author;
+ QStringList oldVersions;
+ QString currentVersion;
+ QString mcVersion;
+ QString mods;
+ QString logo;
+
+ //Technical data
+ QString dir;
+ QString file; //<- Url in the xml, but doesn't make much sense
+
+ bool bugged = false;
+ bool broken = false;
+
+ FtbPackType type;
+ QString packCode;
};
//We need it for the proxy model
diff --git a/api/logic/net/ByteArraySink.h b/api/logic/net/ByteArraySink.h
index 03b77fcc..20e6764c 100644
--- a/api/logic/net/ByteArraySink.h
+++ b/api/logic/net/ByteArraySink.h
@@ -9,54 +9,54 @@ namespace Net {
class ByteArraySink : public Sink
{
public:
- ByteArraySink(QByteArray *output)
- :m_output(output)
- {
- // nil
- };
+ ByteArraySink(QByteArray *output)
+ :m_output(output)
+ {
+ // nil
+ };
- virtual ~ByteArraySink()
- {
- // nil
- }
+ virtual ~ByteArraySink()
+ {
+ // nil
+ }
public:
- JobStatus init(QNetworkRequest & request) override
- {
- m_output->clear();
- if(initAllValidators(request))
- return Job_InProgress;
- return Job_Failed;
- };
-
- JobStatus write(QByteArray & data) override
- {
- m_output->append(data);
- if(writeAllValidators(data))
- return Job_InProgress;
- return Job_Failed;
- }
-
- JobStatus abort() override
- {
- m_output->clear();
- failAllValidators();
- return Job_Failed;
- }
-
- JobStatus finalize(QNetworkReply &reply) override
- {
- if(finalizeAllValidators(reply))
- return Job_Finished;
- return Job_Failed;
- }
-
- bool hasLocalData() override
- {
- return false;
- }
+ JobStatus init(QNetworkRequest & request) override
+ {
+ m_output->clear();
+ if(initAllValidators(request))
+ return Job_InProgress;
+ return Job_Failed;
+ };
+
+ JobStatus write(QByteArray & data) override
+ {
+ m_output->append(data);
+ if(writeAllValidators(data))
+ return Job_InProgress;
+ return Job_Failed;
+ }
+
+ JobStatus abort() override
+ {
+ m_output->clear();
+ failAllValidators();
+ return Job_Failed;
+ }
+
+ JobStatus finalize(QNetworkReply &reply) override
+ {
+ if(finalizeAllValidators(reply))
+ return Job_Finished;
+ return Job_Failed;
+ }
+
+ bool hasLocalData() override
+ {
+ return false;
+ }
private:
- QByteArray * m_output;
+ QByteArray * m_output;
};
}
diff --git a/api/logic/net/ChecksumValidator.h b/api/logic/net/ChecksumValidator.h
index 3cd25bc3..0d6b19c2 100644
--- a/api/logic/net/ChecksumValidator.h
+++ b/api/logic/net/ChecksumValidator.h
@@ -9,49 +9,47 @@ namespace Net {
class ChecksumValidator: public Validator
{
public: /* con/des */
- ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray())
- :m_checksum(algorithm), m_expected(expected)
- {
- };
- virtual ~ChecksumValidator() {};
+ ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray())
+ :m_checksum(algorithm), m_expected(expected)
+ {
+ };
+ virtual ~ChecksumValidator() {};
public: /* methods */
- bool init(QNetworkRequest &) override
- {
- m_checksum.reset();
- return true;
- }
- bool write(QByteArray & data) override
- {
- m_checksum.addData(data);
- this->data.append(data);
- return true;
- }
- bool abort() override
- {
- return true;
- }
- bool validate(QNetworkReply &) override
- {
- if(m_expected.size() && m_expected != hash())
- {
- qWarning() << "Checksum mismatch, download is bad.";
- return false;
- }
- return true;
- }
- QByteArray hash()
- {
- return m_checksum.result();
- }
- void setExpected(QByteArray expected)
- {
- m_expected = expected;
- }
+ bool init(QNetworkRequest &) override
+ {
+ m_checksum.reset();
+ return true;
+ }
+ bool write(QByteArray & data) override
+ {
+ m_checksum.addData(data);
+ return true;
+ }
+ bool abort() override
+ {
+ return true;
+ }
+ bool validate(QNetworkReply &) override
+ {
+ if(m_expected.size() && m_expected != hash())
+ {
+ qWarning() << "Checksum mismatch, download is bad.";
+ return false;
+ }
+ return true;
+ }
+ QByteArray hash()
+ {
+ return m_checksum.result();
+ }
+ void setExpected(QByteArray expected)
+ {
+ m_expected = expected;
+ }
private: /* data */
- QByteArray data;
- QCryptographicHash m_checksum;
- QByteArray m_expected;
+ QCryptographicHash m_checksum;
+ QByteArray m_expected;
};
} \ No newline at end of file
diff --git a/api/logic/net/Download.cpp b/api/logic/net/Download.cpp
index 631d3076..229782b0 100644
--- a/api/logic/net/Download.cpp
+++ b/api/logic/net/Download.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,271 +28,281 @@ namespace Net {
Download::Download():NetAction()
{
- m_status = Job_NotStarted;
+ m_status = Job_NotStarted;
}
Download::Ptr Download::makeCached(QUrl url, MetaEntryPtr entry, Options options)
{
- Download * dl = new Download();
- dl->m_url = url;
- dl->m_options = options;
- auto md5Node = new ChecksumValidator(QCryptographicHash::Md5);
- auto cachedNode = new MetaCacheSink(entry, md5Node);
- dl->m_sink.reset(cachedNode);
- dl->m_target_path = entry->getFullPath();
- return std::shared_ptr<Download>(dl);
+ Download * dl = new Download();
+ dl->m_url = url;
+ dl->m_options = options;
+ auto md5Node = new ChecksumValidator(QCryptographicHash::Md5);
+ auto cachedNode = new MetaCacheSink(entry, md5Node);
+ dl->m_sink.reset(cachedNode);
+ dl->m_target_path = entry->getFullPath();
+ return std::shared_ptr<Download>(dl);
}
Download::Ptr Download::makeByteArray(QUrl url, QByteArray *output, Options options)
{
- Download * dl = new Download();
- dl->m_url = url;
- dl->m_options = options;
- dl->m_sink.reset(new ByteArraySink(output));
- return std::shared_ptr<Download>(dl);
+ Download * dl = new Download();
+ dl->m_url = url;
+ dl->m_options = options;
+ dl->m_sink.reset(new ByteArraySink(output));
+ return std::shared_ptr<Download>(dl);
}
Download::Ptr Download::makeFile(QUrl url, QString path, Options options)
{
- Download * dl = new Download();
- dl->m_url = url;
- dl->m_options = options;
- dl->m_sink.reset(new FileSink(path));
- return std::shared_ptr<Download>(dl);
+ Download * dl = new Download();
+ dl->m_url = url;
+ dl->m_options = options;
+ dl->m_sink.reset(new FileSink(path));
+ return std::shared_ptr<Download>(dl);
}
void Download::addValidator(Validator * v)
{
- m_sink->addValidator(v);
+ m_sink->addValidator(v);
}
void Download::start()
{
- if(m_status == Job_Aborted)
- {
- qWarning() << "Attempt to start an aborted Download:" << m_url.toString();
- emit aborted(m_index_within_job);
- return;
- }
- QNetworkRequest request(m_url);
- m_status = m_sink->init(request);
- switch(m_status)
- {
- case Job_Finished:
- emit succeeded(m_index_within_job);
- qDebug() << "Download cache hit " << m_url.toString();
- return;
- case Job_InProgress:
- qDebug() << "Downloading " << m_url.toString();
- break;
- case Job_Failed_Proceed: // this is meaningless in this context. We do need a sink.
- case Job_NotStarted:
- case Job_Failed:
- emit failed(m_index_within_job);
- return;
- case Job_Aborted:
- return;
- }
+ if(m_status == Job_Aborted)
+ {
+ qWarning() << "Attempt to start an aborted Download:" << m_url.toString();
+ emit aborted(m_index_within_job);
+ return;
+ }
+ QNetworkRequest request(m_url);
+ m_status = m_sink->init(request);
+ switch(m_status)
+ {
+ case Job_Finished:
+ emit succeeded(m_index_within_job);
+ qDebug() << "Download cache hit " << m_url.toString();
+ return;
+ case Job_InProgress:
+ qDebug() << "Downloading " << m_url.toString();
+ break;
+ case Job_Failed_Proceed: // this is meaningless in this context. We do need a sink.
+ case Job_NotStarted:
+ case Job_Failed:
+ emit failed(m_index_within_job);
+ return;
+ case Job_Aborted:
+ return;
+ }
- request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0");
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0");
- QNetworkReply *rep = ENV.qnam().get(request);
+ QNetworkReply *rep = ENV.qnam().get(request);
- m_reply.reset(rep);
- connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64)));
- connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
- connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
- connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors);
- connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead);
+ m_reply.reset(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors);
+ connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead);
}
void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
- m_total_progress = bytesTotal;
- m_progress = bytesReceived;
- emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
}
void Download::downloadError(QNetworkReply::NetworkError error)
{
- if(error == QNetworkReply::OperationCanceledError)
- {
- qCritical() << "Aborted " << m_url.toString();
- m_status = Job_Aborted;
- }
- else
- {
- if(m_options & Option::AcceptLocalFiles)
- {
- if(m_sink->hasLocalData())
- {
- m_status = Job_Failed_Proceed;
- return;
- }
- }
- // error happened during download.
- qCritical() << "Failed " << m_url.toString() << " with reason " << error;
- m_status = Job_Failed;
- }
+ if(error == QNetworkReply::OperationCanceledError)
+ {
+ qCritical() << "Aborted " << m_url.toString();
+ m_status = Job_Aborted;
+ }
+ else
+ {
+ if(m_options & Option::AcceptLocalFiles)
+ {
+ if(m_sink->hasLocalData())
+ {
+ m_status = Job_Failed_Proceed;
+ return;
+ }
+ }
+ // error happened during download.
+ qCritical() << "Failed " << m_url.toString() << " with reason " << error;
+ m_status = Job_Failed;
+ }
}
void Download::sslErrors(const QList<QSslError> & errors)
{
- int i = 1;
- for (auto error : errors)
- {
- qCritical() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
- auto cert = error.certificate();
- qCritical() << "Certificate in question:\n" << cert.toText();
- i++;
- }
+ int i = 1;
+ for (auto error : errors)
+ {
+ qCritical() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
+ auto cert = error.certificate();
+ qCritical() << "Certificate in question:\n" << cert.toText();
+ i++;
+ }
}
bool Download::handleRedirect()
{
- QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
- if(!redirect.isValid())
- {
- if(!m_reply->hasRawHeader("Location"))
- {
- // no redirect -> it's fine to continue
- return false;
- }
- // there is a Location header, but it's not correct. we need to apply some workarounds...
- QByteArray redirectBA = m_reply->rawHeader("Location");
- if(redirectBA.size() == 0)
- {
- // empty, yet present redirect header? WTF?
- return false;
- }
- QString redirectStr = QString::fromUtf8(redirectBA);
+ QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
+ if(!redirect.isValid())
+ {
+ if(!m_reply->hasRawHeader("Location"))
+ {
+ // no redirect -> it's fine to continue
+ return false;
+ }
+ // there is a Location header, but it's not correct. we need to apply some workarounds...
+ QByteArray redirectBA = m_reply->rawHeader("Location");
+ if(redirectBA.size() == 0)
+ {
+ // empty, yet present redirect header? WTF?
+ return false;
+ }
+ QString redirectStr = QString::fromUtf8(redirectBA);
- /*
- * IF the URL begins with //, we need to insert the URL scheme.
- * See: https://bugreports.qt-project.org/browse/QTBUG-41061
- */
- if(redirectStr.startsWith("//"))
- {
- redirectStr = m_reply->url().scheme() + ":" + redirectStr;
- }
+ if(redirectStr.startsWith("//"))
+ {
+ /*
+ * IF the URL begins with //, we need to insert the URL scheme.
+ * See: https://bugreports.qt.io/browse/QTBUG-41061
+ * See: http://tools.ietf.org/html/rfc3986#section-4.2
+ */
+ redirectStr = m_reply->url().scheme() + ":" + redirectStr;
+ }
+ else if(redirectStr.startsWith("/"))
+ {
+ /*
+ * IF the URL begins with /, we need to process it as a relative URL
+ */
+ auto url = m_reply->url();
+ url.setPath(redirectStr, QUrl::TolerantMode);
+ redirectStr = url.toString();
+ }
- /*
- * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues.
- * FIXME: report Qt bug for this
- */
- redirect = QUrl(redirectStr, QUrl::TolerantMode);
- if(!redirect.isValid())
- {
- qWarning() << "Failed to parse redirect URL:" << redirectStr;
- downloadError(QNetworkReply::ProtocolFailure);
- return false;
- }
- qDebug() << "Fixed location header:" << redirect;
- }
- else
- {
- qDebug() << "Location header:" << redirect;
- }
+ /*
+ * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues.
+ * FIXME: report Qt bug for this
+ */
+ redirect = QUrl(redirectStr, QUrl::TolerantMode);
+ if(!redirect.isValid())
+ {
+ qWarning() << "Failed to parse redirect URL:" << redirectStr;
+ downloadError(QNetworkReply::ProtocolFailure);
+ return false;
+ }
+ qDebug() << "Fixed location header:" << redirect;
+ }
+ else
+ {
+ qDebug() << "Location header:" << redirect;
+ }
- m_url = QUrl(redirect.toString());
- qDebug() << "Following redirect to " << m_url.toString();
- start();
- return true;
+ m_url = QUrl(redirect.toString());
+ qDebug() << "Following redirect to " << m_url.toString();
+ start();
+ return true;
}
void Download::downloadFinished()
{
- // handle HTTP redirection first
- if(handleRedirect())
- {
- qDebug() << "Download redirected:" << m_url.toString();
- return;
- }
+ // handle HTTP redirection first
+ if(handleRedirect())
+ {
+ qDebug() << "Download redirected:" << m_url.toString();
+ return;
+ }
- // if the download failed before this point ...
- if (m_status == Job_Failed_Proceed)
- {
- qDebug() << "Download failed but we are allowed to proceed:" << m_url.toString();
- m_sink->abort();
- m_reply.reset();
- emit succeeded(m_index_within_job);
- return;
- }
- else if (m_status == Job_Failed)
- {
- qDebug() << "Download failed in previous step:" << m_url.toString();
- m_sink->abort();
- m_reply.reset();
- emit failed(m_index_within_job);
- return;
- }
- else if(m_status == Job_Aborted)
- {
- qDebug() << "Download aborted in previous step:" << m_url.toString();
- m_sink->abort();
- m_reply.reset();
- emit aborted(m_index_within_job);
- return;
- }
+ // if the download failed before this point ...
+ if (m_status == Job_Failed_Proceed)
+ {
+ qDebug() << "Download failed but we are allowed to proceed:" << m_url.toString();
+ m_sink->abort();
+ m_reply.reset();
+ emit succeeded(m_index_within_job);
+ return;
+ }
+ else if (m_status == Job_Failed)
+ {
+ qDebug() << "Download failed in previous step:" << m_url.toString();
+ m_sink->abort();
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
+ else if(m_status == Job_Aborted)
+ {
+ qDebug() << "Download aborted in previous step:" << m_url.toString();
+ m_sink->abort();
+ m_reply.reset();
+ emit aborted(m_index_within_job);
+ return;
+ }
- // make sure we got all the remaining data, if any
- auto data = m_reply->readAll();
- if(data.size())
- {
- qDebug() << "Writing extra" << data.size() << "bytes to" << m_target_path;
- m_status = m_sink->write(data);
- }
+ // make sure we got all the remaining data, if any
+ auto data = m_reply->readAll();
+ if(data.size())
+ {
+ qDebug() << "Writing extra" << data.size() << "bytes to" << m_target_path;
+ m_status = m_sink->write(data);
+ }
- // otherwise, finalize the whole graph
- m_status = m_sink->finalize(*m_reply.get());
- if (m_status != Job_Finished)
- {
- qDebug() << "Download failed to finalize:" << m_url.toString();
- m_sink->abort();
- m_reply.reset();
- emit failed(m_index_within_job);
- return;
- }
- m_reply.reset();
- qDebug() << "Download succeeded:" << m_url.toString();
- emit succeeded(m_index_within_job);
+ // otherwise, finalize the whole graph
+ m_status = m_sink->finalize(*m_reply.get());
+ if (m_status != Job_Finished)
+ {
+ qDebug() << "Download failed to finalize:" << m_url.toString();
+ m_sink->abort();
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
+ m_reply.reset();
+ qDebug() << "Download succeeded:" << m_url.toString();
+ emit succeeded(m_index_within_job);
}
void Download::downloadReadyRead()
{
- if(m_status == Job_InProgress)
- {
- auto data = m_reply->readAll();
- m_status = m_sink->write(data);
- if(m_status == Job_Failed)
- {
- qCritical() << "Failed to process response chunk for " << m_target_path;
- }
- // qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes";
- }
- else
- {
- qCritical() << "Cannot write to " << m_target_path << ", illegal status" << m_status;
- }
+ if(m_status == Job_InProgress)
+ {
+ auto data = m_reply->readAll();
+ m_status = m_sink->write(data);
+ if(m_status == Job_Failed)
+ {
+ qCritical() << "Failed to process response chunk for " << m_target_path;
+ }
+ // qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes";
+ }
+ else
+ {
+ qCritical() << "Cannot write to " << m_target_path << ", illegal status" << m_status;
+ }
}
}
bool Net::Download::abort()
{
- if(m_reply)
- {
- m_reply->abort();
- }
- else
- {
- m_status = Job_Aborted;
- }
- return true;
+ if(m_reply)
+ {
+ m_reply->abort();
+ }
+ else
+ {
+ m_status = Job_Aborted;
+ }
+ return true;
}
bool Net::Download::canAbort()
{
- return true;
+ return true;
}
diff --git a/api/logic/net/Download.h b/api/logic/net/Download.h
index 00bf108c..18472911 100644
--- a/api/logic/net/Download.h
+++ b/api/logic/net/Download.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,52 +24,52 @@
namespace Net {
class MULTIMC_LOGIC_EXPORT Download : public NetAction
{
- Q_OBJECT
+ Q_OBJECT
public: /* types */
- typedef std::shared_ptr<class Download> Ptr;
- enum class Option
- {
- NoOptions = 0,
- AcceptLocalFiles = 1
- };
- Q_DECLARE_FLAGS(Options, Option)
+ typedef std::shared_ptr<class Download> Ptr;
+ enum class Option
+ {
+ NoOptions = 0,
+ AcceptLocalFiles = 1
+ };
+ Q_DECLARE_FLAGS(Options, Option)
protected: /* con/des */
- explicit Download();
+ explicit Download();
public:
- virtual ~Download(){};
- static Download::Ptr makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions);
- static Download::Ptr makeByteArray(QUrl url, QByteArray *output, Options options = Option::NoOptions);
- static Download::Ptr makeFile(QUrl url, QString path, Options options = Option::NoOptions);
+ virtual ~Download(){};
+ static Download::Ptr makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions);
+ static Download::Ptr makeByteArray(QUrl url, QByteArray *output, Options options = Option::NoOptions);
+ static Download::Ptr makeFile(QUrl url, QString path, Options options = Option::NoOptions);
public: /* methods */
- QString getTargetFilepath()
- {
- return m_target_path;
- }
- void addValidator(Validator * v);
- bool abort() override;
- bool canAbort() override;
+ QString getTargetFilepath()
+ {
+ return m_target_path;
+ }
+ void addValidator(Validator * v);
+ bool abort() override;
+ bool canAbort() override;
private: /* methods */
- bool handleRedirect();
+ bool handleRedirect();
protected slots:
- void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
- void downloadError(QNetworkReply::NetworkError error) override;
+ void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
+ void downloadError(QNetworkReply::NetworkError error) override;
void sslErrors(const QList<QSslError> & errors);
- void downloadFinished() override;
- void downloadReadyRead() override;
+ void downloadFinished() override;
+ void downloadReadyRead() override;
public slots:
- void start() override;
+ void start() override;
private: /* data */
- // FIXME: remove this, it has no business being here.
- QString m_target_path;
- std::unique_ptr<Sink> m_sink;
- Options m_options;
+ // FIXME: remove this, it has no business being here.
+ QString m_target_path;
+ std::unique_ptr<Sink> m_sink;
+ Options m_options;
};
}
diff --git a/api/logic/net/FileSink.cpp b/api/logic/net/FileSink.cpp
index f52c5788..8b3e917d 100644
--- a/api/logic/net/FileSink.cpp
+++ b/api/logic/net/FileSink.cpp
@@ -7,109 +7,109 @@
namespace Net {
FileSink::FileSink(QString filename)
- :m_filename(filename)
+ :m_filename(filename)
{
- // nil
-};
+ // nil
+}
FileSink::~FileSink()
{
- // nil
-};
+ // nil
+}
JobStatus FileSink::init(QNetworkRequest& request)
{
- auto result = initCache(request);
- if(result != Job_InProgress)
- {
- return result;
- }
- // create a new save file and open it for writing
- if (!FS::ensureFilePathExists(m_filename))
- {
- qCritical() << "Could not create folder for " + m_filename;
- return Job_Failed;
- }
- wroteAnyData = false;
- m_output_file.reset(new QSaveFile(m_filename));
- if (!m_output_file->open(QIODevice::WriteOnly))
- {
- qCritical() << "Could not open " + m_filename + " for writing";
- return Job_Failed;
- }
+ auto result = initCache(request);
+ if(result != Job_InProgress)
+ {
+ return result;
+ }
+ // create a new save file and open it for writing
+ if (!FS::ensureFilePathExists(m_filename))
+ {
+ qCritical() << "Could not create folder for " + m_filename;
+ return Job_Failed;
+ }
+ wroteAnyData = false;
+ m_output_file.reset(new QSaveFile(m_filename));
+ if (!m_output_file->open(QIODevice::WriteOnly))
+ {
+ qCritical() << "Could not open " + m_filename + " for writing";
+ return Job_Failed;
+ }
- if(initAllValidators(request))
- return Job_InProgress;
- return Job_Failed;
+ if(initAllValidators(request))
+ return Job_InProgress;
+ return Job_Failed;
}
JobStatus FileSink::initCache(QNetworkRequest &)
{
- return Job_InProgress;
+ return Job_InProgress;
}
JobStatus FileSink::write(QByteArray& data)
{
- if (!writeAllValidators(data) || m_output_file->write(data) != data.size())
- {
- qCritical() << "Failed writing into " + m_filename;
- m_output_file->cancelWriting();
- m_output_file.reset();
- wroteAnyData = false;
- return Job_Failed;
- }
- wroteAnyData = true;
- return Job_InProgress;
+ if (!writeAllValidators(data) || m_output_file->write(data) != data.size())
+ {
+ qCritical() << "Failed writing into " + m_filename;
+ m_output_file->cancelWriting();
+ m_output_file.reset();
+ wroteAnyData = false;
+ return Job_Failed;
+ }
+ wroteAnyData = true;
+ return Job_InProgress;
}
JobStatus FileSink::abort()
{
- m_output_file->cancelWriting();
- failAllValidators();
- return Job_Failed;
+ m_output_file->cancelWriting();
+ failAllValidators();
+ return Job_Failed;
}
JobStatus FileSink::finalize(QNetworkReply& reply)
{
- bool gotFile = false;
- QVariant statusCodeV = reply.attribute(QNetworkRequest::HttpStatusCodeAttribute);
- bool validStatus = false;
- int statusCode = statusCodeV.toInt(&validStatus);
- if(validStatus)
- {
- // this leaves out 304 Not Modified
- gotFile = statusCode == 200 || statusCode == 203;
- }
- // if we wrote any data to the save file, we try to commit the data to the real file.
- // if it actually got a proper file, we write it even if it was empty
- if (gotFile || wroteAnyData)
- {
- // ask validators for data consistency
- // we only do this for actual downloads, not 'your data is still the same' cache hits
- if(!finalizeAllValidators(reply))
- return Job_Failed;
- // nothing went wrong...
- if (!m_output_file->commit())
- {
- qCritical() << "Failed to commit changes to " << m_filename;
- m_output_file->cancelWriting();
- return Job_Failed;
- }
- }
- // then get rid of the save file
- m_output_file.reset();
+ bool gotFile = false;
+ QVariant statusCodeV = reply.attribute(QNetworkRequest::HttpStatusCodeAttribute);
+ bool validStatus = false;
+ int statusCode = statusCodeV.toInt(&validStatus);
+ if(validStatus)
+ {
+ // this leaves out 304 Not Modified
+ gotFile = statusCode == 200 || statusCode == 203;
+ }
+ // if we wrote any data to the save file, we try to commit the data to the real file.
+ // if it actually got a proper file, we write it even if it was empty
+ if (gotFile || wroteAnyData)
+ {
+ // ask validators for data consistency
+ // we only do this for actual downloads, not 'your data is still the same' cache hits
+ if(!finalizeAllValidators(reply))
+ return Job_Failed;
+ // nothing went wrong...
+ if (!m_output_file->commit())
+ {
+ qCritical() << "Failed to commit changes to " << m_filename;
+ m_output_file->cancelWriting();
+ return Job_Failed;
+ }
+ }
+ // then get rid of the save file
+ m_output_file.reset();
- return finalizeCache(reply);
+ return finalizeCache(reply);
}
JobStatus FileSink::finalizeCache(QNetworkReply &)
{
- return Job_Finished;
+ return Job_Finished;
}
bool FileSink::hasLocalData()
{
- QFileInfo info(m_filename);
- return info.exists() && info.size() != 0;
+ QFileInfo info(m_filename);
+ return info.exists() && info.size() != 0;
}
}
diff --git a/api/logic/net/FileSink.h b/api/logic/net/FileSink.h
index 035d5bfd..875fe511 100644
--- a/api/logic/net/FileSink.h
+++ b/api/logic/net/FileSink.h
@@ -6,23 +6,23 @@ namespace Net {
class FileSink : public Sink
{
public: /* con/des */
- FileSink(QString filename);
- virtual ~FileSink();
+ FileSink(QString filename);
+ virtual ~FileSink();
public: /* methods */
- JobStatus init(QNetworkRequest & request) override;
- JobStatus write(QByteArray & data) override;
- JobStatus abort() override;
- JobStatus finalize(QNetworkReply & reply) override;
- bool hasLocalData() override;
+ JobStatus init(QNetworkRequest & request) override;
+ JobStatus write(QByteArray & data) override;
+ JobStatus abort() override;
+ JobStatus finalize(QNetworkReply & reply) override;
+ bool hasLocalData() override;
protected: /* methods */
- virtual JobStatus initCache(QNetworkRequest &);
- virtual JobStatus finalizeCache(QNetworkReply &reply);
+ virtual JobStatus initCache(QNetworkRequest &);
+ virtual JobStatus finalizeCache(QNetworkReply &reply);
protected: /* data */
- QString m_filename;
- bool wroteAnyData = false;
- std::unique_ptr<QSaveFile> m_output_file;
+ QString m_filename;
+ bool wroteAnyData = false;
+ std::unique_ptr<QSaveFile> m_output_file;
};
}
diff --git a/api/logic/net/HttpMetaCache.cpp b/api/logic/net/HttpMetaCache.cpp
index ebcb0a27..0e3d41b5 100644
--- a/api/logic/net/HttpMetaCache.cpp
+++ b/api/logic/net/HttpMetaCache.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,244 +30,244 @@
QString MetaEntry::getFullPath()
{
- // FIXME: make local?
- return FS::PathCombine(basePath, relativePath);
+ // FIXME: make local?
+ return FS::PathCombine(basePath, relativePath);
}
HttpMetaCache::HttpMetaCache(QString path) : QObject()
{
- m_index_file = path;
- saveBatchingTimer.setSingleShot(true);
- saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer);
- connect(&saveBatchingTimer, SIGNAL(timeout()), SLOT(SaveNow()));
+ m_index_file = path;
+ saveBatchingTimer.setSingleShot(true);
+ saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer);
+ connect(&saveBatchingTimer, SIGNAL(timeout()), SLOT(SaveNow()));
}
HttpMetaCache::~HttpMetaCache()
{
- saveBatchingTimer.stop();
- SaveNow();
+ saveBatchingTimer.stop();
+ SaveNow();
}
MetaEntryPtr HttpMetaCache::getEntry(QString base, QString resource_path)
{
- // no base. no base path. can't store
- if (!m_entries.contains(base))
- {
- // TODO: log problem
- return MetaEntryPtr();
- }
- EntryMap &map = m_entries[base];
- if (map.entry_list.contains(resource_path))
- {
- return map.entry_list[resource_path];
- }
- return MetaEntryPtr();
+ // no base. no base path. can't store
+ if (!m_entries.contains(base))
+ {
+ // TODO: log problem
+ return MetaEntryPtr();
+ }
+ EntryMap &map = m_entries[base];
+ if (map.entry_list.contains(resource_path))
+ {
+ return map.entry_list[resource_path];
+ }
+ return MetaEntryPtr();
}
MetaEntryPtr HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag)
{
- auto entry = getEntry(base, resource_path);
- // it's not present? generate a default stale entry
- if (!entry)
- {
- return staleEntry(base, resource_path);
- }
+ auto entry = getEntry(base, resource_path);
+ // it's not present? generate a default stale entry
+ if (!entry)
+ {
+ return staleEntry(base, resource_path);
+ }
- auto &selected_base = m_entries[base];
- QString real_path = FS::PathCombine(selected_base.base_path, resource_path);
- QFileInfo finfo(real_path);
+ auto &selected_base = m_entries[base];
+ QString real_path = FS::PathCombine(selected_base.base_path, resource_path);
+ QFileInfo finfo(real_path);
- // is the file really there? if not -> stale
- if (!finfo.isFile() || !finfo.isReadable())
- {
- // if the file doesn't exist, we disown the entry
- selected_base.entry_list.remove(resource_path);
- return staleEntry(base, resource_path);
- }
+ // is the file really there? if not -> stale
+ if (!finfo.isFile() || !finfo.isReadable())
+ {
+ // if the file doesn't exist, we disown the entry
+ selected_base.entry_list.remove(resource_path);
+ return staleEntry(base, resource_path);
+ }
- if (!expected_etag.isEmpty() && expected_etag != entry->etag)
- {
- // if the etag doesn't match expected, we disown the entry
- selected_base.entry_list.remove(resource_path);
- return staleEntry(base, resource_path);
- }
+ if (!expected_etag.isEmpty() && expected_etag != entry->etag)
+ {
+ // if the etag doesn't match expected, we disown the entry
+ selected_base.entry_list.remove(resource_path);
+ return staleEntry(base, resource_path);
+ }
- // if the file changed, check md5sum
- qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch();
- if (file_last_changed != entry->local_changed_timestamp)
- {
- QFile input(real_path);
- input.open(QIODevice::ReadOnly);
- QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5)
- .toHex()
- .constData();
- if (entry->md5sum != md5sum)
- {
- selected_base.entry_list.remove(resource_path);
- return staleEntry(base, resource_path);
- }
- // md5sums matched... keep entry and save the new state to file
- entry->local_changed_timestamp = file_last_changed;
- SaveEventually();
- }
+ // if the file changed, check md5sum
+ qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch();
+ if (file_last_changed != entry->local_changed_timestamp)
+ {
+ QFile input(real_path);
+ input.open(QIODevice::ReadOnly);
+ QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5)
+ .toHex()
+ .constData();
+ if (entry->md5sum != md5sum)
+ {
+ selected_base.entry_list.remove(resource_path);
+ return staleEntry(base, resource_path);
+ }
+ // md5sums matched... keep entry and save the new state to file
+ entry->local_changed_timestamp = file_last_changed;
+ SaveEventually();
+ }
- // entry passed all the checks we cared about.
- entry->basePath = getBasePath(base);
- return entry;
+ // entry passed all the checks we cared about.
+ entry->basePath = getBasePath(base);
+ return entry;
}
bool HttpMetaCache::updateEntry(MetaEntryPtr stale_entry)
{
- if (!m_entries.contains(stale_entry->baseId))
- {
- qCritical() << "Cannot add entry with unknown base: "
- << stale_entry->baseId.toLocal8Bit();
- return false;
- }
- if (stale_entry->stale)
- {
- qCritical() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();
- return false;
- }
- m_entries[stale_entry->baseId].entry_list[stale_entry->relativePath] = stale_entry;
- SaveEventually();
- return true;
+ if (!m_entries.contains(stale_entry->baseId))
+ {
+ qCritical() << "Cannot add entry with unknown base: "
+ << stale_entry->baseId.toLocal8Bit();
+ return false;
+ }
+ if (stale_entry->stale)
+ {
+ qCritical() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();
+ return false;
+ }
+ m_entries[stale_entry->baseId].entry_list[stale_entry->relativePath] = stale_entry;
+ SaveEventually();
+ return true;
}
bool HttpMetaCache::evictEntry(MetaEntryPtr entry)
{
- if(entry)
- {
- entry->stale = true;
- SaveEventually();
- return true;
- }
- return false;
+ if(entry)
+ {
+ entry->stale = true;
+ SaveEventually();
+ return true;
+ }
+ return false;
}
MetaEntryPtr HttpMetaCache::staleEntry(QString base, QString resource_path)
{
- auto foo = new MetaEntry();
- foo->baseId = base;
- foo->basePath = getBasePath(base);
- foo->relativePath = resource_path;
- foo->stale = true;
- return MetaEntryPtr(foo);
+ auto foo = new MetaEntry();
+ foo->baseId = base;
+ foo->basePath = getBasePath(base);
+ foo->relativePath = resource_path;
+ foo->stale = true;
+ return MetaEntryPtr(foo);
}
void HttpMetaCache::addBase(QString base, QString base_root)
{
- // TODO: report error
- if (m_entries.contains(base))
- return;
- // TODO: check if the base path is valid
- EntryMap foo;
- foo.base_path = base_root;
- m_entries[base] = foo;
+ // TODO: report error
+ if (m_entries.contains(base))
+ return;
+ // TODO: check if the base path is valid
+ EntryMap foo;
+ foo.base_path = base_root;
+ m_entries[base] = foo;
}
QString HttpMetaCache::getBasePath(QString base)
{
- if (m_entries.contains(base))
- {
- return m_entries[base].base_path;
- }
- return QString();
+ if (m_entries.contains(base))
+ {
+ return m_entries[base].base_path;
+ }
+ return QString();
}
void HttpMetaCache::Load()
{
- if(m_index_file.isNull())
- return;
+ if(m_index_file.isNull())
+ return;
- QFile index(m_index_file);
- if (!index.open(QIODevice::ReadOnly))
- return;
+ QFile index(m_index_file);
+ if (!index.open(QIODevice::ReadOnly))
+ return;
- QJsonDocument json = QJsonDocument::fromJson(index.readAll());
- if (!json.isObject())
- return;
- auto root = json.object();
- // check file version first
- auto version_val = root.value("version");
- if (!version_val.isString())
- return;
- if (version_val.toString() != "1")
- return;
+ QJsonDocument json = QJsonDocument::fromJson(index.readAll());
+ if (!json.isObject())
+ return;
+ auto root = json.object();
+ // check file version first
+ auto version_val = root.value("version");
+ if (!version_val.isString())
+ return;
+ if (version_val.toString() != "1")
+ return;
- // read the entry array
- auto entries_val = root.value("entries");
- if (!entries_val.isArray())
- return;
- QJsonArray array = entries_val.toArray();
- for (auto element : array)
- {
- if (!element.isObject())
- return;
- auto element_obj = element.toObject();
- QString base = element_obj.value("base").toString();
- if (!m_entries.contains(base))
- continue;
- auto &entrymap = m_entries[base];
- auto foo = new MetaEntry();
- foo->baseId = base;
- QString path = foo->relativePath = element_obj.value("path").toString();
- foo->md5sum = element_obj.value("md5sum").toString();
- foo->etag = element_obj.value("etag").toString();
- foo->local_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble();
- foo->remote_changed_timestamp =
- element_obj.value("remote_changed_timestamp").toString();
- // presumed innocent until closer examination
- foo->stale = false;
- entrymap.entry_list[path] = MetaEntryPtr(foo);
- }
+ // read the entry array
+ auto entries_val = root.value("entries");
+ if (!entries_val.isArray())
+ return;
+ QJsonArray array = entries_val.toArray();
+ for (auto element : array)
+ {
+ if (!element.isObject())
+ return;
+ auto element_obj = element.toObject();
+ QString base = element_obj.value("base").toString();
+ if (!m_entries.contains(base))
+ continue;
+ auto &entrymap = m_entries[base];
+ auto foo = new MetaEntry();
+ foo->baseId = base;
+ QString path = foo->relativePath = element_obj.value("path").toString();
+ foo->md5sum = element_obj.value("md5sum").toString();
+ foo->etag = element_obj.value("etag").toString();
+ foo->local_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble();
+ foo->remote_changed_timestamp =
+ element_obj.value("remote_changed_timestamp").toString();
+ // presumed innocent until closer examination
+ foo->stale = false;
+ entrymap.entry_list[path] = MetaEntryPtr(foo);
+ }
}
void HttpMetaCache::SaveEventually()
{
- // reset the save timer
- saveBatchingTimer.stop();
- saveBatchingTimer.start(30000);
+ // reset the save timer
+ saveBatchingTimer.stop();
+ saveBatchingTimer.start(30000);
}
void HttpMetaCache::SaveNow()
{
- if(m_index_file.isNull())
- return;
- QJsonObject toplevel;
- toplevel.insert("version", QJsonValue(QString("1")));
- QJsonArray entriesArr;
- for (auto group : m_entries)
- {
- for (auto entry : group.entry_list)
- {
- // do not save stale entries. they are dead.
- if(entry->stale)
- {
- continue;
- }
- QJsonObject entryObj;
- entryObj.insert("base", QJsonValue(entry->baseId));
- entryObj.insert("path", QJsonValue(entry->relativePath));
- entryObj.insert("md5sum", QJsonValue(entry->md5sum));
- entryObj.insert("etag", QJsonValue(entry->etag));
- entryObj.insert("last_changed_timestamp",
- QJsonValue(double(entry->local_changed_timestamp)));
- if (!entry->remote_changed_timestamp.isEmpty())
- entryObj.insert("remote_changed_timestamp",
- QJsonValue(entry->remote_changed_timestamp));
- entriesArr.append(entryObj);
- }
- }
- toplevel.insert("entries", entriesArr);
+ if(m_index_file.isNull())
+ return;
+ QJsonObject toplevel;
+ toplevel.insert("version", QJsonValue(QString("1")));
+ QJsonArray entriesArr;
+ for (auto group : m_entries)
+ {
+ for (auto entry : group.entry_list)
+ {
+ // do not save stale entries. they are dead.
+ if(entry->stale)
+ {
+ continue;
+ }
+ QJsonObject entryObj;
+ entryObj.insert("base", QJsonValue(entry->baseId));
+ entryObj.insert("path", QJsonValue(entry->relativePath));
+ entryObj.insert("md5sum", QJsonValue(entry->md5sum));
+ entryObj.insert("etag", QJsonValue(entry->etag));
+ entryObj.insert("last_changed_timestamp",
+ QJsonValue(double(entry->local_changed_timestamp)));
+ if (!entry->remote_changed_timestamp.isEmpty())
+ entryObj.insert("remote_changed_timestamp",
+ QJsonValue(entry->remote_changed_timestamp));
+ entriesArr.append(entryObj);
+ }
+ }
+ toplevel.insert("entries", entriesArr);
- QJsonDocument doc(toplevel);
- try
- {
- FS::write(m_index_file, doc.toJson());
- }
- catch (Exception & e)
- {
- qWarning() << e.what();
- }
+ QJsonDocument doc(toplevel);
+ try
+ {
+ FS::write(m_index_file, doc.toJson());
+ }
+ catch (const Exception &e)
+ {
+ qWarning() << e.what();
+ }
}
diff --git a/api/logic/net/HttpMetaCache.h b/api/logic/net/HttpMetaCache.h
index 7ee5f735..aa92f8ff 100644
--- a/api/logic/net/HttpMetaCache.h
+++ b/api/logic/net/HttpMetaCache.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,99 +27,99 @@ class MULTIMC_LOGIC_EXPORT MetaEntry
{
friend class HttpMetaCache;
protected:
- MetaEntry() {}
+ MetaEntry() {}
public:
- bool isStale()
- {
- return stale;
- }
- void setStale(bool stale)
- {
- this->stale = stale;
- }
- QString getFullPath();
- QString getRemoteChangedTimestamp()
- {
- return remote_changed_timestamp;
- }
- void setRemoteChangedTimestamp(QString remote_changed_timestamp)
- {
- this->remote_changed_timestamp = remote_changed_timestamp;
- }
- void setLocalChangedTimestamp(qint64 timestamp)
- {
- local_changed_timestamp = timestamp;
- }
- QString getETag()
- {
- return etag;
- }
- void setETag(QString etag)
- {
- this->etag = etag;
- }
- QString getMD5Sum()
- {
- return md5sum;
- }
- void setMD5Sum(QString md5sum)
- {
- this->md5sum = md5sum;
- }
+ bool isStale()
+ {
+ return stale;
+ }
+ void setStale(bool stale)
+ {
+ this->stale = stale;
+ }
+ QString getFullPath();
+ QString getRemoteChangedTimestamp()
+ {
+ return remote_changed_timestamp;
+ }
+ void setRemoteChangedTimestamp(QString remote_changed_timestamp)
+ {
+ this->remote_changed_timestamp = remote_changed_timestamp;
+ }
+ void setLocalChangedTimestamp(qint64 timestamp)
+ {
+ local_changed_timestamp = timestamp;
+ }
+ QString getETag()
+ {
+ return etag;
+ }
+ void setETag(QString etag)
+ {
+ this->etag = etag;
+ }
+ QString getMD5Sum()
+ {
+ return md5sum;
+ }
+ void setMD5Sum(QString md5sum)
+ {
+ this->md5sum = md5sum;
+ }
protected:
- QString baseId;
- QString basePath;
- QString relativePath;
- QString md5sum;
- QString etag;
- qint64 local_changed_timestamp = 0;
- QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time
- bool stale = true;
+ QString baseId;
+ QString basePath;
+ QString relativePath;
+ QString md5sum;
+ QString etag;
+ qint64 local_changed_timestamp = 0;
+ QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time
+ bool stale = true;
};
typedef std::shared_ptr<MetaEntry> MetaEntryPtr;
class MULTIMC_LOGIC_EXPORT HttpMetaCache : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- // supply path to the cache index file
- HttpMetaCache(QString path = QString());
- ~HttpMetaCache();
+ // supply path to the cache index file
+ HttpMetaCache(QString path = QString());
+ ~HttpMetaCache();
- // get the entry solely from the cache
- // you probably don't want this, unless you have some specific caching needs.
- MetaEntryPtr getEntry(QString base, QString resource_path);
+ // get the entry solely from the cache
+ // you probably don't want this, unless you have some specific caching needs.
+ MetaEntryPtr getEntry(QString base, QString resource_path);
- // get the entry from cache and verify that it isn't stale (within reason)
- MetaEntryPtr resolveEntry(QString base, QString resource_path,
- QString expected_etag = QString());
+ // get the entry from cache and verify that it isn't stale (within reason)
+ MetaEntryPtr resolveEntry(QString base, QString resource_path,
+ QString expected_etag = QString());
- // add a previously resolved stale entry
- bool updateEntry(MetaEntryPtr stale_entry);
+ // add a previously resolved stale entry
+ bool updateEntry(MetaEntryPtr stale_entry);
- // evict selected entry from cache
- bool evictEntry(MetaEntryPtr entry);
+ // evict selected entry from cache
+ bool evictEntry(MetaEntryPtr entry);
- void addBase(QString base, QString base_root);
+ void addBase(QString base, QString base_root);
- // (re)start a timer that calls SaveNow later.
- void SaveEventually();
- void Load();
- QString getBasePath(QString base);
+ // (re)start a timer that calls SaveNow later.
+ void SaveEventually();
+ void Load();
+ QString getBasePath(QString base);
public
slots:
- void SaveNow();
+ void SaveNow();
private:
- // create a new stale entry, given the parameters
- MetaEntryPtr staleEntry(QString base, QString resource_path);
- struct EntryMap
- {
- QString base_path;
- QMap<QString, MetaEntryPtr> entry_list;
- };
- QMap<QString, EntryMap> m_entries;
- QString m_index_file;
- QTimer saveBatchingTimer;
+ // create a new stale entry, given the parameters
+ MetaEntryPtr staleEntry(QString base, QString resource_path);
+ struct EntryMap
+ {
+ QString base_path;
+ QMap<QString, MetaEntryPtr> entry_list;
+ };
+ QMap<QString, EntryMap> m_entries;
+ QString m_index_file;
+ QTimer saveBatchingTimer;
};
diff --git a/api/logic/net/MetaCacheSink.cpp b/api/logic/net/MetaCacheSink.cpp
index c9d75310..d7f18533 100644
--- a/api/logic/net/MetaCacheSink.cpp
+++ b/api/logic/net/MetaCacheSink.cpp
@@ -7,59 +7,59 @@
namespace Net {
MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator * md5sum)
- :Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum)
+ :Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum)
{
- addValidator(md5sum);
-};
+ addValidator(md5sum);
+}
MetaCacheSink::~MetaCacheSink()
{
- // nil
-};
+ // nil
+}
JobStatus MetaCacheSink::initCache(QNetworkRequest& request)
{
- if (!m_entry->isStale())
- {
- return Job_Finished;
- }
- // check if file exists, if it does, use its information for the request
- QFile current(m_filename);
- if(current.exists() && current.size() != 0)
- {
- if (m_entry->getRemoteChangedTimestamp().size())
- {
- request.setRawHeader(QString("If-Modified-Since").toLatin1(), m_entry->getRemoteChangedTimestamp().toLatin1());
- }
- if (m_entry->getETag().size())
- {
- request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1());
- }
- }
- return Job_InProgress;
+ if (!m_entry->isStale())
+ {
+ return Job_Finished;
+ }
+ // check if file exists, if it does, use its information for the request
+ QFile current(m_filename);
+ if(current.exists() && current.size() != 0)
+ {
+ if (m_entry->getRemoteChangedTimestamp().size())
+ {
+ request.setRawHeader(QString("If-Modified-Since").toLatin1(), m_entry->getRemoteChangedTimestamp().toLatin1());
+ }
+ if (m_entry->getETag().size())
+ {
+ request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1());
+ }
+ }
+ return Job_InProgress;
}
JobStatus MetaCacheSink::finalizeCache(QNetworkReply & reply)
{
- QFileInfo output_file_info(m_filename);
- if(wroteAnyData)
- {
- m_entry->setMD5Sum(m_md5Node->hash().toHex().constData());
- }
- m_entry->setETag(reply.rawHeader("ETag").constData());
- if (reply.hasRawHeader("Last-Modified"))
- {
- m_entry->setRemoteChangedTimestamp(reply.rawHeader("Last-Modified").constData());
- }
- m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch());
- m_entry->setStale(false);
- ENV.metacache()->updateEntry(m_entry);
- return Job_Finished;
+ QFileInfo output_file_info(m_filename);
+ if(wroteAnyData)
+ {
+ m_entry->setMD5Sum(m_md5Node->hash().toHex().constData());
+ }
+ m_entry->setETag(reply.rawHeader("ETag").constData());
+ if (reply.hasRawHeader("Last-Modified"))
+ {
+ m_entry->setRemoteChangedTimestamp(reply.rawHeader("Last-Modified").constData());
+ }
+ m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch());
+ m_entry->setStale(false);
+ ENV.metacache()->updateEntry(m_entry);
+ return Job_Finished;
}
bool MetaCacheSink::hasLocalData()
{
- QFileInfo info(m_filename);
- return info.exists() && info.size() != 0;
+ QFileInfo info(m_filename);
+ return info.exists() && info.size() != 0;
}
}
diff --git a/api/logic/net/MetaCacheSink.h b/api/logic/net/MetaCacheSink.h
index 0f3bbdf6..edcf7ad1 100644
--- a/api/logic/net/MetaCacheSink.h
+++ b/api/logic/net/MetaCacheSink.h
@@ -7,16 +7,16 @@ namespace Net {
class MetaCacheSink : public FileSink
{
public: /* con/des */
- MetaCacheSink(MetaEntryPtr entry, ChecksumValidator * md5sum);
- virtual ~MetaCacheSink();
- bool hasLocalData() override;
+ MetaCacheSink(MetaEntryPtr entry, ChecksumValidator * md5sum);
+ virtual ~MetaCacheSink();
+ bool hasLocalData() override;
protected: /* methods */
- JobStatus initCache(QNetworkRequest & request) override;
- JobStatus finalizeCache(QNetworkReply & reply) override;
+ JobStatus initCache(QNetworkRequest & request) override;
+ JobStatus finalizeCache(QNetworkReply & reply) override;
private: /* data */
- MetaEntryPtr m_entry;
- ChecksumValidator * m_md5Node;
+ MetaEntryPtr m_entry;
+ ChecksumValidator * m_md5Node;
};
}
diff --git a/api/logic/net/Mode.h b/api/logic/net/Mode.h
index 62e26d92..9a95f5ad 100644
--- a/api/logic/net/Mode.h
+++ b/api/logic/net/Mode.h
@@ -4,7 +4,7 @@ namespace Net
{
enum class Mode
{
- Offline,
- Online
+ Offline,
+ Online
};
}
diff --git a/api/logic/net/NetAction.h b/api/logic/net/NetAction.h
index 08e40a29..da34a04e 100644
--- a/api/logic/net/NetAction.h
+++ b/api/logic/net/NetAction.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,91 +25,91 @@
enum JobStatus
{
- Job_NotStarted,
- Job_InProgress,
- Job_Finished,
- Job_Failed,
- Job_Aborted,
- /*
- * FIXME: @NUKE this confuses the task failing with us having a fallback in the form of local data. Clear up the confusion.
- * Same could be true for aborted task - the presence of pre-existing result is a separate concern
- */
- Job_Failed_Proceed
+ Job_NotStarted,
+ Job_InProgress,
+ Job_Finished,
+ Job_Failed,
+ Job_Aborted,
+ /*
+ * FIXME: @NUKE this confuses the task failing with us having a fallback in the form of local data. Clear up the confusion.
+ * Same could be true for aborted task - the presence of pre-existing result is a separate concern
+ */
+ Job_Failed_Proceed
};
typedef std::shared_ptr<class NetAction> NetActionPtr;
class MULTIMC_LOGIC_EXPORT NetAction : public QObject
{
- Q_OBJECT
+ Q_OBJECT
protected:
- explicit NetAction() : QObject(0) {};
+ explicit NetAction() : QObject(0) {};
public:
- virtual ~NetAction() {};
-
- bool isRunning() const
- {
- return m_status == Job_InProgress;
- }
- bool isFinished() const
- {
- return m_status >= Job_Finished;
- }
- bool wasSuccessful() const
- {
- return m_status == Job_Finished || m_status == Job_Failed_Proceed;
- }
-
- qint64 totalProgress() const
- {
- return m_total_progress;
- }
- qint64 currentProgress() const
- {
- return m_progress;
- }
- virtual bool abort()
- {
- return false;
- }
- virtual bool canAbort()
- {
- return false;
- }
- QUrl url()
- {
- return m_url;
- }
+ virtual ~NetAction() {};
+
+ bool isRunning() const
+ {
+ return m_status == Job_InProgress;
+ }
+ bool isFinished() const
+ {
+ return m_status >= Job_Finished;
+ }
+ bool wasSuccessful() const
+ {
+ return m_status == Job_Finished || m_status == Job_Failed_Proceed;
+ }
+
+ qint64 totalProgress() const
+ {
+ return m_total_progress;
+ }
+ qint64 currentProgress() const
+ {
+ return m_progress;
+ }
+ virtual bool abort()
+ {
+ return false;
+ }
+ virtual bool canAbort()
+ {
+ return false;
+ }
+ QUrl url()
+ {
+ return m_url;
+ }
signals:
- void started(int index);
- void netActionProgress(int index, qint64 current, qint64 total);
- void succeeded(int index);
- void failed(int index);
- void aborted(int index);
+ void started(int index);
+ void netActionProgress(int index, qint64 current, qint64 total);
+ void succeeded(int index);
+ void failed(int index);
+ void aborted(int index);
protected slots:
- virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0;
- virtual void downloadError(QNetworkReply::NetworkError error) = 0;
- virtual void downloadFinished() = 0;
- virtual void downloadReadyRead() = 0;
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0;
+ virtual void downloadError(QNetworkReply::NetworkError error) = 0;
+ virtual void downloadFinished() = 0;
+ virtual void downloadReadyRead() = 0;
public slots:
- virtual void start() = 0;
+ virtual void start() = 0;
public:
- /// index within the parent job, FIXME: nuke
- int m_index_within_job = 0;
+ /// index within the parent job, FIXME: nuke
+ int m_index_within_job = 0;
- /// the network reply
- unique_qobject_ptr<QNetworkReply> m_reply;
+ /// the network reply
+ unique_qobject_ptr<QNetworkReply> m_reply;
- /// source URL
- QUrl m_url;
+ /// source URL
+ QUrl m_url;
- qint64 m_progress = 0;
- qint64 m_total_progress = 1;
+ qint64 m_progress = 0;
+ qint64 m_total_progress = 1;
protected:
- JobStatus m_status = Job_NotStarted;
+ JobStatus m_status = Job_NotStarted;
};
diff --git a/api/logic/net/NetJob.cpp b/api/logic/net/NetJob.cpp
index 1ff8e28f..71e31736 100644
--- a/api/logic/net/NetJob.cpp
+++ b/api/logic/net/NetJob.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,197 +20,197 @@
void NetJob::partSucceeded(int index)
{
- // do progress. all slots are 1 in size at least
- auto &slot = parts_progress[index];
- partProgress(index, slot.total_progress, slot.total_progress);
-
- m_doing.remove(index);
- m_done.insert(index);
- downloads[index].get()->disconnect(this);
- startMoreParts();
+ // do progress. all slots are 1 in size at least
+ auto &slot = parts_progress[index];
+ partProgress(index, slot.total_progress, slot.total_progress);
+
+ m_doing.remove(index);
+ m_done.insert(index);
+ downloads[index].get()->disconnect(this);
+ startMoreParts();
}
void NetJob::partFailed(int index)
{
- m_doing.remove(index);
- auto &slot = parts_progress[index];
- if (slot.failures == 3)
- {
- m_failed.insert(index);
- }
- else
- {
- slot.failures++;
- m_todo.enqueue(index);
- }
- downloads[index].get()->disconnect(this);
- startMoreParts();
+ m_doing.remove(index);
+ auto &slot = parts_progress[index];
+ if (slot.failures == 3)
+ {
+ m_failed.insert(index);
+ }
+ else
+ {
+ slot.failures++;
+ m_todo.enqueue(index);
+ }
+ downloads[index].get()->disconnect(this);
+ startMoreParts();
}
void NetJob::partAborted(int index)
{
- m_aborted = true;
- m_doing.remove(index);
- m_failed.insert(index);
- downloads[index].get()->disconnect(this);
- startMoreParts();
+ m_aborted = true;
+ m_doing.remove(index);
+ m_failed.insert(index);
+ downloads[index].get()->disconnect(this);
+ startMoreParts();
}
void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal)
{
- auto &slot = parts_progress[index];
- slot.current_progress = bytesReceived;
- slot.total_progress = bytesTotal;
-
- int done = m_done.size();
- int doing = m_doing.size();
- int all = parts_progress.size();
-
- qint64 bytesAll = 0;
- qint64 bytesTotalAll = 0;
- for(auto & partIdx: m_doing)
- {
- auto part = parts_progress[partIdx];
- // do not count parts with unknown/nonsensical total size
- if(part.total_progress <= 0)
- {
- continue;
- }
- bytesAll += part.current_progress;
- bytesTotalAll += part.total_progress;
- }
-
- qint64 inprogress = (bytesTotalAll == 0) ? 0 : (bytesAll * 1000) / bytesTotalAll;
- auto current = done * 1000 + doing * inprogress;
- auto current_total = all * 1000;
- // HACK: make sure it never jumps backwards.
- // FAIL: This breaks if the size is not known (or is it something else?) and jumps to 1000, so if it is 1000 reset it to inprogress
- if(m_current_progress == 1000) {
- m_current_progress = inprogress;
- }
- if(m_current_progress > current)
- {
- current = m_current_progress;
- }
- m_current_progress = current;
- setProgress(current, current_total);
+ auto &slot = parts_progress[index];
+ slot.current_progress = bytesReceived;
+ slot.total_progress = bytesTotal;
+
+ int done = m_done.size();
+ int doing = m_doing.size();
+ int all = parts_progress.size();
+
+ qint64 bytesAll = 0;
+ qint64 bytesTotalAll = 0;
+ for(auto & partIdx: m_doing)
+ {
+ auto part = parts_progress[partIdx];
+ // do not count parts with unknown/nonsensical total size
+ if(part.total_progress <= 0)
+ {
+ continue;
+ }
+ bytesAll += part.current_progress;
+ bytesTotalAll += part.total_progress;
+ }
+
+ qint64 inprogress = (bytesTotalAll == 0) ? 0 : (bytesAll * 1000) / bytesTotalAll;
+ auto current = done * 1000 + doing * inprogress;
+ auto current_total = all * 1000;
+ // HACK: make sure it never jumps backwards.
+ // FAIL: This breaks if the size is not known (or is it something else?) and jumps to 1000, so if it is 1000 reset it to inprogress
+ if(m_current_progress == 1000) {
+ m_current_progress = inprogress;
+ }
+ if(m_current_progress > current)
+ {
+ current = m_current_progress;
+ }
+ m_current_progress = current;
+ setProgress(current, current_total);
}
void NetJob::executeTask()
{
- // hack that delays early failures so they can be caught easier
- QMetaObject::invokeMethod(this, "startMoreParts", Qt::QueuedConnection);
+ // hack that delays early failures so they can be caught easier
+ QMetaObject::invokeMethod(this, "startMoreParts", Qt::QueuedConnection);
}
void NetJob::startMoreParts()
{
- if(!isRunning())
- {
- // this actually makes sense. You can put running downloads into a NetJob and then not start it until much later.
- return;
- }
- // OK. We are actively processing tasks, proceed.
- // Check for final conditions if there's nothing in the queue.
- if(!m_todo.size())
- {
- if(!m_doing.size())
- {
- if(!m_failed.size())
- {
- emitSucceeded();
- }
- else if(m_aborted)
- {
- emitAborted();
- }
- else
- {
- emitFailed(tr("Job '%1' failed to process:\n%2").arg(objectName()).arg(getFailedFiles().join("\n")));
- }
- }
- return;
- }
- // There's work to do, try to start more parts.
- while (m_doing.size() < 6)
- {
- if(!m_todo.size())
- return;
- int doThis = m_todo.dequeue();
- m_doing.insert(doThis);
- auto part = downloads[doThis];
- // connect signals :D
- connect(part.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
- connect(part.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
- connect(part.get(), SIGNAL(aborted(int)), SLOT(partAborted(int)));
- connect(part.get(), SIGNAL(netActionProgress(int, qint64, qint64)),
- SLOT(partProgress(int, qint64, qint64)));
- part->start();
- }
+ if(!isRunning())
+ {
+ // this actually makes sense. You can put running downloads into a NetJob and then not start it until much later.
+ return;
+ }
+ // OK. We are actively processing tasks, proceed.
+ // Check for final conditions if there's nothing in the queue.
+ if(!m_todo.size())
+ {
+ if(!m_doing.size())
+ {
+ if(!m_failed.size())
+ {
+ emitSucceeded();
+ }
+ else if(m_aborted)
+ {
+ emitAborted();
+ }
+ else
+ {
+ emitFailed(tr("Job '%1' failed to process:\n%2").arg(objectName()).arg(getFailedFiles().join("\n")));
+ }
+ }
+ return;
+ }
+ // There's work to do, try to start more parts.
+ while (m_doing.size() < 6)
+ {
+ if(!m_todo.size())
+ return;
+ int doThis = m_todo.dequeue();
+ m_doing.insert(doThis);
+ auto part = downloads[doThis];
+ // connect signals :D
+ connect(part.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
+ connect(part.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
+ connect(part.get(), SIGNAL(aborted(int)), SLOT(partAborted(int)));
+ connect(part.get(), SIGNAL(netActionProgress(int, qint64, qint64)),
+ SLOT(partProgress(int, qint64, qint64)));
+ part->start();
+ }
}
QStringList NetJob::getFailedFiles()
{
- QStringList failed;
- for (auto index: m_failed)
- {
- failed.push_back(downloads[index]->url().toString());
- }
- failed.sort();
- return failed;
+ QStringList failed;
+ for (auto index: m_failed)
+ {
+ failed.push_back(downloads[index]->url().toString());
+ }
+ failed.sort();
+ return failed;
}
bool NetJob::canAbort() const
{
- bool canFullyAbort = true;
- // can abort the waiting?
- for(auto index: m_todo)
- {
- auto part = downloads[index];
- canFullyAbort &= part->canAbort();
- }
- // can abort the active?
- for(auto index: m_doing)
- {
- auto part = downloads[index];
- canFullyAbort &= part->canAbort();
- }
- return canFullyAbort;
+ bool canFullyAbort = true;
+ // can abort the waiting?
+ for(auto index: m_todo)
+ {
+ auto part = downloads[index];
+ canFullyAbort &= part->canAbort();
+ }
+ // can abort the active?
+ for(auto index: m_doing)
+ {
+ auto part = downloads[index];
+ canFullyAbort &= part->canAbort();
+ }
+ return canFullyAbort;
}
bool NetJob::abort()
{
- bool fullyAborted = true;
- // fail all waiting
- m_failed.unite(m_todo.toSet());
- m_todo.clear();
- // abort active
- auto toKill = m_doing.toList();
- for(auto index: toKill)
- {
- auto part = downloads[index];
- fullyAborted &= part->abort();
- }
- return fullyAborted;
+ bool fullyAborted = true;
+ // fail all waiting
+ m_failed.unite(m_todo.toSet());
+ m_todo.clear();
+ // abort active
+ auto toKill = m_doing.toList();
+ for(auto index: toKill)
+ {
+ auto part = downloads[index];
+ fullyAborted &= part->abort();
+ }
+ return fullyAborted;
}
bool NetJob::addNetAction(NetActionPtr action)
{
- action->m_index_within_job = downloads.size();
- downloads.append(action);
- part_info pi;
- parts_progress.append(pi);
- partProgress(parts_progress.count() - 1, action->currentProgress(), action->totalProgress());
-
- if(action->isRunning())
- {
- connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
- connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
- connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)), SLOT(partProgress(int, qint64, qint64)));
- }
- else
- {
- m_todo.append(parts_progress.size() - 1);
- }
- return true;
+ action->m_index_within_job = downloads.size();
+ downloads.append(action);
+ part_info pi;
+ parts_progress.append(pi);
+ partProgress(parts_progress.count() - 1, action->currentProgress(), action->totalProgress());
+
+ if(action->isRunning())
+ {
+ connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
+ connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
+ connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)), SLOT(partProgress(int, qint64, qint64)));
+ }
+ else
+ {
+ m_todo.append(parts_progress.size() - 1);
+ }
+ return true;
}
diff --git a/api/logic/net/NetJob.h b/api/logic/net/NetJob.h
index be58c61a..0b56bdaa 100644
--- a/api/logic/net/NetJob.h
+++ b/api/logic/net/NetJob.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,64 +28,64 @@ typedef shared_qobject_ptr<NetJob> NetJobPtr;
class MULTIMC_LOGIC_EXPORT NetJob : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit NetJob(QString job_name) : Task()
- {
- setObjectName(job_name);
- }
- virtual ~NetJob() {}
+ explicit NetJob(QString job_name) : Task()
+ {
+ setObjectName(job_name);
+ }
+ virtual ~NetJob() {}
- bool addNetAction(NetActionPtr action);
+ bool addNetAction(NetActionPtr action);
- NetActionPtr operator[](int index)
- {
- return downloads[index];
- }
- const NetActionPtr at(const int index)
- {
- return downloads.at(index);
- }
- NetActionPtr first()
- {
- if (downloads.size())
- return downloads[0];
- return NetActionPtr();
- }
- int size() const
- {
- return downloads.size();
- }
- QStringList getFailedFiles();
+ NetActionPtr operator[](int index)
+ {
+ return downloads[index];
+ }
+ const NetActionPtr at(const int index)
+ {
+ return downloads.at(index);
+ }
+ NetActionPtr first()
+ {
+ if (downloads.size())
+ return downloads[0];
+ return NetActionPtr();
+ }
+ int size() const
+ {
+ return downloads.size();
+ }
+ QStringList getFailedFiles();
- bool canAbort() const override;
+ bool canAbort() const override;
private slots:
- void startMoreParts();
+ void startMoreParts();
public slots:
- virtual void executeTask() override;
- virtual bool abort() override;
+ virtual void executeTask() override;
+ virtual bool abort() override;
private slots:
- void partProgress(int index, qint64 bytesReceived, qint64 bytesTotal);
- void partSucceeded(int index);
- void partFailed(int index);
- void partAborted(int index);
+ void partProgress(int index, qint64 bytesReceived, qint64 bytesTotal);
+ void partSucceeded(int index);
+ void partFailed(int index);
+ void partAborted(int index);
private:
- struct part_info
- {
- qint64 current_progress = 0;
- qint64 total_progress = 1;
- int failures = 0;
- };
- QList<NetActionPtr> downloads;
- QList<part_info> parts_progress;
- QQueue<int> m_todo;
- QSet<int> m_doing;
- QSet<int> m_done;
- QSet<int> m_failed;
- qint64 m_current_progress = 0;
- bool m_aborted = false;
+ struct part_info
+ {
+ qint64 current_progress = 0;
+ qint64 total_progress = 1;
+ int failures = 0;
+ };
+ QList<NetActionPtr> downloads;
+ QList<part_info> parts_progress;
+ QQueue<int> m_todo;
+ QSet<int> m_doing;
+ QSet<int> m_done;
+ QSet<int> m_failed;
+ qint64 m_current_progress = 0;
+ bool m_aborted = false;
};
diff --git a/api/logic/net/PasteUpload.cpp b/api/logic/net/PasteUpload.cpp
index d1ddf39d..3526e207 100644
--- a/api/logic/net/PasteUpload.cpp
+++ b/api/logic/net/PasteUpload.cpp
@@ -8,18 +8,18 @@
PasteUpload::PasteUpload(QWidget *window, QString text, QString key) : m_window(window)
{
- m_key = key;
- QByteArray temp;
- QJsonObject topLevelObj;
- QJsonObject sectionObject;
- sectionObject.insert("contents", text);
- QJsonArray sectionArray;
- sectionArray.append(sectionObject);
- topLevelObj.insert("description", "MultiMC Log Upload");
- topLevelObj.insert("sections", sectionArray);
- QJsonDocument docOut;
- docOut.setObject(topLevelObj);
- m_jsonContent = docOut.toJson();
+ m_key = key;
+ QByteArray temp;
+ QJsonObject topLevelObj;
+ QJsonObject sectionObject;
+ sectionObject.insert("contents", text);
+ QJsonArray sectionArray;
+ sectionArray.append(sectionObject);
+ topLevelObj.insert("description", "MultiMC Log Upload");
+ topLevelObj.insert("sections", sectionArray);
+ QJsonDocument docOut;
+ docOut.setObject(topLevelObj);
+ m_jsonContent = docOut.toJson();
}
PasteUpload::~PasteUpload()
@@ -28,76 +28,76 @@ PasteUpload::~PasteUpload()
bool PasteUpload::validateText()
{
- return m_jsonContent.size() <= maxSize();
+ return m_jsonContent.size() <= maxSize();
}
void PasteUpload::executeTask()
{
- QNetworkRequest request(QUrl("https://api.paste.ee/v1/pastes"));
- request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
+ QNetworkRequest request(QUrl("https://api.paste.ee/v1/pastes"));
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
- request.setRawHeader("Content-Type", "application/json");
- request.setRawHeader("Content-Length", QByteArray::number(m_jsonContent.size()));
- request.setRawHeader("X-Auth-Token", m_key.toStdString().c_str());
+ request.setRawHeader("Content-Type", "application/json");
+ request.setRawHeader("Content-Length", QByteArray::number(m_jsonContent.size()));
+ request.setRawHeader("X-Auth-Token", m_key.toStdString().c_str());
- QNetworkReply *rep = ENV.qnam().post(request, m_jsonContent);
+ QNetworkReply *rep = ENV.qnam().post(request, m_jsonContent);
- m_reply = std::shared_ptr<QNetworkReply>(rep);
- setStatus(tr("Uploading to paste.ee"));
- connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
- connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
- connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ setStatus(tr("Uploading to paste.ee"));
+ connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
}
void PasteUpload::downloadError(QNetworkReply::NetworkError error)
{
- // error happened during download.
- qCritical() << "Network error: " << error;
- emitFailed(m_reply->errorString());
+ // error happened during download.
+ qCritical() << "Network error: " << error;
+ emitFailed(m_reply->errorString());
}
void PasteUpload::downloadFinished()
{
- QByteArray data = m_reply->readAll();
- // if the download succeeded
- if (m_reply->error() == QNetworkReply::NetworkError::NoError)
- {
- m_reply.reset();
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if (jsonError.error != QJsonParseError::NoError)
- {
- emitFailed(jsonError.errorString());
- return;
- }
- if (!parseResult(doc))
- {
- emitFailed(tr("paste.ee returned an error. Please consult the logs for more information"));
- return;
- }
- }
- // else the download failed
- else
- {
- emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
- m_reply.reset();
- return;
- }
- emitSucceeded();
+ QByteArray data = m_reply->readAll();
+ // if the download succeeded
+ if (m_reply->error() == QNetworkReply::NetworkError::NoError)
+ {
+ m_reply.reset();
+ QJsonParseError jsonError;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ emitFailed(jsonError.errorString());
+ return;
+ }
+ if (!parseResult(doc))
+ {
+ emitFailed(tr("paste.ee returned an error. Please consult the logs for more information"));
+ return;
+ }
+ }
+ // else the download failed
+ else
+ {
+ emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
+ m_reply.reset();
+ return;
+ }
+ emitSucceeded();
}
bool PasteUpload::parseResult(QJsonDocument doc)
{
- auto object = doc.object();
- auto status = object.value("success").toBool();
- if (!status)
- {
- qCritical() << "paste.ee reported error:" << QString(object.value("error").toString());
- return false;
- }
- m_pasteLink = object.value("link").toString();
- m_pasteID = object.value("id").toString();
- qDebug() << m_pasteLink;
- return true;
+ auto object = doc.object();
+ auto status = object.value("success").toBool();
+ if (!status)
+ {
+ qCritical() << "paste.ee reported error:" << QString(object.value("error").toString());
+ return false;
+ }
+ m_pasteLink = object.value("link").toString();
+ m_pasteID = object.value("id").toString();
+ qDebug() << m_pasteLink;
+ return true;
}
diff --git a/api/logic/net/PasteUpload.h b/api/logic/net/PasteUpload.h
index 62d2e766..11e05c2e 100644
--- a/api/logic/net/PasteUpload.h
+++ b/api/logic/net/PasteUpload.h
@@ -8,41 +8,42 @@
class MULTIMC_LOGIC_EXPORT PasteUpload : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- PasteUpload(QWidget *window, QString text, QString key = "public");
- virtual ~PasteUpload();
- QString pasteLink()
- {
- return m_pasteLink;
- }
- QString pasteID()
- {
- return m_pasteID;
- }
- int maxSize()
- {
- // 2MB for paste.ee - public
- if(m_key == "public")
- return 1024*1024*2;
- // 12MB for paste.ee - with actual key
- return 1024*1024*12;
- }
- bool validateText();
+ PasteUpload(QWidget *window, QString text, QString key = "public");
+ virtual ~PasteUpload();
+
+ QString pasteLink()
+ {
+ return m_pasteLink;
+ }
+ QString pasteID()
+ {
+ return m_pasteID;
+ }
+ int maxSize()
+ {
+ // 2MB for paste.ee - public
+ if(m_key == "public")
+ return 1024*1024*2;
+ // 12MB for paste.ee - with actual key
+ return 1024*1024*12;
+ }
+ bool validateText();
protected:
- virtual void executeTask();
+ virtual void executeTask();
private:
- bool parseResult(QJsonDocument doc);
- QString m_error;
- QWidget *m_window;
- QString m_pasteID;
- QString m_pasteLink;
- QString m_key;
- QByteArray m_jsonContent;
- std::shared_ptr<QNetworkReply> m_reply;
+ bool parseResult(QJsonDocument doc);
+ QString m_error;
+ QWidget *m_window;
+ QString m_pasteID;
+ QString m_pasteLink;
+ QString m_key;
+ QByteArray m_jsonContent;
+ std::shared_ptr<QNetworkReply> m_reply;
public
slots:
- void downloadError(QNetworkReply::NetworkError);
- void downloadFinished();
+ void downloadError(QNetworkReply::NetworkError);
+ void downloadFinished();
};
diff --git a/api/logic/net/Sink.h b/api/logic/net/Sink.h
index de9b1722..d526895c 100644
--- a/api/logic/net/Sink.h
+++ b/api/logic/net/Sink.h
@@ -9,63 +9,63 @@ namespace Net {
class MULTIMC_LOGIC_EXPORT Sink
{
public: /* con/des */
- Sink() {};
- virtual ~Sink() {};
+ Sink() {};
+ virtual ~Sink() {};
public: /* methods */
- virtual JobStatus init(QNetworkRequest & request) = 0;
- virtual JobStatus write(QByteArray & data) = 0;
- virtual JobStatus abort() = 0;
- virtual JobStatus finalize(QNetworkReply & reply) = 0;
- virtual bool hasLocalData() = 0;
+ virtual JobStatus init(QNetworkRequest & request) = 0;
+ virtual JobStatus write(QByteArray & data) = 0;
+ virtual JobStatus abort() = 0;
+ virtual JobStatus finalize(QNetworkReply & reply) = 0;
+ virtual bool hasLocalData() = 0;
- void addValidator(Validator * validator)
- {
- if(validator)
- {
- validators.push_back(std::shared_ptr<Validator>(validator));
- }
- }
+ void addValidator(Validator * validator)
+ {
+ if(validator)
+ {
+ validators.push_back(std::shared_ptr<Validator>(validator));
+ }
+ }
protected: /* methods */
- bool finalizeAllValidators(QNetworkReply & reply)
- {
- for(auto & validator: validators)
- {
- if(!validator->validate(reply))
- return false;
- }
- return true;
- }
- bool failAllValidators()
- {
- bool success = true;
- for(auto & validator: validators)
- {
- success &= validator->abort();
- }
- return success;
- }
- bool initAllValidators(QNetworkRequest & request)
- {
- for(auto & validator: validators)
- {
- if(!validator->init(request))
- return false;
- }
- return true;
- }
- bool writeAllValidators(QByteArray & data)
- {
- for(auto & validator: validators)
- {
- if(!validator->write(data))
- return false;
- }
- return true;
- }
+ bool finalizeAllValidators(QNetworkReply & reply)
+ {
+ for(auto & validator: validators)
+ {
+ if(!validator->validate(reply))
+ return false;
+ }
+ return true;
+ }
+ bool failAllValidators()
+ {
+ bool success = true;
+ for(auto & validator: validators)
+ {
+ success &= validator->abort();
+ }
+ return success;
+ }
+ bool initAllValidators(QNetworkRequest & request)
+ {
+ for(auto & validator: validators)
+ {
+ if(!validator->init(request))
+ return false;
+ }
+ return true;
+ }
+ bool writeAllValidators(QByteArray & data)
+ {
+ for(auto & validator: validators)
+ {
+ if(!validator->write(data))
+ return false;
+ }
+ return true;
+ }
protected: /* data */
- std::vector<std::shared_ptr<Validator>> validators;
+ std::vector<std::shared_ptr<Validator>> validators;
};
}
diff --git a/api/logic/net/URLConstants.cpp b/api/logic/net/URLConstants.cpp
index bd476b2c..9a4d920b 100644
--- a/api/logic/net/URLConstants.cpp
+++ b/api/logic/net/URLConstants.cpp
@@ -4,12 +4,12 @@ namespace URLConstants {
QString getLegacyJarUrl(QString version)
{
- return "http://" + AWS_DOWNLOAD_VERSIONS + getJarPath(version);
+ return AWS_DOWNLOAD_VERSIONS + getJarPath(version);
}
QString getJarPath(QString version)
{
- return version + "/" + version + ".jar";
+ return version + "/" + version + ".jar";
}
diff --git a/api/logic/net/URLConstants.h b/api/logic/net/URLConstants.h
index 22d128b2..5ff0f794 100644
--- a/api/logic/net/URLConstants.h
+++ b/api/logic/net/URLConstants.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,21 +19,17 @@
namespace URLConstants
{
-const QString AWS_DOWNLOAD_VERSIONS("s3.amazonaws.com/Minecraft.Download/versions/");
-const QString RESOURCE_BASE("resources.download.minecraft.net/");
-const QString LIBRARY_BASE("libraries.minecraft.net/");
-//const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/");
-const QString SKINS_BASE("crafatar.com/skins/");
-const QString AUTH_BASE("authserver.mojang.com/");
-const QString FORGE_LEGACY_URL("http://files.minecraftforge.net/minecraftforge/json");
-const QString FORGE_GRADLE_URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/json");
-const QString MOJANG_STATUS_URL("http://status.mojang.com/check");
-const QString MOJANG_STATUS_NEWS_URL("http://status.mojang.com/news");
-const QString LITELOADER_URL("http://dl.liteloader.com/versions/versions.json");
+const QString AWS_DOWNLOAD_VERSIONS("https://s3.amazonaws.com/Minecraft.Download/versions/");
+const QString RESOURCE_BASE("https://resources.download.minecraft.net/");
+const QString LIBRARY_BASE("https://libraries.minecraft.net/");
+const QString SKINS_BASE("https://crafatar.com/skins/");
+const QString AUTH_BASE("https://authserver.mojang.com/");
+const QString MOJANG_STATUS_URL("https://status.mojang.com/check");
const QString IMGUR_BASE_URL("https://api.imgur.com/3/");
-const QString FMLLIBS_OUR_BASE_URL("http://files.multimc.org/fmllibs/");
-const QString FMLLIBS_FORGE_BASE_URL("http://files.minecraftforge.net/fmllibs/");
-const QString TRANSLATIONS_BASE_URL("http://files.multimc.org/translations/");
+const QString FMLLIBS_OUR_BASE_URL("https://files.multimc.org/fmllibs/");
+const QString FMLLIBS_FORGE_BASE_URL("https://files.minecraftforge.net/fmllibs/");
+const QString TRANSLATIONS_BASE_URL("https://files.multimc.org/translations/");
+const QString FTB_CDN_BASE_URL("https://ftb.forgecdn.net/FTB2/");
QString getJarPath(QString version);
QString getLegacyJarUrl(QString version);
diff --git a/api/logic/net/Validator.h b/api/logic/net/Validator.h
index a390ab28..955412ce 100644
--- a/api/logic/net/Validator.h
+++ b/api/logic/net/Validator.h
@@ -8,13 +8,13 @@ namespace Net {
class MULTIMC_LOGIC_EXPORT Validator
{
public: /* con/des */
- Validator() {};
- virtual ~Validator() {};
+ Validator() {};
+ virtual ~Validator() {};
public: /* methods */
- virtual bool init(QNetworkRequest & request) = 0;
- virtual bool write(QByteArray & data) = 0;
- virtual bool abort() = 0;
- virtual bool validate(QNetworkReply & reply) = 0;
+ virtual bool init(QNetworkRequest & request) = 0;
+ virtual bool write(QByteArray & data) = 0;
+ virtual bool abort() = 0;
+ virtual bool validate(QNetworkReply & reply) = 0;
};
} \ No newline at end of file
diff --git a/api/logic/news/NewsChecker.cpp b/api/logic/news/NewsChecker.cpp
index 0ee3f9da..a68022e8 100644
--- a/api/logic/news/NewsChecker.cpp
+++ b/api/logic/news/NewsChecker.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,110 +22,110 @@
NewsChecker::NewsChecker(const QString& feedUrl)
{
- m_feedUrl = feedUrl;
+ m_feedUrl = feedUrl;
}
void NewsChecker::reloadNews()
{
- // Start a netjob to download the RSS feed and call rssDownloadFinished() when it's done.
- if (isLoadingNews())
- {
- qDebug() << "Ignored request to reload news. Currently reloading already.";
- return;
- }
-
- qDebug() << "Reloading news.";
-
- NetJob* job = new NetJob("News RSS Feed");
- job->addNetAction(Net::Download::makeByteArray(m_feedUrl, &newsData));
- QObject::connect(job, &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished);
- QObject::connect(job, &NetJob::failed, this, &NewsChecker::rssDownloadFailed);
- m_newsNetJob.reset(job);
- job->start();
+ // Start a netjob to download the RSS feed and call rssDownloadFinished() when it's done.
+ if (isLoadingNews())
+ {
+ qDebug() << "Ignored request to reload news. Currently reloading already.";
+ return;
+ }
+
+ qDebug() << "Reloading news.";
+
+ NetJob* job = new NetJob("News RSS Feed");
+ job->addNetAction(Net::Download::makeByteArray(m_feedUrl, &newsData));
+ QObject::connect(job, &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished);
+ QObject::connect(job, &NetJob::failed, this, &NewsChecker::rssDownloadFailed);
+ m_newsNetJob.reset(job);
+ job->start();
}
void NewsChecker::rssDownloadFinished()
{
- // Parse the XML file and process the RSS feed entries.
- qDebug() << "Finished loading RSS feed.";
-
- m_newsNetJob.reset();
- QDomDocument doc;
- {
- // Stuff to store error info in.
- QString errorMsg = "Unknown error.";
- int errorLine = -1;
- int errorCol = -1;
-
- // Parse the XML.
- if (!doc.setContent(newsData, false, &errorMsg, &errorLine, &errorCol))
- {
- QString fullErrorMsg = QString("Error parsing RSS feed XML. %s at %d:%d.").arg(errorMsg, errorLine, errorCol);
- fail(fullErrorMsg);
- newsData.clear();
- return;
- }
- newsData.clear();
- }
-
- // If the parsing succeeded, read it.
- QDomNodeList items = doc.elementsByTagName("item");
- m_newsEntries.clear();
- for (int i = 0; i < items.length(); i++)
- {
- QDomElement element = items.at(i).toElement();
- NewsEntryPtr entry;
- entry.reset(new NewsEntry());
- QString errorMsg = "An unknown error occurred.";
- if (NewsEntry::fromXmlElement(element, entry.get(), &errorMsg))
- {
- qDebug() << "Loaded news entry" << entry->title;
- m_newsEntries.append(entry);
- }
- else
- {
- qWarning() << "Failed to load news entry at index" << i << ":" << errorMsg;
- }
- }
-
- succeed();
+ // Parse the XML file and process the RSS feed entries.
+ qDebug() << "Finished loading RSS feed.";
+
+ m_newsNetJob.reset();
+ QDomDocument doc;
+ {
+ // Stuff to store error info in.
+ QString errorMsg = "Unknown error.";
+ int errorLine = -1;
+ int errorCol = -1;
+
+ // Parse the XML.
+ if (!doc.setContent(newsData, false, &errorMsg, &errorLine, &errorCol))
+ {
+ QString fullErrorMsg = QString("Error parsing RSS feed XML. %s at %d:%d.").arg(errorMsg, errorLine, errorCol);
+ fail(fullErrorMsg);
+ newsData.clear();
+ return;
+ }
+ newsData.clear();
+ }
+
+ // If the parsing succeeded, read it.
+ QDomNodeList items = doc.elementsByTagName("item");
+ m_newsEntries.clear();
+ for (int i = 0; i < items.length(); i++)
+ {
+ QDomElement element = items.at(i).toElement();
+ NewsEntryPtr entry;
+ entry.reset(new NewsEntry());
+ QString errorMsg = "An unknown error occurred.";
+ if (NewsEntry::fromXmlElement(element, entry.get(), &errorMsg))
+ {
+ qDebug() << "Loaded news entry" << entry->title;
+ m_newsEntries.append(entry);
+ }
+ else
+ {
+ qWarning() << "Failed to load news entry at index" << i << ":" << errorMsg;
+ }
+ }
+
+ succeed();
}
void NewsChecker::rssDownloadFailed(QString reason)
{
- // Set an error message and fail.
- fail(tr("Failed to load news RSS feed:\n%1").arg(reason));
+ // Set an error message and fail.
+ fail(tr("Failed to load news RSS feed:\n%1").arg(reason));
}
QList<NewsEntryPtr> NewsChecker::getNewsEntries() const
{
- return m_newsEntries;
+ return m_newsEntries;
}
bool NewsChecker::isLoadingNews() const
{
- return m_newsNetJob.get() != nullptr;
+ return m_newsNetJob.get() != nullptr;
}
QString NewsChecker::getLastLoadErrorMsg() const
{
- return m_lastLoadError;
+ return m_lastLoadError;
}
void NewsChecker::succeed()
{
- m_lastLoadError = "";
- qDebug() << "News loading succeeded.";
- m_newsNetJob.reset();
- emit newsLoaded();
+ m_lastLoadError = "";
+ qDebug() << "News loading succeeded.";
+ m_newsNetJob.reset();
+ emit newsLoaded();
}
void NewsChecker::fail(const QString& errorMsg)
{
- m_lastLoadError = errorMsg;
- qDebug() << "Failed to load news:" << errorMsg;
- m_newsNetJob.reset();
- emit newsLoadingFailed(errorMsg);
+ m_lastLoadError = errorMsg;
+ qDebug() << "Failed to load news:" << errorMsg;
+ m_newsNetJob.reset();
+ emit newsLoadingFailed(errorMsg);
}
diff --git a/api/logic/news/NewsChecker.h b/api/logic/news/NewsChecker.h
index 44f2534a..5983eeb3 100644
--- a/api/logic/news/NewsChecker.h
+++ b/api/logic/news/NewsChecker.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,79 +27,79 @@
class MULTIMC_LOGIC_EXPORT NewsChecker : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- /*!
- * Constructs a news reader to read from the given RSS feed URL.
- */
- NewsChecker(const QString& feedUrl);
-
- /*!
- * Returns the error message for the last time the news was loaded.
- * Empty string if the last load was successful.
- */
- QString getLastLoadErrorMsg() const;
-
- /*!
- * Returns true if the news has been loaded successfully.
- */
- bool isNewsLoaded() const;
-
- //! True if the news is currently loading. If true, reloadNews() will do nothing.
- bool isLoadingNews() const;
-
- /*!
- * Returns a list of news entries.
- */
- QList<NewsEntryPtr> getNewsEntries() const;
-
- /*!
- * Reloads the news from the website's RSS feed.
- * If the news is already loading, this does nothing.
- */
- void Q_SLOT reloadNews();
+ /*!
+ * Constructs a news reader to read from the given RSS feed URL.
+ */
+ NewsChecker(const QString& feedUrl);
+
+ /*!
+ * Returns the error message for the last time the news was loaded.
+ * Empty string if the last load was successful.
+ */
+ QString getLastLoadErrorMsg() const;
+
+ /*!
+ * Returns true if the news has been loaded successfully.
+ */
+ bool isNewsLoaded() const;
+
+ //! True if the news is currently loading. If true, reloadNews() will do nothing.
+ bool isLoadingNews() const;
+
+ /*!
+ * Returns a list of news entries.
+ */
+ QList<NewsEntryPtr> getNewsEntries() const;
+
+ /*!
+ * Reloads the news from the website's RSS feed.
+ * If the news is already loading, this does nothing.
+ */
+ void Q_SLOT reloadNews();
signals:
- /*!
- * Signal fired after the news has finished loading.
- */
- void newsLoaded();
+ /*!
+ * Signal fired after the news has finished loading.
+ */
+ void newsLoaded();
- /*!
- * Signal fired after the news fails to load.
- */
- void newsLoadingFailed(QString errorMsg);
+ /*!
+ * Signal fired after the news fails to load.
+ */
+ void newsLoadingFailed(QString errorMsg);
protected slots:
- void rssDownloadFinished();
- void rssDownloadFailed(QString reason);
+ void rssDownloadFinished();
+ void rssDownloadFailed(QString reason);
protected: /* data */
- //! The URL for the RSS feed to fetch.
- QString m_feedUrl;
+ //! The URL for the RSS feed to fetch.
+ QString m_feedUrl;
- //! List of news entries.
- QList<NewsEntryPtr> m_newsEntries;
+ //! List of news entries.
+ QList<NewsEntryPtr> m_newsEntries;
- //! The network job to use to load the news.
- NetJobPtr m_newsNetJob;
+ //! The network job to use to load the news.
+ NetJobPtr m_newsNetJob;
- //! True if news has been loaded.
- bool m_loadedNews;
+ //! True if news has been loaded.
+ bool m_loadedNews;
- QByteArray newsData;
+ QByteArray newsData;
- /*!
- * Gets the error message that was given last time the news was loaded.
- * If the last news load succeeded, this will be an empty string.
- */
- QString m_lastLoadError;
+ /*!
+ * Gets the error message that was given last time the news was loaded.
+ * If the last news load succeeded, this will be an empty string.
+ */
+ QString m_lastLoadError;
protected slots:
- /// Emits newsLoaded() and sets m_lastLoadError to empty string.
- void succeed();
+ /// Emits newsLoaded() and sets m_lastLoadError to empty string.
+ void succeed();
- /// Emits newsLoadingFailed() and sets m_lastLoadError to the given message.
- void fail(const QString& errorMsg);
+ /// Emits newsLoadingFailed() and sets m_lastLoadError to the given message.
+ void fail(const QString& errorMsg);
};
diff --git a/api/logic/news/NewsEntry.cpp b/api/logic/news/NewsEntry.cpp
index 4377f766..2edffb1b 100644
--- a/api/logic/news/NewsEntry.cpp
+++ b/api/logic/news/NewsEntry.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,23 +19,23 @@
#include <QVariant>
NewsEntry::NewsEntry(QObject* parent) :
- QObject(parent)
+ QObject(parent)
{
- this->title = tr("Untitled");
- this->content = tr("No content.");
- this->link = "";
- this->author = tr("Unknown Author");
- this->pubDate = QDateTime::currentDateTime();
+ this->title = tr("Untitled");
+ this->content = tr("No content.");
+ this->link = "";
+ this->author = tr("Unknown Author");
+ this->pubDate = QDateTime::currentDateTime();
}
NewsEntry::NewsEntry(const QString& title, const QString& content, const QString& link, const QString& author, const QDateTime& pubDate, QObject* parent) :
- QObject(parent)
+ QObject(parent)
{
- this->title = title;
- this->content = content;
- this->link = link;
- this->author = author;
- this->pubDate = pubDate;
+ this->title = title;
+ this->content = content;
+ this->link = link;
+ this->author = author;
+ this->pubDate = pubDate;
}
/*!
@@ -43,35 +43,35 @@ NewsEntry::NewsEntry(const QString& title, const QString& content, const QString
*/
inline QString childValue(const QDomElement& element, const QString& childName, QString defaultVal="")
{
- QDomNodeList nodes = element.elementsByTagName(childName);
- if (nodes.count() > 0)
- {
- QDomElement element = nodes.at(0).toElement();
- return element.text();
- }
- else
- {
- return defaultVal;
- }
+ QDomNodeList nodes = element.elementsByTagName(childName);
+ if (nodes.count() > 0)
+ {
+ QDomElement element = nodes.at(0).toElement();
+ return element.text();
+ }
+ else
+ {
+ return defaultVal;
+ }
}
bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg)
{
- QString title = childValue(element, "title", tr("Untitled"));
- QString content = childValue(element, "description", tr("No content."));
- QString link = childValue(element, "link");
- QString author = childValue(element, "dc:creator", tr("Unknown Author"));
- QString pubDateStr = childValue(element, "pubDate");
+ QString title = childValue(element, "title", tr("Untitled"));
+ QString content = childValue(element, "description", tr("No content."));
+ QString link = childValue(element, "link");
+ QString author = childValue(element, "dc:creator", tr("Unknown Author"));
+ QString pubDateStr = childValue(element, "pubDate");
- // FIXME: For now, we're just ignoring timezones. We assume that all time zones in the RSS feed are the same.
- QString dateFormat("ddd, dd MMM yyyy hh:mm:ss");
- QDateTime pubDate = QDateTime::fromString(pubDateStr, dateFormat);
+ // FIXME: For now, we're just ignoring timezones. We assume that all time zones in the RSS feed are the same.
+ QString dateFormat("ddd, dd MMM yyyy hh:mm:ss");
+ QDateTime pubDate = QDateTime::fromString(pubDateStr, dateFormat);
- entry->title = title;
- entry->content = content;
- entry->link = link;
- entry->author = author;
- entry->pubDate = pubDate;
- return true;
+ entry->title = title;
+ entry->content = content;
+ entry->link = link;
+ entry->author = author;
+ entry->pubDate = pubDate;
+ return true;
}
diff --git a/api/logic/news/NewsEntry.h b/api/logic/news/NewsEntry.h
index 16a17f9c..94a9e761 100644
--- a/api/logic/news/NewsEntry.h
+++ b/api/logic/news/NewsEntry.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,41 +24,41 @@
class NewsEntry : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- /*!
- * Constructs an empty news entry.
- */
- explicit NewsEntry(QObject* parent=0);
+ /*!
+ * Constructs an empty news entry.
+ */
+ explicit NewsEntry(QObject* parent=0);
- /*!
- * Constructs a new news entry.
- * Note that content may contain HTML.
- */
- NewsEntry(const QString& title, const QString& content, const QString& link, const QString& author, const QDateTime& pubDate, QObject* parent=0);
+ /*!
+ * Constructs a new news entry.
+ * Note that content may contain HTML.
+ */
+ NewsEntry(const QString& title, const QString& content, const QString& link, const QString& author, const QDateTime& pubDate, QObject* parent=0);
- /*!
- * Attempts to load information from the given XML element into the given news entry pointer.
- * If this fails, the function will return false and store an error message in the errorMsg pointer.
- */
- static bool fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg=0);
+ /*!
+ * Attempts to load information from the given XML element into the given news entry pointer.
+ * If this fails, the function will return false and store an error message in the errorMsg pointer.
+ */
+ static bool fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg=0);
- //! The post title.
- QString title;
+ //! The post title.
+ QString title;
- //! The post's content. May contain HTML.
- QString content;
+ //! The post's content. May contain HTML.
+ QString content;
- //! URL to the post.
- QString link;
+ //! URL to the post.
+ QString link;
- //! The post's author.
- QString author;
-
- //! The date and time that this post was published.
- QDateTime pubDate;
+ //! The post's author.
+ QString author;
+
+ //! The date and time that this post was published.
+ QDateTime pubDate;
};
typedef std::shared_ptr<NewsEntry> NewsEntryPtr;
diff --git a/api/logic/notifications/NotificationChecker.cpp b/api/logic/notifications/NotificationChecker.cpp
index 6d006c31..8209c28b 100644
--- a/api/logic/notifications/NotificationChecker.cpp
+++ b/api/logic/notifications/NotificationChecker.cpp
@@ -10,120 +10,120 @@
NotificationChecker::NotificationChecker(QObject *parent)
- : QObject(parent)
+ : QObject(parent)
{
}
void NotificationChecker::setNotificationsUrl(const QUrl &notificationsUrl)
{
- m_notificationsUrl = notificationsUrl;
+ m_notificationsUrl = notificationsUrl;
}
void NotificationChecker::setApplicationChannel(QString channel)
{
- m_appVersionChannel = channel;
+ m_appVersionChannel = channel;
}
void NotificationChecker::setApplicationFullVersion(QString version)
{
- m_appFullVersion = version;
+ m_appFullVersion = version;
}
void NotificationChecker::setApplicationPlatform(QString platform)
{
- m_appPlatform = platform;
+ m_appPlatform = platform;
}
QList<NotificationChecker::NotificationEntry> NotificationChecker::notificationEntries() const
{
- return m_entries;
+ return m_entries;
}
void NotificationChecker::checkForNotifications()
{
- if (!m_notificationsUrl.isValid())
- {
- qCritical() << "Failed to check for notifications. No notifications URL set."
- << "If you'd like to use MultiMC's notification system, please pass the "
- "URL to CMake at compile time.";
- return;
- }
- if (m_checkJob)
- {
- return;
- }
- m_checkJob.reset(new NetJob("Checking for notifications"));
- auto entry = ENV.metacache()->resolveEntry("root", "notifications.json");
- entry->setStale(true);
- m_checkJob->addNetAction(m_download = Net::Download::makeCached(m_notificationsUrl, entry));
- connect(m_download.get(), &Net::Download::succeeded, this, &NotificationChecker::downloadSucceeded);
- m_checkJob->start();
+ if (!m_notificationsUrl.isValid())
+ {
+ qCritical() << "Failed to check for notifications. No notifications URL set."
+ << "If you'd like to use MultiMC's notification system, please pass the "
+ "URL to CMake at compile time.";
+ return;
+ }
+ if (m_checkJob)
+ {
+ return;
+ }
+ m_checkJob.reset(new NetJob("Checking for notifications"));
+ auto entry = ENV.metacache()->resolveEntry("root", "notifications.json");
+ entry->setStale(true);
+ m_checkJob->addNetAction(m_download = Net::Download::makeCached(m_notificationsUrl, entry));
+ connect(m_download.get(), &Net::Download::succeeded, this, &NotificationChecker::downloadSucceeded);
+ m_checkJob->start();
}
void NotificationChecker::downloadSucceeded(int)
{
- m_entries.clear();
-
- QFile file(m_download->getTargetFilepath());
- if (file.open(QFile::ReadOnly))
- {
- QJsonArray root = QJsonDocument::fromJson(file.readAll()).array();
- for (auto it = root.begin(); it != root.end(); ++it)
- {
- QJsonObject obj = (*it).toObject();
- NotificationEntry entry;
- entry.id = obj.value("id").toDouble();
- entry.message = obj.value("message").toString();
- entry.channel = obj.value("channel").toString();
- entry.platform = obj.value("platform").toString();
- entry.from = obj.value("from").toString();
- entry.to = obj.value("to").toString();
- const QString type = obj.value("type").toString("critical");
- if (type == "critical")
- {
- entry.type = NotificationEntry::Critical;
- }
- else if (type == "warning")
- {
- entry.type = NotificationEntry::Warning;
- }
- else if (type == "information")
- {
- entry.type = NotificationEntry::Information;
- }
- if(entryApplies(entry))
- m_entries.append(entry);
- }
- }
-
- m_checkJob.reset();
-
- emit notificationCheckFinished();
+ m_entries.clear();
+
+ QFile file(m_download->getTargetFilepath());
+ if (file.open(QFile::ReadOnly))
+ {
+ QJsonArray root = QJsonDocument::fromJson(file.readAll()).array();
+ for (auto it = root.begin(); it != root.end(); ++it)
+ {
+ QJsonObject obj = (*it).toObject();
+ NotificationEntry entry;
+ entry.id = obj.value("id").toDouble();
+ entry.message = obj.value("message").toString();
+ entry.channel = obj.value("channel").toString();
+ entry.platform = obj.value("platform").toString();
+ entry.from = obj.value("from").toString();
+ entry.to = obj.value("to").toString();
+ const QString type = obj.value("type").toString("critical");
+ if (type == "critical")
+ {
+ entry.type = NotificationEntry::Critical;
+ }
+ else if (type == "warning")
+ {
+ entry.type = NotificationEntry::Warning;
+ }
+ else if (type == "information")
+ {
+ entry.type = NotificationEntry::Information;
+ }
+ if(entryApplies(entry))
+ m_entries.append(entry);
+ }
+ }
+
+ m_checkJob.reset();
+
+ emit notificationCheckFinished();
}
bool versionLessThan(const QString &v1, const QString &v2)
{
- QStringList l1 = v1.split('.');
- QStringList l2 = v2.split('.');
- while (!l1.isEmpty() && !l2.isEmpty())
- {
- int one = l1.isEmpty() ? 0 : l1.takeFirst().toInt();
- int two = l2.isEmpty() ? 0 : l2.takeFirst().toInt();
- if (one != two)
- {
- return one < two;
- }
- }
- return false;
+ QStringList l1 = v1.split('.');
+ QStringList l2 = v2.split('.');
+ while (!l1.isEmpty() && !l2.isEmpty())
+ {
+ int one = l1.isEmpty() ? 0 : l1.takeFirst().toInt();
+ int two = l2.isEmpty() ? 0 : l2.takeFirst().toInt();
+ if (one != two)
+ {
+ return one < two;
+ }
+ }
+ return false;
}
bool NotificationChecker::entryApplies(const NotificationChecker::NotificationEntry& entry) const
{
- bool channelApplies = entry.channel.isEmpty() || entry.channel == m_appVersionChannel;
- bool platformApplies = entry.platform.isEmpty() || entry.platform == m_appPlatform;
- bool fromApplies =
- entry.from.isEmpty() || entry.from == m_appFullVersion || !versionLessThan(m_appFullVersion, entry.from);
- bool toApplies =
- entry.to.isEmpty() || entry.to == m_appFullVersion || !versionLessThan(entry.to, m_appFullVersion);
- return channelApplies && platformApplies && fromApplies && toApplies;
+ bool channelApplies = entry.channel.isEmpty() || entry.channel == m_appVersionChannel;
+ bool platformApplies = entry.platform.isEmpty() || entry.platform == m_appPlatform;
+ bool fromApplies =
+ entry.from.isEmpty() || entry.from == m_appFullVersion || !versionLessThan(m_appFullVersion, entry.from);
+ bool toApplies =
+ entry.to.isEmpty() || entry.to == m_appFullVersion || !versionLessThan(entry.to, m_appFullVersion);
+ return channelApplies && platformApplies && fromApplies && toApplies;
}
diff --git a/api/logic/notifications/NotificationChecker.h b/api/logic/notifications/NotificationChecker.h
index c8e831d5..4b1b893d 100644
--- a/api/logic/notifications/NotificationChecker.h
+++ b/api/logic/notifications/NotificationChecker.h
@@ -9,55 +9,55 @@
class MULTIMC_LOGIC_EXPORT NotificationChecker : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit NotificationChecker(QObject *parent = 0);
-
- void setNotificationsUrl(const QUrl &notificationsUrl);
- void setApplicationPlatform(QString platform);
- void setApplicationChannel(QString channel);
- void setApplicationFullVersion(QString version);
-
- struct NotificationEntry
- {
- int id;
- QString message;
- enum
- {
- Critical,
- Warning,
- Information
- } type;
- QString channel;
- QString platform;
- QString from;
- QString to;
- };
-
- QList<NotificationEntry> notificationEntries() const;
+ explicit NotificationChecker(QObject *parent = 0);
+
+ void setNotificationsUrl(const QUrl &notificationsUrl);
+ void setApplicationPlatform(QString platform);
+ void setApplicationChannel(QString channel);
+ void setApplicationFullVersion(QString version);
+
+ struct NotificationEntry
+ {
+ int id;
+ QString message;
+ enum
+ {
+ Critical,
+ Warning,
+ Information
+ } type;
+ QString channel;
+ QString platform;
+ QString from;
+ QString to;
+ };
+
+ QList<NotificationEntry> notificationEntries() const;
public
slots:
- void checkForNotifications();
+ void checkForNotifications();
private
slots:
- void downloadSucceeded(int);
+ void downloadSucceeded(int);
signals:
- void notificationCheckFinished();
+ void notificationCheckFinished();
private:
- bool entryApplies(const NotificationEntry &entry) const;
+ bool entryApplies(const NotificationEntry &entry) const;
private:
- QList<NotificationEntry> m_entries;
- QUrl m_notificationsUrl;
- NetJobPtr m_checkJob;
- Net::Download::Ptr m_download;
-
- QString m_appVersionChannel;
- QString m_appPlatform;
- QString m_appFullVersion;
+ QList<NotificationEntry> m_entries;
+ QUrl m_notificationsUrl;
+ NetJobPtr m_checkJob;
+ Net::Download::Ptr m_download;
+
+ QString m_appVersionChannel;
+ QString m_appPlatform;
+ QString m_appFullVersion;
};
diff --git a/api/logic/pathmatcher/FSTreeMatcher.h b/api/logic/pathmatcher/FSTreeMatcher.h
index a5bed57c..361924af 100644
--- a/api/logic/pathmatcher/FSTreeMatcher.h
+++ b/api/logic/pathmatcher/FSTreeMatcher.h
@@ -7,15 +7,15 @@
class FSTreeMatcher : public IPathMatcher
{
public:
- virtual ~FSTreeMatcher() {};
- FSTreeMatcher(SeparatorPrefixTree<'/'> & tree) : m_fsTree(tree)
- {
- }
+ virtual ~FSTreeMatcher() {};
+ FSTreeMatcher(SeparatorPrefixTree<'/'> & tree) : m_fsTree(tree)
+ {
+ }
- virtual bool matches(const QString &string) const override
- {
- return m_fsTree.covers(string);
- }
+ virtual bool matches(const QString &string) const override
+ {
+ return m_fsTree.covers(string);
+ }
- SeparatorPrefixTree<'/'> & m_fsTree;
+ SeparatorPrefixTree<'/'> & m_fsTree;
};
diff --git a/api/logic/pathmatcher/IPathMatcher.h b/api/logic/pathmatcher/IPathMatcher.h
index 1d410947..b60621c9 100644
--- a/api/logic/pathmatcher/IPathMatcher.h
+++ b/api/logic/pathmatcher/IPathMatcher.h
@@ -4,9 +4,9 @@
class IPathMatcher
{
public:
- typedef std::shared_ptr<IPathMatcher> Ptr;
+ typedef std::shared_ptr<IPathMatcher> Ptr;
public:
- virtual ~IPathMatcher(){};
- virtual bool matches(const QString &string) const = 0;
+ virtual ~IPathMatcher(){};
+ virtual bool matches(const QString &string) const = 0;
};
diff --git a/api/logic/pathmatcher/MultiMatcher.h b/api/logic/pathmatcher/MultiMatcher.h
index 91f70aa4..8bc1b6ee 100644
--- a/api/logic/pathmatcher/MultiMatcher.h
+++ b/api/logic/pathmatcher/MultiMatcher.h
@@ -5,27 +5,27 @@
class MultiMatcher : public IPathMatcher
{
public:
- virtual ~MultiMatcher() {};
- MultiMatcher()
- {
- }
- MultiMatcher &add(Ptr add)
- {
- m_matchers.append(add);
- return *this;
- }
+ virtual ~MultiMatcher() {};
+ MultiMatcher()
+ {
+ }
+ MultiMatcher &add(Ptr add)
+ {
+ m_matchers.append(add);
+ return *this;
+ }
- virtual bool matches(const QString &string) const override
- {
- for(auto iter: m_matchers)
- {
- if(iter->matches(string))
- {
- return true;
- }
- }
- return false;
- }
+ virtual bool matches(const QString &string) const override
+ {
+ for(auto iter: m_matchers)
+ {
+ if(iter->matches(string))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
- QList<Ptr> m_matchers;
+ QList<Ptr> m_matchers;
};
diff --git a/api/logic/pathmatcher/RegexpMatcher.h b/api/logic/pathmatcher/RegexpMatcher.h
index da552123..825d488c 100644
--- a/api/logic/pathmatcher/RegexpMatcher.h
+++ b/api/logic/pathmatcher/RegexpMatcher.h
@@ -4,39 +4,39 @@
class RegexpMatcher : public IPathMatcher
{
public:
- virtual ~RegexpMatcher() {};
- RegexpMatcher(const QString &regexp)
- {
- m_regexp.setPattern(regexp);
- m_onlyFilenamePart = !regexp.contains('/');
- }
+ virtual ~RegexpMatcher() {};
+ RegexpMatcher(const QString &regexp)
+ {
+ m_regexp.setPattern(regexp);
+ m_onlyFilenamePart = !regexp.contains('/');
+ }
- RegexpMatcher &caseSensitive(bool cs = true)
- {
- if(cs)
- {
- m_regexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
- }
- else
- {
- m_regexp.setPatternOptions(QRegularExpression::NoPatternOption);
- }
- return *this;
- }
+ RegexpMatcher &caseSensitive(bool cs = true)
+ {
+ if(cs)
+ {
+ m_regexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+ }
+ else
+ {
+ m_regexp.setPatternOptions(QRegularExpression::NoPatternOption);
+ }
+ return *this;
+ }
- virtual bool matches(const QString &string) const override
- {
- if(m_onlyFilenamePart)
- {
- auto slash = string.lastIndexOf('/');
- if(slash != -1)
- {
- auto part = string.mid(slash + 1);
- return m_regexp.match(part).hasMatch();
- }
- }
- return m_regexp.match(string).hasMatch();
- }
- QRegularExpression m_regexp;
- bool m_onlyFilenamePart = false;
+ virtual bool matches(const QString &string) const override
+ {
+ if(m_onlyFilenamePart)
+ {
+ auto slash = string.lastIndexOf('/');
+ if(slash != -1)
+ {
+ auto part = string.mid(slash + 1);
+ return m_regexp.match(part).hasMatch();
+ }
+ }
+ return m_regexp.match(string).hasMatch();
+ }
+ QRegularExpression m_regexp;
+ bool m_onlyFilenamePart = false;
};
diff --git a/api/logic/screenshots/ImgurAlbumCreation.cpp b/api/logic/screenshots/ImgurAlbumCreation.cpp
index 3724e3df..3d32f597 100644
--- a/api/logic/screenshots/ImgurAlbumCreation.cpp
+++ b/api/logic/screenshots/ImgurAlbumCreation.cpp
@@ -12,77 +12,77 @@
ImgurAlbumCreation::ImgurAlbumCreation(QList<ScreenshotPtr> screenshots) : NetAction(), m_screenshots(screenshots)
{
- m_url = URLConstants::IMGUR_BASE_URL + "album.json";
- m_status = Job_NotStarted;
+ m_url = URLConstants::IMGUR_BASE_URL + "album.json";
+ m_status = Job_NotStarted;
}
void ImgurAlbumCreation::start()
{
- m_status = Job_InProgress;
- QNetworkRequest request(m_url);
- request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- request.setRawHeader("Authorization", "Client-ID 5b97b0713fba4a3");
- request.setRawHeader("Accept", "application/json");
+ m_status = Job_InProgress;
+ QNetworkRequest request(m_url);
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+ request.setRawHeader("Authorization", "Client-ID 5b97b0713fba4a3");
+ request.setRawHeader("Accept", "application/json");
- QStringList hashes;
- for (auto shot : m_screenshots)
- {
- hashes.append(shot->m_imgurDeleteHash);
- }
+ QStringList hashes;
+ for (auto shot : m_screenshots)
+ {
+ hashes.append(shot->m_imgurDeleteHash);
+ }
- const QByteArray data = "deletehashes=" + hashes.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden";
+ const QByteArray data = "deletehashes=" + hashes.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden";
- QNetworkReply *rep = ENV.qnam().post(request, data);
+ QNetworkReply *rep = ENV.qnam().post(request, data);
- m_reply.reset(rep);
- connect(rep, &QNetworkReply::uploadProgress, this, &ImgurAlbumCreation::downloadProgress);
- connect(rep, &QNetworkReply::finished, this, &ImgurAlbumCreation::downloadFinished);
- connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
+ m_reply.reset(rep);
+ connect(rep, &QNetworkReply::uploadProgress, this, &ImgurAlbumCreation::downloadProgress);
+ connect(rep, &QNetworkReply::finished, this, &ImgurAlbumCreation::downloadFinished);
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
}
void ImgurAlbumCreation::downloadError(QNetworkReply::NetworkError error)
{
- qDebug() << m_reply->errorString();
- m_status = Job_Failed;
+ qDebug() << m_reply->errorString();
+ m_status = Job_Failed;
}
void ImgurAlbumCreation::downloadFinished()
{
- if (m_status != Job_Failed)
- {
- QByteArray data = m_reply->readAll();
- m_reply.reset();
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if (jsonError.error != QJsonParseError::NoError)
- {
- qDebug() << jsonError.errorString();
- emit failed(m_index_within_job);
- return;
- }
- auto object = doc.object();
- if (!object.value("success").toBool())
- {
- qDebug() << doc.toJson();
- emit failed(m_index_within_job);
- return;
- }
- m_deleteHash = object.value("data").toObject().value("deletehash").toString();
- m_id = object.value("data").toObject().value("id").toString();
- m_status = Job_Finished;
- emit succeeded(m_index_within_job);
- return;
- }
- else
- {
- qDebug() << m_reply->readAll();
- m_reply.reset();
- emit failed(m_index_within_job);
- return;
- }
+ if (m_status != Job_Failed)
+ {
+ QByteArray data = m_reply->readAll();
+ m_reply.reset();
+ QJsonParseError jsonError;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ qDebug() << jsonError.errorString();
+ emit failed(m_index_within_job);
+ return;
+ }
+ auto object = doc.object();
+ if (!object.value("success").toBool())
+ {
+ qDebug() << doc.toJson();
+ emit failed(m_index_within_job);
+ return;
+ }
+ m_deleteHash = object.value("data").toObject().value("deletehash").toString();
+ m_id = object.value("data").toObject().value("id").toString();
+ m_status = Job_Finished;
+ emit succeeded(m_index_within_job);
+ return;
+ }
+ else
+ {
+ qDebug() << m_reply->readAll();
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
}
void ImgurAlbumCreation::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
- m_total_progress = bytesTotal;
- m_progress = bytesReceived;
- emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
}
diff --git a/api/logic/screenshots/ImgurAlbumCreation.h b/api/logic/screenshots/ImgurAlbumCreation.h
index 469174e4..55478021 100644
--- a/api/logic/screenshots/ImgurAlbumCreation.h
+++ b/api/logic/screenshots/ImgurAlbumCreation.h
@@ -8,37 +8,37 @@ typedef std::shared_ptr<class ImgurAlbumCreation> ImgurAlbumCreationPtr;
class MULTIMC_LOGIC_EXPORT ImgurAlbumCreation : public NetAction
{
public:
- explicit ImgurAlbumCreation(QList<ScreenshotPtr> screenshots);
- static ImgurAlbumCreationPtr make(QList<ScreenshotPtr> screenshots)
- {
- return ImgurAlbumCreationPtr(new ImgurAlbumCreation(screenshots));
- }
+ explicit ImgurAlbumCreation(QList<ScreenshotPtr> screenshots);
+ static ImgurAlbumCreationPtr make(QList<ScreenshotPtr> screenshots)
+ {
+ return ImgurAlbumCreationPtr(new ImgurAlbumCreation(screenshots));
+ }
- QString deleteHash() const
- {
- return m_deleteHash;
- }
- QString id() const
- {
- return m_id;
- }
+ QString deleteHash() const
+ {
+ return m_deleteHash;
+ }
+ QString id() const
+ {
+ return m_id;
+ }
protected
slots:
- virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
- virtual void downloadError(QNetworkReply::NetworkError error);
- virtual void downloadFinished();
- virtual void downloadReadyRead()
- {
- }
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ virtual void downloadError(QNetworkReply::NetworkError error);
+ virtual void downloadFinished();
+ virtual void downloadReadyRead()
+ {
+ }
public
slots:
- virtual void start();
+ virtual void start();
private:
- QList<ScreenshotPtr> m_screenshots;
+ QList<ScreenshotPtr> m_screenshots;
- QString m_deleteHash;
- QString m_id;
+ QString m_deleteHash;
+ QString m_id;
};
diff --git a/api/logic/screenshots/ImgurUpload.cpp b/api/logic/screenshots/ImgurUpload.cpp
index 659879f6..74165869 100644
--- a/api/logic/screenshots/ImgurUpload.cpp
+++ b/api/logic/screenshots/ImgurUpload.cpp
@@ -14,101 +14,101 @@
ImgurUpload::ImgurUpload(ScreenshotPtr shot) : NetAction(), m_shot(shot)
{
- m_url = URLConstants::IMGUR_BASE_URL + "upload.json";
- m_status = Job_NotStarted;
+ m_url = URLConstants::IMGUR_BASE_URL + "upload.json";
+ m_status = Job_NotStarted;
}
void ImgurUpload::start()
{
- finished = false;
- m_status = Job_InProgress;
- QNetworkRequest request(m_url);
- request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
- request.setRawHeader("Authorization", "Client-ID 5b97b0713fba4a3");
- request.setRawHeader("Accept", "application/json");
+ finished = false;
+ m_status = Job_InProgress;
+ QNetworkRequest request(m_url);
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
+ request.setRawHeader("Authorization", "Client-ID 5b97b0713fba4a3");
+ request.setRawHeader("Accept", "application/json");
- QFile f(m_shot->m_file.absoluteFilePath());
- if (!f.open(QFile::ReadOnly))
- {
- emit failed(m_index_within_job);
- return;
- }
+ QFile f(m_shot->m_file.absoluteFilePath());
+ if (!f.open(QFile::ReadOnly))
+ {
+ emit failed(m_index_within_job);
+ return;
+ }
- QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
- QHttpPart filePart;
- filePart.setBody(f.readAll().toBase64());
- filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
- filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\"");
- multipart->append(filePart);
- QHttpPart typePart;
- typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\"");
- typePart.setBody("base64");
- multipart->append(typePart);
- QHttpPart namePart;
- namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\"");
- namePart.setBody(m_shot->m_file.baseName().toUtf8());
- multipart->append(namePart);
+ QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
+ QHttpPart filePart;
+ filePart.setBody(f.readAll().toBase64());
+ filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
+ filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\"");
+ multipart->append(filePart);
+ QHttpPart typePart;
+ typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\"");
+ typePart.setBody("base64");
+ multipart->append(typePart);
+ QHttpPart namePart;
+ namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\"");
+ namePart.setBody(m_shot->m_file.baseName().toUtf8());
+ multipart->append(namePart);
- QNetworkReply *rep = ENV.qnam().post(request, multipart);
+ QNetworkReply *rep = ENV.qnam().post(request, multipart);
- m_reply.reset(rep);
- connect(rep, &QNetworkReply::uploadProgress, this, &ImgurUpload::downloadProgress);
- connect(rep, &QNetworkReply::finished, this, &ImgurUpload::downloadFinished);
- connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
- SLOT(downloadError(QNetworkReply::NetworkError)));
+ m_reply.reset(rep);
+ connect(rep, &QNetworkReply::uploadProgress, this, &ImgurUpload::downloadProgress);
+ connect(rep, &QNetworkReply::finished, this, &ImgurUpload::downloadFinished);
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ SLOT(downloadError(QNetworkReply::NetworkError)));
}
void ImgurUpload::downloadError(QNetworkReply::NetworkError error)
{
- qCritical() << "ImgurUpload failed with error" << m_reply->errorString() << "Server reply:\n" << m_reply->readAll();
- if(finished)
- {
- qCritical() << "Double finished ImgurUpload!";
- return;
- }
- m_status = Job_Failed;
- finished = true;
- m_reply.reset();
- emit failed(m_index_within_job);
+ qCritical() << "ImgurUpload failed with error" << m_reply->errorString() << "Server reply:\n" << m_reply->readAll();
+ if(finished)
+ {
+ qCritical() << "Double finished ImgurUpload!";
+ return;
+ }
+ m_status = Job_Failed;
+ finished = true;
+ m_reply.reset();
+ emit failed(m_index_within_job);
}
void ImgurUpload::downloadFinished()
{
- if(finished)
- {
- qCritical() << "Double finished ImgurUpload!";
- return;
- }
- QByteArray data = m_reply->readAll();
- m_reply.reset();
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if (jsonError.error != QJsonParseError::NoError)
- {
- qDebug() << "imgur server did not reply with JSON" << jsonError.errorString();
- finished = true;
- m_reply.reset();
- emit failed(m_index_within_job);
- return;
- }
- auto object = doc.object();
- if (!object.value("success").toBool())
- {
- qDebug() << "Screenshot upload not successful:" << doc.toJson();
- finished = true;
- m_reply.reset();
- emit failed(m_index_within_job);
- return;
- }
- m_shot->m_imgurId = object.value("data").toObject().value("id").toString();
- m_shot->m_url = object.value("data").toObject().value("link").toString();
- m_shot->m_imgurDeleteHash = object.value("data").toObject().value("deletehash").toString();
- m_status = Job_Finished;
- finished = true;
- emit succeeded(m_index_within_job);
- return;
+ if(finished)
+ {
+ qCritical() << "Double finished ImgurUpload!";
+ return;
+ }
+ QByteArray data = m_reply->readAll();
+ m_reply.reset();
+ QJsonParseError jsonError;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ qDebug() << "imgur server did not reply with JSON" << jsonError.errorString();
+ finished = true;
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
+ auto object = doc.object();
+ if (!object.value("success").toBool())
+ {
+ qDebug() << "Screenshot upload not successful:" << doc.toJson();
+ finished = true;
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
+ m_shot->m_imgurId = object.value("data").toObject().value("id").toString();
+ m_shot->m_url = object.value("data").toObject().value("link").toString();
+ m_shot->m_imgurDeleteHash = object.value("data").toObject().value("deletehash").toString();
+ m_status = Job_Finished;
+ finished = true;
+ emit succeeded(m_index_within_job);
+ return;
}
void ImgurUpload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
- m_total_progress = bytesTotal;
- m_progress = bytesReceived;
- emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
}
diff --git a/api/logic/screenshots/ImgurUpload.h b/api/logic/screenshots/ImgurUpload.h
index 0a766b8f..d79807f2 100644
--- a/api/logic/screenshots/ImgurUpload.h
+++ b/api/logic/screenshots/ImgurUpload.h
@@ -8,26 +8,26 @@ typedef std::shared_ptr<class ImgurUpload> ImgurUploadPtr;
class MULTIMC_LOGIC_EXPORT ImgurUpload : public NetAction
{
public:
- explicit ImgurUpload(ScreenshotPtr shot);
- static ImgurUploadPtr make(ScreenshotPtr shot)
- {
- return ImgurUploadPtr(new ImgurUpload(shot));
- }
+ explicit ImgurUpload(ScreenshotPtr shot);
+ static ImgurUploadPtr make(ScreenshotPtr shot)
+ {
+ return ImgurUploadPtr(new ImgurUpload(shot));
+ }
protected
slots:
- virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
- virtual void downloadError(QNetworkReply::NetworkError error);
- virtual void downloadFinished();
- virtual void downloadReadyRead()
- {
- }
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ virtual void downloadError(QNetworkReply::NetworkError error);
+ virtual void downloadFinished();
+ virtual void downloadReadyRead()
+ {
+ }
public
slots:
- virtual void start();
+ virtual void start();
private:
- ScreenshotPtr m_shot;
- bool finished = true;
+ ScreenshotPtr m_shot;
+ bool finished = true;
};
diff --git a/api/logic/screenshots/Screenshot.h b/api/logic/screenshots/Screenshot.h
index 2c70ecf5..9db3a8a1 100644
--- a/api/logic/screenshots/Screenshot.h
+++ b/api/logic/screenshots/Screenshot.h
@@ -7,14 +7,14 @@
struct ScreenShot
{
- ScreenShot(QFileInfo file)
- {
- m_file = file;
- }
- QFileInfo m_file;
- QString m_url;
- QString m_imgurId;
- QString m_imgurDeleteHash;
+ ScreenShot(QFileInfo file)
+ {
+ m_file = file;
+ }
+ QFileInfo m_file;
+ QString m_url;
+ QString m_imgurId;
+ QString m_imgurDeleteHash;
};
typedef std::shared_ptr<ScreenShot> ScreenshotPtr;
diff --git a/api/logic/settings/INIFile.cpp b/api/logic/settings/INIFile.cpp
index 9e97f861..ff6d5cf3 100644
--- a/api/logic/settings/INIFile.cpp
+++ b/api/logic/settings/INIFile.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,124 +28,136 @@ INIFile::INIFile()
QString INIFile::unescape(QString orig)
{
- QString out;
- QChar prev = 0;
- for(auto c: orig)
- {
- if(prev == '\\')
- {
- if(c == 'n')
- out += '\n';
- else if (c == 't')
- out += '\t';
- else
- out += c;
- prev = 0;
- }
- else
- {
- if(c == '\\')
- {
- prev = c;
- continue;
- }
- out += c;
- prev = 0;
- }
- }
- return out;
+ QString out;
+ QChar prev = 0;
+ for(auto c: orig)
+ {
+ if(prev == '\\')
+ {
+ if(c == 'n')
+ out += '\n';
+ else if(c == 't')
+ out += '\t';
+ else if(c == '#')
+ out += '#';
+ else
+ out += c;
+ prev = 0;
+ }
+ else
+ {
+ if(c == '\\')
+ {
+ prev = c;
+ continue;
+ }
+ out += c;
+ prev = 0;
+ }
+ }
+ return out;
}
QString INIFile::escape(QString orig)
{
- QString out;
- for(auto c: orig)
- {
- if(c == '\n')
- out += "\\n";
- else if (c == '\t')
- out += "\\t";
- else if(c == '\\')
- out += "\\\\";
- else
- out += c;
- }
- return out;
+ QString out;
+ for(auto c: orig)
+ {
+ if(c == '\n')
+ out += "\\n";
+ else if (c == '\t')
+ out += "\\t";
+ else if(c == '\\')
+ out += "\\\\";
+ else if(c == '#')
+ out += "\\#";
+ else
+ out += c;
+ }
+ return out;
}
bool INIFile::saveFile(QString fileName)
{
- QByteArray outArray;
- for (Iterator iter = begin(); iter != end(); iter++)
- {
- QString value = iter.value().toString();
- value = escape(value);
- outArray.append(iter.key().toUtf8());
- outArray.append('=');
- outArray.append(value.toUtf8());
- outArray.append('\n');
- }
-
- try
- {
- FS::write(fileName, outArray);
- }
- catch (Exception & e)
- {
- qCritical() << e.what();
- return false;
- }
-
- return true;
+ QByteArray outArray;
+ for (Iterator iter = begin(); iter != end(); iter++)
+ {
+ QString value = iter.value().toString();
+ value = escape(value);
+ outArray.append(iter.key().toUtf8());
+ outArray.append('=');
+ outArray.append(value.toUtf8());
+ outArray.append('\n');
+ }
+
+ try
+ {
+ FS::write(fileName, outArray);
+ }
+ catch (const Exception &e)
+ {
+ qCritical() << e.what();
+ return false;
+ }
+
+ return true;
}
bool INIFile::loadFile(QString fileName)
{
- QFile file(fileName);
- if (!file.open(QIODevice::ReadOnly))
- return false;
- bool success = loadFile(file.readAll());
- file.close();
- return success;
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+ bool success = loadFile(file.readAll());
+ file.close();
+ return success;
}
bool INIFile::loadFile(QByteArray file)
{
- QTextStream in(file);
- in.setCodec("UTF-8");
-
- QStringList lines = in.readAll().split('\n');
- for (int i = 0; i < lines.count(); i++)
- {
- QString &lineRaw = lines[i];
- // Ignore comments.
- QString line = lineRaw.left(lineRaw.indexOf('#')).trimmed();
-
- int eqPos = line.indexOf('=');
- if (eqPos == -1)
- continue;
- QString key = line.left(eqPos).trimmed();
- QString valueStr = line.right(line.length() - eqPos - 1).trimmed();
-
- valueStr = unescape(valueStr);
-
- QVariant value(valueStr);
- this->operator[](key) = value;
- }
-
- return true;
+ QTextStream in(file);
+ in.setCodec("UTF-8");
+
+ QStringList lines = in.readAll().split('\n');
+ for (int i = 0; i < lines.count(); i++)
+ {
+ QString &lineRaw = lines[i];
+ // Ignore comments.
+ int commentIndex = 0;
+ QString line = lineRaw;
+ // Search for comments until no more escaped # are available
+ while((commentIndex = line.indexOf('#', commentIndex + 1)) != -1) {
+ if(commentIndex > 0 && line.at(commentIndex - 1) == '\\') {
+ continue;
+ }
+ line = line.left(lineRaw.indexOf('#')).trimmed();
+ }
+
+ int eqPos = line.indexOf('=');
+ if (eqPos == -1)
+ continue;
+ QString key = line.left(eqPos).trimmed();
+ QString valueStr = line.right(line.length() - eqPos - 1).trimmed();
+
+ valueStr = unescape(valueStr);
+
+ QVariant value(valueStr);
+ this->operator[](key) = value;
+ }
+
+ return true;
}
QVariant INIFile::get(QString key, QVariant def) const
{
- if (!this->contains(key))
- return def;
- else
- return this->operator[](key);
+ if (!this->contains(key))
+ return def;
+ else
+ return this->operator[](key);
}
void INIFile::set(QString key, QVariant val)
{
- this->operator[](key) = val;
+ this->operator[](key) = val;
}
diff --git a/api/logic/settings/INIFile.h b/api/logic/settings/INIFile.h
index f0c63d3c..9406f7bd 100644
--- a/api/logic/settings/INIFile.h
+++ b/api/logic/settings/INIFile.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,14 +25,14 @@
class MULTIMC_LOGIC_EXPORT INIFile : public QMap<QString, QVariant>
{
public:
- explicit INIFile();
+ explicit INIFile();
- bool loadFile(QByteArray file);
- bool loadFile(QString fileName);
- bool saveFile(QString fileName);
+ bool loadFile(QByteArray file);
+ bool loadFile(QString fileName);
+ bool saveFile(QString fileName);
- QVariant get(QString key, QVariant def) const;
- void set(QString key, QVariant val);
- static QString unescape(QString orig);
- static QString escape(QString orig);
+ QVariant get(QString key, QVariant def) const;
+ void set(QString key, QVariant val);
+ static QString unescape(QString orig);
+ static QString escape(QString orig);
};
diff --git a/api/logic/settings/INIFile_test.cpp b/api/logic/settings/INIFile_test.cpp
index b3ae7375..08c2155e 100644
--- a/api/logic/settings/INIFile_test.cpp
+++ b/api/logic/settings/INIFile_test.cpp
@@ -5,56 +5,57 @@
class IniFileTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
private
slots:
- void initTestCase()
- {
-
- }
- void cleanupTestCase()
- {
-
- }
-
- void test_Escape_data()
- {
- QTest::addColumn<QString>("through");
-
- QTest::newRow("unix path") << "/abc/def/ghi/jkl";
- QTest::newRow("windows path") << "C:\\Program files\\terrible\\name\\of something\\";
- QTest::newRow("Plain text") << "Lorem ipsum dolor sit amet.";
- QTest::newRow("Escape sequences") << "Lorem\n\t\n\\n\\tAAZ\nipsum dolor\n\nsit amet.";
- QTest::newRow("Escape sequences 2") << "\"\n\n\"";
- }
- void test_Escape()
- {
- QFETCH(QString, through);
-
- QString there = INIFile::escape(through);
- QString back = INIFile::unescape(there);
-
- QCOMPARE(back, through);
- }
-
- void test_SaveLoad()
- {
- QString a = "a";
- QString b = "a\nb\t\n\\\\\\C:\\Program files\\terrible\\name\\of something\\";
- QString filename = "test_SaveLoad.ini";
-
- // save
- INIFile f;
- f.set("a", a);
- f.set("b", b);
- f.saveFile(filename);
-
- // load
- INIFile f2;
- f2.loadFile(filename);
- QCOMPARE(a, f2.get("a","NOT SET").toString());
- QCOMPARE(b, f2.get("b","NOT SET").toString());
- }
+ void initTestCase()
+ {
+
+ }
+ void cleanupTestCase()
+ {
+
+ }
+
+ void test_Escape_data()
+ {
+ QTest::addColumn<QString>("through");
+
+ QTest::newRow("unix path") << "/abc/def/ghi/jkl";
+ QTest::newRow("windows path") << "C:\\Program files\\terrible\\name\\of something\\";
+ QTest::newRow("Plain text") << "Lorem ipsum dolor sit amet.";
+ QTest::newRow("Escape sequences") << "Lorem\n\t\n\\n\\tAAZ\nipsum dolor\n\nsit amet.";
+ QTest::newRow("Escape sequences 2") << "\"\n\n\"";
+ QTest::newRow("Hashtags") << "some data#something";
+ }
+ void test_Escape()
+ {
+ QFETCH(QString, through);
+
+ QString there = INIFile::escape(through);
+ QString back = INIFile::unescape(there);
+
+ QCOMPARE(back, through);
+ }
+
+ void test_SaveLoad()
+ {
+ QString a = "a";
+ QString b = "a\nb\t\n\\\\\\C:\\Program files\\terrible\\name\\of something\\#thisIsNotAComment";
+ QString filename = "test_SaveLoad.ini";
+
+ // save
+ INIFile f;
+ f.set("a", a);
+ f.set("b", b);
+ f.saveFile(filename);
+
+ // load
+ INIFile f2;
+ f2.loadFile(filename);
+ QCOMPARE(a, f2.get("a","NOT SET").toString());
+ QCOMPARE(b, f2.get("b","NOT SET").toString());
+ }
};
QTEST_GUILESS_MAIN(IniFileTest)
diff --git a/api/logic/settings/INISettingsObject.cpp b/api/logic/settings/INISettingsObject.cpp
index ff2cee31..f04a66b5 100644
--- a/api/logic/settings/INISettingsObject.cpp
+++ b/api/logic/settings/INISettingsObject.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,91 +17,91 @@
#include "Setting.h"
INISettingsObject::INISettingsObject(const QString &path, QObject *parent)
- : SettingsObject(parent)
+ : SettingsObject(parent)
{
- m_filePath = path;
- m_ini.loadFile(path);
+ m_filePath = path;
+ m_ini.loadFile(path);
}
void INISettingsObject::setFilePath(const QString &filePath)
{
- m_filePath = filePath;
+ m_filePath = filePath;
}
bool INISettingsObject::reload()
{
- return m_ini.loadFile(m_filePath) && SettingsObject::reload();
+ return m_ini.loadFile(m_filePath) && SettingsObject::reload();
}
void INISettingsObject::suspendSave()
{
- m_suspendSave = true;
+ m_suspendSave = true;
}
void INISettingsObject::resumeSave()
{
- m_suspendSave = false;
- if(m_doSave)
- {
- m_ini.saveFile(m_filePath);
- }
+ m_suspendSave = false;
+ if(m_doSave)
+ {
+ m_ini.saveFile(m_filePath);
+ }
}
void INISettingsObject::changeSetting(const Setting &setting, QVariant value)
{
- if (contains(setting.id()))
- {
- // valid value -> set the main config, remove all the sysnonyms
- if (value.isValid())
- {
- auto list = setting.configKeys();
- m_ini.set(list.takeFirst(), value);
- for(auto iter: list)
- m_ini.remove(iter);
- }
- // invalid -> remove all (just like resetSetting)
- else
- {
- for(auto iter: setting.configKeys())
- m_ini.remove(iter);
- }
- doSave();
- }
+ if (contains(setting.id()))
+ {
+ // valid value -> set the main config, remove all the sysnonyms
+ if (value.isValid())
+ {
+ auto list = setting.configKeys();
+ m_ini.set(list.takeFirst(), value);
+ for(auto iter: list)
+ m_ini.remove(iter);
+ }
+ // invalid -> remove all (just like resetSetting)
+ else
+ {
+ for(auto iter: setting.configKeys())
+ m_ini.remove(iter);
+ }
+ doSave();
+ }
}
void INISettingsObject::doSave()
{
- if(m_suspendSave)
- {
- m_doSave = true;
- }
- else
- {
- m_ini.saveFile(m_filePath);
- }
+ if(m_suspendSave)
+ {
+ m_doSave = true;
+ }
+ else
+ {
+ m_ini.saveFile(m_filePath);
+ }
}
void INISettingsObject::resetSetting(const Setting &setting)
{
- // if we have the setting, remove all the synonyms. ALL OF THEM
- if (contains(setting.id()))
- {
- for(auto iter: setting.configKeys())
- m_ini.remove(iter);
- doSave();
- }
+ // if we have the setting, remove all the synonyms. ALL OF THEM
+ if (contains(setting.id()))
+ {
+ for(auto iter: setting.configKeys())
+ m_ini.remove(iter);
+ doSave();
+ }
}
QVariant INISettingsObject::retrieveValue(const Setting &setting)
{
- // if we have the setting, return value of the first matching synonym
- if (contains(setting.id()))
- {
- for(auto iter: setting.configKeys())
- {
- if(m_ini.contains(iter))
- return m_ini[iter];
- }
- }
- return QVariant();
+ // if we have the setting, return value of the first matching synonym
+ if (contains(setting.id()))
+ {
+ for(auto iter: setting.configKeys())
+ {
+ if(m_ini.contains(iter))
+ return m_ini[iter];
+ }
+ }
+ return QVariant();
}
diff --git a/api/logic/settings/INISettingsObject.h b/api/logic/settings/INISettingsObject.h
index 111215e6..c8ea99ca 100644
--- a/api/logic/settings/INISettingsObject.h
+++ b/api/logic/settings/INISettingsObject.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,39 +28,39 @@
*/
class MULTIMC_LOGIC_EXPORT INISettingsObject : public SettingsObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit INISettingsObject(const QString &path, QObject *parent = 0);
+ explicit INISettingsObject(const QString &path, QObject *parent = 0);
- /*!
- * \brief Gets the path to the INI file.
- * \return The path to the INI file.
- */
- virtual QString filePath() const
- {
- return m_filePath;
- }
+ /*!
+ * \brief Gets the path to the INI file.
+ * \return The path to the INI file.
+ */
+ virtual QString filePath() const
+ {
+ return m_filePath;
+ }
- /*!
- * \brief Sets the path to the INI file and reloads it.
- * \param filePath The INI file's new path.
- */
- virtual void setFilePath(const QString &filePath);
+ /*!
+ * \brief Sets the path to the INI file and reloads it.
+ * \param filePath The INI file's new path.
+ */
+ virtual void setFilePath(const QString &filePath);
- bool reload() override;
+ bool reload() override;
- void suspendSave() override;
- void resumeSave() override;
+ void suspendSave() override;
+ void resumeSave() override;
protected slots:
- virtual void changeSetting(const Setting &setting, QVariant value) override;
- virtual void resetSetting(const Setting &setting) override;
+ virtual void changeSetting(const Setting &setting, QVariant value) override;
+ virtual void resetSetting(const Setting &setting) override;
protected:
- virtual QVariant retrieveValue(const Setting &setting) override;
- void doSave();
+ virtual QVariant retrieveValue(const Setting &setting) override;
+ void doSave();
protected:
- INIFile m_ini;
- QString m_filePath;
+ INIFile m_ini;
+ QString m_filePath;
};
diff --git a/api/logic/settings/OverrideSetting.cpp b/api/logic/settings/OverrideSetting.cpp
index a3d48e03..1efdebac 100644
--- a/api/logic/settings/OverrideSetting.cpp
+++ b/api/logic/settings/OverrideSetting.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,39 +16,39 @@
#include "OverrideSetting.h"
OverrideSetting::OverrideSetting(std::shared_ptr<Setting> other, std::shared_ptr<Setting> gate)
- : Setting(other->configKeys(), QVariant())
+ : Setting(other->configKeys(), QVariant())
{
- Q_ASSERT(other);
- Q_ASSERT(gate);
- m_other = other;
- m_gate = gate;
+ Q_ASSERT(other);
+ Q_ASSERT(gate);
+ m_other = other;
+ m_gate = gate;
}
bool OverrideSetting::isOverriding() const
{
- return m_gate->get().toBool();
+ return m_gate->get().toBool();
}
QVariant OverrideSetting::defValue() const
{
- return m_other->get();
+ return m_other->get();
}
QVariant OverrideSetting::get() const
{
- if(isOverriding())
- {
- return Setting::get();
- }
- return m_other->get();
+ if(isOverriding())
+ {
+ return Setting::get();
+ }
+ return m_other->get();
}
void OverrideSetting::reset()
{
- Setting::reset();
+ Setting::reset();
}
void OverrideSetting::set(QVariant value)
{
- Setting::set(value);
+ Setting::set(value);
}
diff --git a/api/logic/settings/OverrideSetting.h b/api/logic/settings/OverrideSetting.h
index f2cbc5dc..f99caff1 100644
--- a/api/logic/settings/OverrideSetting.h
+++ b/api/logic/settings/OverrideSetting.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,19 +28,19 @@
*/
class OverrideSetting : public Setting
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit OverrideSetting(std::shared_ptr<Setting> overriden, std::shared_ptr<Setting> gate);
+ explicit OverrideSetting(std::shared_ptr<Setting> overriden, std::shared_ptr<Setting> gate);
- virtual QVariant defValue() const;
- virtual QVariant get() const;
- virtual void set (QVariant value);
- virtual void reset();
+ virtual QVariant defValue() const;
+ virtual QVariant get() const;
+ virtual void set (QVariant value);
+ virtual void reset();
private:
- bool isOverriding() const;
+ bool isOverriding() const;
protected:
- std::shared_ptr<Setting> m_other;
- std::shared_ptr<Setting> m_gate;
+ std::shared_ptr<Setting> m_other;
+ std::shared_ptr<Setting> m_gate;
};
diff --git a/api/logic/settings/PassthroughSetting.cpp b/api/logic/settings/PassthroughSetting.cpp
index 5da5d11c..beee4152 100644
--- a/api/logic/settings/PassthroughSetting.cpp
+++ b/api/logic/settings/PassthroughSetting.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,54 +16,54 @@
#include "PassthroughSetting.h"
PassthroughSetting::PassthroughSetting(std::shared_ptr<Setting> other, std::shared_ptr<Setting> gate)
- : Setting(other->configKeys(), QVariant())
+ : Setting(other->configKeys(), QVariant())
{
- Q_ASSERT(other);
- m_other = other;
- m_gate = gate;
+ Q_ASSERT(other);
+ m_other = other;
+ m_gate = gate;
}
bool PassthroughSetting::isOverriding() const
{
- if(!m_gate)
- {
- return false;
- }
- return m_gate->get().toBool();
+ if(!m_gate)
+ {
+ return false;
+ }
+ return m_gate->get().toBool();
}
QVariant PassthroughSetting::defValue() const
{
- if(isOverriding())
- {
- return m_other->get();
- }
- return m_other->defValue();
+ if(isOverriding())
+ {
+ return m_other->get();
+ }
+ return m_other->defValue();
}
QVariant PassthroughSetting::get() const
{
- if(isOverriding())
- {
- return Setting::get();
- }
- return m_other->get();
+ if(isOverriding())
+ {
+ return Setting::get();
+ }
+ return m_other->get();
}
void PassthroughSetting::reset()
{
- if(isOverriding())
- {
- Setting::reset();
- }
- m_other->reset();
+ if(isOverriding())
+ {
+ Setting::reset();
+ }
+ m_other->reset();
}
void PassthroughSetting::set(QVariant value)
{
- if(isOverriding())
- {
- Setting::set(value);
- }
- m_other->set(value);
+ if(isOverriding())
+ {
+ Setting::set(value);
+ }
+ m_other->set(value);
}
diff --git a/api/logic/settings/PassthroughSetting.h b/api/logic/settings/PassthroughSetting.h
index ee844da4..00212072 100644
--- a/api/logic/settings/PassthroughSetting.h
+++ b/api/logic/settings/PassthroughSetting.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,19 +27,19 @@
*/
class PassthroughSetting : public Setting
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit PassthroughSetting(std::shared_ptr<Setting> overriden, std::shared_ptr<Setting> gate);
+ explicit PassthroughSetting(std::shared_ptr<Setting> overriden, std::shared_ptr<Setting> gate);
- virtual QVariant defValue() const;
- virtual QVariant get() const;
- virtual void set (QVariant value);
- virtual void reset();
+ virtual QVariant defValue() const;
+ virtual QVariant get() const;
+ virtual void set (QVariant value);
+ virtual void reset();
private:
- bool isOverriding() const;
+ bool isOverriding() const;
protected:
- std::shared_ptr<Setting> m_other;
- std::shared_ptr<Setting> m_gate;
+ std::shared_ptr<Setting> m_other;
+ std::shared_ptr<Setting> m_gate;
};
diff --git a/api/logic/settings/Setting.cpp b/api/logic/settings/Setting.cpp
index fa0041e0..419dfa2f 100644
--- a/api/logic/settings/Setting.cpp
+++ b/api/logic/settings/Setting.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,37 +17,37 @@
#include "settings/SettingsObject.h"
Setting::Setting(QStringList synonyms, QVariant defVal)
- : QObject(), m_synonyms(synonyms), m_defVal(defVal)
+ : QObject(), m_synonyms(synonyms), m_defVal(defVal)
{
}
QVariant Setting::get() const
{
- SettingsObject *sbase = m_storage;
- if (!sbase)
- {
- return defValue();
- }
- else
- {
- QVariant test = sbase->retrieveValue(*this);
- if (!test.isValid())
- return defValue();
- return test;
- }
+ SettingsObject *sbase = m_storage;
+ if (!sbase)
+ {
+ return defValue();
+ }
+ else
+ {
+ QVariant test = sbase->retrieveValue(*this);
+ if (!test.isValid())
+ return defValue();
+ return test;
+ }
}
QVariant Setting::defValue() const
{
- return m_defVal;
+ return m_defVal;
}
void Setting::set(QVariant value)
{
- emit SettingChanged(*this, value);
+ emit SettingChanged(*this, value);
}
void Setting::reset()
{
- emit settingReset(*this);
+ emit settingReset(*this);
}
diff --git a/api/logic/settings/Setting.h b/api/logic/settings/Setting.h
index 3edea7be..9d827c72 100644
--- a/api/logic/settings/Setting.h
+++ b/api/logic/settings/Setting.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,91 +29,91 @@ class SettingsObject;
*/
class MULTIMC_LOGIC_EXPORT Setting : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- /**
- * Construct a Setting
- *
- * Synonyms are all the possible names used in the settings object, in order of preference.
- * First synonym is the ID, which identifies the setting in MultiMC.
- *
- * defVal is the default value that will be returned when the settings object
- * doesn't have any value for this setting.
- */
- explicit Setting(QStringList synonyms, QVariant defVal = QVariant());
+ /**
+ * Construct a Setting
+ *
+ * Synonyms are all the possible names used in the settings object, in order of preference.
+ * First synonym is the ID, which identifies the setting in MultiMC.
+ *
+ * defVal is the default value that will be returned when the settings object
+ * doesn't have any value for this setting.
+ */
+ explicit Setting(QStringList synonyms, QVariant defVal = QVariant());
- /*!
- * \brief Gets this setting's ID.
- * This is used to refer to the setting within the application.
- * \warning Changing the ID while the setting is registered with a SettingsObject results in
- * undefined behavior.
- * \return The ID of the setting.
- */
- virtual QString id() const
- {
- return m_synonyms.first();
- }
+ /*!
+ * \brief Gets this setting's ID.
+ * This is used to refer to the setting within the application.
+ * \warning Changing the ID while the setting is registered with a SettingsObject results in
+ * undefined behavior.
+ * \return The ID of the setting.
+ */
+ virtual QString id() const
+ {
+ return m_synonyms.first();
+ }
- /*!
- * \brief Gets this setting's config file key.
- * This is used to store the setting's value in the config file. It is usually
- * the same as the setting's ID, but it can be different.
- * \return The setting's config file key.
- */
- virtual QStringList configKeys() const
- {
- return m_synonyms;
- }
+ /*!
+ * \brief Gets this setting's config file key.
+ * This is used to store the setting's value in the config file. It is usually
+ * the same as the setting's ID, but it can be different.
+ * \return The setting's config file key.
+ */
+ virtual QStringList configKeys() const
+ {
+ return m_synonyms;
+ }
- /*!
- * \brief Gets this setting's value as a QVariant.
- * This is done by calling the SettingsObject's retrieveValue() function.
- * If this Setting doesn't have a SettingsObject, this returns an invalid QVariant.
- * \return QVariant containing this setting's value.
- * \sa value()
- */
- virtual QVariant get() const;
+ /*!
+ * \brief Gets this setting's value as a QVariant.
+ * This is done by calling the SettingsObject's retrieveValue() function.
+ * If this Setting doesn't have a SettingsObject, this returns an invalid QVariant.
+ * \return QVariant containing this setting's value.
+ * \sa value()
+ */
+ virtual QVariant get() const;
- /*!
- * \brief Gets this setting's default value.
- * \return The default value of this setting.
- */
- virtual QVariant defValue() const;
+ /*!
+ * \brief Gets this setting's default value.
+ * \return The default value of this setting.
+ */
+ virtual QVariant defValue() const;
signals:
- /*!
- * \brief Signal emitted when this Setting object's value changes.
- * \param setting A reference to the Setting that changed.
- * \param value This Setting object's new value.
- */
- void SettingChanged(const Setting &setting, QVariant value);
+ /*!
+ * \brief Signal emitted when this Setting object's value changes.
+ * \param setting A reference to the Setting that changed.
+ * \param value This Setting object's new value.
+ */
+ void SettingChanged(const Setting &setting, QVariant value);
- /*!
- * \brief Signal emitted when this Setting object's value resets to default.
- * \param setting A reference to the Setting that changed.
- */
- void settingReset(const Setting &setting);
+ /*!
+ * \brief Signal emitted when this Setting object's value resets to default.
+ * \param setting A reference to the Setting that changed.
+ */
+ void settingReset(const Setting &setting);
public
slots:
- /*!
- * \brief Changes the setting's value.
- * This is done by emitting the SettingChanged() signal which will then be
- * handled by the SettingsObject object and cause the setting to change.
- * \param value The new value.
- */
- virtual void set(QVariant value);
+ /*!
+ * \brief Changes the setting's value.
+ * This is done by emitting the SettingChanged() signal which will then be
+ * handled by the SettingsObject object and cause the setting to change.
+ * \param value The new value.
+ */
+ virtual void set(QVariant value);
- /*!
- * \brief Reset the setting to default
- * This is done by emitting the settingReset() signal which will then be
- * handled by the SettingsObject object and cause the setting to change.
- */
- virtual void reset();
+ /*!
+ * \brief Reset the setting to default
+ * This is done by emitting the settingReset() signal which will then be
+ * handled by the SettingsObject object and cause the setting to change.
+ */
+ virtual void reset();
protected:
- friend class SettingsObject;
- SettingsObject * m_storage;
- QStringList m_synonyms;
- QVariant m_defVal;
+ friend class SettingsObject;
+ SettingsObject * m_storage;
+ QStringList m_synonyms;
+ QVariant m_defVal;
};
diff --git a/api/logic/settings/SettingsObject.cpp b/api/logic/settings/SettingsObject.cpp
index 87a8c2a8..b4fc5a68 100644
--- a/api/logic/settings/SettingsObject.cpp
+++ b/api/logic/settings/SettingsObject.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,116 +27,116 @@ SettingsObject::SettingsObject(QObject *parent) : QObject(parent)
SettingsObject::~SettingsObject()
{
- m_settings.clear();
+ m_settings.clear();
}
std::shared_ptr<Setting> SettingsObject::registerOverride(std::shared_ptr<Setting> original,
- std::shared_ptr<Setting> gate)
+ std::shared_ptr<Setting> gate)
{
- if (contains(original->id()))
- {
- qCritical() << QString("Failed to register setting %1. ID already exists.")
- .arg(original->id());
- return nullptr; // Fail
- }
- auto override = std::make_shared<OverrideSetting>(original, gate);
- override->m_storage = this;
- connectSignals(*override);
- m_settings.insert(override->id(), override);
- return override;
+ if (contains(original->id()))
+ {
+ qCritical() << QString("Failed to register setting %1. ID already exists.")
+ .arg(original->id());
+ return nullptr; // Fail
+ }
+ auto override = std::make_shared<OverrideSetting>(original, gate);
+ override->m_storage = this;
+ connectSignals(*override);
+ m_settings.insert(override->id(), override);
+ return override;
}
std::shared_ptr<Setting> SettingsObject::registerPassthrough(std::shared_ptr<Setting> original,
- std::shared_ptr<Setting> gate)
+ std::shared_ptr<Setting> gate)
{
- if (contains(original->id()))
- {
- qCritical() << QString("Failed to register setting %1. ID already exists.")
- .arg(original->id());
- return nullptr; // Fail
- }
- auto passthrough = std::make_shared<PassthroughSetting>(original, gate);
- passthrough->m_storage = this;
- connectSignals(*passthrough);
- m_settings.insert(passthrough->id(), passthrough);
- return passthrough;
+ if (contains(original->id()))
+ {
+ qCritical() << QString("Failed to register setting %1. ID already exists.")
+ .arg(original->id());
+ return nullptr; // Fail
+ }
+ auto passthrough = std::make_shared<PassthroughSetting>(original, gate);
+ passthrough->m_storage = this;
+ connectSignals(*passthrough);
+ m_settings.insert(passthrough->id(), passthrough);
+ return passthrough;
}
std::shared_ptr<Setting> SettingsObject::registerSetting(QStringList synonyms, QVariant defVal)
{
- if (synonyms.empty())
- return nullptr;
- if (contains(synonyms.first()))
- {
- qCritical() << QString("Failed to register setting %1. ID already exists.")
- .arg(synonyms.first());
- return nullptr; // Fail
- }
- auto setting = std::make_shared<Setting>(synonyms, defVal);
- setting->m_storage = this;
- connectSignals(*setting);
- m_settings.insert(setting->id(), setting);
- return setting;
+ if (synonyms.empty())
+ return nullptr;
+ if (contains(synonyms.first()))
+ {
+ qCritical() << QString("Failed to register setting %1. ID already exists.")
+ .arg(synonyms.first());
+ return nullptr; // Fail
+ }
+ auto setting = std::make_shared<Setting>(synonyms, defVal);
+ setting->m_storage = this;
+ connectSignals(*setting);
+ m_settings.insert(setting->id(), setting);
+ return setting;
}
std::shared_ptr<Setting> SettingsObject::getSetting(const QString &id) const
{
- // Make sure there is a setting with the given ID.
- if (!m_settings.contains(id))
- return NULL;
+ // Make sure there is a setting with the given ID.
+ if (!m_settings.contains(id))
+ return NULL;
- return m_settings[id];
+ return m_settings[id];
}
QVariant SettingsObject::get(const QString &id) const
{
- auto setting = getSetting(id);
- return (setting ? setting->get() : QVariant());
+ auto setting = getSetting(id);
+ return (setting ? setting->get() : QVariant());
}
bool SettingsObject::set(const QString &id, QVariant value)
{
- auto setting = getSetting(id);
- if (!setting)
- {
- qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id);
- return false;
- }
- else
- {
- setting->set(value);
- return true;
- }
+ auto setting = getSetting(id);
+ if (!setting)
+ {
+ qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id);
+ return false;
+ }
+ else
+ {
+ setting->set(value);
+ return true;
+ }
}
void SettingsObject::reset(const QString &id) const
{
- auto setting = getSetting(id);
- if (setting)
- setting->reset();
+ auto setting = getSetting(id);
+ if (setting)
+ setting->reset();
}
bool SettingsObject::contains(const QString &id)
{
- return m_settings.contains(id);
+ return m_settings.contains(id);
}
bool SettingsObject::reload()
{
- for (auto setting : m_settings.values())
- {
- setting->set(setting->get());
- }
- return true;
+ for (auto setting : m_settings.values())
+ {
+ setting->set(setting->get());
+ }
+ return true;
}
void SettingsObject::connectSignals(const Setting &setting)
{
- connect(&setting, SIGNAL(SettingChanged(const Setting &, QVariant)),
- SLOT(changeSetting(const Setting &, QVariant)));
- connect(&setting, SIGNAL(SettingChanged(const Setting &, QVariant)),
- SIGNAL(SettingChanged(const Setting &, QVariant)));
+ connect(&setting, SIGNAL(SettingChanged(const Setting &, QVariant)),
+ SLOT(changeSetting(const Setting &, QVariant)));
+ connect(&setting, SIGNAL(SettingChanged(const Setting &, QVariant)),
+ SIGNAL(SettingChanged(const Setting &, QVariant)));
- connect(&setting, SIGNAL(settingReset(Setting)), SLOT(resetSetting(const Setting &)));
- connect(&setting, SIGNAL(settingReset(Setting)), SIGNAL(settingReset(const Setting &)));
+ connect(&setting, SIGNAL(settingReset(Setting)), SLOT(resetSetting(const Setting &)));
+ connect(&setting, SIGNAL(settingReset(Setting)), SIGNAL(settingReset(const Setting &)));
}
diff --git a/api/logic/settings/SettingsObject.h b/api/logic/settings/SettingsObject.h
index 8582d8ad..11dc50ca 100644
--- a/api/logic/settings/SettingsObject.h
+++ b/api/logic/settings/SettingsObject.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,173 +42,173 @@ typedef std::shared_ptr<SettingsObject> SettingsObjectPtr;
*/
class MULTIMC_LOGIC_EXPORT SettingsObject : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- class Lock
- {
- public:
- Lock(SettingsObjectPtr locked)
- :m_locked(locked)
- {
- m_locked->suspendSave();
- }
- ~Lock()
- {
- m_locked->resumeSave();
- }
- private:
- SettingsObjectPtr m_locked;
- };
+ class Lock
+ {
+ public:
+ Lock(SettingsObjectPtr locked)
+ :m_locked(locked)
+ {
+ m_locked->suspendSave();
+ }
+ ~Lock()
+ {
+ m_locked->resumeSave();
+ }
+ private:
+ SettingsObjectPtr m_locked;
+ };
public:
- explicit SettingsObject(QObject *parent = 0);
- virtual ~SettingsObject();
- /*!
- * Registers an override setting for the given original setting in this settings object
- * gate decides if the passthrough (true) or the original (false) is used for value
- *
- * This will fail if there is already a setting with the same ID as
- * the one that is being registered.
- * \return A valid Setting shared pointer if successful.
- */
- std::shared_ptr<Setting> registerOverride(std::shared_ptr<Setting> original, std::shared_ptr<Setting> gate);
-
- /*!
- * Registers a passthorugh setting for the given original setting in this settings object
- * gate decides if the passthrough (true) or the original (false) is used for value
- *
- * This will fail if there is already a setting with the same ID as
- * the one that is being registered.
- * \return A valid Setting shared pointer if successful.
- */
- std::shared_ptr<Setting> registerPassthrough(std::shared_ptr<Setting> original, std::shared_ptr<Setting> gate);
-
- /*!
- * Registers the given setting with this SettingsObject and connects the necessary signals.
- *
- * This will fail if there is already a setting with the same ID as
- * the one that is being registered.
- * \return A valid Setting shared pointer if successful.
- */
- std::shared_ptr<Setting> registerSetting(QStringList synonyms,
- QVariant defVal = QVariant());
-
- /*!
- * Registers the given setting with this SettingsObject and connects the necessary signals.
- *
- * This will fail if there is already a setting with the same ID as
- * the one that is being registered.
- * \return A valid Setting shared pointer if successful.
- */
- std::shared_ptr<Setting> registerSetting(QString id, QVariant defVal = QVariant())
- {
- return registerSetting(QStringList(id), defVal);
- }
-
- /*!
- * \brief Gets the setting with the given ID.
- * \param id The ID of the setting to get.
- * \return A pointer to the setting with the given ID.
- * Returns null if there is no setting with the given ID.
- * \sa operator []()
- */
- std::shared_ptr<Setting> getSetting(const QString &id) const;
-
- /*!
- * \brief Gets the value of the setting with the given ID.
- * \param id The ID of the setting to get.
- * \return The setting's value as a QVariant.
- * If no setting with the given ID exists, returns an invalid QVariant.
- */
- QVariant get(const QString &id) const;
-
- /*!
- * \brief Sets the value of the setting with the given ID.
- * If no setting with the given ID exists, returns false
- * \param id The ID of the setting to change.
- * \param value The new value of the setting.
- * \return True if successful, false if it failed.
- */
- bool set(const QString &id, QVariant value);
-
- /*!
- * \brief Reverts the setting with the given ID to default.
- * \param id The ID of the setting to reset.
- */
- void reset(const QString &id) const;
-
- /*!
- * \brief Checks if this SettingsObject contains a setting with the given ID.
- * \param id The ID to check for.
- * \return True if the SettingsObject has a setting with the given ID.
- */
- bool contains(const QString &id);
-
- /*!
- * \brief Reloads the settings and emit signals for changed settings
- * \return True if reloading was successful
- */
- virtual bool reload();
-
- virtual void suspendSave() = 0;
- virtual void resumeSave() = 0;
+ explicit SettingsObject(QObject *parent = 0);
+ virtual ~SettingsObject();
+ /*!
+ * Registers an override setting for the given original setting in this settings object
+ * gate decides if the passthrough (true) or the original (false) is used for value
+ *
+ * This will fail if there is already a setting with the same ID as
+ * the one that is being registered.
+ * \return A valid Setting shared pointer if successful.
+ */
+ std::shared_ptr<Setting> registerOverride(std::shared_ptr<Setting> original, std::shared_ptr<Setting> gate);
+
+ /*!
+ * Registers a passthorugh setting for the given original setting in this settings object
+ * gate decides if the passthrough (true) or the original (false) is used for value
+ *
+ * This will fail if there is already a setting with the same ID as
+ * the one that is being registered.
+ * \return A valid Setting shared pointer if successful.
+ */
+ std::shared_ptr<Setting> registerPassthrough(std::shared_ptr<Setting> original, std::shared_ptr<Setting> gate);
+
+ /*!
+ * Registers the given setting with this SettingsObject and connects the necessary signals.
+ *
+ * This will fail if there is already a setting with the same ID as
+ * the one that is being registered.
+ * \return A valid Setting shared pointer if successful.
+ */
+ std::shared_ptr<Setting> registerSetting(QStringList synonyms,
+ QVariant defVal = QVariant());
+
+ /*!
+ * Registers the given setting with this SettingsObject and connects the necessary signals.
+ *
+ * This will fail if there is already a setting with the same ID as
+ * the one that is being registered.
+ * \return A valid Setting shared pointer if successful.
+ */
+ std::shared_ptr<Setting> registerSetting(QString id, QVariant defVal = QVariant())
+ {
+ return registerSetting(QStringList(id), defVal);
+ }
+
+ /*!
+ * \brief Gets the setting with the given ID.
+ * \param id The ID of the setting to get.
+ * \return A pointer to the setting with the given ID.
+ * Returns null if there is no setting with the given ID.
+ * \sa operator []()
+ */
+ std::shared_ptr<Setting> getSetting(const QString &id) const;
+
+ /*!
+ * \brief Gets the value of the setting with the given ID.
+ * \param id The ID of the setting to get.
+ * \return The setting's value as a QVariant.
+ * If no setting with the given ID exists, returns an invalid QVariant.
+ */
+ QVariant get(const QString &id) const;
+
+ /*!
+ * \brief Sets the value of the setting with the given ID.
+ * If no setting with the given ID exists, returns false
+ * \param id The ID of the setting to change.
+ * \param value The new value of the setting.
+ * \return True if successful, false if it failed.
+ */
+ bool set(const QString &id, QVariant value);
+
+ /*!
+ * \brief Reverts the setting with the given ID to default.
+ * \param id The ID of the setting to reset.
+ */
+ void reset(const QString &id) const;
+
+ /*!
+ * \brief Checks if this SettingsObject contains a setting with the given ID.
+ * \param id The ID to check for.
+ * \return True if the SettingsObject has a setting with the given ID.
+ */
+ bool contains(const QString &id);
+
+ /*!
+ * \brief Reloads the settings and emit signals for changed settings
+ * \return True if reloading was successful
+ */
+ virtual bool reload();
+
+ virtual void suspendSave() = 0;
+ virtual void resumeSave() = 0;
signals:
- /*!
- * \brief Signal emitted when one of this SettingsObject object's settings changes.
- * This is usually just connected directly to each Setting object's
- * SettingChanged() signals.
- * \param setting A reference to the Setting object that changed.
- * \param value The Setting object's new value.
- */
- void SettingChanged(const Setting &setting, QVariant value);
-
- /*!
- * \brief Signal emitted when one of this SettingsObject object's settings resets.
- * This is usually just connected directly to each Setting object's
- * settingReset() signals.
- * \param setting A reference to the Setting object that changed.
- */
- void settingReset(const Setting &setting);
+ /*!
+ * \brief Signal emitted when one of this SettingsObject object's settings changes.
+ * This is usually just connected directly to each Setting object's
+ * SettingChanged() signals.
+ * \param setting A reference to the Setting object that changed.
+ * \param value The Setting object's new value.
+ */
+ void SettingChanged(const Setting &setting, QVariant value);
+
+ /*!
+ * \brief Signal emitted when one of this SettingsObject object's settings resets.
+ * This is usually just connected directly to each Setting object's
+ * settingReset() signals.
+ * \param setting A reference to the Setting object that changed.
+ */
+ void settingReset(const Setting &setting);
protected
slots:
- /*!
- * \brief Changes a setting.
- * This slot is usually connected to each Setting object's
- * SettingChanged() signal. The signal is emitted, causing this slot
- * to update the setting's value in the config file.
- * \param setting A reference to the Setting object that changed.
- * \param value The setting's new value.
- */
- virtual void changeSetting(const Setting &setting, QVariant value) = 0;
-
- /*!
- * \brief Resets a setting.
- * This slot is usually connected to each Setting object's
- * settingReset() signal. The signal is emitted, causing this slot
- * to update the setting's value in the config file.
- * \param setting A reference to the Setting object that changed.
- */
- virtual void resetSetting(const Setting &setting) = 0;
+ /*!
+ * \brief Changes a setting.
+ * This slot is usually connected to each Setting object's
+ * SettingChanged() signal. The signal is emitted, causing this slot
+ * to update the setting's value in the config file.
+ * \param setting A reference to the Setting object that changed.
+ * \param value The setting's new value.
+ */
+ virtual void changeSetting(const Setting &setting, QVariant value) = 0;
+
+ /*!
+ * \brief Resets a setting.
+ * This slot is usually connected to each Setting object's
+ * settingReset() signal. The signal is emitted, causing this slot
+ * to update the setting's value in the config file.
+ * \param setting A reference to the Setting object that changed.
+ */
+ virtual void resetSetting(const Setting &setting) = 0;
protected:
- /*!
- * \brief Connects the necessary signals to the given Setting.
- * \param setting The setting to connect.
- */
- void connectSignals(const Setting &setting);
+ /*!
+ * \brief Connects the necessary signals to the given Setting.
+ * \param setting The setting to connect.
+ */
+ void connectSignals(const Setting &setting);
- /*!
- * \brief Function used by Setting objects to get their values from the SettingsObject.
- * \param setting The
- * \return
- */
- virtual QVariant retrieveValue(const Setting &setting) = 0;
+ /*!
+ * \brief Function used by Setting objects to get their values from the SettingsObject.
+ * \param setting The
+ * \return
+ */
+ virtual QVariant retrieveValue(const Setting &setting) = 0;
- friend class Setting;
+ friend class Setting;
private:
- QMap<QString, std::shared_ptr<Setting>> m_settings;
+ QMap<QString, std::shared_ptr<Setting>> m_settings;
protected:
- bool m_suspendSave = false;
- bool m_doSave = false;
+ bool m_suspendSave = false;
+ bool m_doSave = false;
};
diff --git a/api/logic/status/StatusChecker.cpp b/api/logic/status/StatusChecker.cpp
index bff9fda9..f18ed364 100644
--- a/api/logic/status/StatusChecker.cpp
+++ b/api/logic/status/StatusChecker.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,121 +28,121 @@ StatusChecker::StatusChecker()
void StatusChecker::timerEvent(QTimerEvent *e)
{
- QObject::timerEvent(e);
- reloadStatus();
+ QObject::timerEvent(e);
+ reloadStatus();
}
void StatusChecker::reloadStatus()
{
- if (isLoadingStatus())
- {
- // qDebug() << "Ignored request to reload status. Currently reloading already.";
- return;
- }
-
- // qDebug() << "Reloading status.";
-
- NetJob* job = new NetJob("Status JSON");
- job->addNetAction(Net::Download::makeByteArray(URLConstants::MOJANG_STATUS_URL, &dataSink));
- QObject::connect(job, &NetJob::succeeded, this, &StatusChecker::statusDownloadFinished);
- QObject::connect(job, &NetJob::failed, this, &StatusChecker::statusDownloadFailed);
- m_statusNetJob.reset(job);
- emit statusLoading(true);
- job->start();
+ if (isLoadingStatus())
+ {
+ // qDebug() << "Ignored request to reload status. Currently reloading already.";
+ return;
+ }
+
+ // qDebug() << "Reloading status.";
+
+ NetJob* job = new NetJob("Status JSON");
+ job->addNetAction(Net::Download::makeByteArray(URLConstants::MOJANG_STATUS_URL, &dataSink));
+ QObject::connect(job, &NetJob::succeeded, this, &StatusChecker::statusDownloadFinished);
+ QObject::connect(job, &NetJob::failed, this, &StatusChecker::statusDownloadFailed);
+ m_statusNetJob.reset(job);
+ emit statusLoading(true);
+ job->start();
}
void StatusChecker::statusDownloadFinished()
{
- qDebug() << "Finished loading status JSON.";
- m_statusEntries.clear();
- m_statusNetJob.reset();
-
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(dataSink, &jsonError);
-
- if (jsonError.error != QJsonParseError::NoError)
- {
- fail("Error parsing status JSON:" + jsonError.errorString());
- return;
- }
-
- if (!jsonDoc.isArray())
- {
- fail("Error parsing status JSON: JSON root is not an array");
- return;
- }
-
- QJsonArray root = jsonDoc.array();
-
- for(auto status = root.begin(); status != root.end(); ++status)
- {
- QVariantMap map = (*status).toObject().toVariantMap();
-
- for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
- {
- QString key = iter.key();
- QVariant value = iter.value();
-
- if(value.type() == QVariant::Type::String)
- {
- m_statusEntries.insert(key, value.toString());
- //qDebug() << "Status JSON object: " << key << m_statusEntries[key];
- }
- else
- {
- fail("Malformed status JSON: expected status type to be a string.");
- return;
- }
- }
- }
-
- succeed();
+ qDebug() << "Finished loading status JSON.";
+ m_statusEntries.clear();
+ m_statusNetJob.reset();
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(dataSink, &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ fail("Error parsing status JSON:" + jsonError.errorString());
+ return;
+ }
+
+ if (!jsonDoc.isArray())
+ {
+ fail("Error parsing status JSON: JSON root is not an array");
+ return;
+ }
+
+ QJsonArray root = jsonDoc.array();
+
+ for(auto status = root.begin(); status != root.end(); ++status)
+ {
+ QVariantMap map = (*status).toObject().toVariantMap();
+
+ for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
+ {
+ QString key = iter.key();
+ QVariant value = iter.value();
+
+ if(value.type() == QVariant::Type::String)
+ {
+ m_statusEntries.insert(key, value.toString());
+ //qDebug() << "Status JSON object: " << key << m_statusEntries[key];
+ }
+ else
+ {
+ fail("Malformed status JSON: expected status type to be a string.");
+ return;
+ }
+ }
+ }
+
+ succeed();
}
void StatusChecker::statusDownloadFailed(QString reason)
{
- fail(tr("Failed to load status JSON:\n%1").arg(reason));
+ fail(tr("Failed to load status JSON:\n%1").arg(reason));
}
QMap<QString, QString> StatusChecker::getStatusEntries() const
{
- return m_statusEntries;
+ return m_statusEntries;
}
bool StatusChecker::isLoadingStatus() const
{
- return m_statusNetJob.get() != nullptr;
+ return m_statusNetJob.get() != nullptr;
}
QString StatusChecker::getLastLoadErrorMsg() const
{
- return m_lastLoadError;
+ return m_lastLoadError;
}
void StatusChecker::succeed()
{
- if(m_prevEntries != m_statusEntries)
- {
- emit statusChanged(m_statusEntries);
- m_prevEntries = m_statusEntries;
- }
- m_lastLoadError = "";
- qDebug() << "Status loading succeeded.";
- m_statusNetJob.reset();
- emit statusLoading(false);
+ if(m_prevEntries != m_statusEntries)
+ {
+ emit statusChanged(m_statusEntries);
+ m_prevEntries = m_statusEntries;
+ }
+ m_lastLoadError = "";
+ qDebug() << "Status loading succeeded.";
+ m_statusNetJob.reset();
+ emit statusLoading(false);
}
void StatusChecker::fail(const QString& errorMsg)
{
- if(m_prevEntries != m_statusEntries)
- {
- emit statusChanged(m_statusEntries);
- m_prevEntries = m_statusEntries;
- }
- m_lastLoadError = errorMsg;
- qDebug() << "Failed to load status:" << errorMsg;
- m_statusNetJob.reset();
- emit statusLoading(false);
+ if(m_prevEntries != m_statusEntries)
+ {
+ emit statusChanged(m_statusEntries);
+ m_prevEntries = m_statusEntries;
+ }
+ m_lastLoadError = errorMsg;
+ qDebug() << "Failed to load status:" << errorMsg;
+ m_statusNetJob.reset();
+ emit statusLoading(false);
}
diff --git a/api/logic/status/StatusChecker.h b/api/logic/status/StatusChecker.h
index f19aba9a..829b139d 100644
--- a/api/logic/status/StatusChecker.h
+++ b/api/logic/status/StatusChecker.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,36 +25,36 @@
class MULTIMC_LOGIC_EXPORT StatusChecker : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public: /* con/des */
- StatusChecker();
+ StatusChecker();
public: /* methods */
- QString getLastLoadErrorMsg() const;
- bool isLoadingStatus() const;
- QMap<QString, QString> getStatusEntries() const;
+ QString getLastLoadErrorMsg() const;
+ bool isLoadingStatus() const;
+ QMap<QString, QString> getStatusEntries() const;
signals:
- void statusLoading(bool loading);
- void statusChanged(QMap<QString, QString> newStatus);
+ void statusLoading(bool loading);
+ void statusChanged(QMap<QString, QString> newStatus);
public slots:
- void reloadStatus();
+ void reloadStatus();
protected: /* methods */
- virtual void timerEvent(QTimerEvent *);
+ virtual void timerEvent(QTimerEvent *);
protected slots:
- void statusDownloadFinished();
- void statusDownloadFailed(QString reason);
- void succeed();
- void fail(const QString& errorMsg);
+ void statusDownloadFinished();
+ void statusDownloadFailed(QString reason);
+ void succeed();
+ void fail(const QString& errorMsg);
protected: /* data */
- QMap<QString, QString> m_prevEntries;
- QMap<QString, QString> m_statusEntries;
- NetJobPtr m_statusNetJob;
- QString m_lastLoadError;
- QByteArray dataSink;
+ QMap<QString, QString> m_prevEntries;
+ QMap<QString, QString> m_statusEntries;
+ NetJobPtr m_statusNetJob;
+ QString m_lastLoadError;
+ QByteArray dataSink;
};
diff --git a/api/logic/tasks/SequentialTask.cpp b/api/logic/tasks/SequentialTask.cpp
index ac0e7820..d0777132 100644
--- a/api/logic/tasks/SequentialTask.cpp
+++ b/api/logic/tasks/SequentialTask.cpp
@@ -6,50 +6,50 @@ SequentialTask::SequentialTask(QObject *parent) : Task(parent), m_currentIndex(-
void SequentialTask::addTask(std::shared_ptr<Task> task)
{
- m_queue.append(task);
+ m_queue.append(task);
}
void SequentialTask::executeTask()
{
- m_currentIndex = -1;
- startNext();
+ m_currentIndex = -1;
+ startNext();
}
void SequentialTask::startNext()
{
- if (m_currentIndex != -1)
- {
- std::shared_ptr<Task> previous = m_queue[m_currentIndex];
- disconnect(previous.get(), 0, this, 0);
- }
- m_currentIndex++;
- if (m_queue.isEmpty() || m_currentIndex >= m_queue.size())
- {
- emitSucceeded();
- return;
- }
- std::shared_ptr<Task> next = m_queue[m_currentIndex];
- connect(next.get(), SIGNAL(failed(QString)), this, SLOT(subTaskFailed(QString)));
- connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString)));
- connect(next.get(), SIGNAL(progress(qint64, qint64)), this, SLOT(subTaskProgress(qint64, qint64)));
- connect(next.get(), SIGNAL(succeeded()), this, SLOT(startNext()));
- next->start();
+ if (m_currentIndex != -1)
+ {
+ std::shared_ptr<Task> previous = m_queue[m_currentIndex];
+ disconnect(previous.get(), 0, this, 0);
+ }
+ m_currentIndex++;
+ if (m_queue.isEmpty() || m_currentIndex >= m_queue.size())
+ {
+ emitSucceeded();
+ return;
+ }
+ std::shared_ptr<Task> next = m_queue[m_currentIndex];
+ connect(next.get(), SIGNAL(failed(QString)), this, SLOT(subTaskFailed(QString)));
+ connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString)));
+ connect(next.get(), SIGNAL(progress(qint64, qint64)), this, SLOT(subTaskProgress(qint64, qint64)));
+ connect(next.get(), SIGNAL(succeeded()), this, SLOT(startNext()));
+ next->start();
}
void SequentialTask::subTaskFailed(const QString &msg)
{
- emitFailed(msg);
+ emitFailed(msg);
}
void SequentialTask::subTaskStatus(const QString &msg)
{
- setStatus(msg);
+ setStatus(msg);
}
void SequentialTask::subTaskProgress(qint64 current, qint64 total)
{
- if(total == 0)
- {
- setProgress(0, 100);
- return;
- }
- setProgress(current, total);
+ if(total == 0)
+ {
+ setProgress(0, 100);
+ return;
+ }
+ setProgress(current, total);
}
diff --git a/api/logic/tasks/SequentialTask.h b/api/logic/tasks/SequentialTask.h
index 69031095..2ca77c00 100644
--- a/api/logic/tasks/SequentialTask.h
+++ b/api/logic/tasks/SequentialTask.h
@@ -9,23 +9,24 @@
class MULTIMC_LOGIC_EXPORT SequentialTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit SequentialTask(QObject *parent = 0);
+ explicit SequentialTask(QObject *parent = 0);
+ virtual ~SequentialTask() {};
- void addTask(std::shared_ptr<Task> task);
+ void addTask(std::shared_ptr<Task> task);
protected:
- void executeTask();
+ void executeTask();
private
slots:
- void startNext();
- void subTaskFailed(const QString &msg);
- void subTaskStatus(const QString &msg);
- void subTaskProgress(qint64 current, qint64 total);
+ void startNext();
+ void subTaskFailed(const QString &msg);
+ void subTaskStatus(const QString &msg);
+ void subTaskProgress(qint64 current, qint64 total);
private:
- QQueue<std::shared_ptr<Task> > m_queue;
- int m_currentIndex;
+ QQueue<std::shared_ptr<Task> > m_queue;
+ int m_currentIndex;
};
diff --git a/api/logic/tasks/Task.cpp b/api/logic/tasks/Task.cpp
index 2523aeb2..accb39b6 100644
--- a/api/logic/tasks/Task.cpp
+++ b/api/logic/tasks/Task.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,124 +23,146 @@ Task::Task(QObject *parent) : QObject(parent)
void Task::setStatus(const QString &new_status)
{
- if(m_status != new_status)
- {
- m_status = new_status;
- emit status(m_status);
- }
+ if(m_status != new_status)
+ {
+ m_status = new_status;
+ emit status(m_status);
+ }
}
void Task::setProgress(qint64 current, qint64 total)
{
- m_progress = current;
- m_progressTotal = total;
- emit progress(m_progress, m_progressTotal);
+ m_progress = current;
+ m_progressTotal = total;
+ emit progress(m_progress, m_progressTotal);
}
void Task::start()
{
- m_running = true;
- emit started();
- qDebug() << "Task" << describe() << "started";
- executeTask();
+ switch(m_state)
+ {
+ case State::Inactive:
+ {
+ qDebug() << "Task" << describe() << "starting for the first time";
+ break;
+ }
+ case State::AbortedByUser:
+ {
+ qDebug() << "Task" << describe() << "restarting for after being aborted by user";
+ break;
+ }
+ case State::Failed:
+ {
+ qDebug() << "Task" << describe() << "restarting for after failing at first";
+ break;
+ }
+ case State::Succeeded:
+ {
+ qDebug() << "Task" << describe() << "restarting for after succeeding at first";
+ break;
+ }
+ case State::Running:
+ {
+ qWarning() << "MultiMC tried to start task" << describe() << "while it was already running!";
+ return;
+ }
+ }
+ // NOTE: only fall thorugh to here in end states
+ m_state = State::Running;
+ emit started();
+ executeTask();
}
void Task::emitFailed(QString reason)
{
- // Don't fail twice.
- if (!m_running)
- {
- qCritical() << "Task" << describe() << "failed while not running!!!!: " << reason;
- return;
- }
- m_running = false;
- m_finished = true;
- m_succeeded = false;
- m_failReason = reason;
- qCritical() << "Task" << describe() << "failed: " << reason;
- emit failed(reason);
- emit finished();
+ // Don't fail twice.
+ if (!isRunning())
+ {
+ qCritical() << "Task" << describe() << "failed while not running!!!!: " << reason;
+ return;
+ }
+ m_state = State::Failed;
+ m_failReason = reason;
+ qCritical() << "Task" << describe() << "failed: " << reason;
+ emit failed(reason);
+ emit finished();
}
void Task::emitAborted()
{
- // Don't abort twice.
- if (!m_running)
- {
- qCritical() << "Task" << describe() << "aborted while not running!!!!";
- return;
- }
- m_running = false;
- m_finished = true;
- m_succeeded = false;
- m_failReason = "Aborted.";
- qDebug() << "Task" << describe() << "aborted.";
- emit failed(m_failReason);
- emit finished();
+ // Don't abort twice.
+ if (!isRunning())
+ {
+ qCritical() << "Task" << describe() << "aborted while not running!!!!";
+ return;
+ }
+ m_state = State::AbortedByUser;
+ m_failReason = "Aborted.";
+ qDebug() << "Task" << describe() << "aborted.";
+ emit failed(m_failReason);
+ emit finished();
}
void Task::emitSucceeded()
{
- // Don't succeed twice.
- if (!m_running)
- {
- qCritical() << "Task" << describe() << "succeeded while not running!!!!";
- return;
- }
- m_running = false;
- m_finished = true;
- m_succeeded = true;
- qDebug() << "Task" << describe() << "succeeded";
- emit succeeded();
- emit finished();
+ // Don't succeed twice.
+ if (!isRunning())
+ {
+ qCritical() << "Task" << describe() << "succeeded while not running!!!!";
+ return;
+ }
+ m_state = State::Succeeded;
+ qDebug() << "Task" << describe() << "succeeded";
+ emit succeeded();
+ emit finished();
}
QString Task::describe()
{
- QString outStr;
- QTextStream out(&outStr);
- out << metaObject()->className() << QChar('(');
- auto name = objectName();
- if(name.isEmpty())
- {
- out << QString("0x%1").arg((quintptr)this, 0, 16);
- }
- else
- {
- out << name;
- }
- out << QChar(')');
- out.flush();
- return outStr;
+ QString outStr;
+ QTextStream out(&outStr);
+ out << metaObject()->className() << QChar('(');
+ auto name = objectName();
+ if(name.isEmpty())
+ {
+ out << QString("0x%1").arg((quintptr)this, 0, 16);
+ }
+ else
+ {
+ out << name;
+ }
+ out << QChar(')');
+ out.flush();
+ return outStr;
}
bool Task::isRunning() const
{
- return m_running;
+ return m_state == State::Running;
}
bool Task::isFinished() const
{
- return m_finished;
+ return m_state != State::Running && m_state != State::Inactive;
}
bool Task::wasSuccessful() const
{
- return m_succeeded;
+ return m_state == State::Succeeded;
}
QString Task::failReason() const
{
- return m_failReason;
+ return m_failReason;
}
void Task::logWarning(const QString& line)
{
- qWarning() << line;
- m_Warnings.append(line);
+ qWarning() << line;
+ m_Warnings.append(line);
}
QStringList Task::warnings() const
{
- return m_Warnings;
+ return m_Warnings;
}
diff --git a/api/logic/tasks/Task.h b/api/logic/tasks/Task.h
index 643f8510..72bfeca8 100644
--- a/api/logic/tasks/Task.h
+++ b/api/logic/tasks/Task.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,78 +23,86 @@
class MULTIMC_LOGIC_EXPORT Task : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit Task(QObject *parent = 0);
- virtual ~Task() {};
+ enum class State
+ {
+ Inactive,
+ Running,
+ Succeeded,
+ Failed,
+ AbortedByUser
+ };
- bool isRunning() const;
- bool isFinished() const;
- bool wasSuccessful() const;
+public:
+ explicit Task(QObject *parent = 0);
+ virtual ~Task() {};
+
+ bool isRunning() const;
+ bool isFinished() const;
+ bool wasSuccessful() const;
- /*!
- * Returns the string that was passed to emitFailed as the error message when the task failed.
- * If the task hasn't failed, returns an empty string.
- */
- QString failReason() const;
+ /*!
+ * Returns the string that was passed to emitFailed as the error message when the task failed.
+ * If the task hasn't failed, returns an empty string.
+ */
+ QString failReason() const;
- virtual QStringList warnings() const;
+ virtual QStringList warnings() const;
- virtual bool canAbort() const { return false; }
+ virtual bool canAbort() const { return false; }
- QString getStatus()
- {
- return m_status;
- }
+ QString getStatus()
+ {
+ return m_status;
+ }
- qint64 getProgress()
- {
- return m_progress;
- }
+ qint64 getProgress()
+ {
+ return m_progress;
+ }
- qint64 getTotalProgress()
- {
- return m_progressTotal;
- }
+ qint64 getTotalProgress()
+ {
+ return m_progressTotal;
+ }
protected:
- void logWarning(const QString & line);
+ void logWarning(const QString & line);
private:
- QString describe();
+ QString describe();
signals:
- void started();
- void progress(qint64 current, qint64 total);
- void finished();
- void succeeded();
- void failed(QString reason);
- void status(QString status);
+ void started();
+ void progress(qint64 current, qint64 total);
+ void finished();
+ void succeeded();
+ void failed(QString reason);
+ void status(QString status);
public slots:
- virtual void start();
- virtual bool abort() { return false; };
+ virtual void start();
+ virtual bool abort() { return false; };
protected:
- virtual void executeTask() = 0;
+ virtual void executeTask() = 0;
protected slots:
- virtual void emitSucceeded();
- virtual void emitAborted();
- virtual void emitFailed(QString reason);
+ virtual void emitSucceeded();
+ virtual void emitAborted();
+ virtual void emitFailed(QString reason);
public slots:
- void setStatus(const QString &status);
- void setProgress(qint64 current, qint64 total);
+ void setStatus(const QString &status);
+ void setProgress(qint64 current, qint64 total);
private:
- bool m_running = false;
- bool m_finished = false;
- bool m_succeeded = false;
- QStringList m_Warnings;
- QString m_failReason = "";
- QString m_status;
- int m_progress = 0;
- int m_progressTotal = 100;
+ State m_state = State::Inactive;
+ QStringList m_Warnings;
+ QString m_failReason = "";
+ QString m_status;
+ int m_progress = 0;
+ int m_progressTotal = 100;
};
diff --git a/api/logic/tools/BaseExternalTool.cpp b/api/logic/tools/BaseExternalTool.cpp
index 2b97c3c9..38d81788 100644
--- a/api/logic/tools/BaseExternalTool.cpp
+++ b/api/logic/tools/BaseExternalTool.cpp
@@ -10,7 +10,7 @@
#include "BaseInstance.h"
BaseExternalTool::BaseExternalTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent)
- : QObject(parent), m_instance(instance), globalSettings(settings)
+ : QObject(parent), m_instance(instance), globalSettings(settings)
{
}
@@ -19,14 +19,14 @@ BaseExternalTool::~BaseExternalTool()
}
BaseDetachedTool::BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent)
- : BaseExternalTool(settings, instance, parent)
+ : BaseExternalTool(settings, instance, parent)
{
}
void BaseDetachedTool::run()
{
- runImpl();
+ runImpl();
}
@@ -35,7 +35,7 @@ BaseExternalToolFactory::~BaseExternalToolFactory()
}
BaseDetachedTool *BaseDetachedToolFactory::createDetachedTool(InstancePtr instance,
- QObject *parent)
+ QObject *parent)
{
- return qobject_cast<BaseDetachedTool *>(createTool(instance, parent));
+ return qobject_cast<BaseDetachedTool *>(createTool(instance, parent));
}
diff --git a/api/logic/tools/BaseExternalTool.h b/api/logic/tools/BaseExternalTool.h
index fe1b5dc6..b393b9ef 100644
--- a/api/logic/tools/BaseExternalTool.h
+++ b/api/logic/tools/BaseExternalTool.h
@@ -11,50 +11,50 @@ class QProcess;
class MULTIMC_LOGIC_EXPORT BaseExternalTool : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit BaseExternalTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
- virtual ~BaseExternalTool();
+ explicit BaseExternalTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
+ virtual ~BaseExternalTool();
protected:
- InstancePtr m_instance;
- SettingsObjectPtr globalSettings;
+ InstancePtr m_instance;
+ SettingsObjectPtr globalSettings;
};
class MULTIMC_LOGIC_EXPORT BaseDetachedTool : public BaseExternalTool
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
+ explicit BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
public
slots:
- void run();
+ void run();
protected:
- virtual void runImpl() = 0;
+ virtual void runImpl() = 0;
};
class MULTIMC_LOGIC_EXPORT BaseExternalToolFactory
{
public:
- virtual ~BaseExternalToolFactory();
+ virtual ~BaseExternalToolFactory();
- virtual QString name() const = 0;
+ virtual QString name() const = 0;
- virtual void registerSettings(SettingsObjectPtr settings) = 0;
+ virtual void registerSettings(SettingsObjectPtr settings) = 0;
- virtual BaseExternalTool *createTool(InstancePtr instance, QObject *parent = 0) = 0;
+ virtual BaseExternalTool *createTool(InstancePtr instance, QObject *parent = 0) = 0;
- virtual bool check(QString *error) = 0;
- virtual bool check(const QString &path, QString *error) = 0;
+ virtual bool check(QString *error) = 0;
+ virtual bool check(const QString &path, QString *error) = 0;
protected:
- SettingsObjectPtr globalSettings;
+ SettingsObjectPtr globalSettings;
};
class MULTIMC_LOGIC_EXPORT BaseDetachedToolFactory : public BaseExternalToolFactory
{
public:
- virtual BaseDetachedTool *createDetachedTool(InstancePtr instance, QObject *parent = 0);
+ virtual BaseDetachedTool *createDetachedTool(InstancePtr instance, QObject *parent = 0);
};
diff --git a/api/logic/tools/BaseProfiler.cpp b/api/logic/tools/BaseProfiler.cpp
index 5ff0fa44..300d1a73 100644
--- a/api/logic/tools/BaseProfiler.cpp
+++ b/api/logic/tools/BaseProfiler.cpp
@@ -1,35 +1,36 @@
#include "BaseProfiler.h"
+#include "QObjectPtr.h"
#include <QProcess>
BaseProfiler::BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent)
- : BaseExternalTool(settings, instance, parent)
+ : BaseExternalTool(settings, instance, parent)
{
}
-void BaseProfiler::beginProfiling(std::shared_ptr<LaunchTask> process)
+void BaseProfiler::beginProfiling(shared_qobject_ptr<LaunchTask> process)
{
- beginProfilingImpl(process);
+ beginProfilingImpl(process);
}
void BaseProfiler::abortProfiling()
{
- abortProfilingImpl();
+ abortProfilingImpl();
}
void BaseProfiler::abortProfilingImpl()
{
- if (!m_profilerProcess)
- {
- return;
- }
- m_profilerProcess->terminate();
- m_profilerProcess->deleteLater();
- m_profilerProcess = 0;
- emit abortLaunch(tr("Profiler aborted"));
+ if (!m_profilerProcess)
+ {
+ return;
+ }
+ m_profilerProcess->terminate();
+ m_profilerProcess->deleteLater();
+ m_profilerProcess = 0;
+ emit abortLaunch(tr("Profiler aborted"));
}
BaseProfiler *BaseProfilerFactory::createProfiler(InstancePtr instance, QObject *parent)
{
- return qobject_cast<BaseProfiler *>(createTool(instance, parent));
+ return qobject_cast<BaseProfiler *>(createTool(instance, parent));
}
diff --git a/api/logic/tools/BaseProfiler.h b/api/logic/tools/BaseProfiler.h
index 3340b7e4..da817f52 100644
--- a/api/logic/tools/BaseProfiler.h
+++ b/api/logic/tools/BaseProfiler.h
@@ -1,6 +1,7 @@
#pragma once
#include "BaseExternalTool.h"
+#include "QObjectPtr.h"
#include "multimc_logic_export.h"
@@ -11,28 +12,28 @@ class QProcess;
class MULTIMC_LOGIC_EXPORT BaseProfiler : public BaseExternalTool
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
+ explicit BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
public
slots:
- void beginProfiling(std::shared_ptr<LaunchTask> process);
- void abortProfiling();
+ void beginProfiling(shared_qobject_ptr<LaunchTask> process);
+ void abortProfiling();
protected:
- QProcess *m_profilerProcess;
+ QProcess *m_profilerProcess;
- virtual void beginProfilingImpl(std::shared_ptr<LaunchTask> process) = 0;
- virtual void abortProfilingImpl();
+ virtual void beginProfilingImpl(shared_qobject_ptr<LaunchTask> process) = 0;
+ virtual void abortProfilingImpl();
signals:
- void readyToLaunch(const QString &message);
- void abortLaunch(const QString &message);
+ void readyToLaunch(const QString &message);
+ void abortLaunch(const QString &message);
};
class MULTIMC_LOGIC_EXPORT BaseProfilerFactory : public BaseExternalToolFactory
{
public:
- virtual BaseProfiler *createProfiler(InstancePtr instance, QObject *parent = 0);
+ virtual BaseProfiler *createProfiler(InstancePtr instance, QObject *parent = 0);
};
diff --git a/api/logic/tools/JProfiler.cpp b/api/logic/tools/JProfiler.cpp
index a0e3c895..1dc0d109 100644
--- a/api/logic/tools/JProfiler.cpp
+++ b/api/logic/tools/JProfiler.cpp
@@ -8,109 +8,109 @@
class JProfiler : public BaseProfiler
{
- Q_OBJECT
+ Q_OBJECT
public:
- JProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
+ JProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
private slots:
- void profilerStarted();
- void profilerFinished(int exit, QProcess::ExitStatus status);
+ void profilerStarted();
+ void profilerFinished(int exit, QProcess::ExitStatus status);
protected:
- void beginProfilingImpl(std::shared_ptr<LaunchTask> process);
+ void beginProfilingImpl(shared_qobject_ptr<LaunchTask> process);
private:
- int listeningPort = 0;
+ int listeningPort = 0;
};
JProfiler::JProfiler(SettingsObjectPtr settings, InstancePtr instance,
- QObject *parent)
- : BaseProfiler(settings, instance, parent)
+ QObject *parent)
+ : BaseProfiler(settings, instance, parent)
{
}
void JProfiler::profilerStarted()
{
- emit readyToLaunch(tr("Listening on port: %1").arg(listeningPort));
+ emit readyToLaunch(tr("Listening on port: %1").arg(listeningPort));
}
void JProfiler::profilerFinished(int exit, QProcess::ExitStatus status)
{
- if (status == QProcess::CrashExit)
- {
- emit abortLaunch(tr("Profiler aborted"));
- }
- if (m_profilerProcess)
- {
- m_profilerProcess->deleteLater();
- m_profilerProcess = 0;
- }
+ if (status == QProcess::CrashExit)
+ {
+ emit abortLaunch(tr("Profiler aborted"));
+ }
+ if (m_profilerProcess)
+ {
+ m_profilerProcess->deleteLater();
+ m_profilerProcess = 0;
+ }
}
-void JProfiler::beginProfilingImpl(std::shared_ptr<LaunchTask> process)
+void JProfiler::beginProfilingImpl(shared_qobject_ptr<LaunchTask> process)
{
- listeningPort = globalSettings->get("JProfilerPort").toInt();
- QProcess *profiler = new QProcess(this);
- QStringList profilerArgs =
- {
- "-d", QString::number(process->pid()),
- "--gui",
- "-p", QString::number(listeningPort)
- };
- auto basePath = globalSettings->get("JProfilerPath").toString();
+ listeningPort = globalSettings->get("JProfilerPort").toInt();
+ QProcess *profiler = new QProcess(this);
+ QStringList profilerArgs =
+ {
+ "-d", QString::number(process->pid()),
+ "--gui",
+ "-p", QString::number(listeningPort)
+ };
+ auto basePath = globalSettings->get("JProfilerPath").toString();
#ifdef Q_OS_WIN
- QString profilerProgram = QDir(basePath).absoluteFilePath("bin/jpenable.exe");
+ QString profilerProgram = QDir(basePath).absoluteFilePath("bin/jpenable.exe");
#else
- QString profilerProgram = QDir(basePath).absoluteFilePath("bin/jpenable");
+ QString profilerProgram = QDir(basePath).absoluteFilePath("bin/jpenable");
#endif
- profiler->setArguments(profilerArgs);
- profiler->setProgram(profilerProgram);
+ profiler->setArguments(profilerArgs);
+ profiler->setProgram(profilerProgram);
- connect(profiler, SIGNAL(started()), SLOT(profilerStarted()));
- connect(profiler, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(profilerFinished(int,QProcess::ExitStatus)));
+ connect(profiler, SIGNAL(started()), SLOT(profilerStarted()));
+ connect(profiler, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(profilerFinished(int,QProcess::ExitStatus)));
- m_profilerProcess = profiler;
- profiler->start();
+ m_profilerProcess = profiler;
+ profiler->start();
}
void JProfilerFactory::registerSettings(SettingsObjectPtr settings)
{
- settings->registerSetting("JProfilerPath");
- settings->registerSetting("JProfilerPort", 42042);
- globalSettings = settings;
+ settings->registerSetting("JProfilerPath");
+ settings->registerSetting("JProfilerPort", 42042);
+ globalSettings = settings;
}
BaseExternalTool *JProfilerFactory::createTool(InstancePtr instance, QObject *parent)
{
- return new JProfiler(globalSettings, instance, parent);
+ return new JProfiler(globalSettings, instance, parent);
}
bool JProfilerFactory::check(QString *error)
{
- return check(globalSettings->get("JProfilerPath").toString(), error);
+ return check(globalSettings->get("JProfilerPath").toString(), error);
}
bool JProfilerFactory::check(const QString &path, QString *error)
{
- if (path.isEmpty())
- {
- *error = QObject::tr("Empty path");
- return false;
- }
- QDir dir(path);
- if (!dir.exists())
- {
- *error = QObject::tr("Path does not exist");
- return false;
- }
- if (!dir.exists("bin") || !(dir.exists("bin/jprofiler") || dir.exists("bin/jprofiler.exe")) || !dir.exists("bin/agent.jar"))
- {
- *error = QObject::tr("Invalid JProfiler install");
- return false;
- }
- return true;
+ if (path.isEmpty())
+ {
+ *error = QObject::tr("Empty path");
+ return false;
+ }
+ QDir dir(path);
+ if (!dir.exists())
+ {
+ *error = QObject::tr("Path does not exist");
+ return false;
+ }
+ if (!dir.exists("bin") || !(dir.exists("bin/jprofiler") || dir.exists("bin/jprofiler.exe")) || !dir.exists("bin/agent.jar"))
+ {
+ *error = QObject::tr("Invalid JProfiler install");
+ return false;
+ }
+ return true;
}
#include "JProfiler.moc"
diff --git a/api/logic/tools/JProfiler.h b/api/logic/tools/JProfiler.h
index d658d6c2..f211ddbf 100644
--- a/api/logic/tools/JProfiler.h
+++ b/api/logic/tools/JProfiler.h
@@ -7,9 +7,9 @@
class MULTIMC_LOGIC_EXPORT JProfilerFactory : public BaseProfilerFactory
{
public:
- QString name() const override { return "JProfiler"; }
- void registerSettings(SettingsObjectPtr settings) override;
- BaseExternalTool *createTool(InstancePtr instance, QObject *parent = 0) override;
- bool check(QString *error) override;
- bool check(const QString &path, QString *error) override;
+ QString name() const override { return "JProfiler"; }
+ void registerSettings(SettingsObjectPtr settings) override;
+ BaseExternalTool *createTool(InstancePtr instance, QObject *parent = 0) override;
+ bool check(QString *error) override;
+ bool check(const QString &path, QString *error) override;
};
diff --git a/api/logic/tools/JVisualVM.cpp b/api/logic/tools/JVisualVM.cpp
index 8fdb594f..b1acc3c0 100644
--- a/api/logic/tools/JVisualVM.cpp
+++ b/api/logic/tools/JVisualVM.cpp
@@ -9,96 +9,96 @@
class JVisualVM : public BaseProfiler
{
- Q_OBJECT
+ Q_OBJECT
public:
- JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
+ JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
private slots:
- void profilerStarted();
- void profilerFinished(int exit, QProcess::ExitStatus status);
+ void profilerStarted();
+ void profilerFinished(int exit, QProcess::ExitStatus status);
protected:
- void beginProfilingImpl(std::shared_ptr<LaunchTask> process);
+ void beginProfilingImpl(shared_qobject_ptr<LaunchTask> process);
};
JVisualVM::JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject *parent)
- : BaseProfiler(settings, instance, parent)
+ : BaseProfiler(settings, instance, parent)
{
}
void JVisualVM::profilerStarted()
{
- emit readyToLaunch(tr("JVisualVM started"));
+ emit readyToLaunch(tr("JVisualVM started"));
}
void JVisualVM::profilerFinished(int exit, QProcess::ExitStatus status)
{
- if (status == QProcess::CrashExit)
- {
- emit abortLaunch(tr("Profiler aborted"));
- }
- if (m_profilerProcess)
- {
- m_profilerProcess->deleteLater();
- m_profilerProcess = 0;
- }
+ if (status == QProcess::CrashExit)
+ {
+ emit abortLaunch(tr("Profiler aborted"));
+ }
+ if (m_profilerProcess)
+ {
+ m_profilerProcess->deleteLater();
+ m_profilerProcess = 0;
+ }
}
-void JVisualVM::beginProfilingImpl(std::shared_ptr<LaunchTask> process)
+void JVisualVM::beginProfilingImpl(shared_qobject_ptr<LaunchTask> process)
{
- QProcess *profiler = new QProcess(this);
- QStringList profilerArgs =
- {
- "--openpid", QString::number(process->pid())
- };
- auto programPath = globalSettings->get("JVisualVMPath").toString();
+ QProcess *profiler = new QProcess(this);
+ QStringList profilerArgs =
+ {
+ "--openpid", QString::number(process->pid())
+ };
+ auto programPath = globalSettings->get("JVisualVMPath").toString();
- profiler->setArguments(profilerArgs);
- profiler->setProgram(programPath);
+ profiler->setArguments(profilerArgs);
+ profiler->setProgram(programPath);
- connect(profiler, SIGNAL(started()), SLOT(profilerStarted()));
- connect(profiler, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(profilerFinished(int,QProcess::ExitStatus)));
+ connect(profiler, SIGNAL(started()), SLOT(profilerStarted()));
+ connect(profiler, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(profilerFinished(int,QProcess::ExitStatus)));
- profiler->start();
- m_profilerProcess = profiler;
+ profiler->start();
+ m_profilerProcess = profiler;
}
void JVisualVMFactory::registerSettings(SettingsObjectPtr settings)
{
- QString defaultValue = QStandardPaths::findExecutable("jvisualvm");
- if (defaultValue.isNull())
- {
- defaultValue = QStandardPaths::findExecutable("visualvm");
- }
- settings->registerSetting("JVisualVMPath", defaultValue);
- globalSettings = settings;
+ QString defaultValue = QStandardPaths::findExecutable("jvisualvm");
+ if (defaultValue.isNull())
+ {
+ defaultValue = QStandardPaths::findExecutable("visualvm");
+ }
+ settings->registerSetting("JVisualVMPath", defaultValue);
+ globalSettings = settings;
}
BaseExternalTool *JVisualVMFactory::createTool(InstancePtr instance, QObject *parent)
{
- return new JVisualVM(globalSettings, instance, parent);
+ return new JVisualVM(globalSettings, instance, parent);
}
bool JVisualVMFactory::check(QString *error)
{
- return check(globalSettings->get("JVisualVMPath").toString(), error);
+ return check(globalSettings->get("JVisualVMPath").toString(), error);
}
bool JVisualVMFactory::check(const QString &path, QString *error)
{
- if (path.isEmpty())
- {
- *error = QObject::tr("Empty path");
- return false;
- }
- QFileInfo finfo(path);
- if (!finfo.isExecutable() || !finfo.fileName().contains("visualvm"))
- {
- *error = QObject::tr("Invalid path to JVisualVM");
- return false;
- }
- return true;
+ if (path.isEmpty())
+ {
+ *error = QObject::tr("Empty path");
+ return false;
+ }
+ QFileInfo finfo(path);
+ if (!finfo.isExecutable() || !finfo.fileName().contains("visualvm"))
+ {
+ *error = QObject::tr("Invalid path to JVisualVM");
+ return false;
+ }
+ return true;
}
#include "JVisualVM.moc"
diff --git a/api/logic/tools/JVisualVM.h b/api/logic/tools/JVisualVM.h
index 0674da13..91d48d94 100644
--- a/api/logic/tools/JVisualVM.h
+++ b/api/logic/tools/JVisualVM.h
@@ -7,9 +7,9 @@
class MULTIMC_LOGIC_EXPORT JVisualVMFactory : public BaseProfilerFactory
{
public:
- QString name() const override { return "JVisualVM"; }
- void registerSettings(SettingsObjectPtr settings) override;
- BaseExternalTool *createTool(InstancePtr instance, QObject *parent = 0) override;
- bool check(QString *error) override;
- bool check(const QString &path, QString *error) override;
+ QString name() const override { return "JVisualVM"; }
+ void registerSettings(SettingsObjectPtr settings) override;
+ BaseExternalTool *createTool(InstancePtr instance, QObject *parent = 0) override;
+ bool check(QString *error) override;
+ bool check(const QString &path, QString *error) override;
};
diff --git a/api/logic/tools/MCEditTool.cpp b/api/logic/tools/MCEditTool.cpp
index 74715d3f..880327c7 100644
--- a/api/logic/tools/MCEditTool.cpp
+++ b/api/logic/tools/MCEditTool.cpp
@@ -10,68 +10,68 @@
MCEditTool::MCEditTool(SettingsObjectPtr settings)
{
- settings->registerSetting("MCEditPath");
- m_settings = settings;
+ settings->registerSetting("MCEditPath");
+ m_settings = settings;
}
void MCEditTool::setPath(QString& path)
{
- m_settings->set("MCEditPath", path);
+ m_settings->set("MCEditPath", path);
}
QString MCEditTool::path() const
{
- return m_settings->get("MCEditPath").toString();
+ return m_settings->get("MCEditPath").toString();
}
bool MCEditTool::check(const QString& toolPath, QString& error)
{
- if (toolPath.isEmpty())
- {
- error = QObject::tr("Path is empty");
- return false;
- }
- const QDir dir(toolPath);
- if (!dir.exists())
- {
- error = QObject::tr("Path does not exist");
- return false;
- }
- if (!dir.exists("mcedit.sh") && !dir.exists("mcedit.py") && !dir.exists("mcedit.exe") && !dir.exists("Contents") && !dir.exists("mcedit2.exe"))
- {
- error = QObject::tr("Path does not seem to be a MCEdit path");
- return false;
- }
- return true;
+ if (toolPath.isEmpty())
+ {
+ error = QObject::tr("Path is empty");
+ return false;
+ }
+ const QDir dir(toolPath);
+ if (!dir.exists())
+ {
+ error = QObject::tr("Path does not exist");
+ return false;
+ }
+ if (!dir.exists("mcedit.sh") && !dir.exists("mcedit.py") && !dir.exists("mcedit.exe") && !dir.exists("Contents") && !dir.exists("mcedit2.exe"))
+ {
+ error = QObject::tr("Path does not seem to be a MCEdit path");
+ return false;
+ }
+ return true;
}
QString MCEditTool::getProgramPath()
{
#ifdef Q_OS_OSX
- return path();
+ return path();
#else
- const QString mceditPath = path();
- QDir mceditDir(mceditPath);
+ const QString mceditPath = path();
+ QDir mceditDir(mceditPath);
#ifdef Q_OS_LINUX
- if (mceditDir.exists("mcedit.sh"))
- {
- return mceditDir.absoluteFilePath("mcedit.sh");
- }
- else if (mceditDir.exists("mcedit.py"))
- {
- return mceditDir.absoluteFilePath("mcedit.py");
- }
- return QString();
+ if (mceditDir.exists("mcedit.sh"))
+ {
+ return mceditDir.absoluteFilePath("mcedit.sh");
+ }
+ else if (mceditDir.exists("mcedit.py"))
+ {
+ return mceditDir.absoluteFilePath("mcedit.py");
+ }
+ return QString();
#elif defined(Q_OS_WIN32)
- if (mceditDir.exists("mcedit.exe"))
- {
- return mceditDir.absoluteFilePath("mcedit.exe");
- }
- else if (mceditDir.exists("mcedit2.exe"))
- {
- return mceditDir.absoluteFilePath("mcedit2.exe");
- }
- return QString();
+ if (mceditDir.exists("mcedit.exe"))
+ {
+ return mceditDir.absoluteFilePath("mcedit.exe");
+ }
+ else if (mceditDir.exists("mcedit2.exe"))
+ {
+ return mceditDir.absoluteFilePath("mcedit2.exe");
+ }
+ return QString();
#endif
#endif
}
diff --git a/api/logic/tools/MCEditTool.h b/api/logic/tools/MCEditTool.h
index 51feb1fe..1465494e 100644
--- a/api/logic/tools/MCEditTool.h
+++ b/api/logic/tools/MCEditTool.h
@@ -7,11 +7,11 @@
class MULTIMC_LOGIC_EXPORT MCEditTool
{
public:
- MCEditTool(SettingsObjectPtr settings);
- void setPath(QString & path);
- QString path() const;
- bool check(const QString &toolPath, QString &error);
- QString getProgramPath();
+ MCEditTool(SettingsObjectPtr settings);
+ void setPath(QString & path);
+ QString path() const;
+ bool check(const QString &toolPath, QString &error);
+ QString getProgramPath();
private:
- SettingsObjectPtr m_settings;
+ SettingsObjectPtr m_settings;
};
diff --git a/api/logic/translations/POTranslator.cpp b/api/logic/translations/POTranslator.cpp
new file mode 100644
index 00000000..1ffcb9a4
--- /dev/null
+++ b/api/logic/translations/POTranslator.cpp
@@ -0,0 +1,373 @@
+#include "POTranslator.h"
+
+#include <QDebug>
+#include "FileSystem.h"
+
+struct POEntry
+{
+ QString text;
+ bool fuzzy;
+};
+
+struct POTranslatorPrivate
+{
+ QString filename;
+ QHash<QByteArray, POEntry> mapping;
+ QHash<QByteArray, POEntry> mapping_disambiguatrion;
+ bool loaded = false;
+
+ void reload();
+};
+
+class ParserArray : public QByteArray
+{
+public:
+ ParserArray(const QByteArray &in) : QByteArray(in)
+ {
+ }
+ bool chomp(const char * data, int length)
+ {
+ if(startsWith(data))
+ {
+ remove(0, length);
+ return true;
+ }
+ return false;
+ }
+ bool chompString(QByteArray & appendHere)
+ {
+ QByteArray msg;
+ bool escape = false;
+ if(size() < 2)
+ {
+ qDebug() << "String fragment is too short";
+ return false;
+ }
+ if(!startsWith('"'))
+ {
+ qDebug() << "String fragment does not start with \"";
+ return false;
+ }
+ if(!endsWith('"'))
+ {
+ qDebug() << "String fragment does not end with \", instead, there is" << at(size() - 1);
+ return false;
+ }
+ for(int i = 1; i < size() - 1; i++)
+ {
+ char c = operator[](i);
+ if(escape)
+ {
+ switch(c)
+ {
+ case 'r':
+ msg += '\r';
+ break;
+ case 'n':
+ msg += '\n';
+ break;
+ case 't':
+ msg += '\t';
+ break;
+ case 'v':
+ msg += '\v';
+ break;
+ case 'a':
+ msg += '\a';
+ break;
+ case 'b':
+ msg += '\b';
+ break;
+ case 'f':
+ msg += '\f';
+ break;
+ case '"':
+ msg += '"';
+ break;
+ case '\\':
+ msg.append('\\');
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int octal_start = i;
+ while ((c = operator[](i)) >= '0' && c <= '7')
+ {
+ i++;
+ if (i == length() - 1)
+ {
+ qDebug() << "Something went bad while parsing an octal escape string...";
+ return false;
+ }
+ }
+ msg += mid(octal_start, i - octal_start).toUInt(0, 8);
+ break;
+ }
+ case 'x':
+ {
+ // chomp the 'x'
+ i++;
+ int hex_start = i;
+ while (isxdigit(operator[](i)))
+ {
+ i++;
+ if (i == length() - 1)
+ {
+ qDebug() << "Something went bad while parsing a hex escape string...";
+ return false;
+ }
+ }
+ msg += mid(hex_start, i - hex_start).toUInt(0, 16);
+ break;
+ }
+ default:
+ {
+ qDebug() << "Invalid escape sequence character:" << c;
+ return false;
+ }
+ }
+ escape = false;
+ }
+ else if(c == '\\')
+ {
+ escape = true;
+ }
+ else
+ {
+ msg += c;
+ }
+ }
+ if(escape)
+ {
+ qDebug() << "Unterminated escape sequence...";
+ return false;
+ }
+ appendHere += msg;
+ return true;
+ }
+};
+
+void POTranslatorPrivate::reload()
+{
+ QFile file(filename);
+ if(!file.open(QFile::OpenMode::enum_type::ReadOnly | QFile::OpenMode::enum_type::Text))
+ {
+ qDebug() << "Failed to open PO file:" << filename;
+ return;
+ }
+
+ QByteArray context;
+ QByteArray disambiguation;
+ QByteArray id;
+ QByteArray str;
+ bool fuzzy = false;
+ bool nextFuzzy = false;
+
+ enum class Mode
+ {
+ First,
+ MessageContext,
+ MessageId,
+ MessageString
+ } mode = Mode::First;
+
+ int lineNumber = 0;
+ QHash<QByteArray, POEntry> newMapping;
+ QHash<QByteArray, POEntry> newMapping_disambiguation;
+ auto endEntry = [&]() {
+ auto strStr = QString::fromUtf8(str);
+ // NOTE: PO header has empty id. We skip it.
+ if(!id.isEmpty())
+ {
+ auto normalKey = context + "|" + id;
+ newMapping.insert(normalKey, {strStr, fuzzy});
+ if(!disambiguation.isEmpty())
+ {
+ auto disambiguationKey = context + "|" + id + "@" + disambiguation;
+ newMapping_disambiguation.insert(disambiguationKey, {strStr, fuzzy});
+ }
+ }
+ context.clear();
+ disambiguation.clear();
+ id.clear();
+ str.clear();
+ fuzzy = nextFuzzy;
+ nextFuzzy = false;
+ };
+ while (!file.atEnd())
+ {
+ ParserArray line = file.readLine();
+ if(line.endsWith('\n'))
+ {
+ line.resize(line.size() - 1);
+ }
+ if(line.endsWith('\r'))
+ {
+ line.resize(line.size() - 1);
+ }
+
+ if(!line.size())
+ {
+ // NIL
+ }
+ else if(line[0] == '#')
+ {
+ if(line.contains(", fuzzy"))
+ {
+ nextFuzzy = true;
+ }
+ }
+ else if(line.startsWith('"'))
+ {
+ QByteArray temp;
+ QByteArray *out = &temp;
+
+ switch(mode)
+ {
+ case Mode::First:
+ qDebug() << "Unexpected escaped string during initial state... line:" << lineNumber;
+ return;
+ case Mode::MessageString:
+ out = &str;
+ break;
+ case Mode::MessageContext:
+ out = &context;
+ break;
+ case Mode::MessageId:
+ out = &id;
+ break;
+ }
+ if(!line.chompString(*out))
+ {
+ qDebug() << "Badly formatted string on line:" << lineNumber;
+ return;
+ }
+ }
+ else if(line.chomp("msgctxt ", 8))
+ {
+ switch(mode)
+ {
+ case Mode::First:
+ break;
+ case Mode::MessageString:
+ endEntry();
+ break;
+ case Mode::MessageContext:
+ case Mode::MessageId:
+ qDebug() << "Unexpected msgctxt line:" << lineNumber;
+ return;
+ }
+ if(line.chompString(context))
+ {
+ auto parts = context.split('|');
+ context = parts[0];
+ if(parts.size() > 1 && !parts[1].isEmpty())
+ {
+ disambiguation = parts[1];
+ }
+ mode = Mode::MessageContext;
+ }
+ }
+ else if (line.chomp("msgid ", 6))
+ {
+ switch(mode)
+ {
+ case Mode::MessageContext:
+ case Mode::First:
+ break;
+ case Mode::MessageString:
+ endEntry();
+ break;
+ case Mode::MessageId:
+ qDebug() << "Unexpected msgid line:" << lineNumber;
+ return;
+ }
+ if(line.chompString(id))
+ {
+ mode = Mode::MessageId;
+ }
+ }
+ else if (line.chomp("msgstr ", 7))
+ {
+ switch(mode)
+ {
+ case Mode::First:
+ case Mode::MessageString:
+ case Mode::MessageContext:
+ qDebug() << "Unexpected msgstr line:" << lineNumber;
+ return;
+ case Mode::MessageId:
+ break;
+ }
+ if(line.chompString(str))
+ {
+ mode = Mode::MessageString;
+ }
+ }
+ else
+ {
+ qDebug() << "I did not understand line: " << lineNumber << ":" << QString::fromUtf8(line);
+ }
+ lineNumber++;
+ }
+ endEntry();
+ mapping = std::move(newMapping);
+ mapping_disambiguatrion = std::move(newMapping_disambiguation);
+ loaded = true;
+}
+
+POTranslator::POTranslator(const QString& filename, QObject* parent) : QTranslator(parent)
+{
+ d = new POTranslatorPrivate;
+ d->filename = filename;
+ d->reload();
+}
+
+QString POTranslator::translate(const char* context, const char* sourceText, const char* disambiguation, int n) const
+{
+ if(disambiguation)
+ {
+ auto disambiguationKey = QByteArray(context) + "|" + QByteArray(sourceText) + "@" + QByteArray(disambiguation);
+ auto iter = d->mapping_disambiguatrion.find(disambiguationKey);
+ if(iter != d->mapping_disambiguatrion.end())
+ {
+ auto & entry = *iter;
+ if(entry.text.isEmpty())
+ {
+ qDebug() << "Translation entry has no content:" << disambiguationKey;
+ }
+ if(entry.fuzzy)
+ {
+ qDebug() << "Translation entry is fuzzy:" << disambiguationKey << "->" << entry.text;
+ }
+ return entry.text;
+ }
+ }
+ auto key = QByteArray(context) + "|" + QByteArray(sourceText);
+ auto iter = d->mapping.find(key);
+ if(iter != d->mapping.end())
+ {
+ auto & entry = *iter;
+ if(entry.text.isEmpty())
+ {
+ qDebug() << "Translation entry has no content:" << key;
+ }
+ if(entry.fuzzy)
+ {
+ qDebug() << "Translation entry is fuzzy:" << key << "->" << entry.text;
+ }
+ return entry.text;
+ }
+ return QString();
+}
+
+bool POTranslator::isEmpty() const
+{
+ return !d->loaded;
+}
diff --git a/api/logic/translations/POTranslator.h b/api/logic/translations/POTranslator.h
new file mode 100644
index 00000000..6d518560
--- /dev/null
+++ b/api/logic/translations/POTranslator.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <QTranslator>
+
+struct POTranslatorPrivate;
+
+class POTranslator : public QTranslator
+{
+ Q_OBJECT
+public:
+ explicit POTranslator(const QString& filename, QObject * parent = nullptr);
+ QString translate(const char * context, const char * sourceText, const char * disambiguation, int n) const override;
+ bool isEmpty() const override;
+private:
+ POTranslatorPrivate * d;
+};
diff --git a/api/logic/translations/TranslationsModel.cpp b/api/logic/translations/TranslationsModel.cpp
index 868aa98f..0d9eccbf 100644
--- a/api/logic/translations/TranslationsModel.cpp
+++ b/api/logic/translations/TranslationsModel.cpp
@@ -8,312 +8,642 @@
#include <QDebug>
#include <FileSystem.h>
#include <net/NetJob.h>
+#include <net/ChecksumValidator.h>
#include <Env.h>
#include <net/URLConstants.h>
+#include "Json.h"
+
+#include "POTranslator.h"
const static QLatin1Literal defaultLangCode("en");
+enum class FileType
+{
+ NONE,
+ QM,
+ PO
+};
+
struct Language
{
- QString key;
- QLocale locale;
- bool updated;
+ Language()
+ {
+ updated = true;
+ }
+ Language(const QString & _key)
+ {
+ key = _key;
+ locale = QLocale(key);
+ updated = (key == defaultLangCode);
+ }
+
+ float percentTranslated() const
+ {
+ if (total == 0)
+ {
+ return 100.0f;
+ }
+ return 100.0f * float(translated) / float(total);
+ }
+
+ void setTranslationStats(unsigned _translated, unsigned _untranslated, unsigned _fuzzy)
+ {
+ translated = _translated;
+ untranslated = _untranslated;
+ fuzzy = _fuzzy;
+ total = translated + untranslated + fuzzy;
+ }
+
+ bool isOfSameNameAs(const Language& other) const
+ {
+ return key == other.key;
+ }
+
+ bool isIdenticalTo(const Language& other) const
+ {
+ return
+ (
+ key == other.key &&
+ file_name == other.file_name &&
+ file_size == other.file_size &&
+ file_sha1 == other.file_sha1 &&
+ translated == other.translated &&
+ fuzzy == other.fuzzy &&
+ total == other.fuzzy &&
+ localFileType == other.localFileType
+ );
+ }
+
+ Language & apply(Language & other)
+ {
+ if(!isOfSameNameAs(other))
+ {
+ return *this;
+ }
+ file_name = other.file_name;
+ file_size = other.file_size;
+ file_sha1 = other.file_sha1;
+ translated = other.translated;
+ fuzzy = other.fuzzy;
+ total = other.total;
+ localFileType = other.localFileType;
+ return *this;
+ }
+
+ QString key;
+ QLocale locale;
+ bool updated;
+
+ QString file_name = QString();
+ std::size_t file_size = 0;
+ QString file_sha1 = QString();
+
+ unsigned translated = 0;
+ unsigned untranslated = 0;
+ unsigned fuzzy = 0;
+ unsigned total = 0;
+
+ FileType localFileType = FileType::NONE;
};
+
+
struct TranslationsModel::Private
{
- QDir m_dir;
-
- // initial state is just english
- QVector<Language> m_languages = {{defaultLangCode, QLocale(defaultLangCode), false}};
- QString m_selectedLanguage = defaultLangCode;
- std::unique_ptr<QTranslator> m_qt_translator;
- std::unique_ptr<QTranslator> m_app_translator;
-
- std::shared_ptr<Net::Download> m_index_task;
- QString m_downloadingTranslation;
- NetJobPtr m_dl_job;
- NetJobPtr m_index_job;
- QString m_nextDownload;
+ QDir m_dir;
+
+ // initial state is just english
+ QVector<Language> m_languages = {Language (defaultLangCode)};
+
+ QString m_selectedLanguage = defaultLangCode;
+ std::unique_ptr<QTranslator> m_qt_translator;
+ std::unique_ptr<QTranslator> m_app_translator;
+
+ std::shared_ptr<Net::Download> m_index_task;
+ QString m_downloadingTranslation;
+ NetJobPtr m_dl_job;
+ NetJobPtr m_index_job;
+ QString m_nextDownload;
+
+ std::unique_ptr<POTranslator> m_po_translator;
+ QFileSystemWatcher *watcher;
};
TranslationsModel::TranslationsModel(QString path, QObject* parent): QAbstractListModel(parent)
{
- d.reset(new Private);
- d->m_dir.setPath(path);
- loadLocalIndex();
+ d.reset(new Private);
+ d->m_dir.setPath(path);
+ FS::ensureFolderPathExists(path);
+ reloadLocalFiles();
+
+ d->watcher = new QFileSystemWatcher(this);
+ connect(d->watcher, &QFileSystemWatcher::directoryChanged, this, &TranslationsModel::translationDirChanged);
+ d->watcher->addPath(d->m_dir.canonicalPath());
}
TranslationsModel::~TranslationsModel()
{
}
+void TranslationsModel::translationDirChanged(const QString& path)
+{
+ qDebug() << "Dir changed:" << path;
+ reloadLocalFiles();
+ selectLanguage(selectedLanguage());
+}
+
+void TranslationsModel::indexReceived()
+{
+ qDebug() << "Got translations index!";
+ d->m_index_job.reset();
+ if(d->m_selectedLanguage != defaultLangCode)
+ {
+ downloadTranslation(d->m_selectedLanguage);
+ }
+}
+
+namespace {
+void readIndex(const QString & path, QMap<QString, Language>& languages)
+{
+ QByteArray data;
+ try
+ {
+ data = FS::read(path);
+ }
+ catch (const Exception &e)
+ {
+ qCritical() << "Translations Download Failed: index file not readable";
+ return;
+ }
+
+ int index = 1;
+ try
+ {
+ auto doc = Json::requireObject(Json::requireDocument(data));
+ auto file_type = Json::requireString(doc, "file_type");
+ if(file_type != "MMC-TRANSLATION-INDEX")
+ {
+ qCritical() << "Translations Download Failed: index file is of unknown file type" << file_type;
+ return;
+ }
+ auto version = Json::requireInteger(doc, "version");
+ if(version > 2)
+ {
+ qCritical() << "Translations Download Failed: index file is of unknown format version" << file_type;
+ return;
+ }
+ auto langObjs = Json::requireObject(doc, "languages");
+ for(auto iter = langObjs.begin(); iter != langObjs.end(); iter++)
+ {
+ Language lang(iter.key());
+
+ auto langObj = Json::requireObject(iter.value());
+ lang.setTranslationStats(
+ Json::ensureInteger(langObj, "translated", 0),
+ Json::ensureInteger(langObj, "untranslated", 0),
+ Json::ensureInteger(langObj, "fuzzy", 0)
+ );
+ lang.file_name = Json::requireString(langObj, "file");
+ lang.file_sha1 = Json::requireString(langObj, "sha1");
+ lang.file_size = Json::requireInteger(langObj, "size");
+
+ languages.insert(lang.key, lang);
+ index++;
+ }
+ }
+ catch (Json::JsonException & e)
+ {
+ qCritical() << "Translations Download Failed: index file could not be parsed as json";
+ }
+}
+}
+
+void TranslationsModel::reloadLocalFiles()
+{
+ QMap<QString, Language> languages = {{defaultLangCode, Language(defaultLangCode)}};
+
+ readIndex(d->m_dir.absoluteFilePath("index_v2.json"), languages);
+ auto entries = d->m_dir.entryInfoList({"mmc_*.qm", "*.po"}, QDir::Files | QDir::NoDotAndDotDot);
+ for(auto & entry: entries)
+ {
+ auto completeSuffix = entry.completeSuffix();
+ QString langCode;
+ FileType fileType = FileType::NONE;
+ if(completeSuffix == "qm")
+ {
+ langCode = entry.baseName().remove(0,4);
+ fileType = FileType::QM;
+ }
+ else if(completeSuffix == "po")
+ {
+ langCode = entry.baseName();
+ fileType = FileType::PO;
+ }
+ else
+ {
+ continue;
+ }
+
+ auto langIter = languages.find(langCode);
+ if(langIter != languages.end())
+ {
+ auto & language = *langIter;
+ if(int(fileType) > int(language.localFileType))
+ {
+ language.localFileType = fileType;
+ }
+ }
+ else
+ {
+ if(fileType == FileType::PO)
+ {
+ Language localFound(langCode);
+ localFound.localFileType = FileType::PO;
+ languages.insert(langCode, localFound);
+ }
+ }
+ }
+
+ // changed and removed languages
+ for(auto iter = d->m_languages.begin(); iter != d->m_languages.end();)
+ {
+ auto &language = *iter;
+ auto row = iter - d->m_languages.begin();
+
+ auto updatedLanguageIter = languages.find(language.key);
+ if(updatedLanguageIter != languages.end())
+ {
+ if(language.isIdenticalTo(*updatedLanguageIter))
+ {
+ languages.remove(language.key);
+ }
+ else
+ {
+ language.apply(*updatedLanguageIter);
+ emit dataChanged(index(row), index(row));
+ languages.remove(language.key);
+ }
+ iter++;
+ }
+ else
+ {
+ beginRemoveRows(QModelIndex(), row, row);
+ iter = d->m_languages.erase(iter);
+ endRemoveRows();
+ }
+ }
+ // added languages
+ if(languages.isEmpty())
+ {
+ return;
+ }
+ beginInsertRows(QModelIndex(), d->m_languages.size(), d->m_languages.size() + languages.size() - 1);
+ for(auto & language: languages)
+ {
+ d->m_languages.append(language);
+ }
+ endInsertRows();
+}
+
+namespace {
+enum class Column
+{
+ Language,
+ Completeness
+};
+}
+
+
QVariant TranslationsModel::data(const QModelIndex& index, int role) const
{
- if (!index.isValid())
- return QVariant();
-
- int row = index.row();
-
- if (row < 0 || row >= d->m_languages.size())
- return QVariant();
-
- switch (role)
- {
- case Qt::DisplayRole:
- return d->m_languages[row].locale.nativeLanguageName();
- case Qt::UserRole:
- return d->m_languages[row].key;
- default:
- return QVariant();
- }
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+ auto column = static_cast<Column>(index.column());
+
+ if (row < 0 || row >= d->m_languages.size())
+ return QVariant();
+
+ auto & lang = d->m_languages[row];
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ {
+ switch(column)
+ {
+ case Column::Language:
+ {
+ return d->m_languages[row].locale.nativeLanguageName();
+ }
+ case Column::Completeness:
+ {
+ QString text;
+ text.sprintf("%3.1f %%", lang.percentTranslated());
+ return text;
+ }
+ }
+ }
+ case Qt::ToolTipRole:
+ {
+ return tr("%1:\n%2 translated\n%3 fuzzy\n%4 total").arg(lang.key, QString::number(lang.translated), QString::number(lang.fuzzy), QString::number(lang.total));
+ }
+ case Qt::UserRole:
+ return d->m_languages[row].key;
+ default:
+ return QVariant();
+ }
}
-int TranslationsModel::rowCount(const QModelIndex& parent) const
+QVariant TranslationsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
- return d->m_languages.size();
+ auto column = static_cast<Column>(section);
+ if(role == Qt::DisplayRole)
+ {
+ switch(column)
+ {
+ case Column::Language:
+ {
+ return tr("Language");
+ }
+ case Column::Completeness:
+ {
+ return tr("Completeness");
+ }
+ }
+ }
+ else if(role == Qt::ToolTipRole)
+ {
+ switch(column)
+ {
+ case Column::Language:
+ {
+ return tr("The native language name.");
+ }
+ case Column::Completeness:
+ {
+ return tr("Completeness is the percentage of fully translated strings, not counting automatically guessed ones.");
+ }
+ }
+ }
+ return QAbstractListModel::headerData(section, orientation, role);
}
-Language * TranslationsModel::findLanguage(const QString& key)
+int TranslationsModel::rowCount(const QModelIndex& parent) const
{
- auto found = std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language & lang)
- {
- return lang.key == key;
- });
- if(found == d->m_languages.end())
- {
- return nullptr;
- }
- else
- {
- return found;
- }
+ return d->m_languages.size();
}
-bool TranslationsModel::selectLanguage(QString key)
+int TranslationsModel::columnCount(const QModelIndex& parent) const
{
- QString &langCode = key;
- auto langPtr = findLanguage(key);
- if(!langPtr)
- {
- qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
- langCode = defaultLangCode;
- }
- else
- {
- langCode = langPtr->key;
- }
-
- // uninstall existing translators if there are any
- if (d->m_app_translator)
- {
- QCoreApplication::removeTranslator(d->m_app_translator.get());
- d->m_app_translator.reset();
- }
- if (d->m_qt_translator)
- {
- QCoreApplication::removeTranslator(d->m_qt_translator.get());
- d->m_qt_translator.reset();
- }
-
- /*
- * FIXME: potential source of crashes:
- * In a multithreaded application, the default locale should be set at application startup, before any non-GUI threads are created.
- * This function is not reentrant.
- */
- QLocale locale(langCode);
- QLocale::setDefault(locale);
-
- // if it's the default UI language, finish
- if(langCode == defaultLangCode)
- {
- d->m_selectedLanguage = langCode;
- return true;
- }
-
- // otherwise install new translations
- bool successful = false;
- // FIXME: this is likely never present. FIX IT.
- d->m_qt_translator.reset(new QTranslator());
- if (d->m_qt_translator->load("qt_" + langCode, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
- {
- qDebug() << "Loading Qt Language File for" << langCode.toLocal8Bit().constData() << "...";
- if (!QCoreApplication::installTranslator(d->m_qt_translator.get()))
- {
- qCritical() << "Loading Qt Language File failed.";
- d->m_qt_translator.reset();
- }
- else
- {
- successful = true;
- }
- }
- else
- {
- d->m_qt_translator.reset();
- }
-
- d->m_app_translator.reset(new QTranslator());
- if (d->m_app_translator->load("mmc_" + langCode, d->m_dir.path()))
- {
- qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "...";
- if (!QCoreApplication::installTranslator(d->m_app_translator.get()))
- {
- qCritical() << "Loading Application Language File failed.";
- d->m_app_translator.reset();
- }
- else
- {
- successful = true;
- }
- }
- else
- {
- d->m_app_translator.reset();
- }
- d->m_selectedLanguage = langCode;
- return successful;
+ return 2;
}
-QModelIndex TranslationsModel::selectedIndex()
+Language * TranslationsModel::findLanguage(const QString& key)
{
- auto found = findLanguage(d->m_selectedLanguage);
- if(found)
- {
- // QVector iterator freely converts to pointer to contained type
- return index(found - d->m_languages.begin(), 0, QModelIndex());
- }
- return QModelIndex();
+ auto found = std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language & lang)
+ {
+ return lang.key == key;
+ });
+ if(found == d->m_languages.end())
+ {
+ return nullptr;
+ }
+ else
+ {
+ return found;
+ }
}
-QString TranslationsModel::selectedLanguage()
+bool TranslationsModel::selectLanguage(QString key)
{
- return d->m_selectedLanguage;
+ QString &langCode = key;
+ auto langPtr = findLanguage(key);
+ if(!langPtr)
+ {
+ qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
+ langCode = defaultLangCode;
+ }
+ else
+ {
+ langCode = langPtr->key;
+ }
+
+ // uninstall existing translators if there are any
+ if (d->m_app_translator)
+ {
+ QCoreApplication::removeTranslator(d->m_app_translator.get());
+ d->m_app_translator.reset();
+ }
+ if (d->m_qt_translator)
+ {
+ QCoreApplication::removeTranslator(d->m_qt_translator.get());
+ d->m_qt_translator.reset();
+ }
+
+ /*
+ * FIXME: potential source of crashes:
+ * In a multithreaded application, the default locale should be set at application startup, before any non-GUI threads are created.
+ * This function is not reentrant.
+ */
+ QLocale locale(langCode);
+ QLocale::setDefault(locale);
+
+ // if it's the default UI language, finish
+ if(langCode == defaultLangCode)
+ {
+ d->m_selectedLanguage = langCode;
+ return true;
+ }
+
+ // otherwise install new translations
+ bool successful = false;
+ // FIXME: this is likely never present. FIX IT.
+ d->m_qt_translator.reset(new QTranslator());
+ if (d->m_qt_translator->load("qt_" + langCode, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
+ {
+ qDebug() << "Loading Qt Language File for" << langCode.toLocal8Bit().constData() << "...";
+ if (!QCoreApplication::installTranslator(d->m_qt_translator.get()))
+ {
+ qCritical() << "Loading Qt Language File failed.";
+ d->m_qt_translator.reset();
+ }
+ else
+ {
+ successful = true;
+ }
+ }
+ else
+ {
+ d->m_qt_translator.reset();
+ }
+
+ if(langPtr->localFileType == FileType::PO)
+ {
+ qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "...";
+ auto poTranslator = new POTranslator(FS::PathCombine(d->m_dir.path(), langCode + ".po"));
+ if(!poTranslator->isEmpty())
+ {
+ if (!QCoreApplication::installTranslator(poTranslator))
+ {
+ delete poTranslator;
+ qCritical() << "Installing Application Language File failed.";
+ }
+ else
+ {
+ d->m_app_translator.reset(poTranslator);
+ successful = true;
+ }
+ }
+ else
+ {
+ qCritical() << "Loading Application Language File failed.";
+ d->m_app_translator.reset();
+ }
+ }
+ else if(langPtr->localFileType == FileType::QM)
+ {
+ d->m_app_translator.reset(new QTranslator());
+ if (d->m_app_translator->load("mmc_" + langCode, d->m_dir.path()))
+ {
+ qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "...";
+ if (!QCoreApplication::installTranslator(d->m_app_translator.get()))
+ {
+ qCritical() << "Installing Application Language File failed.";
+ d->m_app_translator.reset();
+ }
+ else
+ {
+ successful = true;
+ }
+ }
+ else
+ {
+ d->m_app_translator.reset();
+ }
+ }
+ else
+ {
+ d->m_app_translator.reset();
+ }
+ d->m_selectedLanguage = langCode;
+ return successful;
}
-void TranslationsModel::downloadIndex()
+QModelIndex TranslationsModel::selectedIndex()
{
- if(d->m_index_job || d->m_dl_job)
- {
- return;
- }
- qDebug() << "Downloading Translations Index...";
- d->m_index_job.reset(new NetJob("Translations Index"));
- MetaEntryPtr entry = ENV.metacache()->resolveEntry("translations", "index");
- d->m_index_task = Net::Download::makeCached(QUrl("http://files.multimc.org/translations/index"), entry);
- d->m_index_job->addNetAction(d->m_index_task);
- connect(d->m_index_job.get(), &NetJob::failed, this, &TranslationsModel::indexFailed);
- connect(d->m_index_job.get(), &NetJob::succeeded, this, &TranslationsModel::indexRecieved);
- d->m_index_job->start();
+ auto found = findLanguage(d->m_selectedLanguage);
+ if(found)
+ {
+ // QVector iterator freely converts to pointer to contained type
+ return index(found - d->m_languages.begin(), 0, QModelIndex());
+ }
+ return QModelIndex();
}
-void TranslationsModel::indexRecieved()
+QString TranslationsModel::selectedLanguage()
{
- qDebug() << "Got translations index!";
- d->m_index_job.reset();
- loadLocalIndex();
- if(d->m_selectedLanguage != defaultLangCode)
- {
- downloadTranslation(d->m_selectedLanguage);
- }
+ return d->m_selectedLanguage;
}
-void TranslationsModel::loadLocalIndex()
+void TranslationsModel::downloadIndex()
{
- QByteArray data;
- try
- {
- data = FS::read(d->m_dir.absoluteFilePath("index"));
- }
- catch (Exception &e)
- {
- qCritical() << "Translations Download Failed: index file not readable";
- return;
- }
- QVector<Language> languages;
- QList<QByteArray> lines = data.split('\n');
- // add the default english.
- languages.append({defaultLangCode, QLocale(defaultLangCode), true});
- for (const auto line : lines)
- {
- if(!line.isEmpty())
- {
- auto str = QString::fromLatin1(line);
- str.remove(".qm");
- languages.append({str, QLocale(str), false});
- }
- }
- beginResetModel();
- d->m_languages.swap(languages);
- endResetModel();
+ if(d->m_index_job || d->m_dl_job)
+ {
+ return;
+ }
+ qDebug() << "Downloading Translations Index...";
+ d->m_index_job.reset(new NetJob("Translations Index"));
+ MetaEntryPtr entry = ENV.metacache()->resolveEntry("translations", "index_v2.json");
+ entry->setStale(true);
+ d->m_index_task = Net::Download::makeCached(QUrl("https://files.multimc.org/translations/index_v2.json"), entry);
+ d->m_index_job->addNetAction(d->m_index_task);
+ connect(d->m_index_job.get(), &NetJob::failed, this, &TranslationsModel::indexFailed);
+ connect(d->m_index_job.get(), &NetJob::succeeded, this, &TranslationsModel::indexReceived);
+ d->m_index_job->start();
}
void TranslationsModel::updateLanguage(QString key)
{
- if(key == defaultLangCode)
- {
- qWarning() << "Cannot update builtin language" << key;
- return;
- }
- auto found = findLanguage(key);
- if(!found)
- {
- qWarning() << "Cannot update invalid language" << key;
- return;
- }
- if(!found->updated)
- {
- downloadTranslation(key);
- }
+ if(key == defaultLangCode)
+ {
+ qWarning() << "Cannot update builtin language" << key;
+ return;
+ }
+ auto found = findLanguage(key);
+ if(!found)
+ {
+ qWarning() << "Cannot update invalid language" << key;
+ return;
+ }
+ if(!found->updated)
+ {
+ downloadTranslation(key);
+ }
}
void TranslationsModel::downloadTranslation(QString key)
{
- if(d->m_dl_job)
- {
- d->m_nextDownload = key;
- return;
- }
- d->m_downloadingTranslation = key;
- MetaEntryPtr entry = ENV.metacache()->resolveEntry("translations", "mmc_" + key + ".qm");
- entry->setStale(true);
- d->m_dl_job.reset(new NetJob("Translation for " + key));
- d->m_dl_job->addNetAction(Net::Download::makeCached(QUrl(URLConstants::TRANSLATIONS_BASE_URL + key + ".qm"), entry));
- connect(d->m_dl_job.get(), &NetJob::succeeded, this, &TranslationsModel::dlGood);
- connect(d->m_dl_job.get(), &NetJob::failed, this, &TranslationsModel::dlFailed);
- d->m_dl_job->start();
+ if(d->m_dl_job)
+ {
+ d->m_nextDownload = key;
+ return;
+ }
+ auto lang = findLanguage(key);
+ if(!lang)
+ {
+ qWarning() << "Will not download an unknown translation" << key;
+ return;
+ }
+
+ d->m_downloadingTranslation = key;
+ MetaEntryPtr entry = ENV.metacache()->resolveEntry("translations", "mmc_" + key + ".qm");
+ entry->setStale(true);
+
+ auto dl = Net::Download::makeCached(QUrl(URLConstants::TRANSLATIONS_BASE_URL + lang->file_name), entry);
+ auto rawHash = QByteArray::fromHex(lang->file_sha1.toLatin1());
+ dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
+ dl->m_total_progress = lang->file_size;
+
+ d->m_dl_job.reset(new NetJob("Translation for " + key));
+ d->m_dl_job->addNetAction(dl);
+
+ connect(d->m_dl_job.get(), &NetJob::succeeded, this, &TranslationsModel::dlGood);
+ connect(d->m_dl_job.get(), &NetJob::failed, this, &TranslationsModel::dlFailed);
+
+ d->m_dl_job->start();
}
void TranslationsModel::downloadNext()
{
- if(!d->m_nextDownload.isEmpty())
- {
- downloadTranslation(d->m_nextDownload);
- d->m_nextDownload.clear();
- }
+ if(!d->m_nextDownload.isEmpty())
+ {
+ downloadTranslation(d->m_nextDownload);
+ d->m_nextDownload.clear();
+ }
}
void TranslationsModel::dlFailed(QString reason)
{
- qCritical() << "Translations Download Failed:" << reason;
- d->m_dl_job.reset();
- downloadNext();
+ qCritical() << "Translations Download Failed:" << reason;
+ d->m_dl_job.reset();
+ downloadNext();
}
void TranslationsModel::dlGood()
{
- qDebug() << "Got translation:" << d->m_downloadingTranslation;
-
- if(d->m_downloadingTranslation == d->m_selectedLanguage)
- {
- selectLanguage(d->m_selectedLanguage);
- }
- d->m_dl_job.reset();
- downloadNext();
+ qDebug() << "Got translation:" << d->m_downloadingTranslation;
+
+ if(d->m_downloadingTranslation == d->m_selectedLanguage)
+ {
+ selectLanguage(d->m_selectedLanguage);
+ }
+ d->m_dl_job.reset();
+ downloadNext();
}
void TranslationsModel::indexFailed(QString reason)
{
- qCritical() << "Translations Index Download Failed:" << reason;
- d->m_index_job.reset();
+ qCritical() << "Translations Index Download Failed:" << reason;
+ d->m_index_job.reset();
}
diff --git a/api/logic/translations/TranslationsModel.h b/api/logic/translations/TranslationsModel.h
index bd481134..a0327fcd 100644
--- a/api/logic/translations/TranslationsModel.h
+++ b/api/logic/translations/TranslationsModel.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,39 +23,43 @@ struct Language;
class MULTIMC_LOGIC_EXPORT TranslationsModel : public QAbstractListModel
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit TranslationsModel(QString path, QObject *parent = 0);
- virtual ~TranslationsModel();
+ explicit TranslationsModel(QString path, QObject *parent = 0);
+ virtual ~TranslationsModel();
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex & parent) const override;
- bool selectLanguage(QString key);
- void updateLanguage(QString key);
- QModelIndex selectedIndex();
- QString selectedLanguage();
+ bool selectLanguage(QString key);
+ void updateLanguage(QString key);
+ QModelIndex selectedIndex();
+ QString selectedLanguage();
- void downloadIndex();
+ void downloadIndex();
private:
- Language *findLanguage(const QString & key);
- void loadLocalIndex();
- void downloadTranslation(QString key);
- void downloadNext();
+ Language *findLanguage(const QString & key);
+ void reloadLocalFiles();
+ void downloadTranslation(QString key);
+ void downloadNext();
- // hide copy constructor
- TranslationsModel(const TranslationsModel &) = delete;
- // hide assign op
- TranslationsModel &operator=(const TranslationsModel &) = delete;
+ // hide copy constructor
+ TranslationsModel(const TranslationsModel &) = delete;
+ // hide assign op
+ TranslationsModel &operator=(const TranslationsModel &) = delete;
private slots:
- void indexRecieved();
- void indexFailed(QString reason);
- void dlFailed(QString reason);
- void dlGood();
+ void indexReceived();
+ void indexFailed(QString reason);
+ void dlFailed(QString reason);
+ void dlGood();
+ void translationDirChanged(const QString &path);
+
private: /* data */
- struct Private;
- std::unique_ptr<Private> d;
+ struct Private;
+ std::unique_ptr<Private> d;
};
diff --git a/api/logic/updater/DownloadTask.cpp b/api/logic/updater/DownloadTask.cpp
index e0adf593..dbd8c65a 100644
--- a/api/logic/updater/DownloadTask.cpp
+++ b/api/logic/updater/DownloadTask.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,140 +27,147 @@ namespace GoUpdate
{
DownloadTask::DownloadTask(Status status, QString target, QObject *parent)
- : Task(parent), m_updateFilesDir(target)
+ : Task(parent), m_updateFilesDir(target)
{
- m_status = status;
+ m_status = status;
- m_updateFilesDir.setAutoRemove(false);
+ m_updateFilesDir.setAutoRemove(false);
}
void DownloadTask::executeTask()
{
- loadVersionInfo();
+ loadVersionInfo();
}
void DownloadTask::loadVersionInfo()
{
- setStatus(tr("Loading version information..."));
+ setStatus(tr("Loading version information..."));
- NetJob *netJob = new NetJob("Version Info");
+ NetJob *netJob = new NetJob("Version Info");
- // Find the index URL.
- QUrl newIndexUrl = QUrl(m_status.newRepoUrl).resolved(QString::number(m_status.newVersionId) + ".json");
- qDebug() << m_status.newRepoUrl << " turns into " << newIndexUrl;
+ // Find the index URL.
+ QUrl newIndexUrl = QUrl(m_status.newRepoUrl).resolved(QString::number(m_status.newVersionId) + ".json");
+ qDebug() << m_status.newRepoUrl << " turns into " << newIndexUrl;
- netJob->addNetAction(m_newVersionFileListDownload = Net::Download::makeByteArray(newIndexUrl, &newVersionFileListData));
+ netJob->addNetAction(m_newVersionFileListDownload = Net::Download::makeByteArray(newIndexUrl, &newVersionFileListData));
- // If we have a current version URL, get that one too.
- if (!m_status.currentRepoUrl.isEmpty())
- {
- QUrl cIndexUrl = QUrl(m_status.currentRepoUrl).resolved(QString::number(m_status.currentVersionId) + ".json");
- netJob->addNetAction(m_currentVersionFileListDownload = Net::Download::makeByteArray(cIndexUrl, &currentVersionFileListData));
- qDebug() << m_status.currentRepoUrl << " turns into " << cIndexUrl;
- }
+ // If we have a current version URL, get that one too.
+ if (!m_status.currentRepoUrl.isEmpty())
+ {
+ QUrl cIndexUrl = QUrl(m_status.currentRepoUrl).resolved(QString::number(m_status.currentVersionId) + ".json");
+ netJob->addNetAction(m_currentVersionFileListDownload = Net::Download::makeByteArray(cIndexUrl, &currentVersionFileListData));
+ qDebug() << m_status.currentRepoUrl << " turns into " << cIndexUrl;
+ }
- // connect signals and start the job
- connect(netJob, &NetJob::succeeded, this, &DownloadTask::processDownloadedVersionInfo);
- connect(netJob, &NetJob::failed, this, &DownloadTask::vinfoDownloadFailed);
- m_vinfoNetJob.reset(netJob);
- netJob->start();
+ // connect signals and start the job
+ connect(netJob, &NetJob::succeeded, this, &DownloadTask::processDownloadedVersionInfo);
+ connect(netJob, &NetJob::failed, this, &DownloadTask::vinfoDownloadFailed);
+ m_vinfoNetJob.reset(netJob);
+ netJob->start();
}
void DownloadTask::vinfoDownloadFailed()
{
- // Something failed. We really need the second download (current version info), so parse
- // downloads anyways as long as the first one succeeded.
- if (m_newVersionFileListDownload->wasSuccessful())
- {
- processDownloadedVersionInfo();
- return;
- }
-
- // TODO: Give a more detailed error message.
- qCritical() << "Failed to download version info files.";
- emitFailed(tr("Failed to download version info files."));
+ // Something failed. We really need the second download (current version info), so parse
+ // downloads anyways as long as the first one succeeded.
+ if (m_newVersionFileListDownload->wasSuccessful())
+ {
+ processDownloadedVersionInfo();
+ return;
+ }
+
+ // TODO: Give a more detailed error message.
+ qCritical() << "Failed to download version info files.";
+ emitFailed(tr("Failed to download version info files."));
}
void DownloadTask::processDownloadedVersionInfo()
{
- VersionFileList m_currentVersionFileList;
- VersionFileList m_newVersionFileList;
-
- setStatus(tr("Reading file list for new version..."));
- qDebug() << "Reading file list for new version...";
- QString error;
- if (!parseVersionInfo(newVersionFileListData, m_newVersionFileList, error))
- {
- qCritical() << error;
- emitFailed(error);
- return;
- }
-
- // if we have the current version info, use it.
- if (m_currentVersionFileListDownload && m_currentVersionFileListDownload->wasSuccessful())
- {
- setStatus(tr("Reading file list for current version..."));
- qDebug() << "Reading file list for current version...";
- // if this fails, it's not a complete loss.
- QString error;
- if(!parseVersionInfo( currentVersionFileListData, m_currentVersionFileList, error))
- {
- qDebug() << error << "This is not a fatal error.";
- }
- }
-
- // We don't need this any more.
- m_currentVersionFileListDownload.reset();
- m_newVersionFileListDownload.reset();
- m_vinfoNetJob.reset();
-
- setStatus(tr("Processing file lists - figuring out how to install the update..."));
-
- // make a new netjob for the actual update files
- NetJobPtr netJob (new NetJob("Update Files"));
-
- // fill netJob and operationList
- if (!processFileLists(m_currentVersionFileList, m_newVersionFileList, m_status.rootPath, m_updateFilesDir.path(), netJob, m_operations))
- {
- emitFailed(tr("Failed to process update lists..."));
- return;
- }
-
- // Now start the download.
- QObject::connect(netJob.get(), &NetJob::succeeded, this, &DownloadTask::fileDownloadFinished);
- QObject::connect(netJob.get(), &NetJob::progress, this, &DownloadTask::fileDownloadProgressChanged);
- QObject::connect(netJob.get(), &NetJob::failed, this, &DownloadTask::fileDownloadFailed);
-
- setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size())));
- qDebug() << "Begin downloading update files to" << m_updateFilesDir.path();
- m_filesNetJob = netJob;
- m_filesNetJob->start();
+ VersionFileList m_currentVersionFileList;
+ VersionFileList m_newVersionFileList;
+
+ setStatus(tr("Reading file list for new version..."));
+ qDebug() << "Reading file list for new version...";
+ QString error;
+ if (!parseVersionInfo(newVersionFileListData, m_newVersionFileList, error))
+ {
+ qCritical() << error;
+ emitFailed(error);
+ return;
+ }
+
+ // if we have the current version info, use it.
+ if (m_currentVersionFileListDownload && m_currentVersionFileListDownload->wasSuccessful())
+ {
+ setStatus(tr("Reading file list for current version..."));
+ qDebug() << "Reading file list for current version...";
+ // if this fails, it's not a complete loss.
+ QString error;
+ if(!parseVersionInfo( currentVersionFileListData, m_currentVersionFileList, error))
+ {
+ qDebug() << error << "This is not a fatal error.";
+ }
+ }
+
+ // We don't need this any more.
+ m_currentVersionFileListDownload.reset();
+ m_newVersionFileListDownload.reset();
+ m_vinfoNetJob.reset();
+
+ setStatus(tr("Processing file lists - figuring out how to install the update..."));
+
+ // make a new netjob for the actual update files
+ NetJobPtr netJob (new NetJob("Update Files"));
+
+ // fill netJob and operationList
+ if (!processFileLists(m_currentVersionFileList, m_newVersionFileList, m_status.rootPath, m_updateFilesDir.path(), netJob, m_operations))
+ {
+ emitFailed(tr("Failed to process update lists..."));
+ return;
+ }
+
+ // Now start the download.
+ QObject::connect(netJob.get(), &NetJob::succeeded, this, &DownloadTask::fileDownloadFinished);
+ QObject::connect(netJob.get(), &NetJob::progress, this, &DownloadTask::fileDownloadProgressChanged);
+ QObject::connect(netJob.get(), &NetJob::failed, this, &DownloadTask::fileDownloadFailed);
+
+ if(netJob->size() == 1) // Translation issues... see https://github.com/MultiMC/MultiMC5/issues/1701
+ {
+ setStatus(tr("Downloading one update file."));
+ }
+ else
+ {
+ setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size())));
+ }
+ qDebug() << "Begin downloading update files to" << m_updateFilesDir.path();
+ m_filesNetJob = netJob;
+ m_filesNetJob->start();
}
void DownloadTask::fileDownloadFinished()
{
- emitSucceeded();
+ emitSucceeded();
}
void DownloadTask::fileDownloadFailed(QString reason)
{
- qCritical() << "Failed to download update files:" << reason;
- emitFailed(tr("Failed to download update files: %1").arg(reason));
+ qCritical() << "Failed to download update files:" << reason;
+ emitFailed(tr("Failed to download update files: %1").arg(reason));
}
void DownloadTask::fileDownloadProgressChanged(qint64 current, qint64 total)
{
- setProgress(current, total);
+ setProgress(current, total);
}
QString DownloadTask::updateFilesDir()
{
- return m_updateFilesDir.path();
+ return m_updateFilesDir.path();
}
OperationList DownloadTask::operations()
{
- return m_operations;
+ return m_operations;
}
} \ No newline at end of file
diff --git a/api/logic/updater/DownloadTask.h b/api/logic/updater/DownloadTask.h
index 186826e2..5c973912 100644
--- a/api/logic/updater/DownloadTask.h
+++ b/api/logic/updater/DownloadTask.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,69 +29,70 @@ namespace GoUpdate
*/
class MULTIMC_LOGIC_EXPORT DownloadTask : public Task
{
- Q_OBJECT
+ Q_OBJECT
public:
- /**
- * Create a download task
- *
- * target is a template - XXXXXX at the end will be replaced with a random generated string, ensuring uniqueness
- */
- explicit DownloadTask(Status status, QString target, QObject* parent = 0);
+ /**
+ * Create a download task
+ *
+ * target is a template - XXXXXX at the end will be replaced with a random generated string, ensuring uniqueness
+ */
+ explicit DownloadTask(Status status, QString target, QObject* parent = 0);
+ virtual ~DownloadTask() {};
- /// Get the directory that will contain the update files.
- QString updateFilesDir();
+ /// Get the directory that will contain the update files.
+ QString updateFilesDir();
- /// Get the list of operations that should be done
- OperationList operations();
+ /// Get the list of operations that should be done
+ OperationList operations();
- /// set updater download behavior
- void setUseLocalUpdater(bool useLocal);
+ /// set updater download behavior
+ void setUseLocalUpdater(bool useLocal);
protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
-
- /*!
- * Downloads the version info files from the repository.
- * The files for both the current build, and the build that we're updating to need to be downloaded.
- * If the current version's info file can't be found, MultiMC will not delete files that
- * were removed between versions. It will still replace files that have changed, however.
- * Note that although the repository URL for the current version is not given to the update task,
- * the task will attempt to look it up in the UpdateChecker's channel list.
- * If an error occurs here, the function will call emitFailed and return false.
- */
- void loadVersionInfo();
-
- NetJobPtr m_vinfoNetJob;
- QByteArray currentVersionFileListData;
- QByteArray newVersionFileListData;
- Net::Download::Ptr m_currentVersionFileListDownload;
- Net::Download::Ptr m_newVersionFileListDownload;
-
- NetJobPtr m_filesNetJob;
-
- Status m_status;
-
- OperationList m_operations;
-
- /*!
- * Temporary directory to store update files in.
- * This will be set to not auto delete. Task will fail if this fails to be created.
- */
- QTemporaryDir m_updateFilesDir;
+ //! Entry point for tasks.
+ virtual void executeTask() override;
+
+ /*!
+ * Downloads the version info files from the repository.
+ * The files for both the current build, and the build that we're updating to need to be downloaded.
+ * If the current version's info file can't be found, MultiMC will not delete files that
+ * were removed between versions. It will still replace files that have changed, however.
+ * Note that although the repository URL for the current version is not given to the update task,
+ * the task will attempt to look it up in the UpdateChecker's channel list.
+ * If an error occurs here, the function will call emitFailed and return false.
+ */
+ void loadVersionInfo();
+
+ NetJobPtr m_vinfoNetJob;
+ QByteArray currentVersionFileListData;
+ QByteArray newVersionFileListData;
+ Net::Download::Ptr m_currentVersionFileListDownload;
+ Net::Download::Ptr m_newVersionFileListDownload;
+
+ NetJobPtr m_filesNetJob;
+
+ Status m_status;
+
+ OperationList m_operations;
+
+ /*!
+ * Temporary directory to store update files in.
+ * This will be set to not auto delete. Task will fail if this fails to be created.
+ */
+ QTemporaryDir m_updateFilesDir;
protected slots:
- /*!
- * This function is called when version information is finished downloading
- * and at least the new file list download succeeded
- */
- void processDownloadedVersionInfo();
- void vinfoDownloadFailed();
-
- void fileDownloadFinished();
- void fileDownloadFailed(QString reason);
- void fileDownloadProgressChanged(qint64 current, qint64 total);
+ /*!
+ * This function is called when version information is finished downloading
+ * and at least the new file list download succeeded
+ */
+ void processDownloadedVersionInfo();
+ void vinfoDownloadFailed();
+
+ void fileDownloadFinished();
+ void fileDownloadFailed(QString reason);
+ void fileDownloadProgressChanged(qint64 current, qint64 total);
};
} \ No newline at end of file
diff --git a/api/logic/updater/DownloadTask_test.cpp b/api/logic/updater/DownloadTask_test.cpp
index e75c3ffa..531b2527 100644
--- a/api/logic/updater/DownloadTask_test.cpp
+++ b/api/logic/updater/DownloadTask_test.cpp
@@ -12,11 +12,11 @@ using namespace GoUpdate;
FileSourceList encodeBaseFile(const char *suffix)
{
- auto base = QDir::currentPath();
- QUrl localFile = QUrl::fromLocalFile(base + suffix);
- QString localUrlString = localFile.toString(QUrl::FullyEncoded);
- auto item = FileSource("http", localUrlString);
- return FileSourceList({item});
+ auto base = QDir::currentPath();
+ QUrl localFile = QUrl::fromLocalFile(base + suffix);
+ QString localUrlString = localFile.toString(QUrl::FullyEncoded);
+ auto item = FileSource("http", localUrlString);
+ return FileSourceList({item});
}
Q_DECLARE_METATYPE(VersionFileList)
@@ -24,191 +24,191 @@ Q_DECLARE_METATYPE(Operation)
QDebug operator<<(QDebug dbg, const FileSource &f)
{
- dbg.nospace() << "FileSource(type=" << f.type << " url=" << f.url
- << " comp=" << f.compressionType << ")";
- return dbg.maybeSpace();
+ dbg.nospace() << "FileSource(type=" << f.type << " url=" << f.url
+ << " comp=" << f.compressionType << ")";
+ return dbg.maybeSpace();
}
QDebug operator<<(QDebug dbg, const VersionFileEntry &v)
{
- dbg.nospace() << "VersionFileEntry(path=" << v.path << " mode=" << v.mode
- << " md5=" << v.md5 << " sources=" << v.sources << ")";
- return dbg.maybeSpace();
+ dbg.nospace() << "VersionFileEntry(path=" << v.path << " mode=" << v.mode
+ << " md5=" << v.md5 << " sources=" << v.sources << ")";
+ return dbg.maybeSpace();
}
QDebug operator<<(QDebug dbg, const Operation::Type &t)
{
- switch (t)
- {
- case Operation::OP_REPLACE:
- dbg << "OP_COPY";
- break;
- case Operation::OP_DELETE:
- dbg << "OP_DELETE";
- break;
- }
- return dbg.maybeSpace();
+ switch (t)
+ {
+ case Operation::OP_REPLACE:
+ dbg << "OP_COPY";
+ break;
+ case Operation::OP_DELETE:
+ dbg << "OP_DELETE";
+ break;
+ }
+ return dbg.maybeSpace();
}
QDebug operator<<(QDebug dbg, const Operation &u)
{
- dbg.nospace() << "Operation(type=" << u.type << " file=" << u.source
- << " dest=" << u.destination << " mode=" << u.destinationMode << ")";
- return dbg.maybeSpace();
+ dbg.nospace() << "Operation(type=" << u.type << " file=" << u.source
+ << " dest=" << u.destination << " mode=" << u.destinationMode << ")";
+ return dbg.maybeSpace();
}
class DownloadTaskTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
private
slots:
- void initTestCase()
- {
- }
- void cleanupTestCase()
- {
- }
-
- void test_parseVersionInfo_data()
- {
- QTest::addColumn<QByteArray>("data");
- QTest::addColumn<VersionFileList>("list");
- QTest::addColumn<QString>("error");
- QTest::addColumn<bool>("ret");
-
- QTest::newRow("one")
- << MULTIMC_GET_TEST_FILE("data/1.json")
- << (VersionFileList()
- << VersionFileEntry{"fileOne",
- 493,
- encodeBaseFile("/data/fileOneA"),
- "9eb84090956c484e32cb6c08455a667b"}
- << VersionFileEntry{"fileTwo",
- 644,
- encodeBaseFile("/data/fileTwo"),
- "38f94f54fa3eb72b0ea836538c10b043"}
- << VersionFileEntry{"fileThree",
- 750,
- encodeBaseFile("/data/fileThree"),
- "f12df554b21e320be6471d7154130e70"})
- << QString() << true;
- QTest::newRow("two")
- << MULTIMC_GET_TEST_FILE("data/2.json")
- << (VersionFileList()
- << VersionFileEntry{"fileOne",
- 493,
- encodeBaseFile("/data/fileOneB"),
- "42915a71277c9016668cce7b82c6b577"}
- << VersionFileEntry{"fileTwo",
- 644,
- encodeBaseFile("/data/fileTwo"),
- "38f94f54fa3eb72b0ea836538c10b043"})
- << QString() << true;
- }
- void test_parseVersionInfo()
- {
- QFETCH(QByteArray, data);
- QFETCH(VersionFileList, list);
- QFETCH(QString, error);
- QFETCH(bool, ret);
-
- VersionFileList outList;
- QString outError;
- bool outRet = parseVersionInfo(data, outList, outError);
- QCOMPARE(outRet, ret);
- QCOMPARE(outList, list);
- QCOMPARE(outError, error);
- }
-
- void test_processFileLists_data()
- {
- QTest::addColumn<QString>("tempFolder");
- QTest::addColumn<VersionFileList>("currentVersion");
- QTest::addColumn<VersionFileList>("newVersion");
- QTest::addColumn<OperationList>("expectedOperations");
-
- QTemporaryDir tempFolderObj;
- QString tempFolder = tempFolderObj.path();
- // update fileOne, keep fileTwo, remove fileThree
- QTest::newRow("test 1")
- << tempFolder << (VersionFileList()
- << VersionFileEntry{
- "data/fileOne", 493,
- FileSourceList()
- << FileSource(
- "http", "http://host/path/fileOne-1"),
- "9eb84090956c484e32cb6c08455a667b"}
- << VersionFileEntry{
- "data/fileTwo", 644,
- FileSourceList()
- << FileSource(
- "http", "http://host/path/fileTwo-1"),
- "38f94f54fa3eb72b0ea836538c10b043"}
- << VersionFileEntry{
- "data/fileThree", 420,
- FileSourceList()
- << FileSource(
- "http", "http://host/path/fileThree-1"),
- "f12df554b21e320be6471d7154130e70"})
- << (VersionFileList()
- << VersionFileEntry{
- "data/fileOne", 493,
- FileSourceList()
- << FileSource("http",
- "http://host/path/fileOne-2"),
- "42915a71277c9016668cce7b82c6b577"}
- << VersionFileEntry{
- "data/fileTwo", 644,
- FileSourceList()
- << FileSource("http",
- "http://host/path/fileTwo-2"),
- "38f94f54fa3eb72b0ea836538c10b043"})
- << (OperationList()
- << Operation::DeleteOp("data/fileThree")
- << Operation::CopyOp(
- FS::PathCombine(tempFolder,
- QString("data/fileOne").replace("/", "_")),
- "data/fileOne", 493));
- }
- void test_processFileLists()
- {
- QFETCH(QString, tempFolder);
- QFETCH(VersionFileList, currentVersion);
- QFETCH(VersionFileList, newVersion);
- QFETCH(OperationList, expectedOperations);
-
- OperationList operations;
-
- processFileLists(currentVersion, newVersion, QDir::currentPath(), tempFolder, new NetJob("Dummy"), operations);
- qDebug() << (operations == expectedOperations);
- qDebug() << operations;
- qDebug() << expectedOperations;
- QCOMPARE(operations, expectedOperations);
- }
-
- void test_OSXPathFixup()
- {
- QString path, pathOrig;
- bool result;
- // Proper OSX path
- pathOrig = path = "MultiMC.app/Foo/Bar/Baz";
- qDebug() << "Proper OSX path: " << path;
- result = fixPathForOSX(path);
- QCOMPARE(path, QString("Foo/Bar/Baz"));
- QCOMPARE(result, true);
-
- // Bad OSX path
- pathOrig = path = "translations/klingon.lol";
- qDebug() << "Bad OSX path: " << path;
- result = fixPathForOSX(path);
- QCOMPARE(path, pathOrig);
- QCOMPARE(result, false);
- }
+ void initTestCase()
+ {
+ }
+ void cleanupTestCase()
+ {
+ }
+
+ void test_parseVersionInfo_data()
+ {
+ QTest::addColumn<QByteArray>("data");
+ QTest::addColumn<VersionFileList>("list");
+ QTest::addColumn<QString>("error");
+ QTest::addColumn<bool>("ret");
+
+ QTest::newRow("one")
+ << MULTIMC_GET_TEST_FILE("data/1.json")
+ << (VersionFileList()
+ << VersionFileEntry{"fileOne",
+ 493,
+ encodeBaseFile("/data/fileOneA"),
+ "9eb84090956c484e32cb6c08455a667b"}
+ << VersionFileEntry{"fileTwo",
+ 644,
+ encodeBaseFile("/data/fileTwo"),
+ "38f94f54fa3eb72b0ea836538c10b043"}
+ << VersionFileEntry{"fileThree",
+ 750,
+ encodeBaseFile("/data/fileThree"),
+ "f12df554b21e320be6471d7154130e70"})
+ << QString() << true;
+ QTest::newRow("two")
+ << MULTIMC_GET_TEST_FILE("data/2.json")
+ << (VersionFileList()
+ << VersionFileEntry{"fileOne",
+ 493,
+ encodeBaseFile("/data/fileOneB"),
+ "42915a71277c9016668cce7b82c6b577"}
+ << VersionFileEntry{"fileTwo",
+ 644,
+ encodeBaseFile("/data/fileTwo"),
+ "38f94f54fa3eb72b0ea836538c10b043"})
+ << QString() << true;
+ }
+ void test_parseVersionInfo()
+ {
+ QFETCH(QByteArray, data);
+ QFETCH(VersionFileList, list);
+ QFETCH(QString, error);
+ QFETCH(bool, ret);
+
+ VersionFileList outList;
+ QString outError;
+ bool outRet = parseVersionInfo(data, outList, outError);
+ QCOMPARE(outRet, ret);
+ QCOMPARE(outList, list);
+ QCOMPARE(outError, error);
+ }
+
+ void test_processFileLists_data()
+ {
+ QTest::addColumn<QString>("tempFolder");
+ QTest::addColumn<VersionFileList>("currentVersion");
+ QTest::addColumn<VersionFileList>("newVersion");
+ QTest::addColumn<OperationList>("expectedOperations");
+
+ QTemporaryDir tempFolderObj;
+ QString tempFolder = tempFolderObj.path();
+ // update fileOne, keep fileTwo, remove fileThree
+ QTest::newRow("test 1")
+ << tempFolder << (VersionFileList()
+ << VersionFileEntry{
+ "data/fileOne", 493,
+ FileSourceList()
+ << FileSource(
+ "http", "http://host/path/fileOne-1"),
+ "9eb84090956c484e32cb6c08455a667b"}
+ << VersionFileEntry{
+ "data/fileTwo", 644,
+ FileSourceList()
+ << FileSource(
+ "http", "http://host/path/fileTwo-1"),
+ "38f94f54fa3eb72b0ea836538c10b043"}
+ << VersionFileEntry{
+ "data/fileThree", 420,
+ FileSourceList()
+ << FileSource(
+ "http", "http://host/path/fileThree-1"),
+ "f12df554b21e320be6471d7154130e70"})
+ << (VersionFileList()
+ << VersionFileEntry{
+ "data/fileOne", 493,
+ FileSourceList()
+ << FileSource("http",
+ "http://host/path/fileOne-2"),
+ "42915a71277c9016668cce7b82c6b577"}
+ << VersionFileEntry{
+ "data/fileTwo", 644,
+ FileSourceList()
+ << FileSource("http",
+ "http://host/path/fileTwo-2"),
+ "38f94f54fa3eb72b0ea836538c10b043"})
+ << (OperationList()
+ << Operation::DeleteOp("data/fileThree")
+ << Operation::CopyOp(
+ FS::PathCombine(tempFolder,
+ QString("data/fileOne").replace("/", "_")),
+ "data/fileOne", 493));
+ }
+ void test_processFileLists()
+ {
+ QFETCH(QString, tempFolder);
+ QFETCH(VersionFileList, currentVersion);
+ QFETCH(VersionFileList, newVersion);
+ QFETCH(OperationList, expectedOperations);
+
+ OperationList operations;
+
+ processFileLists(currentVersion, newVersion, QDir::currentPath(), tempFolder, new NetJob("Dummy"), operations);
+ qDebug() << (operations == expectedOperations);
+ qDebug() << operations;
+ qDebug() << expectedOperations;
+ QCOMPARE(operations, expectedOperations);
+ }
+
+ void test_OSXPathFixup()
+ {
+ QString path, pathOrig;
+ bool result;
+ // Proper OSX path
+ pathOrig = path = "MultiMC.app/Foo/Bar/Baz";
+ qDebug() << "Proper OSX path: " << path;
+ result = fixPathForOSX(path);
+ QCOMPARE(path, QString("Foo/Bar/Baz"));
+ QCOMPARE(result, true);
+
+ // Bad OSX path
+ pathOrig = path = "translations/klingon.lol";
+ qDebug() << "Bad OSX path: " << path;
+ result = fixPathForOSX(path);
+ QCOMPARE(path, pathOrig);
+ QCOMPARE(result, false);
+ }
};
extern "C"
{
- QTEST_GUILESS_MAIN(DownloadTaskTest)
+ QTEST_GUILESS_MAIN(DownloadTaskTest)
}
#include "DownloadTask_test.moc"
diff --git a/api/logic/updater/GoUpdate.cpp b/api/logic/updater/GoUpdate.cpp
index 716048a0..ef040db6 100644
--- a/api/logic/updater/GoUpdate.cpp
+++ b/api/logic/updater/GoUpdate.cpp
@@ -12,208 +12,208 @@ namespace GoUpdate
bool parseVersionInfo(const QByteArray &data, VersionFileList &list, QString &error)
{
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
- if (jsonError.error != QJsonParseError::NoError)
- {
- error = QString("Failed to parse version info JSON: %1 at %2")
- .arg(jsonError.errorString())
- .arg(jsonError.offset);
- qCritical() << error;
- return false;
- }
-
- QJsonObject json = jsonDoc.object();
-
- qDebug() << data;
- qDebug() << "Loading version info from JSON.";
- QJsonArray filesArray = json.value("Files").toArray();
- for (QJsonValue fileValue : filesArray)
- {
- QJsonObject fileObj = fileValue.toObject();
-
- QString file_path = fileObj.value("Path").toString();
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ error = QString("Failed to parse version info JSON: %1 at %2")
+ .arg(jsonError.errorString())
+ .arg(jsonError.offset);
+ qCritical() << error;
+ return false;
+ }
+
+ QJsonObject json = jsonDoc.object();
+
+ qDebug() << data;
+ qDebug() << "Loading version info from JSON.";
+ QJsonArray filesArray = json.value("Files").toArray();
+ for (QJsonValue fileValue : filesArray)
+ {
+ QJsonObject fileObj = fileValue.toObject();
+
+ QString file_path = fileObj.value("Path").toString();
#ifdef Q_OS_MAC
- // On OSX, the paths for the updater need to be fixed.
- // basically, anything that isn't in the .app folder is ignored.
- // everything else is changed so the code that processes the files actually finds
- // them and puts the replacements in the right spots.
- fixPathForOSX(file_path);
+ // On OSX, the paths for the updater need to be fixed.
+ // basically, anything that isn't in the .app folder is ignored.
+ // everything else is changed so the code that processes the files actually finds
+ // them and puts the replacements in the right spots.
+ fixPathForOSX(file_path);
#endif
- VersionFileEntry file{file_path, fileObj.value("Perms").toVariant().toInt(),
- FileSourceList(), fileObj.value("MD5").toString(), };
- qDebug() << "File" << file.path << "with perms" << file.mode;
-
- QJsonArray sourceArray = fileObj.value("Sources").toArray();
- for (QJsonValue val : sourceArray)
- {
- QJsonObject sourceObj = val.toObject();
-
- QString type = sourceObj.value("SourceType").toString();
- if (type == "http")
- {
- file.sources.append(FileSource("http", sourceObj.value("Url").toString()));
- }
- else
- {
- qWarning() << "Unknown source type" << type << "ignored.";
- }
- }
-
- qDebug() << "Loaded info for" << file.path;
-
- list.append(file);
- }
-
- return true;
+ VersionFileEntry file{file_path, fileObj.value("Perms").toVariant().toInt(),
+ FileSourceList(), fileObj.value("MD5").toString(), };
+ qDebug() << "File" << file.path << "with perms" << file.mode;
+
+ QJsonArray sourceArray = fileObj.value("Sources").toArray();
+ for (QJsonValue val : sourceArray)
+ {
+ QJsonObject sourceObj = val.toObject();
+
+ QString type = sourceObj.value("SourceType").toString();
+ if (type == "http")
+ {
+ file.sources.append(FileSource("http", sourceObj.value("Url").toString()));
+ }
+ else
+ {
+ qWarning() << "Unknown source type" << type << "ignored.";
+ }
+ }
+
+ qDebug() << "Loaded info for" << file.path;
+
+ list.append(file);
+ }
+
+ return true;
}
bool processFileLists
(
- const VersionFileList &currentVersion,
- const VersionFileList &newVersion,
- const QString &rootPath,
- const QString &tempPath,
- NetJobPtr job,
- OperationList &ops
+ const VersionFileList &currentVersion,
+ const VersionFileList &newVersion,
+ const QString &rootPath,
+ const QString &tempPath,
+ NetJobPtr job,
+ OperationList &ops
)
{
- // First, if we've loaded the current version's file list, we need to iterate through it and
- // delete anything in the current one version's list that isn't in the new version's list.
- for (VersionFileEntry entry : currentVersion)
- {
- QFileInfo toDelete(FS::PathCombine(rootPath, entry.path));
- if (!toDelete.exists())
- {
- qCritical() << "Expected file " << toDelete.absoluteFilePath()
- << " doesn't exist!";
- }
- bool keep = false;
-
- //
- for (VersionFileEntry newEntry : newVersion)
- {
- if (newEntry.path == entry.path)
- {
- qDebug() << "Not deleting" << entry.path
- << "because it is still present in the new version.";
- keep = true;
- break;
- }
- }
-
- // If the loop reaches the end and we didn't find a match, delete the file.
- if (!keep)
- {
- if (toDelete.exists())
- ops.append(Operation::DeleteOp(entry.path));
- }
- }
-
- // Next, check each file in MultiMC's folder and see if we need to update them.
- for (VersionFileEntry entry : newVersion)
- {
- // TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a
- // way to do this in the background.
- QString fileMD5;
- QString realEntryPath = FS::PathCombine(rootPath, entry.path);
- QFile entryFile(realEntryPath);
- QFileInfo entryInfo(realEntryPath);
-
- bool needs_upgrade = false;
- if (!entryFile.exists())
- {
- needs_upgrade = true;
- }
- else
- {
- bool pass = true;
- if (!entryInfo.isReadable())
- {
- qCritical() << "File " << realEntryPath << " is not readable.";
- pass = false;
- }
- if (!entryInfo.isWritable())
- {
- qCritical() << "File " << realEntryPath << " is not writable.";
- pass = false;
- }
- if (!entryFile.open(QFile::ReadOnly))
- {
- qCritical() << "File " << realEntryPath << " cannot be opened for reading.";
- pass = false;
- }
- if (!pass)
- {
- ops.clear();
- return false;
- }
- }
-
- if(!needs_upgrade)
- {
- QCryptographicHash hash(QCryptographicHash::Md5);
- auto foo = entryFile.readAll();
-
- hash.addData(foo);
- fileMD5 = hash.result().toHex();
- if ((fileMD5 != entry.md5))
- {
- qDebug() << "MD5Sum does not match!";
- qDebug() << "Expected:'" << entry.md5 << "'";
- qDebug() << "Got: '" << fileMD5 << "'";
- needs_upgrade = true;
- }
- }
-
- // skip file. it doesn't need an upgrade.
- if (!needs_upgrade)
- {
- qDebug() << "File" << realEntryPath << " does not need updating.";
- continue;
- }
-
- // yep. this file actually needs an upgrade. PROCEED.
- qDebug() << "Found file" << realEntryPath << " that needs updating.";
-
- // Go through the sources list and find one to use.
- // TODO: Make a NetAction that takes a source list and tries each of them until one
- // works. For now, we'll just use the first http one.
- for (FileSource source : entry.sources)
- {
- if (source.type != "http")
- continue;
-
- qDebug() << "Will download" << entry.path << "from" << source.url;
-
- // Download it to updatedir/<filepath>-<md5> where filepath is the file's
- // path with slashes replaced by underscores.
- QString dlPath = FS::PathCombine(tempPath, QString(entry.path).replace("/", "_"));
-
- // We need to download the file to the updatefiles folder and add a task
- // to copy it to its install path.
- auto download = Net::Download::makeFile(source.url, dlPath);
- auto rawMd5 = QByteArray::fromHex(entry.md5.toLatin1());
- download->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
- job->addNetAction(download);
- ops.append(Operation::CopyOp(dlPath, entry.path, entry.mode));
- }
- }
- return true;
+ // First, if we've loaded the current version's file list, we need to iterate through it and
+ // delete anything in the current one version's list that isn't in the new version's list.
+ for (VersionFileEntry entry : currentVersion)
+ {
+ QFileInfo toDelete(FS::PathCombine(rootPath, entry.path));
+ if (!toDelete.exists())
+ {
+ qCritical() << "Expected file " << toDelete.absoluteFilePath()
+ << " doesn't exist!";
+ }
+ bool keep = false;
+
+ //
+ for (VersionFileEntry newEntry : newVersion)
+ {
+ if (newEntry.path == entry.path)
+ {
+ qDebug() << "Not deleting" << entry.path
+ << "because it is still present in the new version.";
+ keep = true;
+ break;
+ }
+ }
+
+ // If the loop reaches the end and we didn't find a match, delete the file.
+ if (!keep)
+ {
+ if (toDelete.exists())
+ ops.append(Operation::DeleteOp(entry.path));
+ }
+ }
+
+ // Next, check each file in MultiMC's folder and see if we need to update them.
+ for (VersionFileEntry entry : newVersion)
+ {
+ // TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a
+ // way to do this in the background.
+ QString fileMD5;
+ QString realEntryPath = FS::PathCombine(rootPath, entry.path);
+ QFile entryFile(realEntryPath);
+ QFileInfo entryInfo(realEntryPath);
+
+ bool needs_upgrade = false;
+ if (!entryFile.exists())
+ {
+ needs_upgrade = true;
+ }
+ else
+ {
+ bool pass = true;
+ if (!entryInfo.isReadable())
+ {
+ qCritical() << "File " << realEntryPath << " is not readable.";
+ pass = false;
+ }
+ if (!entryInfo.isWritable())
+ {
+ qCritical() << "File " << realEntryPath << " is not writable.";
+ pass = false;
+ }
+ if (!entryFile.open(QFile::ReadOnly))
+ {
+ qCritical() << "File " << realEntryPath << " cannot be opened for reading.";
+ pass = false;
+ }
+ if (!pass)
+ {
+ ops.clear();
+ return false;
+ }
+ }
+
+ if(!needs_upgrade)
+ {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ auto foo = entryFile.readAll();
+
+ hash.addData(foo);
+ fileMD5 = hash.result().toHex();
+ if ((fileMD5 != entry.md5))
+ {
+ qDebug() << "MD5Sum does not match!";
+ qDebug() << "Expected:'" << entry.md5 << "'";
+ qDebug() << "Got: '" << fileMD5 << "'";
+ needs_upgrade = true;
+ }
+ }
+
+ // skip file. it doesn't need an upgrade.
+ if (!needs_upgrade)
+ {
+ qDebug() << "File" << realEntryPath << " does not need updating.";
+ continue;
+ }
+
+ // yep. this file actually needs an upgrade. PROCEED.
+ qDebug() << "Found file" << realEntryPath << " that needs updating.";
+
+ // Go through the sources list and find one to use.
+ // TODO: Make a NetAction that takes a source list and tries each of them until one
+ // works. For now, we'll just use the first http one.
+ for (FileSource source : entry.sources)
+ {
+ if (source.type != "http")
+ continue;
+
+ qDebug() << "Will download" << entry.path << "from" << source.url;
+
+ // Download it to updatedir/<filepath>-<md5> where filepath is the file's
+ // path with slashes replaced by underscores.
+ QString dlPath = FS::PathCombine(tempPath, QString(entry.path).replace("/", "_"));
+
+ // We need to download the file to the updatefiles folder and add a task
+ // to copy it to its install path.
+ auto download = Net::Download::makeFile(source.url, dlPath);
+ auto rawMd5 = QByteArray::fromHex(entry.md5.toLatin1());
+ download->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
+ job->addNetAction(download);
+ ops.append(Operation::CopyOp(dlPath, entry.path, entry.mode));
+ }
+ }
+ return true;
}
bool fixPathForOSX(QString &path)
{
- if (path.startsWith("MultiMC.app/"))
- {
- // remove the prefix and add a new, more appropriate one.
- path.remove(0, 12);
- return true;
- }
- else
- {
- qCritical() << "Update path not within .app: " << path;
- return false;
- }
+ if (path.startsWith("MultiMC.app/"))
+ {
+ // remove the prefix and add a new, more appropriate one.
+ path.remove(0, 12);
+ return true;
+ }
+ else
+ {
+ qCritical() << "Update path not within .app: " << path;
+ return false;
+ }
}
} \ No newline at end of file
diff --git a/api/logic/updater/GoUpdate.h b/api/logic/updater/GoUpdate.h
index 95f26b8c..54559a3c 100644
--- a/api/logic/updater/GoUpdate.h
+++ b/api/logic/updater/GoUpdate.h
@@ -12,16 +12,16 @@ namespace GoUpdate
*/
struct MULTIMC_LOGIC_EXPORT Status
{
- bool updateAvailable = false;
+ bool updateAvailable = false;
- int newVersionId = -1;
- QString newRepoUrl;
+ int newVersionId = -1;
+ QString newRepoUrl;
- int currentVersionId = -1;
- QString currentRepoUrl;
+ int currentVersionId = -1;
+ QString currentRepoUrl;
- // path to the root of the application
- QString rootPath;
+ // path to the root of the application
+ QString rootPath;
};
/**
@@ -29,21 +29,21 @@ struct MULTIMC_LOGIC_EXPORT Status
*/
struct MULTIMC_LOGIC_EXPORT FileSource
{
- FileSource(QString type, QString url, QString compression="")
- {
- this->type = type;
- this->url = url;
- this->compressionType = compression;
- }
-
- bool operator==(const FileSource &f2) const
- {
- return type == f2.type && url == f2.url && compressionType == f2.compressionType;
- }
-
- QString type;
- QString url;
- QString compressionType;
+ FileSource(QString type, QString url, QString compression="")
+ {
+ this->type = type;
+ this->url = url;
+ this->compressionType = compression;
+ }
+
+ bool operator==(const FileSource &f2) const
+ {
+ return type == f2.type && url == f2.url && compressionType == f2.compressionType;
+ }
+
+ QString type;
+ QString url;
+ QString compressionType;
};
typedef QList<FileSource> FileSourceList;
@@ -52,14 +52,14 @@ typedef QList<FileSource> FileSourceList;
*/
struct MULTIMC_LOGIC_EXPORT VersionFileEntry
{
- QString path;
- int mode;
- FileSourceList sources;
- QString md5;
- bool operator==(const VersionFileEntry &v2) const
- {
- return path == v2.path && mode == v2.mode && sources == v2.sources && md5 == v2.md5;
- }
+ QString path;
+ int mode;
+ FileSourceList sources;
+ QString md5;
+ bool operator==(const VersionFileEntry &v2) const
+ {
+ return path == v2.path && mode == v2.mode && sources == v2.sources && md5 == v2.md5;
+ }
};
typedef QList<VersionFileEntry> VersionFileList;
@@ -68,39 +68,39 @@ typedef QList<VersionFileEntry> VersionFileList;
*/
struct MULTIMC_LOGIC_EXPORT Operation
{
- static Operation CopyOp(QString from, QString to, int fmode=0644)
- {
- return Operation{OP_REPLACE, from, to, fmode};
- }
- static Operation DeleteOp(QString file)
- {
- return Operation{OP_DELETE, QString(), file, 0644};
- }
-
- // FIXME: for some types, some of the other fields are irrelevant!
- bool operator==(const Operation &u2) const
- {
- return type == u2.type &&
- source == u2.source &&
- destination == u2.destination &&
- destinationMode == u2.destinationMode;
- }
-
- //! Specifies the type of operation that this is.
- enum Type
- {
- OP_REPLACE,
- OP_DELETE,
- } type;
-
- //! The source file, if any
- QString source;
-
- //! The destination file.
- QString destination;
-
- //! The mode to change the destination file to.
- int destinationMode;
+ static Operation CopyOp(QString from, QString to, int fmode=0644)
+ {
+ return Operation{OP_REPLACE, from, to, fmode};
+ }
+ static Operation DeleteOp(QString file)
+ {
+ return Operation{OP_DELETE, QString(), file, 0644};
+ }
+
+ // FIXME: for some types, some of the other fields are irrelevant!
+ bool operator==(const Operation &u2) const
+ {
+ return type == u2.type &&
+ source == u2.source &&
+ destination == u2.destination &&
+ destinationMode == u2.destinationMode;
+ }
+
+ //! Specifies the type of operation that this is.
+ enum Type
+ {
+ OP_REPLACE,
+ OP_DELETE,
+ } type;
+
+ //! The source file, if any
+ QString source;
+
+ //! The destination file.
+ QString destination;
+
+ //! The mode to change the destination file to.
+ int destinationMode;
};
typedef QList<Operation> OperationList;
@@ -115,12 +115,12 @@ bool MULTIMC_LOGIC_EXPORT parseVersionInfo(const QByteArray &data, VersionFileLi
*/
bool MULTIMC_LOGIC_EXPORT processFileLists
(
- const VersionFileList &currentVersion,
- const VersionFileList &newVersion,
- const QString &rootPath,
- const QString &tempPath,
- NetJobPtr job,
- OperationList &ops
+ const VersionFileList &currentVersion,
+ const VersionFileList &newVersion,
+ const QString &rootPath,
+ const QString &tempPath,
+ NetJobPtr job,
+ OperationList &ops
);
/*!
@@ -133,4 +133,4 @@ bool MULTIMC_LOGIC_EXPORT processFileLists
bool MULTIMC_LOGIC_EXPORT fixPathForOSX(QString &path);
}
-Q_DECLARE_METATYPE(GoUpdate::Status); \ No newline at end of file
+Q_DECLARE_METATYPE(GoUpdate::Status)
diff --git a/api/logic/updater/UpdateChecker.cpp b/api/logic/updater/UpdateChecker.cpp
index d8be2c1a..b3c2641d 100644
--- a/api/logic/updater/UpdateChecker.cpp
+++ b/api/logic/updater/UpdateChecker.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,236 +25,236 @@
UpdateChecker::UpdateChecker(QString channelListUrl, QString currentChannel, int currentBuild)
{
- m_channelListUrl = channelListUrl;
- m_currentChannel = currentChannel;
- m_currentBuild = currentBuild;
+ m_channelListUrl = channelListUrl;
+ m_currentChannel = currentChannel;
+ m_currentBuild = currentBuild;
}
QList<UpdateChecker::ChannelListEntry> UpdateChecker::getChannelList() const
{
- return m_channels;
+ return m_channels;
}
bool UpdateChecker::hasChannels() const
{
- return !m_channels.isEmpty();
+ return !m_channels.isEmpty();
}
void UpdateChecker::checkForUpdate(QString updateChannel, bool notifyNoUpdate)
{
- qDebug() << "Checking for updates.";
-
- // If the channel list hasn't loaded yet, load it and defer checking for updates until
- // later.
- if (!m_chanListLoaded)
- {
- qDebug() << "Channel list isn't loaded yet. Loading channel list and deferring "
- "update check.";
- m_checkUpdateWaiting = true;
- m_deferredUpdateChannel = updateChannel;
- updateChanList(notifyNoUpdate);
- return;
- }
-
- if (m_updateChecking)
- {
- qDebug() << "Ignoring update check request. Already checking for updates.";
- return;
- }
-
- m_updateChecking = true;
-
- // Find the desired channel within the channel list and get its repo URL. If if cannot be
- // found, error.
- m_newRepoUrl = "";
- for (ChannelListEntry entry : m_channels)
- {
- if (entry.id == updateChannel)
- m_newRepoUrl = entry.url;
- if (entry.id == m_currentChannel)
- m_currentRepoUrl = entry.url;
- }
-
- qDebug() << "m_repoUrl = " << m_newRepoUrl;
-
- // If we didn't find our channel, error.
- if (m_newRepoUrl.isEmpty())
- {
- qCritical() << "m_repoUrl is empty!";
- emit updateCheckFailed();
- return;
- }
-
- QUrl indexUrl = QUrl(m_newRepoUrl).resolved(QUrl("index.json"));
-
- auto job = new NetJob("GoUpdate Repository Index");
- job->addNetAction(Net::Download::makeByteArray(indexUrl, &indexData));
- connect(job, &NetJob::succeeded, [this, notifyNoUpdate](){ updateCheckFinished(notifyNoUpdate); });
- connect(job, &NetJob::failed, this, &UpdateChecker::updateCheckFailed);
- indexJob.reset(job);
- job->start();
+ qDebug() << "Checking for updates.";
+
+ // If the channel list hasn't loaded yet, load it and defer checking for updates until
+ // later.
+ if (!m_chanListLoaded)
+ {
+ qDebug() << "Channel list isn't loaded yet. Loading channel list and deferring "
+ "update check.";
+ m_checkUpdateWaiting = true;
+ m_deferredUpdateChannel = updateChannel;
+ updateChanList(notifyNoUpdate);
+ return;
+ }
+
+ if (m_updateChecking)
+ {
+ qDebug() << "Ignoring update check request. Already checking for updates.";
+ return;
+ }
+
+ m_updateChecking = true;
+
+ // Find the desired channel within the channel list and get its repo URL. If if cannot be
+ // found, error.
+ m_newRepoUrl = "";
+ for (ChannelListEntry entry : m_channels)
+ {
+ if (entry.id == updateChannel)
+ m_newRepoUrl = entry.url;
+ if (entry.id == m_currentChannel)
+ m_currentRepoUrl = entry.url;
+ }
+
+ qDebug() << "m_repoUrl = " << m_newRepoUrl;
+
+ // If we didn't find our channel, error.
+ if (m_newRepoUrl.isEmpty())
+ {
+ qCritical() << "m_repoUrl is empty!";
+ emit updateCheckFailed();
+ return;
+ }
+
+ QUrl indexUrl = QUrl(m_newRepoUrl).resolved(QUrl("index.json"));
+
+ auto job = new NetJob("GoUpdate Repository Index");
+ job->addNetAction(Net::Download::makeByteArray(indexUrl, &indexData));
+ connect(job, &NetJob::succeeded, [this, notifyNoUpdate](){ updateCheckFinished(notifyNoUpdate); });
+ connect(job, &NetJob::failed, this, &UpdateChecker::updateCheckFailed);
+ indexJob.reset(job);
+ job->start();
}
void UpdateChecker::updateCheckFinished(bool notifyNoUpdate)
{
- qDebug() << "Finished downloading repo index. Checking for new versions.";
-
- QJsonParseError jsonError;
- indexJob.reset();
-
- QJsonDocument jsonDoc = QJsonDocument::fromJson(indexData, &jsonError);
- indexData.clear();
- if (jsonError.error != QJsonParseError::NoError || !jsonDoc.isObject())
- {
- qCritical() << "Failed to parse GoUpdate repository index. JSON error"
- << jsonError.errorString() << "at offset" << jsonError.offset;
- m_updateChecking = false;
- return;
- }
-
- QJsonObject object = jsonDoc.object();
-
- bool success = false;
- int apiVersion = object.value("ApiVersion").toVariant().toInt(&success);
- if (apiVersion != API_VERSION || !success)
- {
- qCritical() << "Failed to check for updates. API version mismatch. We're using"
- << API_VERSION << "server has" << apiVersion;
- m_updateChecking = false;
- return;
- }
-
- qDebug() << "Processing repository version list.";
- QJsonObject newestVersion;
- QJsonArray versions = object.value("Versions").toArray();
- for (QJsonValue versionVal : versions)
- {
- QJsonObject version = versionVal.toObject();
- if (newestVersion.value("Id").toVariant().toInt() <
- version.value("Id").toVariant().toInt())
- {
- newestVersion = version;
- }
- }
-
- // We've got the version with the greatest ID number. Now compare it to our current build
- // number and update if they're different.
- int newBuildNumber = newestVersion.value("Id").toVariant().toInt();
- if (newBuildNumber != m_currentBuild)
- {
- qDebug() << "Found newer version with ID" << newBuildNumber;
- // Update!
- GoUpdate::Status updateStatus;
- updateStatus.updateAvailable = true;
- updateStatus.currentVersionId = m_currentBuild;
- updateStatus.currentRepoUrl = m_currentRepoUrl;
- updateStatus.newVersionId = newBuildNumber;
- updateStatus.newRepoUrl = m_newRepoUrl;
- emit updateAvailable(updateStatus);
- }
- else if (notifyNoUpdate)
- {
- emit noUpdateFound();
- }
- m_updateChecking = false;
+ qDebug() << "Finished downloading repo index. Checking for new versions.";
+
+ QJsonParseError jsonError;
+ indexJob.reset();
+
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(indexData, &jsonError);
+ indexData.clear();
+ if (jsonError.error != QJsonParseError::NoError || !jsonDoc.isObject())
+ {
+ qCritical() << "Failed to parse GoUpdate repository index. JSON error"
+ << jsonError.errorString() << "at offset" << jsonError.offset;
+ m_updateChecking = false;
+ return;
+ }
+
+ QJsonObject object = jsonDoc.object();
+
+ bool success = false;
+ int apiVersion = object.value("ApiVersion").toVariant().toInt(&success);
+ if (apiVersion != API_VERSION || !success)
+ {
+ qCritical() << "Failed to check for updates. API version mismatch. We're using"
+ << API_VERSION << "server has" << apiVersion;
+ m_updateChecking = false;
+ return;
+ }
+
+ qDebug() << "Processing repository version list.";
+ QJsonObject newestVersion;
+ QJsonArray versions = object.value("Versions").toArray();
+ for (QJsonValue versionVal : versions)
+ {
+ QJsonObject version = versionVal.toObject();
+ if (newestVersion.value("Id").toVariant().toInt() <
+ version.value("Id").toVariant().toInt())
+ {
+ newestVersion = version;
+ }
+ }
+
+ // We've got the version with the greatest ID number. Now compare it to our current build
+ // number and update if they're different.
+ int newBuildNumber = newestVersion.value("Id").toVariant().toInt();
+ if (newBuildNumber != m_currentBuild)
+ {
+ qDebug() << "Found newer version with ID" << newBuildNumber;
+ // Update!
+ GoUpdate::Status updateStatus;
+ updateStatus.updateAvailable = true;
+ updateStatus.currentVersionId = m_currentBuild;
+ updateStatus.currentRepoUrl = m_currentRepoUrl;
+ updateStatus.newVersionId = newBuildNumber;
+ updateStatus.newRepoUrl = m_newRepoUrl;
+ emit updateAvailable(updateStatus);
+ }
+ else if (notifyNoUpdate)
+ {
+ emit noUpdateFound();
+ }
+ m_updateChecking = false;
}
void UpdateChecker::updateCheckFailed()
{
- qCritical() << "Update check failed for reasons unknown.";
+ qCritical() << "Update check failed for reasons unknown.";
}
void UpdateChecker::updateChanList(bool notifyNoUpdate)
{
- qDebug() << "Loading the channel list.";
-
- if (m_chanListLoading)
- {
- qDebug() << "Ignoring channel list update request. Already grabbing channel list.";
- return;
- }
-
- if (m_channelListUrl.isEmpty())
- {
- qCritical() << "Failed to update channel list. No channel list URL set."
- << "If you'd like to use MultiMC's update system, please pass the channel "
- "list URL to CMake at compile time.";
- return;
- }
-
- m_chanListLoading = true;
- NetJob *job = new NetJob("Update System Channel List");
- job->addNetAction(Net::Download::makeByteArray(QUrl(m_channelListUrl), &chanlistData));
- connect(job, &NetJob::succeeded, [this, notifyNoUpdate]() { chanListDownloadFinished(notifyNoUpdate); });
- QObject::connect(job, &NetJob::failed, this, &UpdateChecker::chanListDownloadFailed);
- chanListJob.reset(job);
- job->start();
+ qDebug() << "Loading the channel list.";
+
+ if (m_chanListLoading)
+ {
+ qDebug() << "Ignoring channel list update request. Already grabbing channel list.";
+ return;
+ }
+
+ if (m_channelListUrl.isEmpty())
+ {
+ qCritical() << "Failed to update channel list. No channel list URL set."
+ << "If you'd like to use MultiMC's update system, please pass the channel "
+ "list URL to CMake at compile time.";
+ return;
+ }
+
+ m_chanListLoading = true;
+ NetJob *job = new NetJob("Update System Channel List");
+ job->addNetAction(Net::Download::makeByteArray(QUrl(m_channelListUrl), &chanlistData));
+ connect(job, &NetJob::succeeded, [this, notifyNoUpdate]() { chanListDownloadFinished(notifyNoUpdate); });
+ QObject::connect(job, &NetJob::failed, this, &UpdateChecker::chanListDownloadFailed);
+ chanListJob.reset(job);
+ job->start();
}
void UpdateChecker::chanListDownloadFinished(bool notifyNoUpdate)
{
- chanListJob.reset();
-
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(chanlistData, &jsonError);
- chanlistData.clear();
- if (jsonError.error != QJsonParseError::NoError)
- {
- // TODO: Report errors to the user.
- qCritical() << "Failed to parse channel list JSON:" << jsonError.errorString() << "at" << jsonError.offset;
- m_chanListLoading = false;
- return;
- }
-
- QJsonObject object = jsonDoc.object();
-
- bool success = false;
- int formatVersion = object.value("format_version").toVariant().toInt(&success);
- if (formatVersion != CHANLIST_FORMAT || !success)
- {
- qCritical()
- << "Failed to check for updates. Channel list format version mismatch. We're using"
- << CHANLIST_FORMAT << "server has" << formatVersion;
- m_chanListLoading = false;
- return;
- }
-
- // Load channels into a temporary array.
- QList<ChannelListEntry> loadedChannels;
- QJsonArray channelArray = object.value("channels").toArray();
- for (QJsonValue chanVal : channelArray)
- {
- QJsonObject channelObj = chanVal.toObject();
- ChannelListEntry entry{channelObj.value("id").toVariant().toString(),
- channelObj.value("name").toVariant().toString(),
- channelObj.value("description").toVariant().toString(),
- channelObj.value("url").toVariant().toString()};
- if (entry.id.isEmpty() || entry.name.isEmpty() || entry.url.isEmpty())
- {
- qCritical() << "Channel list entry with empty ID, name, or URL. Skipping.";
- continue;
- }
- loadedChannels.append(entry);
- }
-
- // Swap the channel list we just loaded into the object's channel list.
- m_channels.swap(loadedChannels);
-
- m_chanListLoading = false;
- m_chanListLoaded = true;
- qDebug() << "Successfully loaded UpdateChecker channel list.";
-
- // If we're waiting to check for updates, do that now.
- if (m_checkUpdateWaiting)
- checkForUpdate(m_deferredUpdateChannel, notifyNoUpdate);
-
- emit channelListLoaded();
+ chanListJob.reset();
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(chanlistData, &jsonError);
+ chanlistData.clear();
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ // TODO: Report errors to the user.
+ qCritical() << "Failed to parse channel list JSON:" << jsonError.errorString() << "at" << jsonError.offset;
+ m_chanListLoading = false;
+ return;
+ }
+
+ QJsonObject object = jsonDoc.object();
+
+ bool success = false;
+ int formatVersion = object.value("format_version").toVariant().toInt(&success);
+ if (formatVersion != CHANLIST_FORMAT || !success)
+ {
+ qCritical()
+ << "Failed to check for updates. Channel list format version mismatch. We're using"
+ << CHANLIST_FORMAT << "server has" << formatVersion;
+ m_chanListLoading = false;
+ return;
+ }
+
+ // Load channels into a temporary array.
+ QList<ChannelListEntry> loadedChannels;
+ QJsonArray channelArray = object.value("channels").toArray();
+ for (QJsonValue chanVal : channelArray)
+ {
+ QJsonObject channelObj = chanVal.toObject();
+ ChannelListEntry entry{channelObj.value("id").toVariant().toString(),
+ channelObj.value("name").toVariant().toString(),
+ channelObj.value("description").toVariant().toString(),
+ channelObj.value("url").toVariant().toString()};
+ if (entry.id.isEmpty() || entry.name.isEmpty() || entry.url.isEmpty())
+ {
+ qCritical() << "Channel list entry with empty ID, name, or URL. Skipping.";
+ continue;
+ }
+ loadedChannels.append(entry);
+ }
+
+ // Swap the channel list we just loaded into the object's channel list.
+ m_channels.swap(loadedChannels);
+
+ m_chanListLoading = false;
+ m_chanListLoaded = true;
+ qDebug() << "Successfully loaded UpdateChecker channel list.";
+
+ // If we're waiting to check for updates, do that now.
+ if (m_checkUpdateWaiting)
+ checkForUpdate(m_deferredUpdateChannel, notifyNoUpdate);
+
+ emit channelListLoaded();
}
void UpdateChecker::chanListDownloadFailed(QString reason)
{
- m_chanListLoading = false;
- qCritical() << QString("Failed to download channel list: %1").arg(reason);
- emit channelListLoaded();
+ m_chanListLoading = false;
+ qCritical() << QString("Failed to download channel list: %1").arg(reason);
+ emit channelListLoaded();
}
diff --git a/api/logic/updater/UpdateChecker.h b/api/logic/updater/UpdateChecker.h
index 4996da26..4ad39c0b 100644
--- a/api/logic/updater/UpdateChecker.h
+++ b/api/logic/updater/UpdateChecker.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 MultiMC Contributors
+/* Copyright 2013-2019 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,100 +22,100 @@
class MULTIMC_LOGIC_EXPORT UpdateChecker : public QObject
{
- Q_OBJECT
+ Q_OBJECT
public:
- UpdateChecker(QString channelListUrl, QString currentChannel, int currentBuild);
- void checkForUpdate(QString updateChannel, bool notifyNoUpdate);
-
- /*!
- * Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake).
- * If this isn't called before checkForUpdate(), it will automatically be called.
- */
- void updateChanList(bool notifyNoUpdate);
-
- /*!
- * An entry in the channel list.
- */
- struct ChannelListEntry
- {
- QString id;
- QString name;
- QString description;
- QString url;
- };
-
- /*!
- * Returns a the current channel list.
- * If the channel list hasn't been loaded, this list will be empty.
- */
- QList<ChannelListEntry> getChannelList() const;
-
- /*!
- * Returns false if the channel list is empty.
- */
- bool hasChannels() const;
+ UpdateChecker(QString channelListUrl, QString currentChannel, int currentBuild);
+ void checkForUpdate(QString updateChannel, bool notifyNoUpdate);
+
+ /*!
+ * Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake).
+ * If this isn't called before checkForUpdate(), it will automatically be called.
+ */
+ void updateChanList(bool notifyNoUpdate);
+
+ /*!
+ * An entry in the channel list.
+ */
+ struct ChannelListEntry
+ {
+ QString id;
+ QString name;
+ QString description;
+ QString url;
+ };
+
+ /*!
+ * Returns a the current channel list.
+ * If the channel list hasn't been loaded, this list will be empty.
+ */
+ QList<ChannelListEntry> getChannelList() const;
+
+ /*!
+ * Returns false if the channel list is empty.
+ */
+ bool hasChannels() const;
signals:
- //! Signal emitted when an update is available. Passes the URL for the repo and the ID and name for the version.
- void updateAvailable(GoUpdate::Status status);
+ //! Signal emitted when an update is available. Passes the URL for the repo and the ID and name for the version.
+ void updateAvailable(GoUpdate::Status status);
- //! Signal emitted when the channel list finishes loading or fails to load.
- void channelListLoaded();
+ //! Signal emitted when the channel list finishes loading or fails to load.
+ void channelListLoaded();
- void noUpdateFound();
+ void noUpdateFound();
private slots:
- void updateCheckFinished(bool notifyNoUpdate);
- void updateCheckFailed();
+ void updateCheckFinished(bool notifyNoUpdate);
+ void updateCheckFailed();
- void chanListDownloadFinished(bool notifyNoUpdate);
- void chanListDownloadFailed(QString reason);
+ void chanListDownloadFinished(bool notifyNoUpdate);
+ void chanListDownloadFailed(QString reason);
private:
- friend class UpdateCheckerTest;
-
- NetJobPtr indexJob;
- QByteArray indexData;
- NetJobPtr chanListJob;
- QByteArray chanlistData;
-
- QString m_channelListUrl;
-
- QList<ChannelListEntry> m_channels;
-
- /*!
- * True while the system is checking for updates.
- * If checkForUpdate is called while this is true, it will be ignored.
- */
- bool m_updateChecking = false;
-
- /*!
- * True if the channel list has loaded.
- * If this is false, trying to check for updates will call updateChanList first.
- */
- bool m_chanListLoaded = false;
-
- /*!
- * Set to true while the channel list is currently loading.
- */
- bool m_chanListLoading = false;
-
- /*!
- * Set to true when checkForUpdate is called while the channel list isn't loaded.
- * When the channel list finishes loading, if this is true, the update checker will check for updates.
- */
- bool m_checkUpdateWaiting = false;
-
- /*!
- * if m_checkUpdateWaiting, this is the last used update channel
- */
- QString m_deferredUpdateChannel;
-
- int m_currentBuild = -1;
- QString m_currentChannel;
- QString m_currentRepoUrl;
-
- QString m_newRepoUrl;
+ friend class UpdateCheckerTest;
+
+ NetJobPtr indexJob;
+ QByteArray indexData;
+ NetJobPtr chanListJob;
+ QByteArray chanlistData;
+
+ QString m_channelListUrl;
+
+ QList<ChannelListEntry> m_channels;
+
+ /*!
+ * True while the system is checking for updates.
+ * If checkForUpdate is called while this is true, it will be ignored.
+ */
+ bool m_updateChecking = false;
+
+ /*!
+ * True if the channel list has loaded.
+ * If this is false, trying to check for updates will call updateChanList first.
+ */
+ bool m_chanListLoaded = false;
+
+ /*!
+ * Set to true while the channel list is currently loading.
+ */
+ bool m_chanListLoading = false;
+
+ /*!
+ * Set to true when checkForUpdate is called while the channel list isn't loaded.
+ * When the channel list finishes loading, if this is true, the update checker will check for updates.
+ */
+ bool m_checkUpdateWaiting = false;
+
+ /*!
+ * if m_checkUpdateWaiting, this is the last used update channel
+ */
+ QString m_deferredUpdateChannel;
+
+ int m_currentBuild = -1;
+ QString m_currentChannel;
+ QString m_currentRepoUrl;
+
+ QString m_newRepoUrl;
};
diff --git a/api/logic/updater/UpdateChecker_test.cpp b/api/logic/updater/UpdateChecker_test.cpp
index 16b21614..5702d9c6 100644
--- a/api/logic/updater/UpdateChecker_test.cpp
+++ b/api/logic/updater/UpdateChecker_test.cpp
@@ -8,137 +8,138 @@ Q_DECLARE_METATYPE(UpdateChecker::ChannelListEntry)
bool operator==(const UpdateChecker::ChannelListEntry &e1, const UpdateChecker::ChannelListEntry &e2)
{
- qDebug() << e1.url << "vs" << e2.url;
- return e1.id == e2.id &&
- e1.name == e2.name &&
- e1.description == e2.description &&
- e1.url == e2.url;
+ qDebug() << e1.url << "vs" << e2.url;
+ return e1.id == e2.id &&
+ e1.name == e2.name &&
+ e1.description == e2.description &&
+ e1.url == e2.url;
}
QDebug operator<<(QDebug dbg, const UpdateChecker::ChannelListEntry &c)
{
- dbg.nospace() << "ChannelListEntry(id=" << c.id << " name=" << c.name << " description=" << c.description << " url=" << c.url << ")";
- return dbg.maybeSpace();
+ dbg.nospace() << "ChannelListEntry(id=" << c.id << " name=" << c.name << " description=" << c.description << " url=" << c.url << ")";
+ return dbg.maybeSpace();
+}
+
+QString findTestDataUrl(const char *file)
+{
+ return QUrl::fromLocalFile(QFINDTESTDATA(file)).toString();
}
class UpdateCheckerTest : public QObject
{
- Q_OBJECT
+ Q_OBJECT
private
slots:
- void initTestCase()
- {
-
- }
- void cleanupTestCase()
- {
-
- }
-
- static QString findTestDataUrl(const char *file)
- {
- return QUrl::fromLocalFile(QFINDTESTDATA(file)).toString();
- }
- void tst_ChannelListParsing_data()
- {
- QTest::addColumn<QString>("channel");
- QTest::addColumn<QString>("channelUrl");
- QTest::addColumn<bool>("hasChannels");
- QTest::addColumn<bool>("valid");
- QTest::addColumn<QList<UpdateChecker::ChannelListEntry> >("result");
-
- QTest::newRow("garbage")
- << QString()
- << findTestDataUrl("data/garbageChannels.json")
- << false
- << false
- << QList<UpdateChecker::ChannelListEntry>();
- QTest::newRow("errors")
- << QString()
- << findTestDataUrl("data/errorChannels.json")
- << false
- << true
- << QList<UpdateChecker::ChannelListEntry>();
- QTest::newRow("no channels")
- << QString()
- << findTestDataUrl("data/noChannels.json")
- << false
- << true
- << QList<UpdateChecker::ChannelListEntry>();
- QTest::newRow("one channel")
- << QString("develop")
- << findTestDataUrl("data/oneChannel.json")
- << true
- << true
- << (QList<UpdateChecker::ChannelListEntry>() << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", "http://example.org/stuff"});
- QTest::newRow("several channels")
- << QString("develop")
- << findTestDataUrl("data/channels.json")
- << true
- << true
- << (QList<UpdateChecker::ChannelListEntry>()
- << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", findTestDataUrl("data")}
- << UpdateChecker::ChannelListEntry{"stable", "Stable", "It's stable at least", findTestDataUrl("data")}
- << UpdateChecker::ChannelListEntry{"42", "The Channel", "This is the channel that is going to answer all of your questions", "https://dent.me/tea"});
- }
- void tst_ChannelListParsing()
- {
-
- QFETCH(QString, channel);
- QFETCH(QString, channelUrl);
- QFETCH(bool, hasChannels);
- QFETCH(bool, valid);
- QFETCH(QList<UpdateChecker::ChannelListEntry>, result);
-
- UpdateChecker checker(channelUrl, channel, 0);
-
- QSignalSpy channelListLoadedSpy(&checker, SIGNAL(channelListLoaded()));
- QVERIFY(channelListLoadedSpy.isValid());
-
- checker.updateChanList(false);
-
- if (valid)
- {
- QVERIFY(channelListLoadedSpy.wait());
- QCOMPARE(channelListLoadedSpy.size(), 1);
- }
- else
- {
- channelListLoadedSpy.wait();
- QCOMPARE(channelListLoadedSpy.size(), 0);
- }
-
- QCOMPARE(checker.hasChannels(), hasChannels);
- QCOMPARE(checker.getChannelList(), result);
- }
-
- void tst_UpdateChecking()
- {
- QString channel = "develop";
- QString channelUrl = findTestDataUrl("data/channels.json");
- int currentBuild = 2;
-
- UpdateChecker checker(channelUrl, channel, currentBuild);
-
- QSignalSpy updateAvailableSpy(&checker, SIGNAL(updateAvailable(GoUpdate::Status)));
- QVERIFY(updateAvailableSpy.isValid());
- QSignalSpy channelListLoadedSpy(&checker, SIGNAL(channelListLoaded()));
- QVERIFY(channelListLoadedSpy.isValid());
-
- checker.updateChanList(false);
- QVERIFY(channelListLoadedSpy.wait());
-
- qDebug() << "CWD:" << QDir::current().absolutePath();
- checker.m_channels[0].url = findTestDataUrl("data/");
- checker.checkForUpdate(channel, false);
-
- QVERIFY(updateAvailableSpy.wait());
-
- auto status = updateAvailableSpy.first().first().value<GoUpdate::Status>();
- QCOMPARE(checker.m_channels[0].url, status.newRepoUrl);
- QCOMPARE(3, status.newVersionId);
- QCOMPARE(currentBuild, status.currentVersionId);
- }
+ void initTestCase()
+ {
+
+ }
+ void cleanupTestCase()
+ {
+
+ }
+
+ void tst_ChannelListParsing_data()
+ {
+ QTest::addColumn<QString>("channel");
+ QTest::addColumn<QString>("channelUrl");
+ QTest::addColumn<bool>("hasChannels");
+ QTest::addColumn<bool>("valid");
+ QTest::addColumn<QList<UpdateChecker::ChannelListEntry> >("result");
+
+ QTest::newRow("garbage")
+ << QString()
+ << findTestDataUrl("data/garbageChannels.json")
+ << false
+ << false
+ << QList<UpdateChecker::ChannelListEntry>();
+ QTest::newRow("errors")
+ << QString()
+ << findTestDataUrl("data/errorChannels.json")
+ << false
+ << true
+ << QList<UpdateChecker::ChannelListEntry>();
+ QTest::newRow("no channels")
+ << QString()
+ << findTestDataUrl("data/noChannels.json")
+ << false
+ << true
+ << QList<UpdateChecker::ChannelListEntry>();
+ QTest::newRow("one channel")
+ << QString("develop")
+ << findTestDataUrl("data/oneChannel.json")
+ << true
+ << true
+ << (QList<UpdateChecker::ChannelListEntry>() << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", "http://example.org/stuff"});
+ QTest::newRow("several channels")
+ << QString("develop")
+ << findTestDataUrl("data/channels.json")
+ << true
+ << true
+ << (QList<UpdateChecker::ChannelListEntry>()
+ << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", findTestDataUrl("data")}
+ << UpdateChecker::ChannelListEntry{"stable", "Stable", "It's stable at least", findTestDataUrl("data")}
+ << UpdateChecker::ChannelListEntry{"42", "The Channel", "This is the channel that is going to answer all of your questions", "https://dent.me/tea"});
+ }
+ void tst_ChannelListParsing()
+ {
+
+ QFETCH(QString, channel);
+ QFETCH(QString, channelUrl);
+ QFETCH(bool, hasChannels);
+ QFETCH(bool, valid);
+ QFETCH(QList<UpdateChecker::ChannelListEntry>, result);
+
+ UpdateChecker checker(channelUrl, channel, 0);
+
+ QSignalSpy channelListLoadedSpy(&checker, SIGNAL(channelListLoaded()));
+ QVERIFY(channelListLoadedSpy.isValid());
+
+ checker.updateChanList(false);
+
+ if (valid)
+ {
+ QVERIFY(channelListLoadedSpy.wait());
+ QCOMPARE(channelListLoadedSpy.size(), 1);
+ }
+ else
+ {
+ channelListLoadedSpy.wait();
+ QCOMPARE(channelListLoadedSpy.size(), 0);
+ }
+
+ QCOMPARE(checker.hasChannels(), hasChannels);
+ QCOMPARE(checker.getChannelList(), result);
+ }
+
+ void tst_UpdateChecking()
+ {
+ QString channel = "develop";
+ QString channelUrl = findTestDataUrl("data/channels.json");
+ int currentBuild = 2;
+
+ UpdateChecker checker(channelUrl, channel, currentBuild);
+
+ QSignalSpy updateAvailableSpy(&checker, SIGNAL(updateAvailable(GoUpdate::Status)));
+ QVERIFY(updateAvailableSpy.isValid());
+ QSignalSpy channelListLoadedSpy(&checker, SIGNAL(channelListLoaded()));
+ QVERIFY(channelListLoadedSpy.isValid());
+
+ checker.updateChanList(false);
+ QVERIFY(channelListLoadedSpy.wait());
+
+ qDebug() << "CWD:" << QDir::current().absolutePath();
+ checker.m_channels[0].url = findTestDataUrl("data/");
+ checker.checkForUpdate(channel, false);
+
+ QVERIFY(updateAvailableSpy.wait());
+
+ auto status = updateAvailableSpy.first().first().value<GoUpdate::Status>();
+ QCOMPARE(checker.m_channels[0].url, status.newRepoUrl);
+ QCOMPARE(3, status.newVersionId);
+ QCOMPARE(currentBuild, status.currentVersionId);
+ }
};
QTEST_GUILESS_MAIN(UpdateCheckerTest)
diff --git a/api/logic/updater/testdata/1.json b/api/logic/updater/testdata/1.json
index 3dd189e5..7af7e52d 100644
--- a/api/logic/updater/testdata/1.json
+++ b/api/logic/updater/testdata/1.json
@@ -1,43 +1,43 @@
{
- "ApiVersion": 0,
- "Id": 1,
- "Name": "1.0.1",
- "Files": [
- {
- "Path": "fileOne",
- "Sources": [
- {
- "SourceType": "http",
- "Url": "@TEST_DATA_URL@/fileOneA"
- }
- ],
- "Executable": true,
- "Perms": 493,
- "MD5": "9eb84090956c484e32cb6c08455a667b"
- },
- {
- "Path": "fileTwo",
- "Sources": [
- {
- "SourceType": "http",
- "Url": "@TEST_DATA_URL@/fileTwo"
- }
- ],
- "Executable": false,
- "Perms": 644,
- "MD5": "38f94f54fa3eb72b0ea836538c10b043"
- },
- {
- "Path": "fileThree",
- "Sources": [
- {
- "SourceType": "http",
- "Url": "@TEST_DATA_URL@/fileThree"
- }
- ],
- "Executable": false,
- "Perms": "750",
- "MD5": "f12df554b21e320be6471d7154130e70"
- }
- ]
+ "ApiVersion": 0,
+ "Id": 1,
+ "Name": "1.0.1",
+ "Files": [
+ {
+ "Path": "fileOne",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@TEST_DATA_URL@/fileOneA"
+ }
+ ],
+ "Executable": true,
+ "Perms": 493,
+ "MD5": "9eb84090956c484e32cb6c08455a667b"
+ },
+ {
+ "Path": "fileTwo",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@TEST_DATA_URL@/fileTwo"
+ }
+ ],
+ "Executable": false,
+ "Perms": 644,
+ "MD5": "38f94f54fa3eb72b0ea836538c10b043"
+ },
+ {
+ "Path": "fileThree",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@TEST_DATA_URL@/fileThree"
+ }
+ ],
+ "Executable": false,
+ "Perms": "750",
+ "MD5": "f12df554b21e320be6471d7154130e70"
+ }
+ ]
}
diff --git a/api/logic/updater/testdata/2.json b/api/logic/updater/testdata/2.json
index a7ba7029..96d430d5 100644
--- a/api/logic/updater/testdata/2.json
+++ b/api/logic/updater/testdata/2.json
@@ -1,31 +1,31 @@
{
- "ApiVersion": 0,
- "Id": 1,
- "Name": "1.0.1",
- "Files": [
- {
- "Path": "fileOne",
- "Sources": [
- {
- "SourceType": "http",
- "Url": "@TEST_DATA_URL@/fileOneB"
- }
- ],
- "Executable": true,
- "Perms": 493,
- "MD5": "42915a71277c9016668cce7b82c6b577"
- },
- {
- "Path": "fileTwo",
- "Sources": [
- {
- "SourceType": "http",
- "Url": "@TEST_DATA_URL@/fileTwo"
- }
- ],
- "Executable": false,
- "Perms": 644,
- "MD5": "38f94f54fa3eb72b0ea836538c10b043"
- }
- ]
+ "ApiVersion": 0,
+ "Id": 1,
+ "Name": "1.0.1",
+ "Files": [
+ {
+ "Path": "fileOne",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@TEST_DATA_URL@/fileOneB"
+ }
+ ],
+ "Executable": true,
+ "Perms": 493,
+ "MD5": "42915a71277c9016668cce7b82c6b577"
+ },
+ {
+ "Path": "fileTwo",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@TEST_DATA_URL@/fileTwo"
+ }
+ ],
+ "Executable": false,
+ "Perms": 644,
+ "MD5": "38f94f54fa3eb72b0ea836538c10b043"
+ }
+ ]
}
diff --git a/api/logic/updater/testdata/channels.json b/api/logic/updater/testdata/channels.json
index b46c64c8..5c6e42cb 100644
--- a/api/logic/updater/testdata/channels.json
+++ b/api/logic/updater/testdata/channels.json
@@ -1,23 +1,23 @@
{
- "format_version": 0,
- "channels": [
- {
- "id": "develop",
- "name": "Develop",
- "description": "The channel called \"develop\"",
- "url": "@TEST_DATA_URL@"
- },
- {
- "id": "stable",
- "name": "Stable",
- "description": "It's stable at least",
- "url": "@TEST_DATA_URL@"
- },
- {
- "id": "42",
- "name": "The Channel",
- "description": "This is the channel that is going to answer all of your questions",
- "url": "https://dent.me/tea"
- }
- ]
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "develop",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+ "url": "@TEST_DATA_URL@"
+ },
+ {
+ "id": "stable",
+ "name": "Stable",
+ "description": "It's stable at least",
+ "url": "@TEST_DATA_URL@"
+ },
+ {
+ "id": "42",
+ "name": "The Channel",
+ "description": "This is the channel that is going to answer all of your questions",
+ "url": "https://dent.me/tea"
+ }
+ ]
}
diff --git a/api/logic/updater/testdata/errorChannels.json b/api/logic/updater/testdata/errorChannels.json
index 333cd445..a2cb2165 100644
--- a/api/logic/updater/testdata/errorChannels.json
+++ b/api/logic/updater/testdata/errorChannels.json
@@ -1,23 +1,23 @@
{
- "format_version": 0,
- "channels": [
- {
- "id": "",
- "name": "Develop",
- "description": "The channel called \"develop\"",
- "url": "http://example.org/stuff"
- },
- {
- "id": "stable",
- "name": "",
- "description": "It's stable at least",
- "url": "ftp://username@host/path/to/stuff"
- },
- {
- "id": "42",
- "name": "The Channel",
- "description": "This is the channel that is going to answer all of your questions",
- "url": ""
- }
- ]
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+ "url": "http://example.org/stuff"
+ },
+ {
+ "id": "stable",
+ "name": "",
+ "description": "It's stable at least",
+ "url": "ftp://username@host/path/to/stuff"
+ },
+ {
+ "id": "42",
+ "name": "The Channel",
+ "description": "This is the channel that is going to answer all of your questions",
+ "url": ""
+ }
+ ]
}
diff --git a/api/logic/updater/testdata/garbageChannels.json b/api/logic/updater/testdata/garbageChannels.json
index 1450fb9c..34437451 100644
--- a/api/logic/updater/testdata/garbageChannels.json
+++ b/api/logic/updater/testdata/garbageChannels.json
@@ -1,22 +1,22 @@
{
- "format_version": 0,
- "channels": [
- {
- "id": "develop",
- "name": "Develop",
- "description": "The channel called \"develop\"",
-aa "url": "http://example.org/stuff"
- },
-a "id": "stable",
- "name": "Stable",
- "description": "It's stable at least",
- "url": "ftp://username@host/path/to/stuff"
- },
- {
- "id": "42"f
- "name": "The Channel",
- "description": "This is the channel that is going to answer all of your questions",
- "url": "https://dent.me/tea"
- }
- ]
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "develop",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+aa "url": "http://example.org/stuff"
+ },
+a "id": "stable",
+ "name": "Stable",
+ "description": "It's stable at least",
+ "url": "ftp://username@host/path/to/stuff"
+ },
+ {
+ "id": "42"f
+ "name": "The Channel",
+ "description": "This is the channel that is going to answer all of your questions",
+ "url": "https://dent.me/tea"
+ }
+ ]
}
diff --git a/api/logic/updater/testdata/index.json b/api/logic/updater/testdata/index.json
index 20ceb9f4..867bdcfb 100644
--- a/api/logic/updater/testdata/index.json
+++ b/api/logic/updater/testdata/index.json
@@ -1,9 +1,9 @@
{
- "ApiVersion": 0,
- "Versions": [
- { "Id": 0, "Name": "1.0.0" },
- { "Id": 1, "Name": "1.0.1" },
- { "Id": 2, "Name": "1.0.2" },
- { "Id": 3, "Name": "1.0.3" }
- ]
+ "ApiVersion": 0,
+ "Versions": [
+ { "Id": 0, "Name": "1.0.0" },
+ { "Id": 1, "Name": "1.0.1" },
+ { "Id": 2, "Name": "1.0.2" },
+ { "Id": 3, "Name": "1.0.3" }
+ ]
}
diff --git a/api/logic/updater/testdata/noChannels.json b/api/logic/updater/testdata/noChannels.json
index bbb2cb70..76988982 100644
--- a/api/logic/updater/testdata/noChannels.json
+++ b/api/logic/updater/testdata/noChannels.json
@@ -1,5 +1,5 @@
{
- "format_version": 0,
- "channels": [
- ]
+ "format_version": 0,
+ "channels": [
+ ]
}
diff --git a/api/logic/updater/testdata/oneChannel.json b/api/logic/updater/testdata/oneChannel.json
index 84727ac7..cc8ed255 100644
--- a/api/logic/updater/testdata/oneChannel.json
+++ b/api/logic/updater/testdata/oneChannel.json
@@ -1,11 +1,11 @@
{
- "format_version": 0,
- "channels": [
- {
- "id": "develop",
- "name": "Develop",
- "description": "The channel called \"develop\"",
- "url": "http://example.org/stuff"
- }
- ]
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "develop",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+ "url": "http://example.org/stuff"
+ }
+ ]
}