aboutsummaryrefslogtreecommitdiff
path: root/api/logic
diff options
context:
space:
mode:
Diffstat (limited to 'api/logic')
-rw-r--r--api/logic/BaseInstaller.cpp61
-rw-r--r--api/logic/BaseInstaller.h46
-rw-r--r--api/logic/BaseInstance.cpp275
-rw-r--r--api/logic/BaseInstance.h272
-rw-r--r--api/logic/BaseVersion.h59
-rw-r--r--api/logic/BaseVersionList.cpp99
-rw-r--r--api/logic/BaseVersionList.h122
-rw-r--r--api/logic/CMakeLists.txt564
-rw-r--r--api/logic/Commandline.cpp483
-rw-r--r--api/logic/Commandline.h252
-rw-r--r--api/logic/DefaultVariable.h35
-rw-r--r--api/logic/Env.cpp211
-rw-r--r--api/logic/Env.h65
-rw-r--r--api/logic/Exception.h34
-rw-r--r--api/logic/ExponentialSeries.h43
-rw-r--r--api/logic/FileSystem.cpp457
-rw-r--r--api/logic/FileSystem.h128
-rw-r--r--api/logic/FileSystem_test.cpp164
-rw-r--r--api/logic/Filter.cpp31
-rw-r--r--api/logic/Filter.h44
-rw-r--r--api/logic/GZip.cpp115
-rw-r--r--api/logic/GZip.h12
-rw-r--r--api/logic/GZip_test.cpp57
-rw-r--r--api/logic/InstanceCopyTask.cpp60
-rw-r--r--api/logic/InstanceCopyTask.h32
-rw-r--r--api/logic/InstanceCreationTask.cpp31
-rw-r--r--api/logic/InstanceCreationTask.h23
-rw-r--r--api/logic/InstanceImportTask.cpp456
-rw-r--r--api/logic/InstanceImportTask.h73
-rw-r--r--api/logic/InstanceList.cpp867
-rw-r--r--api/logic/InstanceList.h175
-rw-r--r--api/logic/InstanceTask.cpp9
-rw-r--r--api/logic/InstanceTask.h53
-rw-r--r--api/logic/Json.cpp272
-rw-r--r--api/logic/Json.h249
-rw-r--r--api/logic/LoggedProcess.cpp176
-rw-r--r--api/logic/LoggedProcess.h80
-rw-r--r--api/logic/MMCStrings.cpp76
-rw-r--r--api/logic/MMCStrings.h10
-rw-r--r--api/logic/MMCZip.cpp312
-rw-r--r--api/logic/MMCZip.h94
-rw-r--r--api/logic/MessageLevel.cpp36
-rw-r--r--api/logic/MessageLevel.h28
-rw-r--r--api/logic/NullInstance.h76
-rw-r--r--api/logic/ProblemProvider.h49
-rw-r--r--api/logic/QObjectPtr.h83
-rw-r--r--api/logic/RWStorage.h66
-rw-r--r--api/logic/RecursiveFileSystemWatcher.cpp111
-rw-r--r--api/logic/RecursiveFileSystemWatcher.h63
-rw-r--r--api/logic/SeparatorPrefixTree.h298
-rw-r--r--api/logic/Usable.h58
-rw-r--r--api/logic/Version.cpp85
-rw-r--r--api/logic/Version.h107
-rw-r--r--api/logic/Version_test.cpp85
-rw-r--r--api/logic/WatchLock.h20
-rw-r--r--api/logic/icons/IIconList.cpp7
-rw-r--r--api/logic/icons/IIconList.h26
-rw-r--r--api/logic/icons/IconUtils.cpp62
-rw-r--r--api/logic/icons/IconUtils.h14
-rw-r--r--api/logic/java/JavaChecker.cpp166
-rw-r--r--api/logic/java/JavaChecker.h63
-rw-r--r--api/logic/java/JavaCheckerJob.cpp44
-rw-r--r--api/logic/java/JavaCheckerJob.h61
-rw-r--r--api/logic/java/JavaInstall.cpp28
-rw-r--r--api/logic/java/JavaInstall.h38
-rw-r--r--api/logic/java/JavaInstallList.cpp208
-rw-r--r--api/logic/java/JavaInstallList.h83
-rw-r--r--api/logic/java/JavaUtils.cpp399
-rw-r--r--api/logic/java/JavaUtils.h44
-rw-r--r--api/logic/java/JavaVersion.cpp121
-rw-r--r--api/logic/java/JavaVersion.h50
-rw-r--r--api/logic/java/JavaVersion_test.cpp116
-rw-r--r--api/logic/java/launch/CheckJava.cpp139
-rw-r--r--api/logic/java/launch/CheckJava.h45
-rw-r--r--api/logic/launch/LaunchStep.cpp27
-rw-r--r--api/logic/launch/LaunchStep.h50
-rw-r--r--api/logic/launch/LaunchTask.cpp280
-rw-r--r--api/logic/launch/LaunchTask.h125
-rw-r--r--api/logic/launch/LogModel.cpp167
-rw-r--r--api/logic/launch/LogModel.h60
-rw-r--r--api/logic/launch/steps/LookupServerAddress.cpp95
-rw-r--r--api/logic/launch/steps/LookupServerAddress.h49
-rw-r--r--api/logic/launch/steps/PostLaunchCommand.cpp84
-rw-r--r--api/logic/launch/steps/PostLaunchCommand.h41
-rw-r--r--api/logic/launch/steps/PreLaunchCommand.cpp85
-rw-r--r--api/logic/launch/steps/PreLaunchCommand.h41
-rw-r--r--api/logic/launch/steps/TextPrint.cpp29
-rw-r--r--api/logic/launch/steps/TextPrint.h43
-rw-r--r--api/logic/launch/steps/Update.cpp80
-rw-r--r--api/logic/launch/steps/Update.h45
-rw-r--r--api/logic/meta/BaseEntity.cpp168
-rw-r--r--api/logic/meta/BaseEntity.h68
-rw-r--r--api/logic/meta/Index.cpp148
-rw-r--r--api/logic/meta/Index.h71
-rw-r--r--api/logic/meta/Index_test.cpp44
-rw-r--r--api/logic/meta/JsonFormat.cpp218
-rw-r--r--api/logic/meta/JsonFormat.h83
-rw-r--r--api/logic/meta/Version.cpp140
-rw-r--r--api/logic/meta/Version.h118
-rw-r--r--api/logic/meta/VersionList.cpp245
-rw-r--r--api/logic/meta/VersionList.h101
-rw-r--r--api/logic/minecraft/AssetsUtils.cpp333
-rw-r--r--api/logic/minecraft/AssetsUtils.h53
-rw-r--r--api/logic/minecraft/Component.cpp439
-rw-r--r--api/logic/minecraft/Component.h111
-rw-r--r--api/logic/minecraft/ComponentUpdateTask.cpp704
-rw-r--r--api/logic/minecraft/ComponentUpdateTask.h37
-rw-r--r--api/logic/minecraft/ComponentUpdateTask_p.h32
-rw-r--r--api/logic/minecraft/GradleSpecifier.h151
-rw-r--r--api/logic/minecraft/GradleSpecifier_test.cpp78
-rw-r--r--api/logic/minecraft/LaunchProfile.cpp319
-rw-r--r--api/logic/minecraft/LaunchProfile.h104
-rw-r--r--api/logic/minecraft/Library.cpp309
-rw-r--r--api/logic/minecraft/Library.h219
-rw-r--r--api/logic/minecraft/Library_test.cpp272
-rw-r--r--api/logic/minecraft/MinecraftInstance.cpp1054
-rw-r--r--api/logic/minecraft/MinecraftInstance.h133
-rw-r--r--api/logic/minecraft/MinecraftLoadAndCheck.cpp45
-rw-r--r--api/logic/minecraft/MinecraftLoadAndCheck.h48
-rw-r--r--api/logic/minecraft/MinecraftUpdate.cpp182
-rw-r--r--api/logic/minecraft/MinecraftUpdate.h57
-rw-r--r--api/logic/minecraft/MojangDownloadInfo.h82
-rw-r--r--api/logic/minecraft/MojangVersionFormat.cpp383
-rw-r--r--api/logic/minecraft/MojangVersionFormat.h26
-rw-r--r--api/logic/minecraft/MojangVersionFormat_test.cpp55
-rw-r--r--api/logic/minecraft/OneSixVersionFormat.cpp391
-rw-r--r--api/logic/minecraft/OneSixVersionFormat.h30
-rw-r--r--api/logic/minecraft/OpSys.cpp42
-rw-r--r--api/logic/minecraft/OpSys.h37
-rw-r--r--api/logic/minecraft/PackProfile.cpp1225
-rw-r--r--api/logic/minecraft/PackProfile.h152
-rw-r--r--api/logic/minecraft/PackProfile_p.h42
-rw-r--r--api/logic/minecraft/ParseUtils.cpp34
-rw-r--r--api/logic/minecraft/ParseUtils.h11
-rw-r--r--api/logic/minecraft/ParseUtils_test.cpp45
-rw-r--r--api/logic/minecraft/ProfileUtils.cpp178
-rw-r--r--api/logic/minecraft/ProfileUtils.h28
-rw-r--r--api/logic/minecraft/Rule.cpp93
-rw-r--r--api/logic/minecraft/Rule.h101
-rw-r--r--api/logic/minecraft/VersionFile.cpp60
-rw-r--r--api/logic/minecraft/VersionFile.h114
-rw-r--r--api/logic/minecraft/VersionFilterData.cpp71
-rw-r--r--api/logic/minecraft/VersionFilterData.h31
-rw-r--r--api/logic/minecraft/World.cpp520
-rw-r--r--api/logic/minecraft/World.h113
-rw-r--r--api/logic/minecraft/WorldList.cpp387
-rw-r--r--api/logic/minecraft/WorldList.h131
-rw-r--r--api/logic/minecraft/auth-msa/BuildConfig.cpp.in9
-rw-r--r--api/logic/minecraft/auth-msa/BuildConfig.h11
-rw-r--r--api/logic/minecraft/auth-msa/CMakeLists.txt28
-rw-r--r--api/logic/minecraft/auth-msa/context.cpp938
-rw-r--r--api/logic/minecraft/auth-msa/context.h128
-rw-r--r--api/logic/minecraft/auth-msa/main.cpp100
-rw-r--r--api/logic/minecraft/auth-msa/mainwindow.cpp97
-rw-r--r--api/logic/minecraft/auth-msa/mainwindow.h34
-rw-r--r--api/logic/minecraft/auth-msa/mainwindow.ui72
-rw-r--r--api/logic/minecraft/auth/AuthSession.cpp30
-rw-r--r--api/logic/minecraft/auth/AuthSession.h54
-rw-r--r--api/logic/minecraft/auth/MojangAccount.cpp315
-rw-r--r--api/logic/minecraft/auth/MojangAccount.h182
-rw-r--r--api/logic/minecraft/auth/MojangAccountList.cpp468
-rw-r--r--api/logic/minecraft/auth/MojangAccountList.h201
-rw-r--r--api/logic/minecraft/auth/YggdrasilTask.cpp255
-rw-r--r--api/logic/minecraft/auth/YggdrasilTask.h151
-rw-r--r--api/logic/minecraft/auth/flows/AuthenticateTask.cpp202
-rw-r--r--api/logic/minecraft/auth/flows/AuthenticateTask.h46
-rw-r--r--api/logic/minecraft/auth/flows/RefreshTask.cpp144
-rw-r--r--api/logic/minecraft/auth/flows/RefreshTask.h44
-rw-r--r--api/logic/minecraft/auth/flows/ValidateTask.cpp61
-rw-r--r--api/logic/minecraft/auth/flows/ValidateTask.h47
-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.cpp24
-rw-r--r--api/logic/minecraft/launch/ClaimAccount.h37
-rw-r--r--api/logic/minecraft/launch/CreateGameFolders.cpp28
-rw-r--r--api/logic/minecraft/launch/CreateGameFolders.h37
-rw-r--r--api/logic/minecraft/launch/DirectJavaLaunch.cpp148
-rw-r--r--api/logic/minecraft/launch/DirectJavaLaunch.h58
-rw-r--r--api/logic/minecraft/launch/ExtractNatives.cpp111
-rw-r--r--api/logic/minecraft/launch/ExtractNatives.h38
-rw-r--r--api/logic/minecraft/launch/LauncherPartLaunch.cpp218
-rw-r--r--api/logic/minecraft/launch/LauncherPartLaunch.h60
-rw-r--r--api/logic/minecraft/launch/MinecraftServerTarget.cpp66
-rw-r--r--api/logic/minecraft/launch/MinecraftServerTarget.h30
-rw-r--r--api/logic/minecraft/launch/ModMinecraftJar.cpp82
-rw-r--r--api/logic/minecraft/launch/ModMinecraftJar.h36
-rw-r--r--api/logic/minecraft/launch/PrintInstanceInfo.cpp106
-rw-r--r--api/logic/minecraft/launch/PrintInstanceInfo.h41
-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.cpp59
-rw-r--r--api/logic/minecraft/launch/ScanModFolders.h42
-rw-r--r--api/logic/minecraft/launch/VerifyJavaInstall.cpp34
-rw-r--r--api/logic/minecraft/launch/VerifyJavaInstall.h17
-rw-r--r--api/logic/minecraft/legacy/LegacyInstance.cpp256
-rw-r--r--api/logic/minecraft/legacy/LegacyInstance.h142
-rw-r--r--api/logic/minecraft/legacy/LegacyModList.cpp136
-rw-r--r--api/logic/minecraft/legacy/LegacyModList.h49
-rw-r--r--api/logic/minecraft/legacy/LegacyUpgradeTask.cpp138
-rw-r--r--api/logic/minecraft/legacy/LegacyUpgradeTask.h30
-rw-r--r--api/logic/minecraft/mod/LocalModParseTask.cpp467
-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.h149
-rw-r--r--api/logic/minecraft/mod/ModFolderModel_test.cpp53
-rw-r--r--api/logic/minecraft/mod/ResourcePackFolderModel.cpp23
-rw-r--r--api/logic/minecraft/mod/ResourcePackFolderModel.h13
-rw-r--r--api/logic/minecraft/mod/TexturePackFolderModel.cpp23
-rw-r--r--api/logic/minecraft/mod/TexturePackFolderModel.h13
-rw-r--r--api/logic/minecraft/services/SkinDelete.cpp42
-rw-r--r--api/logic/minecraft/services/SkinDelete.h30
-rw-r--r--api/logic/minecraft/services/SkinUpload.cpp66
-rw-r--r--api/logic/minecraft/services/SkinUpload.h39
-rw-r--r--api/logic/minecraft/testdata/1.9-simple.json198
-rw-r--r--api/logic/minecraft/testdata/1.9.json529
-rw-r--r--api/logic/minecraft/testdata/codecwav-20101023.jar1
-rw-r--r--api/logic/minecraft/testdata/lib-native-arch.json46
-rw-r--r--api/logic/minecraft/testdata/lib-native.json52
-rw-r--r--api/logic/minecraft/testdata/lib-simple.json11
-rw-r--r--api/logic/minecraft/testdata/testname-testversion-linux-32.jar1
-rw-r--r--api/logic/minecraft/update/AssetUpdateTask.cpp107
-rw-r--r--api/logic/minecraft/update/AssetUpdateTask.h28
-rw-r--r--api/logic/minecraft/update/FMLLibrariesTask.cpp131
-rw-r--r--api/logic/minecraft/update/FMLLibrariesTask.h31
-rw-r--r--api/logic/minecraft/update/FoldersTask.cpp21
-rw-r--r--api/logic/minecraft/update/FoldersTask.h17
-rw-r--r--api/logic/minecraft/update/LibrariesTask.cpp90
-rw-r--r--api/logic/minecraft/update/LibrariesTask.h26
-rw-r--r--api/logic/modplatform/atlauncher/ATLPackIndex.cpp33
-rw-r--r--api/logic/modplatform/atlauncher/ATLPackIndex.h36
-rw-r--r--api/logic/modplatform/atlauncher/ATLPackInstallTask.cpp764
-rw-r--r--api/logic/modplatform/atlauncher/ATLPackInstallTask.h102
-rw-r--r--api/logic/modplatform/atlauncher/ATLPackManifest.cpp218
-rw-r--r--api/logic/modplatform/atlauncher/ATLPackManifest.h126
-rw-r--r--api/logic/modplatform/flame/FileResolvingTask.cpp63
-rw-r--r--api/logic/modplatform/flame/FileResolvingTask.h34
-rw-r--r--api/logic/modplatform/flame/FlamePackIndex.cpp92
-rw-r--r--api/logic/modplatform/flame/FlamePackIndex.h43
-rw-r--r--api/logic/modplatform/flame/PackManifest.cpp126
-rw-r--r--api/logic/modplatform/flame/PackManifest.h62
-rw-r--r--api/logic/modplatform/legacy_ftb/PackFetchTask.cpp172
-rw-r--r--api/logic/modplatform/legacy_ftb/PackFetchTask.h44
-rw-r--r--api/logic/modplatform/legacy_ftb/PackHelpers.h45
-rw-r--r--api/logic/modplatform/legacy_ftb/PackInstallTask.cpp214
-rw-r--r--api/logic/modplatform/legacy_ftb/PackInstallTask.h55
-rw-r--r--api/logic/modplatform/legacy_ftb/PrivatePackManager.cpp41
-rw-r--r--api/logic/modplatform/legacy_ftb/PrivatePackManager.h44
-rw-r--r--api/logic/modplatform/modpacksch/FTBPackInstallTask.cpp209
-rw-r--r--api/logic/modplatform/modpacksch/FTBPackInstallTask.h47
-rw-r--r--api/logic/modplatform/modpacksch/FTBPackManifest.cpp156
-rw-r--r--api/logic/modplatform/modpacksch/FTBPackManifest.h127
-rw-r--r--api/logic/modplatform/technic/SingleZipPackInstallTask.cpp141
-rw-r--r--api/logic/modplatform/technic/SingleZipPackInstallTask.h65
-rw-r--r--api/logic/modplatform/technic/SolderPackInstallTask.cpp207
-rw-r--r--api/logic/modplatform/technic/SolderPackInstallTask.h60
-rw-r--r--api/logic/modplatform/technic/TechnicPackProcessor.cpp208
-rw-r--r--api/logic/modplatform/technic/TechnicPackProcessor.h35
-rw-r--r--api/logic/mojang/PackageManifest.cpp427
-rw-r--r--api/logic/mojang/PackageManifest.h173
-rw-r--r--api/logic/mojang/PackageManifest_test.cpp344
-rw-r--r--api/logic/mojang/testdata/1.8.0_202-x64.json1
-rwxr-xr-xapi/logic/mojang/testdata/inspect/a/b.txt0
l---------api/logic/mojang/testdata/inspect/a/b/b.txt1
-rw-r--r--api/logic/mojang/testdata/inspect_win/a/b.txt0
-rw-r--r--api/logic/mojang/testdata/inspect_win/a/b/b.txt0
-rw-r--r--api/logic/net/ByteArraySink.h62
-rw-r--r--api/logic/net/ChecksumValidator.h55
-rw-r--r--api/logic/net/Download.cpp309
-rw-r--r--api/logic/net/Download.h76
-rw-r--r--api/logic/net/FileSink.cpp115
-rw-r--r--api/logic/net/FileSink.h28
-rw-r--r--api/logic/net/HttpMetaCache.cpp273
-rw-r--r--api/logic/net/HttpMetaCache.h125
-rw-r--r--api/logic/net/MetaCacheSink.cpp65
-rw-r--r--api/logic/net/MetaCacheSink.h22
-rw-r--r--api/logic/net/Mode.h10
-rw-r--r--api/logic/net/NetAction.h115
-rw-r--r--api/logic/net/NetJob.cpp218
-rw-r--r--api/logic/net/NetJob.h91
-rw-r--r--api/logic/net/PasteUpload.cpp104
-rw-r--r--api/logic/net/PasteUpload.h49
-rw-r--r--api/logic/net/Sink.h71
-rw-r--r--api/logic/net/Validator.h20
-rw-r--r--api/logic/news/NewsChecker.cpp131
-rw-r--r--api/logic/news/NewsChecker.h105
-rw-r--r--api/logic/news/NewsEntry.cpp77
-rw-r--r--api/logic/news/NewsEntry.h65
-rw-r--r--api/logic/notifications/NotificationChecker.cpp129
-rw-r--r--api/logic/notifications/NotificationChecker.h63
-rw-r--r--api/logic/pathmatcher/FSTreeMatcher.h21
-rw-r--r--api/logic/pathmatcher/IPathMatcher.h12
-rw-r--r--api/logic/pathmatcher/MultiMatcher.h31
-rw-r--r--api/logic/pathmatcher/RegexpMatcher.h42
-rw-r--r--api/logic/screenshots/ImgurAlbumCreation.cpp88
-rw-r--r--api/logic/screenshots/ImgurAlbumCreation.h44
-rw-r--r--api/logic/screenshots/ImgurUpload.cpp114
-rw-r--r--api/logic/screenshots/ImgurUpload.h33
-rw-r--r--api/logic/screenshots/Screenshot.h20
-rw-r--r--api/logic/settings/INIFile.cpp163
-rw-r--r--api/logic/settings/INIFile.h38
-rw-r--r--api/logic/settings/INIFile_test.cpp63
-rw-r--r--api/logic/settings/INISettingsObject.cpp107
-rw-r--r--api/logic/settings/INISettingsObject.h66
-rw-r--r--api/logic/settings/OverrideSetting.cpp54
-rw-r--r--api/logic/settings/OverrideSetting.h46
-rw-r--r--api/logic/settings/PassthroughSetting.cpp69
-rw-r--r--api/logic/settings/PassthroughSetting.h45
-rw-r--r--api/logic/settings/Setting.cpp53
-rw-r--r--api/logic/settings/Setting.h119
-rw-r--r--api/logic/settings/SettingsObject.cpp142
-rw-r--r--api/logic/settings/SettingsObject.h214
-rw-r--r--api/logic/status/StatusChecker.cpp148
-rw-r--r--api/logic/status/StatusChecker.h60
-rw-r--r--api/logic/tasks/SequentialTask.cpp55
-rw-r--r--api/logic/tasks/SequentialTask.h32
-rw-r--r--api/logic/tasks/Task.cpp168
-rw-r--r--api/logic/tasks/Task.h108
-rwxr-xr-xapi/logic/testdata/FileSystem-test_createShortcut-unix6
-rw-r--r--api/logic/testdata/test_folder/assets/minecraft/textures/blah.txt1
-rw-r--r--api/logic/testdata/test_folder/pack.mcmeta6
-rw-r--r--api/logic/testdata/test_folder/pack.nfo1
-rw-r--r--api/logic/tools/BaseExternalTool.cpp41
-rw-r--r--api/logic/tools/BaseExternalTool.h60
-rw-r--r--api/logic/tools/BaseProfiler.cpp36
-rw-r--r--api/logic/tools/BaseProfiler.h39
-rw-r--r--api/logic/tools/JProfiler.cpp116
-rw-r--r--api/logic/tools/JProfiler.h15
-rw-r--r--api/logic/tools/JVisualVM.cpp104
-rw-r--r--api/logic/tools/JVisualVM.h15
-rw-r--r--api/logic/tools/MCEditTool.cpp77
-rw-r--r--api/logic/tools/MCEditTool.h17
-rw-r--r--api/logic/translations/POTranslator.cpp373
-rw-r--r--api/logic/translations/POTranslator.h16
-rw-r--r--api/logic/translations/TranslationsModel.cpp653
-rw-r--r--api/logic/translations/TranslationsModel.h65
-rw-r--r--api/logic/updater/DownloadTask.cpp173
-rw-r--r--api/logic/updater/DownloadTask.h98
-rw-r--r--api/logic/updater/DownloadTask_test.cpp195
-rw-r--r--api/logic/updater/GoUpdate.cpp198
-rw-r--r--api/logic/updater/GoUpdate.h127
-rw-r--r--api/logic/updater/UpdateChecker.cpp260
-rw-r--r--api/logic/updater/UpdateChecker.h121
-rw-r--r--api/logic/updater/UpdateChecker_test.cpp147
-rw-r--r--api/logic/updater/testdata/1.json43
-rw-r--r--api/logic/updater/testdata/2.json31
-rw-r--r--api/logic/updater/testdata/channels.json23
-rw-r--r--api/logic/updater/testdata/errorChannels.json23
-rw-r--r--api/logic/updater/testdata/fileOneA1
-rw-r--r--api/logic/updater/testdata/fileOneB3
-rw-r--r--api/logic/updater/testdata/fileThree1
-rw-r--r--api/logic/updater/testdata/fileTwo1
-rw-r--r--api/logic/updater/testdata/garbageChannels.json22
-rw-r--r--api/logic/updater/testdata/index.json9
-rw-r--r--api/logic/updater/testdata/noChannels.json5
-rw-r--r--api/logic/updater/testdata/oneChannel.json11
-rw-r--r--api/logic/updater/testdata/tst_DownloadTask-test_writeInstallScript.xml17
361 files changed, 0 insertions, 42538 deletions
diff --git a/api/logic/BaseInstaller.cpp b/api/logic/BaseInstaller.cpp
deleted file mode 100644
index d61c3fe9..00000000
--- a/api/logic/BaseInstaller.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QFile>
-
-#include "BaseInstaller.h"
-#include "minecraft/MinecraftInstance.h"
-
-BaseInstaller::BaseInstaller()
-{
-
-}
-
-bool BaseInstaller::isApplied(MinecraftInstance *on)
-{
- 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;
-}
-
-bool BaseInstaller::remove(MinecraftInstance *from)
-{
- return QFile::remove(filename(from->instanceRoot()));
-}
-
-QString BaseInstaller::filename(const QString &root) const
-{
- return patchesDir(root).absoluteFilePath(id() + ".json");
-}
-QDir BaseInstaller::patchesDir(const QString &root) const
-{
- return QDir(root + "/patches/");
-}
diff --git a/api/logic/BaseInstaller.h b/api/logic/BaseInstaller.h
deleted file mode 100644
index 3e40b355..00000000
--- a/api/logic/BaseInstaller.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-
-#include "multimc_logic_export.h"
-
-class MinecraftInstance;
-class QDir;
-class QString;
-class QObject;
-class Task;
-class BaseVersion;
-typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
-
-class MULTIMC_LOGIC_EXPORT BaseInstaller
-{
-public:
- BaseInstaller();
- virtual ~BaseInstaller(){};
- bool isApplied(MinecraftInstance *on);
-
- virtual bool add(MinecraftInstance *to);
- virtual bool remove(MinecraftInstance *from);
-
- 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;
-};
diff --git a/api/logic/BaseInstance.cpp b/api/logic/BaseInstance.cpp
deleted file mode 100644
index 46b45827..00000000
--- a/api/logic/BaseInstance.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BaseInstance.h"
-
-#include <QFileInfo>
-#include <QDir>
-#include <QDebug>
-
-#include "settings/INISettingsObject.h"
-#include "settings/Setting.h"
-#include "settings/OverrideSetting.h"
-
-#include "FileSystem.h"
-#include "Commandline.h"
-
-BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
- : 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);
- m_settings->registerSetting("lastTimePlayed", 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();
-}
-
-QString BaseInstance::getWrapperCommand()
-{
- return settings()->get("WrapperCommand").toString();
-}
-
-QString BaseInstance::getPostExitCommand()
-{
- 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;
-}
-
-bool BaseInstance::shouldStopOnConsoleOverflow() const
-{
- return settings()->get("ConsoleOverflowStop").toBool();
-}
-
-void BaseInstance::iconUpdated(QString key)
-{
- if(iconKey() == key)
- {
- emit propertiesChanged(this);
- }
-}
-
-void BaseInstance::invalidate()
-{
- 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);
- }
-}
-
-BaseInstance::Status BaseInstance::currentStatus() const
-{
- return m_status;
-}
-
-QString BaseInstance::id() const
-{
- return QFileInfo(instanceRoot()).fileName();
-}
-
-bool BaseInstance::isRunning() const
-{
- return m_isRunning;
-}
-
-void BaseInstance::setRunning(bool running)
-{
- if(running == m_isRunning)
- return;
-
- m_isRunning = running;
-
- if(!m_settings->get("RecordGameTime").toBool())
- {
- emit runningStatusChanged(running);
- return;
- }
-
- if(running)
- {
- m_timeStarted = QDateTime::currentDateTime();
- }
- else
- {
- QDateTime timeEnded = QDateTime::currentDateTime();
-
- qint64 current = settings()->get("totalTimePlayed").toLongLong();
- settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded));
- settings()->set("lastTimePlayed", 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;
-}
-
-int64_t BaseInstance::lastTimePlayed() const
-{
- if(m_isRunning)
- {
- QDateTime timeNow = QDateTime::currentDateTime();
- return m_timeStarted.secsTo(timeNow);
- }
- return settings()->get("lastTimePlayed").toLongLong();
-}
-
-void BaseInstance::resetTimePlayed()
-{
- settings()->reset("totalTimePlayed");
- settings()->reset("lastTimePlayed");
-}
-
-QString BaseInstance::instanceType() const
-{
- return m_settings->get("InstanceType").toString();
-}
-
-QString BaseInstance::instanceRoot() const
-{
- return m_rootDir;
-}
-
-SettingsObjectPtr BaseInstance::settings() const
-{
- return m_settings;
-}
-
-bool BaseInstance::canLaunch() const
-{
- return (!hasVersionBroken() && !isRunning());
-}
-
-bool BaseInstance::reloadSettings()
-{
- return m_settings->reload();
-}
-
-qint64 BaseInstance::lastLaunch() const
-{
- 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::setNotes(QString 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();
-}
-
-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);
-}
-
-QString BaseInstance::iconKey() const
-{
- 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);
-}
-
-QString BaseInstance::name() const
-{
- return m_settings->get("name").toString();
-}
-
-QString BaseInstance::windowTitle() const
-{
- 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());
-}
-
-shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
-{
- return m_launchProcess;
-}
diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h
deleted file mode 100644
index d250e03e..00000000
--- a/api/logic/BaseInstance.h
+++ /dev/null
@@ -1,272 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <cassert>
-
-#include <QObject>
-#include "QObjectPtr.h"
-#include <QDateTime>
-#include <QSet>
-#include <QProcess>
-
-#include "settings/SettingsObject.h"
-
-#include "settings/INIFile.h"
-#include "BaseVersionList.h"
-#include "minecraft/auth/MojangAccount.h"
-#include "MessageLevel.h"
-#include "pathmatcher/IPathMatcher.h"
-
-#include "net/Mode.h"
-
-#include "multimc_logic_export.h"
-
-#include "minecraft/launch/MinecraftServerTarget.h"
-
-class QDir;
-class Task;
-class LaunchTask;
-class BaseInstance;
-
-// pointer for lazy people
-typedef std::shared_ptr<BaseInstance> InstancePtr;
-
-/*!
- * \brief Base class for instances.
- * This class implements many functions that are common between instances and
- * provides a standard interface for all instances.
- *
- * To create a new instance type, create a new class inheriting from this class
- * and implement the pure virtual functions.
- */
-class MULTIMC_LOGIC_EXPORT BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
-{
- Q_OBJECT
-protected:
- /// no-touchy!
- BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
-
-public: /* types */
- enum class Status
- {
- Present,
- Gone // either nuked or invalidated
- };
-
-public:
- /// virtual destructor to make sure the destruction is COMPLETE
- virtual ~BaseInstance() {};
-
- virtual void saveNow() = 0;
-
- /***
- * 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;
- int64_t lastTimePlayed() 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, MinecraftServerTargetPtr serverToJoin) = 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 description of the instance into a QStringList
- */
- virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) = 0;
-
- Status currentStatus() const;
-
- int getConsoleMaxLines() const;
- bool shouldStopOnConsoleOverflow() const;
-
-protected:
- void changeStatus(Status newStatus);
-
-signals:
- /*!
- * \brief Signal emitted when properties relevant to the instance view change
- */
- void propertiesChanged(BaseInstance *inst);
-
- void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
-
- void runningStatusChanged(bool running);
-
- void statusChanged(Status from, Status to);
-
-protected slots:
- void iconUpdated(QString key);
-
-protected: /* data */
- 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;
-};
-
-Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
-//Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
-//Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)
diff --git a/api/logic/BaseVersion.h b/api/logic/BaseVersion.h
deleted file mode 100644
index b88105fb..00000000
--- a/api/logic/BaseVersion.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-#include <QString>
-#include <QMetaType>
-
-/*!
- * An abstract base class for versions.
- */
-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();
- };
-};
-
-typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
-
-Q_DECLARE_METATYPE(BaseVersionPtr)
diff --git a/api/logic/BaseVersionList.cpp b/api/logic/BaseVersionList.cpp
deleted file mode 100644
index aa9cb6cf..00000000
--- a/api/logic/BaseVersionList.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BaseVersionList.h"
-#include "BaseVersion.h"
-
-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();
-}
-
-BaseVersionPtr BaseVersionList::getRecommended() const
-{
- 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.row() > count())
- return QVariant();
-
- BaseVersionPtr version = at(index.row());
-
- switch (role)
- {
- case VersionPointerRole:
- return qVariantFromValue(version);
-
- case VersionRole:
- return version->name();
-
- case VersionIdRole:
- return version->descriptor();
-
- case TypeRole:
- return version->typeString();
-
- default:
- return QVariant();
- }
-}
-
-BaseVersionList::RoleList BaseVersionList::providesRoles() const
-{
- return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole};
-}
-
-int BaseVersionList::rowCount(const QModelIndex &parent) const
-{
- // Return count
- return count();
-}
-
-int BaseVersionList::columnCount(const QModelIndex &parent) const
-{
- 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;
-}
diff --git a/api/logic/BaseVersionList.h b/api/logic/BaseVersionList.h
deleted file mode 100644
index 29e21bdb..00000000
--- a/api/logic/BaseVersionList.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QVariant>
-#include <QAbstractListModel>
-
-#include "BaseVersion.h"
-#include "tasks/Task.h"
-#include "multimc_logic_export.h"
-#include "QObjectPtr.h"
-
-/*!
- * \brief Class that each instance type's version list derives from.
- * Version lists are the lists that keep track of the available game versions
- * for that instance. This list will not be loaded on startup. It will be loaded
- * when the list's load function is called. Before using the version list, you
- * should check to see if it has been loaded yet and if not, load the list.
- *
- * Note that this class also inherits from QAbstractListModel. Methods from that
- * class determine how this version list shows up in a list view. Said methods
- * all have a default implementation, but they can be overridden by plugins to
- * change the behavior of the list.
- */
-class MULTIMC_LOGIC_EXPORT BaseVersionList : public QAbstractListModel
-{
- Q_OBJECT
-public:
- enum ModelRoles
- {
- VersionPointerRole = Qt::UserRole,
- VersionRole,
- VersionIdRole,
- ParentVersionRole,
- RecommendedRole,
- LatestRole,
- TypeRole,
- BranchRole,
- PathRole,
- ArchitectureRole,
- SortRole
- };
- typedef QList<int> RoleList;
-
- 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;
-
- //! 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;
-
- //! 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;
-
- //! 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 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;
-
-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;
-};
diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt
deleted file mode 100644
index 6d269714..00000000
--- a/api/logic/CMakeLists.txt
+++ /dev/null
@@ -1,564 +0,0 @@
-project(MultiMC_logic)
-
-include (UnitTest)
-
-set(CORE_SOURCES
- # LOGIC - Base classes and infrastructure
- BaseInstaller.h
- BaseInstaller.cpp
- BaseVersionList.h
- BaseVersionList.cpp
- InstanceList.h
- InstanceList.cpp
- InstanceTask.h
- InstanceTask.cpp
- LoggedProcess.h
- LoggedProcess.cpp
- MessageLevel.cpp
- MessageLevel.h
- BaseVersion.h
- BaseInstance.h
- BaseInstance.cpp
- NullInstance.h
- MMCZip.h
- MMCZip.cpp
- MMCStrings.h
- MMCStrings.cpp
-
- # Basic instance manipulation tasks (derived from InstanceTask)
- InstanceCreationTask.h
- InstanceCreationTask.cpp
- InstanceCopyTask.h
- InstanceCopyTask.cpp
- InstanceImportTask.h
- InstanceImportTask.cpp
-
- # Use tracking separate from memory management
- Usable.h
-
- # Prefix tree where node names are strings between separators
- SeparatorPrefixTree.h
-
- # WARNING: globals live here
- Env.h
- Env.cpp
-
- # String filters
- Filter.h
- Filter.cpp
-
- # JSON parsing helpers
- Json.h
- Json.cpp
-
- FileSystem.h
- FileSystem.cpp
-
- Exception.h
-
- # RW lock protected map
- RWStorage.h
-
- # A variable that has an implicit default value and keeps track of changes
- DefaultVariable.h
-
- # a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
- QObjectPtr.h
-
- # Compression support
- GZip.h
- GZip.cpp
-
- # Command line parameter parsing
- Commandline.h
- Commandline.cpp
-
- # Version number string support
- Version.h
- Version.cpp
-
- # A Recursive file system watcher
- RecursiveFileSystemWatcher.h
- RecursiveFileSystemWatcher.cpp
-)
-
-add_unit_test(FileSystem
- SOURCES FileSystem_test.cpp
- LIBS MultiMC_logic
- DATA testdata
- )
-
-add_unit_test(GZip
- SOURCES GZip_test.cpp
- LIBS MultiMC_logic
- )
-
-set(PATHMATCHER_SOURCES
- # Path matchers
- pathmatcher/FSTreeMatcher.h
- pathmatcher/IPathMatcher.h
- pathmatcher/MultiMatcher.h
- pathmatcher/RegexpMatcher.h
-)
-
-set(NET_SOURCES
- # network stuffs
- net/ByteArraySink.h
- net/ChecksumValidator.h
- net/Download.cpp
- net/Download.h
- net/FileSink.cpp
- net/FileSink.h
- net/HttpMetaCache.cpp
- net/HttpMetaCache.h
- net/MetaCacheSink.cpp
- net/MetaCacheSink.h
- net/NetAction.h
- net/NetJob.cpp
- net/NetJob.h
- net/PasteUpload.cpp
- net/PasteUpload.h
- net/Sink.h
- net/Validator.h
-)
-
-# Game launch logic
-set(LAUNCH_SOURCES
- launch/steps/LookupServerAddress.cpp
- launch/steps/LookupServerAddress.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
-)
-
-add_unit_test(UpdateChecker
- SOURCES updater/UpdateChecker_test.cpp
- LIBS MultiMC_logic
- DATA updater/testdata
- )
-
-add_unit_test(DownloadTask
- SOURCES updater/DownloadTask_test.cpp
- LIBS MultiMC_logic
- DATA updater/testdata
- )
-
-# Rarely used notifications
-set(NOTIFICATIONS_SOURCES
- # Notifications - short warning messages
- notifications/NotificationChecker.h
- notifications/NotificationChecker.cpp
-)
-
-# Backend for the news bar... there's usually no news.
-set(NEWS_SOURCES
- # News System
- news/NewsChecker.h
- news/NewsChecker.cpp
- news/NewsEntry.h
- news/NewsEntry.cpp
-)
-
-# Icon interface
-set(ICONS_SOURCES
- # Icons System and related code
- icons/IIconList.h
- icons/IIconList.cpp
- icons/IconUtils.h
- icons/IconUtils.cpp
-)
-
-# Minecraft services status checker
-set(STATUS_SOURCES
- # Status system
- status/StatusChecker.h
- status/StatusChecker.cpp
-)
-
-# Support for Minecraft instances and launch
-set(MINECRAFT_SOURCES
- # Minecraft support
- minecraft/auth/AuthSession.h
- minecraft/auth/AuthSession.cpp
- minecraft/auth/MojangAccountList.h
- minecraft/auth/MojangAccountList.cpp
- minecraft/auth/MojangAccount.h
- minecraft/auth/MojangAccount.cpp
- minecraft/auth/YggdrasilTask.h
- minecraft/auth/YggdrasilTask.cpp
- minecraft/auth/flows/AuthenticateTask.h
- minecraft/auth/flows/AuthenticateTask.cpp
- minecraft/auth/flows/RefreshTask.cpp
- minecraft/auth/flows/RefreshTask.cpp
- minecraft/auth/flows/ValidateTask.h
- minecraft/auth/flows/ValidateTask.cpp
-
- minecraft/gameoptions/GameOptions.h
- minecraft/gameoptions/GameOptions.cpp
-
- minecraft/update/AssetUpdateTask.h
- minecraft/update/AssetUpdateTask.cpp
- minecraft/update/FMLLibrariesTask.cpp
- minecraft/update/FMLLibrariesTask.h
- minecraft/update/FoldersTask.cpp
- minecraft/update/FoldersTask.h
- minecraft/update/LibrariesTask.cpp
- minecraft/update/LibrariesTask.h
-
- minecraft/launch/ClaimAccount.cpp
- minecraft/launch/ClaimAccount.h
- minecraft/launch/CreateGameFolders.cpp
- minecraft/launch/CreateGameFolders.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/MinecraftServerTarget.cpp
- minecraft/launch/MinecraftServerTarget.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/launch/VerifyJavaInstall.cpp
- minecraft/launch/VerifyJavaInstall.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/PackProfile.cpp
- minecraft/PackProfile.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
- minecraft/mod/ResourcePackFolderModel.h
- minecraft/mod/ResourcePackFolderModel.cpp
- minecraft/mod/TexturePackFolderModel.h
- minecraft/mod/TexturePackFolderModel.cpp
-
- # Assets
- minecraft/AssetsUtils.h
- minecraft/AssetsUtils.cpp
-
- # Minecraft services
- minecraft/services/SkinUpload.cpp
- minecraft/services/SkinUpload.h
- minecraft/services/SkinDelete.cpp
- minecraft/services/SkinDelete.h
-
- mojang/PackageManifest.h
- mojang/PackageManifest.cpp
- )
-
-add_unit_test(GradleSpecifier
- SOURCES minecraft/GradleSpecifier_test.cpp
- LIBS MultiMC_logic
- )
-
-add_executable(PackageManifest
- mojang/PackageManifest_test.cpp
-)
-target_link_libraries(PackageManifest
- MultiMC_logic
- Qt5::Test
-)
-target_include_directories(PackageManifest
- PRIVATE ../../cmake/UnitTest/
-)
-add_test(
- NAME PackageManifest
- COMMAND PackageManifest
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-)
-
-add_unit_test(MojangVersionFormat
- SOURCES minecraft/MojangVersionFormat_test.cpp
- LIBS MultiMC_logic
- DATA minecraft/testdata
- )
-
-add_unit_test(Library
- SOURCES minecraft/Library_test.cpp
- LIBS MultiMC_logic
- )
-
-# FIXME: shares data with FileSystem test
-add_unit_test(ModFolderModel
- SOURCES minecraft/mod/ModFolderModel_test.cpp
- DATA testdata
- LIBS MultiMC_logic
- )
-
-add_unit_test(ParseUtils
- SOURCES minecraft/ParseUtils_test.cpp
- LIBS MultiMC_logic
- )
-
-# the screenshots feature
-set(SCREENSHOTS_SOURCES
- screenshots/Screenshot.h
- screenshots/ImgurUpload.h
- screenshots/ImgurUpload.cpp
- screenshots/ImgurAlbumCreation.h
- screenshots/ImgurAlbumCreation.cpp
-)
-
-set(TASKS_SOURCES
- # Tasks
- tasks/Task.h
- tasks/Task.cpp
- tasks/SequentialTask.h
- tasks/SequentialTask.cpp
-)
-
-set(SETTINGS_SOURCES
- # Settings
- settings/INIFile.cpp
- settings/INIFile.h
- settings/INISettingsObject.cpp
- settings/INISettingsObject.h
- settings/OverrideSetting.cpp
- settings/OverrideSetting.h
- settings/PassthroughSetting.cpp
- settings/PassthroughSetting.h
- settings/Setting.cpp
- settings/Setting.h
- settings/SettingsObject.cpp
- settings/SettingsObject.h
-)
-
-add_unit_test(INIFile
- SOURCES settings/INIFile_test.cpp
- LIBS MultiMC_logic
- )
-
-set(JAVA_SOURCES
- # Java related code
- java/launch/CheckJava.cpp
- java/launch/CheckJava.h
- java/JavaChecker.h
- java/JavaChecker.cpp
- java/JavaCheckerJob.h
- java/JavaCheckerJob.cpp
- java/JavaInstall.h
- java/JavaInstall.cpp
- java/JavaInstallList.h
- java/JavaInstallList.cpp
- java/JavaUtils.h
- java/JavaUtils.cpp
- java/JavaVersion.h
- java/JavaVersion.cpp
-)
-
-add_unit_test(JavaVersion
- SOURCES java/JavaVersion_test.cpp
- LIBS MultiMC_logic
- )
-
-set(TRANSLATIONS_SOURCES
- translations/TranslationsModel.h
- translations/TranslationsModel.cpp
- translations/POTranslator.h
- translations/POTranslator.cpp
-)
-
-set(TOOLS_SOURCES
- # Tools
- tools/BaseExternalTool.cpp
- tools/BaseExternalTool.h
- tools/BaseProfiler.cpp
- tools/BaseProfiler.h
- tools/JProfiler.cpp
- tools/JProfiler.h
- tools/JVisualVM.cpp
- tools/JVisualVM.h
- tools/MCEditTool.cpp
- tools/MCEditTool.h
-)
-
-set(META_SOURCES
- # Metadata sources
- meta/JsonFormat.cpp
- meta/JsonFormat.h
- meta/BaseEntity.cpp
- meta/BaseEntity.h
- meta/VersionList.cpp
- meta/VersionList.h
- meta/Version.cpp
- meta/Version.h
- meta/Index.cpp
- meta/Index.h
-)
-
-set(FTB_SOURCES
- modplatform/legacy_ftb/PackFetchTask.h
- modplatform/legacy_ftb/PackFetchTask.cpp
- modplatform/legacy_ftb/PackInstallTask.h
- modplatform/legacy_ftb/PackInstallTask.cpp
- modplatform/legacy_ftb/PrivatePackManager.h
- modplatform/legacy_ftb/PrivatePackManager.cpp
-
- modplatform/legacy_ftb/PackHelpers.h
-)
-
-set(FLAME_SOURCES
- # Flame
- modplatform/flame/FlamePackIndex.cpp
- modplatform/flame/FlamePackIndex.h
- modplatform/flame/PackManifest.h
- modplatform/flame/PackManifest.cpp
- modplatform/flame/FileResolvingTask.h
- modplatform/flame/FileResolvingTask.cpp
-)
-
-set(MODPACKSCH_SOURCES
- modplatform/modpacksch/FTBPackInstallTask.h
- modplatform/modpacksch/FTBPackInstallTask.cpp
- modplatform/modpacksch/FTBPackManifest.h
- modplatform/modpacksch/FTBPackManifest.cpp
-)
-
-set(TECHNIC_SOURCES
- modplatform/technic/SingleZipPackInstallTask.h
- modplatform/technic/SingleZipPackInstallTask.cpp
- modplatform/technic/SolderPackInstallTask.h
- modplatform/technic/SolderPackInstallTask.cpp
- modplatform/technic/TechnicPackProcessor.h
- modplatform/technic/TechnicPackProcessor.cpp
-)
-
-set(ATLAUNCHER_SOURCES
- modplatform/atlauncher/ATLPackIndex.cpp
- modplatform/atlauncher/ATLPackIndex.h
- modplatform/atlauncher/ATLPackInstallTask.cpp
- modplatform/atlauncher/ATLPackInstallTask.h
- modplatform/atlauncher/ATLPackManifest.cpp
- modplatform/atlauncher/ATLPackManifest.h
-)
-
-add_unit_test(Index
- SOURCES meta/Index_test.cpp
- LIBS MultiMC_logic
- )
-
-################################ COMPILE ################################
-
-# we need zlib
-find_package(ZLIB REQUIRED)
-
-set(LOGIC_SOURCES
- ${CORE_SOURCES}
- ${PATHMATCHER_SOURCES}
- ${NET_SOURCES}
- ${LAUNCH_SOURCES}
- ${UPDATE_SOURCES}
- ${NOTIFICATIONS_SOURCES}
- ${NEWS_SOURCES}
- ${STATUS_SOURCES}
- ${MINECRAFT_SOURCES}
- ${SCREENSHOTS_SOURCES}
- ${TASKS_SOURCES}
- ${SETTINGS_SOURCES}
- ${JAVA_SOURCES}
- ${TRANSLATIONS_SOURCES}
- ${TOOLS_SOURCES}
- ${META_SOURCES}
- ${ICONS_SOURCES}
- ${FTB_SOURCES}
- ${FLAME_SOURCES}
- ${MODPACKSCH_SOURCES}
- ${TECHNIC_SOURCES}
- ${ATLAUNCHER_SOURCES}
-)
-
-add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
-set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
-
-generate_export_header(MultiMC_logic)
-
-# Link
-target_link_libraries(MultiMC_logic systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES} optional-bare tomlc99 BuildConfig)
-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}
-)
diff --git a/api/logic/Commandline.cpp b/api/logic/Commandline.cpp
deleted file mode 100644
index 2c0fde64..00000000
--- a/api/logic/Commandline.cpp
+++ /dev/null
@@ -1,483 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Commandline.h"
-
-/**
- * @file libutil/src/cmdutils.cpp
- */
-
-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;
-}
-
-Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle)
-{
- m_flagStyle = flagStyle;
- m_argStyle = argStyle;
-}
-
-// styles setter/getter
-void Parser::setArgumentStyle(ArgumentStyle::Enum style)
-{
- m_argStyle = style;
-}
-ArgumentStyle::Enum Parser::argumentStyle()
-{
- return m_argStyle;
-}
-
-void Parser::setFlagStyle(FlagStyle::Enum style)
-{
- m_flagStyle = style;
-}
-FlagStyle::Enum Parser::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);
-}
-
-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);
-}
-
-void Parser::addArgument(QString name, bool required, QVariant def)
-{
- 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;
-
- 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";
-
- 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;
-}
-
-// 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("");
-}
-
-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("");
-}
-
-// parsing
-QHash<QString, QVariant> Parser::parse(QStringList argv)
-{
- QHash<QString, QVariant> map;
-
- QStringListIterator it(argv);
- QString programName = it.next();
-
- QString optionPrefix;
- QString flagPrefix;
- QListIterator<PositionalDef *> positionals(m_positionals);
- QStringList expecting;
-
- getPrefix(optionPrefix, flagPrefix);
-
- while (it.hasNext())
- {
- QString arg = it.next();
-
- 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));
-*/
- 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];
-/*
- 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;
-}
-
-// 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;
- }
-}
-
-// Destructor
-Parser::~Parser()
-{
- 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 = "-";
- }
-}
-
-// ParsingError
-ParsingError::ParsingError(const QString &what) : std::runtime_error(what.toStdString())
-{
-}
-} \ No newline at end of file
diff --git a/api/logic/Commandline.h b/api/logic/Commandline.h
deleted file mode 100644
index 09c1707e..00000000
--- a/api/logic/Commandline.h
+++ /dev/null
@@ -1,252 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <exception>
-#include <stdexcept>
-
-#include <QString>
-#include <QVariant>
-#include <QHash>
-#include <QStringList>
-
-#include "multimc_logic_export.h"
-
-/**
- * @file libutil/include/cmdutils.h
- * @brief commandline parsing and processing utilities
- */
-
-namespace Commandline
-{
-
-/**
- * @brief split a string into argv items like a shell would do
- * @param args the argument string
- * @return a QStringList containing all arguments
- */
-MULTIMC_LOGIC_EXPORT QStringList splitArgs(QString args);
-
-/**
- * @brief The FlagStyle enum
- * Specifies how flags are decorated
- */
-
-namespace FlagStyle
-{
-enum Enum
-{
- GNU, /**< --option and -o (GNU Style) */
- Unix, /**< -option and -o (Unix Style) */
- Windows, /**< /option and /o (Windows Style) */
-#ifdef Q_OS_WIN32
- Default = Windows
-#else
- Default = GNU
-#endif
-};
-}
-
-/**
- * @brief The ArgumentStyle enum
- */
-namespace ArgumentStyle
-{
-enum Enum
-{
- Space, /**< --option value */
- Equals, /**< --option=value */
- SpaceAndEquals, /**< --option[= ]value */
-#ifdef Q_OS_WIN32
- Default = Equals
-#else
- Default = SpaceAndEquals
-#endif
-};
-}
-
-/**
- * @brief The ParsingError class
- */
-class MULTIMC_LOGIC_EXPORT ParsingError : public std::runtime_error
-{
-public:
- ParsingError(const QString &what);
-};
-
-/**
- * @brief The Parser class
- */
-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();
-
-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);
-};
-}
diff --git a/api/logic/DefaultVariable.h b/api/logic/DefaultVariable.h
deleted file mode 100644
index 5c069bd3..00000000
--- a/api/logic/DefaultVariable.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-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;
- }
-private:
- T currentValue;
- T defaultValue;
- bool is_default = true;
- bool is_explicit = false;
-};
diff --git a/api/logic/Env.cpp b/api/logic/Env.cpp
deleted file mode 100644
index 71b49d95..00000000
--- a/api/logic/Env.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-#include "Env.h"
-#include "net/HttpMetaCache.h"
-#include "BaseVersion.h"
-#include "BaseVersionList.h"
-#include <QDir>
-#include <QCoreApplication>
-#include <QNetworkProxy>
-#include <QNetworkAccessManager>
-#include <QDebug>
-#include "tasks/Task.h"
-#include "meta/Index.h"
-#include "FileSystem.h"
-#include <QDebug>
-
-
-struct Env::Private
-{
- QNetworkAccessManager m_qnam;
- shared_qobject_ptr<HttpMetaCache> m_metacache;
- std::shared_ptr<IIconList> m_iconlist;
- shared_qobject_ptr<Meta::Index> m_metadataIndex;
- QString m_jarsPath;
- QSet<QString> m_features;
-};
-
-static Env * instance;
-
-/*
- * The *NEW* global rat nest of an object. Handle with care.
- */
-
-Env::Env()
-{
- d = new Private();
-}
-
-Env::~Env()
-{
- delete d;
-}
-
-Env& Env::Env::getInstance()
-{
- if(!instance)
- {
- instance = new Env();
- }
- return *instance;
-}
-
-void Env::dispose()
-{
- delete instance;
- instance = nullptr;
-}
-
-shared_qobject_ptr< HttpMetaCache > Env::metacache()
-{
- return d->m_metacache;
-}
-
-QNetworkAccessManager& Env::qnam() const
-{
- return d->m_qnam;
-}
-
-std::shared_ptr<IIconList> Env::icons()
-{
- return d->m_iconlist;
-}
-
-void Env::registerIconList(std::shared_ptr<IIconList> iconlist)
-{
- d->m_iconlist = iconlist;
-}
-
-shared_qobject_ptr<Meta::Index> Env::metadataIndex()
-{
- if (!d->m_metadataIndex)
- {
- d->m_metadataIndex.reset(new Meta::Index());
- }
- return d->m_metadataIndex;
-}
-
-
-void Env::initHttpMetaCache()
-{
- auto &m_metacache = d->m_metacache;
- m_metacache.reset(new HttpMetaCache("metacache"));
- m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
- m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
- m_metacache->addBase("versions", QDir("versions").absolutePath());
- m_metacache->addBase("libraries", QDir("libraries").absolutePath());
- m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
- m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
- m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
- m_metacache->addBase("general", QDir("cache").absolutePath());
- m_metacache->addBase("ATLauncherPacks", QDir("cache/ATLauncherPacks").absolutePath());
- m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
- m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
- m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
- m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").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("%1:%2")
- .arg(proxy.hostName())
- .arg(proxy.port());
- qDebug() << proxyDesc;
-}
-
-QString Env::getJarsPath()
-{
- if(d->m_jarsPath.isEmpty())
- {
- return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
- }
- return d->m_jarsPath;
-}
-
-void Env::setJarsPath(const QString& path)
-{
- d->m_jarsPath = path;
-}
-
-void Env::enableFeature(const QString& featureName, bool state)
-{
- if(state)
- {
- d->m_features.insert(featureName);
- }
- else
- {
- d->m_features.remove(featureName);
- }
-}
-
-bool Env::isFeatureEnabled(const QString& featureName) const
-{
- return d->m_features.contains(featureName);
-}
-
-void Env::getEnabledFeatures(QSet<QString>& features) const
-{
- features = d->m_features;
-}
-
-void Env::setEnabledFeatures(const QSet<QString>& features) const
-{
- d->m_features = features;
-}
diff --git a/api/logic/Env.h b/api/logic/Env.h
deleted file mode 100644
index 8b9b827e..00000000
--- a/api/logic/Env.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-
-#include <memory>
-#include "icons/IIconList.h"
-#include <QString>
-#include <QMap>
-
-#include "multimc_logic_export.h"
-
-#include "QObjectPtr.h"
-
-class QNetworkAccessManager;
-class HttpMetaCache;
-class BaseVersionList;
-class BaseVersion;
-
-namespace Meta
-{
-class Index;
-}
-
-#if defined(ENV)
- #undef ENV
-#endif
-#define ENV (Env::getInstance())
-
-
-class MULTIMC_LOGIC_EXPORT Env
-{
- friend class MultiMC;
-private:
- struct Private;
- Env();
- ~Env();
- static void dispose();
-public:
- static Env& getInstance();
-
- QNetworkAccessManager &qnam() const;
-
- shared_qobject_ptr<HttpMetaCache> metacache();
-
- std::shared_ptr<IIconList> icons();
-
- /// init the cache. FIXME: possible future hook point
- void initHttpMetaCache();
-
- /// Updates the application proxy settings from the settings object.
- void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
-
- void registerIconList(std::shared_ptr<IIconList> iconlist);
-
- shared_qobject_ptr<Meta::Index> metadataIndex();
-
- QString getJarsPath();
- void setJarsPath(const QString & path);
-
- bool isFeatureEnabled(const QString & featureName) const;
- void enableFeature(const QString & featureName, bool state = true);
- void getEnabledFeatures(QSet<QString> & features) const;
- void setEnabledFeatures(const QSet<QString> & features) const;
-
-protected:
- Private * d;
-};
diff --git a/api/logic/Exception.h b/api/logic/Exception.h
deleted file mode 100644
index 9400b3f8..00000000
--- a/api/logic/Exception.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Licensed under the Apache-2.0 license. See README.md for details.
-
-#pragma once
-
-#include <QString>
-#include <QDebug>
-#include <exception>
-
-#include "multimc_logic_export.h"
-
-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;
- }
-
-private:
- QString m_message;
-};
diff --git a/api/logic/ExponentialSeries.h b/api/logic/ExponentialSeries.h
deleted file mode 100644
index a9487f0a..00000000
--- a/api/logic/ExponentialSeries.h
+++ /dev/null
@@ -1,43 +0,0 @@
-
-#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
deleted file mode 100644
index 13f05b86..00000000
--- a/api/logic/FileSystem.cpp
+++ /dev/null
@@ -1,457 +0,0 @@
-// Licensed under the Apache-2.0 license. See README.md for details.
-
-#include "FileSystem.h"
-
-#include <QDir>
-#include <QFile>
-#include <QSaveFile>
-#include <QFileInfo>
-#include <QDebug>
-#include <QUrl>
-#include <QStandardPaths>
-#include <QTextStream>
-
-#if defined Q_OS_WIN32
- #include <windows.h>
- #include <string>
- #include <sys/utime.h>
- #include <winnls.h>
- #include <shobjidl.h>
- #include <objbase.h>
- #include <objidl.h>
- #include <shlguid.h>
- #include <shlobj.h>
-#else
- #include <utime.h>
-#endif
-
-namespace FS {
-
-void ensureExists(const QDir &dir)
-{
- 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());
- }
-}
-
-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;
-}
-
-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);
-#else
- 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;
-}
-
-bool ensureFolderPathExists(QString foldernamepath)
-{
- 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;
-}
-
-bool deletePath(QString path)
-{
- 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());
- }
- }
-#else
- // 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;
-}
-
-
-QString PathCombine(const QString & path1, const QString & path2)
-{
- 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);
-}
-
-QString PathCombine(const QString & path1, const QString & path2, const QString & path3, const QString & path4)
-{
- return PathCombine(PathCombine(path1, path2, path3), path4);
-}
-
-QString AbsolutePath(QString path)
-{
- 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();
-}
-
-/**
- * Normalize path
- *
- * Any paths inside the current folder will be normalized to relative paths (to current)
- * Other paths will be made absolute
- */
-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;
- }
-}
-
-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;
-}
-
-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;
-}
-
-// 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);
-}
-
-// Win32 crap
-#if defined Q_OS_WIN
-
-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%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);
-}
-
-// Cross-platform Shortcut creation
-bool createShortCut(QString location, QString dest, QStringList args, QString name,
- QString icon)
-{
-#if defined Q_OS_LINUX
- location = PathCombine(location, name + ".desktop");
-
- QFile f(location);
- f.open(QIODevice::WriteOnly | QIODevice::Text);
- QTextStream stream(&f);
-
- 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.flush();
- f.close();
-
- f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
- QFileDevice::ExeOther);
-
- 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;
-#else
- qWarning("Desktop Shortcuts not supported on your platform!");
- return false;
-#endif
-}
-}
diff --git a/api/logic/FileSystem.h b/api/logic/FileSystem.h
deleted file mode 100644
index 55ec6a58..00000000
--- a/api/logic/FileSystem.h
+++ /dev/null
@@ -1,128 +0,0 @@
-// Licensed under the Apache-2.0 license. See README.md for details.
-
-#pragma once
-
-#include "Exception.h"
-#include "pathmatcher/IPathMatcher.h"
-
-#include "multimc_logic_export.h"
-#include <QDir>
-#include <QFlags>
-
-namespace FS
-{
-
-class MULTIMC_LOGIC_EXPORT FileSystemException : public ::Exception
-{
-public:
- FileSystemException(const QString &message) : Exception(message) {}
-};
-
-/**
- * write data to a file safely
- */
-MULTIMC_LOGIC_EXPORT void write(const QString &filename, const QByteArray &data);
-
-/**
- * read data from a file safely\
- */
-MULTIMC_LOGIC_EXPORT QByteArray read(const QString &filename);
-
-/**
- * Update the last changed timestamp of an existing file
- */
-MULTIMC_LOGIC_EXPORT bool updateTimestamp(const QString & filename);
-
-/**
- * Creates all the folders in a path for the specified path
- * last segment of the path is treated as a file name and is ignored!
- */
-MULTIMC_LOGIC_EXPORT bool ensureFilePathExists(QString filenamepath);
-
-/**
- * Creates all the folders in a path for the specified path
- * last segment of the path is treated as a folder name and is created!
- */
-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());
- }
-
-private:
- bool operator()(const QString &offset);
-
-private:
- bool m_followSymlinks = true;
- const IPathMatcher * m_blacklist = nullptr;
- QDir m_src;
- QDir m_dst;
-};
-
-/**
- * Delete a folder recursively
- */
-MULTIMC_LOGIC_EXPORT bool deletePath(QString path);
-
-MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2);
-MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
-MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
-
-MULTIMC_LOGIC_EXPORT QString AbsolutePath(QString path);
-
-/**
- * Resolve an executable
- *
- * Will resolve:
- * single executable (by name)
- * relative path
- * absolute path
- *
- * @return absolute path to executable or null string
- */
-MULTIMC_LOGIC_EXPORT QString ResolveExecutable(QString path);
-
-/**
- * Normalize path
- *
- * Any paths inside the current directory will be normalized to relative paths (to current)
- * Other paths will be made absolute
- *
- * Returns false if the path logic somehow filed (and normalizedPath in invalid)
- */
-MULTIMC_LOGIC_EXPORT QString NormalizePath(QString path);
-
-MULTIMC_LOGIC_EXPORT QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
-
-MULTIMC_LOGIC_EXPORT QString DirNameFromString(QString string, QString inDir = ".");
-
-/// Checks if the a given Path contains "!"
-MULTIMC_LOGIC_EXPORT bool checkProblemticPathJava(QDir folder);
-
-// Get the Directory representing the User's Desktop
-MULTIMC_LOGIC_EXPORT QString getDesktopDir();
-
-// Create a shortcut at *location*, pointing to *dest* called with the arguments *args*
-// call it *name* and assign it the icon *icon*
-// return true if operation succeeded
-MULTIMC_LOGIC_EXPORT bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
-}
diff --git a/api/logic/FileSystem_test.cpp b/api/logic/FileSystem_test.cpp
deleted file mode 100644
index df653ea1..00000000
--- a/api/logic/FileSystem_test.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-#include <QTest>
-#include <QTemporaryDir>
-#include <QStandardPaths>
-#include "TestUtil.h"
-
-#include "FileSystem.h"
-
-class FileSystemTest : public QObject
-{
- Q_OBJECT
-
- 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";
-#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";
-#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";
-#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";
-#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));
- }
-
-// 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);
- }
-#endif
-};
-
-QTEST_GUILESS_MAIN(FileSystemTest)
-
-#include "FileSystem_test.moc"
diff --git a/api/logic/Filter.cpp b/api/logic/Filter.cpp
deleted file mode 100644
index c65ca0ce..00000000
--- a/api/logic/Filter.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "Filter.h"
-
-Filter::~Filter(){}
-
-ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){}
-ContainsFilter::~ContainsFilter(){}
-bool ContainsFilter::accepts(const QString& value)
-{
- return value.contains(pattern);
-}
-
-ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){}
-ExactFilter::~ExactFilter(){}
-bool ExactFilter::accepts(const QString& value)
-{
- return value == pattern;
-}
-
-RegexpFilter::RegexpFilter(const QString& regexp, bool invert)
- :invert(invert)
-{
- pattern.setPattern(regexp);
- pattern.optimize();
-}
-RegexpFilter::~RegexpFilter(){}
-bool RegexpFilter::accepts(const QString& value)
-{
- auto match = pattern.match(value);
- bool matched = match.hasMatch();
- return invert ? (!matched) : (matched);
-}
diff --git a/api/logic/Filter.h b/api/logic/Filter.h
deleted file mode 100644
index 1ba48e48..00000000
--- a/api/logic/Filter.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QRegularExpression>
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT Filter
-{
-public:
- virtual ~Filter();
- virtual bool accepts(const QString & value) = 0;
-};
-
-class MULTIMC_LOGIC_EXPORT ContainsFilter: public Filter
-{
-public:
- ContainsFilter(const QString &pattern);
- virtual ~ContainsFilter();
- bool accepts(const QString & value) override;
-private:
- QString pattern;
-};
-
-class MULTIMC_LOGIC_EXPORT ExactFilter: public Filter
-{
-public:
- ExactFilter(const QString &pattern);
- virtual ~ExactFilter();
- bool accepts(const QString & value) override;
-private:
- QString pattern;
-};
-
-class MULTIMC_LOGIC_EXPORT RegexpFilter: public Filter
-{
-public:
- RegexpFilter(const QString &regexp, bool invert);
- virtual ~RegexpFilter();
- bool accepts(const QString & value) override;
-private:
- QRegularExpression pattern;
- bool invert = false;
-};
diff --git a/api/logic/GZip.cpp b/api/logic/GZip.cpp
deleted file mode 100644
index 0368c32d..00000000
--- a/api/logic/GZip.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-#include "GZip.h"
-#include <zlib.h>
-#include <QByteArray>
-
-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;
-}
-
-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;
-} \ No newline at end of file
diff --git a/api/logic/GZip.h b/api/logic/GZip.h
deleted file mode 100644
index c7eddbb3..00000000
--- a/api/logic/GZip.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-#include <QByteArray>
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT GZip
-{
-public:
- 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
deleted file mode 100644
index 3f4d181c..00000000
--- a/api/logic/GZip_test.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "GZip.h"
-#include <random>
-
-void fib(int &prev, int &cur)
-{
- auto ret = prev + cur;
- prev = cur;
- cur = ret;
-}
-
-class GZipTest : public QObject
-{
- 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);
- }
-};
-
-QTEST_GUILESS_MAIN(GZipTest)
-
-#include "GZip_test.moc"
diff --git a/api/logic/InstanceCopyTask.cpp b/api/logic/InstanceCopyTask.cpp
deleted file mode 100644
index 35adeaf9..00000000
--- a/api/logic/InstanceCopyTask.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "InstanceCopyTask.h"
-#include "settings/INISettingsObject.h"
-#include "FileSystem.h"
-#include "NullInstance.h"
-#include "pathmatcher/RegexpMatcher.h"
-#include <QtConcurrentRun>
-
-InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime)
-{
- m_origInstance = origInstance;
- m_keepPlaytime = keepPlaytime;
-
- 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()));
-
- 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);
-}
-
-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);
- if(!m_keepPlaytime) {
- inst->resetTimePlayed();
- }
- emitSucceeded();
-}
-
-void InstanceCopyTask::copyAborted()
-{
- emitFailed(tr("Instance folder copy has been aborted."));
- return;
-}
diff --git a/api/logic/InstanceCopyTask.h b/api/logic/InstanceCopyTask.h
deleted file mode 100644
index 6465e92d..00000000
--- a/api/logic/InstanceCopyTask.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include "tasks/Task.h"
-#include "multimc_logic_export.h"
-#include "net/NetJob.h"
-#include <QUrl>
-#include <QFuture>
-#include <QFutureWatcher>
-#include "settings/SettingsObject.h"
-#include "BaseVersion.h"
-#include "BaseInstance.h"
-#include "InstanceTask.h"
-
-class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask
-{
- Q_OBJECT
-public:
- explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime);
-
-protected:
- //! 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;
- bool m_keepPlaytime;
-};
diff --git a/api/logic/InstanceCreationTask.cpp b/api/logic/InstanceCreationTask.cpp
deleted file mode 100644
index eafc5126..00000000
--- a/api/logic/InstanceCreationTask.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "InstanceCreationTask.h"
-#include "settings/INISettingsObject.h"
-#include "FileSystem.h"
-
-//FIXME: remove this
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-
-InstanceCreationTask::InstanceCreationTask(BaseVersionPtr 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.getPackProfile();
- 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
deleted file mode 100644
index 154a854f..00000000
--- a/api/logic/InstanceCreationTask.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include "tasks/Task.h"
-#include "multimc_logic_export.h"
-#include "net/NetJob.h"
-#include <QUrl>
-#include "settings/SettingsObject.h"
-#include "BaseVersion.h"
-#include "InstanceTask.h"
-
-class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public InstanceTask
-{
- Q_OBJECT
-public:
- explicit InstanceCreationTask(BaseVersionPtr version);
-
-protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
-
-private: /* data */
- BaseVersionPtr m_version;
-};
diff --git a/api/logic/InstanceImportTask.cpp b/api/logic/InstanceImportTask.cpp
deleted file mode 100644
index 3eac4d57..00000000
--- a/api/logic/InstanceImportTask.cpp
+++ /dev/null
@@ -1,456 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "InstanceImportTask.h"
-#include "BaseInstance.h"
-#include "FileSystem.h"
-#include "Env.h"
-#include "MMCZip.h"
-#include "NullInstance.h"
-#include "settings/INISettingsObject.h"
-#include "icons/IIconList.h"
-#include "icons/IconUtils.h"
-#include <QtConcurrentRun>
-
-// FIXME: this does not belong here, it's Minecraft/Flame specific
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "modplatform/flame/FileResolvingTask.h"
-#include "modplatform/flame/PackManifest.h"
-#include "Json.h"
-#include <quazipdir.h>
-#include "modplatform/technic/TechnicPackProcessor.h"
-
-InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
-{
- m_sourceUrl = sourceUrl;
-}
-
-void InstanceImportTask::executeTask()
-{
- if (m_sourceUrl.isLocalFile())
- {
- m_archivePath = m_sourceUrl.toLocalFile();
- processZipPack();
- }
- else
- {
- setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
- m_downloadRequired = true;
-
- const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
- auto entry = ENV.metacache()->resolveEntry("general", path);
- entry->setStale(true);
- m_filesNetJob.reset(new NetJob(tr("Modpack download")));
- m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
- m_archivePath = entry->getFullPath();
- auto job = m_filesNetJob.get();
- connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
- connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
- connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
- m_filesNetJob->start();
- }
-}
-
-void InstanceImportTask::downloadSucceeded()
-{
- processZipPack();
- m_filesNetJob.reset();
-}
-
-void InstanceImportTask::downloadFailed(QString reason)
-{
- emitFailed(reason);
- m_filesNetJob.reset();
-}
-
-void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
-{
- setProgress(current / 2, total);
-}
-
-void InstanceImportTask::processZipPack()
-{
- setStatus(tr("Extracting modpack"));
- QDir extractDir(m_stagingPath);
- qDebug() << "Attempting to create instance from" << m_archivePath;
-
- // open the zip and find relevant files in it
- m_packZip.reset(new QuaZip(m_archivePath));
- if (!m_packZip->open(QuaZip::mdUnzip))
- {
- emitFailed(tr("Unable to open supplied modpack zip file."));
- return;
- }
-
- QStringList blacklist = {"instance.cfg", "manifest.json"};
- QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
- bool technicFound = QuaZipDir(m_packZip.get()).exists("/bin/modpack.jar") || QuaZipDir(m_packZip.get()).exists("/bin/version.json");
- 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 (technicFound)
- {
- // process as Technic pack
- qDebug() << "Technic:" << technicFound;
- extractDir.mkpath(".minecraft");
- extractDir.cd(".minecraft");
- m_modpackType = ModpackType::Technic;
- }
- else if(!flameFound.isNull())
- {
- // process as Flame pack
- qDebug() << "Flame:" << flameFound;
- root = flameFound;
- m_modpackType = ModpackType::Flame;
- }
- if(m_modpackType == ModpackType::Unknown)
- {
- emitFailed(tr("Archive does not contain a recognized modpack type."));
- return;
- }
-
- // make sure we extract just the pack
- m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
- m_extractFutureWatcher.setFuture(m_extractFuture);
-}
-
-void InstanceImportTask::extractFinished()
-{
- m_packZip.reset();
- if (!m_extractFuture.result())
- {
- emitFailed(tr("Failed to extract modpack"));
- return;
- }
- QDir extractDir(m_stagingPath);
-
- qDebug() << "Fixing permissions for extracted pack files...";
- QDirIterator it(extractDir, QDirIterator::Subdirectories);
- while (it.hasNext())
- {
- auto filepath = it.next();
- QFileInfo file(filepath);
- auto permissions = QFile::permissions(filepath);
- auto origPermissions = permissions;
- if(file.isDir())
- {
- // Folder +rwx for current user
- permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
- }
- else
- {
- // File +rw for current user
- permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
- }
- if(origPermissions != permissions)
- {
- if(!QFile::setPermissions(filepath, permissions))
- {
- logWarning(tr("Could not fix permissions for %1").arg(filepath));
- }
- else
- {
- qDebug() << "Fixed" << filepath;
- }
- }
- }
-
- switch(m_modpackType)
- {
- case ModpackType::Flame:
- processFlame();
- return;
- case ModpackType::MultiMC:
- processMultiMC();
- return;
- case ModpackType::Technic:
- processTechnic();
- return;
- case ModpackType::Unknown:
- emitFailed(tr("Archive does not contain a recognized modpack type."));
- return;
- }
-}
-
-void InstanceImportTask::extractAborted()
-{
- emitFailed(tr("Instance import has been aborted."));
- return;
-}
-
-void InstanceImportTask::processFlame()
-{
- const static QMap<QString,QString> forgemap = {
- {"1.2.5", "3.4.9.171"},
- {"1.4.2", "6.0.1.355"},
- {"1.4.7", "6.6.2.534"},
- {"1.5.2", "7.8.1.737"}
- };
- Flame::Manifest pack;
- try
- {
- QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
- Flame::loadManifest(pack, configPath);
- QFile::remove(configPath);
- }
- catch (const JSONValidationError &e)
- {
- emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
- return;
- }
- if(!pack.overrides.isEmpty())
- {
- QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
- if (QFile::exists(overridePath))
- {
- QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
- if (!QFile::rename(overridePath, mcPath))
- {
- emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
- return;
- }
- }
- else
- {
- logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
- }
- }
-
- QString forgeVersion;
- QString fabricVersion;
- for(auto &loader: pack.minecraft.modLoaders)
- {
- auto id = loader.id;
- if(id.startsWith("forge-"))
- {
- id.remove("forge-");
- forgeVersion = id;
- continue;
- }
- if(id.startsWith("fabric-"))
- {
- id.remove("fabric-");
- fabricVersion = 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.getPackProfile();
- 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(!fabricVersion.isEmpty())
- {
- components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
- }
- 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.getPackProfile();
- profile->installJarMods(jarMods);
- // nuke the original files
- FS::deletePath(jarmodsPath);
- }
- instance.setName(m_instName);
- m_modIdResolver.reset(new Flame::FileResolvingTask(pack));
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
- {
- auto results = m_modIdResolver->getResults();
- m_filesNetJob.reset(new NetJob(tr("Mod download")));
- for(auto result: results.files)
- {
- QString filename = result.fileName;
- if(!result.required)
- {
- filename += ".disabled";
- }
-
- auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
- auto path = FS::PathCombine(m_stagingPath , relpath);
-
- switch(result.type)
- {
- case Flame::File::Type::Folder:
- {
- logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
- // fall-through intentional, we treat these as plain old mods and dump them wherever.
- }
- case Flame::File::Type::SingleFile:
- case Flame::File::Type::Mod:
- {
- qDebug() << "Will download" << result.url << "to" << path;
- auto dl = Net::Download::makeFile(result.url, path);
- m_filesNetJob->addNetAction(dl);
- break;
- }
- case Flame::File::Type::Modpack:
- logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
- break;
- case Flame::File::Type::Cmod2:
- case Flame::File::Type::Ctoc:
- case Flame::File::Type::Unknown:
- logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
- break;
- }
- }
- m_modIdResolver.reset();
- connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
- {
- m_filesNetJob.reset();
- emitSucceeded();
- }
- );
- connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
- {
- m_filesNetJob.reset();
- emitFailed(reason);
- });
- connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
- {
- setProgress(current, total);
- });
- setStatus(tr("Downloading mods..."));
- m_filesNetJob->start();
- }
- );
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
- {
- m_modIdResolver.reset();
- emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
- });
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
- {
- setProgress(current, total);
- });
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
- {
- setStatus(status);
- });
- m_modIdResolver->start();
-}
-
-void InstanceImportTask::processTechnic()
-{
- shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
- connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
- connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
- packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath);
-}
-
-void InstanceImportTask::processMultiMC()
-{
- // FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
- QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
- instanceSettings->registerSetting("InstanceType", "Legacy");
-
- NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
-
- // reset time played on import... because packs.
- instance.resetTimePlayed();
-
- // set a new nice name
- instance.setName(m_instName);
-
- // if the icon was specified by user, use that. otherwise pull icon from the pack
- if (m_instIcon != "default")
- {
- instance.setIconKey(m_instIcon);
- }
- else
- {
- m_instIcon = instance.iconKey();
-
- auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
- if (!importIconPath.isNull() && QFile::exists(importIconPath))
- {
- // import icon
- auto iconList = ENV.icons();
- if (iconList->iconFileExists(m_instIcon))
- {
- iconList->deleteIcon(m_instIcon);
- }
- iconList->installIcons({importIconPath});
- }
- }
- emitSucceeded();
-}
diff --git a/api/logic/InstanceImportTask.h b/api/logic/InstanceImportTask.h
deleted file mode 100644
index 7291324d..00000000
--- a/api/logic/InstanceImportTask.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "InstanceTask.h"
-#include "multimc_logic_export.h"
-#include "net/NetJob.h"
-#include <QUrl>
-#include <QFuture>
-#include <QFutureWatcher>
-#include "settings/SettingsObject.h"
-#include "QObjectPtr.h"
-
-#include <nonstd/optional>
-
-class QuaZip;
-namespace Flame
-{
- class FileResolvingTask;
-}
-
-class MULTIMC_LOGIC_EXPORT InstanceImportTask : public InstanceTask
-{
- Q_OBJECT
-public:
- explicit InstanceImportTask(const QUrl sourceUrl);
-
-protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
-
-private:
- void processZipPack();
- void processMultiMC();
- void processFlame();
- void processTechnic();
-
-private slots:
- 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<nonstd::optional<QStringList>> m_extractFuture;
- QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
- enum class ModpackType{
- Unknown,
- MultiMC,
- Flame,
- Technic
- } m_modpackType = ModpackType::Unknown;
-};
diff --git a/api/logic/InstanceList.cpp b/api/logic/InstanceList.cpp
deleted file mode 100644
index cb38853b..00000000
--- a/api/logic/InstanceList.cpp
+++ /dev/null
@@ -1,867 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <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"
-
-const static int GROUP_FILE_FORMAT_VERSION = 1;
-
-InstanceList::InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent)
- : QAbstractListModel(parent), m_globalSettings(settings)
-{
- 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()
-{
-}
-
-int InstanceList::rowCount(const QModelIndex &parent) const
-{
- 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());
-}
-
-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:
- {
- return pdata->id();
- }
- 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 | Qt::ItemIsEditable);
- }
- return f;
-}
-
-GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
-{
- auto inst = getInstanceById(id);
- if(!inst)
- {
- return GroupId();
- }
- auto iter = m_instanceGroupIndex.find(inst->id());
- if(iter != m_instanceGroupIndex.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_instanceGroupIndex.find(inst->id());
- if(iter != m_instanceGroupIndex.end())
- {
- if(*iter != name)
- {
- *iter = name;
- changed = true;
- }
- }
- else
- {
- changed = true;
- m_instanceGroupIndex[id] = name;
- }
-
- if(changed)
- {
- m_groupNameCache.insert(name);
- auto idx = getInstIndex(inst.get());
- emit dataChanged(index(idx), index(idx), {GroupRole});
- saveGroupList();
- }
-}
-
-QStringList InstanceList::getGroups()
-{
- return m_groupNameCache.toList();
-}
-
-void InstanceList::deleteGroup(const QString& name)
-{
- 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_instanceGroupIndex.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();
- }
-}
-
-bool InstanceList::isGroupCollapsed(const QString& group)
-{
- return m_collapsedGroups.contains(group);
-}
-
-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_instanceGroupIndex.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;
-}
-
-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;
- updateTotalPlayTime();
- return NoError;
-}
-
-void InstanceList::updateTotalPlayTime()
-{
- totalPlayTime = 0;
- for(auto const& itr : m_instances)
- {
- totalPlayTime += itr.get()->totalTimePlayed();
- }
-}
-
-void InstanceList::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();
-}
-
-void InstanceList::resumeWatch()
-{
- 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 --;
-}
-
-void InstanceList::providerUpdated()
-{
- m_dirty = true;
- if(m_watchLevel == 1)
- {
- loadList();
- }
-}
-
-InstancePtr InstanceList::getInstanceById(QString instId) const
-{
- if(instId.isEmpty())
- return InstancePtr();
- for(auto & inst: m_instances)
- {
- if (inst->id() == instId)
- {
- return inst;
- }
- }
- return InstancePtr();
-}
-
-QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
-{
- return index(getInstIndex(getInstanceById(id).get()));
-}
-
-int InstanceList::getInstIndex(BaseInstance *inst) const
-{
- int count = m_instances.count();
- for (int i = 0; i < count; i++)
- {
- if (inst == m_instances[i].get())
- {
- return i;
- }
- }
- return -1;
-}
-
-void InstanceList::propertiesChanged(BaseInstance *inst)
-{
- int i = getInstIndex(inst);
- if (i != -1)
- {
- emit dataChanged(index(i), index(i));
- updateTotalPlayTime();
- }
-}
-
-InstancePtr InstanceList::loadInstance(const InstanceId& id)
-{
- if(!m_groupsLoaded)
- {
- loadGroupList();
- }
-
- auto instanceRoot = FS::PathCombine(m_instDir, id);
- auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
- InstancePtr inst;
-
- instanceSettings->registerSetting("InstanceType", "Legacy");
-
- QString inst_type = instanceSettings->get("InstanceType").toString();
-
- if (inst_type == "OneSix" || inst_type == "Nostalgia")
- {
- inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
- }
- else if (inst_type == "Legacy")
- {
- inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
- }
- else
- {
- inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
- }
- qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
- return inst;
-}
-
-void InstanceList::saveGroupList()
-{
- qDebug() << "Will save group list now.";
- if(!m_instancesProbed)
- {
- qDebug() << "Group saving prevented because we don't know the full list of instances yet.";
- return;
- }
- WatchLock foo(m_watcher, m_instDir);
- QString groupFileName = m_instDir + "/instgroups.json";
- QMap<QString, QSet<QString>> reverseGroupMap;
- for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.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(m_collapsedGroups.contains(name)));
- 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.";
-
- 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;
- }
-
- QSet<QString> groupSet;
- m_instanceGroupIndex.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);
-
- auto hidden = groupObj.value("hidden").toBool(false);
- if(hidden) {
- m_collapsedGroups.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_instanceGroupIndex[(*iter2).toString()] = groupName;
- }
- }
- m_groupsLoaded = true;
- m_groupNameCache.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();
- }
-}
-
-void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed)
-{
- qDebug() << "Group" << group << (collapsed ? "collapsed" : "expanded");
- if(collapsed) {
- m_collapsedGroups.insert(group);
- } else {
- m_collapsedGroups.remove(group);
- }
- saveGroupList();
-}
-
-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_instanceGroupIndex[instID] = groupName;
- instanceSet.insert(instID);
- m_groupNameCache.insert(groupName);
- emit instancesChanged();
- emit instanceSelectRequest(instID);
- }
- saveGroupList();
- return true;
-}
-
-bool InstanceList::destroyStagingPath(const QString& keyPath)
-{
- return FS::deletePath(keyPath);
-}
-
-int InstanceList::getTotalPlayTime() {
- updateTotalPlayTime();
- return totalPlayTime;
-}
-
-#include "InstanceList.moc"
diff --git a/api/logic/InstanceList.h b/api/logic/InstanceList.h
deleted file mode 100644
index 56ee3be4..00000000
--- a/api/logic/InstanceList.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QAbstractListModel>
-#include <QSet>
-#include <QList>
-
-#include "BaseInstance.h"
-
-#include "multimc_logic_export.h"
-
-#include "QObjectPtr.h"
-
-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
-
-public:
- 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 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();
- bool isGroupCollapsed(const QString &groupName);
-
- 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);
-
- int getTotalPlayTime();
-
-signals:
- void dataIsInvalid();
- void instancesChanged();
- void instanceSelectRequest(QString instanceId);
- void groupsChanged(QSet<QString> groups);
-
-public slots:
- void on_InstFolderChanged(const Setting &setting, QVariant value);
- void on_GroupStateChanged(const QString &group, bool collapsed);
-
-private slots:
- void propertiesChanged(BaseInstance *inst);
- void providerUpdated();
- void instanceDirContentsChanged(const QString &path);
-
-private:
- int getInstIndex(BaseInstance *inst) const;
- void updateTotalPlayTime();
- void suspendWatch();
- void resumeWatch();
- void add(const QList<InstancePtr> &list);
- void loadGroupList();
- void saveGroupList();
- QList<InstanceId> discoverInstances();
- InstancePtr loadInstance(const InstanceId& id);
-
-private:
- int m_watchLevel = 0;
- int totalPlayTime = 0;
- bool m_dirty = false;
- QList<InstancePtr> m_instances;
- QSet<QString> m_groupNameCache;
-
- SettingsObjectPtr m_globalSettings;
- QString m_instDir;
- QFileSystemWatcher * m_watcher;
- // FIXME: this is so inefficient that looking at it is almost painful.
- QSet<QString> m_collapsedGroups;
- QMap<InstanceId, GroupId> m_instanceGroupIndex;
- QSet<InstanceId> instanceSet;
- bool m_groupsLoaded = false;
- bool m_instancesProbed = false;
-};
diff --git a/api/logic/InstanceTask.cpp b/api/logic/InstanceTask.cpp
deleted file mode 100644
index dd132877..00000000
--- a/api/logic/InstanceTask.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "InstanceTask.h"
-
-InstanceTask::InstanceTask()
-{
-}
-
-InstanceTask::~InstanceTask()
-{
-}
diff --git a/api/logic/InstanceTask.h b/api/logic/InstanceTask.h
deleted file mode 100644
index c5f6c7fd..00000000
--- a/api/logic/InstanceTask.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#pragma once
-
-#include "tasks/Task.h"
-#include "multimc_logic_export.h"
-#include "settings/SettingsObject.h"
-
-class MULTIMC_LOGIC_EXPORT InstanceTask : public Task
-{
- Q_OBJECT
-public:
- explicit InstanceTask();
- virtual ~InstanceTask();
-
- void setParentSettings(SettingsObjectPtr settings)
- {
- m_globalSettings = settings;
- }
-
- void setStagingPath(const QString &stagingPath)
- {
- m_stagingPath = stagingPath;
- }
-
- void setName(const QString &name)
- {
- m_instName = name;
- }
- QString name() const
- {
- return m_instName;
- }
-
- void setIcon(const QString &icon)
- {
- m_instIcon = icon;
- }
-
- void setGroup(const QString &group)
- {
- m_instGroup = group;
- }
- QString group() const
- {
- return m_instGroup;
- }
-
-protected: /* data */
- SettingsObjectPtr m_globalSettings;
- QString m_instName;
- QString m_instIcon;
- QString m_instGroup;
- QString m_stagingPath;
-};
diff --git a/api/logic/Json.cpp b/api/logic/Json.cpp
deleted file mode 100644
index 37ada1aa..00000000
--- a/api/logic/Json.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-// Licensed under the Apache-2.0 license. See README.md for details.
-
-#include "Json.h"
-
-#include <QFile>
-
-#include "FileSystem.h"
-#include <math.h>
-
-namespace Json
-{
-void write(const QJsonDocument &doc, const QString &filename)
-{
- FS::write(filename, doc.toJson());
-}
-void write(const QJsonObject &object, const QString &filename)
-{
- write(QJsonDocument(object), filename);
-}
-void write(const QJsonArray &array, const QString &filename)
-{
- write(QJsonDocument(array), filename);
-}
-
-QByteArray toBinary(const QJsonObject &obj)
-{
- return QJsonDocument(obj).toBinaryData();
-}
-QByteArray toBinary(const QJsonArray &array)
-{
- return QJsonDocument(array).toBinaryData();
-}
-QByteArray toText(const QJsonObject &obj)
-{
- return QJsonDocument(obj).toJson(QJsonDocument::Compact);
-}
-QByteArray toText(const QJsonArray &array)
-{
- 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;
-}
-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;
- }
-}
-QJsonDocument requireDocument(const QString &filename, const QString &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();
-}
-QJsonArray requireArray(const QJsonDocument &doc, const QString &what)
-{
- 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);
- }
-}
-
-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);
- }
-}
-
-template<>
-QJsonValue toJson<QUrl>(const QUrl &url)
-{
- return QJsonValue(url.toString(QUrl::FullyEncoded));
-}
-template<>
-QJsonValue toJson<QByteArray>(const QByteArray &data)
-{
- return QJsonValue(QString::fromLatin1(data.toHex()));
-}
-template<>
-QJsonValue toJson<QDateTime>(const QDateTime &datetime)
-{
- return QJsonValue(datetime.toString(Qt::ISODate));
-}
-template<>
-QJsonValue toJson<QDir>(const QDir &dir)
-{
- return QDir::current().relativeFilePath(dir.absolutePath());
-}
-template<>
-QJsonValue toJson<QUuid>(const QUuid &uuid)
-{
- return uuid.toString();
-}
-template<>
-QJsonValue toJson<QVariant>(const QVariant &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());
-}
-
-template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what)
-{
- 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();
-}
-
-template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what)
-{
- 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();
-}
-
-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);
-}
-
-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;
-}
-
-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;
-}
-
-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);
-}
-
-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;
-}
-
-template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what)
-{
- 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();
-}
-
-template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what)
-{
- 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
deleted file mode 100644
index 34ff6fe2..00000000
--- a/api/logic/Json.h
+++ /dev/null
@@ -1,249 +0,0 @@
-// Licensed under the Apache-2.0 license. See README.md for details.
-
-#pragma once
-
-#include <QJsonDocument>
-#include <QJsonArray>
-#include <QJsonObject>
-#include <QDateTime>
-#include <QUrl>
-#include <QDir>
-#include <QUuid>
-#include <QVariant>
-#include <memory>
-
-#include "Exception.h"
-
-namespace Json
-{
-class MULTIMC_LOGIC_EXPORT JsonException : public ::Exception
-{
-public:
- JsonException(const QString &message) : Exception(message) {}
-};
-
-/// @throw FileSystemException
-MULTIMC_LOGIC_EXPORT void write(const QJsonDocument &doc, const QString &filename);
-/// @throw FileSystemException
-MULTIMC_LOGIC_EXPORT void write(const QJsonObject &object, const QString &filename);
-/// @throw FileSystemException
-MULTIMC_LOGIC_EXPORT void write(const QJsonArray &array, const QString &filename);
-
-MULTIMC_LOGIC_EXPORT QByteArray toBinary(const QJsonObject &obj);
-MULTIMC_LOGIC_EXPORT QByteArray toBinary(const QJsonArray &array);
-MULTIMC_LOGIC_EXPORT QByteArray toText(const QJsonObject &obj);
-MULTIMC_LOGIC_EXPORT QByteArray toText(const QJsonArray &array);
-
-/// @throw JsonException
-MULTIMC_LOGIC_EXPORT QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document");
-/// @throw JsonException
-MULTIMC_LOGIC_EXPORT QJsonDocument requireDocument(const QString &filename, const QString &what = "Document");
-/// @throw JsonException
-MULTIMC_LOGIC_EXPORT QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document");
-/// @throw JsonException
-MULTIMC_LOGIC_EXPORT QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document");
-
-/////////////////// WRITING ////////////////////
-
-void writeString(QJsonObject & to, const QString &key, const QString &value);
-void writeStringList(QJsonObject & to, const QString &key, const QStringList &values);
-
-template<typename T>
-QJsonValue toJson(const T &t)
-{
- return QJsonValue(t);
-}
-template<>
-QJsonValue toJson<QUrl>(const QUrl &url);
-template<>
-QJsonValue toJson<QByteArray>(const QByteArray &data);
-template<>
-QJsonValue toJson<QDateTime>(const QDateTime &datetime);
-template<>
-QJsonValue toJson<QDir>(const QDir &dir);
-template<>
-QJsonValue toJson<QUuid>(const QUuid &uuid);
-template<>
-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;
-}
-
-////////////////// READING ////////////////////
-
-/// @throw JsonException
-template <typename T>
-T requireIsType(const QJsonValue &value, const QString &what = "Value");
-
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT double requireIsType<double>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT bool requireIsType<bool>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT int requireIsType<int>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QString requireIsType<QString>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QDir requireIsType<QDir>(const QJsonValue &value, const QString &what);
-/// @throw JsonException
-template<> MULTIMC_LOGIC_EXPORT QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what);
-
-// the following functions are higher level functions, that make use of the above functions for
-// type conversion
-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 (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);
-}
-
-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);
-}
-
-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;
-}
-
-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;
-}
-
-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);
-}
-
-/// @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);
-}
-
-template <typename T>
-QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key,
- 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);
-}
-
-// 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); \
- }
-
-JSON_HELPERFUNCTIONS(Array, QJsonArray)
-JSON_HELPERFUNCTIONS(Object, QJsonObject)
-JSON_HELPERFUNCTIONS(JsonValue, QJsonValue)
-JSON_HELPERFUNCTIONS(String, QString)
-JSON_HELPERFUNCTIONS(Boolean, bool)
-JSON_HELPERFUNCTIONS(Double, double)
-JSON_HELPERFUNCTIONS(Integer, int)
-JSON_HELPERFUNCTIONS(DateTime, QDateTime)
-JSON_HELPERFUNCTIONS(Url, QUrl)
-JSON_HELPERFUNCTIONS(ByteArray, QByteArray)
-JSON_HELPERFUNCTIONS(Dir, QDir)
-JSON_HELPERFUNCTIONS(Uuid, QUuid)
-JSON_HELPERFUNCTIONS(Variant, QVariant)
-
-#undef JSON_HELPERFUNCTIONS
-
-}
-using JSONValidationError = Json::JsonException;
diff --git a/api/logic/LoggedProcess.cpp b/api/logic/LoggedProcess.cpp
deleted file mode 100644
index 822c0f04..00000000
--- a/api/logic/LoggedProcess.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-#include "LoggedProcess.h"
-#include "MessageLevel.h"
-#include <QDebug>
-
-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);
-}
-
-LoggedProcess::~LoggedProcess()
-{
- if(m_is_detachable)
- {
- setProcessState(QProcess::NotRunning);
- }
-}
-
-QStringList reprocess(const QByteArray & data, QString & leftover)
-{
- QString str = leftover + QString::fromLocal8Bit(data);
-
- 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);
-}
-
-void LoggedProcess::on_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);
- }
-}
-
-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;
- }
-}
-
-void LoggedProcess::kill()
-{
- m_is_aborting = true;
- QProcess::kill();
-}
-
-int LoggedProcess::exitCode() const
-{
- return m_exit_code;
-}
-
-void LoggedProcess::changeState(LoggedProcess::State state)
-{
- if(state == m_state)
- return;
- m_state = state;
- emit stateChanged(m_state);
-}
-
-LoggedProcess::State LoggedProcess::state() const
-{
- 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;
- }
- }
-}
-
-#if defined Q_OS_WIN32
-#include <windows.h>
-#endif
-
-qint64 LoggedProcess::processId() const
-{
-#ifdef Q_OS_WIN
- return pid() ? pid()->dwProcessId : 0;
-#else
- return pid();
-#endif
-}
-
-void LoggedProcess::setDetachable(bool detachable)
-{
- m_is_detachable = detachable;
-}
diff --git a/api/logic/LoggedProcess.h b/api/logic/LoggedProcess.h
deleted file mode 100644
index 327cdc6a..00000000
--- a/api/logic/LoggedProcess.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QProcess>
-#include "MessageLevel.h"
-#include "multimc_logic_export.h"
-
-/*
- * This is a basic process.
- * It has line-based logging support and hides some of the nasty bits.
- */
-class MULTIMC_LOGIC_EXPORT LoggedProcess : public QProcess
-{
-Q_OBJECT
-public:
- enum State
- {
- NotRunning,
- Starting,
- FailedToStart,
- Running,
- Finished,
- Crashed,
- Aborted
- };
-
-public:
- explicit LoggedProcess(QObject* parent = 0);
- virtual ~LoggedProcess();
-
- State state() const;
- int exitCode() const;
- qint64 processId() const;
-
- void setDetachable(bool detachable);
-
-signals:
- void log(QStringList lines, MessageLevel::Enum level);
- void stateChanged(LoggedProcess::State state);
-
-public slots:
- /**
- * @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);
-
-private:
- 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;
-};
diff --git a/api/logic/MMCStrings.cpp b/api/logic/MMCStrings.cpp
deleted file mode 100644
index dc91c8d6..00000000
--- a/api/logic/MMCStrings.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "MMCStrings.h"
-
-/// 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();
-}
-
-/// 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);
-
- 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);
-}
diff --git a/api/logic/MMCStrings.h b/api/logic/MMCStrings.h
deleted file mode 100644
index 493ba3d2..00000000
--- a/api/logic/MMCStrings.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-#include <QString>
-
-#include "multimc_logic_export.h"
-
-namespace Strings
-{
- int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
-}
diff --git a/api/logic/MMCZip.cpp b/api/logic/MMCZip.cpp
deleted file mode 100644
index b25c61e7..00000000
--- a/api/logic/MMCZip.cpp
+++ /dev/null
@@ -1,312 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <quazip.h>
-#include <quazipdir.h>
-#include <quazipfile.h>
-#include <JlCompress.h>
-#include "MMCZip.h"
-#include "FileSystem.h"
-
-#include <QDebug>
-
-// 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;
-}
-
-// 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);
- 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;
-}
-
-// 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();
-}
-
-// 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();
-}
-
-
-// ours
-nonstd::optional<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;
- auto numEntries = zip->getEntriesCount();
- if(numEntries < 0) {
- qWarning() << "Failed to enumerate files in archive";
- return nonstd::nullopt;
- }
- else if(numEntries == 0) {
- qDebug() << "Extracting empty archives seems odd...";
- return extracted;
- }
- else if (!zip->goToFirstFile())
- {
- qWarning() << "Failed to seek to first file in zip";
- return nonstd::nullopt;
- }
-
- 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 nonstd::nullopt;
- }
- extracted.append(absFilePath);
- qDebug() << "Extracted file" << name;
- } while (zip->goToNextFile());
- return extracted;
-}
-
-// ours
-bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &target)
-{
- return JlCompress::extractFile(zip, file, target);
-}
-
-// ours
-nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
-{
- QuaZip zip(fileCompressed);
- if (!zip.open(QuaZip::mdUnzip))
- {
- // check if this is a minimum size empty zip file...
- QFileInfo fileInfo(fileCompressed);
- if(fileInfo.size() == 22) {
- return QStringList();
- }
- qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
- return nonstd::nullopt;
- }
- return MMCZip::extractSubDir(&zip, "", dir);
-}
-
-// ours
-nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir)
-{
- QuaZip zip(fileCompressed);
- if (!zip.open(QuaZip::mdUnzip))
- {
- // check if this is a minimum size empty zip file...
- QFileInfo fileInfo(fileCompressed);
- if(fileInfo.size() == 22) {
- return QStringList();
- }
- qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
- return nonstd::nullopt;
- }
- return MMCZip::extractSubDir(&zip, subdir, dir);
-}
-
-// ours
-bool MMCZip::extractFile(QString fileCompressed, QString file, QString target)
-{
- QuaZip zip(fileCompressed);
- if (!zip.open(QuaZip::mdUnzip))
- {
- // check if this is a minimum size empty zip file...
- QFileInfo fileInfo(fileCompressed);
- if(fileInfo.size() == 22) {
- return true;
- }
- qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
- return false;
- }
- return MMCZip::extractRelFile(&zip, file, target);
-}
diff --git a/api/logic/MMCZip.h b/api/logic/MMCZip.h
deleted file mode 100644
index 98d9cd5b..00000000
--- a/api/logic/MMCZip.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QString>
-#include <QFileInfo>
-#include <QSet>
-#include "minecraft/mod/Mod.h"
-#include <functional>
-
-#include "multimc_logic_export.h"
-
-#include <JlCompress.h>
-#include <nonstd/optional>
-
-namespace MMCZip
-{
-
- /**
- * Merge two zip files, using a filter function
- */
- bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
- const JlCompress::FilterFunction filter = nullptr);
-
- /**
- * take a source jar, add mods to it, resulting in target jar
- */
- bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
-
- /**
- * Find a single file in archive by file name (not path)
- *
- * \return the path prefix where the file is
- */
- QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
-
- /**
- * Find a multiple files of the same name in archive by file name
- * If a file is found in a path, no deeper paths are searched
- *
- * \return true if anything was found
- */
- bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
-
- /**
- * Extract a subdirectory from an archive
- */
- nonstd::optional<QStringList> MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
-
- bool MULTIMC_LOGIC_EXPORT extractRelFile(QuaZip *zip, const QString & file, 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.
- */
- nonstd::optional<QStringList> MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir);
-
- /**
- * Extract a subdirectory from an archive
- *
- * \param fileCompressed The name of the archive.
- * \param subdir The directory within the archive to extract
- * \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.
- */
- nonstd::optional<QStringList> MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString subdir, QString dir);
-
- /**
- * Extract a single file from an archive into a directory
- *
- * \param fileCompressed The name of the archive.
- * \param file The file within the archive to extract
- * \param dir The directory to extract to, the current directory if left empty.
- * \return true for success or false for failure
- */
- bool MULTIMC_LOGIC_EXPORT extractFile(QString fileCompressed, QString file, QString dir);
-
-}
diff --git a/api/logic/MessageLevel.cpp b/api/logic/MessageLevel.cpp
deleted file mode 100644
index 0a2cd23d..00000000
--- a/api/logic/MessageLevel.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "MessageLevel.h"
-
-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;
-}
-
-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;
-}
diff --git a/api/logic/MessageLevel.h b/api/logic/MessageLevel.h
deleted file mode 100644
index f19048e3..00000000
--- a/api/logic/MessageLevel.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <QString>
-
-/**
- * @brief the MessageLevel Enum
- * defines what level a log message is
- */
-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 */
-};
-MessageLevel::Enum getLevel(const QString &levelName);
-
-/* Get message level from a line. Line is modified if it was successful. */
-MessageLevel::Enum fromLine(QString &line);
-}
diff --git a/api/logic/NullInstance.h b/api/logic/NullInstance.h
deleted file mode 100644
index 94ed6c3a..00000000
--- a/api/logic/NullInstance.h
+++ /dev/null
@@ -1,76 +0,0 @@
-#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() {};
- 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, MinecraftServerTargetPtr) 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, MinecraftServerTargetPtr serverToJoin) override
- {
- QStringList out;
- out << "Null instance - placeholder.";
- return out;
- }
-};
diff --git a/api/logic/ProblemProvider.h b/api/logic/ProblemProvider.h
deleted file mode 100644
index 33c9d364..00000000
--- a/api/logic/ProblemProvider.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-#include "multimc_logic_export.h"
-
-enum class ProblemSeverity
-{
- None,
- Warning,
- Error
-};
-
-struct PatchProblem
-{
- 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;
-};
-
-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});
- }
-
-private:
- QList<PatchProblem> m_problems;
- ProblemSeverity m_problemSeverity = ProblemSeverity::None;
-};
diff --git a/api/logic/QObjectPtr.h b/api/logic/QObjectPtr.h
deleted file mode 100644
index 0ff51136..00000000
--- a/api/logic/QObjectPtr.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#pragma once
-
-#include <functional>
-#include <memory>
-#include <QObject>
-
-namespace details
-{
-struct DeleteQObjectLater
-{
- void operator()(QObject *obj) const
- {
- obj->deleteLater();
- }
-};
-}
-/**
- * A unique pointer class with unique pointer semantics intended for derivates of QObject
- * Calls deleteLater() instead of destroying the contained object immediately
- */
-template<typename T> using unique_qobject_ptr = std::unique_ptr<T, details::DeleteQObjectLater>;
-
-/**
- * A shared pointer class with shared pointer semantics intended for derivates of QObject
- * Calls deleteLater() instead of destroying the contained object immediately
- */
-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();
- }
-
-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;
- }
-
-private:
- std::shared_ptr <T> m_ptr;
-};
diff --git a/api/logic/RWStorage.h b/api/logic/RWStorage.h
deleted file mode 100644
index 3028388e..00000000
--- a/api/logic/RWStorage.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#pragma once
-#include <QWriteLocker>
-#include <QReadLocker>
-#include <QMap>
-#include <QSet>
-
-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();
- }
-private:
- QReadWriteLock lock;
- QMap<K, V> cache;
- QSet<K> stale_entries;
-};
diff --git a/api/logic/RecursiveFileSystemWatcher.cpp b/api/logic/RecursiveFileSystemWatcher.cpp
deleted file mode 100644
index b7417cdf..00000000
--- a/api/logic/RecursiveFileSystemWatcher.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-#include "RecursiveFileSystemWatcher.h"
-
-#include <QRegularExpression>
-#include <QDebug>
-
-RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent)
- : QObject(parent), m_watcher(new QFileSystemWatcher(this))
-{
- 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();
- }
-}
-void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles)
-{
- 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;
-}
-void RecursiveFileSystemWatcher::disable()
-{
- 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();
- }
-}
-
-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());
- }
- }
-}
-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;
-}
-
-void RecursiveFileSystemWatcher::fileChange(const QString &path)
-{
- emit fileChanged(path);
-}
-void RecursiveFileSystemWatcher::directoryChange(const QString &path)
-{
- setFiles(scanRecursive(m_root));
-}
diff --git a/api/logic/RecursiveFileSystemWatcher.h b/api/logic/RecursiveFileSystemWatcher.h
deleted file mode 100644
index c9c39f49..00000000
--- a/api/logic/RecursiveFileSystemWatcher.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#pragma once
-
-#include <QFileSystemWatcher>
-#include <QDir>
-#include "pathmatcher/IPathMatcher.h"
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT RecursiveFileSystemWatcher : public QObject
-{
- 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;
- }
-
-signals:
- void filesChanged();
- void fileChanged(const QString &path);
-
-public slots:
- void enable();
- void disable();
-
-private:
- QDir m_root;
- bool m_watchFiles = false;
- bool m_isEnabled = false;
- IPathMatcher::Ptr m_matcher;
-
- QFileSystemWatcher *m_watcher;
-
- QStringList m_files;
- void setFiles(const QStringList &files);
-
- void addFilesToWatcherRecursive(const QDir &dir);
- QStringList scanRecursive(const QDir &dir);
-
-private slots:
- void fileChange(const QString &path);
- void directoryChange(const QString &path);
-};
diff --git a/api/logic/SeparatorPrefixTree.h b/api/logic/SeparatorPrefixTree.h
deleted file mode 100644
index 7a841cb7..00000000
--- a/api/logic/SeparatorPrefixTree.h
+++ /dev/null
@@ -1,298 +0,0 @@
-#pragma once
-#include <QString>
-#include <QMap>
-#include <QStringList>
-
-template <char Tseparator>
-class SeparatorPrefixTree
-{
-public:
- SeparatorPrefixTree(QStringList paths)
- {
- insert(paths);
- }
-
- SeparatorPrefixTree(bool contained = false)
- {
- m_contained = contained;
- }
-
- 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));
- }
- }
-
- /// 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));
- }
- }
-
- /// 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));
- }
- }
-
- /// 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 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;
- }
-
- /// 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;
- }
-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;
- }
-
-private:
- QMap<QString,SeparatorPrefixTree<Tseparator>> children;
- bool m_contained = false;
-};
diff --git a/api/logic/Usable.h b/api/logic/Usable.h
deleted file mode 100644
index 83dd083d..00000000
--- a/api/logic/Usable.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#pragma once
-
-#include <cstddef>
-#include <memory>
-
-class Usable;
-
-/**
- * Base class for things that can be used by multiple other things and we want to track the use count.
- *
- * @see UseLock
- */
-class Usable
-{
- friend class UseLock;
-public:
- std::size_t useCount()
- {
- return m_useCount;
- }
- bool isInUse()
- {
- return m_useCount > 0;
- }
-protected:
- virtual void decrementUses()
- {
- m_useCount--;
- }
- virtual void incrementUses()
- {
- m_useCount++;
- }
-private:
- std::size_t m_useCount = 0;
-};
-
-/**
- * Lock class to use for keeping track of uses of other things derived from Usable
- *
- * @see Usable
- */
-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();
- }
-private:
- std::shared_ptr<Usable> m_usable;
-};
diff --git a/api/logic/Version.cpp b/api/logic/Version.cpp
deleted file mode 100644
index 6392a50f..00000000
--- a/api/logic/Version.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "Version.h"
-
-#include <QStringList>
-#include <QUrl>
-#include <QRegularExpression>
-#include <QRegularExpressionMatch>
-
-Version::Version(const QString &str) : m_string(str)
-{
- 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;
- }
- }
-
- return false;
-}
-bool Version::operator<=(const Version &other) const
-{
- 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;
- }
- }
-
- return false;
-}
-bool Version::operator>=(const Version &other) const
-{
- 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;
- }
- }
-
- return true;
-}
-bool Version::operator!=(const Version &other) const
-{
- return !operator==(other);
-}
-
-void Version::parse()
-{
- m_sections.clear();
-
- // 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));
- }
-}
diff --git a/api/logic/Version.h b/api/logic/Version.h
deleted file mode 100644
index c5d93081..00000000
--- a/api/logic/Version.h
+++ /dev/null
@@ -1,107 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QList>
-
-#include "multimc_logic_export.h"
-
-class QUrl;
-
-class MULTIMC_LOGIC_EXPORT 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;
-
- 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;
-
- 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();
-};
diff --git a/api/logic/Version_test.cpp b/api/logic/Version_test.cpp
deleted file mode 100644
index b2d657a6..00000000
--- a/api/logic/Version_test.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QTest>
-
-#include "TestUtil.h"
-#include <Version.h>
-
-class ModUtilsTest : public QObject
-{
- 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("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;
- }
-
-private slots:
- void initTestCase()
- {
-
- }
- void cleanupTestCase()
- {
-
- }
-
- 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);
-
- QCOMPARE(v1 < v2, lessThan);
- QCOMPARE(v1 > v2, !lessThan && !equal);
- QCOMPARE(v1 == v2, equal);
- }
-};
-
-QTEST_GUILESS_MAIN(ModUtilsTest)
-
-#include "Version_test.moc"
diff --git a/api/logic/WatchLock.h b/api/logic/WatchLock.h
deleted file mode 100644
index 3e08b413..00000000
--- a/api/logic/WatchLock.h
+++ /dev/null
@@ -1,20 +0,0 @@
-
-#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.cpp b/api/logic/icons/IIconList.cpp
deleted file mode 100644
index b3a8fb43..00000000
--- a/api/logic/icons/IIconList.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "IIconList.h"
-
-// blargh
-IIconList::~IIconList()
-{
-}
-
diff --git a/api/logic/icons/IIconList.h b/api/logic/icons/IIconList.h
deleted file mode 100644
index 9a3fe022..00000000
--- a/api/logic/icons/IIconList.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QStringList>
-#include "multimc_logic_export.h"
-
-enum IconType : unsigned
-{
- Builtin,
- Transient,
- FileBased,
- ICONS_TOTAL,
- ToBeDeleted
-};
-
-class MULTIMC_LOGIC_EXPORT IIconList
-{
-public:
- virtual ~IIconList();
- virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0;
- virtual bool deleteIcon(const QString &key) = 0;
- virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
- virtual bool iconFileExists(const QString &key) const = 0;
- virtual void installIcons(const QStringList &iconFiles) = 0;
- virtual void installIcon(const QString &file, const QString &name) = 0;
-};
diff --git a/api/logic/icons/IconUtils.cpp b/api/logic/icons/IconUtils.cpp
deleted file mode 100644
index bf530c16..00000000
--- a/api/logic/icons/IconUtils.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#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
deleted file mode 100644
index ce236946..00000000
--- a/api/logic/icons/IconUtils.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#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
deleted file mode 100644
index d78d6505..00000000
--- a/api/logic/java/JavaChecker.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-#include "JavaChecker.h"
-#include "JavaUtils.h"
-#include <FileSystem.h>
-#include <Commandline.h>
-#include <QFile>
-#include <QProcess>
-#include <QMap>
-#include <QCoreApplication>
-#include <QDebug>
-
-#include "Env.h"
-
-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();
-}
-
-void JavaChecker::stdoutReady()
-{
- 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;
-}
-
-void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
-{
- 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") || !results.contains("java.vendor") || !success)
- {
- result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
- emit checkFinished(result);
- return;
- }
-
- auto os_arch = results["os.arch"];
- auto java_version = results["java.version"];
- auto java_vendor = results["java.vendor"];
- 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;
- result.javaVendor = java_vendor;
- 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;
- }
-}
-
-void JavaChecker::timeout()
-{
- // 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
deleted file mode 100644
index 0a96249a..00000000
--- a/api/logic/java/JavaChecker.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#pragma once
-#include <QProcess>
-#include <QTimer>
-#include <memory>
-
-#include "QObjectPtr.h"
-
-#include "multimc_logic_export.h"
-
-#include "JavaVersion.h"
-
-class JavaChecker;
-
-struct MULTIMC_LOGIC_EXPORT JavaCheckResult
-{
- QString path;
- QString mojangPlatform;
- QString realPlatform;
- JavaVersion javaVersion;
- QString javaVendor;
- QString outLog;
- QString errorLog;
- bool is_64bit = false;
- int id;
- enum class Validity
- {
- Errored,
- ReturnedInvalidData,
- Valid
- } validity = Validity::Errored;
-};
-
-typedef shared_qobject_ptr<QProcess> QProcessPtr;
-typedef shared_qobject_ptr<JavaChecker> JavaCheckerPtr;
-class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject
-{
- Q_OBJECT
-public:
- 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;
-
-signals:
- void checkFinished(JavaCheckResult result);
-private:
- 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();
-};
diff --git a/api/logic/java/JavaCheckerJob.cpp b/api/logic/java/JavaCheckerJob.cpp
deleted file mode 100644
index 67d70066..00000000
--- a/api/logic/java/JavaCheckerJob.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "JavaCheckerJob.h"
-
-#include <QDebug>
-
-void JavaCheckerJob::partFinished(JavaCheckResult result)
-{
- num_finished++;
- qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/"
- << javacheckers.size();
- setProgress(num_finished, javacheckers.size());
-
- javaresults.replace(result.id, result);
-
- 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();
- }
-}
diff --git a/api/logic/java/JavaCheckerJob.h b/api/logic/java/JavaCheckerJob.h
deleted file mode 100644
index c0986420..00000000
--- a/api/logic/java/JavaCheckerJob.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QtNetwork>
-#include "JavaChecker.h"
-#include "tasks/Task.h"
-
-class JavaCheckerJob;
-typedef shared_qobject_ptr<JavaCheckerJob> JavaCheckerJobPtr;
-
-// FIXME: this just seems horribly redundant
-class JavaCheckerJob : public Task
-{
- Q_OBJECT
-public:
- 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);
-
-protected:
- virtual void executeTask() override;
-
-private:
- 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
deleted file mode 100644
index 5bcf7bcb..00000000
--- a/api/logic/java/JavaInstall.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "JavaInstall.h"
-#include <MMCStrings.h>
-
-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;
-}
-
-bool JavaInstall::operator==(const JavaInstall &rhs)
-{
- return arch == rhs.arch && id == rhs.id && path == rhs.path;
-}
-
-bool JavaInstall::operator>(const JavaInstall &rhs)
-{
- return (!operator<(rhs)) && (!operator==(rhs));
-}
diff --git a/api/logic/java/JavaInstall.h b/api/logic/java/JavaInstall.h
deleted file mode 100644
index 64be40d1..00000000
--- a/api/logic/java/JavaInstall.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include "BaseVersion.h"
-#include "JavaVersion.h"
-
-struct JavaInstall : public BaseVersion
-{
- 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 typeString() const
- {
- return arch;
- }
-
- bool operator<(const JavaInstall & rhs);
- bool operator==(const JavaInstall & rhs);
- bool operator>(const JavaInstall & rhs);
-
- 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
deleted file mode 100644
index 0bded03c..00000000
--- a/api/logic/java/JavaInstallList.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QtNetwork>
-#include <QtXml>
-#include <QRegExp>
-
-#include <QDebug>
-
-#include "java/JavaInstallList.h"
-#include "java/JavaCheckerJob.h"
-#include "java/JavaUtils.h"
-#include "MMCStrings.h"
-#include "minecraft/VersionFilterData.h"
-
-JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
-{
-}
-
-shared_qobject_ptr<Task> JavaInstallList::getLoadTask()
-{
- load();
- return getCurrentTask();
-}
-
-shared_qobject_ptr<Task> JavaInstallList::getCurrentTask()
-{
- 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();
- }
-}
-
-const BaseVersionPtr JavaInstallList::at(int i) const
-{
- return m_vlist.at(i);
-}
-
-bool JavaInstallList::isLoaded()
-{
- return m_status == JavaInstallList::Status::Done;
-}
-
-int JavaInstallList::count() const
-{
- 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();
- }
-}
-
-BaseVersionList::RoleList JavaInstallList::providesRoles() const
-{
- 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();
-}
-
-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);
-}
-
-void JavaInstallList::sortVersions()
-{
- beginResetModel();
- std::sort(m_vlist.begin(), m_vlist.end(), sortJavas);
- endResetModel();
-}
-
-JavaListLoadTask::JavaListLoadTask(JavaInstallList *vlist) : Task()
-{
- m_list = vlist;
- m_currentRecommended = NULL;
-}
-
-JavaListLoadTask::~JavaListLoadTask()
-{
-}
-
-void JavaListLoadTask::executeTask()
-{
- setStatus(tr("Detecting Java installations..."));
-
- JavaUtils ju;
- QList<QString> candidate_paths = ju.FindJavaPaths();
-
- 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;
-
- auto candidate_checker = new JavaChecker();
- candidate_checker->m_path = candidate;
- candidate_checker->m_id = id;
- m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
-
- id++;
- }
-
- 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();
-}
diff --git a/api/logic/java/JavaInstallList.h b/api/logic/java/JavaInstallList.h
deleted file mode 100644
index 1785a7b6..00000000
--- a/api/logic/java/JavaInstallList.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QAbstractListModel>
-
-#include "BaseVersionList.h"
-#include "tasks/Task.h"
-
-#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
- };
-public:
- 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;
-
- QVariant data(const QModelIndex &index, int role) const override;
- RoleList providesRoles() const override;
-
-public slots:
- void updateListData(QList<BaseVersionPtr> versions) override;
-
-protected:
- void load();
- shared_qobject_ptr<Task> getCurrentTask();
-
-protected:
- Status m_status = Status::NotDone;
- shared_qobject_ptr<JavaListLoadTask> m_loadTask;
- QList<BaseVersionPtr> m_vlist;
-};
-
-class JavaListLoadTask : public Task
-{
- Q_OBJECT
-
-public:
- explicit JavaListLoadTask(JavaInstallList *vlist);
- virtual ~JavaListLoadTask();
-
- void executeTask() override;
-public slots:
- void javaCheckerFinished();
-
-protected:
- 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
deleted file mode 100644
index 4b231e6a..00000000
--- a/api/logic/java/JavaUtils.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QStringList>
-#include <QString>
-#include <QDir>
-#include <QStringList>
-
-#include <settings/Setting.h>
-
-#include <QDebug>
-#include "java/JavaUtils.h"
-#include "java/JavaInstallList.h"
-#include "FileSystem.h"
-
-#define IBUS "@im=ibus"
-
-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(':');
-}
-#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;
- }
-#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;
- }
-#endif
- // 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", "");
- }
-#endif
-
- return env;
-}
-
-JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch)
-{
- JavaInstallPtr javaVersion(new JavaInstall());
-
- javaVersion->id = id;
- javaVersion->arch = arch;
- javaVersion->path = path;
-
- return javaVersion;
-}
-
-JavaInstallPtr JavaUtils::GetDefaultJava()
-{
- JavaInstallPtr javaVersion(new JavaInstall());
-
- javaVersion->id = "java";
- javaVersion->arch = "unknown";
-#if defined(Q_OS_WIN32)
- javaVersion->path = "javaw";
-#else
- javaVersion->path = "java";
-#endif
-
- return javaVersion;
-}
-
-#if defined(Q_OS_WIN32)
-QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix)
-{
- 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);
- }
-
- 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 + subkeySuffix;
-
- 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, keyJavaDir.toStdString().c_str(), NULL, NULL, (BYTE *)value,
- &valueSz) == ERROR_MORE_DATA)
- {
- value = new char[valueSz];
- RegQueryValueEx(newKey, keyJavaDir.toStdString().c_str(), 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;
-
- // Oracle
- QList<JavaInstallPtr> JRE64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome");
- QList<JavaInstallPtr> JDK64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome");
- QList<JavaInstallPtr> JRE32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome");
- QList<JavaInstallPtr> JDK32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome");
-
- // Oracle for Java 9 and newer
- QList<JavaInstallPtr> NEWJRE64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome");
- QList<JavaInstallPtr> NEWJDK64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome");
- QList<JavaInstallPtr> NEWJRE32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome");
- QList<JavaInstallPtr> NEWJDK32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome");
-
- // AdoptOpenJDK
- QList<JavaInstallPtr> ADOPTOPENJRE32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI");
- QList<JavaInstallPtr> ADOPTOPENJRE64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI");
- QList<JavaInstallPtr> ADOPTOPENJDK32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI");
- QList<JavaInstallPtr> ADOPTOPENJDK64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI");
-
- // Microsoft
- QList<JavaInstallPtr> MICROSOFTJDK64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI");
-
- // Azul Zulu
- QList<JavaInstallPtr> ZULU64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath");
- QList<JavaInstallPtr> ZULU32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath");
-
- // BellSoft Liberica
- QList<JavaInstallPtr> LIBERICA64s = this->FindJavaFromRegistryKey(
- KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath");
- QList<JavaInstallPtr> LIBERICA32s = this->FindJavaFromRegistryKey(
- KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath");
-
- // List x64 before x86
- java_candidates.append(JRE64s);
- java_candidates.append(NEWJRE64s);
- java_candidates.append(ADOPTOPENJRE64s);
- 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(NEWJDK64s);
- java_candidates.append(ADOPTOPENJDK64s);
- java_candidates.append(MICROSOFTJDK64s);
- java_candidates.append(ZULU64s);
- java_candidates.append(LIBERICA64s);
-
- java_candidates.append(JRE32s);
- java_candidates.append(NEWJRE32s);
- java_candidates.append(ADOPTOPENJRE32s);
- 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(NEWJDK32s);
- java_candidates.append(ADOPTOPENJDK32s);
- java_candidates.append(ZULU32s);
- java_candidates.append(LIBERICA32s);
-
- 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;
-}
-
-#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");
- // manually installed JDKs in /opt
- scanJavaDir("/opt/jdk");
- scanJavaDir("/opt/jdks");
- return javas;
-}
-#else
-QList<QString> JavaUtils::FindJavaPaths()
-{
- qDebug() << "Unknown operating system build - defaulting to \"java\"";
-
- QList<QString> javas;
- javas.append(this->GetDefaultJava()->path);
-
- return javas;
-}
-#endif
diff --git a/api/logic/java/JavaUtils.h b/api/logic/java/JavaUtils.h
deleted file mode 100644
index 206acf89..00000000
--- a/api/logic/java/JavaUtils.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QStringList>
-
-#include "JavaChecker.h"
-#include "JavaInstallList.h"
-
-#ifdef Q_OS_WIN
-#include <windows.h>
-#endif
-
-#include "multimc_logic_export.h"
-
-QProcessEnvironment CleanEnviroment();
-
-class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject
-{
- Q_OBJECT
-public:
- JavaUtils();
-
- 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, QString keyJavaDir, QString subkeySuffix = "");
-#endif
-};
diff --git a/api/logic/java/JavaVersion.cpp b/api/logic/java/JavaVersion.cpp
deleted file mode 100644
index 179ccd8d..00000000
--- a/api/logic/java/JavaVersion.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-#include "JavaVersion.h"
-#include <MMCStrings.h>
-
-#include <QRegularExpression>
-#include <QString>
-
-JavaVersion & JavaVersion::operator=(const QString & 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();
- };
-
- 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;
-}
-
-JavaVersion::JavaVersion(const QString &rhs)
-{
- operator=(rhs);
-}
-
-QString JavaVersion::toString()
-{
- return m_string;
-}
-
-bool JavaVersion::requiresPermGen()
-{
- 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;
-
- // 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;
-
- // 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;
-}
-
-bool JavaVersion::operator>(const JavaVersion &rhs)
-{
- return (!operator<(rhs)) && (!operator==(rhs));
-}
diff --git a/api/logic/java/JavaVersion.h b/api/logic/java/JavaVersion.h
deleted file mode 100644
index 8589c21a..00000000
--- a/api/logic/java/JavaVersion.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#pragma once
-
-#include "multimc_logic_export.h"
-#include <QString>
-
-// NOTE: apparently the GNU C library pollutes the global namespace with these... undef them.
-#ifdef major
- #undef major
-#endif
-#ifdef minor
- #undef minor
-#endif
-
-class MULTIMC_LOGIC_EXPORT JavaVersion
-{
- 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;
- }
-private:
- 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
deleted file mode 100644
index 10ae13a7..00000000
--- a/api/logic/java/JavaVersion_test.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "java/JavaVersion.h"
-
-class JavaVersionTest : public QObject
-{
- 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");
-
- 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);
-
- 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");
-
- // 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)
-
-#include "JavaVersion_test.moc"
diff --git a/api/logic/java/launch/CheckJava.cpp b/api/logic/java/launch/CheckJava.cpp
deleted file mode 100644
index f58602f0..00000000
--- a/api/logic/java/launch/CheckJava.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "CheckJava.h"
-#include <launch/LaunchTask.h>
-#include <FileSystem.h>
-#include <QStandardPaths>
-#include <QFileInfo>
-#include <sys.h>
-
-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 realJavaPath = QStandardPaths::findExecutable(m_javaPath);
- if (realJavaPath.isEmpty())
- {
- if (perInstance)
- {
- emit logLine(
- QString("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(QString("The java binary \"%1\" couldn't be found. Please set up java in "
- "the settings.").arg(m_javaPath),
- MessageLevel::Warning);
- }
- emitFailed(QString("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();
- auto storedVendor = settings->get("JavaVendor").toString();
- m_javaUnixTime = javaUnixTime;
- // if timestamps are not the same, or something is missing, check!
- if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0 || storedVendor.size() == 0)
- {
- m_JavaChecker = new JavaChecker();
- emit logLine(QString("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();
- auto vendorString = instance->settings()->get("JavaVendor").toString();
- printJavaInfo(verString, archString, vendorString);
- }
- emitSucceeded();
-}
-
-void CheckJava::checkJavaFinished(JavaCheckResult result)
-{
- switch (result.validity)
- {
- case JavaCheckResult::Validity::Errored:
- {
- // Error message displayed if java can't start
- emit logLine(QString("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(QString("Could not start java!"));
- return;
- }
- case JavaCheckResult::Validity::ReturnedInvalidData:
- {
- emit logLine(QString("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, result.javaVendor);
- instance->settings()->set("JavaVersion", result.javaVersion.toString());
- instance->settings()->set("JavaArchitecture", result.mojangPlatform);
- instance->settings()->set("JavaVendor", result.javaVendor);
- instance->settings()->set("JavaTimestamp", m_javaUnixTime);
- emitSucceeded();
- return;
- }
- }
-}
-
-void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString & vendor)
-{
- emit logLine(QString("Java is version %1, using %2-bit architecture, from %3.\n\n").arg(version, architecture, vendor), 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(QString("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(QString("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
deleted file mode 100644
index 68cd618b..00000000
--- a/api/logic/java/launch/CheckJava.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-#include <java/JavaChecker.h>
-
-class CheckJava: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit CheckJava(LaunchTask *parent) :LaunchStep(parent){};
- virtual ~CheckJava() {};
-
- virtual void executeTask();
- virtual bool canAbort() const
- {
- return false;
- }
-private slots:
- void checkJavaFinished(JavaCheckResult result);
-
-private:
- void printJavaInfo(const QString & version, const QString & architecture, const QString & vendor);
- void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
-
-private:
- QString m_javaPath;
- qlonglong m_javaUnixTime;
- JavaCheckerPtr m_JavaChecker;
-};
diff --git a/api/logic/launch/LaunchStep.cpp b/api/logic/launch/LaunchStep.cpp
deleted file mode 100644
index d6bb6e88..00000000
--- a/api/logic/launch/LaunchStep.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LaunchStep.h"
-#include "LaunchTask.h"
-
-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);
-}
diff --git a/api/logic/launch/LaunchStep.h b/api/logic/launch/LaunchStep.h
deleted file mode 100644
index 3939f960..00000000
--- a/api/logic/launch/LaunchStep.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "tasks/Task.h"
-#include "MessageLevel.h"
-
-#include <QStringList>
-
-class LaunchTask;
-class LaunchStep: public Task
-{
- Q_OBJECT
-public: /* methods */
- explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent)
- {
- bind(parent);
- };
- virtual ~LaunchStep() {};
-
-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();
-
-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() {};
-
-protected: /* data */
- LaunchTask *m_parent;
-};
diff --git a/api/logic/launch/LaunchTask.cpp b/api/logic/launch/LaunchTask.cpp
deleted file mode 100644
index e6f6bbac..00000000
--- a/api/logic/launch/LaunchTask.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "launch/LaunchTask.h"
-#include "MessageLevel.h"
-#include "MMCStrings.h"
-#include "java/JavaChecker.h"
-#include "tasks/Task.h"
-#include <QDebug>
-#include <QDir>
-#include <QEventLoop>
-#include <QRegularExpression>
-#include <QCoreApplication>
-#include <QStandardPaths>
-#include <assert.h>
-
-void LaunchTask::init()
-{
- m_instance->setRunning(true);
-}
-
-shared_qobject_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
-{
- shared_qobject_ptr<LaunchTask> proc(new LaunchTask(inst));
- proc->init();
- return proc;
-}
-
-LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance)
-{
-}
-
-void LaunchTask::appendStep(shared_qobject_ptr<LaunchStep> step)
-{
- m_steps.append(step);
-}
-
-void LaunchTask::prependStep(shared_qobject_ptr<LaunchStep> step)
-{
- m_steps.prepend(step);
-}
-
-void LaunchTask::executeTask()
-{
- m_instance->setCrashed(false);
- if(!m_steps.size())
- {
- state = LaunchTask::Finished;
- emitSucceeded();
- }
- state = LaunchTask::Running;
- onStepFinished();
-}
-
-void LaunchTask::onReadyForLaunch()
-{
- state = LaunchTask::Waiting;
- emit readyForLaunch();
-}
-
-void LaunchTask::onStepFinished()
-{
- // 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());
- }
-}
-
-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);
- }
-}
-
-void LaunchTask::onProgressReportingRequested()
-{
- state = LaunchTask::Waiting;
- emit requestProgress(m_steps[currentStep].get());
-}
-
-void LaunchTask::setCensorFilter(QMap<QString, QString> 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;
-}
-
-void LaunchTask::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;
-}
-
-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;
-}
-
-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;
-}
-
-void LaunchTask::onLogLines(const QStringList &lines, MessageLevel::Enum 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 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);
-
- auto &model = *getLogModel();
- model.append(level, line);
-}
-
-void LaunchTask::emitSucceeded()
-{
- m_instance->setRunning(false);
- Task::emitSucceeded();
-}
-
-void LaunchTask::emitFailed(QString 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;
-}
-
diff --git a/api/logic/launch/LaunchTask.h b/api/logic/launch/LaunchTask.h
deleted file mode 100644
index 2be59c83..00000000
--- a/api/logic/launch/LaunchTask.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <QProcess>
-#include <QObjectPtr.h>
-#include "LogModel.h"
-#include "BaseInstance.h"
-#include "MessageLevel.h"
-#include "LoggedProcess.h"
-#include "LaunchStep.h"
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT LaunchTask: public Task
-{
- Q_OBJECT
-protected:
- explicit LaunchTask(InstancePtr instance);
- void init();
-
-public:
- enum State
- {
- NotStarted,
- Running,
- Waiting,
- Failed,
- Aborted,
- Finished
- };
-
-public: /* methods */
- static shared_qobject_ptr<LaunchTask> create(InstancePtr inst);
- virtual ~LaunchTask() {};
-
- 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;
- }
-
- void setPid(qint64 pid)
- {
- m_pid = pid;
- }
-
- qint64 pid()
- {
- return m_pid;
- }
-
- /**
- * @brief prepare the process for launch (for multi-stage launch)
- */
- virtual void executeTask() override;
-
- /**
- * @brief launch the armed instance
- */
- void proceed();
-
- /**
- * @brief abort launch
- */
- bool abort() override;
-
- bool canAbort() const override;
-
- shared_qobject_ptr<LogModel> getLogModel();
-
-public:
- QString substituteVariables(const QString &cmd) const;
- QString censorPrivateInfo(QString in);
-
-protected: /* methods */
- virtual void emitFailed(QString reason) override;
- virtual void emitSucceeded() override;
-
-signals:
- /**
- * @brief emitted when the launch preparations are done
- */
- void readyForLaunch();
-
- void requestProgress(Task *task);
-
- 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();
-
-private: /*methods */
- void finalizeSteps(bool successful, const QString & error);
-
-protected: /* data */
- 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
deleted file mode 100644
index 92f9487a..00000000
--- a/api/logic/launch/LogModel.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-#include "LogModel.h"
-
-LogModel::LogModel(QObject *parent):QAbstractListModel(parent)
-{
- m_content.resize(m_maxLines);
-}
-
-int LogModel::rowCount(const QModelIndex &parent) const
-{
- if (parent.isValid())
- return 0;
-
- 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();
-}
-
-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();
-}
-
-void LogModel::suspend(bool suspend)
-{
- m_suspended = suspend;
-}
-
-bool LogModel::suspended()
-{
- return m_suspended;
-}
-
-void LogModel::clear()
-{
- 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;
-}
-
-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;
-}
-
-int LogModel::getMaxLines()
-{
- return m_maxLines;
-}
-
-void LogModel::setStopOnOverflow(bool stop)
-{
- m_stopOnOverflow = stop;
-}
-
-void LogModel::setOverflowMessage(const QString& overflowMessage)
-{
- m_overflowMessage = overflowMessage;
-}
-
-void LogModel::setLineWrap(bool state)
-{
- if(m_lineWrap != state)
- {
- m_lineWrap = state;
- }
-}
-
-bool LogModel::wrapLines() const
-{
- return m_lineWrap;
-}
diff --git a/api/logic/launch/LogModel.h b/api/logic/launch/LogModel.h
deleted file mode 100644
index bccceaef..00000000
--- a/api/logic/launch/LogModel.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#pragma once
-
-#include <QAbstractListModel>
-#include <QString>
-#include "MessageLevel.h"
-
-#include <multimc_logic_export.h>
-
-class MULTIMC_LOGIC_EXPORT LogModel : public QAbstractListModel
-{
- Q_OBJECT
-public:
- explicit LogModel(QObject *parent = 0);
-
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
- QVariant data(const QModelIndex &index, int role) const;
-
- void append(MessageLevel::Enum, QString line);
- void clear();
-
- void suspend(bool suspend);
- bool suspended();
-
- QString toPlainText();
-
- int getMaxLines();
- void setMaxLines(int maxLines);
- void setStopOnOverflow(bool stop);
- void setOverflowMessage(const QString & overflowMessage);
-
- void setLineWrap(bool state);
- bool wrapLines() const;
-
- enum Roles
- {
- LevelRole = Qt::UserRole
- };
-
-private /* types */:
- 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;
-
-private:
- Q_DISABLE_COPY(LogModel)
-};
diff --git a/api/logic/launch/steps/LookupServerAddress.cpp b/api/logic/launch/steps/LookupServerAddress.cpp
deleted file mode 100644
index de56c28a..00000000
--- a/api/logic/launch/steps/LookupServerAddress.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#include "LookupServerAddress.h"
-
-#include <launch/LaunchTask.h>
-
-LookupServerAddress::LookupServerAddress(LaunchTask *parent) :
- LaunchStep(parent), m_dnsLookup(new QDnsLookup(this))
-{
- connect(m_dnsLookup, &QDnsLookup::finished, this, &LookupServerAddress::on_dnsLookupFinished);
-
- m_dnsLookup->setType(QDnsLookup::SRV);
-}
-
-void LookupServerAddress::setLookupAddress(const QString &lookupAddress)
-{
- m_lookupAddress = lookupAddress;
- m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress));
-}
-
-void LookupServerAddress::setOutputAddressPtr(MinecraftServerTargetPtr output)
-{
- m_output = std::move(output);
-}
-
-bool LookupServerAddress::abort()
-{
- m_dnsLookup->abort();
- emitFailed("Aborted");
- return true;
-}
-
-void LookupServerAddress::executeTask()
-{
- m_dnsLookup->lookup();
-}
-
-void LookupServerAddress::on_dnsLookupFinished()
-{
- if (isFinished())
- {
- // Aborted
- return;
- }
-
- if (m_dnsLookup->error() != QDnsLookup::NoError)
- {
- emit logLine(QString("Failed to resolve server address (this is NOT an error!) %1: %2\n")
- .arg(m_dnsLookup->name(), m_dnsLookup->errorString()), MessageLevel::MultiMC);
- resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch
- // and leave it up to minecraft to fail (or maybe not) when connecting
- return;
- }
-
- const auto records = m_dnsLookup->serviceRecords();
- if (records.empty())
- {
- emit logLine(
- QString("Failed to resolve server address %1: the DNS lookup succeeded, but no records were returned.\n")
- .arg(m_dnsLookup->name()), MessageLevel::Warning);
- resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch
- // and leave it up to minecraft to fail (or maybe not) when connecting
- return;
- }
-
- const auto &firstRecord = records.at(0);
- quint16 port = firstRecord.port();
-
- emit logLine(QString("Resolved server address %1 to %2 with port %3\n").arg(
- m_dnsLookup->name(), firstRecord.target(), QString::number(port)),MessageLevel::MultiMC);
- resolve(firstRecord.target(), port);
-}
-
-void LookupServerAddress::resolve(const QString &address, quint16 port)
-{
- m_output->address = address;
- m_output->port = port;
-
- emitSucceeded();
- m_dnsLookup->deleteLater();
-}
diff --git a/api/logic/launch/steps/LookupServerAddress.h b/api/logic/launch/steps/LookupServerAddress.h
deleted file mode 100644
index 5a5c3de1..00000000
--- a/api/logic/launch/steps/LookupServerAddress.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <QObjectPtr.h>
-#include <QDnsLookup>
-
-#include "minecraft/launch/MinecraftServerTarget.h"
-
-class LookupServerAddress: public LaunchStep {
-Q_OBJECT
-public:
- explicit LookupServerAddress(LaunchTask *parent);
- virtual ~LookupServerAddress() {};
-
- virtual void executeTask();
- virtual bool abort();
- virtual bool canAbort() const
- {
- return true;
- }
-
- void setLookupAddress(const QString &lookupAddress);
- void setOutputAddressPtr(MinecraftServerTargetPtr output);
-
-private slots:
- void on_dnsLookupFinished();
-
-private:
- void resolve(const QString &address, quint16 port);
-
- QDnsLookup *m_dnsLookup;
- QString m_lookupAddress;
- MinecraftServerTargetPtr m_output;
-};
diff --git a/api/logic/launch/steps/PostLaunchCommand.cpp b/api/logic/launch/steps/PostLaunchCommand.cpp
deleted file mode 100644
index d48d03d1..00000000
--- a/api/logic/launch/steps/PostLaunchCommand.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "PostLaunchCommand.h"
-#include <launch/LaunchTask.h>
-
-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);
-}
-
-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);
-}
-
-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;
- }
-}
-
-void PostLaunchCommand::setWorkingDirectory(const QString &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;
-}
diff --git a/api/logic/launch/steps/PostLaunchCommand.h b/api/logic/launch/steps/PostLaunchCommand.h
deleted file mode 100644
index ab4c494f..00000000
--- a/api/logic/launch/steps/PostLaunchCommand.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-
-class PostLaunchCommand: public LaunchStep
-{
- Q_OBJECT
-public:
- 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);
-
-private:
- LoggedProcess m_process;
- QString m_command;
-};
diff --git a/api/logic/launch/steps/PreLaunchCommand.cpp b/api/logic/launch/steps/PreLaunchCommand.cpp
deleted file mode 100644
index 20e089e2..00000000
--- a/api/logic/launch/steps/PreLaunchCommand.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "PreLaunchCommand.h"
-#include <launch/LaunchTask.h>
-
-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);
-}
-
-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);
-}
-
-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;
- }
-}
-
-void PreLaunchCommand::setWorkingDirectory(const QString &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;
-}
diff --git a/api/logic/launch/steps/PreLaunchCommand.h b/api/logic/launch/steps/PreLaunchCommand.h
deleted file mode 100644
index dc069f71..00000000
--- a/api/logic/launch/steps/PreLaunchCommand.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "launch/LaunchStep.h"
-#include "LoggedProcess.h"
-
-class PreLaunchCommand: public LaunchStep
-{
- Q_OBJECT
-public:
- 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);
-
-private:
- LoggedProcess m_process;
- QString m_command;
-};
diff --git a/api/logic/launch/steps/TextPrint.cpp b/api/logic/launch/steps/TextPrint.cpp
deleted file mode 100644
index 0c1f320c..00000000
--- a/api/logic/launch/steps/TextPrint.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "TextPrint.h"
-
-TextPrint::TextPrint(LaunchTask * parent, const QStringList &lines, MessageLevel::Enum level) : LaunchStep(parent)
-{
- 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;
-}
-
-void TextPrint::executeTask()
-{
- emit logLines(m_lines, m_level);
- emitSucceeded();
-}
-
-bool TextPrint::canAbort() const
-{
- return true;
-}
-
-bool TextPrint::abort()
-{
- emitFailed("Aborted.");
- return true;
-}
diff --git a/api/logic/launch/steps/TextPrint.h b/api/logic/launch/steps/TextPrint.h
deleted file mode 100644
index 2937c64a..00000000
--- a/api/logic/launch/steps/TextPrint.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-#include <java/JavaChecker.h>
-
-#include "multimc_logic_export.h"
-
-/*
- * FIXME: maybe do not export
- */
-
-class MULTIMC_LOGIC_EXPORT TextPrint: public LaunchStep
-{
- 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(){};
-
- virtual void executeTask();
- virtual bool canAbort() const;
- virtual bool abort();
-
-private:
- QStringList m_lines;
- MessageLevel::Enum m_level;
-};
diff --git a/api/logic/launch/steps/Update.cpp b/api/logic/launch/steps/Update.cpp
deleted file mode 100644
index 28bd153d..00000000
--- a/api/logic/launch/steps/Update.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Update.h"
-#include <launch/LaunchTask.h>
-
-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();
-}
-
-void Update::proceed()
-{
- 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);
- }
-}
-
-bool Update::canAbort() const
-{
- 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;
-}
diff --git a/api/logic/launch/steps/Update.h b/api/logic/launch/steps/Update.h
deleted file mode 100644
index 0c9d91e0..00000000
--- a/api/logic/launch/steps/Update.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <QObjectPtr.h>
-#include <LoggedProcess.h>
-#include <java/JavaChecker.h>
-#include <net/Mode.h>
-
-// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
-class Update: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {};
- virtual ~Update() {};
-
- void executeTask() override;
- bool canAbort() const override;
- void proceed() override;
-public slots:
- bool abort() override;
-
-private slots:
- void updateFinished();
-
-private:
- 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
deleted file mode 100644
index 5ff7a59a..00000000
--- a/api/logic/meta/BaseEntity.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BaseEntity.h"
-
-#include "Json.h"
-
-#include "net/Download.h"
-#include "net/HttpMetaCache.h"
-#include "net/NetJob.h"
-
-#include "Env.h"
-#include "Json.h"
-
-#include "BuildConfig.h"
-
-class ParsingValidator : public Net::Validator
-{
-public: /* con/des */
- 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
- {
- auto doc = Json::requireDocument(data, fname);
- auto obj = Json::requireObject(doc, fname);
- m_entity->parse(obj);
- return true;
- }
- catch (const Exception &e)
- {
- qWarning() << "Unable to parse response:" << e.cause();
- return false;
- }
- }
-
-private: /* data */
- QByteArray data;
- Meta::BaseEntity *m_entity;
-};
-
-Meta::BaseEntity::~BaseEntity()
-{
-}
-
-QUrl Meta::BaseEntity::url() const
-{
- return QUrl(BuildConfig.META_URL).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
- {
- auto doc = Json::requireDocument(fname, fname);
- auto obj = Json::requireObject(doc, fname);
- parse(obj);
- 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();
-}
-
-bool Meta::BaseEntity::isLoaded() const
-{
- return m_loadStatus > LoadStatus::NotLoaded;
-}
-
-bool Meta::BaseEntity::shouldStartRemoteUpdate() const
-{
- // 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;
-}
diff --git a/api/logic/meta/BaseEntity.h b/api/logic/meta/BaseEntity.h
deleted file mode 100644
index 04a37420..00000000
--- a/api/logic/meta/BaseEntity.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QJsonObject>
-#include <QObject>
-#include "QObjectPtr.h"
-
-#include "multimc_logic_export.h"
-#include "net/Mode.h"
-
-class Task;
-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
- };
-
-public:
- virtual ~BaseEntity();
-
- virtual void parse(const QJsonObject &obj) = 0;
-
- virtual QString localFilename() const = 0;
- virtual QUrl url() const;
-
- bool isLoaded() const;
- bool shouldStartRemoteUpdate() const;
-
- void load(Net::Mode loadType);
- shared_qobject_ptr<Task> getCurrentTask();
-
-protected: /* methods */
- bool loadLocalFile();
-
-private:
- 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
deleted file mode 100644
index 6802470d..00000000
--- a/api/logic/meta/Index.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Index.h"
-
-#include "VersionList.h"
-#include "JsonFormat.h"
-
-namespace Meta
-{
-Index::Index(QObject *parent)
- : QAbstractListModel(parent)
-{
-}
-Index::Index(const QVector<VersionListPtr> &lists, QObject *parent)
- : 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));
- }
-}
-
-QVariant Index::data(const QModelIndex &index, int role) const
-{
- 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();
-}
-int Index::rowCount(const QModelIndex &parent) const
-{
- return m_lists.size();
-}
-int Index::columnCount(const QModelIndex &parent) const
-{
- 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();
- }
-}
-
-bool Index::hasUid(const QString &uid) const
-{
- 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;
-}
-
-VersionPtr Index::get(const QString &uid, const QString &version)
-{
- auto list = get(uid);
- return list->getVersion(version);
-}
-
-void Index::parse(const QJsonObject& obj)
-{
- 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();
- }
- }
- }
-}
-
-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);
- });
-}
-}
diff --git a/api/logic/meta/Index.h b/api/logic/meta/Index.h
deleted file mode 100644
index e9412e70..00000000
--- a/api/logic/meta/Index.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QAbstractListModel>
-#include <memory>
-
-#include "BaseEntity.h"
-
-#include "multimc_logic_export.h"
-
-class Task;
-
-namespace Meta
-{
-using VersionListPtr = std::shared_ptr<class VersionList>;
-using VersionPtr = std::shared_ptr<class Version>;
-
-class MULTIMC_LOGIC_EXPORT Index : public QAbstractListModel, public BaseEntity
-{
- Q_OBJECT
-public:
- explicit Index(QObject *parent = nullptr);
- explicit Index(const QVector<VersionListPtr> &lists, QObject *parent = nullptr);
-
- 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;
-
- 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;
-
- 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;
-
-private:
- QVector<VersionListPtr> m_lists;
- QHash<QString, VersionListPtr> m_uids;
-
- void connectVersionList(const int row, const VersionListPtr &list);
-};
-}
-
diff --git a/api/logic/meta/Index_test.cpp b/api/logic/meta/Index_test.cpp
deleted file mode 100644
index b0892070..00000000
--- a/api/logic/meta/Index_test.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "meta/Index.h"
-#include "meta/VersionList.h"
-#include "Env.h"
-
-class IndexTest : public QObject
-{
- Q_OBJECT
-private
-slots:
- 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_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)
-
-#include "Index_test.moc"
diff --git a/api/logic/meta/JsonFormat.cpp b/api/logic/meta/JsonFormat.cpp
deleted file mode 100644
index 796da4bb..00000000
--- a/api/logic/meta/JsonFormat.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "JsonFormat.h"
-
-// FIXME: remove this from here... somehow
-#include "minecraft/OneSixVersionFormat.h"
-#include "Json.h"
-
-#include "Index.h"
-#include "Version.h"
-#include "VersionList.h"
-
-using namespace Json;
-
-namespace Meta
-{
-
-MetadataVersion currentFormatVersion()
-{
- 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);
-}
-
-// 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;
-}
-
-static std::shared_ptr<Version> parseVersionInternal(const QJsonObject &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 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;
-}
-
-
-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;
- }
-}
-
-void serializeFormatVersion(QJsonObject& obj, Meta::MetadataVersion 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!"));
- }
-}
-
-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!"));
- }
-}
-
-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!"));
- }
-}
-
-/*
-[
-{"uid":"foo", "equals":"version"}
-]
-*/
-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++;
- }
- }
-}
-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);
-}
-
-}
-
diff --git a/api/logic/meta/JsonFormat.h b/api/logic/meta/JsonFormat.h
deleted file mode 100644
index 93217b7e..00000000
--- a/api/logic/meta/JsonFormat.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QJsonObject>
-#include <memory>
-
-#include "Exception.h"
-#include "meta/BaseEntity.h"
-#include <set>
-
-namespace Meta
-{
-class Index;
-class Version;
-class VersionList;
-
-enum class MetadataVersion
-{
- Invalid = -1,
- InitialRelease = 1
-};
-
-class ParseException : public Exception
-{
-public:
- 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;
-};
-
-inline Q_DECL_PURE_FUNCTION uint qHash(const Require &key, uint seed = 0) Q_DECL_NOTHROW
-{
- return qHash(key.uid, seed);
-}
-
-using RequireSet = std::set<Require>;
-
-void parseIndex(const QJsonObject &obj, Index *ptr);
-void parseVersion(const QJsonObject &obj, Version *ptr);
-void parseVersionList(const QJsonObject &obj, VersionList *ptr);
-
-MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required = true);
-void serializeFormatVersion(QJsonObject &obj, MetadataVersion version);
-
-// FIXME: this has a different shape than the others...FIX IT!?
-void parseRequires(const QJsonObject &obj, RequireSet * ptr, const char * keyName = "requires");
-void serializeRequires(QJsonObject & objOut, RequireSet* ptr, const char * keyName = "requires");
-MetadataVersion currentFormatVersion();
-}
-
-Q_DECLARE_METATYPE(std::set<Meta::Require>)
diff --git a/api/logic/meta/Version.cpp b/api/logic/meta/Version.cpp
deleted file mode 100644
index a8dc3169..00000000
--- a/api/logic/meta/Version.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Version.h"
-
-#include <QDateTime>
-
-#include "JsonFormat.h"
-#include "minecraft/PackProfile.h"
-
-Meta::Version::Version(const QString &uid, const QString &version)
- : BaseVersion(), m_uid(uid), m_version(version)
-{
-}
-
-Meta::Version::~Version()
-{
-}
-
-QString Meta::Version::descriptor()
-{
- return m_version;
-}
-QString Meta::Version::name()
-{
- if(m_data)
- return m_data->name;
- return m_uid;
-}
-QString Meta::Version::typeString() const
-{
- return m_type;
-}
-
-QDateTime Meta::Version::time() const
-{
- return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC);
-}
-
-void Meta::Version::parse(const QJsonObject& obj)
-{
- 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);
- }
-}
-
-void Meta::Version::merge(const VersionPtr &other)
-{
- mergeFromList(other);
- if(other->m_data)
- {
- setData(other->m_data);
- }
-}
-
-QString Meta::Version::localFilename() const
-{
- return m_uid + '/' + m_version + ".json";
-}
-
-void Meta::Version::setType(const QString &type)
-{
- m_type = type;
- emit typeChanged();
-}
-
-void Meta::Version::setTime(const qint64 time)
-{
- 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();
-}
-
-void Meta::Version::setVolatile(bool volatile_)
-{
- m_volatile = volatile_;
-}
-
-
-void Meta::Version::setData(const VersionFilePtr &data)
-{
- m_data = data;
-}
-
-void Meta::Version::setProvidesRecommendations()
-{
- m_providesRecommendations = true;
-}
-
-void Meta::Version::setRecommended(bool recommended)
-{
- m_recommended = recommended;
-}
diff --git a/api/logic/meta/Version.h b/api/logic/meta/Version.h
deleted file mode 100644
index a38d7bcd..00000000
--- a/api/logic/meta/Version.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "BaseVersion.h"
-
-#include <QJsonObject>
-#include <QStringList>
-#include <QVector>
-#include <memory>
-
-#include "minecraft/VersionFile.h"
-
-#include "BaseEntity.h"
-
-#include "multimc_logic_export.h"
-
-#include "JsonFormat.h"
-
-namespace Meta
-{
-using VersionPtr = std::shared_ptr<class Version>;
-
-class MULTIMC_LOGIC_EXPORT Version : public QObject, public BaseVersion, public BaseEntity
-{
- 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;
-
-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);
-
-signals:
- 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;
-};
-}
-
-Q_DECLARE_METATYPE(Meta::VersionPtr)
diff --git a/api/logic/meta/VersionList.cpp b/api/logic/meta/VersionList.cpp
deleted file mode 100644
index 607007eb..00000000
--- a/api/logic/meta/VersionList.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "VersionList.h"
-
-#include <QDateTime>
-
-#include "Version.h"
-#include "JsonFormat.h"
-#include "Version.h"
-
-namespace Meta
-{
-VersionList::VersionList(const QString &uid, QObject *parent)
- : BaseVersionList(parent), m_uid(uid)
-{
- setObjectName("Version list: " + uid);
-}
-
-shared_qobject_ptr<Task> VersionList::getLoadTask()
-{
- load(Net::Mode::Online);
- return getCurrentTask();
-}
-
-bool VersionList::isLoaded()
-{
- return BaseEntity::isLoaded();
-}
-
-const BaseVersionPtr VersionList::at(int i) const
-{
- return m_versions.at(i);
-}
-int VersionList::count() const
-{
- 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();
-}
-
-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();
- }
-}
-
-BaseVersionList::RoleList VersionList::providesRoles() const
-{
- 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;
-}
-
-QString VersionList::localFilename() const
-{
- return m_uid + "/index.json";
-}
-
-QString VersionList::humanReadable() const
-{
- 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;
-}
-
-void VersionList::setName(const QString &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));
- }
-
- // 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);
-}
-
-// 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);
-}
-
-void VersionList::mergeFromIndex(const VersionListPtr &other)
-{
- 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();
-}
-
-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); });
-}
-
-BaseVersionPtr VersionList::getRecommended() const
-{
- return m_recommended;
-}
-
-}
diff --git a/api/logic/meta/VersionList.h b/api/logic/meta/VersionList.h
deleted file mode 100644
index bba32ca3..00000000
--- a/api/logic/meta/VersionList.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "BaseEntity.h"
-#include "BaseVersionList.h"
-#include <QJsonObject>
-#include <memory>
-
-namespace Meta
-{
-using VersionPtr = std::shared_ptr<class Version>;
-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)
-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;
- }
-
-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;
-
-signals:
- void nameChanged(const QString &name);
-
-protected slots:
- void updateListData(QList<BaseVersionPtr>) override
- {
- }
-
-private:
- QVector<VersionPtr> m_versions;
- QHash<QString, VersionPtr> m_lookup;
- QString m_uid;
- QString m_name;
-
- VersionPtr m_recommended;
-
- 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
deleted file mode 100644
index c01733b6..00000000
--- a/api/logic/minecraft/AssetsUtils.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QFileInfo>
-#include <QDir>
-#include <QDirIterator>
-#include <QCryptographicHash>
-#include <QJsonParseError>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QVariant>
-#include <QDebug>
-
-#include "AssetsUtils.h"
-#include "FileSystem.h"
-#include "net/Download.h"
-#include "net/ChecksumValidator.h"
-#include "BuildConfig.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
-{
-
-/*
- * Returns true on success, with index populated
- * index is undefined otherwise
- */
-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)
-{
- 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;
-}
-
-// FIXME: ugly code duplication
-bool reconstructAssets(QString assetsId, QString resourcesFolder)
-{
- QDir assetsDir = QDir("assets/");
- QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes"));
- QDir objectDir = QDir(FS::PathCombine(assetsDir.path(), "objects"));
- QDir virtualDir = QDir(FS::PathCombine(assetsDir.path(), "virtual"));
-
- QString indexPath = FS::PathCombine(indexDir.path(), assetsId + ".json");
- QFile indexFile(indexPath);
- QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId));
-
- if (!indexFile.exists())
- {
- qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets!";
- return false;
- }
-
- qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path() << objectDir.path() << virtualDir.path() << virtualRoot.path();
-
- AssetsIndex index;
- 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;
-}
-
-QString AssetObject::getLocalPath()
-{
- return "assets/objects/" + getRelPath();
-}
-
-QUrl AssetObject::getUrl()
-{
- return BuildConfig.RESOURCE_BASE + getRelPath();
-}
-
-QString AssetObject::getRelPath()
-{
- 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;
-}
diff --git a/api/logic/minecraft/AssetsUtils.h b/api/logic/minecraft/AssetsUtils.h
deleted file mode 100644
index 32e57060..00000000
--- a/api/logic/minecraft/AssetsUtils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QString>
-#include <QMap>
-#include "net/NetAction.h"
-#include "net/NetJob.h"
-
-struct AssetObject
-{
- QString getRelPath();
- QUrl getUrl();
- QString getLocalPath();
- NetActionPtr getDownloadAction();
-
- QString hash;
- qint64 size;
-};
-
-struct AssetsIndex
-{
- NetJobPtr getDownloadJob();
-
- QString id;
- QMap<QString, AssetObject> objects;
- bool isVirtual = false;
- bool mapToResources = false;
-};
-
-/// FIXME: this is absolutely horrendous. REDO!!!!
-namespace AssetsUtils
-{
-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
-bool reconstructAssets(QString assetsId, QString resourcesFolder);
-}
diff --git a/api/logic/minecraft/Component.cpp b/api/logic/minecraft/Component.cpp
deleted file mode 100644
index 92821065..00000000
--- a/api/logic/minecraft/Component.cpp
+++ /dev/null
@@ -1,439 +0,0 @@
-#include <meta/VersionList.h>
-#include <meta/Index.h>
-#include <Env.h>
-#include "Component.h"
-
-#include "meta/Version.h"
-#include "VersionFile.h"
-#include "minecraft/PackProfile.h"
-#include <FileSystem.h>
-#include <QSaveFile>
-#include "OneSixVersionFormat.h"
-#include <assert.h>
-
-Component::Component(PackProfile * parent, const QString& uid)
-{
- assert(parent);
- m_parent = parent;
-
- m_uid = uid;
-}
-
-Component::Component(PackProfile * parent, std::shared_ptr<Meta::Version> version)
-{
- 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();
-}
-
-Component::Component(PackProfile * parent, const QString& uid, std::shared_ptr<VersionFile> file)
-{
- assert(parent);
- m_parent = parent;
-
- 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;
-}
-
-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());
- }
-}
-
-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;
- }
-}
-
-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;
-}
-
-int Component::getOrder()
-{
- if(m_orderOverride)
- return m_order;
-
- auto vfile = getVersionFile();
- if(vfile)
- {
- return vfile->order;
- }
- return 0;
-}
-void Component::setOrder(int order)
-{
- m_orderOverride = true;
- m_order = order;
-}
-QString Component::getID()
-{
- return m_uid;
-}
-QString Component::getName()
-{
- if (!m_cachedName.isEmpty())
- return m_cachedName;
- return m_uid;
-}
-QString Component::getVersion()
-{
- return m_cachedVersion;
-}
-QString Component::getFilename()
-{
- 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();
-}
-
-bool Component::isEnabled()
-{
- return !canBeDisabled() || !m_disabled;
-}
-
-bool Component::canBeDisabled()
-{
- 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 Component::isCustom()
-{
- return m_file != nullptr;
-}
-
-bool Component::isCustomizable()
-{
- if(m_metaVersion)
- {
- if(getVersionFile())
- {
- return true;
- }
- }
- return false;
-}
-bool Component::isRemovable()
-{
- return !m_important;
-}
-bool Component::isRevertible()
-{
- 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;
-}
-bool Component::isVersionChangeable()
-{
- 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();
- }
-}
-
-ProblemSeverity Component::getProblemSeverity() const
-{
- 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.")}};
-}
-
-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();
-}
-
-bool Component::customize()
-{
- 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 (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();
-
- // 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;
-}
-
-/**
- * deep inspecting compare for requirement sets
- * By default, only uids are compared for set operations.
- * This compares all fields of the Require structs in the sets.
- */
-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;
-}
-
-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();
- }
-}
diff --git a/api/logic/minecraft/Component.h b/api/logic/minecraft/Component.h
deleted file mode 100644
index cb202f7f..00000000
--- a/api/logic/minecraft/Component.h
+++ /dev/null
@@ -1,111 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <QList>
-#include <QJsonDocument>
-#include <QDateTime>
-#include "meta/JsonFormat.h"
-#include "ProblemProvider.h"
-#include "QObjectPtr.h"
-#include "multimc_logic_export.h"
-
-class PackProfile;
-class LaunchProfile;
-namespace Meta
-{
- class Version;
- class VersionList;
-}
-class VersionFile;
-
-class MULTIMC_LOGIC_EXPORT Component : public QObject, public ProblemProvider
-{
-Q_OBJECT
-public:
- Component(PackProfile * parent, const QString &uid);
-
- // DEPRECATED: remove these constructors?
- Component(PackProfile * parent, std::shared_ptr<Meta::Version> version);
- Component(PackProfile * parent, const QString & uid, std::shared_ptr<VersionFile> file);
-
- virtual ~Component(){};
- void applyTo(LaunchProfile *profile);
-
- bool isEnabled();
- bool setEnabled (bool state);
- bool canBeDisabled();
-
- 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();
-
- QString getID();
- QString getName();
- QString getVersion();
- std::shared_ptr<Meta::Version> getMeta();
- QDateTime getReleaseDateTime();
-
- QString getFilename();
-
- std::shared_ptr<class VersionFile> getVersionFile() const;
- std::shared_ptr<class Meta::VersionList> getVersionList() const;
-
- void setImportant (bool state);
-
-
- const QList<PatchProblem> getProblems() const override;
- ProblemSeverity getProblemSeverity() const override;
-
- void setVersion(const QString & version);
- bool customize();
- bool revert();
-
- void updateCachedData();
-
-signals:
- void dataChanged();
-
-public: /* data */
- PackProfile * 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/ComponentUpdateTask.cpp b/api/logic/minecraft/ComponentUpdateTask.cpp
deleted file mode 100644
index 241d9a49..00000000
--- a/api/logic/minecraft/ComponentUpdateTask.cpp
+++ /dev/null
@@ -1,704 +0,0 @@
-#include "ComponentUpdateTask.h"
-
-#include "PackProfile_p.h"
-#include "PackProfile.h"
-#include "Component.h"
-#include <Env.h>
-#include <meta/Index.h>
-#include <meta/VersionList.h>
-#include <meta/Version.h>
-#include "ComponentUpdateTask_p.h"
-#include <cassert>
-#include <Version.h>
-#include "net/Mode.h"
-#include "OneSixVersionFormat.h"
-
-/*
- * This is responsible for loading the components of a component list AND resolving dependency issues between them
- */
-
-/*
- * FIXME: the 'one shot async task' nature of this does not fit the intended usage
- * Really, it should be a reactor/state machine that receives input from the application
- * and dynamically adapts to changing requirements...
- *
- * The reactor should be the only entry into manipulating the PackProfile.
- * See: https://en.wikipedia.org/wiki/Reactor_pattern
- */
-
-/*
- * Or make this operate on a snapshot of the PackProfile state, then merge results in as long as the snapshot and PackProfile didn't change?
- * If the component list changes, start over.
- */
-
-ComponentUpdateTask::ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile* list, QObject* parent)
- : Task(parent)
-{
- d.reset(new ComponentUpdateTaskData);
- d->m_list = list;
- d->mode = mode;
- d->netmode = netmode;
-}
-
-ComponentUpdateTask::~ComponentUpdateTask()
-{
-}
-
-void ComponentUpdateTask::executeTask()
-{
- qDebug() << "Loading components";
- loadComponents();
-}
-
-namespace
-{
-enum class LoadResult
-{
- LoadedLocal,
- RequiresRemote,
- Failed
-};
-
-LoadResult composeLoadResult(LoadResult a, LoadResult b)
-{
- 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;
-}
-
-// FIXME: dead code. determine if this can still be useful?
-/*
-static LoadResult loadPackProfile(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;
-}
-*/
-
-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;
-}
-}
-
-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...
-#if 0
- switch(d->mode)
- {
- case Mode::Launch:
- {
- singleResult = loadComponent(component, loadTask, d->netmode);
- loadType = RemoteLoadStatus::Type::Version;
- break;
- }
- case Mode::Resolution:
- {
- singleResult = loadPackProfile(component, loadTask, d->netmode);
- loadType = RemoteLoadStatus::Type::List;
- break;
- }
- }
-#else
- 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.PackProfileIndex = 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>;
-}
-
-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};
-}
-
-// 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;
-}
-
-/// 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);
- }
- }
-}
-
-/**
- * handles:
- * - trivial addition (there is an unmet requirement and it can be trivially met by adding something)
- * - trivial version conflict of dependencies == explicit version required and installed is different
- *
- * toAdd - set of requirements than mean adding a new component
- * toChange - set of requirements that mean changing version of an existing component
- */
-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->isCustom()) {
- decision = Decision::LockedVersionNotSame;
- } else {
- 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
-// FIXME: This works directly with the PackProfile internals. It shouldn't! It needs richer data types than PackProfile uses.
-// 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;
-// ############################################################################################################
-// 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";
- }
- 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();
- }
-}
-
-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.PackProfileIndex);
- 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();
-}
-
-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();
- }
-}
diff --git a/api/logic/minecraft/ComponentUpdateTask.h b/api/logic/minecraft/ComponentUpdateTask.h
deleted file mode 100644
index 4274cabb..00000000
--- a/api/logic/minecraft/ComponentUpdateTask.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-
-#include "tasks/Task.h"
-#include "net/Mode.h"
-
-#include <memory>
-class PackProfile;
-struct ComponentUpdateTaskData;
-
-class ComponentUpdateTask : public Task
-{
- Q_OBJECT
-public:
- enum class Mode
- {
- Launch,
- Resolution
- };
-
-public:
- explicit ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile * list, QObject *parent = 0);
- virtual ~ComponentUpdateTask();
-
-protected:
- void executeTask();
-
-private:
- void loadComponents();
- void resolveDependencies(bool checkOnly);
-
- void remoteLoadSucceeded(size_t index);
- void remoteLoadFailed(size_t index, const QString &msg);
- void checkIfAllFinished();
-
-private:
- std::unique_ptr<ComponentUpdateTaskData> d;
-};
diff --git a/api/logic/minecraft/ComponentUpdateTask_p.h b/api/logic/minecraft/ComponentUpdateTask_p.h
deleted file mode 100644
index 5b02431b..00000000
--- a/api/logic/minecraft/ComponentUpdateTask_p.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include <cstddef>
-#include <QString>
-#include <QList>
-#include "net/Mode.h"
-
-class PackProfile;
-
-struct RemoteLoadStatus
-{
- enum class Type
- {
- Index,
- List,
- Version
- } type = Type::Version;
- size_t PackProfileIndex = 0;
- bool finished = false;
- bool succeeded = false;
- QString error;
-};
-
-struct ComponentUpdateTaskData
-{
- PackProfile * 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
deleted file mode 100644
index 60e0a726..00000000
--- a/api/logic/minecraft/GradleSpecifier.h
+++ /dev/null
@@ -1,151 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QStringList>
-#include "DefaultVariable.h"
-
-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
- 0 "org.gradle.test.classifiers:service:1.0:jdk15@jar"
- 1 "org.gradle.test.classifiers"
- 2 "service"
- 3 "1.0"
- 4 "jdk15"
- 5 "jar"
- */
- QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?");
- m_valid = matcher.exactMatch(value);
- if(!m_valid) {
- m_invalidValue = value;
- return *this;
- }
- auto elements = matcher.capturedTexts();
- m_groupId = elements[1];
- m_artifactId = elements[2];
- m_version = elements[3];
- m_classifier = elements[4];
- if(!elements[5].isEmpty())
- {
- m_extension = elements[5];
- }
- return *this;
- }
- QString serialize() const
- {
- if(!m_valid) {
- return m_invalidValue;
- }
- 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
- {
- if(!m_valid) {
- return QString();
- }
- 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 QString();
- }
- 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_invalidValue;
- 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
deleted file mode 100644
index 0900c9d8..00000000
--- a/api/logic/minecraft/GradleSpecifier_test.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "minecraft/GradleSpecifier.h"
-
-class GradleSpecifierTest : public QObject
-{
- 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).serialize();
-
- 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.serialize(), input);
- QCOMPARE(spec.toPath(), QString());
- }
-};
-
-QTEST_GUILESS_MAIN(GradleSpecifierTest)
-
-#include "GradleSpecifier_test.moc"
diff --git a/api/logic/minecraft/LaunchProfile.cpp b/api/logic/minecraft/LaunchProfile.cpp
deleted file mode 100644
index 41705187..00000000
--- a/api/logic/minecraft/LaunchProfile.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-#include "LaunchProfile.h"
-#include <Version.h>
-
-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_mavenFiles.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;
-}
-
-void LaunchProfile::applyMinecraftVersion(const QString& id)
-{
- applyString(id, this->m_minecraftVersion);
-}
-
-void LaunchProfile::applyAppletClass(const QString& appletClass)
-{
- applyString(appletClass, this->m_appletClass);
-}
-
-void LaunchProfile::applyMainClass(const QString& mainClass)
-{
- applyString(mainClass, this->m_mainClass);
-}
-
-void LaunchProfile::applyMinecraftArguments(const QString& minecraftArguments)
-{
- applyString(minecraftArguments, this->m_minecraftArguments);
-}
-
-void LaunchProfile::applyMinecraftVersionType(const QString& type)
-{
- applyString(type, this->m_minecraftVersionType);
-}
-
-void LaunchProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets)
-{
- if(assets)
- {
- m_minecraftAssets = assets;
- }
-}
-
-void LaunchProfile::applyTraits(const QSet<QString>& 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;
-}
-
-void LaunchProfile::applyJarMods(const QList<LibraryPtr>& 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;
-}
-
-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);
- }
- }
-}
-
-void LaunchProfile::applyLibrary(LibraryPtr library)
-{
- if(!library->isActive())
- {
- return;
- }
-
- QList<LibraryPtr> * list = &m_libraries;
- if(library->isNative())
- {
- list = &m_nativeLibraries;
- }
-
- 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;
- }
-
- 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);
- }
-}
-
-void LaunchProfile::applyMavenFile(LibraryPtr mavenFile)
-{
- if(!mavenFile->isActive())
- {
- return;
- }
-
- if(mavenFile->isNative())
- {
- return;
- }
-
- // unlike libraries, we do not keep only one version or try to dedupe them
- m_mavenFiles.append(Library::limitedCopy(mavenFile));
-}
-
-const LibraryPtr LaunchProfile::getMainJar() const
-{
- return m_mainJar;
-}
-
-void LaunchProfile::applyMainJar(LibraryPtr jar)
-{
- if(jar)
- {
- m_mainJar = jar;
- }
-}
-
-void LaunchProfile::applyProblemSeverity(ProblemSeverity severity)
-{
- if (m_problemSeverity < severity)
- {
- m_problemSeverity = severity;
- }
-}
-
-const QList<PatchProblem> LaunchProfile::getProblems() const
-{
- // FIXME: implement something that actually makes sense here
- return {};
-}
-
-QString LaunchProfile::getMinecraftVersion() const
-{
- return m_minecraftVersion;
-}
-
-QString LaunchProfile::getAppletClass() const
-{
- return m_appletClass;
-}
-
-QString LaunchProfile::getMainClass() const
-{
- return m_mainClass;
-}
-
-const QSet<QString> &LaunchProfile::getTraits() const
-{
- return m_traits;
-}
-
-const QStringList & LaunchProfile::getTweakers() const
-{
- return m_tweakers;
-}
-
-bool LaunchProfile::hasTrait(const QString& trait) const
-{
- return m_traits.contains(trait);
-}
-
-ProblemSeverity LaunchProfile::getProblemSeverity() const
-{
- return m_problemSeverity;
-}
-
-QString LaunchProfile::getMinecraftVersionType() const
-{
- return m_minecraftVersionType;
-}
-
-std::shared_ptr<MojangAssetIndexInfo> LaunchProfile::getMinecraftAssets() const
-{
- if(!m_minecraftAssets)
- {
- return std::make_shared<MojangAssetIndexInfo>("legacy");
- }
- return m_minecraftAssets;
-}
-
-QString LaunchProfile::getMinecraftArguments() const
-{
- return m_minecraftArguments;
-}
-
-const QList<LibraryPtr> & LaunchProfile::getJarMods() const
-{
- return m_jarMods;
-}
-
-const QList<LibraryPtr> & LaunchProfile::getLibraries() const
-{
- return m_libraries;
-}
-
-const QList<LibraryPtr> & LaunchProfile::getNativeLibraries() const
-{
- return m_nativeLibraries;
-}
-
-const QList<LibraryPtr> & LaunchProfile::getMavenFiles() const
-{
- return m_mavenFiles;
-}
-
-void LaunchProfile::getLibraryFiles(
- 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);
- }
-}
diff --git a/api/logic/minecraft/LaunchProfile.h b/api/logic/minecraft/LaunchProfile.h
deleted file mode 100644
index c1752531..00000000
--- a/api/logic/minecraft/LaunchProfile.h
+++ /dev/null
@@ -1,104 +0,0 @@
-#pragma once
-#include <QString>
-#include "Library.h"
-#include <ProblemProvider.h>
-
-class LaunchProfile: public ProblemProvider
-{
-public:
- 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 applyMavenFile(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 QList<LibraryPtr> & getMavenFiles() 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;
-
- /// Release type - "release" or "snapshot"
- QString m_minecraftVersionType;
-
- /// 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;
-
- /// A list of all tweaker classes
- QStringList m_tweakers;
-
- /// The main class to load first
- QString m_mainClass;
-
- /// The applet class, for some very old minecraft releases
- QString m_appletClass;
-
- /// the list of libraries
- QList<LibraryPtr> m_libraries;
-
- /// the list of maven files to be placed in the libraries folder, but not acted upon
- QList<LibraryPtr> m_mavenFiles;
-
- /// the main jar
- LibraryPtr m_mainJar;
-
- /// the list of native libraries
- QList<LibraryPtr> m_nativeLibraries;
-
- /// 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;
-
- /// the list of mods
- QList<LibraryPtr> m_mods;
-
- ProblemSeverity m_problemSeverity = ProblemSeverity::None;
-
-};
diff --git a/api/logic/minecraft/Library.cpp b/api/logic/minecraft/Library.cpp
deleted file mode 100644
index f2293679..00000000
--- a/api/logic/minecraft/Library.cpp
+++ /dev/null
@@ -1,309 +0,0 @@
-#include "Library.h"
-#include "MinecraftInstance.h"
-
-#include <net/Download.h>
-#include <net/ChecksumValidator.h>
-#include <Env.h>
-#include <FileSystem.h>
-#include <BuildConfig.h>
-
-
-void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& native, QStringList& native32,
- 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();
- 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& failedLocalFiles,
- const QString & overridePath
-) const
-{
- QList<NetActionPtr> out;
- bool stale = isAlwaysStale();
- bool local = isLocal();
-
- auto check_local_file = [&](QString storage)
- {
- QFileInfo fileinfo(storage);
- QString fileName = fileinfo.fileName();
- auto fullPath = FS::PathCombine(overridePath, fileName);
- QFileInfo localFileInfo(fullPath);
- if(!localFileInfo.exists())
- {
- failedLocalFiles.append(localFileInfo.filePath());
- return false;
- }
- return true;
- };
-
- auto add_download = [&](QString storage, QString url, QString sha1)
- {
- if(local)
- {
- return check_local_file(storage);
- }
- auto entry = cache->resolveEntry("libraries", storage);
- if(stale)
- {
- entry->setStale(true);
- }
- if (!entry->isStale())
- return true;
- Net::Download::Options options;
- if(stale)
- {
- options |= Net::Download::Option::AcceptLocalFiles;
- }
-
- 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().serialize() << "storage:" << storage << "url:" << url;
- out.append(dl);
- }
- else
- {
- out.append(Net::Download::makeCached(url, entry, options));
- qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
- }
- 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.serialize() << "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.serialize() << "because it has no artifact";
- }
- }
- }
- else
- {
- auto raw_dl = [&]()
- {
- if (!m_absoluteURL.isEmpty())
- {
- return m_absoluteURL;
- }
-
- if (m_repositoryURL.isEmpty())
- {
- return BuildConfig.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 Library::isLocal() const
-{
- return m_hint == "local";
-}
-
-bool Library::isAlwaysStale() const
-{
- return m_hint == "always-stale";
-}
-
-void Library::setStoragePrefix(QString prefix)
-{
- m_storagePrefix = prefix;
-}
-
-QString Library::defaultStoragePrefix()
-{
- return "libraries/";
-}
-
-QString Library::storagePrefix() const
-{
- 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();
- }
-
- // 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);
-}
-
-QString Library::storageSuffix(OpSys system) const
-{
- // 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);
-}
diff --git a/api/logic/minecraft/Library.h b/api/logic/minecraft/Library.h
deleted file mode 100644
index acdd6c9c..00000000
--- a/api/logic/minecraft/Library.h
+++ /dev/null
@@ -1,219 +0,0 @@
-#pragma once
-#include <QString>
-#include <net/NetAction.h>
-#include <QPair>
-#include <QList>
-#include <QStringList>
-#include <QMap>
-#include <QDir>
-#include <QUrl>
-#include <memory>
-
-#include "Rule.h"
-#include "minecraft/OpSys.h"
-#include "GradleSpecifier.h"
-#include "MojangDownloadInfo.h"
-
-#include "multimc_logic_export.h"
-
-class Library;
-class MinecraftInstance;
-
-typedef std::shared_ptr<Library> LibraryPtr;
-
-class MULTIMC_LOGIC_EXPORT Library
-{
- 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;
- }
-
-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;
-
- /// 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();
-
- /// 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;
-
- QString hint() const
- {
- return m_hint;
- }
-
-protected: /* data */
- /// 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: MultiMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined
- QString m_absoluteURL;
-
- /// MultiMC extension - filename override
- QString m_filename;
-
- /// DEPRECATED MultiMC extension - display name
- QString m_displayname;
-
- /**
- * 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;
-
- /// 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;
-
- /// native suffixes per OS
- QMap<OpSys, QString> m_nativeClassifiers;
-
- /// 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;
-
- /// 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
deleted file mode 100644
index 75bb4db1..00000000
--- a/api/logic/minecraft/Library_test.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "minecraft/MojangVersionFormat.h"
-#include "minecraft/OneSixVersionFormat.h"
-#include "minecraft/Library.h"
-#include "net/HttpMetaCache.h"
-#include "FileSystem.h"
-
-class LibraryTest : public QObject
-{
- 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();
- ProblemContainer problems;
- return MojangVersionFormat::libraryFromJson(problems, 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);
-
- 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, {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;
-};
-
-QTEST_GUILESS_MAIN(LibraryTest)
-
-#include "Library_test.moc"
diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp
deleted file mode 100644
index dbf9f816..00000000
--- a/api/logic/minecraft/MinecraftInstance.cpp
+++ /dev/null
@@ -1,1054 +0,0 @@
-#include "MinecraftInstance.h"
-#include <minecraft/launch/CreateGameFolders.h>
-#include <minecraft/launch/ExtractNatives.h>
-#include <minecraft/launch/PrintInstanceInfo.h>
-#include <settings/Setting.h>
-#include "settings/SettingsObject.h"
-#include "Env.h"
-#include <MMCStrings.h>
-#include <pathmatcher/RegexpMatcher.h>
-#include <pathmatcher/MultiMatcher.h>
-#include <FileSystem.h>
-#include <java/JavaVersion.h>
-
-#include "launch/LaunchTask.h"
-#include "launch/steps/LookupServerAddress.h"
-#include "launch/steps/PostLaunchCommand.h"
-#include "launch/steps/Update.h"
-#include "launch/steps/PreLaunchCommand.h"
-#include "launch/steps/TextPrint.h"
-#include "minecraft/launch/LauncherPartLaunch.h"
-#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 "minecraft/launch/VerifyJavaInstall.h"
-#include "java/launch/CheckJava.h"
-#include "java/JavaUtils.h"
-#include "meta/Index.h"
-#include "meta/VersionList.h"
-
-#include "mod/ModFolderModel.h"
-#include "mod/ResourcePackFolderModel.h"
-#include "mod/TexturePackFolderModel.h"
-#include "WorldList.h"
-
-#include "icons/IIconList.h"
-
-#include <QCoreApplication>
-#include "PackProfile.h"
-#include "AssetsUtils.h"
-#include "MinecraftUpdate.h"
-#include "MinecraftLoadAndCheck.h"
-#include <minecraft/gameoptions/GameOptions.h>
-#include <minecraft/update/FoldersTask.h>
-
-#define IBUS "@im=ibus"
-
-// all of this because keeping things compatible with deprecated old settings
-// if either of the settings {a, b} is true, this also resolves to true
-class OrSetting : public Setting
-{
- 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) {}
-private:
- 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);
-
- // Native library workarounds
- auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
- m_settings->registerOverride(globalSettings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
- m_settings->registerOverride(globalSettings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
-
- // Game time
- auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
- m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
- m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), gameTimeOverride);
-
- // Join server on launch, this does not have a global override
- m_settings->registerSetting("JoinServerOnLaunch", false);
- m_settings->registerSetting("JoinServerOnLaunchAddress", "");
-
- // 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 PackProfile(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();
-}
-
-QString MinecraftInstance::typeName() const
-{
- return "Minecraft";
-}
-
-std::shared_ptr<PackProfile> MinecraftInstance::getPackProfile() const
-{
- return m_components;
-}
-
-QSet<QString> MinecraftInstance::traits() const
-{
- auto components = getPackProfile();
- if (!components)
- {
- return {"version-incomplete"};
- }
- auto profile = components->getProfile();
- if (!profile)
- {
- return {"version-incomplete"};
- }
- return profile->getTraits();
-}
-
-QString MinecraftInstance::gameRoot() const
-{
- QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
- QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
-
- if (mcDir.exists() && !dotMCDir.exists())
- return mcDir.filePath();
- else
- return dotMCDir.filePath();
-}
-
-QString MinecraftInstance::binRoot() const
-{
- return FS::PathCombine(gameRoot(), "bin");
-}
-
-QString MinecraftInstance::getNativePath() const
-{
- 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();
-}
-
-QString MinecraftInstance::jarModsDir() const
-{
- QDir jarmods_dir(FS::PathCombine(instanceRoot(), "jarmods/"));
- return jarmods_dir.absolutePath();
-}
-
-QString MinecraftInstance::loaderModsDir() const
-{
- return FS::PathCombine(gameRoot(), "mods");
-}
-
-QString MinecraftInstance::modsCacheLocation() const
-{
- return FS::PathCombine(instanceRoot(), "mods.cache");
-}
-
-QString MinecraftInstance::coreModsDir() const
-{
- return FS::PathCombine(gameRoot(), "coremods");
-}
-
-QString MinecraftInstance::resourcePacksDir() const
-{
- return FS::PathCombine(gameRoot(), "resourcepacks");
-}
-
-QString MinecraftInstance::texturePacksDir() const
-{
- return FS::PathCombine(gameRoot(), "texturepacks");
-}
-
-QString MinecraftInstance::instanceConfigFolder() const
-{
- return FS::PathCombine(gameRoot(), "config");
-}
-
-QString MinecraftInstance::libDir() const
-{
- return FS::PathCombine(gameRoot(), "lib");
-}
-
-QString MinecraftInstance::worldDir() const
-{
- return FS::PathCombine(gameRoot(), "saves");
-}
-
-QString MinecraftInstance::resourcesDir() const
-{
- return FS::PathCombine(gameRoot(), "resources");
-}
-
-QDir MinecraftInstance::librariesPath() const
-{
- return QDir::current().absoluteFilePath("libraries");
-}
-
-QDir MinecraftInstance::jarmodsPath() const
-{
- return QDir(jarModsDir());
-}
-
-QDir MinecraftInstance::versionsPath() const
-{
- 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;
-}
-
-QString MinecraftInstance::getMainClass() const
-{
- 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 MinecraftInstance::extraArguments() const
-{
- auto list = BaseInstance::extraArguments();
- auto version = getPackProfile();
- 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;
-
- // custom args go first. we want to override them if we have our own here.
- args.append(extraArguments());
-
- // OSX dock icon and name
-#ifdef Q_OS_MAC
- 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
-#ifdef Q_OS_MAC
- if(traits_.contains("FirstThreadOnMacOS"))
- {
- args << QString("-XstartOnFirstThread");
- }
-#endif
-
- // 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");
-#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;
-}
-
-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(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();
-
- // 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;
-}
-
-QStringList MinecraftInstance::processMinecraftArgs(
- AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const
-{
- auto profile = m_components->getProfile();
- QString args_pattern = profile->getMinecraftArguments();
- for (auto tweaker : profile->getTweakers())
- {
- args_pattern += " --tweakClass " + tweaker;
- }
-
- if (serverToJoin && !serverToJoin->address.isEmpty())
- {
- args_pattern += " --server " + serverToJoin->address;
- args_pattern += " --port " + QString::number(serverToJoin->port);
- }
-
- 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, MinecraftServerTargetPtr serverToJoin)
-{
- 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";
- }
-
- if (serverToJoin && !serverToJoin->address.isEmpty())
- {
- launchScript += "serverAddress " + serverToJoin->address + "\n";
- launchScript += "serverPort " + QString::number(serverToJoin->port) + "\n";
- }
-
- // generic minecraft params
- for (auto param : processMinecraftArgs(
- session,
- nullptr /* When using a launch script, the server parameters are handled by it*/
- ))
- {
- 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, MinecraftServerTargetPtr serverToJoin)
-{
- 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 << "";
- }
-
- auto settings = this->settings();
- bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
- bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
- if (nativeOpenAL || nativeGLFW)
- {
- if (nativeOpenAL)
- out << "Using system OpenAL.";
- if (nativeGLFW)
- out << "Using system GLFW.";
- 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, serverToJoin);
- 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.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;
-}
-
-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;
-}
-
-QString MinecraftInstance::getLogFileRoot()
-{
- 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);
-}
-
-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(m_settings->get("ShowGameTime").toBool())
- {
- if (lastTimePlayed() > 0) {
- description.append(tr(", last played for %1").arg(prettifyTimeDuration(lastTimePlayed())));
- }
-
- if (totalTimePlayed() > 0) {
- description.append(tr(", total 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 MinecraftUpdate(this));
- }
- }
- return nullptr;
-}
-
-shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
-{
- // 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;
- }
-
- // create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732)
- {
- process->appendStep(new CreateGameFolders(pptr));
- }
-
- if (!serverToJoin && m_settings->get("JoinServerOnLaunch").toBool())
- {
- QString fullAddress = m_settings->get("JoinServerOnLaunchAddress").toString();
- serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(fullAddress)));
- }
-
- if(serverToJoin && serverToJoin->port == 25565)
- {
- // Resolve server address to join on launch
- auto *step = new LookupServerAddress(pptr);
- step->setLookupAddress(serverToJoin->address);
- step->setOutputAddressPtr(serverToJoin);
- process->appendStep(step);
- }
-
- // 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));
- }
-
- // Scan mods folders for mods
- {
- process->appendStep(new ScanModFolders(pptr));
- }
-
- // print some instance info here...
- {
- process->appendStep(new PrintInstanceInfo(pptr, session, serverToJoin));
- }
-
- // extract native jars if needed
- {
- process->appendStep(new ExtractNatives(pptr));
- }
-
- // reconstruct assets if needed
- {
- process->appendStep(new ReconstructAssets(pptr));
- }
-
- // verify that minimum Java requirements are met
- {
- process->appendStep(new VerifyJavaInstall(pptr));
- }
-
- {
- // actually launch the game
- auto method = launchMethod();
- if(method == "LauncherPart")
- {
- auto step = new LauncherPartLaunch(pptr);
- step->setWorkingDirectory(gameRoot());
- step->setAuthSession(session);
- step->setServerToJoin(serverToJoin);
- process->appendStep(step);
- }
- else if (method == "DirectJava")
- {
- auto step = new DirectJavaLaunch(pptr);
- step->setWorkingDirectory(gameRoot());
- step->setAuthSession(session);
- step->setServerToJoin(serverToJoin);
- 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();
-}
-
-JavaVersion MinecraftInstance::getJavaVersion() const
-{
- return JavaVersion(settings()->get("JavaVersion").toString());
-}
-
-std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList() const
-{
- 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<ModFolderModel> MinecraftInstance::coreModList() const
-{
- 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<ModFolderModel> MinecraftInstance::resourcePackList() const
-{
- if (!m_resource_pack_list)
- {
- m_resource_pack_list.reset(new ResourcePackFolderModel(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<ModFolderModel> MinecraftInstance::texturePackList() const
-{
- if (!m_texture_pack_list)
- {
- m_texture_pack_list.reset(new TexturePackFolderModel(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;
-}
-
-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;
-}
-
-
-#include "MinecraftInstance.moc"
diff --git a/api/logic/minecraft/MinecraftInstance.h b/api/logic/minecraft/MinecraftInstance.h
deleted file mode 100644
index 05600797..00000000
--- a/api/logic/minecraft/MinecraftInstance.h
+++ /dev/null
@@ -1,133 +0,0 @@
-#pragma once
-#include "BaseInstance.h"
-#include <java/JavaVersion.h>
-#include "minecraft/mod/Mod.h"
-#include <QProcess>
-#include <QDir>
-#include "multimc_logic_export.h"
-#include "minecraft/launch/MinecraftServerTarget.h"
-
-class ModFolderModel;
-class WorldList;
-class GameOptions;
-class LaunchStep;
-class PackProfile;
-
-class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance
-{
- Q_OBJECT
-public:
- 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<PackProfile> getPackProfile() 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, MinecraftServerTargetPtr serverToJoin) override;
- QStringList extraArguments() const override;
- QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
- QList<Mod> getJarMods() const;
- QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin);
- /// 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, MinecraftServerTargetPtr serverToJoin) const;
-
- virtual JavaVersion getJavaVersion() const;
-
-protected:
- QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
- QStringList validLaunchMethods();
- QString launchMethod();
-
-private:
- QString prettifyTimeDuration(int64_t duration);
-
-protected: // data
- std::shared_ptr<PackProfile> 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
deleted file mode 100644
index 79b0c484..00000000
--- a/api/logic/minecraft/MinecraftLoadAndCheck.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "MinecraftLoadAndCheck.h"
-#include "MinecraftInstance.h"
-#include "PackProfile.h"
-
-MinecraftLoadAndCheck::MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
-{
-}
-
-void MinecraftLoadAndCheck::executeTask()
-{
- // add offline metadata load task
- auto components = m_inst->getPackProfile();
- 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);
-}
-
-void MinecraftLoadAndCheck::subtaskSucceeded()
-{
- if(isFinished())
- {
- qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
- return;
- }
- emitSucceeded();
-}
-
-void MinecraftLoadAndCheck::subtaskFailed(QString 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
deleted file mode 100644
index 3435b52b..00000000
--- a/api/logic/minecraft/MinecraftLoadAndCheck.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QList>
-#include <QUrl>
-
-#include "tasks/Task.h"
-#include <quazip.h>
-
-#include "QObjectPtr.h"
-
-class MinecraftVersion;
-class MinecraftInstance;
-
-class MinecraftLoadAndCheck : public Task
-{
- Q_OBJECT
-public:
- explicit MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent = 0);
- virtual ~MinecraftLoadAndCheck() {};
- void executeTask() override;
-
-private slots:
- void subtaskSucceeded();
- void subtaskFailed(QString error);
-
-private:
- 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
deleted file mode 100644
index 8f1565b0..00000000
--- a/api/logic/minecraft/MinecraftUpdate.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Env.h"
-#include "MinecraftUpdate.h"
-#include "MinecraftInstance.h"
-
-#include <QFile>
-#include <QFileInfo>
-#include <QTextStream>
-#include <QDataStream>
-
-#include "BaseInstance.h"
-#include "minecraft/PackProfile.h"
-#include "minecraft/Library.h"
-#include <FileSystem.h>
-
-#include "update/FoldersTask.h"
-#include "update/LibrariesTask.h"
-#include "update/FMLLibrariesTask.h"
-#include "update/AssetUpdateTask.h"
-
-#include <meta/Index.h>
-#include <meta/Version.h>
-
-MinecraftUpdate::MinecraftUpdate(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
-{
-}
-
-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->getPackProfile();
- 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 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, &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 MinecraftUpdate::subtaskSucceeded()
-{
- 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 MinecraftUpdate::subtaskFailed(QString 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 MinecraftUpdate::abort()
-{
- if(!m_abort)
- {
- m_abort = true;
- auto task = m_tasks[m_currentTask];
- if(task->canAbort())
- {
- return task->abort();
- }
- }
- return true;
-}
-
-bool MinecraftUpdate::canAbort() const
-{
- return true;
-}
diff --git a/api/logic/minecraft/MinecraftUpdate.h b/api/logic/minecraft/MinecraftUpdate.h
deleted file mode 100644
index fadebff9..00000000
--- a/api/logic/minecraft/MinecraftUpdate.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QList>
-#include <QUrl>
-
-#include "net/NetJob.h"
-#include "tasks/Task.h"
-#include "minecraft/VersionFilterData.h"
-#include <quazip.h>
-
-class MinecraftVersion;
-class MinecraftInstance;
-
-class MinecraftUpdate : public Task
-{
- Q_OBJECT
-public:
- 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);
-
-private:
- 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;
-};
diff --git a/api/logic/minecraft/MojangDownloadInfo.h b/api/logic/minecraft/MojangDownloadInfo.h
deleted file mode 100644
index 88f87287..00000000
--- a/api/logic/minecraft/MojangDownloadInfo.h
+++ /dev/null
@@ -1,82 +0,0 @@
-#pragma once
-#include <QString>
-#include <QMap>
-#include <memory>
-
-struct MojangDownloadInfo
-{
- // 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;
-};
-
-
-
-struct MojangLibraryDownloadInfo
-{
- MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact): artifact(artifact) {};
- MojangLibraryDownloadInfo() {};
-
- // types
- typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr;
-
- // methods
- MojangDownloadInfo *getDownloadInfo(QString classifier)
- {
- if (classifier.isNull())
- {
- return artifact.get();
- }
-
- return classifiers[classifier].get();
- }
-
- // data
- MojangDownloadInfo::Ptr artifact;
- QMap<QString, MojangDownloadInfo::Ptr> classifiers;
-};
-
-
-
-struct MojangAssetIndexInfo : public MojangDownloadInfo
-{
- // types
- typedef std::shared_ptr<MojangAssetIndexInfo> Ptr;
-
- // 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;
- }
-
- // data
- int totalSize;
- QString id;
- bool known = true;
-};
diff --git a/api/logic/minecraft/MojangVersionFormat.cpp b/api/logic/minecraft/MojangVersionFormat.cpp
deleted file mode 100644
index f9cb2228..00000000
--- a/api/logic/minecraft/MojangVersionFormat.cpp
+++ /dev/null
@@ -1,383 +0,0 @@
-#include "MojangVersionFormat.h"
-#include "OneSixVersionFormat.h"
-#include "MojangDownloadInfo.h"
-
-#include "Json.h"
-using namespace Json;
-#include "ParseUtils.h"
-
-static const int CURRENT_MINIMUM_LAUNCHER_VERSION = 18;
-
-static MojangAssetIndexInfo::Ptr assetIndexFromJson (const QJsonObject &obj);
-static MojangDownloadInfo::Ptr downloadInfoFromJson (const QJsonObject &obj);
-static MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson (const QJsonObject &libObj);
-static QJsonObject assetIndexToJson (MojangAssetIndexInfo::Ptr assetidxinfo);
-static QJsonObject libDownloadInfoToJson (MojangLibraryDownloadInfo::Ptr libinfo);
-static QJsonObject downloadInfoToJson (MojangDownloadInfo::Ptr info);
-
-namespace Bits
-{
-static void readString(const QJsonObject &root, const QString &key, QString &variable)
-{
- 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");
-}
-
-static void readAssetIndex(MojangAssetIndexInfo::Ptr out, const QJsonObject &obj)
-{
- 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;
-}
-
-MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject &obj)
-{
- 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;
-}
-
-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;
-}
-
-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 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;
-}
-
-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, "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(""));
-
- 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");
- }
-
- QJsonObject root = doc.object();
-
- readVersionProperties(root, out.get());
-
- 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);
-
- auto lib = MojangVersionFormat::libraryFromJson(*out, 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);
- }
-}
-
-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);
- }
-
- // write the contents to a json document.
- {
- QJsonDocument out;
- out.setObject(root);
- return out;
- }
-}
-
-LibraryPtr MojangVersionFormat::libraryFromJson(ProblemContainer & problems, 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");
- }
- auto rawName = libObj.value("name").toString();
- out->m_name = rawName;
- if(!out->m_name.valid()) {
- problems.addProblem(ProblemSeverity::Error, QObject::tr("Library %1 name is broken and cannot be processed.").arg(rawName));
- }
-
- 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", library->m_name.serialize());
- 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
deleted file mode 100644
index 2871dae4..00000000
--- a/api/logic/minecraft/MojangVersionFormat.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <minecraft/VersionFile.h>
-#include <minecraft/Library.h>
-#include <QJsonDocument>
-#include <ProblemProvider.h>
-
-#include "multimc_logic_export.h"
-
-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);
-public:
- // version files / profile patches
- static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename);
- static QJsonDocument versionFileToJson(const VersionFilePtr &patch);
-
- // libraries
- static LibraryPtr libraryFromJson(ProblemContainer & problems, 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
deleted file mode 100644
index 9d095340..00000000
--- a/api/logic/minecraft/MojangVersionFormat_test.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#include <QTest>
-#include <QDebug>
-#include "TestUtil.h"
-
-#include "minecraft/MojangVersionFormat.h"
-
-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();
- }
-
-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());
- }
-};
-
-QTEST_GUILESS_MAIN(MojangVersionFormatTest)
-
-#include "MojangVersionFormat_test.moc"
-
diff --git a/api/logic/minecraft/OneSixVersionFormat.cpp b/api/logic/minecraft/OneSixVersionFormat.cpp
deleted file mode 100644
index 0329d70e..00000000
--- a/api/logic/minecraft/OneSixVersionFormat.cpp
+++ /dev/null
@@ -1,391 +0,0 @@
-#include "OneSixVersionFormat.h"
-#include <Json.h>
-#include "minecraft/ParseUtils.h"
-#include <minecraft/MojangVersionFormat.h>
-
-using namespace Json;
-
-static void readString(const QJsonObject &root, const QString &key, QString &variable)
-{
- if (root.contains(key))
- {
- variable = requireString(root.value(key));
- }
-}
-
-LibraryPtr OneSixVersionFormat::libraryFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename)
-{
- LibraryPtr out = MojangVersionFormat::libraryFromJson(problems, 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;
-}
-
-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(*out, 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(*out, 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(*out, libObj, filename);
- // and add to jar mods
- out->mods.append(lib);
- }
- }
-
- auto readLibs = [&](const char * which, QList<LibraryPtr> & outList)
- {
- for (auto libVal : requireArray(root.value(which)))
- {
- QJsonObject libObj = requireObject(libVal);
- // parse the library
- auto lib = libraryFromJson(*out, libObj, filename);
- outList.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", out->libraries);
- readLibs("+libraries", out->libraries);
- }
- else if (hasLibs)
- {
- readLibs("libraries", out->libraries);
- }
- else if(hasPlusLibs)
- {
- readLibs("+libraries", out->libraries);
- }
-
- if(root.contains("mavenFiles")) {
- readLibs("mavenFiles", out->mavenFiles);
- }
-
- // if we have mainJar, just use it
- if(root.contains("mainJar"))
- {
- QJsonObject libObj = requireObject(root, "mainJar");
- out->mainJar = libraryFromJson(*out, 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...
- else
- {
- out->addProblem(
- ProblemSeverity::Error,
- QObject::tr("URL for the main jar could not be determined - Mojang removed the server that we used as fallback.")
- );
- }
- 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->mavenFiles.isEmpty())
- {
- QJsonArray array;
- for (auto value: patch->mavenFiles)
- {
- array.append(OneSixVersionFormat::libraryToJson(value.get()));
- }
- root.insert("mavenFiles", 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(
- ProblemContainer & problems,
- 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 OneSixVersionFormat::jarModFromJson(ProblemContainer & problems, const QJsonObject& libObj, const QString& filename)
-{
- return libraryFromJson(problems, libObj, filename);
-}
-
-
-QJsonObject OneSixVersionFormat::jarModtoJson(Library *jarmod)
-{
- return libraryToJson(jarmod);
-}
-
-LibraryPtr OneSixVersionFormat::modFromJson(ProblemContainer & problems, const QJsonObject& libObj, const QString& filename)
-{
- return libraryFromJson(problems, libObj, filename);
-}
-
-QJsonObject OneSixVersionFormat::modtoJson(Library *jarmod)
-{
- return libraryToJson(jarmod);
-}
diff --git a/api/logic/minecraft/OneSixVersionFormat.h b/api/logic/minecraft/OneSixVersionFormat.h
deleted file mode 100644
index 1a091d88..00000000
--- a/api/logic/minecraft/OneSixVersionFormat.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include <minecraft/VersionFile.h>
-#include <minecraft/PackProfile.h>
-#include <minecraft/Library.h>
-#include <QJsonDocument>
-#include <ProblemProvider.h>
-
-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);
-
- // libraries
- static LibraryPtr libraryFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename);
- static QJsonObject libraryToJson(Library *library);
-
- // DEPRECATED: old 'plus' jar mods generated by the application
- static LibraryPtr plusJarModFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename, const QString &originalName);
-
- // new jar mods derived from libraries
- static LibraryPtr jarModFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename);
- static QJsonObject jarModtoJson(Library * jarmod);
-
- // mods, also derived from libraries
- static LibraryPtr modFromJson(ProblemContainer & problems, 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
deleted file mode 100644
index f6a4ed1c..00000000
--- a/api/logic/minecraft/OpSys.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "OpSys.h"
-
-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;
-}
-
-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";
- }
-} \ No newline at end of file
diff --git a/api/logic/minecraft/OpSys.h b/api/logic/minecraft/OpSys.h
deleted file mode 100644
index 63c750b1..00000000
--- a/api/logic/minecraft/OpSys.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <QString>
-enum OpSys
-{
- Os_Windows,
- Os_Linux,
- Os_OSX,
- Os_Other
-};
-
-OpSys OpSys_fromString(QString);
-QString OpSys_toString(OpSys);
-
-#ifdef Q_OS_WIN32
-#define currentSystem Os_Windows
-#else
-#ifdef Q_OS_MAC
-#define currentSystem Os_OSX
-#else
-#define currentSystem Os_Linux
-#endif
-#endif \ No newline at end of file
diff --git a/api/logic/minecraft/PackProfile.cpp b/api/logic/minecraft/PackProfile.cpp
deleted file mode 100644
index f6918116..00000000
--- a/api/logic/minecraft/PackProfile.cpp
+++ /dev/null
@@ -1,1225 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QFile>
-#include <QCryptographicHash>
-#include <Version.h>
-#include <QDir>
-#include <QJsonDocument>
-#include <QJsonArray>
-#include <QDebug>
-
-#include "Exception.h"
-#include <minecraft/OneSixVersionFormat.h>
-#include <FileSystem.h>
-#include <QSaveFile>
-#include <Env.h>
-#include <meta/Index.h>
-#include <minecraft/MinecraftInstance.h>
-#include <QUuid>
-#include <QTimer>
-#include <Json.h>
-
-#include "PackProfile.h"
-#include "PackProfile_p.h"
-#include "ComponentUpdateTask.h"
-
-PackProfile::PackProfile(MinecraftInstance * instance)
- : QAbstractListModel()
-{
- d.reset(new PackProfileData);
- 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, &PackProfile::disableInteraction);
- connect(&d->m_saveTimer, &QTimer::timeout, this, &PackProfile::save_internal);
-}
-
-PackProfile::~PackProfile()
-{
- saveNow();
-}
-
-// BEGIN: component file format
-
-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;
-}
-
-static ComponentPtr componentFromJsonV1(PackProfile * 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;
-}
-
-// Save the given component container data to a file
-static bool savePackProfile(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;
-}
-
-// Read the given file into component containers
-static bool loadPackProfile(PackProfile * 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 (const JSONValidationError &err)
- {
- qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
- container.clear();
- return false;
- }
- return true;
-}
-
-// END: component file format
-
-// BEGIN: save/load logic
-
-void PackProfile::saveNow()
-{
- if(saveIsScheduled())
- {
- d->m_saveTimer.stop();
- save_internal();
- }
-}
-
-bool PackProfile::saveIsScheduled() const
-{
- return d->dirty;
-}
-
-void PackProfile::buildingFromScratch()
-{
- d->loaded = true;
- d->dirty = true;
-}
-
-void PackProfile::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();
-}
-
-QString PackProfile::componentsFilePath() const
-{
- return FS::PathCombine(d->m_instance->instanceRoot(), "mmc-pack.json");
-}
-
-QString PackProfile::patchesPattern() const
-{
- return FS::PathCombine(d->m_instance->instanceRoot(), "patches", "%1.json");
-}
-
-QString PackProfile::patchFilePathForUid(const QString& uid) const
-{
- return patchesPattern().arg(uid);
-}
-
-void PackProfile::save_internal()
-{
- qDebug() << "Component list save performed now for" << d->m_instance->name();
- auto filename = componentsFilePath();
- savePackProfile(filename, d->components);
- d->dirty = false;
-}
-
-bool PackProfile::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(!loadPackProfile(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, &PackProfile::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, &PackProfile::componentDataChanged);
- d->components.append(component);
- d->componentIndex[component->m_uid] = component;
- }
- endResetModel();
- d->loaded = true;
- return true;
- }
-}
-
-void PackProfile::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);
- }
-}
-
-shared_qobject_ptr<Task> PackProfile::getCurrentTask()
-{
- return d->m_updateTask;
-}
-
-void PackProfile::resolve(Net::Mode netmode)
-{
- auto updateTask = new ComponentUpdateTask(ComponentUpdateTask::Mode::Resolution, netmode, this);
- d->m_updateTask.reset(updateTask);
- connect(updateTask, &ComponentUpdateTask::succeeded, this, &PackProfile::updateSucceeded);
- connect(updateTask, &ComponentUpdateTask::failed, this, &PackProfile::updateFailed);
- d->m_updateTask->start();
-}
-
-
-void PackProfile::updateSucceeded()
-{
- qDebug() << "Component list update/resolve task succeeded for" << d->m_instance->name();
- d->m_updateTask.reset();
- invalidateLaunchProfile();
-}
-
-void PackProfile::updateFailed(const QString& error)
-{
- 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;
- }
- }
-}
-
-/*
- * Migrate old layout to the component based one...
- * - Part of the version information is taken from `instance.cfg` (fed to this class from outside).
- * - Part is taken from the old order.json file.
- * - Part is loaded from loose json files in the instance's `patches` directory.
- */
-bool PackProfile::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 savePackProfile(componentsFilePath(), components);
-}
-
-// END: save/load
-
-void PackProfile::appendComponent(ComponentPtr component)
-{
- insertComponent(d->components.size(), component);
-}
-
-void PackProfile::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, &PackProfile::componentDataChanged);
- scheduleSave();
-}
-
-void PackProfile::componentDataChanged()
-{
- auto objPtr = qobject_cast<Component *>(sender());
- if(!objPtr)
- {
- qWarning() << "PackProfile 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() << "PackProfile got dataChenged signal from a Component which does not belong to it!";
-}
-
-bool PackProfile::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;
-}
-
-bool PackProfile::remove(const QString id)
-{
- int i = 0;
- for (auto patch : d->components)
- {
- if (patch->getID() == id)
- {
- return remove(i);
- }
- i++;
- }
- return false;
-}
-
-bool PackProfile::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;
-}
-
-bool PackProfile::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;
-}
-
-Component * PackProfile::getComponent(const QString &id)
-{
- auto iter = d->componentIndex.find(id);
- if (iter == d->componentIndex.end())
- {
- return nullptr;
- }
- return (*iter).get();
-}
-
-Component * PackProfile::getComponent(int index)
-{
- if(index < 0 || index >= d->components.size())
- {
- return nullptr;
- }
- return d->components[index].get();
-}
-
-QVariant PackProfile::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 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 PackProfile::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;
-}
-
-QVariant PackProfile::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();
-}
-
-// FIXME: zero precision mess
-Qt::ItemFlags PackProfile::flags(const QModelIndex &index) const
-{
- if (!index.isValid()) {
- return Qt::NoItemFlags;
- }
-
- Qt::ItemFlags outFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
-
- int row = index.row();
-
- 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() && !d->interactionDisabled)
- {
- outFlags |= Qt::ItemIsUserCheckable;
- }
- return outFlags;
-}
-
-int PackProfile::rowCount(const QModelIndex &parent) const
-{
- return d->components.size();
-}
-
-int PackProfile::columnCount(const QModelIndex &parent) const
-{
- return NUM_COLUMNS;
-}
-
-void PackProfile::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();
-}
-
-void PackProfile::invalidateLaunchProfile()
-{
- d->m_profile.reset();
-}
-
-void PackProfile::installJarMods(QStringList selectedFiles)
-{
- installJarMods_internal(selectedFiles);
-}
-
-void PackProfile::installCustomJar(QString selectedFile)
-{
- installCustomJar_internal(selectedFile);
-}
-
-bool PackProfile::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;
-}
-
-bool PackProfile::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 PackProfile::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;
-}
-
-bool PackProfile::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;
-}
-
-std::shared_ptr<LaunchProfile> PackProfile::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 (const Exception &error)
- {
- qWarning() << "Couldn't apply profile patches because: " << error.cause();
- }
- }
- return d->m_profile;
-}
-
-void PackProfile::setOldConfigVersion(const QString& uid, const QString& version)
-{
- if(version.isEmpty())
- {
- return;
- }
- d->m_oldConfigVersions[uid] = version;
-}
-
-bool PackProfile::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;
- }
-}
-
-QString PackProfile::getComponentVersion(const QString& uid) const
-{
- const auto iter = d->componentIndex.find(uid);
- if (iter != d->componentIndex.end())
- {
- return (*iter)->getVersion();
- }
- return QString();
-}
-
-void PackProfile::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/PackProfile.h b/api/logic/minecraft/PackProfile.h
deleted file mode 100644
index e55e6a58..00000000
--- a/api/logic/minecraft/PackProfile.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QAbstractListModel>
-
-#include <QString>
-#include <QList>
-#include <memory>
-
-#include "Library.h"
-#include "LaunchProfile.h"
-#include "Component.h"
-#include "ProfileUtils.h"
-#include "BaseVersion.h"
-#include "MojangDownloadInfo.h"
-#include "multimc_logic_export.h"
-#include "net/Mode.h"
-
-class MinecraftInstance;
-struct PackProfileData;
-class ComponentUpdateTask;
-
-class MULTIMC_LOGIC_EXPORT PackProfile : public QAbstractListModel
-{
- Q_OBJECT
- friend ComponentUpdateTask;
-public:
- enum Columns
- {
- NameColumn = 0,
- VersionColumn,
- NUM_COLUMNS
- };
-
- explicit PackProfile(MinecraftInstance * instance);
- virtual ~PackProfile();
-
- 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();
-
- /// install more jar mods
- void installJarMods(QStringList selectedFiles);
-
- /// 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);
-
- /// remove component file # - including files/records
- bool remove(const int index);
-
- /// remove component file by id - including files/records
- bool remove(const QString id);
-
- bool customize(int index);
-
- bool revertToBase(int index);
-
- /// reload the list, reload all components, resolve dependencies
- void reload(Net::Mode netmode);
-
- // reload all components, resolve dependencies
- void resolve(Net::Mode netmode);
-
- /// get current running task...
- shared_qobject_ptr<Task> getCurrentTask();
-
- 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);
-
- QString getComponentVersion(const QString &uid) const;
-
- bool setComponentVersion(const QString &uid, const QString &version, bool important = false);
-
- bool installEmpty(const QString &uid, const QString &name);
-
- QString patchFilePathForUid(const QString &uid) const;
-
- /// 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 index
- Component * getComponent(int index);
-
- /// Add the component to the internal list of patches
- // todo(merged): is this the best approach
- void appendComponent(ComponentPtr component);
-
-private:
- void scheduleSave();
- bool saveIsScheduled() const;
-
- /// apply the component patches. Catches all the errors and returns true/false for success/failure
- void invalidateLaunchProfile();
-
- /// 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;
-
-private slots:
- 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 installCustomJar_internal(QString filepath);
- bool removeComponent_internal(ComponentPtr patch);
-
- bool migratePreComponentConfig();
-
-private: /* data */
-
- std::unique_ptr<PackProfileData> d;
-};
diff --git a/api/logic/minecraft/PackProfile_p.h b/api/logic/minecraft/PackProfile_p.h
deleted file mode 100644
index 6cd2a4e5..00000000
--- a/api/logic/minecraft/PackProfile_p.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#pragma once
-
-#include "Component.h"
-#include <map>
-#include <QTimer>
-#include <QList>
-#include <QMap>
-
-class MinecraftInstance;
-using ComponentContainer = QList<ComponentPtr>;
-using ComponentIndex = QMap<QString, ComponentPtr>;
-
-struct PackProfileData
-{
- // the instance this belongs to
- MinecraftInstance *m_instance;
-
- // 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();
- }
-
- // 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/ParseUtils.cpp b/api/logic/minecraft/ParseUtils.cpp
deleted file mode 100644
index c9640e77..00000000
--- a/api/logic/minecraft/ParseUtils.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include <QDateTime>
-#include <QString>
-#include "ParseUtils.h"
-#include <QDebug>
-#include <cstdlib>
-
-QDateTime timeFromS3Time(QString str)
-{
- 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);
-
- int offsetSeconds = offsetAbs % 60;
- offsetAbs -= offsetSeconds;
-
- 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;
-}
diff --git a/api/logic/minecraft/ParseUtils.h b/api/logic/minecraft/ParseUtils.h
deleted file mode 100644
index 2b367a10..00000000
--- a/api/logic/minecraft/ParseUtils.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-#include <QString>
-#include <QDateTime>
-
-#include "multimc_logic_export.h"
-
-/// take the timestamp used by S3 and turn it into QDateTime
-MULTIMC_LOGIC_EXPORT QDateTime timeFromS3Time(QString str);
-
-/// take a timestamp and convert it into an S3 timestamp
-MULTIMC_LOGIC_EXPORT QString timeToS3Time(QDateTime);
diff --git a/api/logic/minecraft/ParseUtils_test.cpp b/api/logic/minecraft/ParseUtils_test.cpp
deleted file mode 100644
index fcc137e5..00000000
--- a/api/logic/minecraft/ParseUtils_test.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "minecraft/ParseUtils.h"
-
-class ParseUtilsTest : public QObject
-{
- 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);
- }
-
-};
-
-QTEST_GUILESS_MAIN(ParseUtilsTest)
-
-#include "ParseUtils_test.moc"
-
diff --git a/api/logic/minecraft/ProfileUtils.cpp b/api/logic/minecraft/ProfileUtils.cpp
deleted file mode 100644
index 8ca24cc8..00000000
--- a/api/logic/minecraft/ProfileUtils.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-#include "ProfileUtils.h"
-#include "minecraft/VersionFilterData.h"
-#include "minecraft/OneSixVersionFormat.h"
-#include "Json.h"
-#include <QDebug>
-
-#include <QJsonDocument>
-#include <QJsonArray>
-#include <QRegularExpression>
-#include <QSaveFile>
-
-namespace ProfileUtils
-{
-
-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;
- }
-
- // 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 (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;
-}
-
-static VersionFilePtr guardedParseJson(const QJsonDocument & doc,const QString &fileId,const QString &filepath,const bool &requireOrder)
-{
- 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);
-}
-
-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;
-}
-
-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);
-}
-
-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);
-}
-}
diff --git a/api/logic/minecraft/ProfileUtils.h b/api/logic/minecraft/ProfileUtils.h
deleted file mode 100644
index 351c36cb..00000000
--- a/api/logic/minecraft/ProfileUtils.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-#include "Library.h"
-#include "VersionFile.h"
-
-namespace ProfileUtils
-{
-typedef QStringList PatchOrder;
-
-/// Read and parse a OneSix format order file
-bool readOverrideOrders(QString path, PatchOrder &order);
-
-/// Write a OneSix format order file
-bool writeOverrideOrders(QString path, const PatchOrder &order);
-
-
-/// Parse a version file in JSON format
-VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder);
-
-/// Save a JSON file (in any format)
-bool saveJsonFile(const QJsonDocument doc, const QString & filename);
-
-/// Parse a version file in binary JSON format
-VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo);
-
-/// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files.
-void removeLwjglFromPatch(VersionFilePtr patch);
-
-}
diff --git a/api/logic/minecraft/Rule.cpp b/api/logic/minecraft/Rule.cpp
deleted file mode 100644
index af2861e3..00000000
--- a/api/logic/minecraft/Rule.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QJsonObject>
-#include <QJsonArray>
-
-#include "Rule.h"
-
-RuleAction RuleAction_fromString(QString name)
-{
- 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;
-
- 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 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 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;
-}
-
diff --git a/api/logic/minecraft/Rule.h b/api/logic/minecraft/Rule.h
deleted file mode 100644
index 7aa34d96..00000000
--- a/api/logic/minecraft/Rule.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QString>
-#include <QList>
-#include <QJsonObject>
-#include <memory>
-#include "OpSys.h"
-
-class Library;
-class Rule;
-
-enum RuleAction
-{
- Allow,
- Disallow,
- Defer
-};
-
-QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules);
-
-class Rule
-{
-protected:
- 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;
- }
-};
-
-class OsRule : public Rule
-{
-private:
- // 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)
- {
- }
-
-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));
- }
-};
-
-class ImplicitRule : public Rule
-{
-protected:
- 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));
- }
-};
diff --git a/api/logic/minecraft/VersionFile.cpp b/api/logic/minecraft/VersionFile.cpp
deleted file mode 100644
index d0a1a507..00000000
--- a/api/logic/minecraft/VersionFile.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include <QJsonArray>
-#include <QJsonDocument>
-
-#include <QDebug>
-
-#include "minecraft/VersionFile.h"
-#include "minecraft/Library.h"
-#include "minecraft/PackProfile.h"
-#include "ParseUtils.h"
-
-#include <Version.h>
-
-static bool isMinecraftVersion(const QString &uid)
-{
- 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);
- }
- for (auto mavenFile : mavenFiles)
- {
- profile->applyMavenFile(mavenFile);
- }
- 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);
- }
- }
-*/
diff --git a/api/logic/minecraft/VersionFile.h b/api/logic/minecraft/VersionFile.h
deleted file mode 100644
index b79fcd4f..00000000
--- a/api/logic/minecraft/VersionFile.h
+++ /dev/null
@@ -1,114 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QStringList>
-#include <QDateTime>
-#include <QSet>
-
-#include <memory>
-#include "minecraft/OpSys.h"
-#include "minecraft/Rule.h"
-#include "ProblemProvider.h"
-#include "Library.h"
-#include <meta/JsonFormat.h>
-
-class PackProfile;
-class VersionFile;
-class LaunchProfile;
-struct MojangDownloadInfo;
-struct MojangAssetIndexInfo;
-
-using VersionFilePtr = std::shared_ptr<VersionFile>;
-class VersionFile : public ProblemContainer
-{
- friend class MojangVersionFormat;
- friend class OneSixVersionFormat;
-public: /* methods */
- void applyTo(LaunchProfile* profile);
-
-public: /* data */
- /// 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: package ID of this package
- QString uid;
-
- /// MultiMC: version of this package
- QString version;
-
- /// MultiMC: DEPRECATED dependency on a Minecraft version
- QString dependsOnMinecraftVersion;
-
- /// Mojang: DEPRECATED used to version the Mojang version format
- int minimumLauncherVersion = -1;
-
- /// Mojang: DEPRECATED version of Minecraft this is
- QString minecraftVersion;
-
- /// Mojang: class to launch Minecraft with
- QString mainClass;
-
- /// 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: type of the Minecraft version
- QString type;
-
- /// 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 asset group to be used with Minecraft
- QString assets;
-
- /// MultiMC: list of tweaker mod arguments for launchwrapper
- QStringList addTweakers;
-
- /// Mojang: list of libraries to add to the version
- QList<LibraryPtr> libraries;
-
- /// MultiMC: list of maven files to put in the libraries folder, but not in classpath
- QList<LibraryPtr> mavenFiles;
-
- /// 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 jar mods added to this version
- QList<LibraryPtr> jarMods;
-
- /// 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 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;
-
-public:
- // 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;
-};
diff --git a/api/logic/minecraft/VersionFilterData.cpp b/api/logic/minecraft/VersionFilterData.cpp
deleted file mode 100644
index 38e7b60c..00000000
--- a/api/logic/minecraft/VersionFilterData.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "VersionFilterData.h"
-#include "ParseUtils.h"
-
-VersionFilterData g_VersionFilterData = VersionFilterData();
-
-VersionFilterData::VersionFilterData()
-{
- // 1.3.*
- auto libs13 =
- QList<FMLlib>{{"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b"},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f"},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82"}};
-
- fmlLibsMapping["1.3.2"] = libs13;
-
- // 1.4.*
- auto libs14 = QList<FMLlib>{
- {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b"},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f"},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82"},
- {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb"}};
-
- 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"},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a"},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58"},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65"},
- {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8"},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85"}};
-
- // 1.5.1
- fmlLibsMapping["1.5.1"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51"},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a"},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58"},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65"},
- {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6"},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85"}};
-
- // 1.5.2
- fmlLibsMapping["1.5.2"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51"},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a"},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58"},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65"},
- {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9"},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85"}};
-
- // 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"};
-
- java8BeginsDate = timeFromS3Time("2017-03-30T09:32:19+00:00");
- java16BeginsDate = timeFromS3Time("2021-05-12T11:19:15+00:00");
-}
diff --git a/api/logic/minecraft/VersionFilterData.h b/api/logic/minecraft/VersionFilterData.h
deleted file mode 100644
index d100acc3..00000000
--- a/api/logic/minecraft/VersionFilterData.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-#include <QMap>
-#include <QString>
-#include <QSet>
-#include <QDateTime>
-
-#include "multimc_logic_export.h"
-
-struct FMLlib
-{
- QString filename;
- QString checksum;
-};
-
-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;
- // release date of first version to require Java 8 (17w13a)
- QDateTime java8BeginsDate;
- // release data of first version to require Java 16 (21w19a)
- QDateTime java16BeginsDate;
-};
-extern VersionFilterData MULTIMC_LOGIC_EXPORT g_VersionFilterData;
diff --git a/api/logic/minecraft/World.cpp b/api/logic/minecraft/World.cpp
deleted file mode 100644
index a2b4dac7..00000000
--- a/api/logic/minecraft/World.cpp
+++ /dev/null
@@ -1,520 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QDir>
-#include <QString>
-#include <QDebug>
-#include <QSaveFile>
-#include "World.h"
-
-#include "GZip.h"
-#include <MMCZip.h>
-#include <FileSystem.h>
-#include <sstream>
-#include <io/stream_reader.h>
-#include <tag_string.h>
-#include <tag_primitive.h>
-#include <quazip.h>
-#include <quazipfile.h>
-#include <quazipdir.h>
-
-#include <QCoreApplication>
-
-#include <nonstd/optional>
-
-using nonstd::optional;
-using nonstd::nullopt;
-
-GameType::GameType(nonstd::optional<int> original):
- original(original)
-{
- if(!original) {
- return;
- }
- switch(*original) {
- case 0:
- type = GameType::Survival;
- break;
- case 1:
- type = GameType::Creative;
- break;
- case 2:
- type = GameType::Adventure;
- break;
- case 3:
- type = GameType::Spectator;
- break;
- default:
- break;
- }
-}
-
-QString GameType::toTranslatedString() const
-{
- 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;
- }
- if(original) {
- return QCoreApplication::translate("GameType", "Unknown (%1)").arg(*original);
- }
- return QCoreApplication::translate("GameType", "Undefined");
-}
-
-QString GameType::toLogString() const
-{
- switch (type)
- {
- case GameType::Survival:
- return "Survival";
- case GameType::Creative:
- return "Creative";
- case GameType::Adventure:
- return "Adventure";
- case GameType::Spectator:
- return "Spectator";
- default:
- break;
- }
- if(original) {
- return QString("Unknown (%1)").arg(*original);
- }
- return "Undefined";
-}
-
-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()));
- try {
- auto pair = nbt::io::read_compound(foo);
-
- if(pair.first != "")
- return nullptr;
-
- if(pair.second == nullptr)
- return nullptr;
-
- return std::move(pair.second);
- }
- catch (const nbt::io::input_error &e)
- {
- qWarning() << "Unable to parse level.dat:" << e.what();
- return nullptr;
- }
-}
-
-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;
-}
-
-QString getLevelDatFromFS(const QFileInfo &file)
-{
- 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();
-}
-
-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();
-}
-
-World::World(const QFileInfo &file)
-{
- repath(file);
-}
-
-void World::repath(const QFileInfo &file)
-{
- m_containerFile = file;
- m_folderName = file.fileName();
- if(file.isFile() && file.suffix() == "zip")
- {
- m_iconFile = QString();
- readFromZip(file);
- }
- else if(file.isDir())
- {
- QFileInfo assumedIconPath(file.absoluteFilePath() + "/icon.png");
- if(assumedIconPath.exists()) {
- m_iconFile = assumedIconPath.absoluteFilePath();
- }
- readFromFS(file);
- }
-}
-
-bool World::resetIcon()
-{
- if(m_iconFile.isNull()) {
- return false;
- }
- if(QFile(m_iconFile).remove()) {
- m_iconFile = QString();
- return true;
- }
- return false;
-}
-
-void World::readFromFS(const QFileInfo &file)
-{
- 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();
-}
-
-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);
- }
- 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;
-}
-
-namespace {
-
-optional<QString> read_string (nbt::value& parent, const char * name)
-{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::String)
- {
- return nullopt;
- }
- 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.";
- return nullopt;
- }
- catch (const std::bad_cast &e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to string.";
- return nullopt;
- }
-}
-
-optional<int64_t> read_long (nbt::value& parent, const char * name)
-{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::Long)
- {
- return nullopt;
- }
- 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.";
- return nullopt;
- }
- catch (const std::bad_cast &e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to long.";
- return nullopt;
- }
-}
-
-optional<int> read_int (nbt::value& parent, const char * name)
-{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::Int)
- {
- return nullopt;
- }
- 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.";
- return nullopt;
- }
- catch (const std::bad_cast &e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to int.";
- return nullopt;
- }
-}
-
-GameType read_gametype(nbt::value& parent, const char * name) {
- return GameType(read_int(parent, name));
-}
-
-}
-
-void World::loadFromLevelDat(QByteArray data)
-{
- auto levelData = parseLevelDat(data);
- if(!levelData)
- {
- is_valid = false;
- return;
- }
-
- nbt::value * valPtr = nullptr;
- try {
- valPtr = &levelData->at("Data");
- }
- catch (const std::out_of_range &e) {
- qWarning() << "Unable to read NBT tags from " << m_folderName << ":" << e.what();
- is_valid = false;
- return;
- }
- nbt::value &val = *valPtr;
-
- is_valid = val.get_type() == nbt::tag_type::Compound;
- if(!is_valid)
- return;
-
- auto name = read_string(val, "LevelName");
- m_actualName = name ? *name : m_folderName;
-
- auto timestamp = read_long(val, "LastPlayed");
- m_lastPlayed = timestamp ? QDateTime::fromMSecsSinceEpoch(*timestamp) : levelDatTime;
-
- m_gameType = read_gametype(val, "GameType");
-
- optional<int64_t> randomSeed;
- try {
- auto &WorldGen_val = val.at("WorldGenSettings");
- randomSeed = read_long(WorldGen_val, "seed");
- }
- catch (const std::out_of_range &) {}
- if(!randomSeed) {
- randomSeed = read_long(val, "RandomSeed");
- }
- m_randomSeed = randomSeed ? *randomSeed : 0;
-
- qDebug() << "World Name:" << m_actualName;
- qDebug() << "Last Played:" << m_lastPlayed.toString();
- if(randomSeed) {
- qDebug() << "Seed:" << *randomSeed;
- }
- qDebug() << "GameType:" << m_gameType.toLogString();
-}
-
-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;
-}
-
-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;
-}
-
-bool World::operator==(const World &other) const
-{
- return is_valid == other.is_valid && folderName() == other.folderName();
-}
diff --git a/api/logic/minecraft/World.h b/api/logic/minecraft/World.h
deleted file mode 100644
index 1d94d54d..00000000
--- a/api/logic/minecraft/World.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <QFileInfo>
-#include <QDateTime>
-#include <nonstd/optional>
-
-#include "multimc_logic_export.h"
-
-struct MULTIMC_LOGIC_EXPORT GameType {
- GameType() = default;
- GameType (nonstd::optional<int> original);
-
- QString toTranslatedString() const;
- QString toLogString() const;
-
- enum
- {
- Unknown = -1,
- Survival = 0,
- Creative,
- Adventure,
- Spectator
- } type = Unknown;
- nonstd::optional<int> original;
-};
-
-class MULTIMC_LOGIC_EXPORT World
-{
-public:
- World(const QFileInfo &file);
- QString folderName() const
- {
- return m_folderName;
- }
- QString name() const
- {
- return m_actualName;
- }
- QString iconFile() const
- {
- return m_iconFile;
- }
- 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);
- // remove the icon file, if any
- bool resetIcon();
-
- 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;
-
-private:
- 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;
- QString m_iconFile;
- QDateTime levelDatTime;
- QDateTime m_lastPlayed;
- int64_t m_randomSeed = 0;
- GameType m_gameType;
- bool is_valid = false;
-};
diff --git a/api/logic/minecraft/WorldList.cpp b/api/logic/minecraft/WorldList.cpp
deleted file mode 100644
index f6309dbd..00000000
--- a/api/logic/minecraft/WorldList.cpp
+++ /dev/null
@@ -1,387 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "WorldList.h"
-#include <FileSystem.h>
-#include <QMimeData>
-#include <QUrl>
-#include <QUuid>
-#include <QString>
-#include <QFileSystemWatcher>
-#include <QDebug>
-
-WorldList::WorldList(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);
- 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();
- }
-}
-
-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();
- }
-}
-
-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;
-}
-
-void WorldList::directoryChanged(QString path)
-{
- update();
-}
-
-bool WorldList::isValid()
-{
- 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;
-}
-
-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;
-}
-
-bool WorldList::resetIcon(int row)
-{
- if (row >= worlds.size() || row < 0)
- return false;
- World &m = worlds[row];
- if(m.resetIcon()) {
- emit dataChanged(index(row), index(row), {WorldList::IconFileRole});
- return true;
- }
- return false;
-}
-
-
-int WorldList::columnCount(const QModelIndex &parent) const
-{
- 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 GameModeColumn:
- return world.gameType().toTranslatedString();
-
- 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();
- }
- case IconFileRole:
- {
- return world.iconFile();
- }
- 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 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;
-}
-
-class WorldMimeData : public QMimeData
-{
-Q_OBJECT
-
-public:
- WorldMimeData(QList<World> worlds)
- {
- m_worlds = worlds;
-
- }
- 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);
- }
-private:
- 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);
-}
-
-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::DropActions WorldList::supportedDragActions() const
-{
- // 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;
-}
-
-void WorldList::installWorld(QFileInfo filename)
-{
- 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)
-{
- 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
deleted file mode 100644
index 740b1461..00000000
--- a/api/logic/minecraft/WorldList.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QList>
-#include <QString>
-#include <QDir>
-#include <QAbstractListModel>
-#include <QMimeData>
-#include "minecraft/World.h"
-
-#include "multimc_logic_export.h"
-
-class QFileSystemWatcher;
-
-class MULTIMC_LOGIC_EXPORT WorldList : public QAbstractListModel
-{
- Q_OBJECT
-public:
- enum Columns
- {
- NameColumn,
- GameModeColumn,
- LastPlayedColumn
- };
-
- enum Roles
- {
- ObjectRole = Qt::UserRole + 1,
- FolderRole,
- SeedRole,
- NameRole,
- GameModeRole,
- LastPlayedRole,
- IconFileRole
- };
-
- 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);
-
- /// Removes the world icon, if any
- virtual bool resetIcon(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);
-
-signals:
- void changed();
-
-protected:
- QFileSystemWatcher *m_watcher;
- bool is_watching;
- QDir m_dir;
- QList<World> worlds;
-};
diff --git a/api/logic/minecraft/auth-msa/BuildConfig.cpp.in b/api/logic/minecraft/auth-msa/BuildConfig.cpp.in
deleted file mode 100644
index 8f470e25..00000000
--- a/api/logic/minecraft/auth-msa/BuildConfig.cpp.in
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "BuildConfig.h"
-#include <QObject>
-
-const Config BuildConfig;
-
-Config::Config()
-{
- CLIENT_ID = "@MOJANGDEMO_CLIENT_ID@";
-}
diff --git a/api/logic/minecraft/auth-msa/BuildConfig.h b/api/logic/minecraft/auth-msa/BuildConfig.h
deleted file mode 100644
index 7a01d704..00000000
--- a/api/logic/minecraft/auth-msa/BuildConfig.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-#include <QString>
-
-class Config
-{
-public:
- Config();
- QString CLIENT_ID;
-};
-
-extern const Config BuildConfig;
diff --git a/api/logic/minecraft/auth-msa/CMakeLists.txt b/api/logic/minecraft/auth-msa/CMakeLists.txt
deleted file mode 100644
index 22777d1b..00000000
--- a/api/logic/minecraft/auth-msa/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-find_package(Qt5 COMPONENTS Core Gui Network Widgets REQUIRED)
-
-set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTOUIC ON)
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
-
-
-set(MOJANGDEMO_CLIENT_ID "" CACHE STRING "Client ID used for OAuth2 in mojangdemo")
-
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/BuildConfig.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp")
-
-set(mojang_SRCS
- main.cpp
- context.cpp
- context.h
-
- mainwindow.cpp
- mainwindow.h
- mainwindow.ui
-
- ${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp
- BuildConfig.h
-)
-
-add_executable( mojangdemo ${mojang_SRCS} )
-target_link_libraries( mojangdemo Katabasis Qt5::Gui Qt5::Widgets )
-target_include_directories(mojangdemo PRIVATE logic)
diff --git a/api/logic/minecraft/auth-msa/context.cpp b/api/logic/minecraft/auth-msa/context.cpp
deleted file mode 100644
index d7ecda30..00000000
--- a/api/logic/minecraft/auth-msa/context.cpp
+++ /dev/null
@@ -1,938 +0,0 @@
-#include <QNetworkAccessManager>
-#include <QNetworkRequest>
-#include <QNetworkReply>
-#include <QDesktopServices>
-#include <QMetaEnum>
-#include <QDebug>
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-
-#include <QUrlQuery>
-
-#include <QPixmap>
-#include <QPainter>
-
-#include "context.h"
-#include "katabasis/Globals.h"
-#include "katabasis/StoreQSettings.h"
-#include "katabasis/Requestor.h"
-#include "BuildConfig.h"
-
-using OAuth2 = Katabasis::OAuth2;
-using Requestor = Katabasis::Requestor;
-using Activity = Katabasis::Activity;
-
-Context::Context(QObject *parent) :
- QObject(parent)
-{
- mgr = new QNetworkAccessManager(this);
-
- Katabasis::OAuth2::Options opts;
- opts.scope = "XboxLive.signin offline_access";
- opts.clientIdentifier = BuildConfig.CLIENT_ID;
- opts.authorizationUrl = "https://login.live.com/oauth20_authorize.srf";
- opts.accessTokenUrl = "https://login.live.com/oauth20_token.srf";
- opts.listenerPorts = {28562, 28563, 28564, 28565, 28566};
-
- oauth2 = new OAuth2(opts, m_account.msaToken, this, mgr);
-
- connect(oauth2, &OAuth2::linkingFailed, this, &Context::onLinkingFailed);
- connect(oauth2, &OAuth2::linkingSucceeded, this, &Context::onLinkingSucceeded);
- connect(oauth2, &OAuth2::openBrowser, this, &Context::onOpenBrowser);
- connect(oauth2, &OAuth2::closeBrowser, this, &Context::onCloseBrowser);
- connect(oauth2, &OAuth2::activityChanged, this, &Context::onOAuthActivityChanged);
-}
-
-void Context::beginActivity(Activity activity) {
- if(isBusy()) {
- throw 0;
- }
- activity_ = activity;
- emit activityChanged(activity_);
-}
-
-void Context::finishActivity() {
- if(!isBusy()) {
- throw 0;
- }
- activity_ = Katabasis::Activity::Idle;
- m_account.validity_ = m_account.minecraftProfile.validity;
- emit activityChanged(activity_);
-}
-
-QString Context::gameToken() {
- return m_account.minecraftToken.token;
-}
-
-QString Context::userId() {
- return m_account.minecraftProfile.id;
-}
-
-QString Context::userName() {
- return m_account.minecraftProfile.name;
-}
-
-bool Context::silentSignIn() {
- if(isBusy()) {
- return false;
- }
- beginActivity(Activity::Refreshing);
- if(!oauth2->refresh()) {
- finishActivity();
- return false;
- }
-
- requestsDone = 0;
- xboxProfileSucceeded = false;
- mcAuthSucceeded = false;
-
- return true;
-}
-
-bool Context::signIn() {
- if(isBusy()) {
- return false;
- }
-
- requestsDone = 0;
- xboxProfileSucceeded = false;
- mcAuthSucceeded = false;
-
- beginActivity(Activity::LoggingIn);
- oauth2->unlink();
- m_account = AccountData();
- oauth2->link();
- return true;
-}
-
-bool Context::signOut() {
- if(isBusy()) {
- return false;
- }
- beginActivity(Activity::LoggingOut);
- oauth2->unlink();
- m_account = AccountData();
- finishActivity();
- return true;
-}
-
-
-void Context::onOpenBrowser(const QUrl &url) {
- QDesktopServices::openUrl(url);
-}
-
-void Context::onCloseBrowser() {
-
-}
-
-void Context::onLinkingFailed() {
- finishActivity();
-}
-
-void Context::onLinkingSucceeded() {
- auto *o2t = qobject_cast<OAuth2 *>(sender());
- if (!o2t->linked()) {
- finishActivity();
- return;
- }
- QVariantMap extraTokens = o2t->extraTokens();
- if (!extraTokens.isEmpty()) {
- qDebug() << "Extra tokens in response:";
- foreach (QString key, extraTokens.keys()) {
- qDebug() << "\t" << key << ":" << extraTokens.value(key);
- }
- }
- doUserAuth();
-}
-
-void Context::onOAuthActivityChanged(Katabasis::Activity activity) {
- // respond to activity change here
-}
-
-void Context::doUserAuth() {
- QString xbox_auth_template = R"XXX(
-{
- "Properties": {
- "AuthMethod": "RPS",
- "SiteName": "user.auth.xboxlive.com",
- "RpsTicket": "d=%1"
- },
- "RelyingParty": "http://auth.xboxlive.com",
- "TokenType": "JWT"
-}
-)XXX";
- auto xbox_auth_data = xbox_auth_template.arg(m_account.msaToken.token);
-
- QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate"));
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- auto *requestor = new Katabasis::Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onUserAuthDone);
- requestor->post(request, xbox_auth_data.toUtf8());
- qDebug() << "First layer of XBox auth ... commencing.";
-}
-
-namespace {
-bool getDateTime(QJsonValue value, QDateTime & out) {
- if(!value.isString()) {
- return false;
- }
- out = QDateTime::fromString(value.toString(), Qt::ISODateWithMs);
- return out.isValid();
-}
-
-bool getString(QJsonValue value, QString & out) {
- if(!value.isString()) {
- return false;
- }
- out = value.toString();
- return true;
-}
-
-bool getNumber(QJsonValue value, double & out) {
- if(!value.isDouble()) {
- return false;
- }
- out = value.toDouble();
- return true;
-}
-
-/*
-{
- "IssueInstant":"2020-12-07T19:52:08.4463796Z",
- "NotAfter":"2020-12-21T19:52:08.4463796Z",
- "Token":"token",
- "DisplayClaims":{
- "xui":[
- {
- "uhs":"userhash"
- }
- ]
- }
- }
-*/
-// TODO: handle error responses ...
-/*
-{
- "Identity":"0",
- "XErr":2148916238,
- "Message":"",
- "Redirect":"https://start.ui.xboxlive.com/AddChildToFamily"
-}
-// 2148916233 = missing XBox account
-// 2148916238 = child account not linked to a family
-*/
-
-bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output) {
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
- qDebug() << data;
- return false;
- }
-
- auto obj = doc.object();
- if(!getDateTime(obj.value("IssueInstant"), output.issueInstant)) {
- qWarning() << "User IssueInstant is not a timestamp";
- qDebug() << data;
- return false;
- }
- if(!getDateTime(obj.value("NotAfter"), output.notAfter)) {
- qWarning() << "User NotAfter is not a timestamp";
- qDebug() << data;
- return false;
- }
- if(!getString(obj.value("Token"), output.token)) {
- qWarning() << "User Token is not a timestamp";
- qDebug() << data;
- return false;
- }
- auto arrayVal = obj.value("DisplayClaims").toObject().value("xui");
- if(!arrayVal.isArray()) {
- qWarning() << "Missing xui claims array";
- qDebug() << data;
- return false;
- }
- bool foundUHS = false;
- for(auto item: arrayVal.toArray()) {
- if(!item.isObject()) {
- continue;
- }
- auto obj = item.toObject();
- if(obj.contains("uhs")) {
- foundUHS = true;
- } else {
- continue;
- }
- // consume all 'display claims' ... whatever that means
- for(auto iter = obj.begin(); iter != obj.end(); iter++) {
- QString claim;
- if(!getString(obj.value(iter.key()), claim)) {
- qWarning() << "display claim " << iter.key() << " is not a string...";
- qDebug() << data;
- return false;
- }
- output.extra[iter.key()] = claim;
- }
-
- break;
- }
- if(!foundUHS) {
- qWarning() << "Missing uhs";
- qDebug() << data;
- return false;
- }
- output.validity = Katabasis::Validity::Certain;
- qDebug() << data;
- return true;
-}
-
-}
-
-void Context::onUserAuthDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- finishActivity();
- return;
- }
-
- Katabasis::Token temp;
- if(!parseXTokenResponse(replyData, temp)) {
- qWarning() << "Could not parse user authentication response...";
- finishActivity();
- return;
- }
- m_account.userToken = temp;
-
- doSTSAuthMinecraft();
- doSTSAuthGeneric();
-}
-/*
- url = "https://xsts.auth.xboxlive.com/xsts/authorize"
- headers = {"x-xbl-contract-version": "1"}
- data = {
- "RelyingParty": relying_party,
- "TokenType": "JWT",
- "Properties": {
- "UserTokens": [self.user_token.token],
- "SandboxId": "RETAIL",
- },
- }
-*/
-void Context::doSTSAuthMinecraft() {
- QString xbox_auth_template = R"XXX(
-{
- "Properties": {
- "SandboxId": "RETAIL",
- "UserTokens": [
- "%1"
- ]
- },
- "RelyingParty": "rp://api.minecraftservices.com/",
- "TokenType": "JWT"
-}
-)XXX";
- auto xbox_auth_data = xbox_auth_template.arg(m_account.userToken.token);
-
- QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize"));
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onSTSAuthMinecraftDone);
- requestor->post(request, xbox_auth_data.toUtf8());
- qDebug() << "Second layer of XBox auth ... commencing.";
-}
-
-void Context::onSTSAuthMinecraftDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- finishActivity();
- return;
- }
-
- Katabasis::Token temp;
- if(!parseXTokenResponse(replyData, temp)) {
- qWarning() << "Could not parse authorization response for access to mojang services...";
- finishActivity();
- return;
- }
-
- if(temp.extra["uhs"] != m_account.userToken.extra["uhs"]) {
- qWarning() << "Server has changed user hash in the reply... something is wrong. ABORTING";
- qDebug() << replyData;
- finishActivity();
- return;
- }
- m_account.mojangservicesToken = temp;
-
- doMinecraftAuth();
-}
-
-void Context::doSTSAuthGeneric() {
- QString xbox_auth_template = R"XXX(
-{
- "Properties": {
- "SandboxId": "RETAIL",
- "UserTokens": [
- "%1"
- ]
- },
- "RelyingParty": "http://xboxlive.com",
- "TokenType": "JWT"
-}
-)XXX";
- auto xbox_auth_data = xbox_auth_template.arg(m_account.userToken.token);
-
- QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize"));
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onSTSAuthGenericDone);
- requestor->post(request, xbox_auth_data.toUtf8());
- qDebug() << "Second layer of XBox auth ... commencing.";
-}
-
-void Context::onSTSAuthGenericDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- finishActivity();
- return;
- }
-
- Katabasis::Token temp;
- if(!parseXTokenResponse(replyData, temp)) {
- qWarning() << "Could not parse authorization response for access to xbox API...";
- finishActivity();
- return;
- }
-
- if(temp.extra["uhs"] != m_account.userToken.extra["uhs"]) {
- qWarning() << "Server has changed user hash in the reply... something is wrong. ABORTING";
- qDebug() << replyData;
- finishActivity();
- return;
- }
- m_account.xboxApiToken = temp;
-
- doXBoxProfile();
-}
-
-
-void Context::doMinecraftAuth() {
- QString mc_auth_template = R"XXX(
-{
- "identityToken": "XBL3.0 x=%1;%2"
-}
-)XXX";
- auto data = mc_auth_template.arg(m_account.mojangservicesToken.extra["uhs"].toString(), m_account.mojangservicesToken.token);
-
- QNetworkRequest request = QNetworkRequest(QUrl("https://api.minecraftservices.com/authentication/login_with_xbox"));
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onMinecraftAuthDone);
- requestor->post(request, data.toUtf8());
- qDebug() << "Getting Minecraft access token...";
-}
-
-namespace {
-bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) {
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
- qDebug() << data;
- return false;
- }
-
- auto obj = doc.object();
- double expires_in = 0;
- if(!getNumber(obj.value("expires_in"), expires_in)) {
- qWarning() << "expires_in is not a valid number";
- qDebug() << data;
- return false;
- }
- auto currentTime = QDateTime::currentDateTimeUtc();
- output.issueInstant = currentTime;
- output.notAfter = currentTime.addSecs(expires_in);
-
- QString username;
- if(!getString(obj.value("username"), username)) {
- qWarning() << "username is not valid";
- qDebug() << data;
- return false;
- }
-
- // TODO: it's a JWT... validate it?
- if(!getString(obj.value("access_token"), output.token)) {
- qWarning() << "access_token is not valid";
- qDebug() << data;
- return false;
- }
- output.validity = Katabasis::Validity::Certain;
- qDebug() << data;
- return true;
-}
-}
-
-void Context::onMinecraftAuthDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- requestsDone++;
-
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- qDebug() << replyData;
- finishActivity();
- return;
- }
-
- if(!parseMojangResponse(replyData, m_account.minecraftToken)) {
- qWarning() << "Could not parse login_with_xbox response...";
- qDebug() << replyData;
- finishActivity();
- return;
- }
- mcAuthSucceeded = true;
-
- checkResult();
-}
-
-void Context::doXBoxProfile() {
- auto url = QUrl("https://profile.xboxlive.com/users/me/profile/settings");
- QUrlQuery q;
- q.addQueryItem(
- "settings",
- "GameDisplayName,AppDisplayName,AppDisplayPicRaw,GameDisplayPicRaw,"
- "PublicGamerpic,ShowUserAsAvatar,Gamerscore,Gamertag,ModernGamertag,ModernGamertagSuffix,"
- "UniqueModernGamertag,AccountTier,TenureLevel,XboxOneRep,"
- "PreferredColor,Location,Bio,Watermarks,"
- "RealName,RealNameOverride,IsQuarantined"
- );
- url.setQuery(q);
-
- QNetworkRequest request = QNetworkRequest(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- request.setRawHeader("x-xbl-contract-version", "3");
- request.setRawHeader("Authorization", QString("XBL3.0 x=%1;%2").arg(m_account.userToken.extra["uhs"].toString(), m_account.xboxApiToken.token).toUtf8());
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onXBoxProfileDone);
- requestor->get(request);
- qDebug() << "Getting Xbox profile...";
-}
-
-void Context::onXBoxProfileDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- requestsDone ++;
-
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- qDebug() << replyData;
- finishActivity();
- return;
- }
-
- qDebug() << "XBox profile: " << replyData;
-
- xboxProfileSucceeded = true;
- checkResult();
-}
-
-void Context::checkResult() {
- if(requestsDone != 2) {
- return;
- }
- if(mcAuthSucceeded && xboxProfileSucceeded) {
- doMinecraftProfile();
- }
- else {
- finishActivity();
- }
-}
-
-namespace {
-bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) {
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
- qDebug() << data;
- return false;
- }
-
- auto obj = doc.object();
- if(!getString(obj.value("id"), output.id)) {
- qWarning() << "minecraft profile id is not a string";
- qDebug() << data;
- return false;
- }
-
- if(!getString(obj.value("name"), output.name)) {
- qWarning() << "minecraft profile name is not a string";
- qDebug() << data;
- return false;
- }
-
- auto skinsArray = obj.value("skins").toArray();
- for(auto skin: skinsArray) {
- auto skinObj = skin.toObject();
- Skin skinOut;
- if(!getString(skinObj.value("id"), skinOut.id)) {
- continue;
- }
- QString state;
- if(!getString(skinObj.value("state"), state)) {
- continue;
- }
- if(state != "ACTIVE") {
- continue;
- }
- if(!getString(skinObj.value("url"), skinOut.url)) {
- continue;
- }
- if(!getString(skinObj.value("variant"), skinOut.variant)) {
- continue;
- }
- // we deal with only the active skin
- output.skin = skinOut;
- break;
- }
- auto capesArray = obj.value("capes").toArray();
- int i = -1;
- int currentCape = -1;
- for(auto cape: capesArray) {
- i++;
- auto capeObj = cape.toObject();
- Cape capeOut;
- if(!getString(capeObj.value("id"), capeOut.id)) {
- continue;
- }
- QString state;
- if(!getString(capeObj.value("state"), state)) {
- continue;
- }
- if(state == "ACTIVE") {
- currentCape = i;
- }
- if(!getString(capeObj.value("url"), capeOut.url)) {
- continue;
- }
- if(!getString(capeObj.value("alias"), capeOut.alias)) {
- continue;
- }
-
- // we deal with only the active skin
- output.capes.push_back(capeOut);
- }
- output.currentCape = currentCape;
- output.validity = Katabasis::Validity::Certain;
- return true;
-}
-}
-
-void Context::doMinecraftProfile() {
- auto url = QUrl("https://api.minecraftservices.com/minecraft/profile");
- QNetworkRequest request = QNetworkRequest(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- // request.setRawHeader("Accept", "application/json");
- request.setRawHeader("Authorization", QString("Bearer %1").arg(m_account.minecraftToken.token).toUtf8());
-
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onMinecraftProfileDone);
- requestor->get(request);
-}
-
-void Context::onMinecraftProfileDone(int, QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) {
- qDebug() << data;
- if (error == QNetworkReply::ContentNotFoundError) {
- m_account.minecraftProfile = MinecraftProfile();
- finishActivity();
- return;
- }
- if (error != QNetworkReply::NoError) {
- finishActivity();
- return;
- }
- if(!parseMinecraftProfile(data, m_account.minecraftProfile)) {
- m_account.minecraftProfile = MinecraftProfile();
- finishActivity();
- return;
- }
- doGetSkin();
-}
-
-void Context::doGetSkin() {
- auto url = QUrl(m_account.minecraftProfile.skin.url);
- QNetworkRequest request = QNetworkRequest(url);
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
- connect(requestor, &Requestor::finished, this, &Context::onSkinDone);
- requestor->get(request);
-}
-
-void Context::onSkinDone(int, QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair>) {
- if (error == QNetworkReply::NoError) {
- m_account.minecraftProfile.skin.data = data;
- }
- finishActivity();
-}
-
-namespace {
-void tokenToJSON(QJsonObject &parent, Katabasis::Token t, const char * tokenName) {
- if(t.validity == Katabasis::Validity::None || !t.persistent) {
- return;
- }
- QJsonObject out;
- if(t.issueInstant.isValid()) {
- out["iat"] = QJsonValue(t.issueInstant.toSecsSinceEpoch());
- }
-
- if(t.notAfter.isValid()) {
- out["exp"] = QJsonValue(t.notAfter.toSecsSinceEpoch());
- }
-
- if(!t.token.isEmpty()) {
- out["token"] = QJsonValue(t.token);
- }
- if(!t.refresh_token.isEmpty()) {
- out["refresh_token"] = QJsonValue(t.refresh_token);
- }
- if(t.extra.size()) {
- out["extra"] = QJsonObject::fromVariantMap(t.extra);
- }
- if(out.size()) {
- parent[tokenName] = out;
- }
-}
-
-Katabasis::Token tokenFromJSON(const QJsonObject &parent, const char * tokenName) {
- Katabasis::Token out;
- auto tokenObject = parent.value(tokenName).toObject();
- if(tokenObject.isEmpty()) {
- return out;
- }
- auto issueInstant = tokenObject.value("iat");
- if(issueInstant.isDouble()) {
- out.issueInstant = QDateTime::fromSecsSinceEpoch((int64_t) issueInstant.toDouble());
- }
-
- auto notAfter = tokenObject.value("exp");
- if(notAfter.isDouble()) {
- out.notAfter = QDateTime::fromSecsSinceEpoch((int64_t) notAfter.toDouble());
- }
-
- auto token = tokenObject.value("token");
- if(token.isString()) {
- out.token = token.toString();
- out.validity = Katabasis::Validity::Assumed;
- }
-
- auto refresh_token = tokenObject.value("refresh_token");
- if(refresh_token.isString()) {
- out.refresh_token = refresh_token.toString();
- }
-
- auto extra = tokenObject.value("extra");
- if(extra.isObject()) {
- out.extra = extra.toObject().toVariantMap();
- }
- return out;
-}
-
-void profileToJSON(QJsonObject &parent, MinecraftProfile p, const char * tokenName) {
- if(p.id.isEmpty()) {
- return;
- }
- QJsonObject out;
- out["id"] = QJsonValue(p.id);
- out["name"] = QJsonValue(p.name);
- if(p.currentCape != -1) {
- out["cape"] = p.capes[p.currentCape].id;
- }
-
- {
- QJsonObject skinObj;
- skinObj["id"] = p.skin.id;
- skinObj["url"] = p.skin.url;
- skinObj["variant"] = p.skin.variant;
- if(p.skin.data.size()) {
- skinObj["data"] = QString::fromLatin1(p.skin.data.toBase64());
- }
- out["skin"] = skinObj;
- }
-
- QJsonArray capesArray;
- for(auto & cape: p.capes) {
- QJsonObject capeObj;
- capeObj["id"] = cape.id;
- capeObj["url"] = cape.url;
- capeObj["alias"] = cape.alias;
- if(cape.data.size()) {
- capeObj["data"] = QString::fromLatin1(cape.data.toBase64());
- }
- capesArray.push_back(capeObj);
- }
- out["capes"] = capesArray;
- parent[tokenName] = out;
-}
-
-MinecraftProfile profileFromJSON(const QJsonObject &parent, const char * tokenName) {
- MinecraftProfile out;
- auto tokenObject = parent.value(tokenName).toObject();
- if(tokenObject.isEmpty()) {
- return out;
- }
- {
- auto idV = tokenObject.value("id");
- auto nameV = tokenObject.value("name");
- if(!idV.isString() || !nameV.isString()) {
- qWarning() << "mandatory profile attributes are missing or of unexpected type";
- return MinecraftProfile();
- }
- out.name = nameV.toString();
- out.id = idV.toString();
- }
-
- {
- auto skinV = tokenObject.value("skin");
- if(!skinV.isObject()) {
- qWarning() << "skin is missing";
- return MinecraftProfile();
- }
- auto skinObj = skinV.toObject();
- auto idV = skinObj.value("id");
- auto urlV = skinObj.value("url");
- auto variantV = skinObj.value("variant");
- if(!idV.isString() || !urlV.isString() || !variantV.isString()) {
- qWarning() << "mandatory skin attributes are missing or of unexpected type";
- return MinecraftProfile();
- }
- out.skin.id = idV.toString();
- out.skin.url = urlV.toString();
- out.skin.variant = variantV.toString();
-
- // data for skin is optional
- auto dataV = skinObj.value("data");
- if(dataV.isString()) {
- // TODO: validate base64
- out.skin.data = QByteArray::fromBase64(dataV.toString().toLatin1());
- }
- else if (!dataV.isUndefined()) {
- qWarning() << "skin data is something unexpected";
- return MinecraftProfile();
- }
- }
-
- auto capesV = tokenObject.value("capes");
- if(!capesV.isArray()) {
- qWarning() << "capes is not an array!";
- return MinecraftProfile();
- }
- auto capesArray = capesV.toArray();
- for(auto capeV: capesArray) {
- if(!capeV.isObject()) {
- qWarning() << "cape is not an object!";
- return MinecraftProfile();
- }
- auto capeObj = capeV.toObject();
- auto idV = capeObj.value("id");
- auto urlV = capeObj.value("url");
- auto aliasV = capeObj.value("alias");
- if(!idV.isString() || !urlV.isString() || !aliasV.isString()) {
- qWarning() << "mandatory skin attributes are missing or of unexpected type";
- return MinecraftProfile();
- }
- Cape cape;
- cape.id = idV.toString();
- cape.url = urlV.toString();
- cape.alias = aliasV.toString();
-
- // data for cape is optional.
- auto dataV = capeObj.value("data");
- if(dataV.isString()) {
- // TODO: validate base64
- cape.data = QByteArray::fromBase64(dataV.toString().toLatin1());
- }
- else if (!dataV.isUndefined()) {
- qWarning() << "cape data is something unexpected";
- return MinecraftProfile();
- }
- out.capes.push_back(cape);
- }
- out.validity = Katabasis::Validity::Assumed;
- return out;
-}
-
-}
-
-bool Context::resumeFromState(QByteArray data) {
- QJsonParseError error;
- auto doc = QJsonDocument::fromJson(data, &error);
- if(error.error != QJsonParseError::NoError) {
- qWarning() << "Failed to parse account data as JSON.";
- return false;
- }
- auto docObject = doc.object();
- m_account.msaToken = tokenFromJSON(docObject, "msa");
- m_account.userToken = tokenFromJSON(docObject, "utoken");
- m_account.xboxApiToken = tokenFromJSON(docObject, "xrp-main");
- m_account.mojangservicesToken = tokenFromJSON(docObject, "xrp-mc");
- m_account.minecraftToken = tokenFromJSON(docObject, "ygg");
-
- m_account.minecraftProfile = profileFromJSON(docObject, "profile");
-
- m_account.validity_ = m_account.minecraftProfile.validity;
-
- return true;
-}
-
-QByteArray Context::saveState() {
- QJsonDocument doc;
- QJsonObject output;
- tokenToJSON(output, m_account.msaToken, "msa");
- tokenToJSON(output, m_account.userToken, "utoken");
- tokenToJSON(output, m_account.xboxApiToken, "xrp-main");
- tokenToJSON(output, m_account.mojangservicesToken, "xrp-mc");
- tokenToJSON(output, m_account.minecraftToken, "ygg");
- profileToJSON(output, m_account.minecraftProfile, "profile");
- doc.setObject(output);
- return doc.toJson(QJsonDocument::Indented);
-}
diff --git a/api/logic/minecraft/auth-msa/context.h b/api/logic/minecraft/auth-msa/context.h
deleted file mode 100644
index f1ac99b8..00000000
--- a/api/logic/minecraft/auth-msa/context.h
+++ /dev/null
@@ -1,128 +0,0 @@
-#pragma once
-
-#include <QObject>
-#include <QList>
-#include <QVector>
-#include <QNetworkReply>
-#include <QImage>
-
-#include <katabasis/OAuth2.h>
-
-struct Skin {
- QString id;
- QString url;
- QString variant;
-
- QByteArray data;
-};
-
-struct Cape {
- QString id;
- QString url;
- QString alias;
-
- QByteArray data;
-};
-
-struct MinecraftProfile {
- QString id;
- QString name;
- Skin skin;
- int currentCape = -1;
- QVector<Cape> capes;
- Katabasis::Validity validity = Katabasis::Validity::None;
-};
-
-enum class AccountType {
- MSA,
- Mojang
-};
-
-struct AccountData {
- AccountType type = AccountType::MSA;
-
- Katabasis::Token msaToken;
- Katabasis::Token userToken;
- Katabasis::Token xboxApiToken;
- Katabasis::Token mojangservicesToken;
- Katabasis::Token minecraftToken;
-
- MinecraftProfile minecraftProfile;
- Katabasis::Validity validity_ = Katabasis::Validity::None;
-};
-
-class Context : public QObject
-{
- Q_OBJECT
-
-public:
- explicit Context(QObject *parent = 0);
-
- QByteArray saveState();
- bool resumeFromState(QByteArray data);
-
- bool isBusy() {
- return activity_ != Katabasis::Activity::Idle;
- };
- Katabasis::Validity validity() {
- return m_account.validity_;
- };
-
- bool signIn();
- bool silentSignIn();
- bool signOut();
-
- QString userName();
- QString userId();
- QString gameToken();
-signals:
- void succeeded();
- void failed();
- void activityChanged(Katabasis::Activity activity);
-
-private slots:
- void onLinkingSucceeded();
- void onLinkingFailed();
- void onOpenBrowser(const QUrl &url);
- void onCloseBrowser();
- void onOAuthActivityChanged(Katabasis::Activity activity);
-
-private:
- void doUserAuth();
- Q_SLOT void onUserAuthDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void doSTSAuthMinecraft();
- Q_SLOT void onSTSAuthMinecraftDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
- void doMinecraftAuth();
- Q_SLOT void onMinecraftAuthDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void doSTSAuthGeneric();
- Q_SLOT void onSTSAuthGenericDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
- void doXBoxProfile();
- Q_SLOT void onXBoxProfileDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void doMinecraftProfile();
- Q_SLOT void onMinecraftProfileDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void doGetSkin();
- Q_SLOT void onSkinDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void checkResult();
-
-private:
- void beginActivity(Katabasis::Activity activity);
- void finishActivity();
- void clearTokens();
-
-private:
- Katabasis::OAuth2 *oauth2 = nullptr;
-
- int requestsDone = 0;
- bool xboxProfileSucceeded = false;
- bool mcAuthSucceeded = false;
- Katabasis::Activity activity_ = Katabasis::Activity::Idle;
-
- AccountData m_account;
-
- QNetworkAccessManager *mgr = nullptr;
-};
diff --git a/api/logic/minecraft/auth-msa/main.cpp b/api/logic/minecraft/auth-msa/main.cpp
deleted file mode 100644
index 481e0126..00000000
--- a/api/logic/minecraft/auth-msa/main.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#include <QApplication>
-#include <QStringList>
-#include <QTimer>
-#include <QDebug>
-#include <QFile>
-#include <QSaveFile>
-
-#include "context.h"
-#include "mainwindow.h"
-
-void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
-{
- QByteArray localMsg = msg.toLocal8Bit();
- const char *file = context.file ? context.file : "";
- const char *function = context.function ? context.function : "";
- switch (type) {
- case QtDebugMsg:
- fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- case QtInfoMsg:
- fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- case QtWarningMsg:
- fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- case QtCriticalMsg:
- fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- case QtFatalMsg:
- fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- }
-}
-
-class Helper : public QObject {
- Q_OBJECT
-
-public:
- Helper(Context * context) : QObject(), context_(context), msg_(QString()) {
- QFile tokenCache("usercache.dat");
- if(tokenCache.open(QIODevice::ReadOnly)) {
- context_->resumeFromState(tokenCache.readAll());
- }
- }
-
-public slots:
- void run() {
- connect(context_, &Context::activityChanged, this, &Helper::onActivityChanged);
- context_->silentSignIn();
- }
-
- void onFailed() {
- qDebug() << "Login failed";
- }
-
- void onActivityChanged(Katabasis::Activity activity) {
- if(activity == Katabasis::Activity::Idle) {
- switch(context_->validity()) {
- case Katabasis::Validity::None: {
- // account is gone, remove it.
- QFile::remove("usercache.dat");
- }
- break;
- case Katabasis::Validity::Assumed: {
- // this is basically a soft-failed refresh. do nothing.
- }
- break;
- case Katabasis::Validity::Certain: {
- // stuff got refreshed / signed in. Save.
- auto data = context_->saveState();
- QSaveFile tokenCache("usercache.dat");
- if(tokenCache.open(QIODevice::WriteOnly)) {
- tokenCache.write(context_->saveState());
- tokenCache.commit();
- }
- }
- break;
- }
- }
- }
-
-private:
- Context *context_;
- QString msg_;
-};
-
-int main(int argc, char *argv[]) {
- qInstallMessageHandler(myMessageOutput);
- QApplication a(argc, argv);
- QCoreApplication::setOrganizationName("MultiMC");
- QCoreApplication::setApplicationName("MultiMC");
- Context c;
- Helper helper(&c);
- MainWindow window(&c);
- window.show();
- QTimer::singleShot(0, &helper, &Helper::run);
- return a.exec();
-}
-
-#include "main.moc"
diff --git a/api/logic/minecraft/auth-msa/mainwindow.cpp b/api/logic/minecraft/auth-msa/mainwindow.cpp
deleted file mode 100644
index d4e18dc0..00000000
--- a/api/logic/minecraft/auth-msa/mainwindow.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-#include "mainwindow.h"
-#include "ui_mainwindow.h"
-#include <QDebug>
-
-#include <QDesktopServices>
-
-#include "BuildConfig.h"
-
-MainWindow::MainWindow(Context * context, QWidget *parent) :
- QMainWindow(parent),
- m_context(context),
- m_ui(new Ui::MainWindow)
-{
- m_ui->setupUi(this);
- connect(m_ui->signInButton_MSA, &QPushButton::clicked, this, &MainWindow::SignInMSAClicked);
- connect(m_ui->signInButton_Mojang, &QPushButton::clicked, this, &MainWindow::SignInMojangClicked);
- connect(m_ui->signOutButton, &QPushButton::clicked, this, &MainWindow::SignOutClicked);
- connect(m_ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshClicked);
-
- // connect(m_context, &Context::linkingSucceeded, this, &MainWindow::SignInSucceeded);
- // connect(m_context, &Context::linkingFailed, this, &MainWindow::SignInFailed);
- connect(m_context, &Context::activityChanged, this, &MainWindow::ActivityChanged);
- ActivityChanged(Katabasis::Activity::Idle);
-}
-
-MainWindow::~MainWindow() = default;
-
-void MainWindow::ActivityChanged(Katabasis::Activity activity) {
- switch(activity) {
- case Katabasis::Activity::Idle: {
- if(m_context->validity() != Katabasis::Validity::None) {
- m_ui->signInButton_Mojang->setEnabled(false);
- m_ui->signInButton_MSA->setEnabled(false);
- m_ui->signOutButton->setEnabled(true);
- m_ui->refreshButton->setEnabled(true);
- m_ui->statusBar->showMessage(QString("Hello %1!").arg(m_context->userName()));
- }
- else {
- m_ui->signInButton_Mojang->setEnabled(true);
- m_ui->signInButton_MSA->setEnabled(true);
- m_ui->signOutButton->setEnabled(false);
- m_ui->refreshButton->setEnabled(false);
- m_ui->statusBar->showMessage("Press the login button to start.");
- }
- }
- break;
- case Katabasis::Activity::LoggingIn: {
- m_ui->signInButton_Mojang->setEnabled(false);
- m_ui->signInButton_MSA->setEnabled(false);
- m_ui->signOutButton->setEnabled(false);
- m_ui->refreshButton->setEnabled(false);
- m_ui->statusBar->showMessage("Logging in...");
- }
- break;
- case Katabasis::Activity::LoggingOut: {
- m_ui->signInButton_Mojang->setEnabled(false);
- m_ui->signInButton_MSA->setEnabled(false);
- m_ui->signOutButton->setEnabled(false);
- m_ui->refreshButton->setEnabled(false);
- m_ui->statusBar->showMessage("Logging out...");
- }
- break;
- case Katabasis::Activity::Refreshing: {
- m_ui->signInButton_Mojang->setEnabled(false);
- m_ui->signInButton_MSA->setEnabled(false);
- m_ui->signOutButton->setEnabled(false);
- m_ui->refreshButton->setEnabled(false);
- m_ui->statusBar->showMessage("Refreshing login...");
- }
- break;
- }
-}
-
-void MainWindow::SignInMSAClicked() {
- qDebug() << "Sign In MSA";
- // signIn({{"prompt", "select_account"}})
- // FIXME: wrong. very wrong. this should not be operating on the current context
- m_context->signIn();
-}
-
-void MainWindow::SignInMojangClicked() {
- qDebug() << "Sign In Mojang";
- // signIn({{"prompt", "select_account"}})
- // FIXME: wrong. very wrong. this should not be operating on the current context
- m_context->signIn();
-}
-
-
-void MainWindow::SignOutClicked() {
- qDebug() << "Sign Out";
- m_context->signOut();
-}
-
-void MainWindow::RefreshClicked() {
- qDebug() << "Refresh";
- m_context->silentSignIn();
-}
diff --git a/api/logic/minecraft/auth-msa/mainwindow.h b/api/logic/minecraft/auth-msa/mainwindow.h
deleted file mode 100644
index abde52d8..00000000
--- a/api/logic/minecraft/auth-msa/mainwindow.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <QMainWindow>
-#include <QScopedPointer>
-#include <QtNetwork>
-#include <katabasis/Bits.h>
-
-#include "context.h"
-
-namespace Ui {
-class MainWindow;
-}
-
-class MainWindow : public QMainWindow {
- Q_OBJECT
-
-public:
- explicit MainWindow(Context * context, QWidget *parent = nullptr);
- ~MainWindow() override;
-
-private slots:
- void SignInMojangClicked();
- void SignInMSAClicked();
-
- void SignOutClicked();
- void RefreshClicked();
-
- void ActivityChanged(Katabasis::Activity activity);
-
-private:
- Context* m_context;
- QScopedPointer<Ui::MainWindow> m_ui;
-};
-
diff --git a/api/logic/minecraft/auth-msa/mainwindow.ui b/api/logic/minecraft/auth-msa/mainwindow.ui
deleted file mode 100644
index 32b34128..00000000
--- a/api/logic/minecraft/auth-msa/mainwindow.ui
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>1037</width>
- <height>511</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>SmartMapsClient</string>
- </property>
- <property name="dockNestingEnabled">
- <bool>true</bool>
- </property>
- <widget class="QWidget" name="centralWidget">
- <layout class="QGridLayout" name="gridLayout">
- <item row="1" column="3">
- <widget class="QPushButton" name="signInButton_Mojang">
- <property name="text">
- <string>SignIn Mojang</string>
- </property>
- </widget>
- </item>
- <item row="3" column="3">
- <widget class="Line" name="line">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- <item row="1" column="0" rowspan="7" colspan="3">
- <widget class="QTreeView" name="accountView"/>
- </item>
- <item row="5" column="3">
- <widget class="QPushButton" name="refreshButton">
- <property name="text">
- <string>Refresh</string>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QPushButton" name="signInButton_MSA">
- <property name="text">
- <string>SignIn MSA</string>
- </property>
- </widget>
- </item>
- <item row="6" column="3">
- <widget class="QPushButton" name="signOutButton">
- <property name="text">
- <string>SignOut</string>
- </property>
- </widget>
- </item>
- <item row="4" column="3">
- <widget class="QPushButton" name="makeActiveButton">
- <property name="text">
- <string>Make Active</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QStatusBar" name="statusBar"/>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/api/logic/minecraft/auth/AuthSession.cpp b/api/logic/minecraft/auth/AuthSession.cpp
deleted file mode 100644
index 4e858796..00000000
--- a/api/logic/minecraft/auth/AuthSession.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "AuthSession.h"
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QStringList>
-
-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);
-
-}
-
-bool AuthSession::MakeOffline(QString offline_playername)
-{
- 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
deleted file mode 100644
index b397d9a1..00000000
--- a/api/logic/minecraft/auth/AuthSession.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QMultiMap>
-#include <memory>
-
-#include "multimc_logic_export.h"
-
-class MojangAccount;
-
-struct User
-{
- 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;
-};
-
-typedef std::shared_ptr<AuthSession> AuthSessionPtr;
diff --git a/api/logic/minecraft/auth/MojangAccount.cpp b/api/logic/minecraft/auth/MojangAccount.cpp
deleted file mode 100644
index f5853fe3..00000000
--- a/api/logic/minecraft/auth/MojangAccount.cpp
+++ /dev/null
@@ -1,315 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MojangAccount.h"
-#include "flows/RefreshTask.h"
-#include "flows/AuthenticateTask.h"
-
-#include <QUuid>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QRegExp>
-#include <QStringList>
-#include <QJsonDocument>
-
-#include <QDebug>
-
-MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
-{
- // The JSON object must at least have a username for it to be valid.
- if (!object.value("username").isString())
- {
- qCritical() << "Can't load Mojang account info from JSON object. Username field is "
- "missing or of the wrong type.";
- return nullptr;
- }
-
- QString username = object.value("username").toString("");
- QString clientToken = object.value("clientToken").toString("");
- QString accessToken = object.value("accessToken").toString("");
-
- QJsonArray profileArray = object.value("profiles").toArray();
- if (profileArray.size() < 1)
- {
- qCritical() << "Can't load Mojang account with username \"" << username
- << "\". No profiles found.";
- return nullptr;
- }
-
- QList<AccountProfile> profiles;
- for (QJsonValue profileVal : profileArray)
- {
- QJsonObject profileObject = profileVal.toObject();
- QString id = profileObject.value("id").toString("");
- QString name = profileObject.value("name").toString("");
- bool legacy = profileObject.value("legacy").toBool(false);
- if (id.isEmpty() || name.isEmpty())
- {
- qWarning() << "Unable to load a profile because it was missing an ID or a name.";
- continue;
- }
- profiles.append({id, name, legacy});
- }
-
- MojangAccountPtr account(new MojangAccount());
- if (object.value("user").isObject())
- {
- User u;
- QJsonObject userStructure = object.value("user").toObject();
- u.id = userStructure.value("id").toString();
- /*
- QJsonObject propMap = userStructure.value("properties").toObject();
- for(auto key: propMap.keys())
- {
- auto values = propMap.operator[](key).toArray();
- for(auto value: values)
- u.properties.insert(key, value.toString());
- }
- */
- account->m_user = u;
- }
- account->m_username = username;
- account->m_clientToken = clientToken;
- account->m_accessToken = accessToken;
- account->m_profiles = profiles;
-
- // Get the currently selected profile.
- QString currentProfile = object.value("activeProfile").toString("");
- if (!currentProfile.isEmpty())
- account->setCurrentProfile(currentProfile);
-
- return account;
-}
-
-MojangAccountPtr MojangAccount::createFromUsername(const QString &username)
-{
- MojangAccountPtr account(new MojangAccount());
- account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
- account->m_username = username;
- return account;
-}
-
-QJsonObject MojangAccount::saveToJson() const
-{
- QJsonObject json;
- json.insert("username", m_username);
- json.insert("clientToken", m_clientToken);
- json.insert("accessToken", m_accessToken);
-
- QJsonArray profileArray;
- for (AccountProfile profile : m_profiles)
- {
- QJsonObject profileObj;
- profileObj.insert("id", profile.id);
- profileObj.insert("name", profile.name);
- profileObj.insert("legacy", profile.legacy);
- profileArray.append(profileObj);
- }
- json.insert("profiles", profileArray);
-
- QJsonObject userStructure;
- {
- userStructure.insert("id", m_user.id);
- /*
- QJsonObject userAttrs;
- for(auto key: m_user.properties.keys())
- {
- auto array = QJsonArray::fromStringList(m_user.properties.values(key));
- userAttrs.insert(key, array);
- }
- userStructure.insert("properties", userAttrs);
- */
- }
- json.insert("user", userStructure);
-
- if (m_currentProfile != -1)
- json.insert("activeProfile", currentProfile()->id);
-
- return json;
-}
-
-bool MojangAccount::setCurrentProfile(const QString &profileId)
-{
- for (int i = 0; i < m_profiles.length(); i++)
- {
- if (m_profiles[i].id == profileId)
- {
- m_currentProfile = i;
- return true;
- }
- }
- return false;
-}
-
-const AccountProfile *MojangAccount::currentProfile() const
-{
- if (m_currentProfile == -1)
- return nullptr;
- return &m_profiles[m_currentProfile];
-}
-
-AccountStatus MojangAccount::accountStatus() const
-{
- if (m_accessToken.isEmpty())
- return NotVerified;
- else
- return Verified;
-}
-
-std::shared_ptr<YggdrasilTask> MojangAccount::login(AuthSessionPtr session, QString password)
-{
- Q_ASSERT(m_currentTask.get() == nullptr);
-
- // take care of the true offline status
- if (accountStatus() == NotVerified && password.isEmpty())
- {
- if (session)
- {
- session->status = AuthSession::RequiresPassword;
- fillSession(session);
- }
- return nullptr;
- }
-
- if(accountStatus() == Verified && !session->wants_online)
- {
- session->status = AuthSession::PlayableOffline;
- session->auth_server_online = false;
- fillSession(session);
- return nullptr;
- }
- else
- {
- if (password.isEmpty())
- {
- m_currentTask.reset(new RefreshTask(this));
- }
- else
- {
- m_currentTask.reset(new AuthenticateTask(this, password));
- }
- m_currentTask->assignSession(session);
-
- connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
- connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
- }
- return m_currentTask;
-}
-
-void MojangAccount::authSucceeded()
-{
- auto session = m_currentTask->getAssignedSession();
- if (session)
- {
- session->status =
- session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline;
- fillSession(session);
- session->auth_server_online = true;
- }
- m_currentTask.reset();
- emit changed();
-}
-
-void MojangAccount::authFailed(QString reason)
-{
- auto session = m_currentTask->getAssignedSession();
- // This is emitted when the yggdrasil tasks time out or are cancelled.
- // -> we treat the error as no-op
- if (m_currentTask->state() == YggdrasilTask::STATE_FAILED_SOFT)
- {
- if (session)
- {
- session->status = accountStatus() == Verified ? AuthSession::PlayableOffline
- : AuthSession::RequiresPassword;
- session->auth_server_online = false;
- fillSession(session);
- }
- }
- else
- {
- m_accessToken = QString();
- emit changed();
- if (session)
- {
- session->status = AuthSession::RequiresPassword;
- session->auth_server_online = true;
- fillSession(session);
- }
- }
- m_currentTask.reset();
-}
-
-void MojangAccount::fillSession(AuthSessionPtr session)
-{
- // the user name. you have to have an user name
- session->username = m_username;
- // volatile auth token
- session->access_token = m_accessToken;
- // the semi-permanent client token
- session->client_token = m_clientToken;
- if (currentProfile())
- {
- // profile name
- session->player_name = currentProfile()->name;
- // profile ID
- session->uuid = currentProfile()->id;
- // 'legacy' or 'mojang', depending on account type
- session->user_type = currentProfile()->legacy ? "legacy" : "mojang";
- if (!session->access_token.isEmpty())
- {
- session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id;
- }
- else
- {
- session->session = "-";
- }
- }
- else
- {
- session->player_name = "Player";
- session->session = "-";
- }
- session->u = user();
- session->m_accountPtr = shared_from_this();
-}
-
-void MojangAccount::decrementUses()
-{
- Usable::decrementUses();
- if(!isInUse())
- {
- emit changed();
- qWarning() << "Account" << m_username << "is no longer in use.";
- }
-}
-
-void MojangAccount::incrementUses()
-{
- bool wasInUse = isInUse();
- Usable::incrementUses();
- if(!wasInUse)
- {
- emit changed();
- qWarning() << "Account" << m_username << "is now in use.";
- }
-}
-
-void MojangAccount::invalidateClientToken()
-{
- m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
- emit changed();
-}
diff --git a/api/logic/minecraft/auth/MojangAccount.h b/api/logic/minecraft/auth/MojangAccount.h
deleted file mode 100644
index 30a5f2ff..00000000
--- a/api/logic/minecraft/auth/MojangAccount.h
+++ /dev/null
@@ -1,182 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QString>
-#include <QList>
-#include <QJsonObject>
-#include <QPair>
-#include <QMap>
-
-#include <memory>
-#include "AuthSession.h"
-#include "Usable.h"
-
-#include "multimc_logic_export.h"
-
-class Task;
-class YggdrasilTask;
-class MojangAccount;
-
-typedef std::shared_ptr<MojangAccount> MojangAccountPtr;
-Q_DECLARE_METATYPE(MojangAccountPtr)
-
-/**
- * A profile within someone's Mojang account.
- *
- * Currently, the profile system has not been implemented by Mojang yet,
- * but we might as well add some things for it in MultiMC right now so
- * we don't have to rip the code to pieces to add it later.
- */
-struct AccountProfile
-{
- QString id;
- QString name;
- bool legacy;
-};
-
-enum AccountStatus
-{
- NotVerified,
- Verified
-};
-
-/**
- * Object that stores information about a certain Mojang account.
- *
- * Said information may include things such as that account's username, client token, and access
- * token if the user chose to stay logged in.
- */
-class MULTIMC_LOGIC_EXPORT MojangAccount :
- public QObject,
- public Usable,
- public std::enable_shared_from_this<MojangAccount>
-{
- Q_OBJECT
-public: /* construction */
- //! Do not copy accounts. ever.
- explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete;
-
- //! Default constructor
- explicit MojangAccount(QObject *parent = 0) : QObject(parent) {};
-
- //! Creates an empty account for the specified user name.
- static MojangAccountPtr createFromUsername(const QString &username);
-
- //! Loads a MojangAccount from the given JSON object.
- static MojangAccountPtr loadFromJson(const QJsonObject &json);
-
- //! Saves a MojangAccount to a JSON object and returns it.
- QJsonObject saveToJson() const;
-
-public: /* manipulation */
- /**
- * Sets the currently selected profile to the profile with the given ID string.
- * If profileId is not in the list of available profiles, the function will simply return
- * false.
- */
- bool setCurrentProfile(const QString &profileId);
-
- /**
- * Attempt to login. Empty password means we use the token.
- * If the attempt fails because we already are performing some task, it returns false.
- */
- std::shared_ptr<YggdrasilTask> login(AuthSessionPtr session, QString password = QString());
- void invalidateClientToken();
-
-public: /* queries */
- const QString &username() const
- {
- return m_username;
- }
-
- const QString &clientToken() const
- {
- return m_clientToken;
- }
-
- const QString &accessToken() const
- {
- return m_accessToken;
- }
-
- const QList<AccountProfile> &profiles() const
- {
- return m_profiles;
- }
-
- const User &user()
- {
- return m_user;
- }
-
- //! Returns the currently selected profile (if none, returns nullptr)
- const AccountProfile *currentProfile() const;
-
- //! Returns whether the account is NotVerified, Verified or Online
- AccountStatus accountStatus() const;
-
-signals:
- /**
- * This signal is emitted when the account changes
- */
- void changed();
-
- // TODO: better signalling for the various possible state changes - especially errors
-
-protected: /* variables */
- QString m_username;
-
- // Used to identify the client - the user can have multiple clients for the same account
- // Think: different launchers, all connecting to the same account/profile
- QString m_clientToken;
-
- // Blank if not logged in.
- QString m_accessToken;
-
- // Index of the selected profile within the list of available
- // profiles. -1 if nothing is selected.
- int m_currentProfile = -1;
-
- // List of available profiles.
- QList<AccountProfile> m_profiles;
-
- // the user structure, whatever it is.
- User m_user;
-
- // current task we are executing here
- std::shared_ptr<YggdrasilTask> m_currentTask;
-
-protected: /* methods */
-
- void incrementUses() override;
- void decrementUses() override;
-
-private
-slots:
- void authSucceeded();
- void authFailed(QString reason);
-
-private:
- void fillSession(AuthSessionPtr session);
-
-public:
- friend class YggdrasilTask;
- friend class AuthenticateTask;
- friend class ValidateTask;
- friend class RefreshTask;
-};
diff --git a/api/logic/minecraft/auth/MojangAccountList.cpp b/api/logic/minecraft/auth/MojangAccountList.cpp
deleted file mode 100644
index e584cb3b..00000000
--- a/api/logic/minecraft/auth/MojangAccountList.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MojangAccountList.h"
-#include "MojangAccount.h"
-
-#include <QIODevice>
-#include <QFile>
-#include <QTextStream>
-#include <QJsonDocument>
-#include <QJsonArray>
-#include <QJsonObject>
-#include <QJsonParseError>
-#include <QDir>
-
-#include <QDebug>
-
-#include <FileSystem.h>
-
-#define ACCOUNT_LIST_FORMAT_VERSION 2
-
-MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent)
-{
-}
-
-MojangAccountPtr MojangAccountList::findAccount(const QString &username) const
-{
- for (int i = 0; i < count(); i++)
- {
- MojangAccountPtr account = at(i);
- if (account->username() == username)
- return account;
- }
- return nullptr;
-}
-
-const MojangAccountPtr MojangAccountList::at(int i) const
-{
- return MojangAccountPtr(m_accounts.at(i));
-}
-
-void MojangAccountList::addAccount(const MojangAccountPtr account)
-{
- int row = m_accounts.count();
- beginInsertRows(QModelIndex(), row, row);
- connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
- m_accounts.append(account);
- endInsertRows();
- onListChanged();
-}
-
-void MojangAccountList::removeAccount(const QString &username)
-{
- int idx = 0;
- for (auto account : m_accounts)
- {
- if (account->username() == username)
- {
- beginRemoveRows(QModelIndex(), idx, idx);
- m_accounts.removeOne(account);
- endRemoveRows();
- return;
- }
- idx++;
- }
- onListChanged();
-}
-
-void MojangAccountList::removeAccount(QModelIndex index)
-{
- int row = index.row();
- if(index.isValid() && row >= 0 && row < m_accounts.size())
- {
- auto & account = m_accounts[row];
- if(account == m_activeAccount)
- {
- m_activeAccount = nullptr;
- onActiveChanged();
- }
- beginRemoveRows(QModelIndex(), row, row);
- m_accounts.removeAt(index.row());
- endRemoveRows();
- onListChanged();
- }
-}
-
-MojangAccountPtr MojangAccountList::activeAccount() const
-{
- return m_activeAccount;
-}
-
-void MojangAccountList::setActiveAccount(const QString &username)
-{
- if (username.isEmpty() && m_activeAccount)
- {
- int idx = 0;
- auto prevActiveAcc = m_activeAccount;
- m_activeAccount = nullptr;
- for (MojangAccountPtr account : m_accounts)
- {
- if (account == prevActiveAcc)
- {
- emit dataChanged(index(idx), index(idx));
- }
- idx ++;
- }
- onActiveChanged();
- }
- else
- {
- auto currentActiveAccount = m_activeAccount;
- int currentActiveAccountIdx = -1;
- auto newActiveAccount = m_activeAccount;
- int newActiveAccountIdx = -1;
- int idx = 0;
- for (MojangAccountPtr account : m_accounts)
- {
- if (account->username() == username)
- {
- newActiveAccount = account;
- newActiveAccountIdx = idx;
- }
- if(currentActiveAccount == account)
- {
- currentActiveAccountIdx = idx;
- }
- idx++;
- }
- if(currentActiveAccount != newActiveAccount)
- {
- emit dataChanged(index(currentActiveAccountIdx), index(currentActiveAccountIdx));
- emit dataChanged(index(newActiveAccountIdx), index(newActiveAccountIdx));
- m_activeAccount = newActiveAccount;
- onActiveChanged();
- }
- }
-}
-
-void MojangAccountList::accountChanged()
-{
- // the list changed. there is no doubt.
- onListChanged();
-}
-
-void MojangAccountList::onListChanged()
-{
- if (m_autosave)
- // TODO: Alert the user if this fails.
- saveList();
-
- emit listChanged();
-}
-
-void MojangAccountList::onActiveChanged()
-{
- if (m_autosave)
- saveList();
-
- emit activeAccountChanged();
-}
-
-int MojangAccountList::count() const
-{
- return m_accounts.count();
-}
-
-QVariant MojangAccountList::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return QVariant();
-
- if (index.row() > count())
- return QVariant();
-
- MojangAccountPtr account = at(index.row());
-
- switch (role)
- {
- case Qt::DisplayRole:
- switch (index.column())
- {
- case NameColumn:
- return account->username();
-
- default:
- return QVariant();
- }
-
- case Qt::ToolTipRole:
- return account->username();
-
- case PointerRole:
- return qVariantFromValue(account);
-
- case Qt::CheckStateRole:
- switch (index.column())
- {
- case ActiveColumn:
- return account == m_activeAccount ? Qt::Checked : Qt::Unchecked;
- }
-
- default:
- return QVariant();
- }
-}
-
-QVariant MojangAccountList::headerData(int section, Qt::Orientation orientation, int role) const
-{
- switch (role)
- {
- case Qt::DisplayRole:
- switch (section)
- {
- case ActiveColumn:
- return tr("Active?");
-
- case NameColumn:
- return tr("Name");
-
- default:
- return QVariant();
- }
-
- case Qt::ToolTipRole:
- switch (section)
- {
- case NameColumn:
- return tr("The name of the version.");
-
- default:
- return QVariant();
- }
-
- default:
- return QVariant();
- }
-}
-
-int MojangAccountList::rowCount(const QModelIndex &) const
-{
- // Return count
- return count();
-}
-
-int MojangAccountList::columnCount(const QModelIndex &) const
-{
- return 2;
-}
-
-Qt::ItemFlags MojangAccountList::flags(const QModelIndex &index) const
-{
- if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
- {
- return Qt::NoItemFlags;
- }
-
- return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
-}
-
-bool MojangAccountList::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
- {
- return false;
- }
-
- if(role == Qt::CheckStateRole)
- {
- if(value == Qt::Checked)
- {
- MojangAccountPtr account = this->at(index.row());
- this->setActiveAccount(account->username());
- }
- }
-
- emit dataChanged(index, index);
- return true;
-}
-
-void MojangAccountList::updateListData(QList<MojangAccountPtr> versions)
-{
- beginResetModel();
- m_accounts = versions;
- endResetModel();
-}
-
-bool MojangAccountList::loadList(const QString &filePath)
-{
- QString path = filePath;
- if (path.isEmpty())
- path = m_listFilePath;
- if (path.isEmpty())
- {
- qCritical() << "Can't load Mojang account list. No file path given and no default set.";
- return false;
- }
-
- QFile file(path);
-
- // Try to open the file and fail if we can't.
- // TODO: We should probably report this error to the user.
- if (!file.open(QIODevice::ReadOnly))
- {
- qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
- return false;
- }
-
- // Read the file and close it.
- QByteArray jsonData = file.readAll();
- file.close();
-
- QJsonParseError parseError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
-
- // Fail if the JSON is invalid.
- if (parseError.error != QJsonParseError::NoError)
- {
- qCritical() << QString("Failed to parse account list file: %1 at offset %2")
- .arg(parseError.errorString(), QString::number(parseError.offset))
- .toUtf8();
- return false;
- }
-
- // Make sure the root is an object.
- if (!jsonDoc.isObject())
- {
- qCritical() << "Invalid account list JSON: Root should be an array.";
- return false;
- }
-
- QJsonObject root = jsonDoc.object();
-
- // Make sure the format version matches.
- if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION)
- {
- QString newName = "accounts-old.json";
- qWarning() << "Format version mismatch when loading account list. Existing one will be renamed to"
- << newName;
-
- // Attempt to rename the old version.
- file.rename(newName);
- return false;
- }
-
- // Now, load the accounts array.
- beginResetModel();
- QJsonArray accounts = root.value("accounts").toArray();
- for (QJsonValue accountVal : accounts)
- {
- QJsonObject accountObj = accountVal.toObject();
- MojangAccountPtr account = MojangAccount::loadFromJson(accountObj);
- if (account.get() != nullptr)
- {
- connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
- m_accounts.append(account);
- }
- else
- {
- qWarning() << "Failed to load an account.";
- }
- }
- // Load the active account.
- m_activeAccount = findAccount(root.value("activeAccount").toString(""));
- endResetModel();
- return true;
-}
-
-bool MojangAccountList::saveList(const QString &filePath)
-{
- QString path(filePath);
- if (path.isEmpty())
- path = m_listFilePath;
- if (path.isEmpty())
- {
- qCritical() << "Can't save Mojang account list. No file path given and no default set.";
- return false;
- }
-
- // make sure the parent folder exists
- if(!FS::ensureFilePathExists(path))
- return false;
-
- // make sure the file wasn't overwritten with a folder before (fixes a bug)
- QFileInfo finfo(path);
- if(finfo.isDir())
- {
- QDir badDir(path);
- badDir.removeRecursively();
- }
-
- qDebug() << "Writing account list to" << path;
-
- qDebug() << "Building JSON data structure.";
- // Build the JSON document to write to the list file.
- QJsonObject root;
-
- root.insert("formatVersion", ACCOUNT_LIST_FORMAT_VERSION);
-
- // Build a list of accounts.
- qDebug() << "Building account array.";
- QJsonArray accounts;
- for (MojangAccountPtr account : m_accounts)
- {
- QJsonObject accountObj = account->saveToJson();
- accounts.append(accountObj);
- }
-
- // Insert the account list into the root object.
- root.insert("accounts", accounts);
-
- if(m_activeAccount)
- {
- // Save the active account.
- root.insert("activeAccount", m_activeAccount->username());
- }
-
- // Create a JSON document object to convert our JSON to bytes.
- QJsonDocument doc(root);
-
- // Now that we're done building the JSON object, we can write it to the file.
- qDebug() << "Writing account list to file.";
- QFile file(path);
-
- // Try to open the file and fail if we can't.
- // TODO: We should probably report this error to the user.
- if (!file.open(QIODevice::WriteOnly))
- {
- qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
- return false;
- }
-
- // Write the JSON to the file.
- file.write(doc.toJson());
- file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser);
- file.close();
-
- qDebug() << "Saved account list to" << path;
-
- return true;
-}
-
-void MojangAccountList::setListFilePath(QString path, bool autosave)
-{
- m_listFilePath = path;
- m_autosave = autosave;
-}
-
-bool MojangAccountList::anyAccountIsValid()
-{
- for(auto account:m_accounts)
- {
- if(account->accountStatus() != NotVerified)
- return true;
- }
- return false;
-}
diff --git a/api/logic/minecraft/auth/MojangAccountList.h b/api/logic/minecraft/auth/MojangAccountList.h
deleted file mode 100644
index cc3a61a2..00000000
--- a/api/logic/minecraft/auth/MojangAccountList.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "MojangAccount.h"
-
-#include <QObject>
-#include <QVariant>
-#include <QAbstractListModel>
-#include <QSharedPointer>
-
-#include "multimc_logic_export.h"
-
-/*!
- * \brief List of available Mojang accounts.
- * This should be loaded in the background by MultiMC on startup.
- *
- * This class also inherits from QAbstractListModel. Methods from that
- * class determine how this list shows up in a list view. Said methods
- * all have a default implementation, but they can be overridden by subclasses to
- * change the behavior of the list.
- */
-class MULTIMC_LOGIC_EXPORT MojangAccountList : public QAbstractListModel
-{
- Q_OBJECT
-public:
- enum ModelRoles
- {
- PointerRole = 0x34B1CB48
- };
-
- enum VListColumns
- {
- // TODO: Add icon column.
-
- // First column - Active?
- ActiveColumn = 0,
-
- // Second column - Name
- NameColumn,
- };
-
- explicit MojangAccountList(QObject *parent = 0);
-
- //! Gets the account at the given index.
- virtual const MojangAccountPtr at(int i) const;
-
- //! Returns the number of accounts in the list.
- virtual int count() const;
-
- //////// List Model Functions ////////
- virtual QVariant data(const QModelIndex &index, int role) const;
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
- virtual int rowCount(const QModelIndex &parent) const;
- virtual int columnCount(const QModelIndex &parent) const;
- virtual Qt::ItemFlags flags(const QModelIndex &index) const;
- virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
-
- /*!
- * Adds a the given Mojang account to the account list.
- */
- virtual void addAccount(const MojangAccountPtr account);
-
- /*!
- * Removes the mojang account with the given username from the account list.
- */
- virtual void removeAccount(const QString &username);
-
- /*!
- * Removes the account at the given QModelIndex.
- */
- virtual void removeAccount(QModelIndex index);
-
- /*!
- * \brief Finds an account by its username.
- * \param The username of the account to find.
- * \return A const pointer to the account with the given username. NULL if
- * one doesn't exist.
- */
- virtual MojangAccountPtr findAccount(const QString &username) const;
-
- /*!
- * Sets the default path to save the list file to.
- * If autosave is true, this list will automatically save to the given path whenever it changes.
- * THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately
- * after calling this function to ensure an autosaved change doesn't overwrite the list you intended
- * to load.
- */
- virtual void setListFilePath(QString path, bool autosave = false);
-
- /*!
- * \brief Loads the account list from the given file path.
- * If the given file is an empty string (default), will load from the default account list file.
- * \return True if successful, otherwise false.
- */
- virtual bool loadList(const QString &file = "");
-
- /*!
- * \brief Saves the account list to the given file.
- * If the given file is an empty string (default), will save from the default account list file.
- * \return True if successful, otherwise false.
- */
- virtual bool saveList(const QString &file = "");
-
- /*!
- * \brief Gets a pointer to the account that the user has selected as their "active" account.
- * Which account is active can be overridden on a per-instance basis, but this will return the one that
- * is set as active globally.
- * \return The currently active MojangAccount. If there isn't an active account, returns a null pointer.
- */
- virtual MojangAccountPtr activeAccount() const;
-
- /*!
- * Sets the given account as the current active account.
- * If the username given is an empty string, sets the active account to nothing.
- */
- virtual void setActiveAccount(const QString &username);
-
- /*!
- * Returns true if any of the account is at least Validated
- */
- bool anyAccountIsValid();
-
-signals:
- /*!
- * Signal emitted to indicate that the account list has changed.
- * This will also fire if the value of an element in the list changes (will be implemented
- * later).
- */
- void listChanged();
-
- /*!
- * Signal emitted to indicate that the active account has changed.
- */
- void activeAccountChanged();
-
-public
-slots:
- /**
- * This is called when one of the accounts changes and the list needs to be updated
- */
- void accountChanged();
-
-protected:
- /*!
- * Called whenever the list changes.
- * This emits the listChanged() signal and autosaves the list (if autosave is enabled).
- */
- void onListChanged();
-
- /*!
- * Called whenever the active account changes.
- * Emits the activeAccountChanged() signal and autosaves the list if enabled.
- */
- void onActiveChanged();
-
- QList<MojangAccountPtr> m_accounts;
-
- /*!
- * Account that is currently active.
- */
- MojangAccountPtr m_activeAccount;
-
- //! Path to the account list file. Empty string if there isn't one.
- QString m_listFilePath;
-
- /*!
- * If true, the account list will automatically save to the account list path when it changes.
- * Ignored if m_listFilePath is blank.
- */
- bool m_autosave = false;
-
-protected
-slots:
- /*!
- * Updates this list with the given list of accounts.
- * This is done by copying each account in the given list and inserting it
- * into this one.
- * We need to do this so that we can set the parents of the accounts are set to this
- * account list. This can't be done in the load task, because the accounts the load
- * task creates are on the load task's thread and Qt won't allow their parents
- * to be set to something created on another thread.
- * To get around that problem, we invoke this method on the GUI thread, which
- * then copies the accounts and sets their parents correctly.
- * \param accounts List of accounts whose parents should be set.
- */
- virtual void updateListData(QList<MojangAccountPtr> versions);
-};
diff --git a/api/logic/minecraft/auth/YggdrasilTask.cpp b/api/logic/minecraft/auth/YggdrasilTask.cpp
deleted file mode 100644
index 0857b46b..00000000
--- a/api/logic/minecraft/auth/YggdrasilTask.cpp
+++ /dev/null
@@ -1,255 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "YggdrasilTask.h"
-#include "MojangAccount.h"
-
-#include <QObject>
-#include <QString>
-#include <QJsonObject>
-#include <QJsonDocument>
-#include <QNetworkReply>
-#include <QByteArray>
-
-#include <Env.h>
-
-#include <BuildConfig.h>
-
-#include <QDebug>
-
-YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent)
- : Task(parent), m_account(account)
-{
- changeState(STATE_CREATED);
-}
-
-void YggdrasilTask::executeTask()
-{
- changeState(STATE_SENDING_REQUEST);
-
- // Get the content of the request we're going to send to the server.
- QJsonDocument doc(getRequestContent());
-
- QUrl reqUrl(BuildConfig.AUTH_BASE + getEndpoint());
- QNetworkRequest netRequest(reqUrl);
- netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-
- QByteArray requestData = doc.toJson();
- m_netReply = ENV.qnam().post(netRequest, requestData);
- connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply);
- connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers);
- connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers);
- connect(m_netReply, &QNetworkReply::sslErrors, this, &YggdrasilTask::sslErrors);
- timeout_keeper.setSingleShot(true);
- timeout_keeper.start(timeout_max);
- counter.setSingleShot(false);
- counter.start(time_step);
- progress(0, timeout_max);
- connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abortByTimeout);
- connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat);
-}
-
-void YggdrasilTask::refreshTimers(qint64, qint64)
-{
- timeout_keeper.stop();
- timeout_keeper.start(timeout_max);
- progress(count = 0, timeout_max);
-}
-void YggdrasilTask::heartbeat()
-{
- count += time_step;
- progress(count, timeout_max);
-}
-
-bool YggdrasilTask::abort()
-{
- progress(timeout_max, timeout_max);
- // TODO: actually use this in a meaningful way
- m_aborted = YggdrasilTask::BY_USER;
- m_netReply->abort();
- return true;
-}
-
-void YggdrasilTask::abortByTimeout()
-{
- progress(timeout_max, timeout_max);
- // TODO: actually use this in a meaningful way
- m_aborted = YggdrasilTask::BY_TIMEOUT;
- m_netReply->abort();
-}
-
-void YggdrasilTask::sslErrors(QList<QSslError> errors)
-{
- int i = 1;
- for (auto error : errors)
- {
- qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString();
- auto cert = error.certificate();
- qCritical() << "Certificate in question:\n" << cert.toText();
- i++;
- }
-}
-
-void YggdrasilTask::processReply()
-{
- changeState(STATE_PROCESSING_RESPONSE);
-
- switch (m_netReply->error())
- {
- case QNetworkReply::NoError:
- break;
- case QNetworkReply::TimeoutError:
- changeState(STATE_FAILED_SOFT, tr("Authentication operation timed out."));
- return;
- case QNetworkReply::OperationCanceledError:
- changeState(STATE_FAILED_SOFT, tr("Authentication operation cancelled."));
- return;
- case QNetworkReply::SslHandshakeFailedError:
- changeState(
- STATE_FAILED_SOFT,
- tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>"
- "<ul>"
- "<li>You use Windows XP and need to <a "
- "href=\"https://www.microsoft.com/en-us/download/details.aspx?id=38918\">update "
- "your root certificates</a></li>"
- "<li>Some device on your network is interfering with SSL traffic. In that case, "
- "you have bigger worries than Minecraft not starting.</li>"
- "<li>Possibly something else. Check the MultiMC log file for details</li>"
- "</ul>"));
- return;
- // used for invalid credentials and similar errors. Fall through.
- case QNetworkReply::ContentAccessDenied:
- case QNetworkReply::ContentOperationNotPermittedError:
- break;
- default:
- changeState(STATE_FAILED_SOFT,
- tr("Authentication operation failed due to a network error: %1 (%2)")
- .arg(m_netReply->errorString()).arg(m_netReply->error()));
- return;
- }
-
- // Try to parse the response regardless of the response code.
- // Sometimes the auth server will give more information and an error code.
- QJsonParseError jsonError;
- QByteArray replyData = m_netReply->readAll();
- QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
- // Check the response code.
- int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
- if (responseCode == 200)
- {
- // If the response code was 200, then there shouldn't be an error. Make sure
- // anyways.
- // Also, sometimes an empty reply indicates success. If there was no data received,
- // pass an empty json object to the processResponse function.
- if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
- {
- processResponse(replyData.size() > 0 ? doc.object() : QJsonObject());
- return;
- }
- else
- {
- changeState(STATE_FAILED_SOFT, tr("Failed to parse authentication server response "
- "JSON response: %1 at offset %2.")
- .arg(jsonError.errorString())
- .arg(jsonError.offset));
- qCritical() << replyData;
- }
- return;
- }
-
- // If the response code was not 200, then Yggdrasil may have given us information
- // about the error.
- // If we can parse the response, then get information from it. Otherwise just say
- // there was an unknown error.
- if (jsonError.error == QJsonParseError::NoError)
- {
- // We were able to parse the server's response. Woo!
- // Call processError. If a subclass has overridden it then they'll handle their
- // stuff there.
- qDebug() << "The request failed, but the server gave us an error message. "
- "Processing error.";
- processError(doc.object());
- }
- else
- {
- // The server didn't say anything regarding the error. Give the user an unknown
- // error.
- qDebug()
- << "The request failed and the server gave no error message. Unknown error.";
- changeState(STATE_FAILED_SOFT,
- tr("An unknown error occurred when trying to communicate with the "
- "authentication server: %1").arg(m_netReply->errorString()));
- }
-}
-
-void YggdrasilTask::processError(QJsonObject responseData)
-{
- QJsonValue errorVal = responseData.value("error");
- QJsonValue errorMessageValue = responseData.value("errorMessage");
- QJsonValue causeVal = responseData.value("cause");
-
- if (errorVal.isString() && errorMessageValue.isString())
- {
- m_error = std::shared_ptr<Error>(new Error{
- errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")});
- changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose);
- }
- else
- {
- // Error is not in standard format. Don't set m_error and return unknown error.
- changeState(STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred."));
- }
-}
-
-QString YggdrasilTask::getStateMessage() const
-{
- switch (m_state)
- {
- case STATE_CREATED:
- return "Waiting...";
- case STATE_SENDING_REQUEST:
- return tr("Sending request to auth servers...");
- case STATE_PROCESSING_RESPONSE:
- return tr("Processing response from servers...");
- case STATE_SUCCEEDED:
- return tr("Authentication task succeeded.");
- case STATE_FAILED_SOFT:
- return tr("Failed to contact the authentication server.");
- case STATE_FAILED_HARD:
- return tr("Failed to authenticate.");
- default:
- return tr("...");
- }
-}
-
-void YggdrasilTask::changeState(YggdrasilTask::State newState, QString reason)
-{
- m_state = newState;
- setStatus(getStateMessage());
- if (newState == STATE_SUCCEEDED)
- {
- emitSucceeded();
- }
- else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT)
- {
- emitFailed(reason);
- }
-}
-
-YggdrasilTask::State YggdrasilTask::state()
-{
- return m_state;
-}
diff --git a/api/logic/minecraft/auth/YggdrasilTask.h b/api/logic/minecraft/auth/YggdrasilTask.h
deleted file mode 100644
index 8af2e132..00000000
--- a/api/logic/minecraft/auth/YggdrasilTask.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <tasks/Task.h>
-
-#include <QString>
-#include <QJsonObject>
-#include <QTimer>
-#include <qsslerror.h>
-
-#include "MojangAccount.h"
-
-class QNetworkReply;
-
-/**
- * A Yggdrasil task is a task that performs an operation on a given mojang account.
- */
-class YggdrasilTask : public Task
-{
- Q_OBJECT
-public:
- 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;
-
-protected
-slots:
- void processReply();
- void refreshTimers(qint64, qint64);
- void heartbeat();
- void sslErrors(QList<QSslError>);
-
- void changeState(State newState, QString reason=QString());
-public
-slots:
- 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;
-};
diff --git a/api/logic/minecraft/auth/flows/AuthenticateTask.cpp b/api/logic/minecraft/auth/flows/AuthenticateTask.cpp
deleted file mode 100644
index 2e8dc859..00000000
--- a/api/logic/minecraft/auth/flows/AuthenticateTask.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AuthenticateTask.h"
-#include "../MojangAccount.h"
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QVariant>
-
-#include <QDebug>
-#include <QUuid>
-
-AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password,
- QObject *parent)
- : YggdrasilTask(account, parent), m_password(password)
-{
-}
-
-QJsonObject AuthenticateTask::getRequestContent() const
-{
- /*
- * {
- * "agent": { // optional
- * "name": "Minecraft", // So far this is the only encountered value
- * "version": 1 // This number might be increased
- * // by the vanilla client in the future
- * },
- * "username": "mojang account name", // Can be an email address or player name for
- // unmigrated accounts
- * "password": "mojang account password",
- * "clientToken": "client identifier" // optional
- * "requestUser": true/false // request the user structure
- * }
- */
- QJsonObject req;
-
- {
- QJsonObject agent;
- // C++ makes string literals void* for some stupid reason, so we have to tell it
- // QString... Thanks Obama.
- agent.insert("name", QString("Minecraft"));
- agent.insert("version", 1);
- req.insert("agent", agent);
- }
-
- req.insert("username", m_account->username());
- req.insert("password", m_password);
- req.insert("requestUser", true);
-
- // If we already have a client token, give it to the server.
- // Otherwise, let the server give us one.
-
- if(m_account->m_clientToken.isEmpty())
- {
- auto uuid = QUuid::createUuid();
- auto uuidString = uuid.toString().remove('{').remove('-').remove('}');
- m_account->m_clientToken = uuidString;
- }
- req.insert("clientToken", m_account->m_clientToken);
-
- return req;
-}
-
-void AuthenticateTask::processResponse(QJsonObject responseData)
-{
- // Read the response data. We need to get the client token, access token, and the selected
- // profile.
- qDebug() << "Processing authentication response.";
- // qDebug() << responseData;
- // If we already have a client token, make sure the one the server gave us matches our
- // existing one.
- qDebug() << "Getting client token.";
- QString clientToken = responseData.value("clientToken").toString("");
- if (clientToken.isEmpty())
- {
- // Fail if the server gave us an empty client token
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
- return;
- }
- if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
- return;
- }
- // Set the client token.
- m_account->m_clientToken = clientToken;
-
- // Now, we set the access token.
- qDebug() << "Getting access token.";
- QString accessToken = responseData.value("accessToken").toString("");
- if (accessToken.isEmpty())
- {
- // Fail if the server didn't give us an access token.
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
- return;
- }
- // Set the access token.
- m_account->m_accessToken = accessToken;
-
- // Now we load the list of available profiles.
- // Mojang hasn't yet implemented the profile system,
- // but we might as well support what's there so we
- // don't have trouble implementing it later.
- qDebug() << "Loading profile list.";
- QJsonArray availableProfiles = responseData.value("availableProfiles").toArray();
- QList<AccountProfile> loadedProfiles;
- for (auto iter : availableProfiles)
- {
- QJsonObject profile = iter.toObject();
- // Profiles are easy, we just need their ID and name.
- QString id = profile.value("id").toString("");
- QString name = profile.value("name").toString("");
- bool legacy = profile.value("legacy").toBool(false);
-
- if (id.isEmpty() || name.isEmpty())
- {
- // This should never happen, but we might as well
- // warn about it if it does so we can debug it easily.
- // You never know when Mojang might do something truly derpy.
- qWarning() << "Found entry in available profiles list with missing ID or name "
- "field. Ignoring it.";
- }
-
- // Now, add a new AccountProfile entry to the list.
- loadedProfiles.append({id, name, legacy});
- }
- // Put the list of profiles we loaded into the MojangAccount object.
- m_account->m_profiles = loadedProfiles;
-
- // Finally, we set the current profile to the correct value. This is pretty simple.
- // We do need to make sure that the current profile that the server gave us
- // is actually in the available profiles list.
- // If it isn't, we'll just fail horribly (*shouldn't* ever happen, but you never know).
- qDebug() << "Setting current profile.";
- QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
- QString currentProfileId = currentProfile.value("id").toString("");
- if (currentProfileId.isEmpty())
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify a currently selected profile. The account exists, but likely isn't premium."));
- return;
- }
- if (!m_account->setCurrentProfile(currentProfileId))
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server specified a selected profile that wasn't in the available profiles list."));
- return;
- }
-
- // this is what the vanilla launcher passes to the userProperties launch param
- if (responseData.contains("user"))
- {
- User u;
- auto obj = responseData.value("user").toObject();
- u.id = obj.value("id").toString();
- auto propArray = obj.value("properties").toArray();
- for (auto prop : propArray)
- {
- auto propTuple = prop.toObject();
- auto name = propTuple.value("name").toString();
- auto value = propTuple.value("value").toString();
- u.properties.insert(name, value);
- }
- m_account->m_user = u;
- }
-
- // We've made it through the minefield of possible errors. Return true to indicate that
- // we've succeeded.
- qDebug() << "Finished reading authentication response.";
- changeState(STATE_SUCCEEDED);
-}
-
-QString AuthenticateTask::getEndpoint() const
-{
- return "authenticate";
-}
-
-QString AuthenticateTask::getStateMessage() const
-{
- switch (m_state)
- {
- case STATE_SENDING_REQUEST:
- return tr("Authenticating: Sending request...");
- case STATE_PROCESSING_RESPONSE:
- return tr("Authenticating: Processing response...");
- default:
- return YggdrasilTask::getStateMessage();
- }
-}
diff --git a/api/logic/minecraft/auth/flows/AuthenticateTask.h b/api/logic/minecraft/auth/flows/AuthenticateTask.h
deleted file mode 100644
index 4c14eec7..00000000
--- a/api/logic/minecraft/auth/flows/AuthenticateTask.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "../YggdrasilTask.h"
-
-#include <QObject>
-#include <QString>
-#include <QJsonObject>
-
-/**
- * The authenticate task takes a MojangAccount with no access token and password and attempts to
- * authenticate with Mojang's servers.
- * If successful, it will set the MojangAccount's access token.
- */
-class AuthenticateTask : public YggdrasilTask
-{
- Q_OBJECT
-public:
- AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0);
-
-protected:
- virtual QJsonObject getRequestContent() const override;
-
- virtual QString getEndpoint() const override;
-
- virtual void processResponse(QJsonObject responseData) override;
-
- virtual QString getStateMessage() const override;
-
-private:
- QString m_password;
-};
diff --git a/api/logic/minecraft/auth/flows/RefreshTask.cpp b/api/logic/minecraft/auth/flows/RefreshTask.cpp
deleted file mode 100644
index ecba178d..00000000
--- a/api/logic/minecraft/auth/flows/RefreshTask.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RefreshTask.h"
-#include "../MojangAccount.h"
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QVariant>
-
-#include <QDebug>
-
-RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account)
-{
-}
-
-QJsonObject RefreshTask::getRequestContent() const
-{
- /*
- * {
- * "clientToken": "client identifier"
- * "accessToken": "current access token to be refreshed"
- * "selectedProfile": // specifying this causes errors
- * {
- * "id": "profile ID"
- * "name": "profile name"
- * }
- * "requestUser": true/false // request the user structure
- * }
- */
- QJsonObject req;
- req.insert("clientToken", m_account->m_clientToken);
- req.insert("accessToken", m_account->m_accessToken);
- /*
- {
- auto currentProfile = m_account->currentProfile();
- QJsonObject profile;
- profile.insert("id", currentProfile->id());
- profile.insert("name", currentProfile->name());
- req.insert("selectedProfile", profile);
- }
- */
- req.insert("requestUser", true);
-
- return req;
-}
-
-void RefreshTask::processResponse(QJsonObject responseData)
-{
- // Read the response data. We need to get the client token, access token, and the selected
- // profile.
- qDebug() << "Processing authentication response.";
-
- // qDebug() << responseData;
- // If we already have a client token, make sure the one the server gave us matches our
- // existing one.
- QString clientToken = responseData.value("clientToken").toString("");
- if (clientToken.isEmpty())
- {
- // Fail if the server gave us an empty client token
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
- return;
- }
- if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
- return;
- }
-
- // Now, we set the access token.
- qDebug() << "Getting new access token.";
- QString accessToken = responseData.value("accessToken").toString("");
- if (accessToken.isEmpty())
- {
- // Fail if the server didn't give us an access token.
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
- return;
- }
-
- // we validate that the server responded right. (our current profile = returned current
- // profile)
- QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
- QString currentProfileId = currentProfile.value("id").toString("");
- if (m_account->currentProfile()->id != currentProfileId)
- {
- changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify the same prefile as expected."));
- return;
- }
-
- // this is what the vanilla launcher passes to the userProperties launch param
- if (responseData.contains("user"))
- {
- User u;
- auto obj = responseData.value("user").toObject();
- u.id = obj.value("id").toString();
- auto propArray = obj.value("properties").toArray();
- for (auto prop : propArray)
- {
- auto propTuple = prop.toObject();
- auto name = propTuple.value("name").toString();
- auto value = propTuple.value("value").toString();
- u.properties.insert(name, value);
- }
- m_account->m_user = u;
- }
-
- // We've made it through the minefield of possible errors. Return true to indicate that
- // we've succeeded.
- qDebug() << "Finished reading refresh response.";
- // Reset the access token.
- m_account->m_accessToken = accessToken;
- changeState(STATE_SUCCEEDED);
-}
-
-QString RefreshTask::getEndpoint() const
-{
- return "refresh";
-}
-
-QString RefreshTask::getStateMessage() const
-{
- switch (m_state)
- {
- case STATE_SENDING_REQUEST:
- return tr("Refreshing login token...");
- case STATE_PROCESSING_RESPONSE:
- return tr("Refreshing login token: Processing response...");
- default:
- return YggdrasilTask::getStateMessage();
- }
-}
diff --git a/api/logic/minecraft/auth/flows/RefreshTask.h b/api/logic/minecraft/auth/flows/RefreshTask.h
deleted file mode 100644
index f0840dda..00000000
--- a/api/logic/minecraft/auth/flows/RefreshTask.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "../YggdrasilTask.h"
-
-#include <QObject>
-#include <QString>
-#include <QJsonObject>
-
-/**
- * The authenticate task takes a MojangAccount with a possibly timed-out access token
- * and attempts to authenticate with Mojang's servers.
- * If successful, it will set the new access token. The token is considered validated.
- */
-class RefreshTask : public YggdrasilTask
-{
- Q_OBJECT
-public:
- RefreshTask(MojangAccount * account);
-
-protected:
- virtual QJsonObject getRequestContent() const override;
-
- virtual QString getEndpoint() const override;
-
- virtual void processResponse(QJsonObject responseData) override;
-
- virtual QString getStateMessage() const override;
-};
-
diff --git a/api/logic/minecraft/auth/flows/ValidateTask.cpp b/api/logic/minecraft/auth/flows/ValidateTask.cpp
deleted file mode 100644
index 6b3f0a65..00000000
--- a/api/logic/minecraft/auth/flows/ValidateTask.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ValidateTask.h"
-#include "../MojangAccount.h"
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QVariant>
-
-#include <QDebug>
-
-ValidateTask::ValidateTask(MojangAccount * account, QObject *parent)
- : YggdrasilTask(account, parent)
-{
-}
-
-QJsonObject ValidateTask::getRequestContent() const
-{
- QJsonObject req;
- req.insert("accessToken", m_account->m_accessToken);
- return req;
-}
-
-void ValidateTask::processResponse(QJsonObject responseData)
-{
- // Assume that if processError wasn't called, then the request was successful.
- changeState(YggdrasilTask::STATE_SUCCEEDED);
-}
-
-QString ValidateTask::getEndpoint() const
-{
- return "validate";
-}
-
-QString ValidateTask::getStateMessage() const
-{
- switch (m_state)
- {
- case YggdrasilTask::STATE_SENDING_REQUEST:
- return tr("Validating access token: Sending request...");
- case YggdrasilTask::STATE_PROCESSING_RESPONSE:
- return tr("Validating access token: Processing response...");
- default:
- return YggdrasilTask::getStateMessage();
- }
-}
diff --git a/api/logic/minecraft/auth/flows/ValidateTask.h b/api/logic/minecraft/auth/flows/ValidateTask.h
deleted file mode 100644
index 986c2e9f..00000000
--- a/api/logic/minecraft/auth/flows/ValidateTask.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * :FIXME: DEAD CODE, DEAD CODE, DEAD CODE! :FIXME:
- */
-
-#pragma once
-
-#include "../YggdrasilTask.h"
-
-#include <QObject>
-#include <QString>
-#include <QJsonObject>
-
-/**
- * The validate task takes a MojangAccount and checks to make sure its access token is valid.
- */
-class ValidateTask : public YggdrasilTask
-{
- Q_OBJECT
-public:
- ValidateTask(MojangAccount *account, QObject *parent = 0);
-
-protected:
- virtual QJsonObject getRequestContent() const override;
-
- virtual QString getEndpoint() const override;
-
- virtual void processResponse(QJsonObject responseData) override;
-
- virtual QString getStateMessage() const override;
-
-private:
-};
diff --git a/api/logic/minecraft/gameoptions/GameOptions.cpp b/api/logic/minecraft/gameoptions/GameOptions.cpp
deleted file mode 100644
index e547b32a..00000000
--- a/api/logic/minecraft/gameoptions/GameOptions.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-#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
deleted file mode 100644
index c6d25492..00000000
--- a/api/logic/minecraft/gameoptions/GameOptions.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#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
deleted file mode 100644
index a1180f0a..00000000
--- a/api/logic/minecraft/launch/ClaimAccount.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "ClaimAccount.h"
-#include <launch/LaunchTask.h>
-
-ClaimAccount::ClaimAccount(LaunchTask* parent, AuthSessionPtr session): LaunchStep(parent)
-{
- if(session->status == AuthSession::Status::PlayableOnline)
- {
- m_account = session->m_accountPtr;
- }
-}
-
-void ClaimAccount::executeTask()
-{
- if(m_account)
- {
- lock.reset(new UseLock(m_account));
- emitSucceeded();
- }
-}
-
-void ClaimAccount::finalize()
-{
- lock.reset();
-}
diff --git a/api/logic/minecraft/launch/ClaimAccount.h b/api/logic/minecraft/launch/ClaimAccount.h
deleted file mode 100644
index c5bd75f3..00000000
--- a/api/logic/minecraft/launch/ClaimAccount.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <minecraft/auth/MojangAccount.h>
-
-class ClaimAccount: public LaunchStep
-{
- Q_OBJECT
-public:
- 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;
-};
diff --git a/api/logic/minecraft/launch/CreateGameFolders.cpp b/api/logic/minecraft/launch/CreateGameFolders.cpp
deleted file mode 100644
index 4081e72e..00000000
--- a/api/logic/minecraft/launch/CreateGameFolders.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "CreateGameFolders.h"
-#include "minecraft/MinecraftInstance.h"
-#include "launch/LaunchTask.h"
-#include "FileSystem.h"
-
-CreateGameFolders::CreateGameFolders(LaunchTask* parent): LaunchStep(parent)
-{
-}
-
-void CreateGameFolders::executeTask()
-{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
-
- if(!FS::ensureFolderPathExists(minecraftInstance->gameRoot()))
- {
- emit logLine("Couldn't create the main game folder", MessageLevel::Error);
- emitFailed(tr("Couldn't create the main game folder"));
- return;
- }
-
- // HACK: this is a workaround for MCL-3732 - 'server-resource-packs' folder is created.
- if(!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->gameRoot(), "server-resource-packs")))
- {
- emit logLine("Couldn't create the 'server-resource-packs' folder", MessageLevel::Error);
- }
- emitSucceeded();
-}
diff --git a/api/logic/minecraft/launch/CreateGameFolders.h b/api/logic/minecraft/launch/CreateGameFolders.h
deleted file mode 100644
index 9c7d3c94..00000000
--- a/api/logic/minecraft/launch/CreateGameFolders.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-#include <minecraft/auth/AuthSession.h>
-
-// Create the main .minecraft for the instance and any other necessary folders
-class CreateGameFolders: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit CreateGameFolders(LaunchTask *parent);
- virtual ~CreateGameFolders() {};
-
- 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
deleted file mode 100644
index 2110384f..00000000
--- a/api/logic/minecraft/launch/DirectJavaLaunch.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "DirectJavaLaunch.h"
-#include <launch/LaunchTask.h>
-#include <minecraft/MinecraftInstance.h>
-#include <FileSystem.h>
-#include <Commandline.h>
-#include <QStandardPaths>
-
-DirectJavaLaunch::DirectJavaLaunch(LaunchTask *parent) : LaunchStep(parent)
-{
- 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();
-
- args.append("-Djava.library.path=" + minecraftInstance->getNativePath());
-
- auto classPathEntries = minecraftInstance->getClassPath();
- args.append("-cp");
- QString classpath;
-#ifdef Q_OS_WIN32
- classpath = classPathEntries.join(';');
-#else
- 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, m_serverToJoin);
- 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())
- {
- const char *reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found.");
- emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal);
- emitFailed(tr(reason).arg(wrapperCommand));
- 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 instance can't start
- const char *reason = QT_TR_NOOP("Could not launch minecraft!");
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(tr(reason));
- return;
- }
- case LoggedProcess::Aborted:
- case LoggedProcess::Crashed:
- {
- m_parent->setPid(-1);
- emitFailed(tr("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(tr("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(QString("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);
-}
-
-void DirectJavaLaunch::proceed()
-{
- // nil
-}
-
-bool DirectJavaLaunch::abort()
-{
- 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
deleted file mode 100644
index 58b119b8..00000000
--- a/api/logic/minecraft/launch/DirectJavaLaunch.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-#include <minecraft/auth/AuthSession.h>
-
-#include "MinecraftServerTarget.h"
-
-class DirectJavaLaunch: public LaunchStep
-{
- Q_OBJECT
-public:
- 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;
- }
-
- void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
- {
- m_serverToJoin = std::move(serverToJoin);
- }
-
-private slots:
- void on_state(LoggedProcess::State state);
-
-private:
- LoggedProcess m_process;
- QString m_command;
- AuthSessionPtr m_session;
- MinecraftServerTargetPtr m_serverToJoin;
-};
-
diff --git a/api/logic/minecraft/launch/ExtractNatives.cpp b/api/logic/minecraft/launch/ExtractNatives.cpp
deleted file mode 100644
index d57499aa..00000000
--- a/api/logic/minecraft/launch/ExtractNatives.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ExtractNatives.h"
-#include <minecraft/MinecraftInstance.h>
-#include <launch/LaunchTask.h>
-
-#include <quazip.h>
-#include <quazipdir.h>
-#include "MMCZip.h"
-#include "FileSystem.h"
-#include <QDir>
-
-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;
-}
-
-static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL, bool nativeGLFW)
-{
- QuaZip zip(source);
- if(!zip.open(QuaZip::mdUnzip))
- {
- return false;
- }
- QDir directory(targetFolder);
- if (!zip.goToFirstFile())
- {
- return false;
- }
- do
- {
- QString name = zip.getCurrentFileName();
- auto lowercase = name.toLower();
- if (nativeGLFW && name.contains("glfw")) {
- continue;
- }
- if (nativeOpenAL && name.contains("openal")) {
- continue;
- }
- 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 settings = minecraftInstance->settings();
- bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
- bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
-
- auto outputPath = minecraftInstance->getNativePath();
- auto javaVersion = minecraftInstance->getJavaVersion();
- bool jniHackEnabled = javaVersion.major() >= 8;
- for(const auto &source: toExtract)
- {
- if(!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL, nativeGLFW))
- {
- const char *reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'");
- emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal);
- emitFailed(tr(reason).arg(source, outputPath));
- }
- }
- emitSucceeded();
-}
-
-void ExtractNatives::finalize()
-{
- 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
deleted file mode 100644
index 094fcd6b..00000000
--- a/api/logic/minecraft/launch/ExtractNatives.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <memory>
-#include "minecraft/auth/AuthSession.h"
-
-// FIXME: temporary wrapper for existing task.
-class ExtractNatives: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit ExtractNatives(LaunchTask *parent) : LaunchStep(parent){};
- virtual ~ExtractNatives(){};
-
- 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
deleted file mode 100644
index ee469770..00000000
--- a/api/logic/minecraft/launch/LauncherPartLaunch.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LauncherPartLaunch.h"
-#include <QCoreApplication>
-#include <launch/LaunchTask.h>
-#include <minecraft/MinecraftInstance.h>
-#include <FileSystem.h>
-#include <Commandline.h>
-#include <QStandardPaths>
-#include "Env.h"
-
-LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent)
-{
- connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines);
- connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
-}
-
-#ifdef Q_OS_WIN
-// returns 8.3 file format from long path
-#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;
-}
-#endif
-
-// if the string survives roundtrip through local 8bit encoding...
-bool fitsInLocal8bit(const QString & string)
-{
- 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);
-
- m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
- 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());
-
- m_process.setProcessEnvironment(instance->createEnvironment());
-
- // 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 natPath = minecraftInstance->getNativePath();
-#ifdef Q_OS_WIN
- if (!fitsInLocal8bit(natPath))
- {
- args << "-Djava.library.path=" + shortPathName(natPath);
- }
- else
- {
- args << "-Djava.library.path=" + natPath;
- }
-#else
- args << "-Djava.library.path=" + natPath;
-#endif
-
- args << "-cp";
-#ifdef Q_OS_WIN
- QStringList processed;
- for(auto & item: classPath)
- {
- if (!fitsInLocal8bit(item))
- {
- processed << shortPathName(item);
- }
- else
- {
- processed << item;
- }
- }
- args << processed.join(';');
-#else
- 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())
- {
- const char *reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found.");
- emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal);
- emitFailed(tr(reason).arg(wrapperCommand));
- 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
- const char *reason = QT_TR_NOOP("Could not launch minecraft!");
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(tr(reason));
- return;
- }
- case LoggedProcess::Aborted:
- case LoggedProcess::Crashed:
- {
- m_parent->setPid(-1);
- emitFailed(tr("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(tr("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(QString("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);
-}
-
-void LauncherPartLaunch::proceed()
-{
- 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;
-}
diff --git a/api/logic/minecraft/launch/LauncherPartLaunch.h b/api/logic/minecraft/launch/LauncherPartLaunch.h
deleted file mode 100644
index 6a7ee0e5..00000000
--- a/api/logic/minecraft/launch/LauncherPartLaunch.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-#include <minecraft/auth/AuthSession.h>
-
-#include "MinecraftServerTarget.h"
-
-class LauncherPartLaunch: public LaunchStep
-{
- Q_OBJECT
-public:
- 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;
- }
-
- void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
- {
- m_serverToJoin = std::move(serverToJoin);
- }
-
-private slots:
- void on_state(LoggedProcess::State state);
-
-private:
- LoggedProcess m_process;
- QString m_command;
- AuthSessionPtr m_session;
- QString m_launchScript;
- MinecraftServerTargetPtr m_serverToJoin;
-
- bool mayProceed = false;
-};
diff --git a/api/logic/minecraft/launch/MinecraftServerTarget.cpp b/api/logic/minecraft/launch/MinecraftServerTarget.cpp
deleted file mode 100644
index 569273b6..00000000
--- a/api/logic/minecraft/launch/MinecraftServerTarget.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MinecraftServerTarget.h"
-
-#include <QStringList>
-
-MinecraftServerTarget MinecraftServerTarget::parse(const QString &fullAddress) {
- QStringList split = fullAddress.split(":");
-
- // The logic below replicates the exact logic minecraft uses for parsing server addresses.
- // While the conversion is not lossless and eats errors, it ensures the same behavior
- // within Minecraft and MultiMC when entering server addresses.
- if (fullAddress.startsWith("["))
- {
- int bracket = fullAddress.indexOf("]");
- if (bracket > 0)
- {
- QString ipv6 = fullAddress.mid(1, bracket - 1);
- QString port = fullAddress.mid(bracket + 1).trimmed();
-
- if (port.startsWith(":") && !ipv6.isEmpty())
- {
- port = port.mid(1);
- split = QStringList({ ipv6, port });
- }
- else
- {
- split = QStringList({ipv6});
- }
- }
- }
-
- if (split.size() > 2)
- {
- split = QStringList({fullAddress});
- }
-
- QString realAddress = split[0];
-
- quint16 realPort = 25565;
- if (split.size() > 1)
- {
- bool ok;
- realPort = split[1].toUInt(&ok);
-
- if (!ok)
- {
- realPort = 25565;
- }
- }
-
- return MinecraftServerTarget { realAddress, realPort };
-}
diff --git a/api/logic/minecraft/launch/MinecraftServerTarget.h b/api/logic/minecraft/launch/MinecraftServerTarget.h
deleted file mode 100644
index 3c5786f4..00000000
--- a/api/logic/minecraft/launch/MinecraftServerTarget.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-
-#include <QString>
-#include <multimc_logic_export.h>
-
-struct MinecraftServerTarget {
- QString address;
- quint16 port;
-
- static MULTIMC_LOGIC_EXPORT MinecraftServerTarget parse(const QString &fullAddress);
-};
-
-typedef std::shared_ptr<MinecraftServerTarget> MinecraftServerTargetPtr;
diff --git a/api/logic/minecraft/launch/ModMinecraftJar.cpp b/api/logic/minecraft/launch/ModMinecraftJar.cpp
deleted file mode 100644
index 93de9d59..00000000
--- a/api/logic/minecraft/launch/ModMinecraftJar.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ModMinecraftJar.h"
-#include "launch/LaunchTask.h"
-#include "MMCZip.h"
-#include "minecraft/OpSys.h"
-#include "FileSystem.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-
-void ModMinecraftJar::executeTask()
-{
- 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"));
- }
-
- 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->getPackProfile();
- 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();
-}
-
-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;
-}
diff --git a/api/logic/minecraft/launch/ModMinecraftJar.h b/api/logic/minecraft/launch/ModMinecraftJar.h
deleted file mode 100644
index 081c6a91..00000000
--- a/api/logic/minecraft/launch/ModMinecraftJar.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <memory>
-
-class ModMinecraftJar: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit ModMinecraftJar(LaunchTask *parent) : LaunchStep(parent) {};
- virtual ~ModMinecraftJar(){};
-
- virtual void executeTask() override;
- virtual bool canAbort() const override
- {
- return false;
- }
- void finalize() override;
-private:
- bool removeJar();
-};
diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.cpp b/api/logic/minecraft/launch/PrintInstanceInfo.cpp
deleted file mode 100644
index 0b9611ad..00000000
--- a/api/logic/minecraft/launch/PrintInstanceInfo.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fstream>
-#include <string>
-
-#include "PrintInstanceInfo.h"
-#include <launch/LaunchTask.h>
-
-#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)
- {
- 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 * lspci = popen("lspci -k", "r");
-
- if (!lspci)
- return;
-
- while (fgets(buff, 512, lspci) != NULL)
- {
- std::string str(buff);
- if (str.length() < 9)
- continue;
- if (str.substr(8, 3) == "VGA")
- {
- gpuline = cline;
- log << QString::fromStdString(str.substr(35, std::string::npos));
- }
- if (gpuline > -1 && gpuline != cline)
- {
- if (cline - gpuline < 3)
- {
- log << QString::fromStdString(str.substr(1, std::string::npos));
- }
- }
- cline++;
- }
- 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)
- {
- if (strncmp(buff, "OpenGL version string:", 22) == 0)
- {
- 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(log, MessageLevel::MultiMC);
- logLines(instance->verboseDescription(m_session, m_serverToJoin), MessageLevel::MultiMC);
- emitSucceeded();
-}
diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.h b/api/logic/minecraft/launch/PrintInstanceInfo.h
deleted file mode 100644
index fdc30f31..00000000
--- a/api/logic/minecraft/launch/PrintInstanceInfo.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <memory>
-#include "minecraft/auth/AuthSession.h"
-#include "minecraft/launch/MinecraftServerTarget.h"
-
-// FIXME: temporary wrapper for existing task.
-class PrintInstanceInfo: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit PrintInstanceInfo(LaunchTask *parent, AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) :
- LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin) {};
- virtual ~PrintInstanceInfo(){};
-
- virtual void executeTask();
- virtual bool canAbort() const
- {
- return false;
- }
-private:
- AuthSessionPtr m_session;
- MinecraftServerTargetPtr m_serverToJoin;
-};
-
diff --git a/api/logic/minecraft/launch/ReconstructAssets.cpp b/api/logic/minecraft/launch/ReconstructAssets.cpp
deleted file mode 100644
index 4d206665..00000000
--- a/api/logic/minecraft/launch/ReconstructAssets.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ReconstructAssets.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.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->getPackProfile();
- 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
deleted file mode 100644
index 58d7febd..00000000
--- a/api/logic/minecraft/launch/ReconstructAssets.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <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
deleted file mode 100644
index 2a0e21b3..00000000
--- a/api/logic/minecraft/launch/ScanModFolders.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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);
- if(!loaders->update()) {
- m_modsDone = true;
- }
-
- auto cores = m_inst->coreModList();
- connect(cores.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::coreModsDone);
- if(!cores->update()) {
- m_coreModsDone = true;
- }
- checkDone();
-}
-
-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
deleted file mode 100644
index d5989170..00000000
--- a/api/logic/minecraft/launch/ScanModFolders.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <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/launch/VerifyJavaInstall.cpp b/api/logic/minecraft/launch/VerifyJavaInstall.cpp
deleted file mode 100644
index 657669af..00000000
--- a/api/logic/minecraft/launch/VerifyJavaInstall.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "VerifyJavaInstall.h"
-
-#include <launch/LaunchTask.h>
-#include <minecraft/MinecraftInstance.h>
-#include <minecraft/PackProfile.h>
-#include <minecraft/VersionFilterData.h>
-
-void VerifyJavaInstall::executeTask() {
- auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
-
- auto javaVersion = m_inst->getJavaVersion();
- auto minecraftComponent = m_inst->getPackProfile()->getComponent("net.minecraft");
-
- // Java 16 requirement
- if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java16BeginsDate) {
- if (javaVersion.major() < 16) {
- emit logLine("Minecraft 21w19a and above require the use of Java 16",
- MessageLevel::Fatal);
- emitFailed(tr("Minecraft 21w19a and above require the use of Java 16"));
- return;
- }
- }
- // Java 8 requirement
- else if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java8BeginsDate) {
- if (javaVersion.major() < 8) {
- emit logLine("Minecraft 17w13a and above require the use of Java 8",
- MessageLevel::Fatal);
- emitFailed(tr("Minecraft 17w13a and above require the use of Java 8"));
- return;
- }
- }
-
- emitSucceeded();
-}
diff --git a/api/logic/minecraft/launch/VerifyJavaInstall.h b/api/logic/minecraft/launch/VerifyJavaInstall.h
deleted file mode 100644
index a553106d..00000000
--- a/api/logic/minecraft/launch/VerifyJavaInstall.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include <launch/LaunchStep.h>
-
-class VerifyJavaInstall : public LaunchStep {
- Q_OBJECT
-
-public:
- explicit VerifyJavaInstall(LaunchTask *parent) : LaunchStep(parent) {
- };
- ~VerifyJavaInstall() override = default;
-
- void executeTask() override;
- bool canAbort() const override {
- return false;
- }
-};
diff --git a/api/logic/minecraft/legacy/LegacyInstance.cpp b/api/logic/minecraft/legacy/LegacyInstance.cpp
deleted file mode 100644
index 9f9bda5a..00000000
--- a/api/logic/minecraft/legacy/LegacyInstance.cpp
+++ /dev/null
@@ -1,256 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QFileInfo>
-#include <minecraft/launch/LauncherPartLaunch.h>
-#include <QDir>
-#include <settings/Setting.h>
-
-#include "LegacyInstance.h"
-
-#include "minecraft/legacy/LegacyModList.h"
-#include "minecraft/WorldList.h"
-#include <MMCZip.h>
-#include <FileSystem.h>
-
-LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &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", "");
-}
-
-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();
-}
-
-
-QString LegacyInstance::baseJar() const
-{
- 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;
-}
-
-bool LegacyInstance::shouldUseCustomBaseJar() const
-{
- return m_settings->get("UseCustomBaseJar").toBool();
-}
-
-
-shared_qobject_ptr<Task> LegacyInstance::createUpdateTask(Net::Mode)
-{
- return nullptr;
-}
-
-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;
-}
-
-QString LegacyInstance::gameRoot() const
-{
- QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
- QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
-
- if (mcDir.exists() && !dotMCDir.exists())
- return mcDir.filePath();
- else
- return dotMCDir.filePath();
-}
-
-QString LegacyInstance::binRoot() const
-{
- return FS::PathCombine(gameRoot(), "bin");
-}
-
-QString LegacyInstance::jarModsDir() const
-{
- return FS::PathCombine(instanceRoot(), "instMods");
-}
-
-QString LegacyInstance::libDir() const
-{
- return FS::PathCombine(gameRoot(), "lib");
-}
-
-QString LegacyInstance::savesDir() const
-{
- return FS::PathCombine(gameRoot(), "saves");
-}
-
-QString LegacyInstance::loaderModsDir() const
-{
- return FS::PathCombine(gameRoot(), "mods");
-}
-
-QString LegacyInstance::coreModsDir() const
-{
- return FS::PathCombine(gameRoot(), "coremods");
-}
-
-QString LegacyInstance::resourceDir() const
-{
- return FS::PathCombine(gameRoot(), "resources");
-}
-QString LegacyInstance::texturePacksDir() const
-{
- return FS::PathCombine(gameRoot(), "texturepacks");
-}
-
-QString LegacyInstance::runnableJar() const
-{
- return FS::PathCombine(binRoot(), "minecraft.jar");
-}
-
-QString LegacyInstance::modListFile() const
-{
- return FS::PathCombine(instanceRoot(), "modlist");
-}
-
-QString LegacyInstance::instanceConfigFolder() const
-{
- return FS::PathCombine(gameRoot(), "config");
-}
-
-bool LegacyInstance::shouldRebuild() const
-{
- return m_settings->get("NeedsRebuild").toBool();
-}
-
-QString LegacyInstance::currentVersionId() const
-{
- return m_settings->get("JarVersion").toString();
-}
-
-QString LegacyInstance::intendedVersionId() const
-{
- 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;
-}
-
-QString LegacyInstance::defaultBaseJar() const
-{
- return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
-}
-
-QString LegacyInstance::defaultCustomBaseJar() const
-{
- 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;
-}
-
-QString LegacyInstance::typeName() const
-{
- return tr("Legacy");
-}
-
-QString LegacyInstance::getStatusbarDescription()
-{
- return tr("Instance from previous versions.");
-}
-
-QStringList LegacyInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
-{
- 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
deleted file mode 100644
index 325bac7a..00000000
--- a/api/logic/minecraft/legacy/LegacyInstance.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "BaseInstance.h"
-#include "launch/LaunchTask.h"
-
-#include "multimc_logic_export.h"
-
-class ModFolderModel;
-class LegacyModList;
-class WorldList;
-class Task;
-/*
- * WHY: Legacy instances - from MultiMC 3 and 4 - are here only to provide a way to upgrade them to the current format.
- */
-class MULTIMC_LOGIC_EXPORT LegacyInstance : public BaseInstance
-{
- Q_OBJECT
-public:
-
- 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, MinecraftServerTargetPtr serverToJoin) override
- {
- return nullptr;
- }
- IPathMatcher::Ptr getLogFileMatcher() override
- {
- return nullptr;
- }
- QString getLogFileRoot() override
- {
- return gameRoot();
- }
-
- QString getStatusbarDescription() override;
- QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) 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;
-};
diff --git a/api/logic/minecraft/legacy/LegacyModList.cpp b/api/logic/minecraft/legacy/LegacyModList.cpp
deleted file mode 100644
index 7301eb8c..00000000
--- a/api/logic/minecraft/legacy/LegacyModList.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LegacyModList.h"
-#include <FileSystem.h>
-#include <QString>
-#include <QDebug>
-
-LegacyModList::LegacyModList(const QString &dir, const QString &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);
-}
-
- struct OrderItem
- {
- QString id;
- bool enabled = false;
- };
- typedef QList<OrderItem> OrderList;
-
-static void internalSort(QList<LegacyModList::Mod> &what)
-{
- 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;
-
- 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;
-}
-
-bool LegacyModList::update()
-{
- if (!m_dir.exists() || !m_dir.isReadable())
- return 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(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
deleted file mode 100644
index 8881d471..00000000
--- a/api/logic/minecraft/legacy/LegacyModList.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QList>
-#include <QString>
-#include <QDir>
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT LegacyModList
-{
-public:
-
- 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();
-
- QDir dir()
- {
- return m_dir;
- }
-
- const QList<Mod> & allMods()
- {
- return mods;
- }
-
-protected:
- 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
deleted file mode 100644
index a4ea60cd..00000000
--- a/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-#include "LegacyUpgradeTask.h"
-#include "settings/INISettingsObject.h"
-#include "FileSystem.h"
-#include "NullInstance.h"
-#include "pathmatcher/RegexpMatcher.h"
-#include <QtConcurrentRun>
-#include "LegacyInstance.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "LegacyModList.h"
-#include "classparser.h"
-
-LegacyUpgradeTask::LegacyUpgradeTask(InstancePtr origInstance)
-{
- m_origInstance = origInstance;
-}
-
-void LegacyUpgradeTask::executeTask()
-{
- setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
-
- 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);
-}
-
-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();
-}
-
-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 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.getPackProfile();
- 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);
- }
-
- 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.gameRoot(), mcRemovables);
- }
- emitSucceeded();
-}
-
-void LegacyUpgradeTask::copyAborted()
-{
- 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
deleted file mode 100644
index e35e43b7..00000000
--- a/api/logic/minecraft/legacy/LegacyUpgradeTask.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include "InstanceTask.h"
-#include "multimc_logic_export.h"
-#include "net/NetJob.h"
-#include <QUrl>
-#include <QFuture>
-#include <QFutureWatcher>
-#include "settings/SettingsObject.h"
-#include "BaseVersion.h"
-#include "BaseInstance.h"
-
-
-class MULTIMC_LOGIC_EXPORT LegacyUpgradeTask : public InstanceTask
-{
- Q_OBJECT
-public:
- explicit LegacyUpgradeTask(InstancePtr origInstance);
-
-protected:
- //! 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;
-};
diff --git a/api/logic/minecraft/mod/LocalModParseTask.cpp b/api/logic/minecraft/mod/LocalModParseTask.cpp
deleted file mode 100644
index 0d6972fb..00000000
--- a/api/logic/minecraft/mod/LocalModParseTask.cpp
+++ /dev/null
@@ -1,467 +0,0 @@
-#include "LocalModParseTask.h"
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonValue>
-#include <quazip.h>
-#include <quazipfile.h>
-#include <toml.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://github.com/MinecraftForge/Documentation/blob/5ab4ba6cf9abc0ac4c0abd96ad187461aefd72af/docs/gettingstarted/structuring.md
-std::shared_ptr<ModDetails> ReadMCModTOML(QByteArray contents)
-{
- std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
-
- char errbuf[200];
- // top-level table
- toml_table_t* tomlData = toml_parse(contents.data(), errbuf, sizeof(errbuf));
-
- if(!tomlData)
- {
- return nullptr;
- }
-
- // array defined by [[mods]]
- toml_array_t* tomlModsArr = toml_array_in(tomlData, "mods");
- // we only really care about the first element, since multiple mods in one file is not supported by us at the moment
- toml_table_t* tomlModsTable0 = toml_table_at(tomlModsArr, 0);
-
- // mandatory properties - always in [[mods]]
- toml_datum_t modIdDatum = toml_string_in(tomlModsTable0, "modId");
- if(modIdDatum.ok)
- {
- details->mod_id = modIdDatum.u.s;
- // library says this is required for strings
- free(modIdDatum.u.s);
- }
- toml_datum_t versionDatum = toml_string_in(tomlModsTable0, "version");
- if(versionDatum.ok)
- {
- details->version = versionDatum.u.s;
- free(versionDatum.u.s);
- }
- toml_datum_t displayNameDatum = toml_string_in(tomlModsTable0, "displayName");
- if(displayNameDatum.ok)
- {
- details->name = displayNameDatum.u.s;
- free(displayNameDatum.u.s);
- }
- toml_datum_t descriptionDatum = toml_string_in(tomlModsTable0, "description");
- if(descriptionDatum.ok)
- {
- details->description = descriptionDatum.u.s;
- free(descriptionDatum.u.s);
- }
-
- // optional properties - can be in the root table or [[mods]]
- toml_datum_t authorsDatum = toml_string_in(tomlData, "authors");
- QString authors = "";
- if(authorsDatum.ok)
- {
- authors = authorsDatum.u.s;
- free(authorsDatum.u.s);
- }
- else
- {
- authorsDatum = toml_string_in(tomlModsTable0, "authors");
- if(authorsDatum.ok)
- {
- authors = authorsDatum.u.s;
- free(authorsDatum.u.s);
- }
- }
- if(!authors.isEmpty())
- {
- // author information is stored as a string now, not a list
- details->authors.append(authors);
- }
- // is credits even used anywhere? including this for completion/parity with old data version
- toml_datum_t creditsDatum = toml_string_in(tomlData, "credits");
- QString credits = "";
- if(creditsDatum.ok)
- {
- authors = creditsDatum.u.s;
- free(creditsDatum.u.s);
- }
- else
- {
- creditsDatum = toml_string_in(tomlModsTable0, "credits");
- if(creditsDatum.ok)
- {
- credits = creditsDatum.u.s;
- free(creditsDatum.u.s);
- }
- }
- details->credits = credits;
- toml_datum_t homeurlDatum = toml_string_in(tomlData, "displayURL");
- QString homeurl = "";
- if(homeurlDatum.ok)
- {
- homeurl = homeurlDatum.u.s;
- free(homeurlDatum.u.s);
- }
- else
- {
- homeurlDatum = toml_string_in(tomlModsTable0, "displayURL");
- if(homeurlDatum.ok)
- {
- homeurl = homeurlDatum.u.s;
- free(homeurlDatum.u.s);
- }
- }
- if(!homeurl.isEmpty())
- {
- // fix up url.
- if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://"))
- {
- homeurl.prepend("http://");
- }
- }
- details->homeurl = homeurl;
-
- // this seems to be recursive, so it should free everything
- toml_free(tomlData);
-
- return details;
-}
-
-// 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("META-INF/mods.toml"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_result->details = ReadMCModTOML(file.readAll());
- file.close();
-
- // to replace ${file.jarVersion} with the actual version, as needed
- if (m_result->details && m_result->details->version == "${file.jarVersion}")
- {
- if (zip.setCurrentFile("META-INF/MANIFEST.MF"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- // quick and dirty line-by-line parser
- auto manifestLines = file.readAll().split('\n');
- QString manifestVersion = "";
- for (auto &line : manifestLines)
- {
- if (QString(line).startsWith("Implementation-Version: "))
- {
- manifestVersion = QString(line).remove("Implementation-Version: ");
- break;
- }
- }
-
- // some mods use ${projectversion} in their build.gradle, causing this mess to show up in MANIFEST.MF
- // also keep with forge's behavior of setting the version to "NONE" if none is found
- if (manifestVersion.contains("task ':jar' property 'archiveVersion'") || manifestVersion == "")
- {
- manifestVersion = "NONE";
- }
-
- m_result->details->version = manifestVersion;
-
- file.close();
- }
- }
-
- zip.close();
- return;
- }
- else 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
deleted file mode 100644
index 0f119ba6..00000000
--- a/api/logic/minecraft/mod/LocalModParseTask.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#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
deleted file mode 100644
index b6bff29b..00000000
--- a/api/logic/minecraft/mod/Mod.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <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
deleted file mode 100644
index f77ffd41..00000000
--- a/api/logic/minecraft/mod/Mod.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <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
deleted file mode 100644
index 6ab4aee7..00000000
--- a/api/logic/minecraft/mod/ModDetails.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#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
deleted file mode 100644
index 88349877..00000000
--- a/api/logic/minecraft/mod/ModFolderLoadTask.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#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
deleted file mode 100644
index 8d720e65..00000000
--- a/api/logic/minecraft/mod/ModFolderLoadTask.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#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
deleted file mode 100644
index 031eebe5..00000000
--- a/api/logic/minecraft/mod/ModFolderModel.cpp
+++ /dev/null
@@ -1,554 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index b0a76121..00000000
--- a/api/logic/minecraft/mod/ModFolderModel.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <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];
- }
- const Mod &at(size_t index) const
- {
- return mods.at(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
deleted file mode 100644
index 76f16ed5..00000000
--- a/api/logic/minecraft/mod/ModFolderModel_test.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-
-#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/mod/ResourcePackFolderModel.cpp b/api/logic/minecraft/mod/ResourcePackFolderModel.cpp
deleted file mode 100644
index f3d7f566..00000000
--- a/api/logic/minecraft/mod/ResourcePackFolderModel.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "ResourcePackFolderModel.h"
-
-ResourcePackFolderModel::ResourcePackFolderModel(const QString &dir) : ModFolderModel(dir) {
-}
-
-QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const {
- if (role == Qt::ToolTipRole) {
- switch (section) {
- case ActiveColumn:
- return tr("Is the resource pack enabled?");
- case NameColumn:
- return tr("The name of the resource pack.");
- case VersionColumn:
- return tr("The version of the resource pack.");
- case DateColumn:
- return tr("The date and time this resource pack was last changed (or added).");
- default:
- return QVariant();
- }
- }
-
- return ModFolderModel::headerData(section, orientation, role);
-}
diff --git a/api/logic/minecraft/mod/ResourcePackFolderModel.h b/api/logic/minecraft/mod/ResourcePackFolderModel.h
deleted file mode 100644
index 47eb4bb2..00000000
--- a/api/logic/minecraft/mod/ResourcePackFolderModel.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "ModFolderModel.h"
-
-class MULTIMC_LOGIC_EXPORT ResourcePackFolderModel : public ModFolderModel
-{
- Q_OBJECT
-
-public:
- explicit ResourcePackFolderModel(const QString &dir);
-
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
-};
diff --git a/api/logic/minecraft/mod/TexturePackFolderModel.cpp b/api/logic/minecraft/mod/TexturePackFolderModel.cpp
deleted file mode 100644
index d5956da1..00000000
--- a/api/logic/minecraft/mod/TexturePackFolderModel.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "TexturePackFolderModel.h"
-
-TexturePackFolderModel::TexturePackFolderModel(const QString &dir) : ModFolderModel(dir) {
-}
-
-QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const {
- if (role == Qt::ToolTipRole) {
- switch (section) {
- case ActiveColumn:
- return tr("Is the texture pack enabled?");
- case NameColumn:
- return tr("The name of the texture pack.");
- case VersionColumn:
- return tr("The version of the texture pack.");
- case DateColumn:
- return tr("The date and time this texture pack was last changed (or added).");
- default:
- return QVariant();
- }
- }
-
- return ModFolderModel::headerData(section, orientation, role);
-}
diff --git a/api/logic/minecraft/mod/TexturePackFolderModel.h b/api/logic/minecraft/mod/TexturePackFolderModel.h
deleted file mode 100644
index d773b17b..00000000
--- a/api/logic/minecraft/mod/TexturePackFolderModel.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "ModFolderModel.h"
-
-class MULTIMC_LOGIC_EXPORT TexturePackFolderModel : public ModFolderModel
-{
- Q_OBJECT
-
-public:
- explicit TexturePackFolderModel(const QString &dir);
-
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
-};
diff --git a/api/logic/minecraft/services/SkinDelete.cpp b/api/logic/minecraft/services/SkinDelete.cpp
deleted file mode 100644
index 34977257..00000000
--- a/api/logic/minecraft/services/SkinDelete.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "SkinDelete.h"
-#include <QNetworkRequest>
-#include <QHttpMultiPart>
-#include <Env.h>
-
-SkinDelete::SkinDelete(QObject *parent, AuthSessionPtr session)
- : Task(parent), m_session(session)
-{
-}
-
-void SkinDelete::executeTask()
-{
- QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active"));
- request.setRawHeader("Authorization", QString("Bearer %1").arg(m_session->access_token).toLocal8Bit());
- QNetworkReply *rep = ENV.qnam().deleteResource(request);
- m_reply = std::shared_ptr<QNetworkReply>(rep);
-
- setStatus(tr("Deleting 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 SkinDelete::downloadError(QNetworkReply::NetworkError error)
-{
- // error happened during download.
- qCritical() << "Network error: " << error;
- emitFailed(m_reply->errorString());
-}
-
-void SkinDelete::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();
-}
-
diff --git a/api/logic/minecraft/services/SkinDelete.h b/api/logic/minecraft/services/SkinDelete.h
deleted file mode 100644
index 705ce8ef..00000000
--- a/api/logic/minecraft/services/SkinDelete.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include <QFile>
-#include <QtNetwork/QtNetwork>
-#include <memory>
-#include <minecraft/auth/AuthSession.h>
-#include "tasks/Task.h"
-#include "multimc_logic_export.h"
-
-typedef std::shared_ptr<class SkinDelete> SkinDeletePtr;
-
-class MULTIMC_LOGIC_EXPORT SkinDelete : public Task
-{
- Q_OBJECT
-public:
- SkinDelete(QObject *parent, AuthSessionPtr session);
- virtual ~SkinDelete() = default;
-
-private:
- AuthSessionPtr m_session;
- std::shared_ptr<QNetworkReply> m_reply;
-
-protected:
- virtual void executeTask();
-
-public slots:
- void downloadError(QNetworkReply::NetworkError);
- void downloadFinished();
-};
-
diff --git a/api/logic/minecraft/services/SkinUpload.cpp b/api/logic/minecraft/services/SkinUpload.cpp
deleted file mode 100644
index 4e5a1698..00000000
--- a/api/logic/minecraft/services/SkinUpload.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "SkinUpload.h"
-#include <QNetworkRequest>
-#include <QHttpMultiPart>
-#include <Env.h>
-
-QByteArray getVariant(SkinUpload::Model model) {
- switch (model) {
- default:
- qDebug() << "Unknown skin type!";
- case SkinUpload::STEVE:
- return "CLASSIC";
- case SkinUpload::ALEX:
- return "SLIM";
- }
-}
-
-SkinUpload::SkinUpload(QObject *parent, AuthSessionPtr session, QByteArray skin, SkinUpload::Model model)
- : Task(parent), m_model(model), m_skin(skin), m_session(session)
-{
-}
-
-void SkinUpload::executeTask()
-{
- QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins"));
- request.setRawHeader("Authorization", QString("Bearer %1").arg(m_session->access_token).toLocal8Bit());
- QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
-
- 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 model;
- model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\""));
- model.setBody(getVariant(m_model));
-
- multiPart->append(skin);
- multiPart->append(model);
-
- QNetworkReply *rep = ENV.qnam().post(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()));
-}
-
-void SkinUpload::downloadError(QNetworkReply::NetworkError error)
-{
- // 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();
-}
diff --git a/api/logic/minecraft/services/SkinUpload.h b/api/logic/minecraft/services/SkinUpload.h
deleted file mode 100644
index c77abb03..00000000
--- a/api/logic/minecraft/services/SkinUpload.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include <QFile>
-#include <QtNetwork/QtNetwork>
-#include <memory>
-#include <minecraft/auth/AuthSession.h>
-#include "tasks/Task.h"
-#include "multimc_logic_export.h"
-
-typedef std::shared_ptr<class SkinUpload> SkinUploadPtr;
-
-class MULTIMC_LOGIC_EXPORT SkinUpload : public Task
-{
- Q_OBJECT
-public:
- enum Model
- {
- STEVE,
- ALEX
- };
-
- // 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;
-protected:
- virtual void executeTask();
-
-public slots:
-
- void downloadError(QNetworkReply::NetworkError);
-
- void downloadFinished();
-};
diff --git a/api/logic/minecraft/testdata/1.9-simple.json b/api/logic/minecraft/testdata/1.9-simple.json
deleted file mode 100644
index 574c5b06..00000000
--- a/api/logic/minecraft/testdata/1.9-simple.json
+++ /dev/null
@@ -1,198 +0,0 @@
-{
- "assets": "1.9",
- "id": "1.9",
- "libraries": [
- {
- "name": "oshi-project:oshi-core:1.1"
- },
- {
- "name": "net.java.dev.jna:jna:3.4.0"
- },
- {
- "name": "net.java.dev.jna:platform:3.4.0"
- },
- {
- "name": "com.ibm.icu:icu4j-core-mojang:51.2"
- },
- {
- "name": "net.sf.jopt-simple:jopt-simple:4.6"
- },
- {
- "name": "com.paulscode:codecjorbis:20101023"
- },
- {
- "name": "com.paulscode:codecwav:20101023"
- },
- {
- "name": "com.paulscode:libraryjavasound:20101123"
- },
- {
- "name": "com.paulscode:librarylwjglopenal:20100824"
- },
- {
- "name": "com.paulscode:soundsystem:20120107"
- },
- {
- "name": "io.netty:netty-all:4.0.23.Final"
- },
- {
- "name": "com.google.guava:guava:17.0"
- },
- {
- "name": "org.apache.commons:commons-lang3:3.3.2"
- },
- {
- "name": "commons-io:commons-io:2.4"
- },
- {
- "name": "commons-codec:commons-codec:1.9"
- },
- {
- "name": "net.java.jinput:jinput:2.0.5"
- },
- {
- "name": "net.java.jutils:jutils:1.0.0"
- },
- {
- "name": "com.google.code.gson:gson:2.2.4"
- },
- {
- "name": "com.mojang:authlib:1.5.22"
- },
- {
- "name": "com.mojang:realms:1.8.4"
- },
- {
- "name": "org.apache.commons:commons-compress:1.8.1"
- },
- {
- "name": "org.apache.httpcomponents:httpclient:4.3.3"
- },
- {
- "name": "commons-logging:commons-logging:1.1.3"
- },
- {
- "name": "org.apache.httpcomponents:httpcore:4.3.2"
- },
- {
- "name": "org.apache.logging.log4j:log4j-api:2.0-beta9"
- },
- {
- "name": "org.apache.logging.log4j:log4j-core:2.0-beta9"
- },
- {
- "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209",
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209",
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "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"
- }
- }
- ]
- },
- {
- "name": "org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-20140822",
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-20140822",
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.2-nightly-20140822",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- },
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "net.java.jinput:jinput-platform:2.0.5",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- }
- }
- ],
- "mainClass": "net.minecraft.client.main.Main",
- "minecraftArguments": "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}",
- "minimumLauncherVersion": 18,
- "releaseTime": "2016-02-29T13:49:54+00:00",
- "time": "2016-03-01T13:14:53+00:00",
- "type": "release"
-}
diff --git a/api/logic/minecraft/testdata/1.9.json b/api/logic/minecraft/testdata/1.9.json
deleted file mode 100644
index 697c6059..00000000
--- a/api/logic/minecraft/testdata/1.9.json
+++ /dev/null
@@ -1,529 +0,0 @@
-{
- "assetIndex": {
- "id": "1.9",
- "sha1": "cde65b47a43f638653ab1da3848b53f8a7477b16",
- "size": 136916,
- "totalSize": 119917473,
- "url": "https://launchermeta.mojang.com/mc-staging/assets/1.9/cde65b47a43f638653ab1da3848b53f8a7477b16/1.9.json"
- },
- "assets": "1.9",
- "downloads": {
- "client": {
- "sha1": "2f67dfe8953299440d1902f9124f0f2c3a2c940f",
- "size": 8697592,
- "url": "https://launcher.mojang.com/mc/game/1.9/client/2f67dfe8953299440d1902f9124f0f2c3a2c940f/client.jar"
- },
- "server": {
- "sha1": "b4d449cf2918e0f3bd8aa18954b916a4d1880f0d",
- "size": 8848015,
- "url": "https://launcher.mojang.com/mc/game/1.9/server/b4d449cf2918e0f3bd8aa18954b916a4d1880f0d/server.jar"
- }
- },
- "id": "1.9",
- "libraries": [
- {
- "downloads": {
- "artifact": {
- "path": "oshi-project/oshi-core/1.1/oshi-core-1.1.jar",
- "sha1": "9ddf7b048a8d701be231c0f4f95fd986198fd2d8",
- "size": 30973,
- "url": "https://libraries.minecraft.net/oshi-project/oshi-core/1.1/oshi-core-1.1.jar"
- }
- },
- "name": "oshi-project:oshi-core:1.1"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/java/dev/jna/jna/3.4.0/jna-3.4.0.jar",
- "sha1": "803ff252fedbd395baffd43b37341dc4a150a554",
- "size": 1008730,
- "url": "https://libraries.minecraft.net/net/java/dev/jna/jna/3.4.0/jna-3.4.0.jar"
- }
- },
- "name": "net.java.dev.jna:jna:3.4.0"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/java/dev/jna/platform/3.4.0/platform-3.4.0.jar",
- "sha1": "e3f70017be8100d3d6923f50b3d2ee17714e9c13",
- "size": 913436,
- "url": "https://libraries.minecraft.net/net/java/dev/jna/platform/3.4.0/platform-3.4.0.jar"
- }
- },
- "name": "net.java.dev.jna:platform:3.4.0"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/ibm/icu/icu4j-core-mojang/51.2/icu4j-core-mojang-51.2.jar",
- "sha1": "63d216a9311cca6be337c1e458e587f99d382b84",
- "size": 1634692,
- "url": "https://libraries.minecraft.net/com/ibm/icu/icu4j-core-mojang/51.2/icu4j-core-mojang-51.2.jar"
- }
- },
- "name": "com.ibm.icu:icu4j-core-mojang:51.2"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/sf/jopt-simple/jopt-simple/4.6/jopt-simple-4.6.jar",
- "sha1": "306816fb57cf94f108a43c95731b08934dcae15c",
- "size": 62477,
- "url": "https://libraries.minecraft.net/net/sf/jopt-simple/jopt-simple/4.6/jopt-simple-4.6.jar"
- }
- },
- "name": "net.sf.jopt-simple:jopt-simple:4.6"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/paulscode/codecjorbis/20101023/codecjorbis-20101023.jar",
- "sha1": "c73b5636faf089d9f00e8732a829577de25237ee",
- "size": 103871,
- "url": "https://libraries.minecraft.net/com/paulscode/codecjorbis/20101023/codecjorbis-20101023.jar"
- }
- },
- "name": "com.paulscode:codecjorbis: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"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/paulscode/libraryjavasound/20101123/libraryjavasound-20101123.jar",
- "sha1": "5c5e304366f75f9eaa2e8cca546a1fb6109348b3",
- "size": 21679,
- "url": "https://libraries.minecraft.net/com/paulscode/libraryjavasound/20101123/libraryjavasound-20101123.jar"
- }
- },
- "name": "com.paulscode:libraryjavasound:20101123"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/paulscode/librarylwjglopenal/20100824/librarylwjglopenal-20100824.jar",
- "sha1": "73e80d0794c39665aec3f62eee88ca91676674ef",
- "size": 18981,
- "url": "https://libraries.minecraft.net/com/paulscode/librarylwjglopenal/20100824/librarylwjglopenal-20100824.jar"
- }
- },
- "name": "com.paulscode:librarylwjglopenal:20100824"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/paulscode/soundsystem/20120107/soundsystem-20120107.jar",
- "sha1": "419c05fe9be71f792b2d76cfc9b67f1ed0fec7f6",
- "size": 65020,
- "url": "https://libraries.minecraft.net/com/paulscode/soundsystem/20120107/soundsystem-20120107.jar"
- }
- },
- "name": "com.paulscode:soundsystem:20120107"
- },
- {
- "downloads": {
- "artifact": {
- "path": "io/netty/netty-all/4.0.23.Final/netty-all-4.0.23.Final.jar",
- "sha1": "0294104aaf1781d6a56a07d561e792c5d0c95f45",
- "size": 1779991,
- "url": "https://libraries.minecraft.net/io/netty/netty-all/4.0.23.Final/netty-all-4.0.23.Final.jar"
- }
- },
- "name": "io.netty:netty-all:4.0.23.Final"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/google/guava/guava/17.0/guava-17.0.jar",
- "sha1": "9c6ef172e8de35fd8d4d8783e4821e57cdef7445",
- "size": 2243036,
- "url": "https://libraries.minecraft.net/com/google/guava/guava/17.0/guava-17.0.jar"
- }
- },
- "name": "com.google.guava:guava:17.0"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/commons/commons-lang3/3.3.2/commons-lang3-3.3.2.jar",
- "sha1": "90a3822c38ec8c996e84c16a3477ef632cbc87a3",
- "size": 412739,
- "url": "https://libraries.minecraft.net/org/apache/commons/commons-lang3/3.3.2/commons-lang3-3.3.2.jar"
- }
- },
- "name": "org.apache.commons:commons-lang3:3.3.2"
- },
- {
- "downloads": {
- "artifact": {
- "path": "commons-io/commons-io/2.4/commons-io-2.4.jar",
- "sha1": "b1b6ea3b7e4aa4f492509a4952029cd8e48019ad",
- "size": 185140,
- "url": "https://libraries.minecraft.net/commons-io/commons-io/2.4/commons-io-2.4.jar"
- }
- },
- "name": "commons-io:commons-io:2.4"
- },
- {
- "downloads": {
- "artifact": {
- "path": "commons-codec/commons-codec/1.9/commons-codec-1.9.jar",
- "sha1": "9ce04e34240f674bc72680f8b843b1457383161a",
- "size": 263965,
- "url": "https://libraries.minecraft.net/commons-codec/commons-codec/1.9/commons-codec-1.9.jar"
- }
- },
- "name": "commons-codec:commons-codec:1.9"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar",
- "sha1": "39c7796b469a600f72380316f6b1f11db6c2c7c4",
- "size": 208338,
- "url": "https://libraries.minecraft.net/net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar"
- }
- },
- "name": "net.java.jinput:jinput:2.0.5"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar",
- "sha1": "e12fe1fda814bd348c1579329c86943d2cd3c6a6",
- "size": 7508,
- "url": "https://libraries.minecraft.net/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar"
- }
- },
- "name": "net.java.jutils:jutils:1.0.0"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/google/code/gson/gson/2.2.4/gson-2.2.4.jar",
- "sha1": "a60a5e993c98c864010053cb901b7eab25306568",
- "size": 190432,
- "url": "https://libraries.minecraft.net/com/google/code/gson/gson/2.2.4/gson-2.2.4.jar"
- }
- },
- "name": "com.google.code.gson:gson:2.2.4"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/mojang/authlib/1.5.22/authlib-1.5.22.jar",
- "sha1": "afaa8f6df976fcb5520e76ef1d5798c9e6b5c0b2",
- "size": 64539,
- "url": "https://libraries.minecraft.net/com/mojang/authlib/1.5.22/authlib-1.5.22.jar"
- }
- },
- "name": "com.mojang:authlib:1.5.22"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/mojang/realms/1.8.4/realms-1.8.4.jar",
- "sha1": "15f8dc326c97a96dee6e65392e145ad6d1cb46cb",
- "size": 1131574,
- "url": "https://libraries.minecraft.net/com/mojang/realms/1.8.4/realms-1.8.4.jar"
- }
- },
- "name": "com.mojang:realms:1.8.4"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar",
- "sha1": "a698750c16740fd5b3871425f4cb3bbaa87f529d",
- "size": 365552,
- "url": "https://libraries.minecraft.net/org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar"
- }
- },
- "name": "org.apache.commons:commons-compress:1.8.1"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar",
- "sha1": "18f4247ff4572a074444572cee34647c43e7c9c7",
- "size": 589512,
- "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar"
- }
- },
- "name": "org.apache.httpcomponents:httpclient:4.3.3"
- },
- {
- "downloads": {
- "artifact": {
- "path": "commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar",
- "sha1": "f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f",
- "size": 62050,
- "url": "https://libraries.minecraft.net/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar"
- }
- },
- "name": "commons-logging:commons-logging:1.1.3"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar",
- "sha1": "31fbbff1ddbf98f3aa7377c94d33b0447c646b6e",
- "size": 282269,
- "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar"
- }
- },
- "name": "org.apache.httpcomponents:httpcore:4.3.2"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/logging/log4j/log4j-api/2.0-beta9/log4j-api-2.0-beta9.jar",
- "sha1": "1dd66e68cccd907880229f9e2de1314bd13ff785",
- "size": 108161,
- "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-api/2.0-beta9/log4j-api-2.0-beta9.jar"
- }
- },
- "name": "org.apache.logging.log4j:log4j-api:2.0-beta9"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/logging/log4j/log4j-core/2.0-beta9/log4j-core-2.0-beta9.jar",
- "sha1": "678861ba1b2e1fccb594bb0ca03114bb05da9695",
- "size": 681134,
- "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-core/2.0-beta9/log4j-core-2.0-beta9.jar"
- }
- },
- "name": "org.apache.logging.log4j:log4j-core:2.0-beta9"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar",
- "sha1": "697517568c68e78ae0b4544145af031c81082dfe",
- "size": 1047168,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar"
- }
- },
- "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209",
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar",
- "sha1": "d51a7c040a721d13efdfbd34f8b257b2df882ad0",
- "size": 173887,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar"
- }
- },
- "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209",
- "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"
- }
- }
- ]
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl/2.9.2-nightly-20140822/lwjgl-2.9.2-nightly-20140822.jar",
- "sha1": "7707204c9ffa5d91662de95f0a224e2f721b22af",
- "size": 1045632,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.2-nightly-20140822/lwjgl-2.9.2-nightly-20140822.jar"
- }
- },
- "name": "org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-20140822",
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.2-nightly-20140822/lwjgl_util-2.9.2-nightly-20140822.jar",
- "sha1": "f0e612c840a7639c1f77f68d72a28dae2f0c8490",
- "size": 173887,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.2-nightly-20140822/lwjgl_util-2.9.2-nightly-20140822.jar"
- }
- },
- "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-20140822",
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "classifiers": {
- "natives-linux": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar",
- "sha1": "d898a33b5d0a6ef3fed3a4ead506566dce6720a5",
- "size": 578539,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar"
- },
- "natives-osx": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar",
- "sha1": "79f5ce2fea02e77fe47a3c745219167a542121d7",
- "size": 468116,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar"
- },
- "natives-windows": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar",
- "sha1": "78b2a55ce4dc29c6b3ec4df8ca165eba05f9b341",
- "size": 613680,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar"
- }
- }
- },
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.2-nightly-20140822",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- },
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "classifiers": {
- "natives-linux": {
- "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar",
- "sha1": "7ff832a6eb9ab6a767f1ade2b548092d0fa64795",
- "size": 10362,
- "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar"
- },
- "natives-osx": {
- "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar",
- "sha1": "53f9c919f34d2ca9de8c51fc4e1e8282029a9232",
- "size": 12186,
- "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar"
- },
- "natives-windows": {
- "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar",
- "sha1": "385ee093e01f587f30ee1c8a2ee7d408fd732e16",
- "size": 155179,
- "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar"
- }
- }
- },
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "net.java.jinput:jinput-platform:2.0.5",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- }
- }
- ],
- "mainClass": "net.minecraft.client.main.Main",
- "minecraftArguments": "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}",
- "minimumLauncherVersion": 18,
- "releaseTime": "2016-02-29T13:49:54+00:00",
- "time": "2016-03-01T13:14:53+00:00",
- "type": "release"
-}
diff --git a/api/logic/minecraft/testdata/codecwav-20101023.jar b/api/logic/minecraft/testdata/codecwav-20101023.jar
deleted file mode 100644
index f5236083..00000000
--- a/api/logic/minecraft/testdata/codecwav-20101023.jar
+++ /dev/null
@@ -1 +0,0 @@
-dummy test file.
diff --git a/api/logic/minecraft/testdata/lib-native-arch.json b/api/logic/minecraft/testdata/lib-native-arch.json
deleted file mode 100644
index 501826ae..00000000
--- a/api/logic/minecraft/testdata/lib-native-arch.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "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
deleted file mode 100644
index 5b9f3b55..00000000
--- a/api/logic/minecraft/testdata/lib-native.json
+++ /dev/null
@@ -1,52 +0,0 @@
-{
- "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
deleted file mode 100644
index 90bbff07..00000000
--- a/api/logic/minecraft/testdata/lib-simple.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "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/testdata/testname-testversion-linux-32.jar b/api/logic/minecraft/testdata/testname-testversion-linux-32.jar
deleted file mode 100644
index f5236083..00000000
--- a/api/logic/minecraft/testdata/testname-testversion-linux-32.jar
+++ /dev/null
@@ -1 +0,0 @@
-dummy test file.
diff --git a/api/logic/minecraft/update/AssetUpdateTask.cpp b/api/logic/minecraft/update/AssetUpdateTask.cpp
deleted file mode 100644
index e26ab4ef..00000000
--- a/api/logic/minecraft/update/AssetUpdateTask.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-#include "Env.h"
-#include "AssetUpdateTask.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "net/ChecksumValidator.h"
-#include "minecraft/AssetsUtils.h"
-
-AssetUpdateTask::AssetUpdateTask(MinecraftInstance * inst)
-{
- m_inst = inst;
-}
-
-AssetUpdateTask::~AssetUpdateTask()
-{
-}
-
-void AssetUpdateTask::executeTask()
-{
- setStatus(tr("Updating assets index..."));
- auto components = m_inst->getPackProfile();
- 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);
-
- 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);
-
- qDebug() << m_inst->name() << ": Starting asset index download";
- downloadJob->start();
-}
-
-bool AssetUpdateTask::canAbort() const
-{
- return true;
-}
-
-void AssetUpdateTask::assetIndexFinished()
-{
- AssetsIndex index;
- qDebug() << m_inst->name() << ": Finished asset index download";
-
- auto components = m_inst->getPackProfile();
- 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!"));
- }
-
- 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));
-}
-
-void AssetUpdateTask::assetsFailed(QString reason)
-{
- emitFailed(tr("Failed to download assets:\n%1").arg(reason));
-}
-
-bool AssetUpdateTask::abort()
-{
- 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
deleted file mode 100644
index fdfa8f1c..00000000
--- a/api/logic/minecraft/update/AssetUpdateTask.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-#include "tasks/Task.h"
-#include "net/NetJob.h"
-class MinecraftInstance;
-
-class AssetUpdateTask : public Task
-{
- Q_OBJECT
-public:
- AssetUpdateTask(MinecraftInstance * inst);
- virtual ~AssetUpdateTask();
-
- void executeTask() override;
-
- bool canAbort() const override;
-
-private slots:
- void assetIndexFinished();
- void assetIndexFailed(QString reason);
- void assetsFailed(QString reason);
-
-public slots:
- bool abort() override;
-
-private:
- MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
-};
diff --git a/api/logic/minecraft/update/FMLLibrariesTask.cpp b/api/logic/minecraft/update/FMLLibrariesTask.cpp
deleted file mode 100644
index a05a7c2a..00000000
--- a/api/logic/minecraft/update/FMLLibrariesTask.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-#include "Env.h"
-#include <FileSystem.h>
-#include <minecraft/VersionFilterData.h>
-#include "FMLLibrariesTask.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "BuildConfig.h"
-
-FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance * inst)
-{
- m_inst = inst;
-}
-void FMLLibrariesTask::executeTask()
-{
- // Get the mod list
- MinecraftInstance *inst = (MinecraftInstance *)m_inst;
- auto components = inst->getPackProfile();
- auto profile = components->getProfile();
-
- if (!profile->hasTrait("legacyFML"))
- {
- emitSucceeded();
- return;
- }
-
- QString version = components->getComponentVersion("net.minecraft");
- auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
- if (!fmlLibsMapping.contains(version))
- {
- emitSucceeded();
- return;
- }
-
- 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;
- }
-
- // 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;
- }
-
- // 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 = BuildConfig.FMLLIBS_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();
-}
-
-bool FMLLibrariesTask::canAbort() const
-{
- 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();
-}
-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));
-}
-
-bool FMLLibrariesTask::abort()
-{
- 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
deleted file mode 100644
index a1e70ed4..00000000
--- a/api/logic/minecraft/update/FMLLibrariesTask.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-#include "tasks/Task.h"
-#include "net/NetJob.h"
-#include "minecraft/VersionFilterData.h"
-
-class MinecraftInstance;
-
-class FMLLibrariesTask : public Task
-{
- Q_OBJECT
-public:
- FMLLibrariesTask(MinecraftInstance * inst);
- virtual ~FMLLibrariesTask() {};
-
- void executeTask() override;
-
- bool canAbort() const override;
-
-private slots:
- void fmllibsFinished();
- void fmllibsFailed(QString reason);
-
-public slots:
- bool abort() override;
-
-private:
- MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
- QList<FMLlib> fmlLibsToProcess;
-};
-
diff --git a/api/logic/minecraft/update/FoldersTask.cpp b/api/logic/minecraft/update/FoldersTask.cpp
deleted file mode 100644
index e2b1bb48..00000000
--- a/api/logic/minecraft/update/FoldersTask.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "FoldersTask.h"
-#include "minecraft/MinecraftInstance.h"
-#include <QDir>
-
-FoldersTask::FoldersTask(MinecraftInstance * inst)
- :Task()
-{
- m_inst = inst;
-}
-
-void FoldersTask::executeTask()
-{
- // 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
deleted file mode 100644
index f6ed5e6d..00000000
--- a/api/logic/minecraft/update/FoldersTask.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include "tasks/Task.h"
-
-class MinecraftInstance;
-class FoldersTask : public Task
-{
- Q_OBJECT
-public:
- FoldersTask(MinecraftInstance * inst);
- virtual ~FoldersTask() {};
-
- void executeTask() override;
-private:
- MinecraftInstance *m_inst;
-};
-
diff --git a/api/logic/minecraft/update/LibrariesTask.cpp b/api/logic/minecraft/update/LibrariesTask.cpp
deleted file mode 100644
index 7f66a651..00000000
--- a/api/logic/minecraft/update/LibrariesTask.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-#include "Env.h"
-#include "LibrariesTask.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-
-LibrariesTask::LibrariesTask(MinecraftInstance * 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;
-
- // Build a list of URLs that will need to be downloaded.
- auto components = inst->getPackProfile();
- auto profile = components->getProfile();
-
- auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name()));
- downloadJob.reset(job);
-
- auto metacache = ENV.metacache();
-
- 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->getMavenFiles());
- 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;
-}
-
-void LibrariesTask::jarlibFailed(QString 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;
-}
diff --git a/api/logic/minecraft/update/LibrariesTask.h b/api/logic/minecraft/update/LibrariesTask.h
deleted file mode 100644
index 49f76932..00000000
--- a/api/logic/minecraft/update/LibrariesTask.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-#include "tasks/Task.h"
-#include "net/NetJob.h"
-class MinecraftInstance;
-
-class LibrariesTask : public Task
-{
- Q_OBJECT
-public:
- LibrariesTask(MinecraftInstance * inst);
- virtual ~LibrariesTask() {};
-
- void executeTask() override;
-
- bool canAbort() const override;
-
-private slots:
- void jarlibFailed(QString reason);
-
-public slots:
- bool abort() override;
-
-private:
- MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
-};
diff --git a/api/logic/modplatform/atlauncher/ATLPackIndex.cpp b/api/logic/modplatform/atlauncher/ATLPackIndex.cpp
deleted file mode 100644
index 35f50b18..00000000
--- a/api/logic/modplatform/atlauncher/ATLPackIndex.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "ATLPackIndex.h"
-
-#include <QRegularExpression>
-
-#include "Json.h"
-
-static void loadIndexedVersion(ATLauncher::IndexedVersion & v, QJsonObject & obj)
-{
- v.version = Json::requireString(obj, "version");
- v.minecraft = Json::requireString(obj, "minecraft");
-}
-
-void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack & m, QJsonObject & obj)
-{
- m.id = Json::requireInteger(obj, "id");
- m.position = Json::requireInteger(obj, "position");
- m.name = Json::requireString(obj, "name");
- m.type = Json::requireString(obj, "type") == "private" ?
- ATLauncher::PackType::Private :
- ATLauncher::PackType::Public;
- auto versionsArr = Json::requireArray(obj, "versions");
- for (const auto versionRaw : versionsArr)
- {
- auto versionObj = Json::requireObject(versionRaw);
- ATLauncher::IndexedVersion version;
- loadIndexedVersion(version, versionObj);
- m.versions.append(version);
- }
- m.system = Json::ensureBoolean(obj, QString("system"), false);
- m.description = Json::ensureString(obj, "description", "");
-
- m.safeName = Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "");
-}
diff --git a/api/logic/modplatform/atlauncher/ATLPackIndex.h b/api/logic/modplatform/atlauncher/ATLPackIndex.h
deleted file mode 100644
index 5e2e6487..00000000
--- a/api/logic/modplatform/atlauncher/ATLPackIndex.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include "ATLPackManifest.h"
-
-#include <QString>
-#include <QVector>
-#include <QMetaType>
-
-#include "multimc_logic_export.h"
-
-namespace ATLauncher
-{
-
-struct IndexedVersion
-{
- QString version;
- QString minecraft;
-};
-
-struct IndexedPack
-{
- int id;
- int position;
- QString name;
- PackType type;
- QVector<IndexedVersion> versions;
- bool system;
- QString description;
-
- QString safeName;
-};
-
-MULTIMC_LOGIC_EXPORT void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
-}
-
-Q_DECLARE_METATYPE(ATLauncher::IndexedPack)
diff --git a/api/logic/modplatform/atlauncher/ATLPackInstallTask.cpp b/api/logic/modplatform/atlauncher/ATLPackInstallTask.cpp
deleted file mode 100644
index 55087a27..00000000
--- a/api/logic/modplatform/atlauncher/ATLPackInstallTask.cpp
+++ /dev/null
@@ -1,764 +0,0 @@
-#include <Env.h>
-#include <quazip.h>
-#include <QtConcurrent/QtConcurrent>
-#include <MMCZip.h>
-#include <minecraft/OneSixVersionFormat.h>
-#include <Version.h>
-#include <net/ChecksumValidator.h>
-#include "ATLPackInstallTask.h"
-
-#include "BuildConfig.h"
-#include "FileSystem.h"
-#include "Json.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "settings/INISettingsObject.h"
-#include "meta/Index.h"
-#include "meta/Version.h"
-#include "meta/VersionList.h"
-
-namespace ATLauncher {
-
-PackInstallTask::PackInstallTask(UserInteractionSupport *support, QString pack, QString version)
-{
- m_support = support;
- m_pack = pack;
- m_version_name = version;
-}
-
-bool PackInstallTask::abort()
-{
- if(abortable)
- {
- return jobPtr->abort();
- }
- return false;
-}
-
-void PackInstallTask::executeTask()
-{
- qDebug() << "PackInstallTask::executeTask: " << QThread::currentThreadId();
- auto *netJob = new NetJob("ATLauncher::VersionFetch");
- auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json")
- .arg(m_pack).arg(m_version_name);
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
- jobPtr = netJob;
- jobPtr->start();
-
- QObject::connect(netJob, &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
- QObject::connect(netJob, &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
-}
-
-void PackInstallTask::onDownloadSucceeded()
-{
- qDebug() << "PackInstallTask::onDownloadSucceeded: " << QThread::currentThreadId();
- jobPtr.reset();
-
- QJsonParseError parse_error;
- QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
- if(parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString();
- qWarning() << response;
- return;
- }
-
- auto obj = doc.object();
-
- ATLauncher::PackVersion version;
- try
- {
- ATLauncher::loadVersion(version, obj);
- }
- catch (const JSONValidationError &e)
- {
- emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
- return;
- }
- m_version = version;
-
- auto vlist = ENV.metadataIndex()->get("net.minecraft");
- if(!vlist)
- {
- emitFailed(tr("Failed to get local metadata index for %1").arg("net.minecraft"));
- return;
- }
-
- auto ver = vlist->getVersion(m_version.minecraft);
- if (!ver) {
- emitFailed(tr("Failed to get local metadata index for '%1' v%2").arg("net.minecraft").arg(m_version.minecraft));
- return;
- }
- ver->load(Net::Mode::Online);
- minecraftVersion = ver;
-
- if(m_version.noConfigs) {
- downloadMods();
- }
- else {
- installConfigs();
- }
-}
-
-void PackInstallTask::onDownloadFailed(QString reason)
-{
- qDebug() << "PackInstallTask::onDownloadFailed: " << QThread::currentThreadId();
- jobPtr.reset();
- emitFailed(reason);
-}
-
-QString PackInstallTask::getDirForModType(ModType type, QString raw)
-{
- switch (type) {
- // Mod types that can either be ignored at this stage, or ignored
- // completely.
- case ModType::Root:
- case ModType::Extract:
- case ModType::Decomp:
- case ModType::TexturePackExtract:
- case ModType::ResourcePackExtract:
- case ModType::MCPC:
- return Q_NULLPTR;
- case ModType::Forge:
- // Forge detection happens later on, if it cannot be detected it will
- // install a jarmod component.
- case ModType::Jar:
- return "jarmods";
- case ModType::Mods:
- return "mods";
- case ModType::Flan:
- return "Flan";
- case ModType::Dependency:
- return FS::PathCombine("mods", m_version.minecraft);
- case ModType::Ic2Lib:
- return FS::PathCombine("mods", "ic2");
- case ModType::DenLib:
- return FS::PathCombine("mods", "denlib");
- case ModType::Coremods:
- return "coremods";
- case ModType::Plugins:
- return "plugins";
- case ModType::TexturePack:
- return "texturepacks";
- case ModType::ResourcePack:
- return "resourcepacks";
- case ModType::ShaderPack:
- return "shaderpacks";
- case ModType::Millenaire:
- qWarning() << "Unsupported mod type: " + raw;
- return Q_NULLPTR;
- case ModType::Unknown:
- emitFailed(tr("Unknown mod type: %1").arg(raw));
- return Q_NULLPTR;
- }
-
- return Q_NULLPTR;
-}
-
-QString PackInstallTask::getVersionForLoader(QString uid)
-{
- if(m_version.loader.recommended || m_version.loader.latest || m_version.loader.choose) {
- auto vlist = ENV.metadataIndex()->get(uid);
- if(!vlist)
- {
- emitFailed(tr("Failed to get local metadata index for %1").arg(uid));
- return Q_NULLPTR;
- }
-
- if(!vlist->isLoaded()) {
- vlist->load(Net::Mode::Online);
- }
-
- if(m_version.loader.recommended || m_version.loader.latest) {
- for (int i = 0; i < vlist->versions().size(); i++) {
- auto version = vlist->versions().at(i);
- auto reqs = version->requires();
-
- // filter by minecraft version, if the loader depends on a certain version.
- // not all mod loaders depend on a given Minecraft version, so we won't do this
- // filtering for those loaders.
- if (m_version.loader.type != "fabric") {
- auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Meta::Require &req) {
- return req.uid == "net.minecraft";
- });
- if (iter == reqs.end()) continue;
- if (iter->equalsVersion != m_version.minecraft) continue;
- }
-
- if (m_version.loader.recommended) {
- // first recommended build we find, we use.
- if (!version->isRecommended()) continue;
- }
-
- return version->descriptor();
- }
-
- emitFailed(tr("Failed to find version for %1 loader").arg(m_version.loader.type));
- return Q_NULLPTR;
- }
- else if(m_version.loader.choose) {
- // Fabric Loader doesn't depend on a given Minecraft version.
- if (m_version.loader.type == "fabric") {
- return m_support->chooseVersion(vlist, Q_NULLPTR);
- }
-
- return m_support->chooseVersion(vlist, m_version.minecraft);
- }
- }
-
- if (m_version.loader.version == Q_NULLPTR || m_version.loader.version.isEmpty()) {
- emitFailed(tr("No loader version set for modpack!"));
- return Q_NULLPTR;
- }
-
- return m_version.loader.version;
-}
-
-QString PackInstallTask::detectLibrary(VersionLibrary library)
-{
- // Try to detect what the library is
- if (!library.server.isEmpty() && library.server.split("/").length() >= 3) {
- auto lastSlash = library.server.lastIndexOf("/");
- auto locationAndVersion = library.server.mid(0, lastSlash);
- auto fileName = library.server.mid(lastSlash + 1);
-
- lastSlash = locationAndVersion.lastIndexOf("/");
- auto location = locationAndVersion.mid(0, lastSlash);
- auto version = locationAndVersion.mid(lastSlash + 1);
-
- lastSlash = location.lastIndexOf("/");
- auto group = location.mid(0, lastSlash).replace("/", ".");
- auto artefact = location.mid(lastSlash + 1);
-
- return group + ":" + artefact + ":" + version;
- }
-
- if(library.file.contains("-")) {
- auto lastSlash = library.file.lastIndexOf("-");
- auto name = library.file.mid(0, lastSlash);
- auto version = library.file.mid(lastSlash + 1).remove(".jar");
-
- if(name == QString("guava")) {
- return "com.google.guava:guava:" + version;
- }
- else if(name == QString("commons-lang3")) {
- return "org.apache.commons:commons-lang3:" + version;
- }
- }
-
- return "org.multimc.atlauncher:" + library.md5 + ":1";
-}
-
-bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared_ptr<PackProfile> profile)
-{
- if(m_version.libraries.isEmpty()) {
- return true;
- }
-
- QList<GradleSpecifier> exempt;
- for(const auto & componentUid : componentsToInstall.keys()) {
- auto componentVersion = componentsToInstall.value(componentUid);
-
- for(const auto & library : componentVersion->data()->libraries) {
- GradleSpecifier lib(library->rawName());
- exempt.append(lib);
- }
- }
-
- {
- for(const auto & library : minecraftVersion->data()->libraries) {
- GradleSpecifier lib(library->rawName());
- exempt.append(lib);
- }
- }
-
- auto uuid = QUuid::createUuid();
- auto id = uuid.toString().remove('{').remove('}');
- auto target_id = "org.multimc.atlauncher." + id;
-
- auto patchDir = FS::PathCombine(instanceRoot, "patches");
- if(!FS::ensureFolderPathExists(patchDir))
- {
- return false;
- }
- auto patchFileName = FS::PathCombine(patchDir, target_id + ".json");
-
- auto f = std::make_shared<VersionFile>();
- f->name = m_pack + " " + m_version_name + " (libraries)";
-
- for(const auto & lib : m_version.libraries) {
- auto libName = detectLibrary(lib);
- GradleSpecifier libSpecifier(libName);
-
- bool libExempt = false;
- for(const auto & existingLib : exempt) {
- if(libSpecifier.matchName(existingLib)) {
- // If the pack specifies a newer version of the lib, use that!
- libExempt = Version(libSpecifier.version()) >= Version(existingLib.version());
- }
- }
- if(libExempt) continue;
-
- auto library = std::make_shared<Library>();
- library->setRawName(libName);
-
- switch(lib.download) {
- case DownloadType::Server:
- library->setAbsoluteUrl(BuildConfig.ATL_DOWNLOAD_SERVER_URL + lib.url);
- break;
- case DownloadType::Direct:
- library->setAbsoluteUrl(lib.url);
- break;
- case DownloadType::Browser:
- case DownloadType::Unknown:
- emitFailed(tr("Unknown or unsupported download type: %1").arg(lib.download_raw));
- return false;
- }
-
- f->libraries.append(library);
- }
-
- if(f->libraries.isEmpty()) {
- return true;
- }
-
- 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();
-
- profile->appendComponent(new Component(profile.get(), target_id, f));
- return true;
-}
-
-bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr<PackProfile> profile)
-{
- if(m_version.mainClass == QString() && m_version.extraArguments == QString()) {
- return true;
- }
-
- auto uuid = QUuid::createUuid();
- auto id = uuid.toString().remove('{').remove('}');
- auto target_id = "org.multimc.atlauncher." + id;
-
- auto patchDir = FS::PathCombine(instanceRoot, "patches");
- if(!FS::ensureFolderPathExists(patchDir))
- {
- return false;
- }
- auto patchFileName = FS::PathCombine(patchDir, target_id + ".json");
-
- QStringList mainClasses;
- QStringList tweakers;
- for(const auto & componentUid : componentsToInstall.keys()) {
- auto componentVersion = componentsToInstall.value(componentUid);
-
- if(componentVersion->data()->mainClass != QString("")) {
- mainClasses.append(componentVersion->data()->mainClass);
- }
- tweakers.append(componentVersion->data()->addTweakers);
- }
-
- auto f = std::make_shared<VersionFile>();
- f->name = m_pack + " " + m_version_name;
- if(m_version.mainClass != QString() && !mainClasses.contains(m_version.mainClass)) {
- f->mainClass = m_version.mainClass;
- }
-
- // Parse out tweakers
- auto args = m_version.extraArguments.split(" ");
- QString previous;
- for(auto arg : args) {
- if(arg.startsWith("--tweakClass=") || previous == "--tweakClass") {
- auto tweakClass = arg.remove("--tweakClass=");
- if(tweakers.contains(tweakClass)) continue;
-
- f->addTweakers.append(tweakClass);
- }
- previous = arg;
- }
-
- if(f->mainClass == QString() && f->addTweakers.isEmpty()) {
- return true;
- }
-
- 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();
-
- profile->appendComponent(new Component(profile.get(), target_id, f));
- return true;
-}
-
-void PackInstallTask::installConfigs()
-{
- qDebug() << "PackInstallTask::installConfigs: " << QThread::currentThreadId();
- setStatus(tr("Downloading configs..."));
- jobPtr.reset(new NetJob(tr("Config download")));
-
- auto path = QString("Configs/%1/%2.zip").arg(m_pack).arg(m_version_name);
- auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.zip")
- .arg(m_pack).arg(m_version_name);
- auto entry = ENV.metacache()->resolveEntry("ATLauncherPacks", path);
- entry->setStale(true);
-
- auto dl = Net::Download::makeCached(url, entry);
- if (!m_version.configs.sha1.isEmpty()) {
- auto rawSha1 = QByteArray::fromHex(m_version.configs.sha1.toLatin1());
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
- }
- jobPtr->addNetAction(dl);
- archivePath = entry->getFullPath();
-
- connect(jobPtr.get(), &NetJob::succeeded, this, [&]()
- {
- abortable = false;
- jobPtr.reset();
- extractConfigs();
- });
- connect(jobPtr.get(), &NetJob::failed, [&](QString reason)
- {
- abortable = false;
- jobPtr.reset();
- emitFailed(reason);
- });
- connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total)
- {
- abortable = true;
- setProgress(current, total);
- });
-
- jobPtr->start();
-}
-
-void PackInstallTask::extractConfigs()
-{
- qDebug() << "PackInstallTask::extractConfigs: " << QThread::currentThreadId();
- setStatus(tr("Extracting configs..."));
-
- QDir extractDir(m_stagingPath);
-
- QuaZip packZip(archivePath);
- if(!packZip.open(QuaZip::mdUnzip))
- {
- emitFailed(tr("Failed to open pack configs %1!").arg(archivePath));
- return;
- }
-
- m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/minecraft");
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, [&]()
- {
- downloadMods();
- });
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, [&]()
- {
- emitAborted();
- });
- m_extractFutureWatcher.setFuture(m_extractFuture);
-}
-
-void PackInstallTask::downloadMods()
-{
- qDebug() << "PackInstallTask::installMods: " << QThread::currentThreadId();
-
- QVector<ATLauncher::VersionMod> optionalMods;
- for (const auto& mod : m_version.mods) {
- if (mod.optional) {
- optionalMods.push_back(mod);
- }
- }
-
- // Select optional mods, if pack contains any
- QVector<QString> selectedMods;
- if (!optionalMods.isEmpty()) {
- setStatus(tr("Selecting optional mods..."));
- selectedMods = m_support->chooseOptionalMods(optionalMods);
- }
-
- setStatus(tr("Downloading mods..."));
-
- jarmods.clear();
- jobPtr.reset(new NetJob(tr("Mod download")));
- for(const auto& mod : m_version.mods) {
- // skip non-client mods
- if(!mod.client) continue;
-
- // skip optional mods that were not selected
- if(mod.optional && !selectedMods.contains(mod.name)) continue;
-
- QString url;
- switch(mod.download) {
- case DownloadType::Server:
- url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url;
- break;
- case DownloadType::Browser:
- emitFailed(tr("Unsupported download type: %1").arg(mod.download_raw));
- return;
- case DownloadType::Direct:
- url = mod.url;
- break;
- case DownloadType::Unknown:
- emitFailed(tr("Unknown download type: %1").arg(mod.download_raw));
- return;
- }
-
- QFileInfo fileName(mod.file);
- auto cacheName = fileName.completeBaseName() + "-" + mod.md5 + "." + fileName.suffix();
-
- if (mod.type == ModType::Extract || mod.type == ModType::TexturePackExtract || mod.type == ModType::ResourcePackExtract) {
- auto entry = ENV.metacache()->resolveEntry("ATLauncherPacks", cacheName);
- entry->setStale(true);
- modsToExtract.insert(entry->getFullPath(), mod);
-
- auto dl = Net::Download::makeCached(url, entry);
- if (!mod.md5.isEmpty()) {
- auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
- }
- jobPtr->addNetAction(dl);
- }
- else if(mod.type == ModType::Decomp) {
- auto entry = ENV.metacache()->resolveEntry("ATLauncherPacks", cacheName);
- entry->setStale(true);
- modsToDecomp.insert(entry->getFullPath(), mod);
-
- auto dl = Net::Download::makeCached(url, entry);
- if (!mod.md5.isEmpty()) {
- auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
- }
- jobPtr->addNetAction(dl);
- }
- else {
- auto relpath = getDirForModType(mod.type, mod.type_raw);
- if(relpath == Q_NULLPTR) continue;
-
- auto entry = ENV.metacache()->resolveEntry("ATLauncherPacks", cacheName);
- entry->setStale(true);
-
- auto dl = Net::Download::makeCached(url, entry);
- if (!mod.md5.isEmpty()) {
- auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
- }
- jobPtr->addNetAction(dl);
-
- auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file);
- qDebug() << "Will download" << url << "to" << path;
- modsToCopy[entry->getFullPath()] = path;
-
- if(mod.type == ModType::Forge) {
- auto vlist = ENV.metadataIndex()->get("net.minecraftforge");
- if(vlist)
- {
- auto ver = vlist->getVersion(mod.version);
- if(ver) {
- ver->load(Net::Mode::Online);
- componentsToInstall.insert("net.minecraftforge", ver);
- continue;
- }
- }
-
- qDebug() << "Jarmod: " + path;
- jarmods.push_back(path);
- }
-
- if(mod.type == ModType::Jar) {
- qDebug() << "Jarmod: " + path;
- jarmods.push_back(path);
- }
- }
- }
-
- connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded);
- connect(jobPtr.get(), &NetJob::failed, [&](QString reason)
- {
- abortable = false;
- jobPtr.reset();
- emitFailed(reason);
- });
- connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total)
- {
- abortable = true;
- setProgress(current, total);
- });
-
- jobPtr->start();
-}
-
-void PackInstallTask::onModsDownloaded() {
- abortable = false;
-
- qDebug() << "PackInstallTask::onModsDownloaded: " << QThread::currentThreadId();
- jobPtr.reset();
-
- if(!modsToExtract.empty() || !modsToDecomp.empty() || !modsToCopy.empty()) {
- m_modExtractFuture = QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy);
- connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onModsExtracted);
- connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, [&]()
- {
- emitAborted();
- });
- m_modExtractFutureWatcher.setFuture(m_modExtractFuture);
- }
- else {
- install();
- }
-}
-
-void PackInstallTask::onModsExtracted() {
- qDebug() << "PackInstallTask::onModsExtracted: " << QThread::currentThreadId();
- if(m_modExtractFuture.result()) {
- install();
- }
- else {
- emitFailed(tr("Failed to extract mods..."));
- }
-}
-
-bool PackInstallTask::extractMods(
- const QMap<QString, VersionMod> &toExtract,
- const QMap<QString, VersionMod> &toDecomp,
- const QMap<QString, QString> &toCopy
-) {
- qDebug() << "PackInstallTask::extractMods: " << QThread::currentThreadId();
-
- setStatus(tr("Extracting mods..."));
- for (auto iter = toExtract.begin(); iter != toExtract.end(); iter++) {
- auto &modPath = iter.key();
- auto &mod = iter.value();
-
- QString extractToDir;
- if(mod.type == ModType::Extract) {
- extractToDir = getDirForModType(mod.extractTo, mod.extractTo_raw);
- }
- else if(mod.type == ModType::TexturePackExtract) {
- extractToDir = FS::PathCombine("texturepacks", "extracted");
- }
- else if(mod.type == ModType::ResourcePackExtract) {
- extractToDir = FS::PathCombine("resourcepacks", "extracted");
- }
-
- QDir extractDir(m_stagingPath);
- auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir);
-
- QString folderToExtract = "";
- if(mod.type == ModType::Extract) {
- folderToExtract = mod.extractFolder;
- folderToExtract.remove(QRegExp("^/"));
- }
-
- qDebug() << "Extracting " + mod.file + " to " + extractToDir;
- if(!MMCZip::extractDir(modPath, folderToExtract, extractToPath)) {
- // assume error
- return false;
- }
- }
-
- for (auto iter = toDecomp.begin(); iter != toDecomp.end(); iter++) {
- auto &modPath = iter.key();
- auto &mod = iter.value();
- auto extractToDir = getDirForModType(mod.decompType, mod.decompType_raw);
-
- QDir extractDir(m_stagingPath);
- auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir, mod.decompFile);
-
- qDebug() << "Extracting " + mod.decompFile + " to " + extractToDir;
- if(!MMCZip::extractFile(modPath, mod.decompFile, extractToPath)) {
- qWarning() << "Failed to extract" << mod.decompFile;
- return false;
- }
- }
-
- for (auto iter = toCopy.begin(); iter != toCopy.end(); iter++) {
- auto &from = iter.key();
- auto &to = iter.value();
- FS::copy fileCopyOperation(from, to);
- if(!fileCopyOperation()) {
- qWarning() << "Failed to copy" << from << "to" << to;
- return false;
- }
- }
- return true;
-}
-
-void PackInstallTask::install()
-{
- qDebug() << "PackInstallTask::install: " << QThread::currentThreadId();
- setStatus(tr("Installing modpack"));
-
- auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
- instanceSettings->suspendSave();
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
-
- MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
- auto components = instance.getPackProfile();
- components->buildingFromScratch();
-
- // Use a component to add libraries BEFORE Minecraft
- if(!createLibrariesComponent(instance.instanceRoot(), components)) {
- emitFailed(tr("Failed to create libraries component"));
- return;
- }
-
- // Minecraft
- components->setComponentVersion("net.minecraft", m_version.minecraft, true);
-
- // Loader
- if(m_version.loader.type == QString("forge"))
- {
- auto version = getVersionForLoader("net.minecraftforge");
- if(version == Q_NULLPTR) return;
-
- components->setComponentVersion("net.minecraftforge", version, true);
- }
- else if(m_version.loader.type == QString("fabric"))
- {
- auto version = getVersionForLoader("net.fabricmc.fabric-loader");
- if(version == Q_NULLPTR) return;
-
- components->setComponentVersion("net.fabricmc.fabric-loader", version, true);
- }
- else if(m_version.loader.type != QString())
- {
- emitFailed(tr("Unknown loader type: ") + m_version.loader.type);
- return;
- }
-
- for(const auto & componentUid : componentsToInstall.keys()) {
- auto version = componentsToInstall.value(componentUid);
- components->setComponentVersion(componentUid, version->version());
- }
-
- components->installJarMods(jarmods);
-
- // Use a component to fill in the rest of the data
- // todo: use more detection
- if(!createPackComponent(instance.instanceRoot(), components)) {
- emitFailed(tr("Failed to create pack component"));
- return;
- }
-
- components->saveNow();
-
- instance.setName(m_instName);
- instance.setIconKey(m_instIcon);
- instanceSettings->resumeSave();
-
- jarmods.clear();
- emitSucceeded();
-}
-
-}
diff --git a/api/logic/modplatform/atlauncher/ATLPackInstallTask.h b/api/logic/modplatform/atlauncher/ATLPackInstallTask.h
deleted file mode 100644
index 15fd9b32..00000000
--- a/api/logic/modplatform/atlauncher/ATLPackInstallTask.h
+++ /dev/null
@@ -1,102 +0,0 @@
-#pragma once
-
-#include <meta/VersionList.h>
-#include "ATLPackManifest.h"
-
-#include "InstanceTask.h"
-#include "multimc_logic_export.h"
-#include "net/NetJob.h"
-#include "settings/INISettingsObject.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "meta/Version.h"
-
-#include <nonstd/optional>
-
-namespace ATLauncher {
-
-class MULTIMC_LOGIC_EXPORT UserInteractionSupport {
-
-public:
- /**
- * Requests a user interaction to select which optional mods should be installed.
- */
- virtual QVector<QString> chooseOptionalMods(QVector<ATLauncher::VersionMod> mods) = 0;
-
- /**
- * Requests a user interaction to select a component version from a given version list
- * and constrained to a given Minecraft version.
- */
- virtual QString chooseVersion(Meta::VersionListPtr vlist, QString minecraftVersion) = 0;
-
-};
-
-class MULTIMC_LOGIC_EXPORT PackInstallTask : public InstanceTask
-{
-Q_OBJECT
-
-public:
- explicit PackInstallTask(UserInteractionSupport *support, QString pack, QString version);
- virtual ~PackInstallTask(){}
-
- bool canAbort() const override { return true; }
- bool abort() override;
-
-protected:
- virtual void executeTask() override;
-
-private slots:
- void onDownloadSucceeded();
- void onDownloadFailed(QString reason);
-
- void onModsDownloaded();
- void onModsExtracted();
-
-private:
- QString getDirForModType(ModType type, QString raw);
- QString getVersionForLoader(QString uid);
- QString detectLibrary(VersionLibrary library);
-
- bool createLibrariesComponent(QString instanceRoot, std::shared_ptr<PackProfile> profile);
- bool createPackComponent(QString instanceRoot, std::shared_ptr<PackProfile> profile);
-
- void installConfigs();
- void extractConfigs();
- void downloadMods();
- bool extractMods(
- const QMap<QString, VersionMod> &toExtract,
- const QMap<QString, VersionMod> &toDecomp,
- const QMap<QString, QString> &toCopy
- );
- void install();
-
-private:
- UserInteractionSupport *m_support;
-
- bool abortable = false;
-
- NetJobPtr jobPtr;
- QByteArray response;
-
- QString m_pack;
- QString m_version_name;
- PackVersion m_version;
-
- QMap<QString, VersionMod> modsToExtract;
- QMap<QString, VersionMod> modsToDecomp;
- QMap<QString, QString> modsToCopy;
-
- QString archivePath;
- QStringList jarmods;
- Meta::VersionPtr minecraftVersion;
- QMap<QString, Meta::VersionPtr> componentsToInstall;
-
- QFuture<nonstd::optional<QStringList>> m_extractFuture;
- QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
-
- QFuture<bool> m_modExtractFuture;
- QFutureWatcher<bool> m_modExtractFutureWatcher;
-
-};
-
-}
diff --git a/api/logic/modplatform/atlauncher/ATLPackManifest.cpp b/api/logic/modplatform/atlauncher/ATLPackManifest.cpp
deleted file mode 100644
index e25d8346..00000000
--- a/api/logic/modplatform/atlauncher/ATLPackManifest.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-#include "ATLPackManifest.h"
-
-#include "Json.h"
-
-static ATLauncher::DownloadType parseDownloadType(QString rawType) {
- if(rawType == QString("server")) {
- return ATLauncher::DownloadType::Server;
- }
- else if(rawType == QString("browser")) {
- return ATLauncher::DownloadType::Browser;
- }
- else if(rawType == QString("direct")) {
- return ATLauncher::DownloadType::Direct;
- }
-
- return ATLauncher::DownloadType::Unknown;
-}
-
-static ATLauncher::ModType parseModType(QString rawType) {
- // See https://wiki.atlauncher.com/mod_types
- if(rawType == QString("root")) {
- return ATLauncher::ModType::Root;
- }
- else if(rawType == QString("forge")) {
- return ATLauncher::ModType::Forge;
- }
- else if(rawType == QString("jar")) {
- return ATLauncher::ModType::Jar;
- }
- else if(rawType == QString("mods")) {
- return ATLauncher::ModType::Mods;
- }
- else if(rawType == QString("flan")) {
- return ATLauncher::ModType::Flan;
- }
- else if(rawType == QString("dependency") || rawType == QString("depandency")) {
- return ATLauncher::ModType::Dependency;
- }
- else if(rawType == QString("ic2lib")) {
- return ATLauncher::ModType::Ic2Lib;
- }
- else if(rawType == QString("denlib")) {
- return ATLauncher::ModType::DenLib;
- }
- else if(rawType == QString("coremods")) {
- return ATLauncher::ModType::Coremods;
- }
- else if(rawType == QString("mcpc")) {
- return ATLauncher::ModType::MCPC;
- }
- else if(rawType == QString("plugins")) {
- return ATLauncher::ModType::Plugins;
- }
- else if(rawType == QString("extract")) {
- return ATLauncher::ModType::Extract;
- }
- else if(rawType == QString("decomp")) {
- return ATLauncher::ModType::Decomp;
- }
- else if(rawType == QString("texturepack")) {
- return ATLauncher::ModType::TexturePack;
- }
- else if(rawType == QString("resourcepack")) {
- return ATLauncher::ModType::ResourcePack;
- }
- else if(rawType == QString("shaderpack")) {
- return ATLauncher::ModType::ShaderPack;
- }
- else if(rawType == QString("texturepackextract")) {
- return ATLauncher::ModType::TexturePackExtract;
- }
- else if(rawType == QString("resourcepackextract")) {
- return ATLauncher::ModType::ResourcePackExtract;
- }
- else if(rawType == QString("millenaire")) {
- return ATLauncher::ModType::Millenaire;
- }
-
- return ATLauncher::ModType::Unknown;
-}
-
-static void loadVersionLoader(ATLauncher::VersionLoader & p, QJsonObject & obj) {
- p.type = Json::requireString(obj, "type");
- p.choose = Json::ensureBoolean(obj, QString("choose"), false);
-
- auto metadata = Json::requireObject(obj, "metadata");
- p.latest = Json::ensureBoolean(metadata, QString("latest"), false);
- p.recommended = Json::ensureBoolean(metadata, QString("recommended"), false);
-
- // Minecraft Forge
- if (p.type == "forge") {
- p.version = Json::ensureString(metadata, "version", "");
- }
-
- // Fabric Loader
- if (p.type == "fabric") {
- p.version = Json::ensureString(metadata, "loader", "");
- }
-}
-
-static void loadVersionLibrary(ATLauncher::VersionLibrary & p, QJsonObject & obj) {
- p.url = Json::requireString(obj, "url");
- p.file = Json::requireString(obj, "file");
- p.md5 = Json::requireString(obj, "md5");
-
- p.download_raw = Json::requireString(obj, "download");
- p.download = parseDownloadType(p.download_raw);
-
- p.server = Json::ensureString(obj, "server", "");
-}
-
-static void loadVersionConfigs(ATLauncher::VersionConfigs & p, QJsonObject & obj) {
- p.filesize = Json::requireInteger(obj, "filesize");
- p.sha1 = Json::requireString(obj, "sha1");
-}
-
-static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) {
- p.name = Json::requireString(obj, "name");
- p.version = Json::requireString(obj, "version");
- p.url = Json::requireString(obj, "url");
- p.file = Json::requireString(obj, "file");
- p.md5 = Json::ensureString(obj, "md5", "");
-
- p.download_raw = Json::requireString(obj, "download");
- p.download = parseDownloadType(p.download_raw);
-
- p.type_raw = Json::requireString(obj, "type");
- p.type = parseModType(p.type_raw);
-
- // This contributes to the Minecraft Forge detection, where we rely on mod.type being "Forge"
- // when the mod represents Forge. As there is little difference between "Jar" and "Forge, some
- // packs regretfully use "Jar". This will correct the type to "Forge" in these cases (as best
- // it can).
- if(p.name == QString("Minecraft Forge") && p.type == ATLauncher::ModType::Jar) {
- p.type_raw = "forge";
- p.type = ATLauncher::ModType::Forge;
- }
-
- if(obj.contains("extractTo")) {
- p.extractTo_raw = Json::requireString(obj, "extractTo");
- p.extractTo = parseModType(p.extractTo_raw);
- p.extractFolder = Json::ensureString(obj, "extractFolder", "").replace("%s%", "/");
- }
-
- if(obj.contains("decompType")) {
- p.decompType_raw = Json::requireString(obj, "decompType");
- p.decompType = parseModType(p.decompType_raw);
- p.decompFile = Json::requireString(obj, "decompFile");
- }
-
- p.description = Json::ensureString(obj, QString("description"), "");
- p.optional = Json::ensureBoolean(obj, QString("optional"), false);
- p.recommended = Json::ensureBoolean(obj, QString("recommended"), false);
- p.selected = Json::ensureBoolean(obj, QString("selected"), false);
- p.hidden = Json::ensureBoolean(obj, QString("hidden"), false);
- p.library = Json::ensureBoolean(obj, QString("library"), false);
- p.group = Json::ensureString(obj, QString("group"), "");
- if(obj.contains("depends")) {
- auto dependsArr = Json::requireArray(obj, "depends");
- for (const auto depends : dependsArr) {
- p.depends.append(Json::requireString(depends));
- }
- }
-
- p.client = Json::ensureBoolean(obj, QString("client"), false);
-
- // computed
- p.effectively_hidden = p.hidden || p.library;
-}
-
-void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj)
-{
- v.version = Json::requireString(obj, "version");
- v.minecraft = Json::requireString(obj, "minecraft");
- v.noConfigs = Json::ensureBoolean(obj, QString("noConfigs"), false);
-
- if(obj.contains("mainClass")) {
- auto main = Json::requireObject(obj, "mainClass");
- v.mainClass = Json::ensureString(main, "mainClass", "");
- }
-
- if(obj.contains("extraArguments")) {
- auto arguments = Json::requireObject(obj, "extraArguments");
- v.extraArguments = Json::ensureString(arguments, "arguments", "");
- }
-
- if(obj.contains("loader")) {
- auto loader = Json::requireObject(obj, "loader");
- loadVersionLoader(v.loader, loader);
- }
-
- if(obj.contains("libraries")) {
- auto libraries = Json::requireArray(obj, "libraries");
- for (const auto libraryRaw : libraries)
- {
- auto libraryObj = Json::requireObject(libraryRaw);
- ATLauncher::VersionLibrary target;
- loadVersionLibrary(target, libraryObj);
- v.libraries.append(target);
- }
- }
-
- if(obj.contains("mods")) {
- auto mods = Json::requireArray(obj, "mods");
- for (const auto modRaw : mods)
- {
- auto modObj = Json::requireObject(modRaw);
- ATLauncher::VersionMod mod;
- loadVersionMod(mod, modObj);
- v.mods.append(mod);
- }
- }
-
- if(obj.contains("configs")) {
- auto configsObj = Json::requireObject(obj, "configs");
- loadVersionConfigs(v.configs, configsObj);
- }
-}
diff --git a/api/logic/modplatform/atlauncher/ATLPackManifest.h b/api/logic/modplatform/atlauncher/ATLPackManifest.h
deleted file mode 100644
index 17821e4c..00000000
--- a/api/logic/modplatform/atlauncher/ATLPackManifest.h
+++ /dev/null
@@ -1,126 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QVector>
-#include <QJsonObject>
-#include <multimc_logic_export.h>
-
-namespace ATLauncher
-{
-
-enum class PackType
-{
- Public,
- Private
-};
-
-enum class ModType
-{
- Root,
- Forge,
- Jar,
- Mods,
- Flan,
- Dependency,
- Ic2Lib,
- DenLib,
- Coremods,
- MCPC,
- Plugins,
- Extract,
- Decomp,
- TexturePack,
- ResourcePack,
- ShaderPack,
- TexturePackExtract,
- ResourcePackExtract,
- Millenaire,
- Unknown
-};
-
-enum class DownloadType
-{
- Server,
- Browser,
- Direct,
- Unknown
-};
-
-struct VersionLoader
-{
- QString type;
- bool latest;
- bool recommended;
- bool choose;
-
- QString version;
-};
-
-struct VersionLibrary
-{
- QString url;
- QString file;
- QString server;
- QString md5;
- DownloadType download;
- QString download_raw;
-};
-
-struct VersionMod
-{
- QString name;
- QString version;
- QString url;
- QString file;
- QString md5;
- DownloadType download;
- QString download_raw;
- ModType type;
- QString type_raw;
-
- ModType extractTo;
- QString extractTo_raw;
- QString extractFolder;
-
- ModType decompType;
- QString decompType_raw;
- QString decompFile;
-
- QString description;
- bool optional;
- bool recommended;
- bool selected;
- bool hidden;
- bool library;
- QString group;
- QVector<QString> depends;
-
- bool client;
-
- // computed
- bool effectively_hidden;
-};
-
-struct VersionConfigs
-{
- int filesize;
- QString sha1;
-};
-
-struct PackVersion
-{
- QString version;
- QString minecraft;
- bool noConfigs;
- QString mainClass;
- QString extraArguments;
-
- VersionLoader loader;
- QVector<VersionLibrary> libraries;
- QVector<VersionMod> mods;
- VersionConfigs configs;
-};
-
-MULTIMC_LOGIC_EXPORT void loadVersion(PackVersion & v, QJsonObject & obj);
-
-}
diff --git a/api/logic/modplatform/flame/FileResolvingTask.cpp b/api/logic/modplatform/flame/FileResolvingTask.cpp
deleted file mode 100644
index 295574f0..00000000
--- a/api/logic/modplatform/flame/FileResolvingTask.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "FileResolvingTask.h"
-#include "Json.h"
-
-namespace {
- const char * metabase = "https://cursemeta.dries007.net";
-}
-
-Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess)
- : m_toProcess(toProcess)
-{
-}
-
-void Flame::FileResolvingTask::executeTask()
-{
- setStatus(tr("Resolving mod IDs..."));
- setProgress(0, m_toProcess.files.size());
- m_dljob.reset(new NetJob("Mod id resolver"));
- results.resize(m_toProcess.files.size());
- int index = 0;
- for(auto & file: m_toProcess.files)
- {
- auto projectIdStr = QString::number(file.projectId);
- auto fileIdStr = QString::number(file.fileId);
- QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
- auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results[index]);
- m_dljob->addNetAction(dl);
- index ++;
- }
- connect(m_dljob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::netJobFinished);
- m_dljob->start();
-}
-
-void Flame::FileResolvingTask::netJobFinished()
-{
- bool failed = false;
- int index = 0;
- for(auto & bytes: results)
- {
- auto & out = m_toProcess.files[index];
- try
- {
- failed &= (!out.parseFromBytes(bytes));
- }
- catch (const JSONValidationError &e)
- {
-
- qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
- qCritical() << e.cause();
- qCritical() << "JSON:";
- qCritical() << bytes;
- failed = true;
- }
- index++;
- }
- if(!failed)
- {
- emitSucceeded();
- }
- else
- {
- emitFailed(tr("Some mod ID resolving tasks failed."));
- }
-}
diff --git a/api/logic/modplatform/flame/FileResolvingTask.h b/api/logic/modplatform/flame/FileResolvingTask.h
deleted file mode 100644
index 5679b907..00000000
--- a/api/logic/modplatform/flame/FileResolvingTask.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include "tasks/Task.h"
-#include "net/NetJob.h"
-#include "PackManifest.h"
-
-#include "multimc_logic_export.h"
-
-namespace Flame
-{
-class MULTIMC_LOGIC_EXPORT FileResolvingTask : public Task
-{
- Q_OBJECT
-public:
- explicit FileResolvingTask(Flame::Manifest &toProcess);
- virtual ~FileResolvingTask() {};
-
- const Flame::Manifest &getResults() const
- {
- return m_toProcess;
- }
-
-protected:
- virtual void executeTask() override;
-
-protected slots:
- void netJobFinished();
-
-private: /* data */
- Flame::Manifest m_toProcess;
- QVector<QByteArray> results;
- NetJobPtr m_dljob;
-};
-}
diff --git a/api/logic/modplatform/flame/FlamePackIndex.cpp b/api/logic/modplatform/flame/FlamePackIndex.cpp
deleted file mode 100644
index 3d8ea22a..00000000
--- a/api/logic/modplatform/flame/FlamePackIndex.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "FlamePackIndex.h"
-
-#include "Json.h"
-
-void Flame::loadIndexedPack(Flame::IndexedPack & pack, QJsonObject & obj)
-{
- pack.addonId = Json::requireInteger(obj, "id");
- pack.name = Json::requireString(obj, "name");
- pack.websiteUrl = Json::ensureString(obj, "websiteUrl", "");
- pack.description = Json::ensureString(obj, "summary", "");
-
- bool thumbnailFound = false;
- auto attachments = Json::requireArray(obj, "attachments");
- for(auto attachmentRaw: attachments) {
- auto attachmentObj = Json::requireObject(attachmentRaw);
- bool isDefault = attachmentObj.value("isDefault").toBool(false);
- if(isDefault) {
- thumbnailFound = true;
- pack.logoName = Json::requireString(attachmentObj, "title");
- pack.logoUrl = Json::requireString(attachmentObj, "thumbnailUrl");
- break;
- }
- }
-
- if(!thumbnailFound) {
- throw JSONValidationError(QString("Pack without an icon, skipping: %1").arg(pack.name));
- }
-
- auto authors = Json::requireArray(obj, "authors");
- for(auto authorIter: authors) {
- auto author = Json::requireObject(authorIter);
- Flame::ModpackAuthor packAuthor;
- packAuthor.name = Json::requireString(author, "name");
- packAuthor.url = Json::requireString(author, "url");
- pack.authors.append(packAuthor);
- }
- int defaultFileId = Json::requireInteger(obj, "defaultFileId");
-
- bool found = false;
- // check if there are some files before adding the pack
- auto files = Json::requireArray(obj, "latestFiles");
- for(auto fileIter: files) {
- auto file = Json::requireObject(fileIter);
- int id = Json::requireInteger(file, "id");
-
- // NOTE: for now, ignore everything that's not the default...
- if(id != defaultFileId) {
- continue;
- }
-
- auto versionArray = Json::requireArray(file, "gameVersion");
- if(versionArray.size() < 1) {
- continue;
- }
-
- found = true;
- break;
- }
- if(!found) {
- throw JSONValidationError(QString("Pack with no good file, skipping: %1").arg(pack.name));
- }
-}
-
-void Flame::loadIndexedPackVersions(Flame::IndexedPack & pack, QJsonArray & arr)
-{
- QVector<Flame::IndexedVersion> unsortedVersions;
- for(auto versionIter: arr) {
- auto version = Json::requireObject(versionIter);
- Flame::IndexedVersion file;
-
- file.addonId = pack.addonId;
- file.fileId = Json::requireInteger(version, "id");
- auto versionArray = Json::requireArray(version, "gameVersion");
- if(versionArray.size() < 1) {
- continue;
- }
-
- // pick the latest version supported
- file.mcVersion = versionArray[0].toString();
- file.version = Json::requireString(version, "displayName");
- file.downloadUrl = Json::requireString(version, "downloadUrl");
- unsortedVersions.append(file);
- }
-
- auto orderSortPredicate = [](const IndexedVersion & a, const IndexedVersion & b) -> bool
- {
- return a.fileId > b.fileId;
- };
- std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
- pack.versions = unsortedVersions;
- pack.versionsLoaded = true;
-}
diff --git a/api/logic/modplatform/flame/FlamePackIndex.h b/api/logic/modplatform/flame/FlamePackIndex.h
deleted file mode 100644
index cdeb2c13..00000000
--- a/api/logic/modplatform/flame/FlamePackIndex.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma once
-
-#include <QList>
-#include <QMetaType>
-#include <QString>
-#include <QVector>
-
-#include "multimc_logic_export.h"
-
-namespace Flame {
-
-struct ModpackAuthor {
- QString name;
- QString url;
-};
-
-struct IndexedVersion {
- int addonId;
- int fileId;
- QString version;
- QString mcVersion;
- QString downloadUrl;
-};
-
-struct IndexedPack
-{
- int addonId;
- QString name;
- QString description;
- QList<ModpackAuthor> authors;
- QString logoName;
- QString logoUrl;
- QString websiteUrl;
-
- bool versionsLoaded = false;
- QVector<IndexedVersion> versions;
-};
-
-MULTIMC_LOGIC_EXPORT void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
-MULTIMC_LOGIC_EXPORT void loadIndexedPackVersions(IndexedPack & m, QJsonArray & arr);
-}
-
-Q_DECLARE_METATYPE(Flame::IndexedPack)
diff --git a/api/logic/modplatform/flame/PackManifest.cpp b/api/logic/modplatform/flame/PackManifest.cpp
deleted file mode 100644
index b928fd16..00000000
--- a/api/logic/modplatform/flame/PackManifest.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-#include "PackManifest.h"
-#include "Json.h"
-
-static void loadFileV1(Flame::File & f, QJsonObject & file)
-{
- f.projectId = Json::requireInteger(file, "projectID");
- f.fileId = Json::requireInteger(file, "fileID");
- f.required = Json::ensureBoolean(file, QString("required"), true);
-}
-
-static void loadModloaderV1(Flame::Modloader & m, QJsonObject & modLoader)
-{
- m.id = Json::requireString(modLoader, "id");
- m.primary = Json::ensureBoolean(modLoader, QString("primary"), false);
-}
-
-static void loadMinecraftV1(Flame::Minecraft & m, QJsonObject & minecraft)
-{
- m.version = Json::requireString(minecraft, "version");
- // extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack
- // intended use is likely hardcoded in the 'Flame' client, the manifest says nothing
- m.libraries = Json::ensureString(minecraft, QString("libraries"), QString());
- auto arr = Json::ensureArray(minecraft, "modLoaders", QJsonArray());
- for (QJsonValueRef 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 (QJsonValueRef item : arr)
- {
- auto obj = Json::requireObject(item);
- Flame::File file;
- loadFileV1(file, obj);
- m.files.append(file);
- }
- m.overrides = Json::ensureString(manifest, "overrides", "overrides");
-}
-
-void Flame::loadManifest(Flame::Manifest & m, const QString &filepath)
-{
- auto doc = Json::requireDocument(filepath);
- auto obj = Json::requireObject(doc);
- m.manifestType = Json::requireString(obj, "manifestType");
- if(m.manifestType != "minecraftModpack")
- {
- throw JSONValidationError("Not a modpack manifest!");
- }
- m.manifestVersion = Json::requireInteger(obj, "manifestVersion");
- if(m.manifestVersion != 1)
- {
- throw JSONValidationError(QString("Unknown manifest version (%1)").arg(m.manifestVersion));
- }
- loadManifestV1(m, obj);
-}
-
-bool Flame::File::parseFromBytes(const QByteArray& bytes)
-{
- auto doc = Json::requireDocument(bytes);
- auto obj = Json::requireObject(doc);
- // result code signifies true failure.
- if(obj.contains("code"))
- {
- qCritical() << "Resolving of" << projectId << fileId << "failed because of a negative result:";
- qCritical() << bytes;
- return false;
- }
- fileName = Json::requireString(obj, "FileNameOnDisk");
- QString rawUrl = Json::requireString(obj, "DownloadURL");
- url = QUrl(rawUrl, QUrl::TolerantMode);
- if(!url.isValid())
- {
- throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
- }
- // This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
- // It is also optional
- QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
- if(!projObj.isEmpty())
- {
- QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
- if(strType == "singlefile")
- {
- type = File::Type::SingleFile;
- }
- else if(strType == "ctoc")
- {
- type = File::Type::Ctoc;
- }
- else if(strType == "cmod2")
- {
- type = File::Type::Cmod2;
- }
- else if(strType == "mod")
- {
- type = File::Type::Mod;
- }
- else if(strType == "folder")
- {
- type = File::Type::Folder;
- }
- else if(strType == "modpack")
- {
- type = File::Type::Modpack;
- }
- else
- {
- qCritical() << "Resolving of" << projectId << fileId << "failed because of unknown file type:" << strType;
- type = File::Type::Unknown;
- return false;
- }
- targetFolder = Json::ensureString(projObj, "Path", "mods");
- }
- resolved = true;
- return true;
-}
diff --git a/api/logic/modplatform/flame/PackManifest.h b/api/logic/modplatform/flame/PackManifest.h
deleted file mode 100644
index 02f39f0e..00000000
--- a/api/logic/modplatform/flame/PackManifest.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QVector>
-#include <QUrl>
-
-namespace Flame
-{
-struct File
-{
- // NOTE: throws JSONValidationError
- bool parseFromBytes(const QByteArray &bytes);
-
- int projectId = 0;
- int fileId = 0;
- // NOTE: the opposite to 'optional'. This is at the time of writing unused.
- bool required = true;
-
- // our
- bool resolved = false;
- QString fileName;
- QUrl url;
- QString targetFolder = QLatin1Literal("mods");
- enum class Type
- {
- Unknown,
- Folder,
- Ctoc,
- SingleFile,
- Cmod2,
- Modpack,
- Mod
- } type = Type::Mod;
-};
-
-struct Modloader
-{
- QString id;
- bool primary = false;
-};
-
-struct Minecraft
-{
- QString version;
- QString libraries;
- QVector<Flame::Modloader> modLoaders;
-};
-
-struct Manifest
-{
- QString manifestType;
- int manifestVersion = 0;
- Flame::Minecraft minecraft;
- QString name;
- QString version;
- QString author;
- QVector<Flame::File> files;
- QString overrides;
-};
-
-void loadManifest(Flame::Manifest & m, const QString &filepath);
-}
diff --git a/api/logic/modplatform/legacy_ftb/PackFetchTask.cpp b/api/logic/modplatform/legacy_ftb/PackFetchTask.cpp
deleted file mode 100644
index c2ef6436..00000000
--- a/api/logic/modplatform/legacy_ftb/PackFetchTask.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-#include "PackFetchTask.h"
-#include "PrivatePackManager.h"
-
-#include <QDomDocument>
-#include <BuildConfig.h>
-
-namespace LegacyFTB {
-
-void PackFetchTask::fetch()
-{
- publicPacks.clear();
- thirdPartyPacks.clear();
-
- NetJob *netJob = new NetJob("LegacyFTB::ModpackFetch");
-
- QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_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(BuildConfig.LEGACY_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, &PackFetchTask::fileDownloadFinished);
- QObject::connect(netJob, &NetJob::failed, this, &PackFetchTask::fileDownloadFailed);
-
- jobPtr.reset(netJob);
- netJob->start();
-}
-
-void PackFetchTask::fetchPrivate(const QStringList & toFetch)
-{
- QString privatePackBaseUrl = BuildConfig.LEGACY_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]
- {
- ModpackList packs;
- parseAndAddPacks(*data, PackType::Private, packs);
- foreach(Modpack 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();
- }
-}
-
-void PackFetchTask::fileDownloadFinished()
-{
- jobPtr.reset();
-
- QStringList failedLists;
-
- if(!parseAndAddPacks(publicModpacksXmlFileData, PackType::Public, publicPacks))
- {
- failedLists.append(tr("Public Packs"));
- }
-
- if(!parseAndAddPacks(thirdPartyModpacksXmlFileData, PackType::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 PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, ModpackList &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: %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();
-
- Modpack 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 PackFetchTask::fileDownloadFailed(QString reason)
-{
- qWarning() << "Fetching FTBPacks failed:" << reason;
- emit failed(reason);
-}
-
-}
diff --git a/api/logic/modplatform/legacy_ftb/PackFetchTask.h b/api/logic/modplatform/legacy_ftb/PackFetchTask.h
deleted file mode 100644
index 4a8469b1..00000000
--- a/api/logic/modplatform/legacy_ftb/PackFetchTask.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include "net/NetJob.h"
-#include <QTemporaryDir>
-#include <QByteArray>
-#include <QObject>
-#include "PackHelpers.h"
-
-namespace LegacyFTB {
-
-class MULTIMC_LOGIC_EXPORT PackFetchTask : public QObject {
-
- Q_OBJECT
-
-public:
- PackFetchTask() = default;
- virtual ~PackFetchTask() = default;
-
- void fetch();
- void fetchPrivate(const QStringList &toFetch);
-
-private:
- NetJobPtr jobPtr;
-
- QByteArray publicModpacksXmlFileData;
- QByteArray thirdPartyModpacksXmlFileData;
-
- bool parseAndAddPacks(QByteArray &data, PackType packType, ModpackList &list);
- ModpackList publicPacks;
- ModpackList thirdPartyPacks;
-
-protected slots:
- void fileDownloadFinished();
- void fileDownloadFailed(QString reason);
-
-signals:
- void finished(ModpackList publicPacks, ModpackList thirdPartyPacks);
- void failed(QString reason);
-
- void privateFileDownloadFinished(Modpack modpack);
- void privateFileDownloadFailed(QString reason, QString packCode);
-};
-
-}
diff --git a/api/logic/modplatform/legacy_ftb/PackHelpers.h b/api/logic/modplatform/legacy_ftb/PackHelpers.h
deleted file mode 100644
index 566210d0..00000000
--- a/api/logic/modplatform/legacy_ftb/PackHelpers.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#pragma once
-
-#include <QList>
-#include <QString>
-#include <QStringList>
-#include <QMetaType>
-
-namespace LegacyFTB {
-
-//Header for structs etc...
-enum class PackType
-{
- Public,
- ThirdParty,
- Private
-};
-
-struct Modpack
-{
- 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;
-
- PackType type;
- QString packCode;
-};
-
-typedef QList<Modpack> ModpackList;
-
-}
-
-//We need it for the proxy model
-Q_DECLARE_METATYPE(LegacyFTB::Modpack)
diff --git a/api/logic/modplatform/legacy_ftb/PackInstallTask.cpp b/api/logic/modplatform/legacy_ftb/PackInstallTask.cpp
deleted file mode 100644
index c77f3250..00000000
--- a/api/logic/modplatform/legacy_ftb/PackInstallTask.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-#include "PackInstallTask.h"
-
-#include "Env.h"
-#include "MMCZip.h"
-
-#include "BaseInstance.h"
-#include "FileSystem.h"
-#include "settings/INISettingsObject.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "minecraft/GradleSpecifier.h"
-#include "BuildConfig.h"
-
-#include <QtConcurrent>
-
-namespace LegacyFTB {
-
-PackInstallTask::PackInstallTask(Modpack pack, QString version)
-{
- m_pack = pack;
- m_version = version;
-}
-
-void PackInstallTask::executeTask()
-{
- downloadPack();
-}
-
-void PackInstallTask::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;
- if(m_pack.type == PackType::Private)
- {
- url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(packoffset);
- }
- else
- {
- url = QString(BuildConfig.LEGACY_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, &PackInstallTask::onDownloadSucceeded);
- connect(job, &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
- connect(job, &NetJob::progress, this, &PackInstallTask::onDownloadProgress);
- job->start();
-
- progress(1, 4);
-}
-
-void PackInstallTask::onDownloadSucceeded()
-{
- abortable = false;
- unzip();
-}
-
-void PackInstallTask::onDownloadFailed(QString reason)
-{
- abortable = false;
- emitFailed(reason);
-}
-
-void PackInstallTask::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));
-}
-
-void PackInstallTask::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, &PackInstallTask::onUnzipFinished);
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::onUnzipCanceled);
- m_extractFutureWatcher.setFuture(m_extractFuture);
-}
-
-void PackInstallTask::onUnzipFinished()
-{
- install();
-}
-
-void PackInstallTask::onUnzipCanceled()
-{
- emitAborted();
-}
-
-void PackInstallTask::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->suspendSave();
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
-
- MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
- auto components = instance.getPackProfile();
- 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 PackInstallTask::abort()
-{
- if(abortable)
- {
- return netJobContainer->abort();
- }
- return false;
-}
-
-}
diff --git a/api/logic/modplatform/legacy_ftb/PackInstallTask.h b/api/logic/modplatform/legacy_ftb/PackInstallTask.h
deleted file mode 100644
index f3515781..00000000
--- a/api/logic/modplatform/legacy_ftb/PackInstallTask.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma once
-#include "InstanceTask.h"
-#include "net/NetJob.h"
-#include "quazip.h"
-#include "quazipdir.h"
-#include "meta/Index.h"
-#include "meta/Version.h"
-#include "meta/VersionList.h"
-#include "PackHelpers.h"
-
-#include <nonstd/optional>
-
-namespace LegacyFTB {
-
-class MULTIMC_LOGIC_EXPORT PackInstallTask : public InstanceTask
-{
- Q_OBJECT
-
-public:
- explicit PackInstallTask(Modpack pack, QString version);
- virtual ~PackInstallTask(){}
-
- bool canAbort() const override { return true; }
- bool abort() override;
-
-protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
-
-private:
- void downloadPack();
- void unzip();
- void install();
-
-private slots:
- void onDownloadSucceeded();
- void onDownloadFailed(QString reason);
- void onDownloadProgress(qint64 current, qint64 total);
-
- void onUnzipFinished();
- void onUnzipCanceled();
-
-private: /* data */
- bool abortable = false;
- std::unique_ptr<QuaZip> m_packZip;
- QFuture<nonstd::optional<QStringList>> m_extractFuture;
- QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
- NetJobPtr netJobContainer;
- QString archivePath;
-
- Modpack m_pack;
- QString m_version;
-};
-
-}
diff --git a/api/logic/modplatform/legacy_ftb/PrivatePackManager.cpp b/api/logic/modplatform/legacy_ftb/PrivatePackManager.cpp
deleted file mode 100644
index 501e6003..00000000
--- a/api/logic/modplatform/legacy_ftb/PrivatePackManager.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "PrivatePackManager.h"
-
-#include <QDebug>
-
-#include "FileSystem.h"
-
-namespace LegacyFTB {
-
-void PrivatePackManager::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 PrivatePackManager::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/legacy_ftb/PrivatePackManager.h b/api/logic/modplatform/legacy_ftb/PrivatePackManager.h
deleted file mode 100644
index 0232bac7..00000000
--- a/api/logic/modplatform/legacy_ftb/PrivatePackManager.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include <QSet>
-#include <QString>
-#include <QFile>
-#include "multimc_logic_export.h"
-
-namespace LegacyFTB {
-
-class MULTIMC_LOGIC_EXPORT PrivatePackManager
-{
-public:
- ~PrivatePackManager()
- {
- 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/modpacksch/FTBPackInstallTask.cpp b/api/logic/modplatform/modpacksch/FTBPackInstallTask.cpp
deleted file mode 100644
index f22373bc..00000000
--- a/api/logic/modplatform/modpacksch/FTBPackInstallTask.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-#include "FTBPackInstallTask.h"
-
-#include "BuildConfig.h"
-#include "Env.h"
-#include "FileSystem.h"
-#include "Json.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "net/ChecksumValidator.h"
-#include "settings/INISettingsObject.h"
-
-namespace ModpacksCH {
-
-PackInstallTask::PackInstallTask(Modpack pack, QString version)
-{
- m_pack = pack;
- m_version_name = version;
-}
-
-bool PackInstallTask::abort()
-{
- if(abortable)
- {
- return jobPtr->abort();
- }
- return false;
-}
-
-void PackInstallTask::executeTask()
-{
- // Find pack version
- bool found = false;
- VersionInfo version;
-
- for(auto vInfo : m_pack.versions) {
- if (vInfo.name == m_version_name) {
- found = true;
- version = vInfo;
- break;
- }
- }
-
- if(!found) {
- emitFailed(tr("Failed to find pack version %1").arg(m_version_name));
- return;
- }
-
- auto *netJob = new NetJob("ModpacksCH::VersionFetch");
- auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1/%2")
- .arg(m_pack.id).arg(version.id);
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
- jobPtr = netJob;
- jobPtr->start();
-
- QObject::connect(netJob, &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
- QObject::connect(netJob, &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
-}
-
-void PackInstallTask::onDownloadSucceeded()
-{
- jobPtr.reset();
-
- QJsonParseError parse_error;
- QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
- if(parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString();
- qWarning() << response;
- return;
- }
-
- auto obj = doc.object();
-
- ModpacksCH::Version version;
- try
- {
- ModpacksCH::loadVersion(version, obj);
- }
- catch (const JSONValidationError &e)
- {
- emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
- return;
- }
- m_version = version;
-
- downloadPack();
-}
-
-void PackInstallTask::onDownloadFailed(QString reason)
-{
- jobPtr.reset();
- emitFailed(reason);
-}
-
-void PackInstallTask::downloadPack()
-{
- setStatus(tr("Downloading mods..."));
-
- jobPtr.reset(new NetJob(tr("Mod download")));
- for(auto file : m_version.files) {
- if(file.serverOnly) continue;
-
- QFileInfo fileName(file.name);
- auto cacheName = fileName.completeBaseName() + "-" + file.sha1 + "." + fileName.suffix();
-
- auto entry = ENV.metacache()->resolveEntry("ModpacksCHPacks", cacheName);
- entry->setStale(true);
-
- auto relpath = FS::PathCombine("minecraft", file.path, file.name);
- auto path = FS::PathCombine(m_stagingPath, relpath);
-
- qDebug() << "Will download" << file.url << "to" << path;
- filesToCopy[entry->getFullPath()] = path;
-
- auto dl = Net::Download::makeCached(file.url, entry);
- if (!file.sha1.isEmpty()) {
- auto rawSha1 = QByteArray::fromHex(file.sha1.toLatin1());
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
- }
- jobPtr->addNetAction(dl);
- }
-
- connect(jobPtr.get(), &NetJob::succeeded, this, [&]()
- {
- abortable = false;
- jobPtr.reset();
- install();
- });
- connect(jobPtr.get(), &NetJob::failed, [&](QString reason)
- {
- abortable = false;
- jobPtr.reset();
- emitFailed(reason);
- });
- connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total)
- {
- abortable = true;
- setProgress(current, total);
- });
-
- jobPtr->start();
-}
-
-void PackInstallTask::install()
-{
- setStatus(tr("Copying modpack files"));
-
- for (auto iter = filesToCopy.begin(); iter != filesToCopy.end(); iter++) {
- auto &from = iter.key();
- auto &to = iter.value();
- FS::copy fileCopyOperation(from, to);
- if(!fileCopyOperation()) {
- qWarning() << "Failed to copy" << from << "to" << to;
- emitFailed(tr("Failed to copy files"));
- return;
- }
- }
-
- setStatus(tr("Installing modpack"));
-
- auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
- instanceSettings->suspendSave();
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
-
- MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
- auto components = instance.getPackProfile();
- components->buildingFromScratch();
-
- for(auto target : m_version.targets) {
- if(target.type == "game" && target.name == "minecraft") {
- components->setComponentVersion("net.minecraft", target.version, true);
- break;
- }
- }
-
- for(auto target : m_version.targets) {
- if(target.type != "modloader") continue;
-
- if(target.name == "forge") {
- components->setComponentVersion("net.minecraftforge", target.version, true);
- }
- else if(target.name == "fabric") {
- components->setComponentVersion("net.fabricmc.fabric-loader", target.version, true);
- }
- }
-
- // install any jar mods
- QDir jarModsDir(FS::PathCombine(m_stagingPath, "minecraft", "jarmods"));
- if (jarModsDir.exists()) {
- QStringList jarMods;
-
- for (const auto& info : jarModsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
- jarMods.push_back(info.absoluteFilePath());
- }
-
- components->installJarMods(jarMods);
- }
-
- components->saveNow();
-
- instance.setName(m_instName);
- instance.setIconKey(m_instIcon);
- instanceSettings->resumeSave();
-
- emitSucceeded();
-}
-
-}
diff --git a/api/logic/modplatform/modpacksch/FTBPackInstallTask.h b/api/logic/modplatform/modpacksch/FTBPackInstallTask.h
deleted file mode 100644
index 55db3d3c..00000000
--- a/api/logic/modplatform/modpacksch/FTBPackInstallTask.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#include "FTBPackManifest.h"
-
-#include "InstanceTask.h"
-#include "multimc_logic_export.h"
-#include "net/NetJob.h"
-
-namespace ModpacksCH {
-
-class MULTIMC_LOGIC_EXPORT PackInstallTask : public InstanceTask
-{
- Q_OBJECT
-
-public:
- explicit PackInstallTask(Modpack pack, QString version);
- virtual ~PackInstallTask(){}
-
- bool canAbort() const override { return true; }
- bool abort() override;
-
-protected:
- virtual void executeTask() override;
-
-private slots:
- void onDownloadSucceeded();
- void onDownloadFailed(QString reason);
-
-private:
- void downloadPack();
- void install();
-
-private:
- bool abortable = false;
-
- NetJobPtr jobPtr;
- QByteArray response;
-
- Modpack m_pack;
- QString m_version_name;
- Version m_version;
-
- QMap<QString, QString> filesToCopy;
-
-};
-
-}
diff --git a/api/logic/modplatform/modpacksch/FTBPackManifest.cpp b/api/logic/modplatform/modpacksch/FTBPackManifest.cpp
deleted file mode 100644
index fd99d332..00000000
--- a/api/logic/modplatform/modpacksch/FTBPackManifest.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-#include "FTBPackManifest.h"
-
-#include "Json.h"
-
-static void loadSpecs(ModpacksCH::Specs & s, QJsonObject & obj)
-{
- s.id = Json::requireInteger(obj, "id");
- s.minimum = Json::requireInteger(obj, "minimum");
- s.recommended = Json::requireInteger(obj, "recommended");
-}
-
-static void loadTag(ModpacksCH::Tag & t, QJsonObject & obj)
-{
- t.id = Json::requireInteger(obj, "id");
- t.name = Json::requireString(obj, "name");
-}
-
-static void loadArt(ModpacksCH::Art & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.url = Json::requireString(obj, "url");
- a.type = Json::requireString(obj, "type");
- a.width = Json::requireInteger(obj, "width");
- a.height = Json::requireInteger(obj, "height");
- a.compressed = Json::requireBoolean(obj, "compressed");
- a.sha1 = Json::requireString(obj, "sha1");
- a.size = Json::requireInteger(obj, "size");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadAuthor(ModpacksCH::Author & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.name = Json::requireString(obj, "name");
- a.type = Json::requireString(obj, "type");
- a.website = Json::requireString(obj, "website");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadVersionInfo(ModpacksCH::VersionInfo & v, QJsonObject & obj)
-{
- v.id = Json::requireInteger(obj, "id");
- v.name = Json::requireString(obj, "name");
- v.type = Json::requireString(obj, "type");
- v.updated = Json::requireInteger(obj, "updated");
- auto specs = Json::requireObject(obj, "specs");
- loadSpecs(v.specs, specs);
-}
-
-void ModpacksCH::loadModpack(ModpacksCH::Modpack & m, QJsonObject & obj)
-{
- m.id = Json::requireInteger(obj, "id");
- m.name = Json::requireString(obj, "name");
- m.synopsis = Json::requireString(obj, "synopsis");
- m.description = Json::requireString(obj, "description");
- m.type = Json::requireString(obj, "type");
- m.featured = Json::requireBoolean(obj, "featured");
- m.installs = Json::requireInteger(obj, "installs");
- m.plays = Json::requireInteger(obj, "plays");
- m.updated = Json::requireInteger(obj, "updated");
- m.refreshed = Json::requireInteger(obj, "refreshed");
- auto artArr = Json::requireArray(obj, "art");
- for (QJsonValueRef artRaw : artArr)
- {
- auto artObj = Json::requireObject(artRaw);
- ModpacksCH::Art art;
- loadArt(art, artObj);
- m.art.append(art);
- }
- auto authorArr = Json::requireArray(obj, "authors");
- for (QJsonValueRef authorRaw : authorArr)
- {
- auto authorObj = Json::requireObject(authorRaw);
- ModpacksCH::Author author;
- loadAuthor(author, authorObj);
- m.authors.append(author);
- }
- auto versionArr = Json::requireArray(obj, "versions");
- for (QJsonValueRef versionRaw : versionArr)
- {
- auto versionObj = Json::requireObject(versionRaw);
- ModpacksCH::VersionInfo version;
- loadVersionInfo(version, versionObj);
- m.versions.append(version);
- }
- auto tagArr = Json::requireArray(obj, "tags");
- for (QJsonValueRef tagRaw : tagArr)
- {
- auto tagObj = Json::requireObject(tagRaw);
- ModpacksCH::Tag tag;
- loadTag(tag, tagObj);
- m.tags.append(tag);
- }
- m.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadVersionTarget(ModpacksCH::VersionTarget & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.name = Json::requireString(obj, "name");
- a.type = Json::requireString(obj, "type");
- a.version = Json::requireString(obj, "version");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadVersionFile(ModpacksCH::VersionFile & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.type = Json::requireString(obj, "type");
- a.path = Json::requireString(obj, "path");
- a.name = Json::requireString(obj, "name");
- a.version = Json::requireString(obj, "version");
- a.url = Json::requireString(obj, "url");
- a.sha1 = Json::requireString(obj, "sha1");
- a.size = Json::requireInteger(obj, "size");
- a.clientOnly = Json::requireBoolean(obj, "clientonly");
- a.serverOnly = Json::requireBoolean(obj, "serveronly");
- a.optional = Json::requireBoolean(obj, "optional");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-void ModpacksCH::loadVersion(ModpacksCH::Version & m, QJsonObject & obj)
-{
- m.id = Json::requireInteger(obj, "id");
- m.parent = Json::requireInteger(obj, "parent");
- m.name = Json::requireString(obj, "name");
- m.type = Json::requireString(obj, "type");
- m.installs = Json::requireInteger(obj, "installs");
- m.plays = Json::requireInteger(obj, "plays");
- m.updated = Json::requireInteger(obj, "updated");
- m.refreshed = Json::requireInteger(obj, "refreshed");
- auto specs = Json::requireObject(obj, "specs");
- loadSpecs(m.specs, specs);
- auto targetArr = Json::requireArray(obj, "targets");
- for (QJsonValueRef targetRaw : targetArr)
- {
- auto versionObj = Json::requireObject(targetRaw);
- ModpacksCH::VersionTarget target;
- loadVersionTarget(target, versionObj);
- m.targets.append(target);
- }
- auto fileArr = Json::requireArray(obj, "files");
- for (QJsonValueRef fileRaw : fileArr)
- {
- auto fileObj = Json::requireObject(fileRaw);
- ModpacksCH::VersionFile file;
- loadVersionFile(file, fileObj);
- m.files.append(file);
- }
-}
-
-//static void loadVersionChangelog(ModpacksCH::VersionChangelog & m, QJsonObject & obj)
-//{
-// m.content = Json::requireString(obj, "content");
-// m.updated = Json::requireInteger(obj, "updated");
-//}
diff --git a/api/logic/modplatform/modpacksch/FTBPackManifest.h b/api/logic/modplatform/modpacksch/FTBPackManifest.h
deleted file mode 100644
index 518fffbf..00000000
--- a/api/logic/modplatform/modpacksch/FTBPackManifest.h
+++ /dev/null
@@ -1,127 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QVector>
-#include <QUrl>
-#include <QJsonObject>
-#include <QMetaType>
-
-#include "multimc_logic_export.h"
-
-namespace ModpacksCH
-{
-
-struct Specs
-{
- int id;
- int minimum;
- int recommended;
-};
-
-struct Tag
-{
- int id;
- QString name;
-};
-
-struct Art
-{
- int id;
- QString url;
- QString type;
- int width;
- int height;
- bool compressed;
- QString sha1;
- int size;
- int64_t updated;
-};
-
-struct Author
-{
- int id;
- QString name;
- QString type;
- QString website;
- int64_t updated;
-};
-
-struct VersionInfo
-{
- int id;
- QString name;
- QString type;
- int64_t updated;
- Specs specs;
-};
-
-struct Modpack
-{
- int id;
- QString name;
- QString synopsis;
- QString description;
- QString type;
- bool featured;
- int installs;
- int plays;
- int64_t updated;
- int64_t refreshed;
- QVector<Art> art;
- QVector<Author> authors;
- QVector<VersionInfo> versions;
- QVector<Tag> tags;
-};
-
-struct VersionTarget
-{
- int id;
- QString type;
- QString name;
- QString version;
- int64_t updated;
-};
-
-struct VersionFile
-{
- int id;
- QString type;
- QString path;
- QString name;
- QString version;
- QString url;
- QString sha1;
- int size;
- bool clientOnly;
- bool serverOnly;
- bool optional;
- int64_t updated;
-};
-
-struct Version
-{
- int id;
- int parent;
- QString name;
- QString type;
- int installs;
- int plays;
- int64_t updated;
- int64_t refreshed;
- Specs specs;
- QVector<VersionTarget> targets;
- QVector<VersionFile> files;
-};
-
-struct VersionChangelog
-{
- QString content;
- int64_t updated;
-};
-
-MULTIMC_LOGIC_EXPORT void loadModpack(Modpack & m, QJsonObject & obj);
-
-MULTIMC_LOGIC_EXPORT void loadVersion(Version & m, QJsonObject & obj);
-}
-
-Q_DECLARE_METATYPE(ModpacksCH::Modpack)
diff --git a/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp b/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp
deleted file mode 100644
index dbce8e53..00000000
--- a/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SingleZipPackInstallTask.h"
-
-#include "Env.h"
-#include "MMCZip.h"
-#include "TechnicPackProcessor.h"
-
-#include <QtConcurrent>
-#include <FileSystem.h>
-
-Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion)
-{
- m_sourceUrl = sourceUrl;
- m_minecraftVersion = minecraftVersion;
-}
-
-bool Technic::SingleZipPackInstallTask::abort() {
- if(m_abortable)
- {
- return m_filesNetJob->abort();
- }
- return false;
-}
-
-void Technic::SingleZipPackInstallTask::executeTask()
-{
- setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
-
- 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, &Technic::SingleZipPackInstallTask::downloadSucceeded);
- connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged);
- connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed);
- m_filesNetJob->start();
-}
-
-void Technic::SingleZipPackInstallTask::downloadSucceeded()
-{
- m_abortable = false;
-
- setStatus(tr("Extracting modpack"));
- QDir extractDir(FS::PathCombine(m_stagingPath, ".minecraft"));
- 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;
- }
- m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), QString(""), extractDir.absolutePath());
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &Technic::SingleZipPackInstallTask::extractFinished);
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &Technic::SingleZipPackInstallTask::extractAborted);
- m_extractFutureWatcher.setFuture(m_extractFuture);
- m_filesNetJob.reset();
-}
-
-void Technic::SingleZipPackInstallTask::downloadFailed(QString reason)
-{
- m_abortable = false;
- emitFailed(reason);
- m_filesNetJob.reset();
-}
-
-void Technic::SingleZipPackInstallTask::downloadProgressChanged(qint64 current, qint64 total)
-{
- m_abortable = true;
- setProgress(current / 2, total);
-}
-
-void Technic::SingleZipPackInstallTask::extractFinished()
-{
- m_packZip.reset();
- if (!m_extractFuture.result())
- {
- 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;
- }
- }
- }
-
- shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
- connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SingleZipPackInstallTask::emitSucceeded);
- connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SingleZipPackInstallTask::emitFailed);
- packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath, m_minecraftVersion);
-}
-
-void Technic::SingleZipPackInstallTask::extractAborted()
-{
- emitFailed(tr("Instance import has been aborted."));
-}
diff --git a/api/logic/modplatform/technic/SingleZipPackInstallTask.h b/api/logic/modplatform/technic/SingleZipPackInstallTask.h
deleted file mode 100644
index ec2ff605..00000000
--- a/api/logic/modplatform/technic/SingleZipPackInstallTask.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "InstanceTask.h"
-#include "net/NetJob.h"
-#include "multimc_logic_export.h"
-
-#include "quazip.h"
-
-#include <QFutureWatcher>
-#include <QStringList>
-#include <QUrl>
-
-#include <nonstd/optional>
-
-namespace Technic {
-
-class MULTIMC_LOGIC_EXPORT SingleZipPackInstallTask : public InstanceTask
-{
- Q_OBJECT
-
-public:
- SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion);
-
- bool canAbort() const override { return true; }
- bool abort() override;
-
-protected:
- void executeTask() override;
-
-
-private slots:
- void downloadSucceeded();
- void downloadFailed(QString reason);
- void downloadProgressChanged(qint64 current, qint64 total);
- void extractFinished();
- void extractAborted();
-
-private:
- bool m_abortable = false;
-
- QUrl m_sourceUrl;
- QString m_minecraftVersion;
- QString m_archivePath;
- NetJobPtr m_filesNetJob;
- std::unique_ptr<QuaZip> m_packZip;
- QFuture<nonstd::optional<QStringList>> m_extractFuture;
- QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
-};
-
-} // namespace Technic
diff --git a/api/logic/modplatform/technic/SolderPackInstallTask.cpp b/api/logic/modplatform/technic/SolderPackInstallTask.cpp
deleted file mode 100644
index 1b4186d4..00000000
--- a/api/logic/modplatform/technic/SolderPackInstallTask.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SolderPackInstallTask.h"
-
-#include <FileSystem.h>
-#include <Json.h>
-#include <QtConcurrentRun>
-#include <MMCZip.h>
-#include "TechnicPackProcessor.h"
-
-Technic::SolderPackInstallTask::SolderPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion)
-{
- m_sourceUrl = sourceUrl;
- m_minecraftVersion = minecraftVersion;
-}
-
-bool Technic::SolderPackInstallTask::abort() {
- if(m_abortable)
- {
- return m_filesNetJob->abort();
- }
- return false;
-}
-
-void Technic::SolderPackInstallTask::executeTask()
-{
- setStatus(tr("Finding recommended version:\n%1").arg(m_sourceUrl.toString()));
- m_filesNetJob.reset(new NetJob(tr("Finding recommended version")));
- m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
- auto job = m_filesNetJob.get();
- connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::versionSucceeded);
- connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
- m_filesNetJob->start();
-}
-
-void Technic::SolderPackInstallTask::versionSucceeded()
-{
- try
- {
- QJsonDocument doc = Json::requireDocument(m_response);
- QJsonObject obj = Json::requireObject(doc);
- QString version = Json::requireString(obj, "recommended", "__placeholder__");
- m_sourceUrl = m_sourceUrl.toString() + '/' + version;
- }
- catch (const JSONValidationError &e)
- {
- emitFailed(e.cause());
- m_filesNetJob.reset();
- return;
- }
-
- setStatus(tr("Resolving modpack files:\n%1").arg(m_sourceUrl.toString()));
- m_filesNetJob.reset(new NetJob(tr("Resolving modpack files")));
- m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
- auto job = m_filesNetJob.get();
- connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
- connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
- m_filesNetJob->start();
-}
-
-void Technic::SolderPackInstallTask::fileListSucceeded()
-{
- setStatus(tr("Downloading modpack:"));
- QStringList modUrls;
- try
- {
- QJsonDocument doc = Json::requireDocument(m_response);
- QJsonObject obj = Json::requireObject(doc);
- QString minecraftVersion = Json::ensureString(obj, "minecraft", QString(), "__placeholder__");
- if (!minecraftVersion.isEmpty())
- m_minecraftVersion = minecraftVersion;
- QJsonArray mods = Json::requireArray(obj, "mods", "'mods'");
- for (auto mod: mods)
- {
- QJsonObject modObject = Json::requireObject(mod);
- modUrls.append(Json::requireString(modObject, "url", "'url'"));
- }
- }
- catch (const JSONValidationError &e)
- {
- emitFailed(e.cause());
- m_filesNetJob.reset();
- return;
- }
- m_filesNetJob.reset(new NetJob(tr("Downloading modpack")));
- int i = 0;
- for (auto &modUrl: modUrls)
- {
- auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
- m_filesNetJob->addNetAction(Net::Download::makeFile(modUrl, path));
- i++;
- }
-
- m_modCount = modUrls.size();
-
- connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
- connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
- connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
- m_filesNetJob->start();
-}
-
-void Technic::SolderPackInstallTask::downloadSucceeded()
-{
- m_abortable = false;
-
- setStatus(tr("Extracting modpack"));
- m_filesNetJob.reset();
- m_extractFuture = QtConcurrent::run([this]()
- {
- int i = 0;
- QString extractDir = FS::PathCombine(m_stagingPath, ".minecraft");
- FS::ensureFolderPathExists(extractDir);
-
- while (m_modCount > i)
- {
- auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
- if (!MMCZip::extractDir(path, extractDir))
- {
- return false;
- }
- i++;
- }
- return true;
- });
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &Technic::SolderPackInstallTask::extractFinished);
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &Technic::SolderPackInstallTask::extractAborted);
- m_extractFutureWatcher.setFuture(m_extractFuture);
-}
-
-void Technic::SolderPackInstallTask::downloadFailed(QString reason)
-{
- m_abortable = false;
- emitFailed(reason);
- m_filesNetJob.reset();
-}
-
-void Technic::SolderPackInstallTask::downloadProgressChanged(qint64 current, qint64 total)
-{
- m_abortable = true;
- setProgress(current / 2, total);
-}
-
-void Technic::SolderPackInstallTask::extractFinished()
-{
- if (!m_extractFuture.result())
- {
- 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;
- }
- }
- }
-
- shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
- connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SolderPackInstallTask::emitSucceeded);
- connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SolderPackInstallTask::emitFailed);
- packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath, m_minecraftVersion, true);
-}
-
-void Technic::SolderPackInstallTask::extractAborted()
-{
- emitFailed(tr("Instance import has been aborted."));
- return;
-}
-
diff --git a/api/logic/modplatform/technic/SolderPackInstallTask.h b/api/logic/modplatform/technic/SolderPackInstallTask.h
deleted file mode 100644
index 9f0f20a9..00000000
--- a/api/logic/modplatform/technic/SolderPackInstallTask.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <InstanceTask.h>
-#include <net/NetJob.h>
-#include <tasks/Task.h>
-
-#include <QUrl>
-
-namespace Technic
-{
- class MULTIMC_LOGIC_EXPORT SolderPackInstallTask : public InstanceTask
- {
- Q_OBJECT
- public:
- explicit SolderPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion);
-
- bool canAbort() const override { return true; }
- bool abort() override;
-
- protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
-
- private slots:
- void versionSucceeded();
- void fileListSucceeded();
- void downloadSucceeded();
- void downloadFailed(QString reason);
- void downloadProgressChanged(qint64 current, qint64 total);
- void extractFinished();
- void extractAborted();
-
- private:
- bool m_abortable = false;
-
- NetJobPtr m_filesNetJob;
- QUrl m_sourceUrl;
- QString m_minecraftVersion;
- QByteArray m_response;
- QTemporaryDir m_outputDir;
- int m_modCount;
- QFuture<bool> m_extractFuture;
- QFutureWatcher<bool> m_extractFutureWatcher;
- };
-}
diff --git a/api/logic/modplatform/technic/TechnicPackProcessor.cpp b/api/logic/modplatform/technic/TechnicPackProcessor.cpp
deleted file mode 100644
index 52979b7c..00000000
--- a/api/logic/modplatform/technic/TechnicPackProcessor.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/* Copyright 2020-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TechnicPackProcessor.h"
-
-#include <FileSystem.h>
-#include <Json.h>
-#include <minecraft/MinecraftInstance.h>
-#include <minecraft/PackProfile.h>
-#include <quazip.h>
-#include <quazipdir.h>
-#include <quazipfile.h>
-#include <settings/INISettingsObject.h>
-
-#include <memory>
-
-void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const QString &instName, const QString &instIcon, const QString &stagingPath, const QString &minecraftVersion, const bool isSolder)
-{
- QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft");
- QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
- MinecraftInstance instance(globalSettings, instanceSettings, stagingPath);
-
- instance.setName(instName);
-
- if (instIcon != "default")
- {
- instance.setIconKey(instIcon);
- }
-
- auto components = instance.getPackProfile();
- components->buildingFromScratch();
-
- QByteArray data;
-
- QString modpackJar = FS::PathCombine(minecraftPath, "bin", "modpack.jar");
- QString versionJson = FS::PathCombine(minecraftPath, "bin", "version.json");
- QString fmlMinecraftVersion;
- if (QFile::exists(modpackJar))
- {
- QuaZip zipFile(modpackJar);
- if (!zipFile.open(QuaZip::mdUnzip))
- {
- emit failed(tr("Unable to open \"bin/modpack.jar\" file!"));
- return;
- }
- QuaZipDir zipFileRoot(&zipFile, "/");
- if (zipFileRoot.exists("/version.json"))
- {
- if (zipFileRoot.exists("/fmlversion.properties"))
- {
- zipFile.setCurrentFile("fmlversion.properties");
- QuaZipFile file(&zipFile);
- if (!file.open(QIODevice::ReadOnly))
- {
- emit failed(tr("Unable to open \"fmlversion.properties\"!"));
- return;
- }
- QByteArray fmlVersionData = file.readAll();
- file.close();
- INIFile iniFile;
- iniFile.loadFile(fmlVersionData);
- // If not present, this evaluates to a null string
- fmlMinecraftVersion = iniFile["fmlbuild.mcversion"].toString();
- }
- zipFile.setCurrentFile("version.json", QuaZip::csSensitive);
- QuaZipFile file(&zipFile);
- if (!file.open(QIODevice::ReadOnly))
- {
- emit failed(tr("Unable to open \"version.json\"!"));
- return;
- }
- data = file.readAll();
- file.close();
- }
- else
- {
- if (minecraftVersion.isEmpty())
- emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but minecraft version is unknown"));
- components->setComponentVersion("net.minecraft", minecraftVersion, true);
- components->installJarMods({modpackJar});
-
- // Forge for 1.4.7 and for 1.5.2 require extra libraries.
- // Figure out the forge version and add it as a component
- // (the code still comes from the jar mod installed above)
- if (zipFileRoot.exists("/forgeversion.properties"))
- {
- zipFile.setCurrentFile("forgeversion.properties", QuaZip::csSensitive);
- QuaZipFile file(&zipFile);
- if (!file.open(QIODevice::ReadOnly))
- {
- // Really shouldn't happen, but error handling shall not be forgotten
- emit failed(tr("Unable to open \"forgeversion.properties\""));
- return;
- }
- QByteArray forgeVersionData = file.readAll();
- file.close();
- INIFile iniFile;
- iniFile.loadFile(forgeVersionData);
- QString major, minor, revision, build;
- major = iniFile["forge.major.number"].toString();
- minor = iniFile["forge.minor.number"].toString();
- revision = iniFile["forge.revision.number"].toString();
- build = iniFile["forge.build.number"].toString();
-
- if (major.isEmpty() || minor.isEmpty() || revision.isEmpty() || build.isEmpty())
- {
- emit failed(tr("Invalid \"forgeversion.properties\"!"));
- return;
- }
-
- components->setComponentVersion("net.minecraftforge", major + '.' + minor + '.' + revision + '.' + build);
- }
-
- components->saveNow();
- emit succeeded();
- return;
- }
- }
- else if (QFile::exists(versionJson))
- {
- QFile file(versionJson);
- if (!file.open(QIODevice::ReadOnly))
- {
- emit failed(tr("Unable to open \"version.json\"!"));
- return;
- }
- data = file.readAll();
- file.close();
- }
- else
- {
- // This is the "Vanilla" modpack, excluded by the search code
- emit failed(tr("Unable to find a \"version.json\"!"));
- return;
- }
-
- try
- {
- QJsonDocument doc = Json::requireDocument(data);
- QJsonObject root = Json::requireObject(doc, "version.json");
- QString minecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), "");
- if (minecraftVersion.isEmpty())
- {
- if (fmlMinecraftVersion.isEmpty())
- {
- emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing"));
- return;
- }
- minecraftVersion = fmlMinecraftVersion;
- }
- components->setComponentVersion("net.minecraft", minecraftVersion, true);
- for (auto library: Json::ensureArray(root, "libraries", {}))
- {
- if (!library.isObject())
- {
- continue;
- }
-
- auto libraryObject = Json::ensureObject(library, {}, "");
- auto libraryName = Json::ensureString(libraryObject, "name", "", "");
-
- if (libraryName.startsWith("net.minecraftforge:forge:") && libraryName.contains('-'))
- {
- QString libraryVersion = libraryName.section(':', 2);
- if (!libraryVersion.startsWith("1.7.10-"))
- {
- components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1));
- }
- else
- {
- // 1.7.10 versions sometimes look like 1.7.10-10.13.4.1614-1.7.10, this filters out the 10.13.4.1614 part
- components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1, 1));
- }
- }
- else if (libraryName.startsWith("net.minecraftforge:minecraftforge:"))
- {
- components->setComponentVersion("net.minecraftforge", libraryName.section(':', 2));
- }
- else if (libraryName.startsWith("net.fabricmc:fabric-loader:"))
- {
- components->setComponentVersion("net.fabricmc.fabric-loader", libraryName.section(':', 2));
- }
- }
- }
- catch (const JSONValidationError &e)
- {
- emit failed(tr("Could not understand \"version.json\":\n") + e.cause());
- return;
- }
-
- components->saveNow();
- emit succeeded();
-}
diff --git a/api/logic/modplatform/technic/TechnicPackProcessor.h b/api/logic/modplatform/technic/TechnicPackProcessor.h
deleted file mode 100644
index 2ad803b3..00000000
--- a/api/logic/modplatform/technic/TechnicPackProcessor.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright 2020-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QString>
-#include "settings/SettingsObject.h"
-
-namespace Technic
-{
- // not exporting it, only used in SingleZipPackInstallTask, InstanceImportTask and SolderPackInstallTask
- class TechnicPackProcessor : public QObject
- {
- Q_OBJECT
-
- signals:
- void succeeded();
- void failed(QString reason);
-
- public:
- void run(SettingsObjectPtr globalSettings, const QString &instName, const QString &instIcon, const QString &stagingPath, const QString &minecraftVersion=QString(), const bool isSolder = false);
- };
-}
diff --git a/api/logic/mojang/PackageManifest.cpp b/api/logic/mojang/PackageManifest.cpp
deleted file mode 100644
index b3dfd7fc..00000000
--- a/api/logic/mojang/PackageManifest.cpp
+++ /dev/null
@@ -1,427 +0,0 @@
-#include "PackageManifest.h"
-#include <Json.h>
-#include <QDir>
-#include <QDirIterator>
-#include <QCryptographicHash>
-#include <QDebug>
-
-#ifndef Q_OS_WIN32
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#endif
-
-namespace mojang_files {
-
-const Hash hash_of_empty_string = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
-
-int Path::compare(const Path& rhs) const
-{
- auto left_cursor = begin();
- auto left_end = end();
- auto right_cursor = rhs.begin();
- auto right_end = rhs.end();
-
- while (left_cursor != left_end && right_cursor != right_end)
- {
- if(*left_cursor < *right_cursor)
- {
- return -1;
- }
- else if(*left_cursor > *right_cursor)
- {
- return 1;
- }
- left_cursor++;
- right_cursor++;
- }
-
- if(left_cursor == left_end)
- {
- if(right_cursor == right_end)
- {
- return 0;
- }
- return -1;
- }
- return 1;
-}
-
-void Package::addFile(const Path& path, const File& file) {
- addFolder(path.parent_path());
- files[path] = file;
-}
-
-void Package::addFolder(Path folder) {
- if(!folder.has_parent_path()) {
- return;
- }
- do {
- folders.insert(folder);
- folder = folder.parent_path();
- } while(folder.has_parent_path());
-}
-
-void Package::addLink(const Path& path, const Path& target) {
- addFolder(path.parent_path());
- symlinks[path] = target;
-}
-
-void Package::addSource(const FileSource& source) {
- sources[source.hash] = source;
-}
-
-
-namespace {
-void fromJson(QJsonDocument & doc, Package & out) {
- std::set<Path> seen_paths;
- if (!doc.isObject())
- {
- throw JSONValidationError("file manifest is not an object");
- }
- QJsonObject root = doc.object();
-
- auto filesObj = Json::ensureObject(root, "files");
- auto iter = filesObj.begin();
- while (iter != filesObj.end())
- {
- Path objectPath = Path(iter.key());
- auto value = iter.value();
- iter++;
- if(seen_paths.count(objectPath)) {
- throw JSONValidationError("duplicate path inside manifest, the manifest is invalid");
- }
- if (!value.isObject())
- {
- throw JSONValidationError("file entry inside manifest is not an an object");
- }
- seen_paths.insert(objectPath);
-
- auto fileObject = value.toObject();
- auto type = Json::requireString(fileObject, "type");
- if(type == "directory") {
- out.addFolder(objectPath);
- continue;
- }
- else if(type == "file") {
- FileSource bestSource;
- File file;
- file.executable = Json::ensureBoolean(fileObject, QString("executable"), false);
- auto downloads = Json::requireObject(fileObject, "downloads");
- for(auto iter2 = downloads.begin(); iter2 != downloads.end(); iter2++) {
- FileSource source;
-
- auto downloadObject = Json::requireObject(iter2.value());
- source.hash = Json::requireString(downloadObject, "sha1");
- source.size = Json::requireInteger(downloadObject, "size");
- source.url = Json::requireString(downloadObject, "url");
-
- auto compression = iter2.key();
- if(compression == "raw") {
- file.hash = source.hash;
- file.size = source.size;
- source.compression = Compression::Raw;
- }
- else if (compression == "lzma") {
- source.compression = Compression::Lzma;
- }
- else {
- continue;
- }
- bestSource.upgrade(source);
- }
- if(bestSource.isBad()) {
- throw JSONValidationError("No valid compression method for file " + iter.key());
- }
- out.addFile(objectPath, file);
- out.addSource(bestSource);
- }
- else if(type == "link") {
- auto target = Json::requireString(fileObject, "target");
- out.symlinks[objectPath] = target;
- out.addLink(objectPath, target);
- }
- else {
- throw JSONValidationError("Invalid item type in manifest: " + type);
- }
- }
- // make sure the containing folder exists
- out.folders.insert(Path());
-}
-}
-
-Package Package::fromManifestContents(const QByteArray& contents)
-{
- Package out;
- try
- {
- auto doc = Json::requireDocument(contents, "Manifest");
- fromJson(doc, out);
- return out;
- }
- catch (const Exception &e)
- {
- qDebug() << QString("Unable to parse manifest: %1").arg(e.cause());
- out.valid = false;
- return out;
- }
-}
-
-Package Package::fromManifestFile(const QString & filename) {
- Package out;
- try
- {
- auto doc = Json::requireDocument(filename, filename);
- fromJson(doc, out);
- return out;
- }
- catch (const Exception &e)
- {
- qDebug() << QString("Unable to parse manifest file %1: %2").arg(filename, e.cause());
- out.valid = false;
- return out;
- }
-}
-
-#ifndef Q_OS_WIN32
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-namespace {
-// FIXME: Qt obscures symlink targets by making them absolute. that is useless. this is the workaround - we do it ourselves
-bool actually_read_symlink_target(const QString & filepath, Path & out)
-{
- struct ::stat st;
- // FIXME: here, we assume the native filesystem encoding. May the Gods have mercy upon our Souls.
- QByteArray nativePath = filepath.toUtf8();
- const char * filepath_cstr = nativePath.data();
-
- if (lstat(filepath_cstr, &st) != 0)
- {
- return false;
- }
-
- auto size = st.st_size ? st.st_size + 1 : PATH_MAX;
- std::string temp(size, '\0');
- // because we don't realiably know how long the damn thing actually is, we loop and expand. POSIX is naff
- do
- {
- auto link_length = ::readlink(filepath_cstr, &temp[0], temp.size());
- if(link_length == -1)
- {
- return false;
- }
- if(std::string::size_type(link_length) < temp.size())
- {
- // buffer was long enough and we managed to read the link target. RETURN here.
- temp.resize(link_length);
- out = Path(QString::fromUtf8(temp.c_str()));
- return true;
- }
- temp.resize(temp.size() * 2);
- } while (true);
-}
-}
-#endif
-
-// FIXME: Qt filesystem abstraction is bad, but ... let's hope it doesn't break too much?
-// FIXME: The error handling is just DEFICIENT
-Package Package::fromInspectedFolder(const QString& folderPath)
-{
- QDir root(folderPath);
-
- Package out;
- QDirIterator iterator(folderPath, QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden, QDirIterator::Subdirectories);
- while(iterator.hasNext()) {
- iterator.next();
-
- auto fileInfo = iterator.fileInfo();
- auto relPath = root.relativeFilePath(fileInfo.filePath());
- // FIXME: this is probably completely busted on Windows anyway, so just disable it.
- // Qt makes shit up and doesn't understand the platform details
- // TODO: Actually use a filesystem library that isn't terrible and has decen license.
- // I only know one, and I wrote it. Sadly, currently proprietary. PAIN.
-#ifndef Q_OS_WIN32
- if(fileInfo.isSymLink()) {
- Path targetPath;
- if(!actually_read_symlink_target(fileInfo.filePath(), targetPath)) {
- qCritical() << "Folder inspection: Unknown filesystem object:" << fileInfo.absoluteFilePath();
- out.valid = false;
- }
- out.addLink(relPath, targetPath);
- }
- else
-#endif
- if(fileInfo.isDir()) {
- out.addFolder(relPath);
- }
- else if(fileInfo.isFile()) {
- File f;
- f.executable = fileInfo.isExecutable();
- f.size = fileInfo.size();
- // FIXME: async / optimize the hashing
- QFile input(fileInfo.absoluteFilePath());
- if(!input.open(QIODevice::ReadOnly)) {
- qCritical() << "Folder inspection: Failed to open file:" << fileInfo.absoluteFilePath();
- out.valid = false;
- break;
- }
- f.hash = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1).toHex().constData();
- out.addFile(relPath, f);
- }
- else {
- // Something else... oh my
- qCritical() << "Folder inspection: Unknown filesystem object:" << fileInfo.absoluteFilePath();
- out.valid = false;
- break;
- }
- }
- out.folders.insert(Path("."));
- out.valid = true;
- return out;
-}
-
-namespace {
-struct shallow_first_sort
-{
- bool operator()(const Path &lhs, const Path &rhs) const
- {
- auto lhs_depth = lhs.length();
- auto rhs_depth = rhs.length();
- if(lhs_depth < rhs_depth)
- {
- return true;
- }
- else if(lhs_depth == rhs_depth)
- {
- if(lhs < rhs)
- {
- return true;
- }
- }
- return false;
- }
-};
-
-struct deep_first_sort
-{
- bool operator()(const Path &lhs, const Path &rhs) const
- {
- auto lhs_depth = lhs.length();
- auto rhs_depth = rhs.length();
- if(lhs_depth > rhs_depth)
- {
- return true;
- }
- else if(lhs_depth == rhs_depth)
- {
- if(lhs < rhs)
- {
- return true;
- }
- }
- return false;
- }
-};
-}
-
-UpdateOperations UpdateOperations::resolve(const Package& from, const Package& to)
-{
- UpdateOperations out;
-
- if(!from.valid || !to.valid) {
- out.valid = false;
- return out;
- }
-
- // Files
- for(auto iter = from.files.begin(); iter != from.files.end(); iter++) {
- const auto &current_hash = iter->second.hash;
- const auto &current_executable = iter->second.executable;
- const auto &path = iter->first;
-
- auto iter2 = to.files.find(path);
- if(iter2 == to.files.end()) {
- // removed
- out.deletes.push_back(path);
- continue;
- }
- auto new_hash = iter2->second.hash;
- auto new_executable = iter2->second.executable;
- if (current_hash != new_hash) {
- out.deletes.push_back(path);
- out.downloads.emplace(
- std::pair<Path, FileDownload>{
- path,
- FileDownload(to.sources.at(iter2->second.hash), iter2->second.executable)
- }
- );
- }
- else if (current_executable != new_executable) {
- out.executable_fixes[path] = new_executable;
- }
- }
- for(auto iter = to.files.begin(); iter != to.files.end(); iter++) {
- auto path = iter->first;
- if(!from.files.count(path)) {
- out.downloads.emplace(
- std::pair<Path, FileDownload>{
- path,
- FileDownload(to.sources.at(iter->second.hash), iter->second.executable)
- }
- );
- }
- }
-
- // Folders
- std::set<Path, deep_first_sort> remove_folders;
- std::set<Path, shallow_first_sort> make_folders;
- for(auto from_path: from.folders) {
- auto iter = to.folders.find(from_path);
- if(iter == to.folders.end()) {
- remove_folders.insert(from_path);
- }
- }
- for(auto & rmdir: remove_folders) {
- out.rmdirs.push_back(rmdir);
- }
- for(auto to_path: to.folders) {
- auto iter = from.folders.find(to_path);
- if(iter == from.folders.end()) {
- make_folders.insert(to_path);
- }
- }
- for(auto & mkdir: make_folders) {
- out.mkdirs.push_back(mkdir);
- }
-
- // Symlinks
- for(auto iter = from.symlinks.begin(); iter != from.symlinks.end(); iter++) {
- const auto &current_target = iter->second;
- const auto &path = iter->first;
-
- auto iter2 = to.symlinks.find(path);
- if(iter2 == to.symlinks.end()) {
- // removed
- out.deletes.push_back(path);
- continue;
- }
- const auto &new_target = iter2->second;
- if (current_target != new_target) {
- out.deletes.push_back(path);
- out.mklinks[path] = iter2->second;
- }
- }
- for(auto iter = to.symlinks.begin(); iter != to.symlinks.end(); iter++) {
- auto path = iter->first;
- if(!from.symlinks.count(path)) {
- out.mklinks[path] = iter->second;
- }
- }
- out.valid = true;
- return out;
-}
-
-}
diff --git a/api/logic/mojang/PackageManifest.h b/api/logic/mojang/PackageManifest.h
deleted file mode 100644
index d01a0554..00000000
--- a/api/logic/mojang/PackageManifest.h
+++ /dev/null
@@ -1,173 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <map>
-#include <set>
-#include <QStringList>
-#include "tasks/Task.h"
-
-#include "multimc_logic_export.h"
-
-namespace mojang_files {
-
-using Hash = QString;
-extern const Hash empty_hash;
-
-// simple-ish path implementation. assumes always relative and does not allow '..' entries
-class MULTIMC_LOGIC_EXPORT Path
-{
-public:
- using parts_type = QStringList;
-
- Path() = default;
- Path(QString string) {
- auto parts_in = string.split('/');
- for(auto & part: parts_in) {
- if(part.isEmpty() || part == ".") {
- continue;
- }
- if(part == "..") {
- if(parts.size()) {
- parts.pop_back();
- }
- continue;
- }
- parts.push_back(part);
- }
- }
-
- bool has_parent_path() const
- {
- return parts.size() > 0;
- }
-
- Path parent_path() const
- {
- if (parts.empty())
- return Path();
- return Path(parts.begin(), std::prev(parts.end()));
- }
-
- bool empty() const
- {
- return parts.empty();
- }
-
- int length() const
- {
- return parts.length();
- }
-
- bool operator==(const Path & rhs) const {
- return parts == rhs.parts;
- }
-
- bool operator!=(const Path & rhs) const {
- return parts != rhs.parts;
- }
-
- inline bool operator<(const Path& rhs) const
- {
- return compare(rhs) < 0;
- }
-
- parts_type::const_iterator begin() const
- {
- return parts.begin();
- }
-
- parts_type::const_iterator end() const
- {
- return parts.end();
- }
-
- QString toString() const {
- return parts.join("/");
- }
-
-private:
- Path(const parts_type::const_iterator & start, const parts_type::const_iterator & end) {
- auto cursor = start;
- while(cursor != end) {
- parts.push_back(*cursor);
- cursor++;
- }
- }
- int compare(const Path& p) const;
-
- parts_type parts;
-};
-
-
-enum class Compression {
- Raw,
- Lzma,
- Unknown
-};
-
-
-struct MULTIMC_LOGIC_EXPORT FileSource
-{
- Compression compression = Compression::Unknown;
- Hash hash;
- QString url;
- std::size_t size = 0;
- void upgrade(const FileSource & other) {
- if(compression == Compression::Unknown || other.size < size) {
- *this = other;
- }
- }
- bool isBad() const {
- return compression == Compression::Unknown;
- }
-};
-
-struct MULTIMC_LOGIC_EXPORT File
-{
- Hash hash;
- bool executable;
- std::uint64_t size = 0;
-};
-
-struct MULTIMC_LOGIC_EXPORT Package {
- static Package fromInspectedFolder(const QString &folderPath);
- static Package fromManifestFile(const QString &path);
- static Package fromManifestContents(const QByteArray& contents);
-
- explicit operator bool() const
- {
- return valid;
- }
- void addFolder(Path folder);
- void addFile(const Path & path, const File & file);
- void addLink(const Path & path, const Path & target);
- void addSource(const FileSource & source);
-
- std::map<Hash, FileSource> sources;
- bool valid = true;
- std::set<Path> folders;
- std::map<Path, File> files;
- std::map<Path, Path> symlinks;
-};
-
-struct MULTIMC_LOGIC_EXPORT FileDownload : FileSource
-{
- FileDownload(const FileSource& source, bool executable) {
- static_cast<FileSource &> (*this) = source;
- this->executable = executable;
- }
- bool executable = false;
-};
-
-struct MULTIMC_LOGIC_EXPORT UpdateOperations {
- static UpdateOperations resolve(const Package & from, const Package & to);
- bool valid = false;
- std::vector<Path> deletes;
- std::vector<Path> rmdirs;
- std::vector<Path> mkdirs;
- std::map<Path, FileDownload> downloads;
- std::map<Path, Path> mklinks;
- std::map<Path, bool> executable_fixes;
-};
-
-}
diff --git a/api/logic/mojang/PackageManifest_test.cpp b/api/logic/mojang/PackageManifest_test.cpp
deleted file mode 100644
index d4c55c5a..00000000
--- a/api/logic/mojang/PackageManifest_test.cpp
+++ /dev/null
@@ -1,344 +0,0 @@
-#include <QTest>
-#include <QDebug>
-#include "TestUtil.h"
-
-#include "mojang/PackageManifest.h"
-
-using namespace mojang_files;
-
-QDebug operator<<(QDebug debug, const Path &path)
-{
- debug << path.toString();
- return debug;
-}
-
-class PackageManifestTest : public QObject
-{
- Q_OBJECT
-
-private slots:
- void test_parse();
- void test_parse_file();
- void test_inspect();
-#ifndef Q_OS_WIN32
- void test_inspect_symlinks();
-#endif
- void mkdir_deep();
- void rmdir_deep();
-
- void identical_file();
- void changed_file();
- void added_file();
- void removed_file();
-};
-
-namespace {
-QByteArray basic_manifest = R"END(
-{
- "files": {
- "a/b.txt": {
- "type": "file",
- "downloads": {
- "raw": {
- "url": "http://dethware.org/b.txt",
- "sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
- "size": 0
- }
- },
- "executable": true
- },
- "a/b/c": {
- "type": "directory"
- },
- "a/b/c.txt": {
- "type": "link",
- "target": "../b.txt"
- }
- }
-}
-)END";
-}
-
-void PackageManifestTest::test_parse()
-{
- auto manifest = Package::fromManifestContents(basic_manifest);
- QVERIFY(manifest.valid == true);
- QVERIFY(manifest.files.size() == 1);
- QVERIFY(manifest.files.count(Path("a/b.txt")));
- auto &file = manifest.files[Path("a/b.txt")];
- QVERIFY(file.executable == true);
- QVERIFY(file.hash == "da39a3ee5e6b4b0d3255bfef95601890afd80709");
- QVERIFY(file.size == 0);
- QVERIFY(manifest.folders.size() == 4);
- QVERIFY(manifest.folders.count(Path(".")));
- QVERIFY(manifest.folders.count(Path("a")));
- QVERIFY(manifest.folders.count(Path("a/b")));
- QVERIFY(manifest.folders.count(Path("a/b/c")));
- QVERIFY(manifest.symlinks.size() == 1);
- auto symlinkPath = Path("a/b/c.txt");
- QVERIFY(manifest.symlinks.count(symlinkPath));
- auto &symlink = manifest.symlinks[symlinkPath];
- QVERIFY(symlink == Path("../b.txt"));
- QVERIFY(manifest.sources.size() == 1);
-}
-
-void PackageManifestTest::test_parse_file() {
- auto path = QFINDTESTDATA("testdata/1.8.0_202-x64.json");
- auto manifest = Package::fromManifestFile(path);
- QVERIFY(manifest.valid == true);
-}
-
-
-void PackageManifestTest::test_inspect() {
- auto path = QFINDTESTDATA("testdata/inspect_win/");
- auto manifest = Package::fromInspectedFolder(path);
- QVERIFY(manifest.valid == true);
- QVERIFY(manifest.files.size() == 2);
- QVERIFY(manifest.files.count(Path("a/b.txt")));
- auto &file1 = manifest.files[Path("a/b.txt")];
- QVERIFY(file1.executable == false);
- QVERIFY(file1.hash == "da39a3ee5e6b4b0d3255bfef95601890afd80709");
- QVERIFY(file1.size == 0);
- QVERIFY(manifest.files.count(Path("a/b/b.txt")));
- auto &file2 = manifest.files[Path("a/b/b.txt")];
- QVERIFY(file2.executable == false);
- QVERIFY(file2.hash == "da39a3ee5e6b4b0d3255bfef95601890afd80709");
- QVERIFY(file2.size == 0);
- QVERIFY(manifest.folders.size() == 3);
- QVERIFY(manifest.folders.count(Path(".")));
- QVERIFY(manifest.folders.count(Path("a")));
- QVERIFY(manifest.folders.count(Path("a/b")));
- QVERIFY(manifest.symlinks.size() == 0);
-}
-
-#ifndef Q_OS_WIN32
-void PackageManifestTest::test_inspect_symlinks() {
- auto path = QFINDTESTDATA("testdata/inspect/");
- auto manifest = Package::fromInspectedFolder(path);
- QVERIFY(manifest.valid == true);
- QVERIFY(manifest.files.size() == 1);
- QVERIFY(manifest.files.count(Path("a/b.txt")));
- auto &file = manifest.files[Path("a/b.txt")];
- QVERIFY(file.executable == true);
- QVERIFY(file.hash == "da39a3ee5e6b4b0d3255bfef95601890afd80709");
- QVERIFY(file.size == 0);
- QVERIFY(manifest.folders.size() == 3);
- QVERIFY(manifest.folders.count(Path(".")));
- QVERIFY(manifest.folders.count(Path("a")));
- QVERIFY(manifest.folders.count(Path("a/b")));
- QVERIFY(manifest.symlinks.size() == 1);
- QVERIFY(manifest.symlinks.count(Path("a/b/b.txt")));
- qDebug() << manifest.symlinks[Path("a/b/b.txt")];
- QVERIFY(manifest.symlinks[Path("a/b/b.txt")] == Path("../b.txt"));
-}
-#endif
-
-void PackageManifestTest::mkdir_deep() {
-
- Package from;
- auto to = Package::fromManifestContents(R"END(
-{
- "files": {
- "a/b/c/d/e": {
- "type": "directory"
- }
- }
-}
-)END");
- auto operations = UpdateOperations::resolve(from, to);
- QVERIFY(operations.deletes.size() == 0);
- QVERIFY(operations.rmdirs.size() == 0);
-
- QVERIFY(operations.mkdirs.size() == 6);
- QVERIFY(operations.mkdirs[0] == Path("."));
- QVERIFY(operations.mkdirs[1] == Path("a"));
- QVERIFY(operations.mkdirs[2] == Path("a/b"));
- QVERIFY(operations.mkdirs[3] == Path("a/b/c"));
- QVERIFY(operations.mkdirs[4] == Path("a/b/c/d"));
- QVERIFY(operations.mkdirs[5] == Path("a/b/c/d/e"));
-
- QVERIFY(operations.downloads.size() == 0);
- QVERIFY(operations.mklinks.size() == 0);
- QVERIFY(operations.executable_fixes.size() == 0);
-}
-
-void PackageManifestTest::rmdir_deep() {
-
- Package to;
- auto from = Package::fromManifestContents(R"END(
-{
- "files": {
- "a/b/c/d/e": {
- "type": "directory"
- }
- }
-}
-)END");
- auto operations = UpdateOperations::resolve(from, to);
- QVERIFY(operations.deletes.size() == 0);
-
- QVERIFY(operations.rmdirs.size() == 6);
- QVERIFY(operations.rmdirs[0] == Path("a/b/c/d/e"));
- QVERIFY(operations.rmdirs[1] == Path("a/b/c/d"));
- QVERIFY(operations.rmdirs[2] == Path("a/b/c"));
- QVERIFY(operations.rmdirs[3] == Path("a/b"));
- QVERIFY(operations.rmdirs[4] == Path("a"));
- QVERIFY(operations.rmdirs[5] == Path("."));
-
- QVERIFY(operations.mkdirs.size() == 0);
- QVERIFY(operations.downloads.size() == 0);
- QVERIFY(operations.mklinks.size() == 0);
- QVERIFY(operations.executable_fixes.size() == 0);
-}
-
-void PackageManifestTest::identical_file() {
- QByteArray manifest = R"END(
-{
- "files": {
- "a/b/c/d/empty.txt": {
- "type": "file",
- "downloads": {
- "raw": {
- "url": "http://dethware.org/empty.txt",
- "sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
- "size": 0
- }
- },
- "executable": false
- }
- }
-}
-)END";
- auto from = Package::fromManifestContents(manifest);
- auto to = Package::fromManifestContents(manifest);
- auto operations = UpdateOperations::resolve(from, to);
- QVERIFY(operations.deletes.size() == 0);
- QVERIFY(operations.rmdirs.size() == 0);
- QVERIFY(operations.mkdirs.size() == 0);
- QVERIFY(operations.downloads.size() == 0);
- QVERIFY(operations.mklinks.size() == 0);
- QVERIFY(operations.executable_fixes.size() == 0);
-}
-
-void PackageManifestTest::changed_file() {
- auto from = Package::fromManifestContents(R"END(
-{
- "files": {
- "a/b/c/d/file": {
- "type": "file",
- "downloads": {
- "raw": {
- "url": "http://dethware.org/empty.txt",
- "sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
- "size": 0
- }
- },
- "executable": false
- }
- }
-}
-)END");
- auto to = Package::fromManifestContents(R"END(
-{
- "files": {
- "a/b/c/d/file": {
- "type": "file",
- "downloads": {
- "raw": {
- "url": "http://dethware.org/space.txt",
- "sha1": "dd122581c8cd44d0227f9c305581ffcb4b6f1b46",
- "size": 1
- }
- },
- "executable": false
- }
- }
-}
-)END");
- auto operations = UpdateOperations::resolve(from, to);
- QVERIFY(operations.deletes.size() == 1);
- QCOMPARE(operations.deletes[0], Path("a/b/c/d/file"));
- QVERIFY(operations.rmdirs.size() == 0);
- QVERIFY(operations.mkdirs.size() == 0);
- QVERIFY(operations.downloads.size() == 1);
- QVERIFY(operations.mklinks.size() == 0);
- QVERIFY(operations.executable_fixes.size() == 0);
-}
-
-void PackageManifestTest::added_file() {
- auto from = Package::fromManifestContents(R"END(
-{
- "files": {
- "a/b/c/d": {
- "type": "directory"
- }
- }
-}
-)END");
- auto to = Package::fromManifestContents(R"END(
-{
- "files": {
- "a/b/c/d/file": {
- "type": "file",
- "downloads": {
- "raw": {
- "url": "http://dethware.org/space.txt",
- "sha1": "dd122581c8cd44d0227f9c305581ffcb4b6f1b46",
- "size": 1
- }
- },
- "executable": false
- }
- }
-}
-)END");
- auto operations = UpdateOperations::resolve(from, to);
- QVERIFY(operations.deletes.size() == 0);
- QVERIFY(operations.rmdirs.size() == 0);
- QVERIFY(operations.mkdirs.size() == 0);
- QVERIFY(operations.downloads.size() == 1);
- QVERIFY(operations.mklinks.size() == 0);
- QVERIFY(operations.executable_fixes.size() == 0);
-}
-
-void PackageManifestTest::removed_file() {
- auto from = Package::fromManifestContents(R"END(
-{
- "files": {
- "a/b/c/d/file": {
- "type": "file",
- "downloads": {
- "raw": {
- "url": "http://dethware.org/space.txt",
- "sha1": "dd122581c8cd44d0227f9c305581ffcb4b6f1b46",
- "size": 1
- }
- },
- "executable": false
- }
- }
-}
-)END");
- auto to = Package::fromManifestContents(R"END(
-{
- "files": {
- "a/b/c/d": {
- "type": "directory"
- }
- }
-}
-)END");
- auto operations = UpdateOperations::resolve(from, to);
- QVERIFY(operations.deletes.size() == 1);
- QCOMPARE(operations.deletes[0], Path("a/b/c/d/file"));
- QVERIFY(operations.rmdirs.size() == 0);
- QVERIFY(operations.mkdirs.size() == 0);
- QVERIFY(operations.downloads.size() == 0);
- QVERIFY(operations.mklinks.size() == 0);
- QVERIFY(operations.executable_fixes.size() == 0);
-}
-
-QTEST_GUILESS_MAIN(PackageManifestTest)
-
-#include "PackageManifest_test.moc"
-
diff --git a/api/logic/mojang/testdata/1.8.0_202-x64.json b/api/logic/mojang/testdata/1.8.0_202-x64.json
deleted file mode 100644
index 3d99d719..00000000
--- a/api/logic/mojang/testdata/1.8.0_202-x64.json
+++ /dev/null
@@ -1 +0,0 @@
-{"files": {"COPYRIGHT": {"downloads": {"lzma": {"sha1": "dd860e040807f7e53ae89da5f28dd73d57ac605d", "size": 1431, "url": "https://launcher.mojang.com/v1/objects/dd860e040807f7e53ae89da5f28dd73d57ac605d/COPYRIGHT"}, "raw": {"sha1": "c725183c757011e7ba96c83c1e86ee7e8b516a2b", "size": 3244, "url": "https://launcher.mojang.com/v1/objects/c725183c757011e7ba96c83c1e86ee7e8b516a2b/COPYRIGHT"}}, "executable": false, "type": "file"}, "LICENSE": {"downloads": {"raw": {"sha1": "3e86865deec0814c958bcf7fb87f790bccc0e8bd", "size": 40, "url": "https://launcher.mojang.com/v1/objects/3e86865deec0814c958bcf7fb87f790bccc0e8bd/LICENSE"}}, "executable": false, "type": "file"}, "README": {"downloads": {"raw": {"sha1": "f90331df1e5badeadc501d8dd70714c62a920204", "size": 46, "url": "https://launcher.mojang.com/v1/objects/f90331df1e5badeadc501d8dd70714c62a920204/README"}}, "executable": false, "type": "file"}, "THIRDPARTYLICENSEREADME-JAVAFX.txt": {"downloads": {"lzma": {"sha1": "4fee85109d7ff04b982d0576dabd15397f599125", "size": 15455, "url": "https://launcher.mojang.com/v1/objects/4fee85109d7ff04b982d0576dabd15397f599125/THIRDPARTYLICENSEREADME-JAVAFX.txt"}, "raw": {"sha1": "56ff42f87607b997b52ae0ef8bf315e36932e870", "size": 112724, "url": "https://launcher.mojang.com/v1/objects/56ff42f87607b997b52ae0ef8bf315e36932e870/THIRDPARTYLICENSEREADME-JAVAFX.txt"}}, "executable": false, "type": "file"}, "THIRDPARTYLICENSEREADME.txt": {"downloads": {"lzma": {"sha1": "419c1414ba46ae9dbfd38cf4e0601fff61644429", "size": 32266, "url": "https://launcher.mojang.com/v1/objects/419c1414ba46ae9dbfd38cf4e0601fff61644429/THIRDPARTYLICENSEREADME.txt"}, "raw": {"sha1": "b83c3f32261de3e48ccd20614a11e066b1ec9027", "size": 153824, "url": "https://launcher.mojang.com/v1/objects/b83c3f32261de3e48ccd20614a11e066b1ec9027/THIRDPARTYLICENSEREADME.txt"}}, "executable": false, "type": "file"}, "Welcome.html": {"downloads": {"lzma": {"sha1": "01c21a74b4aafb7cbe0388233c43cbdf77dcaaea", "size": 528, "url": "https://launcher.mojang.com/v1/objects/01c21a74b4aafb7cbe0388233c43cbdf77dcaaea/Welcome.html"}, "raw": {"sha1": "d98ae54f03dac87419abc19b97e315830c2da55f", "size": 955, "url": "https://launcher.mojang.com/v1/objects/d98ae54f03dac87419abc19b97e315830c2da55f/Welcome.html"}}, "executable": false, "type": "file"}, "bin": {"type": "directory"}, "bin/ControlPanel": {"target": "jcontrol", "type": "link"}, "bin/java": {"downloads": {"lzma": {"sha1": "3857eea1d59e1bc545c67a753ed2768254807b8a", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/3857eea1d59e1bc545c67a753ed2768254807b8a/java"}, "raw": {"sha1": "3d20560fb5d1a49cb689c2226972e92e06d27ba6", "size": 8464, "url": "https://launcher.mojang.com/v1/objects/3d20560fb5d1a49cb689c2226972e92e06d27ba6/java"}}, "executable": true, "type": "file"}, "bin/javaws": {"downloads": {"lzma": {"sha1": "a6bec5c049e76c4488294a256a2084ea23ddb440", "size": 38173, "url": "https://launcher.mojang.com/v1/objects/a6bec5c049e76c4488294a256a2084ea23ddb440/javaws"}, "raw": {"sha1": "955c0f0066e2f893b0c2b3ccd83e223722e4ab74", "size": 140296, "url": "https://launcher.mojang.com/v1/objects/955c0f0066e2f893b0c2b3ccd83e223722e4ab74/javaws"}}, "executable": true, "type": "file"}, "bin/jcontrol": {"downloads": {"lzma": {"sha1": "40c5e33748f252e1d950b579a4185ab2c23fc908", "size": 2166, "url": "https://launcher.mojang.com/v1/objects/40c5e33748f252e1d950b579a4185ab2c23fc908/jcontrol"}, "raw": {"sha1": "ed541733c8b51e34349c1f8010b277e58ad73f1e", "size": 6264, "url": "https://launcher.mojang.com/v1/objects/ed541733c8b51e34349c1f8010b277e58ad73f1e/jcontrol"}}, "executable": true, "type": "file"}, "bin/jjs": {"downloads": {"lzma": {"sha1": "d44d1ac421979f7671921986214812095a5b0e3b", "size": 2168, "url": "https://launcher.mojang.com/v1/objects/d44d1ac421979f7671921986214812095a5b0e3b/jjs"}, "raw": {"sha1": "f00f944c3dbe556793b5dc686aaeee3e5722e99b", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/f00f944c3dbe556793b5dc686aaeee3e5722e99b/jjs"}}, "executable": true, "type": "file"}, "bin/keytool": {"downloads": {"lzma": {"sha1": "93c607dce450976667c382f609a367167bdec05c", "size": 2175, "url": "https://launcher.mojang.com/v1/objects/93c607dce450976667c382f609a367167bdec05c/keytool"}, "raw": {"sha1": "7114b561546270e441e9ed1bcc24e5188c068a42", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/7114b561546270e441e9ed1bcc24e5188c068a42/keytool"}}, "executable": true, "type": "file"}, "bin/orbd": {"downloads": {"lzma": {"sha1": "b27dfded5e2b2f6f02c555971c94e46ca14ac81b", "size": 2254, "url": "https://launcher.mojang.com/v1/objects/b27dfded5e2b2f6f02c555971c94e46ca14ac81b/orbd"}, "raw": {"sha1": "7f31217fecb3dbbd89f1dd3783fca58793a66fd2", "size": 8656, "url": "https://launcher.mojang.com/v1/objects/7f31217fecb3dbbd89f1dd3783fca58793a66fd2/orbd"}}, "executable": true, "type": "file"}, "bin/pack200": {"downloads": {"lzma": {"sha1": "b52da4497b49b1508b6225a5740857ddb8f52e97", "size": 2183, "url": "https://launcher.mojang.com/v1/objects/b52da4497b49b1508b6225a5740857ddb8f52e97/pack200"}, "raw": {"sha1": "16ef3e801efb57e50bc6477a27a9d95d02d0775b", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/16ef3e801efb57e50bc6477a27a9d95d02d0775b/pack200"}}, "executable": true, "type": "file"}, "bin/policytool": {"downloads": {"lzma": {"sha1": "87da4c07da45f3d1a1a9d732af197cd39bf69d10", "size": 2182, "url": "https://launcher.mojang.com/v1/objects/87da4c07da45f3d1a1a9d732af197cd39bf69d10/policytool"}, "raw": {"sha1": "a52a29424470cb9b8db5c2fb1751d0b697a7ec8e", "size": 8592, "url": "https://launcher.mojang.com/v1/objects/a52a29424470cb9b8db5c2fb1751d0b697a7ec8e/policytool"}}, "executable": true, "type": "file"}, "bin/rmid": {"downloads": {"lzma": {"sha1": "1494c1174fde0c0a93ea117bc7edf7eb936c0512", "size": 2172, "url": "https://launcher.mojang.com/v1/objects/1494c1174fde0c0a93ea117bc7edf7eb936c0512/rmid"}, "raw": {"sha1": "5c8710e1ab924e5b09a07bcb4c6e106293bbd1a8", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/5c8710e1ab924e5b09a07bcb4c6e106293bbd1a8/rmid"}}, "executable": true, "type": "file"}, "bin/rmiregistry": {"downloads": {"lzma": {"sha1": "7070cf2ec5a5e520a880bae699431edf02083e7e", "size": 2174, "url": "https://launcher.mojang.com/v1/objects/7070cf2ec5a5e520a880bae699431edf02083e7e/rmiregistry"}, "raw": {"sha1": "5f518daa7050028d5d9d849634c73136f2b23a54", "size": 8592, "url": "https://launcher.mojang.com/v1/objects/5f518daa7050028d5d9d849634c73136f2b23a54/rmiregistry"}}, "executable": true, "type": "file"}, "bin/servertool": {"downloads": {"lzma": {"sha1": "1db683a11cc9b7313426c84412f4d95be2fa7ccd", "size": 2185, "url": "https://launcher.mojang.com/v1/objects/1db683a11cc9b7313426c84412f4d95be2fa7ccd/servertool"}, "raw": {"sha1": "49d0ebfeb265ce5a8733e1014541ea2525674a60", "size": 8592, "url": "https://launcher.mojang.com/v1/objects/49d0ebfeb265ce5a8733e1014541ea2525674a60/servertool"}}, "executable": true, "type": "file"}, "bin/tnameserv": {"downloads": {"lzma": {"sha1": "36da9c9a2c5a8b662a3f8d52ca67339bce1c2714", "size": 2291, "url": "https://launcher.mojang.com/v1/objects/36da9c9a2c5a8b662a3f8d52ca67339bce1c2714/tnameserv"}, "raw": {"sha1": "09d998f8efcb6f55d0d87f59e08f8b89662796d9", "size": 8656, "url": "https://launcher.mojang.com/v1/objects/09d998f8efcb6f55d0d87f59e08f8b89662796d9/tnameserv"}}, "executable": true, "type": "file"}, "bin/unpack200": {"downloads": {"lzma": {"sha1": "344959e32fc7ee19eebe7b3cf5ab6d1a7d6641f2", "size": 79721, "url": "https://launcher.mojang.com/v1/objects/344959e32fc7ee19eebe7b3cf5ab6d1a7d6641f2/unpack200"}, "raw": {"sha1": "5dd933132f1b202e19e0c8e093f7113711cfdfc1", "size": 182616, "url": "https://launcher.mojang.com/v1/objects/5dd933132f1b202e19e0c8e093f7113711cfdfc1/unpack200"}}, "executable": true, "type": "file"}, "lib": {"type": "directory"}, "lib/amd64": {"type": "directory"}, "lib/amd64/jli": {"type": "directory"}, "lib/amd64/jli/libjli.so": {"downloads": {"lzma": {"sha1": "372331ee8e375888f798a2e88180a94493e141b0", "size": 48327, "url": "https://launcher.mojang.com/v1/objects/372331ee8e375888f798a2e88180a94493e141b0/libjli.so"}, "raw": {"sha1": "73b0cf8b7415686bc40c561ff77ff2740ccf7a44", "size": 108616, "url": "https://launcher.mojang.com/v1/objects/73b0cf8b7415686bc40c561ff77ff2740ccf7a44/libjli.so"}}, "executable": true, "type": "file"}, "lib/amd64/jvm.cfg": {"downloads": {"lzma": {"sha1": "86bcfebec37b38415525ffd77d3eaf70d0b1b4ca", "size": 435, "url": "https://launcher.mojang.com/v1/objects/86bcfebec37b38415525ffd77d3eaf70d0b1b4ca/jvm.cfg"}, "raw": {"sha1": "84b38bdc745de446ba0ca0232ea3aaf2efd721da", "size": 627, "url": "https://launcher.mojang.com/v1/objects/84b38bdc745de446ba0ca0232ea3aaf2efd721da/jvm.cfg"}}, "executable": false, "type": "file"}, "lib/amd64/libavplugin-53.so": {"downloads": {"lzma": {"sha1": "a332366762d9efc7b845a682b7edce62db44618c", "size": 14747, "url": "https://launcher.mojang.com/v1/objects/a332366762d9efc7b845a682b7edce62db44618c/libavplugin-53.so"}, "raw": {"sha1": "9bd1473dd8a0dc7950c7af1cc69a45548df26eb5", "size": 51720, "url": "https://launcher.mojang.com/v1/objects/9bd1473dd8a0dc7950c7af1cc69a45548df26eb5/libavplugin-53.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-54.so": {"downloads": {"lzma": {"sha1": "2c615852a0720a275163e00597c1f711f11341da", "size": 15153, "url": "https://launcher.mojang.com/v1/objects/2c615852a0720a275163e00597c1f711f11341da/libavplugin-54.so"}, "raw": {"sha1": "8808050c5949c4800b42d1b19b1f8b0d120bcacb", "size": 51768, "url": "https://launcher.mojang.com/v1/objects/8808050c5949c4800b42d1b19b1f8b0d120bcacb/libavplugin-54.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-55.so": {"downloads": {"lzma": {"sha1": "39ee8e7fe14f0010c78973962800f539c3e4c16b", "size": 15168, "url": "https://launcher.mojang.com/v1/objects/39ee8e7fe14f0010c78973962800f539c3e4c16b/libavplugin-55.so"}, "raw": {"sha1": "f10ea4ea3489e96d8d161a96790133c417ec44e1", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/f10ea4ea3489e96d8d161a96790133c417ec44e1/libavplugin-55.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-56.so": {"downloads": {"lzma": {"sha1": "abe7feced5a559f1bdc868526dc69484e0e591a0", "size": 15169, "url": "https://launcher.mojang.com/v1/objects/abe7feced5a559f1bdc868526dc69484e0e591a0/libavplugin-56.so"}, "raw": {"sha1": "e5bfcbff5a5a5a5993a3e689a05ef358c131a3ed", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/e5bfcbff5a5a5a5993a3e689a05ef358c131a3ed/libavplugin-56.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-57.so": {"downloads": {"lzma": {"sha1": "4dd26b4ef2294b6929dcb2c7546b47eac5cc78a9", "size": 15174, "url": "https://launcher.mojang.com/v1/objects/4dd26b4ef2294b6929dcb2c7546b47eac5cc78a9/libavplugin-57.so"}, "raw": {"sha1": "2949e7ff9b0ac90e8943c211cff141ab12eec3f8", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/2949e7ff9b0ac90e8943c211cff141ab12eec3f8/libavplugin-57.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-ffmpeg-56.so": {"downloads": {"lzma": {"sha1": "c688ba1cfa442bf18bee43b2fa870b4dc1ce3fb6", "size": 15231, "url": "https://launcher.mojang.com/v1/objects/c688ba1cfa442bf18bee43b2fa870b4dc1ce3fb6/libavplugin-ffmpeg-56.so"}, "raw": {"sha1": "0d36c971a9ad99fc2292092fdec3a4179b1021b9", "size": 51920, "url": "https://launcher.mojang.com/v1/objects/0d36c971a9ad99fc2292092fdec3a4179b1021b9/libavplugin-ffmpeg-56.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-ffmpeg-57.so": {"downloads": {"lzma": {"sha1": "087426bdbffebcfa372a438e863785f4ffbe9a6b", "size": 15180, "url": "https://launcher.mojang.com/v1/objects/087426bdbffebcfa372a438e863785f4ffbe9a6b/libavplugin-ffmpeg-57.so"}, "raw": {"sha1": "5e9c4eb4b49eb8e57c01003ec73a1eb8d6d8c462", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/5e9c4eb4b49eb8e57c01003ec73a1eb8d6d8c462/libavplugin-ffmpeg-57.so"}}, "executable": true, "type": "file"}, "lib/amd64/libawt.so": {"downloads": {"lzma": {"sha1": "018be58b205b73c842a55df811b70d0e8237216e", "size": 195720, "url": "https://launcher.mojang.com/v1/objects/018be58b205b73c842a55df811b70d0e8237216e/libawt.so"}, "raw": {"sha1": "02632cd326e3161c00a7e784599dd7b9ee053dce", "size": 759184, "url": "https://launcher.mojang.com/v1/objects/02632cd326e3161c00a7e784599dd7b9ee053dce/libawt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libawt_headless.so": {"downloads": {"lzma": {"sha1": "7ac2517cff75d4bbb0a0412a9b5f18c74ea188fa", "size": 11211, "url": "https://launcher.mojang.com/v1/objects/7ac2517cff75d4bbb0a0412a9b5f18c74ea188fa/libawt_headless.so"}, "raw": {"sha1": "862157ec957008d0911c5daedc004b3a202623a4", "size": 39176, "url": "https://launcher.mojang.com/v1/objects/862157ec957008d0911c5daedc004b3a202623a4/libawt_headless.so"}}, "executable": true, "type": "file"}, "lib/amd64/libawt_xawt.so": {"downloads": {"lzma": {"sha1": "d536a96af27dfe35de6bb2c8759d51c488cdd8d4", "size": 149598, "url": "https://launcher.mojang.com/v1/objects/d536a96af27dfe35de6bb2c8759d51c488cdd8d4/libawt_xawt.so"}, "raw": {"sha1": "28232b3e01b6f11bfe098bfc6eafc3a513dcebf1", "size": 470232, "url": "https://launcher.mojang.com/v1/objects/28232b3e01b6f11bfe098bfc6eafc3a513dcebf1/libawt_xawt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libbci.so": {"downloads": {"lzma": {"sha1": "c36fad091d11e64c815d5ca17c0ef7a55b0776b1", "size": 3509, "url": "https://launcher.mojang.com/v1/objects/c36fad091d11e64c815d5ca17c0ef7a55b0776b1/libbci.so"}, "raw": {"sha1": "33824051db1ccb6332e22c2b63231055240d0af0", "size": 12760, "url": "https://launcher.mojang.com/v1/objects/33824051db1ccb6332e22c2b63231055240d0af0/libbci.so"}}, "executable": true, "type": "file"}, "lib/amd64/libdcpr.so": {"downloads": {"lzma": {"sha1": "70c6b0933a37f2b1124d6e7c131039241fe796ee", "size": 75969, "url": "https://launcher.mojang.com/v1/objects/70c6b0933a37f2b1124d6e7c131039241fe796ee/libdcpr.so"}, "raw": {"sha1": "fa7001bc5d80579e2716590f3eee8027da0beae7", "size": 204456, "url": "https://launcher.mojang.com/v1/objects/fa7001bc5d80579e2716590f3eee8027da0beae7/libdcpr.so"}}, "executable": true, "type": "file"}, "lib/amd64/libdecora_sse.so": {"downloads": {"lzma": {"sha1": "514acc017dfb6cefaf8cc6d18006ce55781cc9bc", "size": 24397, "url": "https://launcher.mojang.com/v1/objects/514acc017dfb6cefaf8cc6d18006ce55781cc9bc/libdecora_sse.so"}, "raw": {"sha1": "d0c84233504c916e548e29f513e25f6a7479abfc", "size": 74912, "url": "https://launcher.mojang.com/v1/objects/d0c84233504c916e548e29f513e25f6a7479abfc/libdecora_sse.so"}}, "executable": true, "type": "file"}, "lib/amd64/libdeploy.so": {"downloads": {"lzma": {"sha1": "6cf31fd98301c749ac0d2c7825f6d925a4409760", "size": 168999, "url": "https://launcher.mojang.com/v1/objects/6cf31fd98301c749ac0d2c7825f6d925a4409760/libdeploy.so"}, "raw": {"sha1": "b3832e97ed8ca794884b56a591b83d02a2c0c06f", "size": 642368, "url": "https://launcher.mojang.com/v1/objects/b3832e97ed8ca794884b56a591b83d02a2c0c06f/libdeploy.so"}}, "executable": true, "type": "file"}, "lib/amd64/libdt_socket.so": {"downloads": {"lzma": {"sha1": "4cc5c880dbb6fa180436d12d60f0abec8ebb59dc", "size": 7784, "url": "https://launcher.mojang.com/v1/objects/4cc5c880dbb6fa180436d12d60f0abec8ebb59dc/libdt_socket.so"}, "raw": {"sha1": "91ce96f252b8139fc12f0f224ed5b1a041767ab7", "size": 24616, "url": "https://launcher.mojang.com/v1/objects/91ce96f252b8139fc12f0f224ed5b1a041767ab7/libdt_socket.so"}}, "executable": true, "type": "file"}, "lib/amd64/libfontmanager.so": {"downloads": {"lzma": {"sha1": "f94e5e94c71c603ff4d3cd1e7e3d9e181fcc145d", "size": 146951, "url": "https://launcher.mojang.com/v1/objects/f94e5e94c71c603ff4d3cd1e7e3d9e181fcc145d/libfontmanager.so"}, "raw": {"sha1": "2428e805f2c53d1283a033dfd11a86fbb7bd7159", "size": 490672, "url": "https://launcher.mojang.com/v1/objects/2428e805f2c53d1283a033dfd11a86fbb7bd7159/libfontmanager.so"}}, "executable": true, "type": "file"}, "lib/amd64/libfxplugins.so": {"downloads": {"lzma": {"sha1": "a640143365d382a5ad743a784bc2f3706d9d6d67", "size": 50048, "url": "https://launcher.mojang.com/v1/objects/a640143365d382a5ad743a784bc2f3706d9d6d67/libfxplugins.so"}, "raw": {"sha1": "0fd4ac04a84c131f1aaee9e6b0898ff9ea69e3ee", "size": 151448, "url": "https://launcher.mojang.com/v1/objects/0fd4ac04a84c131f1aaee9e6b0898ff9ea69e3ee/libfxplugins.so"}}, "executable": true, "type": "file"}, "lib/amd64/libglass.so": {"downloads": {"lzma": {"sha1": "f1ff517714fa5f2c861f33b32db823fe851541f1", "size": 2856, "url": "https://launcher.mojang.com/v1/objects/f1ff517714fa5f2c861f33b32db823fe851541f1/libglass.so"}, "raw": {"sha1": "e7f4fece30ac727be8148d33b8256abd3a41cef9", "size": 13072, "url": "https://launcher.mojang.com/v1/objects/e7f4fece30ac727be8148d33b8256abd3a41cef9/libglass.so"}}, "executable": true, "type": "file"}, "lib/amd64/libglassgtk2.so": {"downloads": {"lzma": {"sha1": "15b90f7a2baacd15e80aa9785d87cf1e4258376d", "size": 220476, "url": "https://launcher.mojang.com/v1/objects/15b90f7a2baacd15e80aa9785d87cf1e4258376d/libglassgtk2.so"}, "raw": {"sha1": "e30a634c2ff2143bdee512360553d6e0304f33b2", "size": 844984, "url": "https://launcher.mojang.com/v1/objects/e30a634c2ff2143bdee512360553d6e0304f33b2/libglassgtk2.so"}}, "executable": true, "type": "file"}, "lib/amd64/libglassgtk3.so": {"downloads": {"lzma": {"sha1": "868c231165f8c9043b7f0e7de208ec023f06a6e7", "size": 220560, "url": "https://launcher.mojang.com/v1/objects/868c231165f8c9043b7f0e7de208ec023f06a6e7/libglassgtk3.so"}, "raw": {"sha1": "762a11a2b376b7b5a2a7cad780715524fdd176d5", "size": 845304, "url": "https://launcher.mojang.com/v1/objects/762a11a2b376b7b5a2a7cad780715524fdd176d5/libglassgtk3.so"}}, "executable": true, "type": "file"}, "lib/amd64/libglib-lite.so": {"downloads": {"lzma": {"sha1": "61b8871242febe1be262de167dc20ae94bf964b4", "size": 457046, "url": "https://launcher.mojang.com/v1/objects/61b8871242febe1be262de167dc20ae94bf964b4/libglib-lite.so"}, "raw": {"sha1": "63afa060fc3f120af76128e51d32603fc4336fa8", "size": 1538352, "url": "https://launcher.mojang.com/v1/objects/63afa060fc3f120af76128e51d32603fc4336fa8/libglib-lite.so"}}, "executable": true, "type": "file"}, "lib/amd64/libgstreamer-lite.so": {"downloads": {"lzma": {"sha1": "2447dc368406ba1b989a29937d41924620e01988", "size": 673056, "url": "https://launcher.mojang.com/v1/objects/2447dc368406ba1b989a29937d41924620e01988/libgstreamer-lite.so"}, "raw": {"sha1": "5505e7ca592ac64371d3db8fe53bcb602e9723d3", "size": 2263872, "url": "https://launcher.mojang.com/v1/objects/5505e7ca592ac64371d3db8fe53bcb602e9723d3/libgstreamer-lite.so"}}, "executable": true, "type": "file"}, "lib/amd64/libhprof.so": {"downloads": {"lzma": {"sha1": "94a5589c818db1fb1cf1881e24e217c309fce2e4", "size": 64471, "url": "https://launcher.mojang.com/v1/objects/94a5589c818db1fb1cf1881e24e217c309fce2e4/libhprof.so"}, "raw": {"sha1": "4bb9bdeef6133b6dd558d52d691b077c03e9b0ee", "size": 175504, "url": "https://launcher.mojang.com/v1/objects/4bb9bdeef6133b6dd558d52d691b077c03e9b0ee/libhprof.so"}}, "executable": true, "type": "file"}, "lib/amd64/libinstrument.so": {"downloads": {"lzma": {"sha1": "84ffea356caf725b42c86a8ebc9587f477ddde29", "size": 18603, "url": "https://launcher.mojang.com/v1/objects/84ffea356caf725b42c86a8ebc9587f477ddde29/libinstrument.so"}, "raw": {"sha1": "cb8009769601e3fecd7ea2b36c344f737b1a9da7", "size": 51560, "url": "https://launcher.mojang.com/v1/objects/cb8009769601e3fecd7ea2b36c344f737b1a9da7/libinstrument.so"}}, "executable": true, "type": "file"}, "lib/amd64/libj2gss.so": {"downloads": {"lzma": {"sha1": "4b2aa699506b126098b585a9617ce1c05707fa29", "size": 14132, "url": "https://launcher.mojang.com/v1/objects/4b2aa699506b126098b585a9617ce1c05707fa29/libj2gss.so"}, "raw": {"sha1": "cbce4a302b255d4d1924ef7606f038af766c5e86", "size": 47688, "url": "https://launcher.mojang.com/v1/objects/cbce4a302b255d4d1924ef7606f038af766c5e86/libj2gss.so"}}, "executable": true, "type": "file"}, "lib/amd64/libj2pcsc.so": {"downloads": {"lzma": {"sha1": "2361d3b2e3da48593c391b29b0d2b5409e4c55e5", "size": 5074, "url": "https://launcher.mojang.com/v1/objects/2361d3b2e3da48593c391b29b0d2b5409e4c55e5/libj2pcsc.so"}, "raw": {"sha1": "1274178492e7a3e997e12f67794616f7c3d8d0b9", "size": 18296, "url": "https://launcher.mojang.com/v1/objects/1274178492e7a3e997e12f67794616f7c3d8d0b9/libj2pcsc.so"}}, "executable": true, "type": "file"}, "lib/amd64/libj2pkcs11.so": {"downloads": {"lzma": {"sha1": "ef927e2790ba05931d0f0bdd63da3d275a834946", "size": 21573, "url": "https://launcher.mojang.com/v1/objects/ef927e2790ba05931d0f0bdd63da3d275a834946/libj2pkcs11.so"}, "raw": {"sha1": "bd4f2af9bfdc6168633d1920c1a1415de06bb45a", "size": 79472, "url": "https://launcher.mojang.com/v1/objects/bd4f2af9bfdc6168633d1920c1a1415de06bb45a/libj2pkcs11.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjaas_unix.so": {"downloads": {"lzma": {"sha1": "7f7e843544ee1eb1454a5826bdd4218685b79430", "size": 2404, "url": "https://launcher.mojang.com/v1/objects/7f7e843544ee1eb1454a5826bdd4218685b79430/libjaas_unix.so"}, "raw": {"sha1": "4c517925c7d464a5b719898eb0bea1b04df31f1f", "size": 8192, "url": "https://launcher.mojang.com/v1/objects/4c517925c7d464a5b719898eb0bea1b04df31f1f/libjaas_unix.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjava.so": {"downloads": {"lzma": {"sha1": "5eee7a42600a44a8bb8d6d7f510fd96a29637ac0", "size": 63113, "url": "https://launcher.mojang.com/v1/objects/5eee7a42600a44a8bb8d6d7f510fd96a29637ac0/libjava.so"}, "raw": {"sha1": "e280aeddf3fc0ec664aef7efc0e0e197a54aaf02", "size": 227672, "url": "https://launcher.mojang.com/v1/objects/e280aeddf3fc0ec664aef7efc0e0e197a54aaf02/libjava.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjava_crw_demo.so": {"downloads": {"lzma": {"sha1": "b197cf23ae3556eb0b45c663f0a8cb62408b961e", "size": 10412, "url": "https://launcher.mojang.com/v1/objects/b197cf23ae3556eb0b45c663f0a8cb62408b961e/libjava_crw_demo.so"}, "raw": {"sha1": "18f20f906977c90d0090b41dbda8dd5cfead5a4c", "size": 26144, "url": "https://launcher.mojang.com/v1/objects/18f20f906977c90d0090b41dbda8dd5cfead5a4c/libjava_crw_demo.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_font.so": {"downloads": {"lzma": {"sha1": "ffbba0e5022f829412b86063d8a90f95f16709b1", "size": 5608, "url": "https://launcher.mojang.com/v1/objects/ffbba0e5022f829412b86063d8a90f95f16709b1/libjavafx_font.so"}, "raw": {"sha1": "8634a0aca612fc40420a4a7cc8af4cc46cfc6725", "size": 17104, "url": "https://launcher.mojang.com/v1/objects/8634a0aca612fc40420a4a7cc8af4cc46cfc6725/libjavafx_font.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_font_freetype.so": {"downloads": {"lzma": {"sha1": "310271eda8a2ac264ffc3640a9d847b49438d0bd", "size": 6942, "url": "https://launcher.mojang.com/v1/objects/310271eda8a2ac264ffc3640a9d847b49438d0bd/libjavafx_font_freetype.so"}, "raw": {"sha1": "3e7572d047c12ba2bc43acec7f98a67c20af8042", "size": 27616, "url": "https://launcher.mojang.com/v1/objects/3e7572d047c12ba2bc43acec7f98a67c20af8042/libjavafx_font_freetype.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_font_pango.so": {"downloads": {"lzma": {"sha1": "a7bcf0669e70b0f43099a99c81e6b6440cb40ac0", "size": 5820, "url": "https://launcher.mojang.com/v1/objects/a7bcf0669e70b0f43099a99c81e6b6440cb40ac0/libjavafx_font_pango.so"}, "raw": {"sha1": "f0b775cc9a514c7ee8b4d6fb300653ce548caf10", "size": 25560, "url": "https://launcher.mojang.com/v1/objects/f0b775cc9a514c7ee8b4d6fb300653ce548caf10/libjavafx_font_pango.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_font_t2k.so": {"downloads": {"lzma": {"sha1": "551c29dc7c7fc83223aa36a6187f7e0c5d650538", "size": 431450, "url": "https://launcher.mojang.com/v1/objects/551c29dc7c7fc83223aa36a6187f7e0c5d650538/libjavafx_font_t2k.so"}, "raw": {"sha1": "91e5813057c3b852d411540160f8ad05fb9f1ed3", "size": 1486128, "url": "https://launcher.mojang.com/v1/objects/91e5813057c3b852d411540160f8ad05fb9f1ed3/libjavafx_font_t2k.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_iio.so": {"downloads": {"lzma": {"sha1": "c832998fd5e06ed6dcd6428816194c350785420c", "size": 101479, "url": "https://launcher.mojang.com/v1/objects/c832998fd5e06ed6dcd6428816194c350785420c/libjavafx_iio.so"}, "raw": {"sha1": "dcdf68cb25677b76c1cf0bb94294e6e9880a6678", "size": 256336, "url": "https://launcher.mojang.com/v1/objects/dcdf68cb25677b76c1cf0bb94294e6e9880a6678/libjavafx_iio.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjawt.so": {"downloads": {"lzma": {"sha1": "c1ced6aad5c69ff444dc67d0fd7e333558953831", "size": 1872, "url": "https://launcher.mojang.com/v1/objects/c1ced6aad5c69ff444dc67d0fd7e333558953831/libjawt.so"}, "raw": {"sha1": "c5032f2c6fa40bea24e56605cf76b26a27e87b67", "size": 8048, "url": "https://launcher.mojang.com/v1/objects/c5032f2c6fa40bea24e56605cf76b26a27e87b67/libjawt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjdwp.so": {"downloads": {"lzma": {"sha1": "c1aabbb3f5a624b9ad10ed871a1d83510a99b646", "size": 94884, "url": "https://launcher.mojang.com/v1/objects/c1aabbb3f5a624b9ad10ed871a1d83510a99b646/libjdwp.so"}, "raw": {"sha1": "a043e97be47937f6f552e94cf79c76c1c57f9594", "size": 272248, "url": "https://launcher.mojang.com/v1/objects/a043e97be47937f6f552e94cf79c76c1c57f9594/libjdwp.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjfr.so": {"downloads": {"lzma": {"sha1": "11b8e6bfffdccbacbf9dd29dea4b90b753f3c1b7", "size": 8780, "url": "https://launcher.mojang.com/v1/objects/11b8e6bfffdccbacbf9dd29dea4b90b753f3c1b7/libjfr.so"}, "raw": {"sha1": "312392dd186b11c418183e818f1928e8685a07e5", "size": 28384, "url": "https://launcher.mojang.com/v1/objects/312392dd186b11c418183e818f1928e8685a07e5/libjfr.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjfxmedia.so": {"downloads": {"lzma": {"sha1": "a4e7a126eb648ce6e5e6dc151831da37d8334139", "size": 391897, "url": "https://launcher.mojang.com/v1/objects/a4e7a126eb648ce6e5e6dc151831da37d8334139/libjfxmedia.so"}, "raw": {"sha1": "5fa54944327a6012c3d34cb5c1c4432762178dc8", "size": 1636376, "url": "https://launcher.mojang.com/v1/objects/5fa54944327a6012c3d34cb5c1c4432762178dc8/libjfxmedia.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjfxwebkit.so": {"downloads": {"lzma": {"sha1": "b274debd222cdcc2ee84160ebb95144b3880bc97", "size": 20492825, "url": "https://launcher.mojang.com/v1/objects/b274debd222cdcc2ee84160ebb95144b3880bc97/libjfxwebkit.so"}, "raw": {"sha1": "ecee564c3b2f645131b35bb3004abd4caeabd291", "size": 91014584, "url": "https://launcher.mojang.com/v1/objects/ecee564c3b2f645131b35bb3004abd4caeabd291/libjfxwebkit.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjpeg.so": {"downloads": {"lzma": {"sha1": "9ad55e370c5eaaa73c3158339db3c368b1aaf0cb", "size": 113072, "url": "https://launcher.mojang.com/v1/objects/9ad55e370c5eaaa73c3158339db3c368b1aaf0cb/libjpeg.so"}, "raw": {"sha1": "651e6d53ae67db1f0efbf7f104447a9b49b7e333", "size": 292520, "url": "https://launcher.mojang.com/v1/objects/651e6d53ae67db1f0efbf7f104447a9b49b7e333/libjpeg.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjsdt.so": {"downloads": {"lzma": {"sha1": "04b6d1361a34c496b5f652b2477784d69b8b6baf", "size": 3964, "url": "https://launcher.mojang.com/v1/objects/04b6d1361a34c496b5f652b2477784d69b8b6baf/libjsdt.so"}, "raw": {"sha1": "82b48a82bf6183d34cf00a0f81661b45c616f31b", "size": 12904, "url": "https://launcher.mojang.com/v1/objects/82b48a82bf6183d34cf00a0f81661b45c616f31b/libjsdt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjsig.so": {"downloads": {"lzma": {"sha1": "37d3b89abde397216cc4ecb1339d8543d99b8428", "size": 3536, "url": "https://launcher.mojang.com/v1/objects/37d3b89abde397216cc4ecb1339d8543d99b8428/libjsig.so"}, "raw": {"sha1": "42e52ba1bcbe0362ab24bcf65c93797354db6fb9", "size": 13336, "url": "https://launcher.mojang.com/v1/objects/42e52ba1bcbe0362ab24bcf65c93797354db6fb9/libjsig.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjsound.so": {"downloads": {"lzma": {"sha1": "7e3c565d74d8ffae716f32b05544fa4a6f108adc", "size": 2002, "url": "https://launcher.mojang.com/v1/objects/7e3c565d74d8ffae716f32b05544fa4a6f108adc/libjsound.so"}, "raw": {"sha1": "0c0fc63b92d7b83c9960fa80d45c80553ea20254", "size": 8232, "url": "https://launcher.mojang.com/v1/objects/0c0fc63b92d7b83c9960fa80d45c80553ea20254/libjsound.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjsoundalsa.so": {"downloads": {"lzma": {"sha1": "b06c51858a25ff776519495f1b9b3d9f604b089f", "size": 23097, "url": "https://launcher.mojang.com/v1/objects/b06c51858a25ff776519495f1b9b3d9f604b089f/libjsoundalsa.so"}, "raw": {"sha1": "281d37f0326d4a12dc7ea316ead09c198ff7bdf7", "size": 83256, "url": "https://launcher.mojang.com/v1/objects/281d37f0326d4a12dc7ea316ead09c198ff7bdf7/libjsoundalsa.so"}}, "executable": true, "type": "file"}, "lib/amd64/liblcms.so": {"downloads": {"lzma": {"sha1": "7a239baba2086cae49114b382b74b971da02f08e", "size": 176175, "url": "https://launcher.mojang.com/v1/objects/7a239baba2086cae49114b382b74b971da02f08e/liblcms.so"}, "raw": {"sha1": "c8895cc3c3d023d9e059225969ab67954772c0a1", "size": 526872, "url": "https://launcher.mojang.com/v1/objects/c8895cc3c3d023d9e059225969ab67954772c0a1/liblcms.so"}}, "executable": true, "type": "file"}, "lib/amd64/libmanagement.so": {"downloads": {"lzma": {"sha1": "aed3fdbcefd1716abfc6a306687c8b741cbb318e", "size": 12838, "url": "https://launcher.mojang.com/v1/objects/aed3fdbcefd1716abfc6a306687c8b741cbb318e/libmanagement.so"}, "raw": {"sha1": "eba35f61e0d50e30874b7c7b335edf2d52662423", "size": 51808, "url": "https://launcher.mojang.com/v1/objects/eba35f61e0d50e30874b7c7b335edf2d52662423/libmanagement.so"}}, "executable": true, "type": "file"}, "lib/amd64/libmlib_image.so": {"downloads": {"lzma": {"sha1": "1bb181f079492d55c7a458e96488cd17fe0a7b86", "size": 310272, "url": "https://launcher.mojang.com/v1/objects/1bb181f079492d55c7a458e96488cd17fe0a7b86/libmlib_image.so"}, "raw": {"sha1": "c973c450d33873675945d4694be484e3427f58f1", "size": 1048136, "url": "https://launcher.mojang.com/v1/objects/c973c450d33873675945d4694be484e3427f58f1/libmlib_image.so"}}, "executable": true, "type": "file"}, "lib/amd64/libnet.so": {"downloads": {"lzma": {"sha1": "9dd79703b6deb86e0321afe01c6ac508263c8312", "size": 38123, "url": "https://launcher.mojang.com/v1/objects/9dd79703b6deb86e0321afe01c6ac508263c8312/libnet.so"}, "raw": {"sha1": "b3a17b7d53fcdf1e689e1ec29ce851eee6022ead", "size": 116920, "url": "https://launcher.mojang.com/v1/objects/b3a17b7d53fcdf1e689e1ec29ce851eee6022ead/libnet.so"}}, "executable": true, "type": "file"}, "lib/amd64/libnio.so": {"downloads": {"lzma": {"sha1": "5697c89d5d5d9b74f2e1555fcbba79dd4049e287", "size": 24445, "url": "https://launcher.mojang.com/v1/objects/5697c89d5d5d9b74f2e1555fcbba79dd4049e287/libnio.so"}, "raw": {"sha1": "573bf8f64dbcc397f8abd3e1da28f90ab0679f5b", "size": 93872, "url": "https://launcher.mojang.com/v1/objects/573bf8f64dbcc397f8abd3e1da28f90ab0679f5b/libnio.so"}}, "executable": true, "type": "file"}, "lib/amd64/libnpjp2.so": {"downloads": {"lzma": {"sha1": "6fe53b5951ff740e7f2ef7ffe5975af26da06718", "size": 57892, "url": "https://launcher.mojang.com/v1/objects/6fe53b5951ff740e7f2ef7ffe5975af26da06718/libnpjp2.so"}, "raw": {"sha1": "2bb13c53a4280379253475e51216b97eed1d4ce3", "size": 216592, "url": "https://launcher.mojang.com/v1/objects/2bb13c53a4280379253475e51216b97eed1d4ce3/libnpjp2.so"}}, "executable": true, "type": "file"}, "lib/amd64/libnpt.so": {"downloads": {"lzma": {"sha1": "1b170b09a32b1b8b6624fa5d1f94ec60b2bf3876", "size": 5070, "url": "https://launcher.mojang.com/v1/objects/1b170b09a32b1b8b6624fa5d1f94ec60b2bf3876/libnpt.so"}, "raw": {"sha1": "6b1ff6b9b4624f3cc7801f221c82b8046fb76364", "size": 17504, "url": "https://launcher.mojang.com/v1/objects/6b1ff6b9b4624f3cc7801f221c82b8046fb76364/libnpt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libprism_common.so": {"downloads": {"lzma": {"sha1": "f4aca04c90bc7505851c074a08b2c31cae1f94fa", "size": 23315, "url": "https://launcher.mojang.com/v1/objects/f4aca04c90bc7505851c074a08b2c31cae1f94fa/libprism_common.so"}, "raw": {"sha1": "b00866b6ed8646a29a334d46e297267552f27de8", "size": 59008, "url": "https://launcher.mojang.com/v1/objects/b00866b6ed8646a29a334d46e297267552f27de8/libprism_common.so"}}, "executable": true, "type": "file"}, "lib/amd64/libprism_es2.so": {"downloads": {"lzma": {"sha1": "7ff4173c338c7a6f370f231670055737e032da3e", "size": 18416, "url": "https://launcher.mojang.com/v1/objects/7ff4173c338c7a6f370f231670055737e032da3e/libprism_es2.so"}, "raw": {"sha1": "1390a1dc14345e5a948148e59195d62f3a83863f", "size": 63808, "url": "https://launcher.mojang.com/v1/objects/1390a1dc14345e5a948148e59195d62f3a83863f/libprism_es2.so"}}, "executable": true, "type": "file"}, "lib/amd64/libprism_sw.so": {"downloads": {"lzma": {"sha1": "6728e8bf7b214067d715be6fe0325910d63c2468", "size": 29457, "url": "https://launcher.mojang.com/v1/objects/6728e8bf7b214067d715be6fe0325910d63c2468/libprism_sw.so"}, "raw": {"sha1": "7a6c34cb2bbcde411778d1b3f8677c39e32c3ac4", "size": 71608, "url": "https://launcher.mojang.com/v1/objects/7a6c34cb2bbcde411778d1b3f8677c39e32c3ac4/libprism_sw.so"}}, "executable": true, "type": "file"}, "lib/amd64/libresource.so": {"downloads": {"lzma": {"sha1": "1e35e63f1e74915fba620f1febf420b919d49bc5", "size": 2633, "url": "https://launcher.mojang.com/v1/objects/1e35e63f1e74915fba620f1febf420b919d49bc5/libresource.so"}, "raw": {"sha1": "57490353ad0d83ab1930355213dea269795434fe", "size": 13456, "url": "https://launcher.mojang.com/v1/objects/57490353ad0d83ab1930355213dea269795434fe/libresource.so"}}, "executable": true, "type": "file"}, "lib/amd64/libsctp.so": {"downloads": {"lzma": {"sha1": "4340132ed250d7849a016e071be773eaedd33aa8", "size": 9332, "url": "https://launcher.mojang.com/v1/objects/4340132ed250d7849a016e071be773eaedd33aa8/libsctp.so"}, "raw": {"sha1": "4a80e743750f127c0d5a359f5cd60b97e7ee12ae", "size": 29552, "url": "https://launcher.mojang.com/v1/objects/4a80e743750f127c0d5a359f5cd60b97e7ee12ae/libsctp.so"}}, "executable": true, "type": "file"}, "lib/amd64/libsplashscreen.so": {"downloads": {"lzma": {"sha1": "b226c8dbd73a548fc2b042ee6db6cc80e727597c", "size": 190305, "url": "https://launcher.mojang.com/v1/objects/b226c8dbd73a548fc2b042ee6db6cc80e727597c/libsplashscreen.so"}, "raw": {"sha1": "87d6a491f5ba7e6c4d972264a0c9063afea567a2", "size": 441376, "url": "https://launcher.mojang.com/v1/objects/87d6a491f5ba7e6c4d972264a0c9063afea567a2/libsplashscreen.so"}}, "executable": true, "type": "file"}, "lib/amd64/libsunec.so": {"downloads": {"lzma": {"sha1": "6ebba98fab1e3d872d1363235b76497f6f9babdc", "size": 88829, "url": "https://launcher.mojang.com/v1/objects/6ebba98fab1e3d872d1363235b76497f6f9babdc/libsunec.so"}, "raw": {"sha1": "3b262a0a530f6e4e539aed2cd27b4de1d0ed8859", "size": 283368, "url": "https://launcher.mojang.com/v1/objects/3b262a0a530f6e4e539aed2cd27b4de1d0ed8859/libsunec.so"}}, "executable": true, "type": "file"}, "lib/amd64/libt2k.so": {"downloads": {"lzma": {"sha1": "602cb812ef0b350ccf56ce209a260ddbe3743d92", "size": 190720, "url": "https://launcher.mojang.com/v1/objects/602cb812ef0b350ccf56ce209a260ddbe3743d92/libt2k.so"}, "raw": {"sha1": "b072c56df997f61e15e6b5a43b8907b0d25c2043", "size": 504840, "url": "https://launcher.mojang.com/v1/objects/b072c56df997f61e15e6b5a43b8907b0d25c2043/libt2k.so"}}, "executable": true, "type": "file"}, "lib/amd64/libunpack.so": {"downloads": {"lzma": {"sha1": "7107b615e941074f0b14c31c88fb67200aacd37f", "size": 70308, "url": "https://launcher.mojang.com/v1/objects/7107b615e941074f0b14c31c88fb67200aacd37f/libunpack.so"}, "raw": {"sha1": "b05ff862ed87928ed91e80e5604673c5ea710a53", "size": 197712, "url": "https://launcher.mojang.com/v1/objects/b05ff862ed87928ed91e80e5604673c5ea710a53/libunpack.so"}}, "executable": true, "type": "file"}, "lib/amd64/libverify.so": {"downloads": {"lzma": {"sha1": "ecd98efb8c7da441a8c3580e8f5598f3cb4165b1", "size": 21885, "url": "https://launcher.mojang.com/v1/objects/ecd98efb8c7da441a8c3580e8f5598f3cb4165b1/libverify.so"}, "raw": {"sha1": "e2c8d92531c45ab9be69ffb72c87fa12e9e59827", "size": 66112, "url": "https://launcher.mojang.com/v1/objects/e2c8d92531c45ab9be69ffb72c87fa12e9e59827/libverify.so"}}, "executable": true, "type": "file"}, "lib/amd64/libzip.so": {"downloads": {"lzma": {"sha1": "7c562342e3f7b138dc978495447e3e6a96c2cf45", "size": 54876, "url": "https://launcher.mojang.com/v1/objects/7c562342e3f7b138dc978495447e3e6a96c2cf45/libzip.so"}, "raw": {"sha1": "5f4bf35a5c3e8f8c650e891d1031589b8ab6d77f", "size": 127016, "url": "https://launcher.mojang.com/v1/objects/5f4bf35a5c3e8f8c650e891d1031589b8ab6d77f/libzip.so"}}, "executable": true, "type": "file"}, "lib/amd64/server": {"type": "directory"}, "lib/amd64/server/Xusage.txt": {"downloads": {"lzma": {"sha1": "acb2da24a4c765887df83985e4c26d6be302a0a3", "size": 629, "url": "https://launcher.mojang.com/v1/objects/acb2da24a4c765887df83985e4c26d6be302a0a3/Xusage.txt"}, "raw": {"sha1": "6983727eafe140f9dd793c78aa6f3e007438243a", "size": 1423, "url": "https://launcher.mojang.com/v1/objects/6983727eafe140f9dd793c78aa6f3e007438243a/Xusage.txt"}}, "executable": false, "type": "file"}, "lib/amd64/server/libjsig.so": {"target": "../libjsig.so", "type": "link"}, "lib/amd64/server/libjvm.so": {"downloads": {"lzma": {"sha1": "d5c6f3338aaa6712f79d680ac8c3e31beebaa886", "size": 4154311, "url": "https://launcher.mojang.com/v1/objects/d5c6f3338aaa6712f79d680ac8c3e31beebaa886/libjvm.so"}, "raw": {"sha1": "23a98e1eb505cc3fb91bc0cb2adb71ab9270e9ca", "size": 17045016, "url": "https://launcher.mojang.com/v1/objects/23a98e1eb505cc3fb91bc0cb2adb71ab9270e9ca/libjvm.so"}}, "executable": true, "type": "file"}, "lib/applet": {"type": "directory"}, "lib/calendars.properties": {"downloads": {"lzma": {"sha1": "4a757c23f2942bd802a4f80235131146d9267750", "size": 558, "url": "https://launcher.mojang.com/v1/objects/4a757c23f2942bd802a4f80235131146d9267750/calendars.properties"}, "raw": {"sha1": "42ebb0988124433b8f2a6e5d9a74ed41240bcfc6", "size": 1378, "url": "https://launcher.mojang.com/v1/objects/42ebb0988124433b8f2a6e5d9a74ed41240bcfc6/calendars.properties"}}, "executable": false, "type": "file"}, "lib/charsets.jar": {"downloads": {"lzma": {"sha1": "2bf44143b2ad9d7d55045a4de4a562330c194dc0", "size": 412367, "url": "https://launcher.mojang.com/v1/objects/2bf44143b2ad9d7d55045a4de4a562330c194dc0/charsets.jar"}, "raw": {"sha1": "d73ab9f8de255a7e112ddd13622bf7f6b18c8447", "size": 3135615, "url": "https://launcher.mojang.com/v1/objects/d73ab9f8de255a7e112ddd13622bf7f6b18c8447/charsets.jar"}}, "executable": false, "type": "file"}, "lib/classlist": {"downloads": {"lzma": {"sha1": "14e7c73d21b8513b0aff8d86e5cb34c52021fbca", "size": 15024, "url": "https://launcher.mojang.com/v1/objects/14e7c73d21b8513b0aff8d86e5cb34c52021fbca/classlist"}, "raw": {"sha1": "9c0404b63c87e2fed35e3a6cd137d6cf876c42bd", "size": 84311, "url": "https://launcher.mojang.com/v1/objects/9c0404b63c87e2fed35e3a6cd137d6cf876c42bd/classlist"}}, "executable": false, "type": "file"}, "lib/cmm": {"type": "directory"}, "lib/cmm/CIEXYZ.pf": {"downloads": {"lzma": {"sha1": "fcc5ca2fd3f45cac3434b480fa3ce00007e96529", "size": 8964, "url": "https://launcher.mojang.com/v1/objects/fcc5ca2fd3f45cac3434b480fa3ce00007e96529/CIEXYZ.pf"}, "raw": {"sha1": "b7779924c70554647b87c2a86159ca7781e929f8", "size": 51236, "url": "https://launcher.mojang.com/v1/objects/b7779924c70554647b87c2a86159ca7781e929f8/CIEXYZ.pf"}}, "executable": false, "type": "file"}, "lib/cmm/GRAY.pf": {"downloads": {"lzma": {"sha1": "5388ccfe67d3131d6d02143d8e8895003ab14ff6", "size": 299, "url": "https://launcher.mojang.com/v1/objects/5388ccfe67d3131d6d02143d8e8895003ab14ff6/GRAY.pf"}, "raw": {"sha1": "27f93961d66b8230d0cdb8b166bc8b4153d5bc2d", "size": 632, "url": "https://launcher.mojang.com/v1/objects/27f93961d66b8230d0cdb8b166bc8b4153d5bc2d/GRAY.pf"}}, "executable": false, "type": "file"}, "lib/cmm/LINEAR_RGB.pf": {"downloads": {"lzma": {"sha1": "2bd90f09c8deb64b1729d6b8173c78f9e9cab27b", "size": 678, "url": "https://launcher.mojang.com/v1/objects/2bd90f09c8deb64b1729d6b8173c78f9e9cab27b/LINEAR_RGB.pf"}, "raw": {"sha1": "7913274c2f73bafcf888f09ff60990b100214ede", "size": 1044, "url": "https://launcher.mojang.com/v1/objects/7913274c2f73bafcf888f09ff60990b100214ede/LINEAR_RGB.pf"}}, "executable": false, "type": "file"}, "lib/cmm/PYCC.pf": {"downloads": {"lzma": {"sha1": "dbb2197ecff3fcdd142e9006490c8cb5c8d19af8", "size": 171521, "url": "https://launcher.mojang.com/v1/objects/dbb2197ecff3fcdd142e9006490c8cb5c8d19af8/PYCC.pf"}, "raw": {"sha1": "4f7eed05b8f0eea7bcdc8f8f7aaeb1925ce7b144", "size": 274474, "url": "https://launcher.mojang.com/v1/objects/4f7eed05b8f0eea7bcdc8f8f7aaeb1925ce7b144/PYCC.pf"}}, "executable": false, "type": "file"}, "lib/cmm/sRGB.pf": {"downloads": {"raw": {"sha1": "9eaea0911d89d63e39e95f2e2116eaec7e0bb91e", "size": 3144, "url": "https://launcher.mojang.com/v1/objects/9eaea0911d89d63e39e95f2e2116eaec7e0bb91e/sRGB.pf"}}, "executable": false, "type": "file"}, "lib/content-types.properties": {"downloads": {"lzma": {"sha1": "43a23d9a6c637c128b14cfa3feced93cbcf85b1a", "size": 1617, "url": "https://launcher.mojang.com/v1/objects/43a23d9a6c637c128b14cfa3feced93cbcf85b1a/content-types.properties"}, "raw": {"sha1": "b21698017c4a2866b5fabe59681b7592e72c83b1", "size": 5916, "url": "https://launcher.mojang.com/v1/objects/b21698017c4a2866b5fabe59681b7592e72c83b1/content-types.properties"}}, "executable": false, "type": "file"}, "lib/currency.data": {"downloads": {"lzma": {"sha1": "451b3f166ea34ef2aefbb01606ea5adcc0d65b42", "size": 1184, "url": "https://launcher.mojang.com/v1/objects/451b3f166ea34ef2aefbb01606ea5adcc0d65b42/currency.data"}, "raw": {"sha1": "bf524381a7a9b9d5bbab48069c583d2936e367a1", "size": 4134, "url": "https://launcher.mojang.com/v1/objects/bf524381a7a9b9d5bbab48069c583d2936e367a1/currency.data"}}, "executable": false, "type": "file"}, "lib/deploy": {"type": "directory"}, "lib/deploy.jar": {"downloads": {"raw": {"sha1": "fbe1de8fcd9a3d482c59414dce9311e4194766c9", "size": 2255881, "url": "https://launcher.mojang.com/v1/objects/fbe1de8fcd9a3d482c59414dce9311e4194766c9/deploy.jar"}}, "executable": false, "type": "file"}, "lib/deploy/MixedCodeMainDialog.ui": {"downloads": {"lzma": {"sha1": "7d812964343d1e978442f5c847c709784fc18fc0", "size": 683, "url": "https://launcher.mojang.com/v1/objects/7d812964343d1e978442f5c847c709784fc18fc0/MixedCodeMainDialog.ui"}, "raw": {"sha1": "c9b1af1c229e54b2d8a3d642d4f0bb31dc15be79", "size": 4507, "url": "https://launcher.mojang.com/v1/objects/c9b1af1c229e54b2d8a3d642d4f0bb31dc15be79/MixedCodeMainDialog.ui"}}, "executable": false, "type": "file"}, "lib/deploy/MixedCodeMainDialogJs.ui": {"downloads": {"lzma": {"sha1": "54fb58dbcc59e35e0ae896d0e266ae0c5bcf85c2", "size": 792, "url": "https://launcher.mojang.com/v1/objects/54fb58dbcc59e35e0ae896d0e266ae0c5bcf85c2/MixedCodeMainDialogJs.ui"}, "raw": {"sha1": "ad6337fb6d46750e14c12b439a5856f4b6864d0d", "size": 6110, "url": "https://launcher.mojang.com/v1/objects/ad6337fb6d46750e14c12b439a5856f4b6864d0d/MixedCodeMainDialogJs.ui"}}, "executable": false, "type": "file"}, "lib/deploy/cautionshield.icns": {"downloads": {"lzma": {"sha1": "7cea751dc168605054ec38ce8bfa71812be405c1", "size": 2333, "url": "https://launcher.mojang.com/v1/objects/7cea751dc168605054ec38ce8bfa71812be405c1/cautionshield.icns"}, "raw": {"sha1": "1de7ed5d5fc75aa1bcede088c655bee3bde64c38", "size": 3588, "url": "https://launcher.mojang.com/v1/objects/1de7ed5d5fc75aa1bcede088c655bee3bde64c38/cautionshield.icns"}}, "executable": false, "type": "file"}, "lib/deploy/ffjcext.zip": {"downloads": {"lzma": {"sha1": "80bcb9b3794f69d87dba93e90230f288e651e798", "size": 1809, "url": "https://launcher.mojang.com/v1/objects/80bcb9b3794f69d87dba93e90230f288e651e798/ffjcext.zip"}, "raw": {"sha1": "76d051ca7d3666ff25ea8eb9957a05574a45287f", "size": 13454, "url": "https://launcher.mojang.com/v1/objects/76d051ca7d3666ff25ea8eb9957a05574a45287f/ffjcext.zip"}}, "executable": false, "type": "file"}, "lib/deploy/java-icon.ico": {"downloads": {"lzma": {"sha1": "2a24f0207d7ab5976a8b0d92b4b381d49e895c9d", "size": 8468, "url": "https://launcher.mojang.com/v1/objects/2a24f0207d7ab5976a8b0d92b4b381d49e895c9d/java-icon.ico"}, "raw": {"sha1": "2997ceb26ff49a7d7c5e7a2405b5fb50b62c7914", "size": 29926, "url": "https://launcher.mojang.com/v1/objects/2997ceb26ff49a7d7c5e7a2405b5fb50b62c7914/java-icon.ico"}}, "executable": false, "type": "file"}, "lib/deploy/messages.properties": {"downloads": {"lzma": {"sha1": "c1e16f80dc0b1f1a591cecf3cbab4ba5e47492f4", "size": 1225, "url": "https://launcher.mojang.com/v1/objects/c1e16f80dc0b1f1a591cecf3cbab4ba5e47492f4/messages.properties"}, "raw": {"sha1": "dc52841c708e3c1eb2a044088a43396d1291bb5e", "size": 2860, "url": "https://launcher.mojang.com/v1/objects/dc52841c708e3c1eb2a044088a43396d1291bb5e/messages.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_de.properties": {"downloads": {"lzma": {"sha1": "42b42e6e1d2cb2d781f2226bde612ce519b29bc8", "size": 1394, "url": "https://launcher.mojang.com/v1/objects/42b42e6e1d2cb2d781f2226bde612ce519b29bc8/messages_de.properties"}, "raw": {"sha1": "d989fe1b8f7904888d5102294ebefd28d932ecdb", "size": 3306, "url": "https://launcher.mojang.com/v1/objects/d989fe1b8f7904888d5102294ebefd28d932ecdb/messages_de.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_es.properties": {"downloads": {"lzma": {"sha1": "c4a653e9802ca982e892b45d88c1e259c09e8c8e", "size": 1404, "url": "https://launcher.mojang.com/v1/objects/c4a653e9802ca982e892b45d88c1e259c09e8c8e/messages_es.properties"}, "raw": {"sha1": "1b0334b79db481c3a59be6915d5118d760c97baa", "size": 3600, "url": "https://launcher.mojang.com/v1/objects/1b0334b79db481c3a59be6915d5118d760c97baa/messages_es.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_fr.properties": {"downloads": {"lzma": {"sha1": "2d8dee07e3f5aab7318a22e169810b216ac44f97", "size": 1401, "url": "https://launcher.mojang.com/v1/objects/2d8dee07e3f5aab7318a22e169810b216ac44f97/messages_fr.properties"}, "raw": {"sha1": "69bd2d03c2064f8679de5b4e430ea61b567c69c5", "size": 3409, "url": "https://launcher.mojang.com/v1/objects/69bd2d03c2064f8679de5b4e430ea61b567c69c5/messages_fr.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_it.properties": {"downloads": {"lzma": {"sha1": "7c28cdd8d9e34355ba0fc03004c4f64749cae57e", "size": 1375, "url": "https://launcher.mojang.com/v1/objects/7c28cdd8d9e34355ba0fc03004c4f64749cae57e/messages_it.properties"}, "raw": {"sha1": "dbe49949308f28540a42ae6cd2ad58afbf615592", "size": 3223, "url": "https://launcher.mojang.com/v1/objects/dbe49949308f28540a42ae6cd2ad58afbf615592/messages_it.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_ja.properties": {"downloads": {"lzma": {"sha1": "9a6a4c086e48b9e615b72b6bbebb3c724d178ff4", "size": 1680, "url": "https://launcher.mojang.com/v1/objects/9a6a4c086e48b9e615b72b6bbebb3c724d178ff4/messages_ja.properties"}, "raw": {"sha1": "751170a7cdefcb1226604ac3f8196e06a04fd7ac", "size": 6349, "url": "https://launcher.mojang.com/v1/objects/751170a7cdefcb1226604ac3f8196e06a04fd7ac/messages_ja.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_ko.properties": {"downloads": {"lzma": {"sha1": "0c57c2ebfa0830f816657a384898487fc492efac", "size": 1645, "url": "https://launcher.mojang.com/v1/objects/0c57c2ebfa0830f816657a384898487fc492efac/messages_ko.properties"}, "raw": {"sha1": "bf9e055b5ab138ad6d49769e2b7630b7938848d6", "size": 5712, "url": "https://launcher.mojang.com/v1/objects/bf9e055b5ab138ad6d49769e2b7630b7938848d6/messages_ko.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_pt_BR.properties": {"downloads": {"lzma": {"sha1": "f8364dba0aa0a7e445a1a8d0e7ad66b996f70063", "size": 1388, "url": "https://launcher.mojang.com/v1/objects/f8364dba0aa0a7e445a1a8d0e7ad66b996f70063/messages_pt_BR.properties"}, "raw": {"sha1": "24e4951743521ab9a11381c77bd0cdb1ed30f5b5", "size": 3285, "url": "https://launcher.mojang.com/v1/objects/24e4951743521ab9a11381c77bd0cdb1ed30f5b5/messages_pt_BR.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_sv.properties": {"downloads": {"lzma": {"sha1": "65e5897d552258141aacf02f087c7c9c33ad0727", "size": 1355, "url": "https://launcher.mojang.com/v1/objects/65e5897d552258141aacf02f087c7c9c33ad0727/messages_sv.properties"}, "raw": {"sha1": "bb5a4aa0ba499f6b1916a83e3c7922a4583b4adb", "size": 3384, "url": "https://launcher.mojang.com/v1/objects/bb5a4aa0ba499f6b1916a83e3c7922a4583b4adb/messages_sv.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_zh_CN.properties": {"downloads": {"lzma": {"sha1": "de7d39a6e6748e9f47e842c9da90f515921c222c", "size": 1506, "url": "https://launcher.mojang.com/v1/objects/de7d39a6e6748e9f47e842c9da90f515921c222c/messages_zh_CN.properties"}, "raw": {"sha1": "1c2b96673dddd3596890ef4fc22017d484a1f652", "size": 4072, "url": "https://launcher.mojang.com/v1/objects/1c2b96673dddd3596890ef4fc22017d484a1f652/messages_zh_CN.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_zh_HK.properties": {"downloads": {"lzma": {"sha1": "e8d0e3a63caa2535a4f361033941f34dcc170a7e", "size": 1529, "url": "https://launcher.mojang.com/v1/objects/e8d0e3a63caa2535a4f361033941f34dcc170a7e/messages_zh_TW.properties"}, "raw": {"sha1": "37a57aad121c14c25e149206179728fa62203bf0", "size": 3752, "url": "https://launcher.mojang.com/v1/objects/37a57aad121c14c25e149206179728fa62203bf0/messages_zh_TW.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_zh_TW.properties": {"downloads": {"lzma": {"sha1": "e8d0e3a63caa2535a4f361033941f34dcc170a7e", "size": 1529, "url": "https://launcher.mojang.com/v1/objects/e8d0e3a63caa2535a4f361033941f34dcc170a7e/messages_zh_TW.properties"}, "raw": {"sha1": "37a57aad121c14c25e149206179728fa62203bf0", "size": 3752, "url": "https://launcher.mojang.com/v1/objects/37a57aad121c14c25e149206179728fa62203bf0/messages_zh_TW.properties"}}, "executable": false, "type": "file"}, "lib/deploy/mixcode_s.png": {"downloads": {"raw": {"sha1": "4604e9f265eec97bccd0151c3a81afa9e69499e5", "size": 3115, "url": "https://launcher.mojang.com/v1/objects/4604e9f265eec97bccd0151c3a81afa9e69499e5/mixcode_s.png"}}, "executable": false, "type": "file"}, "lib/deploy/splash.gif": {"downloads": {"raw": {"sha1": "20e7aec75f6d036d504277542e507eb7dc24aae8", "size": 8590, "url": "https://launcher.mojang.com/v1/objects/20e7aec75f6d036d504277542e507eb7dc24aae8/splash.gif"}}, "executable": false, "type": "file"}, "lib/deploy/splash@2x.gif": {"downloads": {"raw": {"sha1": "0ae4a5bda2a6d628fac51462390b503c99509fdc", "size": 15276, "url": "https://launcher.mojang.com/v1/objects/0ae4a5bda2a6d628fac51462390b503c99509fdc/splash2x.gif"}}, "executable": false, "type": "file"}, "lib/deploy/splash_11-lic.gif": {"downloads": {"raw": {"sha1": "8def364e07f40142822df84b5bb4f50846cb5e4e", "size": 7805, "url": "https://launcher.mojang.com/v1/objects/8def364e07f40142822df84b5bb4f50846cb5e4e/splash_11-lic.gif"}}, "executable": false, "type": "file"}, "lib/deploy/splash_11@2x-lic.gif": {"downloads": {"raw": {"sha1": "d2bff9bbf7920ca743b81a0ee23b0719b4d057ca", "size": 12250, "url": "https://launcher.mojang.com/v1/objects/d2bff9bbf7920ca743b81a0ee23b0719b4d057ca/splash_11%402x-lic.gif"}}, "executable": false, "type": "file"}, "lib/desktop": {"type": "directory"}, "lib/desktop/applications": {"type": "directory"}, "lib/desktop/applications/sun-java.desktop": {"downloads": {"lzma": {"sha1": "109d1cdf165f38da92da70b403ca940192a7a9a8", "size": 536, "url": "https://launcher.mojang.com/v1/objects/109d1cdf165f38da92da70b403ca940192a7a9a8/sun-java.desktop"}, "raw": {"sha1": "d346dfe90505603ce5aff5a3c6c2e4a23d5bd990", "size": 777, "url": "https://launcher.mojang.com/v1/objects/d346dfe90505603ce5aff5a3c6c2e4a23d5bd990/sun-java.desktop"}}, "executable": false, "type": "file"}, "lib/desktop/applications/sun-javaws.desktop": {"downloads": {"lzma": {"sha1": "5e1815e7f83515881e6998584dc6bb02c5bef09a", "size": 451, "url": "https://launcher.mojang.com/v1/objects/5e1815e7f83515881e6998584dc6bb02c5bef09a/sun-javaws.desktop"}, "raw": {"sha1": "50ce8e519b836e0f53d58ce1a359d98b6cafdda6", "size": 619, "url": "https://launcher.mojang.com/v1/objects/50ce8e519b836e0f53d58ce1a359d98b6cafdda6/sun-javaws.desktop"}}, "executable": false, "type": "file"}, "lib/desktop/applications/sun_java.desktop": {"downloads": {"lzma": {"sha1": "49ab0ccb54c3be68281d05055bc56a88b1281d3c", "size": 447, "url": "https://launcher.mojang.com/v1/objects/49ab0ccb54c3be68281d05055bc56a88b1281d3c/sun_java.desktop"}, "raw": {"sha1": "79120ee8160ad6f3c9b90c2641fb7edf3af96b5d", "size": 624, "url": "https://launcher.mojang.com/v1/objects/79120ee8160ad6f3c9b90c2641fb7edf3af96b5d/sun_java.desktop"}}, "executable": false, "type": "file"}, "lib/desktop/icons": {"type": "directory"}, "lib/desktop/icons/HighContrast": {"type": "directory"}, "lib/desktop/icons/HighContrast/16x16": {"type": "directory"}, "lib/desktop/icons/HighContrast/16x16/apps": {"type": "directory"}, "lib/desktop/icons/HighContrast/16x16/apps/sun-java.png": {"downloads": {"raw": {"sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", "size": 417, "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", "size": 417, "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", "size": 417, "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/mimetypes": {"type": "directory"}, "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "629c48907368ecf32d2395b6459c367f79d84689", "size": 464, "url": "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "629c48907368ecf32d2395b6459c367f79d84689", "size": 464, "url": "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "629c48907368ecf32d2395b6459c367f79d84689", "size": 464, "url": "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48": {"type": "directory"}, "lib/desktop/icons/HighContrast/48x48/apps": {"type": "directory"}, "lib/desktop/icons/HighContrast/48x48/apps/sun-java.png": {"downloads": {"raw": {"sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", "size": 3451, "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", "size": 3451, "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", "size": 3451, "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/mimetypes": {"type": "directory"}, "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/16x16": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/16x16/apps": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-java.png": {"downloads": {"raw": {"sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", "size": 402, "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", "size": 402, "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", "size": 402, "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "1477eceda25e162fcda2e69ee3906091973d8344", "size": 473, "url": "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "1477eceda25e162fcda2e69ee3906091973d8344", "size": 473, "url": "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "1477eceda25e162fcda2e69ee3906091973d8344", "size": 473, "url": "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/48x48/apps": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-java.png": {"downloads": {"raw": {"sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", "size": 3410, "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", "size": 3410, "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", "size": 3410, "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "d66e04dfa25c196bec2e201547325b79846ab674", "size": 2085, "url": "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "d66e04dfa25c196bec2e201547325b79846ab674", "size": 2085, "url": "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "d66e04dfa25c196bec2e201547325b79846ab674", "size": 2085, "url": "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast": {"type": "directory"}, "lib/desktop/icons/LowContrast/16x16": {"type": "directory"}, "lib/desktop/icons/LowContrast/16x16/apps": {"type": "directory"}, "lib/desktop/icons/LowContrast/16x16/apps/sun-java.png": {"downloads": {"raw": {"sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", "size": 519, "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", "size": 519, "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", "size": 519, "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/mimetypes": {"type": "directory"}, "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", "size": 525, "url": "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", "size": 525, "url": "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", "size": 525, "url": "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48": {"type": "directory"}, "lib/desktop/icons/LowContrast/48x48/apps": {"type": "directory"}, "lib/desktop/icons/LowContrast/48x48/apps/sun-java.png": {"downloads": {"raw": {"sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", "size": 1507, "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", "size": 1507, "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", "size": 1507, "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/mimetypes": {"type": "directory"}, "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", "size": 1948, "url": "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", "size": 1948, "url": "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", "size": 1948, "url": "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor": {"type": "directory"}, "lib/desktop/icons/hicolor/16x16": {"type": "directory"}, "lib/desktop/icons/hicolor/16x16/apps": {"type": "directory"}, "lib/desktop/icons/hicolor/16x16/apps/sun-java.png": {"downloads": {"raw": {"sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", "size": 383, "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", "size": 383, "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", "size": 383, "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/mimetypes": {"type": "directory"}, "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", "size": 783, "url": "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", "size": 783, "url": "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", "size": 783, "url": "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48": {"type": "directory"}, "lib/desktop/icons/hicolor/48x48/apps": {"type": "directory"}, "lib/desktop/icons/hicolor/48x48/apps/sun-java.png": {"downloads": {"raw": {"sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", "size": 1439, "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", "size": 1439, "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", "size": 1439, "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/mimetypes": {"type": "directory"}, "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", "size": 3202, "url": "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", "size": 3202, "url": "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", "size": 3202, "url": "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/mime": {"type": "directory"}, "lib/desktop/mime/packages": {"type": "directory"}, "lib/desktop/mime/packages/x-java-archive.xml": {"downloads": {"lzma": {"sha1": "841230729f0a59de2a1071d155d96358232b2ba1", "size": 591, "url": "https://launcher.mojang.com/v1/objects/841230729f0a59de2a1071d155d96358232b2ba1/x-java-archive.xml"}, "raw": {"sha1": "b6297fd36efa799312961f95ebf0c85c920d5037", "size": 1822, "url": "https://launcher.mojang.com/v1/objects/b6297fd36efa799312961f95ebf0c85c920d5037/x-java-archive.xml"}}, "executable": false, "type": "file"}, "lib/desktop/mime/packages/x-java-jnlp-file.xml": {"downloads": {"lzma": {"sha1": "abf9acbe7c18027c4f036c4e42bb2cf1115525fa", "size": 302, "url": "https://launcher.mojang.com/v1/objects/abf9acbe7c18027c4f036c4e42bb2cf1115525fa/x-java-jnlp-file.xml"}, "raw": {"sha1": "72f03da83ddb76c9105f619fcfa4dbdad70e6b30", "size": 412, "url": "https://launcher.mojang.com/v1/objects/72f03da83ddb76c9105f619fcfa4dbdad70e6b30/x-java-jnlp-file.xml"}}, "executable": false, "type": "file"}, "lib/ext": {"type": "directory"}, "lib/ext/cldrdata.jar": {"downloads": {"raw": {"sha1": "6cacc961942d3f02a88907aa8f2eaae8e20c95a0", "size": 3860502, "url": "https://launcher.mojang.com/v1/objects/6cacc961942d3f02a88907aa8f2eaae8e20c95a0/cldrdata.jar"}}, "executable": false, "type": "file"}, "lib/ext/dnsns.jar": {"downloads": {"raw": {"sha1": "93bebdd7514e53ae31d60c5daba673878c8822ec", "size": 8286, "url": "https://launcher.mojang.com/v1/objects/93bebdd7514e53ae31d60c5daba673878c8822ec/dnsns.jar"}}, "executable": false, "type": "file"}, "lib/ext/jaccess.jar": {"downloads": {"raw": {"sha1": "2f54879df7c29ec67c40d40cfc95c0d4a968bea1", "size": 44516, "url": "https://launcher.mojang.com/v1/objects/2f54879df7c29ec67c40d40cfc95c0d4a968bea1/jaccess.jar"}}, "executable": false, "type": "file"}, "lib/ext/jfxrt.jar": {"downloads": {"lzma": {"sha1": "a6c5b6a782ba360ada6651f5322dcab88c75711c", "size": 3374270, "url": "https://launcher.mojang.com/v1/objects/a6c5b6a782ba360ada6651f5322dcab88c75711c/jfxrt.jar"}, "raw": {"sha1": "1ad7a876f045399c23ee4ab7dee380a04ca2ac08", "size": 18508094, "url": "https://launcher.mojang.com/v1/objects/1ad7a876f045399c23ee4ab7dee380a04ca2ac08/jfxrt.jar"}}, "executable": false, "type": "file"}, "lib/ext/localedata.jar": {"downloads": {"raw": {"sha1": "0cc9f550d4e410b5aa29dbfd2c1b5c99391c7f70", "size": 1178926, "url": "https://launcher.mojang.com/v1/objects/0cc9f550d4e410b5aa29dbfd2c1b5c99391c7f70/localedata.jar"}}, "executable": false, "type": "file"}, "lib/ext/meta-index": {"downloads": {"lzma": {"sha1": "1359457529f42bacf495afcb68149ae036442dd9", "size": 594, "url": "https://launcher.mojang.com/v1/objects/1359457529f42bacf495afcb68149ae036442dd9/meta-index"}, "raw": {"sha1": "334649c6e2d5d7248211f30855e97cfcb4558851", "size": 1269, "url": "https://launcher.mojang.com/v1/objects/334649c6e2d5d7248211f30855e97cfcb4558851/meta-index"}}, "executable": false, "type": "file"}, "lib/ext/nashorn.jar": {"downloads": {"raw": {"sha1": "dec5dd17a0f52ae79dfbfb38840bffb8b7a679a5", "size": 2023869, "url": "https://launcher.mojang.com/v1/objects/dec5dd17a0f52ae79dfbfb38840bffb8b7a679a5/nashorn.jar"}}, "executable": false, "type": "file"}, "lib/ext/sunec.jar": {"downloads": {"raw": {"sha1": "bf1c817820341a246f7130fe046e8310b03d04f6", "size": 41672, "url": "https://launcher.mojang.com/v1/objects/bf1c817820341a246f7130fe046e8310b03d04f6/sunec.jar"}}, "executable": false, "type": "file"}, "lib/ext/sunjce_provider.jar": {"downloads": {"raw": {"sha1": "bb3494e4b5cb3c3e60da767207731f18b267cb34", "size": 279326, "url": "https://launcher.mojang.com/v1/objects/bb3494e4b5cb3c3e60da767207731f18b267cb34/sunjce_provider.jar"}}, "executable": false, "type": "file"}, "lib/ext/sunpkcs11.jar": {"downloads": {"raw": {"sha1": "5bb1dedc3344cd3bb86828d4aa8ca82f4a606ed4", "size": 250218, "url": "https://launcher.mojang.com/v1/objects/5bb1dedc3344cd3bb86828d4aa8ca82f4a606ed4/sunpkcs11.jar"}}, "executable": false, "type": "file"}, "lib/ext/zipfs.jar": {"downloads": {"raw": {"sha1": "37b338f0e8e60d6396f51275130e8110816d7b30", "size": 68964, "url": "https://launcher.mojang.com/v1/objects/37b338f0e8e60d6396f51275130e8110816d7b30/zipfs.jar"}}, "executable": false, "type": "file"}, "lib/flavormap.properties": {"downloads": {"lzma": {"sha1": "2d5ef19ee77ccfc95c9413eea155cde59a48fadd", "size": 1541, "url": "https://launcher.mojang.com/v1/objects/2d5ef19ee77ccfc95c9413eea155cde59a48fadd/flavormap.properties"}, "raw": {"sha1": "4e66e8fe12d7f8b3b0c4e1a1915f329bb1fbf6d2", "size": 3901, "url": "https://launcher.mojang.com/v1/objects/4e66e8fe12d7f8b3b0c4e1a1915f329bb1fbf6d2/flavormap.properties"}}, "executable": false, "type": "file"}, "lib/fontconfig.RedHat.5.bfc": {"downloads": {"lzma": {"sha1": "5197f6e387f16458b7408134e38b06f20f625e4c", "size": 795, "url": "https://launcher.mojang.com/v1/objects/5197f6e387f16458b7408134e38b06f20f625e4c/fontconfig.RedHat.5.bfc"}, "raw": {"sha1": "fb806ada6e68f16a9fe2b726a39d9ef5a835c0c2", "size": 4532, "url": "https://launcher.mojang.com/v1/objects/fb806ada6e68f16a9fe2b726a39d9ef5a835c0c2/fontconfig.RedHat.5.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.RedHat.5.properties.src": {"downloads": {"lzma": {"sha1": "3897ae198e96e5a687c9c9b218ff5df60c868e0d", "size": 1089, "url": "https://launcher.mojang.com/v1/objects/3897ae198e96e5a687c9c9b218ff5df60c868e0d/fontconfig.RedHat.5.properties.src"}, "raw": {"sha1": "c67d1a06cb37b66e69560c9f5e4be7cf08af0493", "size": 8841, "url": "https://launcher.mojang.com/v1/objects/c67d1a06cb37b66e69560c9f5e4be7cf08af0493/fontconfig.RedHat.5.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.RedHat.6.bfc": {"downloads": {"lzma": {"sha1": "ef2f5d1f8d620be9927db45d3a28bd75777245cb", "size": 818, "url": "https://launcher.mojang.com/v1/objects/ef2f5d1f8d620be9927db45d3a28bd75777245cb/fontconfig.RedHat.6.bfc"}, "raw": {"sha1": "9ba3b3e2c621c31d0ef1b2053c80f77419a19965", "size": 4250, "url": "https://launcher.mojang.com/v1/objects/9ba3b3e2c621c31d0ef1b2053c80f77419a19965/fontconfig.RedHat.6.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.RedHat.6.properties.src": {"downloads": {"lzma": {"sha1": "74f4148f9d7ec3d67bbd724834d478a72cfdb0db", "size": 1111, "url": "https://launcher.mojang.com/v1/objects/74f4148f9d7ec3d67bbd724834d478a72cfdb0db/fontconfig.RedHat.6.properties.src"}, "raw": {"sha1": "768e58d4d314621c38daf9fde6d67119f370acd9", "size": 8735, "url": "https://launcher.mojang.com/v1/objects/768e58d4d314621c38daf9fde6d67119f370acd9/fontconfig.RedHat.6.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.SuSE.10.bfc": {"downloads": {"lzma": {"sha1": "d8fe9b1d8d02368dcd452de93024c6f60670eb87", "size": 1083, "url": "https://launcher.mojang.com/v1/objects/d8fe9b1d8d02368dcd452de93024c6f60670eb87/fontconfig.SuSE.10.bfc"}, "raw": {"sha1": "ffd0dfbd1553e15b11649a73a0b3f48318138e0d", "size": 6702, "url": "https://launcher.mojang.com/v1/objects/ffd0dfbd1553e15b11649a73a0b3f48318138e0d/fontconfig.SuSE.10.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.SuSE.10.properties.src": {"downloads": {"lzma": {"sha1": "2c382bd741a9e23114be3da82dee8290ebfca8a9", "size": 1555, "url": "https://launcher.mojang.com/v1/objects/2c382bd741a9e23114be3da82dee8290ebfca8a9/fontconfig.SuSE.10.properties.src"}, "raw": {"sha1": "a38dbdbbc514567b8281e1aea726865f37e97894", "size": 16772, "url": "https://launcher.mojang.com/v1/objects/a38dbdbbc514567b8281e1aea726865f37e97894/fontconfig.SuSE.10.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.SuSE.11.bfc": {"downloads": {"lzma": {"sha1": "2b78cbf11289c9858951fea7180696ba3b7176d6", "size": 1092, "url": "https://launcher.mojang.com/v1/objects/2b78cbf11289c9858951fea7180696ba3b7176d6/fontconfig.SuSE.11.bfc"}, "raw": {"sha1": "a4d8500fcb47f6327460a95851b1368660da8302", "size": 7032, "url": "https://launcher.mojang.com/v1/objects/a4d8500fcb47f6327460a95851b1368660da8302/fontconfig.SuSE.11.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.SuSE.11.properties.src": {"downloads": {"lzma": {"sha1": "5c1635803906e2c59d36492dec724dd7ae49a5ab", "size": 1589, "url": "https://launcher.mojang.com/v1/objects/5c1635803906e2c59d36492dec724dd7ae49a5ab/fontconfig.SuSE.11.properties.src"}, "raw": {"sha1": "c4b69589e41a7279a71866a9134213be19cdf88d", "size": 16781, "url": "https://launcher.mojang.com/v1/objects/c4b69589e41a7279a71866a9134213be19cdf88d/fontconfig.SuSE.11.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.Turbo.bfc": {"downloads": {"lzma": {"sha1": "1c771325d9ee4af209a3db92294451d58962c7a4", "size": 822, "url": "https://launcher.mojang.com/v1/objects/1c771325d9ee4af209a3db92294451d58962c7a4/fontconfig.Turbo.bfc"}, "raw": {"sha1": "f24368deeb85cc9d0781083bc56e773518d72219", "size": 4668, "url": "https://launcher.mojang.com/v1/objects/f24368deeb85cc9d0781083bc56e773518d72219/fontconfig.Turbo.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.Turbo.properties.src": {"downloads": {"lzma": {"sha1": "7748ffa17e2c8a34754138efa963ba39bd1cbbb3", "size": 1113, "url": "https://launcher.mojang.com/v1/objects/7748ffa17e2c8a34754138efa963ba39bd1cbbb3/fontconfig.Turbo.properties.src"}, "raw": {"sha1": "2bb7258bed7ccd4f117e4e5f892c9b13424b0c82", "size": 9192, "url": "https://launcher.mojang.com/v1/objects/2bb7258bed7ccd4f117e4e5f892c9b13424b0c82/fontconfig.Turbo.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.bfc": {"downloads": {"lzma": {"sha1": "be6d49ee8c64f458c4f0e64254963fec48d25150", "size": 286, "url": "https://launcher.mojang.com/v1/objects/be6d49ee8c64f458c4f0e64254963fec48d25150/fontconfig.bfc"}, "raw": {"sha1": "de39b0e19637f58d92a0188122514aa7247ebb5b", "size": 1678, "url": "https://launcher.mojang.com/v1/objects/de39b0e19637f58d92a0188122514aa7247ebb5b/fontconfig.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.properties.src": {"downloads": {"lzma": {"sha1": "9498d5e00e5401200667687e826e28c60fa60ba4", "size": 417, "url": "https://launcher.mojang.com/v1/objects/9498d5e00e5401200667687e826e28c60fa60ba4/fontconfig.properties.src"}, "raw": {"sha1": "3617ff1424fd204415242565541facf862b16eb4", "size": 1938, "url": "https://launcher.mojang.com/v1/objects/3617ff1424fd204415242565541facf862b16eb4/fontconfig.properties.src"}}, "executable": false, "type": "file"}, "lib/fonts": {"type": "directory"}, "lib/fonts/LucidaBrightDemiBold.ttf": {"downloads": {"lzma": {"sha1": "4f748750831a7719440dff5457f4d207d0f24d21", "size": 42347, "url": "https://launcher.mojang.com/v1/objects/4f748750831a7719440dff5457f4d207d0f24d21/LucidaBrightDemiBold.ttf"}, "raw": {"sha1": "b5c97f985639e19a3b712193ee48b55dda581fd1", "size": 75144, "url": "https://launcher.mojang.com/v1/objects/b5c97f985639e19a3b712193ee48b55dda581fd1/LucidaBrightDemiBold.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaBrightDemiItalic.ttf": {"downloads": {"lzma": {"sha1": "f82e9a688553c100ecb98412b985807ed56dff5d", "size": 43119, "url": "https://launcher.mojang.com/v1/objects/f82e9a688553c100ecb98412b985807ed56dff5d/LucidaBrightDemiItalic.ttf"}, "raw": {"sha1": "1fd1f757febf3e5f5fbb7fbf7a56587a40d57de7", "size": 75124, "url": "https://launcher.mojang.com/v1/objects/1fd1f757febf3e5f5fbb7fbf7a56587a40d57de7/LucidaBrightDemiItalic.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaBrightItalic.ttf": {"downloads": {"lzma": {"sha1": "6d630df719271319c3d53f90a3d425118b908266", "size": 46206, "url": "https://launcher.mojang.com/v1/objects/6d630df719271319c3d53f90a3d425118b908266/LucidaBrightItalic.ttf"}, "raw": {"sha1": "aa5c037865c563726ecd63d61ca26443589be425", "size": 80856, "url": "https://launcher.mojang.com/v1/objects/aa5c037865c563726ecd63d61ca26443589be425/LucidaBrightItalic.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaBrightRegular.ttf": {"downloads": {"lzma": {"sha1": "4b2e31aaec2238b6ecf9f845bad0a1c6d09fbbfe", "size": 181085, "url": "https://launcher.mojang.com/v1/objects/4b2e31aaec2238b6ecf9f845bad0a1c6d09fbbfe/LucidaBrightRegular.ttf"}, "raw": {"sha1": "5d7ed564791c900a8786936930ba99385653139c", "size": 344908, "url": "https://launcher.mojang.com/v1/objects/5d7ed564791c900a8786936930ba99385653139c/LucidaBrightRegular.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaSansDemiBold.ttf": {"downloads": {"lzma": {"sha1": "079b16dc3c4918ab1f4f760b6dc5e6586c219042", "size": 173229, "url": "https://launcher.mojang.com/v1/objects/079b16dc3c4918ab1f4f760b6dc5e6586c219042/LucidaSansDemiBold.ttf"}, "raw": {"sha1": "92b79fefc35e96190250c602a8fed85276b32a95", "size": 317896, "url": "https://launcher.mojang.com/v1/objects/92b79fefc35e96190250c602a8fed85276b32a95/LucidaSansDemiBold.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaSansRegular.ttf": {"downloads": {"lzma": {"sha1": "64a65d7b94d7153d20957ef6d06bebb4dd0f48e4", "size": 326062, "url": "https://launcher.mojang.com/v1/objects/64a65d7b94d7153d20957ef6d06bebb4dd0f48e4/LucidaSansRegular.ttf"}, "raw": {"sha1": "39cc8bcb8d4a71d4657fc92ef0b9f4e3e9e67add", "size": 698236, "url": "https://launcher.mojang.com/v1/objects/39cc8bcb8d4a71d4657fc92ef0b9f4e3e9e67add/LucidaSansRegular.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaTypewriterBold.ttf": {"downloads": {"lzma": {"sha1": "cdb017f7c34bea0802bc5ea5583aef721ed99c49", "size": 130412, "url": "https://launcher.mojang.com/v1/objects/cdb017f7c34bea0802bc5ea5583aef721ed99c49/LucidaTypewriterBold.ttf"}, "raw": {"sha1": "a5da2eb49448f461470387c939f0e69119310e0b", "size": 234068, "url": "https://launcher.mojang.com/v1/objects/a5da2eb49448f461470387c939f0e69119310e0b/LucidaTypewriterBold.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaTypewriterRegular.ttf": {"downloads": {"lzma": {"sha1": "aeda4a09a53783b0dc97de8e20071bea874cbfe5", "size": 135184, "url": "https://launcher.mojang.com/v1/objects/aeda4a09a53783b0dc97de8e20071bea874cbfe5/LucidaTypewriterRegular.ttf"}, "raw": {"sha1": "c144dcafe4faf2e79cfd74d8134a631f30234db1", "size": 242700, "url": "https://launcher.mojang.com/v1/objects/c144dcafe4faf2e79cfd74d8134a631f30234db1/LucidaTypewriterRegular.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/fonts.dir": {"downloads": {"lzma": {"sha1": "68f2dd93b215ec8b8d9409d2b9c825632c6b907d", "size": 273, "url": "https://launcher.mojang.com/v1/objects/68f2dd93b215ec8b8d9409d2b9c825632c6b907d/fonts.dir"}, "raw": {"sha1": "97f40cca185c954adf5cc582345a7cb8e4c50578", "size": 4041, "url": "https://launcher.mojang.com/v1/objects/97f40cca185c954adf5cc582345a7cb8e4c50578/fonts.dir"}}, "executable": false, "type": "file"}, "lib/hijrah-config-umalqura.properties": {"downloads": {"lzma": {"sha1": "02e8d296e3b18a450f1ed1547cbf2b7275664c9a", "size": 1969, "url": "https://launcher.mojang.com/v1/objects/02e8d296e3b18a450f1ed1547cbf2b7275664c9a/hijrah-config-umalqura.properties"}, "raw": {"sha1": "84aa425100740722e91f4725caf849e7863d12ba", "size": 13962, "url": "https://launcher.mojang.com/v1/objects/84aa425100740722e91f4725caf849e7863d12ba/hijrah-config-umalqura.properties"}}, "executable": false, "type": "file"}, "lib/images": {"type": "directory"}, "lib/images/cursors": {"type": "directory"}, "lib/images/cursors/cursors.properties": {"downloads": {"lzma": {"sha1": "612bd0f610ee1023947c4a2a8d3fc7d6f97e7d8f", "size": 385, "url": "https://launcher.mojang.com/v1/objects/612bd0f610ee1023947c4a2a8d3fc7d6f97e7d8f/cursors.properties"}, "raw": {"sha1": "f2b9a22ddd0a77869497a64f28f07e89a7d41f48", "size": 1274, "url": "https://launcher.mojang.com/v1/objects/f2b9a22ddd0a77869497a64f28f07e89a7d41f48/cursors.properties"}}, "executable": false, "type": "file"}, "lib/images/cursors/invalid32x32.gif": {"downloads": {"raw": {"sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_CopyDrop32x32.gif": {"downloads": {"raw": {"sha1": "eb7620fae702172aa663a19d170a0b929d3b11d1", "size": 158, "url": "https://launcher.mojang.com/v1/objects/eb7620fae702172aa663a19d170a0b929d3b11d1/motif_CopyDrop32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_CopyNoDrop32x32.gif": {"downloads": {"raw": {"sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_LinkDrop32x32.gif": {"downloads": {"raw": {"sha1": "9699137f990c240e714481563181069c8f6c17bb", "size": 162, "url": "https://launcher.mojang.com/v1/objects/9699137f990c240e714481563181069c8f6c17bb/motif_LinkDrop32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_LinkNoDrop32x32.gif": {"downloads": {"raw": {"sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_MoveDrop32x32.gif": {"downloads": {"raw": {"sha1": "03c1617ce3c5ab8af03e46d30a8c8f31ab57fb1b", "size": 141, "url": "https://launcher.mojang.com/v1/objects/03c1617ce3c5ab8af03e46d30a8c8f31ab57fb1b/motif_MoveDrop32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_MoveNoDrop32x32.gif": {"downloads": {"raw": {"sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/icons": {"type": "directory"}, "lib/images/icons/sun-java.png": {"downloads": {"raw": {"sha1": "d101b693aa054f51097eebdfeed8b8a6ca7b55b8", "size": 4707, "url": "https://launcher.mojang.com/v1/objects/d101b693aa054f51097eebdfeed8b8a6ca7b55b8/sun-java.png"}}, "executable": false, "type": "file"}, "lib/images/icons/sun-java_HighContrast.png": {"downloads": {"raw": {"sha1": "a6b1e418d6b5d03719b96f61f0c5236a60970151", "size": 3729, "url": "https://launcher.mojang.com/v1/objects/a6b1e418d6b5d03719b96f61f0c5236a60970151/sun-java_HighContrast.png"}}, "executable": false, "type": "file"}, "lib/images/icons/sun-java_HighContrastInverse.png": {"downloads": {"raw": {"sha1": "2dda28b9bddc9b5b018e3e8a8b062a99d9b2f887", "size": 3777, "url": "https://launcher.mojang.com/v1/objects/2dda28b9bddc9b5b018e3e8a8b062a99d9b2f887/sun-java_HighContrastInverse.png"}}, "executable": false, "type": "file"}, "lib/images/icons/sun-java_LowContrast.png": {"downloads": {"raw": {"sha1": "7714cc4e894c3626c8da6fe742ed22b2829122d9", "size": 4012, "url": "https://launcher.mojang.com/v1/objects/7714cc4e894c3626c8da6fe742ed22b2829122d9/sun-java_LowContrast.png"}}, "executable": false, "type": "file"}, "lib/javafx.properties": {"downloads": {"raw": {"sha1": "49e6b75d109e5fd3f6cbe7cc5fa9a7980796d14d", "size": 56, "url": "https://launcher.mojang.com/v1/objects/49e6b75d109e5fd3f6cbe7cc5fa9a7980796d14d/javafx.properties"}}, "executable": false, "type": "file"}, "lib/javaws.jar": {"downloads": {"raw": {"sha1": "04fa5ae04ead65b91be5dee575497e49ffd49fe9", "size": 488118, "url": "https://launcher.mojang.com/v1/objects/04fa5ae04ead65b91be5dee575497e49ffd49fe9/javaws.jar"}}, "executable": false, "type": "file"}, "lib/jce.jar": {"downloads": {"raw": {"sha1": "5460adee09cc5fc8829c0acfc46c34670a7d70a0", "size": 115646, "url": "https://launcher.mojang.com/v1/objects/5460adee09cc5fc8829c0acfc46c34670a7d70a0/jce.jar"}}, "executable": false, "type": "file"}, "lib/jexec": {"downloads": {"lzma": {"sha1": "2d4323d4e060f8126d026ca6c03b8972aedd2fab", "size": 3311, "url": "https://launcher.mojang.com/v1/objects/2d4323d4e060f8126d026ca6c03b8972aedd2fab/jexec"}, "raw": {"sha1": "6aa01f1d8d103974164bcfaea03c04eeeefd7d41", "size": 13376, "url": "https://launcher.mojang.com/v1/objects/6aa01f1d8d103974164bcfaea03c04eeeefd7d41/jexec"}}, "executable": true, "type": "file"}, "lib/jfr": {"type": "directory"}, "lib/jfr.jar": {"downloads": {"lzma": {"sha1": "5b9d615c91c72f4fe356d9b4105946679452d1e1", "size": 137982, "url": "https://launcher.mojang.com/v1/objects/5b9d615c91c72f4fe356d9b4105946679452d1e1/jfr.jar"}, "raw": {"sha1": "0f3fd66a336703d935bdc22ad8082bc51d34e534", "size": 560713, "url": "https://launcher.mojang.com/v1/objects/0f3fd66a336703d935bdc22ad8082bc51d34e534/jfr.jar"}}, "executable": false, "type": "file"}, "lib/jfr/default.jfc": {"downloads": {"lzma": {"sha1": "373ddd878146dd8ce8991c2c5115a05a82859bdb", "size": 2207, "url": "https://launcher.mojang.com/v1/objects/373ddd878146dd8ce8991c2c5115a05a82859bdb/default.jfc"}, "raw": {"sha1": "1a64b68d0e7d43f8149faba94440be54f4f24527", "size": 20109, "url": "https://launcher.mojang.com/v1/objects/1a64b68d0e7d43f8149faba94440be54f4f24527/default.jfc"}}, "executable": false, "type": "file"}, "lib/jfr/profile.jfc": {"downloads": {"lzma": {"sha1": "3dcdc5feee3ccfb66bc8726b666944cd4bdadae3", "size": 2199, "url": "https://launcher.mojang.com/v1/objects/3dcdc5feee3ccfb66bc8726b666944cd4bdadae3/profile.jfc"}, "raw": {"sha1": "5d7d08a595f76322c51ae43ea966fbba6b69eebe", "size": 20065, "url": "https://launcher.mojang.com/v1/objects/5d7d08a595f76322c51ae43ea966fbba6b69eebe/profile.jfc"}}, "executable": false, "type": "file"}, "lib/jfxswt.jar": {"downloads": {"raw": {"sha1": "99d9a264c898d84c01e1c42565e7fe1a89dcd72d", "size": 33932, "url": "https://launcher.mojang.com/v1/objects/99d9a264c898d84c01e1c42565e7fe1a89dcd72d/jfxswt.jar"}}, "executable": false, "type": "file"}, "lib/jsse.jar": {"downloads": {"lzma": {"sha1": "94a17dfbc2e76cd12c33970a15341424f875a9ce", "size": 187549, "url": "https://launcher.mojang.com/v1/objects/94a17dfbc2e76cd12c33970a15341424f875a9ce/jsse.jar"}, "raw": {"sha1": "92c5c626e8a2d16f41272c0e404d4f992dd8310a", "size": 675599, "url": "https://launcher.mojang.com/v1/objects/92c5c626e8a2d16f41272c0e404d4f992dd8310a/jsse.jar"}}, "executable": false, "type": "file"}, "lib/jvm.hprof.txt": {"downloads": {"lzma": {"sha1": "eccdb240a815b2a83a502749339b27bb8669965b", "size": 1863, "url": "https://launcher.mojang.com/v1/objects/eccdb240a815b2a83a502749339b27bb8669965b/jvm.hprof.txt"}, "raw": {"sha1": "fbd61d52534cdd0c15df332114d469c65d001e33", "size": 4226, "url": "https://launcher.mojang.com/v1/objects/fbd61d52534cdd0c15df332114d469c65d001e33/jvm.hprof.txt"}}, "executable": false, "type": "file"}, "lib/locale": {"type": "directory"}, "lib/locale/de": {"type": "directory"}, "lib/locale/de/LC_MESSAGES": {"type": "directory"}, "lib/locale/de/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "3061d922907cc557208109088fc6ab81d577ff6f", "size": 970, "url": "https://launcher.mojang.com/v1/objects/3061d922907cc557208109088fc6ab81d577ff6f/sunw_java_plugin.mo"}, "raw": {"sha1": "5b223a3d723ac1cfce63623fb109f2868d47d1b7", "size": 2483, "url": "https://launcher.mojang.com/v1/objects/5b223a3d723ac1cfce63623fb109f2868d47d1b7/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/es": {"type": "directory"}, "lib/locale/es/LC_MESSAGES": {"type": "directory"}, "lib/locale/es/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "24338049a89b323e17182b3a3006b50565d4fa0f", "size": 979, "url": "https://launcher.mojang.com/v1/objects/24338049a89b323e17182b3a3006b50565d4fa0f/sunw_java_plugin.mo"}, "raw": {"sha1": "6cc63dc97f2fdb2ed799e48b1dc98c4f37cdecc1", "size": 2477, "url": "https://launcher.mojang.com/v1/objects/6cc63dc97f2fdb2ed799e48b1dc98c4f37cdecc1/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/fr": {"type": "directory"}, "lib/locale/fr/LC_MESSAGES": {"type": "directory"}, "lib/locale/fr/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "22796a48ef39f57d2d6fa70f41308e493d7f05c1", "size": 1033, "url": "https://launcher.mojang.com/v1/objects/22796a48ef39f57d2d6fa70f41308e493d7f05c1/sunw_java_plugin.mo"}, "raw": {"sha1": "d9d5b458db6e83fdf85c3526aeee3f57c4929840", "size": 2746, "url": "https://launcher.mojang.com/v1/objects/d9d5b458db6e83fdf85c3526aeee3f57c4929840/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/it": {"type": "directory"}, "lib/locale/it/LC_MESSAGES": {"type": "directory"}, "lib/locale/it/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "59a4cae38bfb8927745674d0efc2f284bc277987", "size": 958, "url": "https://launcher.mojang.com/v1/objects/59a4cae38bfb8927745674d0efc2f284bc277987/sunw_java_plugin.mo"}, "raw": {"sha1": "f6e72e3b2141ccc3dffab10ae14a754e494577ba", "size": 2434, "url": "https://launcher.mojang.com/v1/objects/f6e72e3b2141ccc3dffab10ae14a754e494577ba/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/ja": {"type": "directory"}, "lib/locale/ja/LC_MESSAGES": {"type": "directory"}, "lib/locale/ja/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "7d6aeed563e1cefcf0224cf522048468088884a9", "size": 1036, "url": "https://launcher.mojang.com/v1/objects/7d6aeed563e1cefcf0224cf522048468088884a9/sunw_java_plugin.mo"}, "raw": {"sha1": "378881a8cb8dd2aebb43eacd0c68519be4f258b1", "size": 2415, "url": "https://launcher.mojang.com/v1/objects/378881a8cb8dd2aebb43eacd0c68519be4f258b1/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/ko": {"type": "directory"}, "lib/locale/ko.UTF-8": {"type": "directory"}, "lib/locale/ko.UTF-8/LC_MESSAGES": {"type": "directory"}, "lib/locale/ko.UTF-8/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "12ee3b21511e8497d95ea0ba9d6fe519227d0b16", "size": 1069, "url": "https://launcher.mojang.com/v1/objects/12ee3b21511e8497d95ea0ba9d6fe519227d0b16/sunw_java_plugin.mo"}, "raw": {"sha1": "cb19df01c59662dbe2f4050b1290d374b82fe1fa", "size": 2753, "url": "https://launcher.mojang.com/v1/objects/cb19df01c59662dbe2f4050b1290d374b82fe1fa/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/ko/LC_MESSAGES": {"type": "directory"}, "lib/locale/ko/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "6e2e47c64c360517fd436bc79c823b5679a1efe6", "size": 996, "url": "https://launcher.mojang.com/v1/objects/6e2e47c64c360517fd436bc79c823b5679a1efe6/sunw_java_plugin.mo"}, "raw": {"sha1": "12c8a118d150c78f719314df6dec49a967af71e9", "size": 2399, "url": "https://launcher.mojang.com/v1/objects/12c8a118d150c78f719314df6dec49a967af71e9/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/pt_BR": {"type": "directory"}, "lib/locale/pt_BR/LC_MESSAGES": {"type": "directory"}, "lib/locale/pt_BR/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "bcaa7e7916493f071f1bf64bf58c6b038e3569c9", "size": 940, "url": "https://launcher.mojang.com/v1/objects/bcaa7e7916493f071f1bf64bf58c6b038e3569c9/sunw_java_plugin.mo"}, "raw": {"sha1": "a3bc0c43994c53c59bba94982cf95f6d36283dd0", "size": 2420, "url": "https://launcher.mojang.com/v1/objects/a3bc0c43994c53c59bba94982cf95f6d36283dd0/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/sv": {"type": "directory"}, "lib/locale/sv/LC_MESSAGES": {"type": "directory"}, "lib/locale/sv/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "76017835d6261fe2eedbcbe5eb08a7484c3080c5", "size": 946, "url": "https://launcher.mojang.com/v1/objects/76017835d6261fe2eedbcbe5eb08a7484c3080c5/sunw_java_plugin.mo"}, "raw": {"sha1": "09a47686edec4bbb34e82fbd08559f8bb6266544", "size": 2359, "url": "https://launcher.mojang.com/v1/objects/09a47686edec4bbb34e82fbd08559f8bb6266544/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh": {"type": "directory"}, "lib/locale/zh.GBK": {"type": "directory"}, "lib/locale/zh.GBK/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh.GBK/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "75fd04045bf5890b8bb822770bfdb90a2e9ea65b", "size": 902, "url": "https://launcher.mojang.com/v1/objects/75fd04045bf5890b8bb822770bfdb90a2e9ea65b/sunw_java_plugin.mo"}, "raw": {"sha1": "7006fe7767b8807441a1f359a90509b3e507b0d1", "size": 2002, "url": "https://launcher.mojang.com/v1/objects/7006fe7767b8807441a1f359a90509b3e507b0d1/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "75fd04045bf5890b8bb822770bfdb90a2e9ea65b", "size": 902, "url": "https://launcher.mojang.com/v1/objects/75fd04045bf5890b8bb822770bfdb90a2e9ea65b/sunw_java_plugin.mo"}, "raw": {"sha1": "7006fe7767b8807441a1f359a90509b3e507b0d1", "size": 2002, "url": "https://launcher.mojang.com/v1/objects/7006fe7767b8807441a1f359a90509b3e507b0d1/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh_HK.BIG5HK": {"type": "directory"}, "lib/locale/zh_HK.BIG5HK/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh_HK.BIG5HK/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "3a1397bb1b1741697be1479232b6d9599940c851", "size": 912, "url": "https://launcher.mojang.com/v1/objects/3a1397bb1b1741697be1479232b6d9599940c851/sunw_java_plugin.mo"}, "raw": {"sha1": "c6023544067278c78599921f1032de353ff7da42", "size": 2025, "url": "https://launcher.mojang.com/v1/objects/c6023544067278c78599921f1032de353ff7da42/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh_TW": {"type": "directory"}, "lib/locale/zh_TW.BIG5": {"type": "directory"}, "lib/locale/zh_TW.BIG5/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh_TW.BIG5/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "3a1397bb1b1741697be1479232b6d9599940c851", "size": 912, "url": "https://launcher.mojang.com/v1/objects/3a1397bb1b1741697be1479232b6d9599940c851/sunw_java_plugin.mo"}, "raw": {"sha1": "c6023544067278c78599921f1032de353ff7da42", "size": 2025, "url": "https://launcher.mojang.com/v1/objects/c6023544067278c78599921f1032de353ff7da42/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh_TW/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh_TW/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "c05e610e75182f0c4e77f3e7a4d9670ed62bf63c", "size": 897, "url": "https://launcher.mojang.com/v1/objects/c05e610e75182f0c4e77f3e7a4d9670ed62bf63c/sunw_java_plugin.mo"}, "raw": {"sha1": "f9b972dd059eae3cd337dfcef6a178e8ed8a7db6", "size": 2025, "url": "https://launcher.mojang.com/v1/objects/f9b972dd059eae3cd337dfcef6a178e8ed8a7db6/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/logging.properties": {"downloads": {"lzma": {"sha1": "642202a58e5216d086ad37c0b5a633be802edc78", "size": 896, "url": "https://launcher.mojang.com/v1/objects/642202a58e5216d086ad37c0b5a633be802edc78/logging.properties"}, "raw": {"sha1": "89da8094484891f9ec1fa40c6c8b61f94c5869d0", "size": 2455, "url": "https://launcher.mojang.com/v1/objects/89da8094484891f9ec1fa40c6c8b61f94c5869d0/logging.properties"}}, "executable": false, "type": "file"}, "lib/management": {"type": "directory"}, "lib/management-agent.jar": {"downloads": {"lzma": {"sha1": "3ea0bf17e14b3428296a0f4011bf4025fcbfa4bd", "size": 243, "url": "https://launcher.mojang.com/v1/objects/3ea0bf17e14b3428296a0f4011bf4025fcbfa4bd/management-agent.jar"}, "raw": {"sha1": "9fbed36522aa3a80bac08a328942cbc5ef39ca8e", "size": 381, "url": "https://launcher.mojang.com/v1/objects/9fbed36522aa3a80bac08a328942cbc5ef39ca8e/management-agent.jar"}}, "executable": false, "type": "file"}, "lib/management/jmxremote.access": {"downloads": {"lzma": {"sha1": "69042ff1b14165db19c9d728614639dec16d6a31", "size": 1419, "url": "https://launcher.mojang.com/v1/objects/69042ff1b14165db19c9d728614639dec16d6a31/jmxremote.access"}, "raw": {"sha1": "21200eaad898ba4a2a8834a032efb6616fabb930", "size": 3998, "url": "https://launcher.mojang.com/v1/objects/21200eaad898ba4a2a8834a032efb6616fabb930/jmxremote.access"}}, "executable": false, "type": "file"}, "lib/management/jmxremote.password.template": {"downloads": {"lzma": {"sha1": "556c64b1e920766f8867be3964de6e49f5b81a60", "size": 1129, "url": "https://launcher.mojang.com/v1/objects/556c64b1e920766f8867be3964de6e49f5b81a60/jmxremote.password.template"}, "raw": {"sha1": "c1e0f01408bf20fbbb8b4810520c725f70050db5", "size": 2856, "url": "https://launcher.mojang.com/v1/objects/c1e0f01408bf20fbbb8b4810520c725f70050db5/jmxremote.password.template"}}, "executable": false, "type": "file"}, "lib/management/management.properties": {"downloads": {"lzma": {"sha1": "3e52f9baa6394ca6956845424c607e5cde5d3c67", "size": 3176, "url": "https://launcher.mojang.com/v1/objects/3e52f9baa6394ca6956845424c607e5cde5d3c67/management.properties"}, "raw": {"sha1": "e0451d8d7d9e84d7b1c39ec7d00993307a5cbbf1", "size": 14630, "url": "https://launcher.mojang.com/v1/objects/e0451d8d7d9e84d7b1c39ec7d00993307a5cbbf1/management.properties"}}, "executable": false, "type": "file"}, "lib/management/snmp.acl.template": {"downloads": {"lzma": {"sha1": "9a4aa6396c3b488b0663bed5e5ecb762985669c9", "size": 1121, "url": "https://launcher.mojang.com/v1/objects/9a4aa6396c3b488b0663bed5e5ecb762985669c9/snmp.acl.template"}, "raw": {"sha1": "2e9f9ac287274532eb1f0d1afcefd7f3e97cc794", "size": 3376, "url": "https://launcher.mojang.com/v1/objects/2e9f9ac287274532eb1f0d1afcefd7f3e97cc794/snmp.acl.template"}}, "executable": false, "type": "file"}, "lib/meta-index": {"downloads": {"lzma": {"sha1": "1ac60b31362fda4725c665b591c5fbe384cbc8c1", "size": 788, "url": "https://launcher.mojang.com/v1/objects/1ac60b31362fda4725c665b591c5fbe384cbc8c1/meta-index"}, "raw": {"sha1": "bf204f09242203e713c31785158a0792f9edb600", "size": 2034, "url": "https://launcher.mojang.com/v1/objects/bf204f09242203e713c31785158a0792f9edb600/meta-index"}}, "executable": false, "type": "file"}, "lib/net.properties": {"downloads": {"lzma": {"sha1": "e9ec3981a0797bf55bb87b24d9eb651ce7e6916b", "size": 1830, "url": "https://launcher.mojang.com/v1/objects/e9ec3981a0797bf55bb87b24d9eb651ce7e6916b/net.properties"}, "raw": {"sha1": "fd9471742eb759f4478bb1de9a0dc0527265b6ea", "size": 5352, "url": "https://launcher.mojang.com/v1/objects/fd9471742eb759f4478bb1de9a0dc0527265b6ea/net.properties"}}, "executable": false, "type": "file"}, "lib/oblique-fonts": {"type": "directory"}, "lib/oblique-fonts/LucidaSansDemiOblique.ttf": {"downloads": {"lzma": {"sha1": "49c8980c1b89bbdbab59d0f5bd5bebf0afcb93b2", "size": 38580, "url": "https://launcher.mojang.com/v1/objects/49c8980c1b89bbdbab59d0f5bd5bebf0afcb93b2/LucidaSansDemiOblique.ttf"}, "raw": {"sha1": "53e4e12a675ac222469341c3dbc102464a1be4c7", "size": 91352, "url": "https://launcher.mojang.com/v1/objects/53e4e12a675ac222469341c3dbc102464a1be4c7/LucidaSansDemiOblique.ttf"}}, "executable": false, "type": "file"}, "lib/oblique-fonts/LucidaSansOblique.ttf": {"downloads": {"lzma": {"sha1": "553123c0edcd08035dede4ffd92b5b81c9a7538a", "size": 116575, "url": "https://launcher.mojang.com/v1/objects/553123c0edcd08035dede4ffd92b5b81c9a7538a/LucidaSansOblique.ttf"}, "raw": {"sha1": "95a195ad4fc520b3e395c85b747fc3024d118dd9", "size": 253724, "url": "https://launcher.mojang.com/v1/objects/95a195ad4fc520b3e395c85b747fc3024d118dd9/LucidaSansOblique.ttf"}}, "executable": false, "type": "file"}, "lib/oblique-fonts/LucidaTypewriterBoldOblique.ttf": {"downloads": {"lzma": {"sha1": "2475b08151556ad4d89bb1d2b6494c6bee9abd82", "size": 29954, "url": "https://launcher.mojang.com/v1/objects/2475b08151556ad4d89bb1d2b6494c6bee9abd82/LucidaTypewriterBoldOblique.ttf"}, "raw": {"sha1": "f331fc8b0cc494702bc46b690f2b8eed36469a02", "size": 63168, "url": "https://launcher.mojang.com/v1/objects/f331fc8b0cc494702bc46b690f2b8eed36469a02/LucidaTypewriterBoldOblique.ttf"}}, "executable": false, "type": "file"}, "lib/oblique-fonts/LucidaTypewriterOblique.ttf": {"downloads": {"lzma": {"sha1": "5b970bc3b7abb21dce1aa28ff7f03459d351e552", "size": 60133, "url": "https://launcher.mojang.com/v1/objects/5b970bc3b7abb21dce1aa28ff7f03459d351e552/LucidaTypewriterOblique.ttf"}, "raw": {"sha1": "f8ea00db73f8a89a27674d050edc37c2280930e1", "size": 137484, "url": "https://launcher.mojang.com/v1/objects/f8ea00db73f8a89a27674d050edc37c2280930e1/LucidaTypewriterOblique.ttf"}}, "executable": false, "type": "file"}, "lib/oblique-fonts/fonts.dir": {"downloads": {"lzma": {"sha1": "067528c789bd713c7c3f34e779aa6e2e8253dcf6", "size": 188, "url": "https://launcher.mojang.com/v1/objects/067528c789bd713c7c3f34e779aa6e2e8253dcf6/fonts.dir"}, "raw": {"sha1": "5aee54ffba9e33de56fd84ef64fa496b898585bb", "size": 2115, "url": "https://launcher.mojang.com/v1/objects/5aee54ffba9e33de56fd84ef64fa496b898585bb/fonts.dir"}}, "executable": false, "type": "file"}, "lib/plugin.jar": {"downloads": {"raw": {"sha1": "3f250842c79112bae5369e372025b166990820e8", "size": 950772, "url": "https://launcher.mojang.com/v1/objects/3f250842c79112bae5369e372025b166990820e8/plugin.jar"}}, "executable": false, "type": "file"}, "lib/psfont.properties.ja": {"downloads": {"lzma": {"sha1": "7ca1cc244ed251cd1eb2347f1eea37d7d18c8ad4", "size": 701, "url": "https://launcher.mojang.com/v1/objects/7ca1cc244ed251cd1eb2347f1eea37d7d18c8ad4/psfont.properties.ja"}, "raw": {"sha1": "56ed1c661eeede17b4fae8c9de7b5edbad387abc", "size": 2796, "url": "https://launcher.mojang.com/v1/objects/56ed1c661eeede17b4fae8c9de7b5edbad387abc/psfont.properties.ja"}}, "executable": false, "type": "file"}, "lib/psfontj2d.properties": {"downloads": {"lzma": {"sha1": "4252fa01af8739a3545e2b705e3383892e22ab40", "size": 2278, "url": "https://launcher.mojang.com/v1/objects/4252fa01af8739a3545e2b705e3383892e22ab40/psfontj2d.properties"}, "raw": {"sha1": "aa327a22a49967f4d74afeee6726f505f209692f", "size": 10393, "url": "https://launcher.mojang.com/v1/objects/aa327a22a49967f4d74afeee6726f505f209692f/psfontj2d.properties"}}, "executable": false, "type": "file"}, "lib/resources.jar": {"downloads": {"lzma": {"sha1": "1b0e08441750dc17efe4b527aa146da6cc14e8a6", "size": 579294, "url": "https://launcher.mojang.com/v1/objects/1b0e08441750dc17efe4b527aa146da6cc14e8a6/resources.jar"}, "raw": {"sha1": "daa021906e4648d4c37e798c11733dc2047f2da1", "size": 3505206, "url": "https://launcher.mojang.com/v1/objects/daa021906e4648d4c37e798c11733dc2047f2da1/resources.jar"}}, "executable": false, "type": "file"}, "lib/rt.jar": {"downloads": {"lzma": {"sha1": "fc4a8681aeda29c2a2a3fd11bad7729543283f3d", "size": 14378994, "url": "https://launcher.mojang.com/v1/objects/fc4a8681aeda29c2a2a3fd11bad7729543283f3d/rt.jar"}, "raw": {"sha1": "5396b0954a20f3210f1f4f1886ead30880d6ebfe", "size": 66334986, "url": "https://launcher.mojang.com/v1/objects/5396b0954a20f3210f1f4f1886ead30880d6ebfe/rt.jar"}}, "executable": false, "type": "file"}, "lib/security": {"type": "directory"}, "lib/security/blacklist": {"downloads": {"lzma": {"sha1": "8206fce6c1d91a39fdf78e8e79e953913994a1cd", "size": 1969, "url": "https://launcher.mojang.com/v1/objects/8206fce6c1d91a39fdf78e8e79e953913994a1cd/blacklist"}, "raw": {"sha1": "d4ffb3857eab403955ce9d156e46d056061e6a5a", "size": 4054, "url": "https://launcher.mojang.com/v1/objects/d4ffb3857eab403955ce9d156e46d056061e6a5a/blacklist"}}, "executable": false, "type": "file"}, "lib/security/blacklisted.certs": {"downloads": {"lzma": {"sha1": "8311bead054caf6cfe678d4b7998de4caaabfa53", "size": 806, "url": "https://launcher.mojang.com/v1/objects/8311bead054caf6cfe678d4b7998de4caaabfa53/blacklisted.certs"}, "raw": {"sha1": "c5c005c29a80493f5c31cd7eb629ac1b9c752404", "size": 1273, "url": "https://launcher.mojang.com/v1/objects/c5c005c29a80493f5c31cd7eb629ac1b9c752404/blacklisted.certs"}}, "executable": false, "type": "file"}, "lib/security/cacerts": {"downloads": {"lzma": {"sha1": "654dd94809655d5b28385cbb5eba8d6ad9f2c1aa", "size": 67802, "url": "https://launcher.mojang.com/v1/objects/654dd94809655d5b28385cbb5eba8d6ad9f2c1aa/cacerts"}, "raw": {"sha1": "2917859c443c68e19f93abcd1315c3c2904cbef9", "size": 104430, "url": "https://launcher.mojang.com/v1/objects/2917859c443c68e19f93abcd1315c3c2904cbef9/cacerts"}}, "executable": false, "type": "file"}, "lib/security/java.policy": {"downloads": {"lzma": {"sha1": "b601c420d02ef3dbd8595453d08fdef91134e8b5", "size": 647, "url": "https://launcher.mojang.com/v1/objects/b601c420d02ef3dbd8595453d08fdef91134e8b5/java.policy"}, "raw": {"sha1": "c0112209a567b3b523cfed7041709f9440227968", "size": 2466, "url": "https://launcher.mojang.com/v1/objects/c0112209a567b3b523cfed7041709f9440227968/java.policy"}}, "executable": false, "type": "file"}, "lib/security/java.security": {"downloads": {"lzma": {"sha1": "531620e82ca0365ce8dc97096bb0ac5a7ace5952", "size": 10959, "url": "https://launcher.mojang.com/v1/objects/531620e82ca0365ce8dc97096bb0ac5a7ace5952/java.security"}, "raw": {"sha1": "5dcc17a168c53d0b366784e520bd4d55aa61ac18", "size": 41528, "url": "https://launcher.mojang.com/v1/objects/5dcc17a168c53d0b366784e520bd4d55aa61ac18/java.security"}}, "executable": false, "type": "file"}, "lib/security/javaws.policy": {"downloads": {"raw": {"sha1": "4384ca5e4d32f7dd86d8baddd1e690730d74e694", "size": 98, "url": "https://launcher.mojang.com/v1/objects/4384ca5e4d32f7dd86d8baddd1e690730d74e694/javaws.policy"}}, "executable": false, "type": "file"}, "lib/security/policy": {"type": "directory"}, "lib/security/policy/limited": {"type": "directory"}, "lib/security/policy/limited/US_export_policy.jar": {"downloads": {"raw": {"sha1": "7d69ea3b385bc067738520f1b5c549e1084be285", "size": 3026, "url": "https://launcher.mojang.com/v1/objects/7d69ea3b385bc067738520f1b5c549e1084be285/US_export_policy.jar"}}, "executable": false, "type": "file"}, "lib/security/policy/limited/local_policy.jar": {"downloads": {"raw": {"sha1": "238b8826e110f58acb2e1959773b0a577cd4d569", "size": 3527, "url": "https://launcher.mojang.com/v1/objects/238b8826e110f58acb2e1959773b0a577cd4d569/local_policy.jar"}}, "executable": false, "type": "file"}, "lib/security/policy/unlimited": {"type": "directory"}, "lib/security/policy/unlimited/US_export_policy.jar": {"downloads": {"raw": {"sha1": "f6fb2af1e87fc622cda194a7d6b5f5f069653ff1", "size": 3023, "url": "https://launcher.mojang.com/v1/objects/f6fb2af1e87fc622cda194a7d6b5f5f069653ff1/US_export_policy.jar"}}, "executable": false, "type": "file"}, "lib/security/policy/unlimited/local_policy.jar": {"downloads": {"raw": {"sha1": "517368ab2cbaf6b42ea0b963f98eeedd996e83e3", "size": 3035, "url": "https://launcher.mojang.com/v1/objects/517368ab2cbaf6b42ea0b963f98eeedd996e83e3/local_policy.jar"}}, "executable": false, "type": "file"}, "lib/security/trusted.libraries": {"downloads": {"raw": {"sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "size": 0, "url": "https://launcher.mojang.com/v1/objects/da39a3ee5e6b4b0d3255bfef95601890afd80709/trusted.libraries"}}, "executable": false, "type": "file"}, "lib/sound.properties": {"downloads": {"lzma": {"sha1": "3b5f7e4ec437d79048af35094290577f483b3fe1", "size": 473, "url": "https://launcher.mojang.com/v1/objects/3b5f7e4ec437d79048af35094290577f483b3fe1/sound.properties"}, "raw": {"sha1": "9afceb218059d981d0fa9f07aad3c5097cf41b0c", "size": 1210, "url": "https://launcher.mojang.com/v1/objects/9afceb218059d981d0fa9f07aad3c5097cf41b0c/sound.properties"}}, "executable": false, "type": "file"}, "lib/tzdb.dat": {"downloads": {"lzma": {"sha1": "39c69339965484afe89c14111baeeb862fdefd97", "size": 32547, "url": "https://launcher.mojang.com/v1/objects/39c69339965484afe89c14111baeeb862fdefd97/tzdb.dat"}, "raw": {"sha1": "b59c07e3619271a3b9861e999f4b138e971baf69", "size": 105734, "url": "https://launcher.mojang.com/v1/objects/b59c07e3619271a3b9861e999f4b138e971baf69/tzdb.dat"}}, "executable": false, "type": "file"}, "man": {"type": "directory"}, "man/ja": {"target": "ja_JP.UTF-8", "type": "link"}, "man/ja_JP.UTF-8": {"type": "directory"}, "man/ja_JP.UTF-8/man1": {"type": "directory"}, "man/ja_JP.UTF-8/man1/java.1": {"downloads": {"lzma": {"sha1": "f9da09710b6c6df23c256e324a0c4df00a0d6ded", "size": 25461, "url": "https://launcher.mojang.com/v1/objects/f9da09710b6c6df23c256e324a0c4df00a0d6ded/java.1"}, "raw": {"sha1": "b0b12a0bb66e6171771ca4b1dfca32fb759bcaec", "size": 148688, "url": "https://launcher.mojang.com/v1/objects/b0b12a0bb66e6171771ca4b1dfca32fb759bcaec/java.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/javaws.1": {"downloads": {"lzma": {"sha1": "6188fae453ca09ccb19be5c9f4d2059926b36267", "size": 2154, "url": "https://launcher.mojang.com/v1/objects/6188fae453ca09ccb19be5c9f4d2059926b36267/javaws.1"}, "raw": {"sha1": "8f39d928870268ace07bedfebd18db1e1d07fc37", "size": 6641, "url": "https://launcher.mojang.com/v1/objects/8f39d928870268ace07bedfebd18db1e1d07fc37/javaws.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/jjs.1": {"downloads": {"lzma": {"sha1": "6e42b989d28b185dc1aab50c0389834e649a37d4", "size": 3452, "url": "https://launcher.mojang.com/v1/objects/6e42b989d28b185dc1aab50c0389834e649a37d4/jjs.1"}, "raw": {"sha1": "e023322a2013912315a2bd1034e6f829a27c76e0", "size": 11365, "url": "https://launcher.mojang.com/v1/objects/e023322a2013912315a2bd1034e6f829a27c76e0/jjs.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/keytool.1": {"downloads": {"lzma": {"sha1": "a78134a4bddd53d684a70aa677e51a215db1c9cb", "size": 20698, "url": "https://launcher.mojang.com/v1/objects/a78134a4bddd53d684a70aa677e51a215db1c9cb/keytool.1"}, "raw": {"sha1": "148583c837eaaf6333ccfd8c9e8df08574e14b0c", "size": 111033, "url": "https://launcher.mojang.com/v1/objects/148583c837eaaf6333ccfd8c9e8df08574e14b0c/keytool.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/orbd.1": {"downloads": {"lzma": {"sha1": "326af0dcbff173ef8aee29163dbe146d7389cc3e", "size": 4225, "url": "https://launcher.mojang.com/v1/objects/326af0dcbff173ef8aee29163dbe146d7389cc3e/orbd.1"}, "raw": {"sha1": "95651622d33c08286858ec337edd3ea72acd93dc", "size": 16092, "url": "https://launcher.mojang.com/v1/objects/95651622d33c08286858ec337edd3ea72acd93dc/orbd.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/pack200.1": {"downloads": {"lzma": {"sha1": "e0eedafa748c61a44e5be4355fe9d44b05048e80", "size": 4293, "url": "https://launcher.mojang.com/v1/objects/e0eedafa748c61a44e5be4355fe9d44b05048e80/pack200.1"}, "raw": {"sha1": "aa21a0ab75707f7fc66e83c7a392e69b37ddf80e", "size": 14482, "url": "https://launcher.mojang.com/v1/objects/aa21a0ab75707f7fc66e83c7a392e69b37ddf80e/pack200.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/policytool.1": {"downloads": {"lzma": {"sha1": "3c766ed12dab58166169d35680c392a6be1814a1", "size": 1380, "url": "https://launcher.mojang.com/v1/objects/3c766ed12dab58166169d35680c392a6be1814a1/policytool.1"}, "raw": {"sha1": "80879c74e072a98fad6f32b3283331aaf9bd002f", "size": 4020, "url": "https://launcher.mojang.com/v1/objects/80879c74e072a98fad6f32b3283331aaf9bd002f/policytool.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/rmid.1": {"downloads": {"lzma": {"sha1": "1e20779d990beacc32a48237777d670fcc47ca14", "size": 4836, "url": "https://launcher.mojang.com/v1/objects/1e20779d990beacc32a48237777d670fcc47ca14/rmid.1"}, "raw": {"sha1": "7e40cb8003d098d6e36f45640b26f979ac94b5c5", "size": 19715, "url": "https://launcher.mojang.com/v1/objects/7e40cb8003d098d6e36f45640b26f979ac94b5c5/rmid.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/rmiregistry.1": {"downloads": {"lzma": {"sha1": "aaf4ffe07e954f8696eef1ecb7a5e244628d0ad9", "size": 1627, "url": "https://launcher.mojang.com/v1/objects/aaf4ffe07e954f8696eef1ecb7a5e244628d0ad9/rmiregistry.1"}, "raw": {"sha1": "c53c52f3ae7a011c135894c9fc51b741e729c33d", "size": 4557, "url": "https://launcher.mojang.com/v1/objects/c53c52f3ae7a011c135894c9fc51b741e729c33d/rmiregistry.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/servertool.1": {"downloads": {"lzma": {"sha1": "3b9e624e9d1cf2959b438a35061162e2100ddecd", "size": 2626, "url": "https://launcher.mojang.com/v1/objects/3b9e624e9d1cf2959b438a35061162e2100ddecd/servertool.1"}, "raw": {"sha1": "50ab8bcd9dd9d0b1a3d81348fbce1c8f82e7189e", "size": 9081, "url": "https://launcher.mojang.com/v1/objects/50ab8bcd9dd9d0b1a3d81348fbce1c8f82e7189e/servertool.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/tnameserv.1": {"downloads": {"lzma": {"sha1": "bb3106ff74c60a76de3d20659b9c2128c70f3bf2", "size": 4478, "url": "https://launcher.mojang.com/v1/objects/bb3106ff74c60a76de3d20659b9c2128c70f3bf2/tnameserv.1"}, "raw": {"sha1": "01e714671ecd1167edcb5310b16a9c59c33c3eaa", "size": 17722, "url": "https://launcher.mojang.com/v1/objects/01e714671ecd1167edcb5310b16a9c59c33c3eaa/tnameserv.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/unpack200.1": {"downloads": {"lzma": {"sha1": "c115a881cf800b08df294df55d9f250ae944e33c", "size": 1973, "url": "https://launcher.mojang.com/v1/objects/c115a881cf800b08df294df55d9f250ae944e33c/unpack200.1"}, "raw": {"sha1": "7c882bba0067367a41ad84868d18793b8a7397a3", "size": 5382, "url": "https://launcher.mojang.com/v1/objects/7c882bba0067367a41ad84868d18793b8a7397a3/unpack200.1"}}, "executable": false, "type": "file"}, "man/man1": {"type": "directory"}, "man/man1/java.1": {"downloads": {"lzma": {"sha1": "06a6b0275c202bf698d73ca71f95618d56d81c15", "size": 25796, "url": "https://launcher.mojang.com/v1/objects/06a6b0275c202bf698d73ca71f95618d56d81c15/java.1"}, "raw": {"sha1": "69fec7a341aa91f18dbdcdb95952dede7e1b689a", "size": 124796, "url": "https://launcher.mojang.com/v1/objects/69fec7a341aa91f18dbdcdb95952dede7e1b689a/java.1"}}, "executable": false, "type": "file"}, "man/man1/javaws.1": {"downloads": {"lzma": {"sha1": "4bae251c6dfb5420f56928815cf80d0b6d517a1f", "size": 1759, "url": "https://launcher.mojang.com/v1/objects/4bae251c6dfb5420f56928815cf80d0b6d517a1f/javaws.1"}, "raw": {"sha1": "e61e44e101b1bc119c2d2d4b10320f38b36a8036", "size": 4897, "url": "https://launcher.mojang.com/v1/objects/e61e44e101b1bc119c2d2d4b10320f38b36a8036/javaws.1"}}, "executable": false, "type": "file"}, "man/man1/jjs.1": {"downloads": {"lzma": {"sha1": "29683cf2bd47015c9461b688749ddffd95f6671d", "size": 1881, "url": "https://launcher.mojang.com/v1/objects/29683cf2bd47015c9461b688749ddffd95f6671d/jjs.1"}, "raw": {"sha1": "78d419bd3a7f3e0802d5220e690429194b5d1beb", "size": 4932, "url": "https://launcher.mojang.com/v1/objects/78d419bd3a7f3e0802d5220e690429194b5d1beb/jjs.1"}}, "executable": false, "type": "file"}, "man/man1/keytool.1": {"downloads": {"lzma": {"sha1": "b67e5126d43713ee3675706724b34061578b42db", "size": 19690, "url": "https://launcher.mojang.com/v1/objects/b67e5126d43713ee3675706724b34061578b42db/keytool.1"}, "raw": {"sha1": "4c976f86057ab779763fcfb98f5702ebef47f629", "size": 86925, "url": "https://launcher.mojang.com/v1/objects/4c976f86057ab779763fcfb98f5702ebef47f629/keytool.1"}}, "executable": false, "type": "file"}, "man/man1/orbd.1": {"downloads": {"lzma": {"sha1": "147064d6f7e027002e296bb246ae572d0ce0495b", "size": 3708, "url": "https://launcher.mojang.com/v1/objects/147064d6f7e027002e296bb246ae572d0ce0495b/orbd.1"}, "raw": {"sha1": "64201e1846fcf1dcc45c786ffeab89426d1c7742", "size": 12180, "url": "https://launcher.mojang.com/v1/objects/64201e1846fcf1dcc45c786ffeab89426d1c7742/orbd.1"}}, "executable": false, "type": "file"}, "man/man1/pack200.1": {"downloads": {"lzma": {"sha1": "fe17486bbe9c58cf4182fa056b9cd124e8295607", "size": 3724, "url": "https://launcher.mojang.com/v1/objects/fe17486bbe9c58cf4182fa056b9cd124e8295607/pack200.1"}, "raw": {"sha1": "26826cf52b89924f2d2a60d6cda798891875eae6", "size": 11623, "url": "https://launcher.mojang.com/v1/objects/26826cf52b89924f2d2a60d6cda798891875eae6/pack200.1"}}, "executable": false, "type": "file"}, "man/man1/policytool.1": {"downloads": {"lzma": {"sha1": "bd154e7c39aca71d15b2098c588866f8d95bc743", "size": 1122, "url": "https://launcher.mojang.com/v1/objects/bd154e7c39aca71d15b2098c588866f8d95bc743/policytool.1"}, "raw": {"sha1": "ab296625155d9a2b25ecc2b4feff2f741b3ad136", "size": 3235, "url": "https://launcher.mojang.com/v1/objects/ab296625155d9a2b25ecc2b4feff2f741b3ad136/policytool.1"}}, "executable": false, "type": "file"}, "man/man1/rmid.1": {"downloads": {"lzma": {"sha1": "6a7da234e7f43ebca5c4ba8cd862fda3be62fbaa", "size": 4255, "url": "https://launcher.mojang.com/v1/objects/6a7da234e7f43ebca5c4ba8cd862fda3be62fbaa/rmid.1"}, "raw": {"sha1": "6f10e214d7950a6a8460524e41dc700f112f89e5", "size": 15979, "url": "https://launcher.mojang.com/v1/objects/6f10e214d7950a6a8460524e41dc700f112f89e5/rmid.1"}}, "executable": false, "type": "file"}, "man/man1/rmiregistry.1": {"downloads": {"lzma": {"sha1": "f40dd17e3a734600ad1828b0c42d3a1685c4c520", "size": 1301, "url": "https://launcher.mojang.com/v1/objects/f40dd17e3a734600ad1828b0c42d3a1685c4c520/rmiregistry.1"}, "raw": {"sha1": "d9a3d23fab689df5bb9a792b88f462f939b49f70", "size": 3449, "url": "https://launcher.mojang.com/v1/objects/d9a3d23fab689df5bb9a792b88f462f939b49f70/rmiregistry.1"}}, "executable": false, "type": "file"}, "man/man1/servertool.1": {"downloads": {"lzma": {"sha1": "74f1e10712202cd3ca0ff5833de05b7ee67092e1", "size": 2307, "url": "https://launcher.mojang.com/v1/objects/74f1e10712202cd3ca0ff5833de05b7ee67092e1/servertool.1"}, "raw": {"sha1": "e6c7b510740ac8681a9bfb5f4ee1f0306125b728", "size": 7237, "url": "https://launcher.mojang.com/v1/objects/e6c7b510740ac8681a9bfb5f4ee1f0306125b728/servertool.1"}}, "executable": false, "type": "file"}, "man/man1/tnameserv.1": {"downloads": {"lzma": {"sha1": "4bec7f4e070d023f124f9352a8971d7acd249a15", "size": 3955, "url": "https://launcher.mojang.com/v1/objects/4bec7f4e070d023f124f9352a8971d7acd249a15/tnameserv.1"}, "raw": {"sha1": "a31dbbe800d49cb371fab9a4b73d22c3bf8799ad", "size": 15747, "url": "https://launcher.mojang.com/v1/objects/a31dbbe800d49cb371fab9a4b73d22c3bf8799ad/tnameserv.1"}}, "executable": false, "type": "file"}, "man/man1/unpack200.1": {"downloads": {"lzma": {"sha1": "f8e73863187929debf2ea6dadefb2995ec7917e7", "size": 1672, "url": "https://launcher.mojang.com/v1/objects/f8e73863187929debf2ea6dadefb2995ec7917e7/unpack200.1"}, "raw": {"sha1": "437f7233d738cb9b822e99003127049005663e0f", "size": 4244, "url": "https://launcher.mojang.com/v1/objects/437f7233d738cb9b822e99003127049005663e0f/unpack200.1"}}, "executable": false, "type": "file"}, "plugin": {"type": "directory"}, "plugin/desktop": {"type": "directory"}, "plugin/desktop/sun_java.desktop": {"downloads": {"lzma": {"sha1": "49ab0ccb54c3be68281d05055bc56a88b1281d3c", "size": 447, "url": "https://launcher.mojang.com/v1/objects/49ab0ccb54c3be68281d05055bc56a88b1281d3c/sun_java.desktop"}, "raw": {"sha1": "79120ee8160ad6f3c9b90c2641fb7edf3af96b5d", "size": 624, "url": "https://launcher.mojang.com/v1/objects/79120ee8160ad6f3c9b90c2641fb7edf3af96b5d/sun_java.desktop"}}, "executable": false, "type": "file"}, "plugin/desktop/sun_java.png": {"downloads": {"raw": {"sha1": "699c41e97a35414e72a80327a54d6e14e874e951", "size": 4351, "url": "https://launcher.mojang.com/v1/objects/699c41e97a35414e72a80327a54d6e14e874e951/sun_java.png"}}, "executable": false, "type": "file"}, "release": {"downloads": {"raw": {"sha1": "cb462682644c0275d94a45b759108815f3112064", "size": 424, "url": "https://launcher.mojang.com/v1/objects/cb462682644c0275d94a45b759108815f3112064/release"}}, "executable": false, "type": "file"}}} \ No newline at end of file
diff --git a/api/logic/mojang/testdata/inspect/a/b.txt b/api/logic/mojang/testdata/inspect/a/b.txt
deleted file mode 100755
index e69de29b..00000000
--- a/api/logic/mojang/testdata/inspect/a/b.txt
+++ /dev/null
diff --git a/api/logic/mojang/testdata/inspect/a/b/b.txt b/api/logic/mojang/testdata/inspect/a/b/b.txt
deleted file mode 120000
index 4e19a044..00000000
--- a/api/logic/mojang/testdata/inspect/a/b/b.txt
+++ /dev/null
@@ -1 +0,0 @@
-../b.txt \ No newline at end of file
diff --git a/api/logic/mojang/testdata/inspect_win/a/b.txt b/api/logic/mojang/testdata/inspect_win/a/b.txt
deleted file mode 100644
index e69de29b..00000000
--- a/api/logic/mojang/testdata/inspect_win/a/b.txt
+++ /dev/null
diff --git a/api/logic/mojang/testdata/inspect_win/a/b/b.txt b/api/logic/mojang/testdata/inspect_win/a/b/b.txt
deleted file mode 100644
index e69de29b..00000000
--- a/api/logic/mojang/testdata/inspect_win/a/b/b.txt
+++ /dev/null
diff --git a/api/logic/net/ByteArraySink.h b/api/logic/net/ByteArraySink.h
deleted file mode 100644
index 20e6764c..00000000
--- a/api/logic/net/ByteArraySink.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma once
-
-#include "Sink.h"
-
-namespace Net {
-/*
- * Sink object for downloads that uses an external QByteArray it doesn't own as a target.
- */
-class ByteArraySink : public Sink
-{
-public:
- ByteArraySink(QByteArray *output)
- :m_output(output)
- {
- // 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;
- }
-
-private:
- QByteArray * m_output;
-};
-}
diff --git a/api/logic/net/ChecksumValidator.h b/api/logic/net/ChecksumValidator.h
deleted file mode 100644
index 0d6b19c2..00000000
--- a/api/logic/net/ChecksumValidator.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma once
-
-#include "Validator.h"
-#include <QCryptographicHash>
-#include <memory>
-#include <QFile>
-
-namespace Net {
-class ChecksumValidator: public Validator
-{
-public: /* con/des */
- 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);
- 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 */
- 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
deleted file mode 100644
index 3f183b7d..00000000
--- a/api/logic/net/Download.cpp
+++ /dev/null
@@ -1,309 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Download.h"
-
-#include "BuildConfig.h"
-#include <QFileInfo>
-#include <QDateTime>
-#include <QDebug>
-#include "Env.h"
-#include <FileSystem.h>
-#include "ChecksumValidator.h"
-#include "MetaCacheSink.h"
-#include "ByteArraySink.h"
-
-namespace Net {
-
-Download::Download():NetAction()
-{
- 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::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::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);
-}
-
-void Download::addValidator(Validator * 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;
- }
-
- request.setHeader(QNetworkRequest::UserAgentHeader, BuildConfig.USER_AGENT);
-
- 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);
-}
-
-void Download::downloadProgress(qint64 bytesReceived, qint64 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;
- }
-}
-
-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++;
- }
-}
-
-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);
-
- 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;
- }
-
- 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;
- }
-
- // 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);
- }
-
- // 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;
- }
-}
-
-}
-
-bool Net::Download::abort()
-{
- if(m_reply)
- {
- m_reply->abort();
- }
- else
- {
- m_status = Job_Aborted;
- }
- return true;
-}
-
-bool Net::Download::canAbort()
-{
- return true;
-}
diff --git a/api/logic/net/Download.h b/api/logic/net/Download.h
deleted file mode 100644
index 2c436032..00000000
--- a/api/logic/net/Download.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "NetAction.h"
-#include "HttpMetaCache.h"
-#include "Validator.h"
-#include "Sink.h"
-
-#include "multimc_logic_export.h"
-namespace Net {
-class MULTIMC_LOGIC_EXPORT Download : public NetAction
-{
- Q_OBJECT
-
-public: /* types */
- typedef std::shared_ptr<class Download> Ptr;
- enum class Option
- {
- NoOptions = 0,
- AcceptLocalFiles = 1
- };
- Q_DECLARE_FLAGS(Options, Option)
-
-protected: /* con/des */
- 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);
-
-public: /* methods */
- QString getTargetFilepath()
- {
- return m_target_path;
- }
- void addValidator(Validator * v);
- bool abort() override;
- bool canAbort() override;
-
-private: /* methods */
- bool handleRedirect();
-
-protected slots:
- 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;
-
-public slots:
- 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;
-};
-}
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(Net::Download::Options)
diff --git a/api/logic/net/FileSink.cpp b/api/logic/net/FileSink.cpp
deleted file mode 100644
index 8b3e917d..00000000
--- a/api/logic/net/FileSink.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-#include "FileSink.h"
-#include <QFile>
-#include <QFileInfo>
-#include "Env.h"
-#include "FileSystem.h"
-
-namespace Net {
-
-FileSink::FileSink(QString filename)
- :m_filename(filename)
-{
- // nil
-}
-
-FileSink::~FileSink()
-{
- // 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;
- }
-
- if(initAllValidators(request))
- return Job_InProgress;
- return Job_Failed;
-}
-
-JobStatus FileSink::initCache(QNetworkRequest &)
-{
- 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;
-}
-
-JobStatus FileSink::abort()
-{
- 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();
-
- return finalizeCache(reply);
-}
-
-JobStatus FileSink::finalizeCache(QNetworkReply &)
-{
- return Job_Finished;
-}
-
-bool FileSink::hasLocalData()
-{
- QFileInfo info(m_filename);
- return info.exists() && info.size() != 0;
-}
-}
diff --git a/api/logic/net/FileSink.h b/api/logic/net/FileSink.h
deleted file mode 100644
index 875fe511..00000000
--- a/api/logic/net/FileSink.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-#include "Sink.h"
-#include <QSaveFile>
-
-namespace Net {
-class FileSink : public Sink
-{
-public: /* con/des */
- 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;
-
-protected: /* methods */
- virtual JobStatus initCache(QNetworkRequest &);
- virtual JobStatus finalizeCache(QNetworkReply &reply);
-
-protected: /* data */
- 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
deleted file mode 100644
index 4bc8fbc8..00000000
--- a/api/logic/net/HttpMetaCache.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Env.h"
-#include "HttpMetaCache.h"
-#include "FileSystem.h"
-
-#include <QFileInfo>
-#include <QFile>
-#include <QDateTime>
-#include <QCryptographicHash>
-
-#include <QDebug>
-
-#include <QJsonDocument>
-#include <QJsonArray>
-#include <QJsonObject>
-
-QString MetaEntry::getFullPath()
-{
- // 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()));
-}
-
-HttpMetaCache::~HttpMetaCache()
-{
- 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();
-}
-
-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 &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);
- }
-
- 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();
- }
-
- // 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;
-}
-
-bool HttpMetaCache::evictEntry(MetaEntryPtr entry)
-{
- 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);
-}
-
-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;
-}
-
-QString HttpMetaCache::getBasePath(QString base)
-{
- if (m_entries.contains(base))
- {
- return m_entries[base].base_path;
- }
- return QString();
-}
-
-void HttpMetaCache::Load()
-{
- if(m_index_file.isNull())
- 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;
-
- // 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);
-}
-
-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);
-
- 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
deleted file mode 100644
index c3248793..00000000
--- a/api/logic/net/HttpMetaCache.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <QString>
-#include <QMap>
-#include <qtimer.h>
-#include <memory>
-
-#include "multimc_logic_export.h"
-
-class HttpMetaCache;
-
-class MULTIMC_LOGIC_EXPORT MetaEntry
-{
-friend class HttpMetaCache;
-protected:
- 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;
- }
-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;
-};
-
-typedef std::shared_ptr<MetaEntry> MetaEntryPtr;
-
-class MULTIMC_LOGIC_EXPORT HttpMetaCache : public QObject
-{
- Q_OBJECT
-public:
- // 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 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);
-
- // evict selected entry from cache
- bool evictEntry(MetaEntryPtr entry);
-
- void addBase(QString base, QString base_root);
-
- // (re)start a timer that calls SaveNow later.
- void SaveEventually();
- void Load();
- QString getBasePath(QString base);
-public
-slots:
- 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;
-};
diff --git a/api/logic/net/MetaCacheSink.cpp b/api/logic/net/MetaCacheSink.cpp
deleted file mode 100644
index d7f18533..00000000
--- a/api/logic/net/MetaCacheSink.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include "MetaCacheSink.h"
-#include <QFile>
-#include <QFileInfo>
-#include "Env.h"
-#include "FileSystem.h"
-
-namespace Net {
-
-MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator * md5sum)
- :Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum)
-{
- addValidator(md5sum);
-}
-
-MetaCacheSink::~MetaCacheSink()
-{
- // 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;
-}
-
-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;
-}
-
-bool MetaCacheSink::hasLocalData()
-{
- QFileInfo info(m_filename);
- return info.exists() && info.size() != 0;
-}
-}
diff --git a/api/logic/net/MetaCacheSink.h b/api/logic/net/MetaCacheSink.h
deleted file mode 100644
index edcf7ad1..00000000
--- a/api/logic/net/MetaCacheSink.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-#include "FileSink.h"
-#include "ChecksumValidator.h"
-#include "net/HttpMetaCache.h"
-
-namespace Net {
-class MetaCacheSink : public FileSink
-{
-public: /* con/des */
- MetaCacheSink(MetaEntryPtr entry, ChecksumValidator * md5sum);
- virtual ~MetaCacheSink();
- bool hasLocalData() override;
-
-protected: /* methods */
- JobStatus initCache(QNetworkRequest & request) override;
- JobStatus finalizeCache(QNetworkReply & reply) override;
-
-private: /* data */
- MetaEntryPtr m_entry;
- ChecksumValidator * m_md5Node;
-};
-}
diff --git a/api/logic/net/Mode.h b/api/logic/net/Mode.h
deleted file mode 100644
index 9a95f5ad..00000000
--- a/api/logic/net/Mode.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-namespace Net
-{
-enum class Mode
-{
- Offline,
- Online
-};
-}
diff --git a/api/logic/net/NetAction.h b/api/logic/net/NetAction.h
deleted file mode 100644
index 02b249a3..00000000
--- a/api/logic/net/NetAction.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QUrl>
-#include <memory>
-#include <QNetworkReply>
-#include <QObjectPtr.h>
-
-#include "multimc_logic_export.h"
-
-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
-};
-
-typedef std::shared_ptr<class NetAction> NetActionPtr;
-class MULTIMC_LOGIC_EXPORT NetAction : public QObject
-{
- Q_OBJECT
-protected:
- 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;
- }
-
-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);
-
-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;
-
-public slots:
- virtual void start() = 0;
-
-public:
- /// index within the parent job, FIXME: nuke
- int m_index_within_job = 0;
-
- /// the network reply
- unique_qobject_ptr<QNetworkReply> m_reply;
-
- /// source URL
- QUrl m_url;
-
- qint64 m_progress = 0;
- qint64 m_total_progress = 1;
-
-protected:
- JobStatus m_status = Job_NotStarted;
-};
diff --git a/api/logic/net/NetJob.cpp b/api/logic/net/NetJob.cpp
deleted file mode 100644
index 029d9e34..00000000
--- a/api/logic/net/NetJob.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NetJob.h"
-#include "Download.h"
-
-#include <QDebug>
-
-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();
-}
-
-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();
-}
-
-void NetJob::partAborted(int index)
-{
- 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);
-}
-
-void NetJob::executeTask()
-{
- // 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();
- }
-}
-
-
-QStringList NetJob::getFailedFiles()
-{
- 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 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 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;
-}
-
-NetJob::~NetJob() = default;
diff --git a/api/logic/net/NetJob.h b/api/logic/net/NetJob.h
deleted file mode 100644
index 480d8037..00000000
--- a/api/logic/net/NetJob.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <QtNetwork>
-#include "NetAction.h"
-#include "Download.h"
-#include "HttpMetaCache.h"
-#include "tasks/Task.h"
-#include "QObjectPtr.h"
-
-#include "multimc_logic_export.h"
-
-class NetJob;
-typedef shared_qobject_ptr<NetJob> NetJobPtr;
-
-class MULTIMC_LOGIC_EXPORT NetJob : public Task
-{
- Q_OBJECT
-public:
- explicit NetJob(QString job_name) : Task()
- {
- setObjectName(job_name);
- }
- virtual ~NetJob();
-
- 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();
-
- bool canAbort() const override;
-
-private slots:
- void startMoreParts();
-
-public slots:
- 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);
-
-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;
-};
diff --git a/api/logic/net/PasteUpload.cpp b/api/logic/net/PasteUpload.cpp
deleted file mode 100644
index cb470c49..00000000
--- a/api/logic/net/PasteUpload.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "PasteUpload.h"
-#include "Env.h"
-#include <QDebug>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QFile>
-#include <BuildConfig.h>
-
-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();
-}
-
-PasteUpload::~PasteUpload()
-{
-}
-
-bool PasteUpload::validateText()
-{
- return m_jsonContent.size() <= maxSize();
-}
-
-void PasteUpload::executeTask()
-{
- QNetworkRequest request(QUrl("https://api.paste.ee/v1/pastes"));
- request.setHeader(QNetworkRequest::UserAgentHeader, BuildConfig.USER_AGENT_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());
-
- 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()));
-}
-
-void PasteUpload::downloadError(QNetworkReply::NetworkError error)
-{
- // 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();
-}
-
-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;
-}
-
diff --git a/api/logic/net/PasteUpload.h b/api/logic/net/PasteUpload.h
deleted file mode 100644
index 11e05c2e..00000000
--- a/api/logic/net/PasteUpload.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-#include "tasks/Task.h"
-#include <QNetworkReply>
-#include <QBuffer>
-#include <memory>
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT PasteUpload : public Task
-{
- 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();
-protected:
- 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;
-public
-slots:
- void downloadError(QNetworkReply::NetworkError);
- void downloadFinished();
-};
diff --git a/api/logic/net/Sink.h b/api/logic/net/Sink.h
deleted file mode 100644
index d526895c..00000000
--- a/api/logic/net/Sink.h
+++ /dev/null
@@ -1,71 +0,0 @@
-#pragma once
-
-#include "net/NetAction.h"
-
-#include "multimc_logic_export.h"
-#include "Validator.h"
-
-namespace Net {
-class MULTIMC_LOGIC_EXPORT Sink
-{
-public: /* con/des */
- 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;
-
- 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;
- }
-
-protected: /* data */
- std::vector<std::shared_ptr<Validator>> validators;
-};
-}
diff --git a/api/logic/net/Validator.h b/api/logic/net/Validator.h
deleted file mode 100644
index 955412ce..00000000
--- a/api/logic/net/Validator.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#include "net/NetAction.h"
-
-#include "multimc_logic_export.h"
-
-namespace Net {
-class MULTIMC_LOGIC_EXPORT Validator
-{
-public: /* con/des */
- 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;
-};
-} \ No newline at end of file
diff --git a/api/logic/news/NewsChecker.cpp b/api/logic/news/NewsChecker.cpp
deleted file mode 100644
index c66f49e1..00000000
--- a/api/logic/news/NewsChecker.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NewsChecker.h"
-
-#include <QByteArray>
-#include <QDomDocument>
-
-#include <QDebug>
-
-NewsChecker::NewsChecker(const QString& 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();
-}
-
-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();
-}
-
-void NewsChecker::rssDownloadFailed(QString 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;
-}
-
-bool NewsChecker::isLoadingNews() const
-{
- return m_newsNetJob.get() != nullptr;
-}
-
-QString NewsChecker::getLastLoadErrorMsg() const
-{
- return m_lastLoadError;
-}
-
-void NewsChecker::succeed()
-{
- 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);
-}
-
diff --git a/api/logic/news/NewsChecker.h b/api/logic/news/NewsChecker.h
deleted file mode 100644
index c473ecab..00000000
--- a/api/logic/news/NewsChecker.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QString>
-#include <QList>
-
-#include <net/NetJob.h>
-
-#include "NewsEntry.h"
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT NewsChecker : public QObject
-{
- 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();
-
-signals:
- /*!
- * Signal fired after the news has finished loading.
- */
- void newsLoaded();
-
- /*!
- * Signal fired after the news fails to load.
- */
- void newsLoadingFailed(QString errorMsg);
-
-protected slots:
- void rssDownloadFinished();
- void rssDownloadFailed(QString reason);
-
-protected: /* data */
- //! The URL for the RSS feed to fetch.
- QString m_feedUrl;
-
- //! List of news entries.
- QList<NewsEntryPtr> m_newsEntries;
-
- //! The network job to use to load the news.
- NetJobPtr m_newsNetJob;
-
- //! True if news has been loaded.
- bool m_loadedNews;
-
- 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;
-
-protected slots:
- /// 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);
-};
-
diff --git a/api/logic/news/NewsEntry.cpp b/api/logic/news/NewsEntry.cpp
deleted file mode 100644
index 7eff657b..00000000
--- a/api/logic/news/NewsEntry.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NewsEntry.h"
-
-#include <QDomNodeList>
-#include <QVariant>
-
-NewsEntry::NewsEntry(QObject* parent) :
- QObject(parent)
-{
- 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)
-{
- this->title = title;
- this->content = content;
- this->link = link;
- this->author = author;
- this->pubDate = pubDate;
-}
-
-/*!
- * Gets the text content of the given child element as a QVariant.
- */
-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;
- }
-}
-
-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");
-
- // 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;
-}
-
diff --git a/api/logic/news/NewsEntry.h b/api/logic/news/NewsEntry.h
deleted file mode 100644
index 0dbc70a5..00000000
--- a/api/logic/news/NewsEntry.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QString>
-#include <QDomElement>
-#include <QDateTime>
-
-#include <memory>
-
-class NewsEntry : public QObject
-{
- Q_OBJECT
-
-public:
- /*!
- * 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);
-
- /*!
- * 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's content. May contain HTML.
- QString content;
-
- //! URL to the post.
- QString link;
-
- //! 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
deleted file mode 100644
index 8209c28b..00000000
--- a/api/logic/notifications/NotificationChecker.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-#include "NotificationChecker.h"
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QDebug>
-
-#include "Env.h"
-#include "net/Download.h"
-
-
-NotificationChecker::NotificationChecker(QObject *parent)
- : QObject(parent)
-{
-}
-
-void NotificationChecker::setNotificationsUrl(const QUrl &notificationsUrl)
-{
- m_notificationsUrl = notificationsUrl;
-}
-
-void NotificationChecker::setApplicationChannel(QString channel)
-{
- m_appVersionChannel = channel;
-}
-
-void NotificationChecker::setApplicationFullVersion(QString version)
-{
- m_appFullVersion = version;
-}
-
-void NotificationChecker::setApplicationPlatform(QString platform)
-{
- m_appPlatform = platform;
-}
-
-QList<NotificationChecker::NotificationEntry> NotificationChecker::notificationEntries() const
-{
- 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();
-}
-
-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();
-}
-
-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;
-}
-
-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;
-}
diff --git a/api/logic/notifications/NotificationChecker.h b/api/logic/notifications/NotificationChecker.h
deleted file mode 100644
index 4b1b893d..00000000
--- a/api/logic/notifications/NotificationChecker.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#pragma once
-
-#include <QObject>
-
-#include "net/NetJob.h"
-#include "net/Download.h"
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT NotificationChecker : public QObject
-{
- 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;
-
-public
-slots:
- void checkForNotifications();
-
-private
-slots:
- void downloadSucceeded(int);
-
-signals:
- void notificationCheckFinished();
-
-private:
- 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;
-};
diff --git a/api/logic/pathmatcher/FSTreeMatcher.h b/api/logic/pathmatcher/FSTreeMatcher.h
deleted file mode 100644
index 361924af..00000000
--- a/api/logic/pathmatcher/FSTreeMatcher.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include "IPathMatcher.h"
-#include <SeparatorPrefixTree.h>
-#include <QRegularExpression>
-
-class FSTreeMatcher : public IPathMatcher
-{
-public:
- virtual ~FSTreeMatcher() {};
- FSTreeMatcher(SeparatorPrefixTree<'/'> & tree) : m_fsTree(tree)
- {
- }
-
- virtual bool matches(const QString &string) const override
- {
- return m_fsTree.covers(string);
- }
-
- SeparatorPrefixTree<'/'> & m_fsTree;
-};
diff --git a/api/logic/pathmatcher/IPathMatcher.h b/api/logic/pathmatcher/IPathMatcher.h
deleted file mode 100644
index b60621c9..00000000
--- a/api/logic/pathmatcher/IPathMatcher.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-#include <memory>
-
-class IPathMatcher
-{
-public:
- typedef std::shared_ptr<IPathMatcher> Ptr;
-
-public:
- virtual ~IPathMatcher(){};
- virtual bool matches(const QString &string) const = 0;
-};
diff --git a/api/logic/pathmatcher/MultiMatcher.h b/api/logic/pathmatcher/MultiMatcher.h
deleted file mode 100644
index 8bc1b6ee..00000000
--- a/api/logic/pathmatcher/MultiMatcher.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "IPathMatcher.h"
-#include <SeparatorPrefixTree.h>
-#include <QRegularExpression>
-
-class MultiMatcher : public IPathMatcher
-{
-public:
- 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;
- }
-
- QList<Ptr> m_matchers;
-};
diff --git a/api/logic/pathmatcher/RegexpMatcher.h b/api/logic/pathmatcher/RegexpMatcher.h
deleted file mode 100644
index 825d488c..00000000
--- a/api/logic/pathmatcher/RegexpMatcher.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "IPathMatcher.h"
-#include <QRegularExpression>
-
-class RegexpMatcher : public IPathMatcher
-{
-public:
- 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;
- }
-
- 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
deleted file mode 100644
index 1f195f00..00000000
--- a/api/logic/screenshots/ImgurAlbumCreation.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#include "ImgurAlbumCreation.h"
-
-#include <QNetworkRequest>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QUrl>
-#include <QStringList>
-
-#include "BuildConfig.h"
-#include "Env.h"
-#include <QDebug>
-
-ImgurAlbumCreation::ImgurAlbumCreation(QList<ScreenshotPtr> screenshots) : NetAction(), m_screenshots(screenshots)
-{
- m_url = BuildConfig.IMGUR_BASE_URL + "album.json";
- m_status = Job_NotStarted;
-}
-
-void ImgurAlbumCreation::start()
-{
- m_status = Job_InProgress;
- QNetworkRequest request(m_url);
- request.setHeader(QNetworkRequest::UserAgentHeader, BuildConfig.USER_AGENT_UNCACHED);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- request.setRawHeader("Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str());
- request.setRawHeader("Accept", "application/json");
-
- QStringList hashes;
- for (auto shot : m_screenshots)
- {
- hashes.append(shot->m_imgurDeleteHash);
- }
-
- const QByteArray data = "deletehashes=" + hashes.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden";
-
- 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)));
-}
-void ImgurAlbumCreation::downloadError(QNetworkReply::NetworkError error)
-{
- 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;
- }
-}
-void ImgurAlbumCreation::downloadProgress(qint64 bytesReceived, qint64 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
deleted file mode 100644
index 55478021..00000000
--- a/api/logic/screenshots/ImgurAlbumCreation.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-#include "net/NetAction.h"
-#include "Screenshot.h"
-
-#include "multimc_logic_export.h"
-
-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));
- }
-
- 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()
- {
- }
-
-public
-slots:
- virtual void start();
-
-private:
- QList<ScreenshotPtr> m_screenshots;
-
- QString m_deleteHash;
- QString m_id;
-};
diff --git a/api/logic/screenshots/ImgurUpload.cpp b/api/logic/screenshots/ImgurUpload.cpp
deleted file mode 100644
index 7e95d5ca..00000000
--- a/api/logic/screenshots/ImgurUpload.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-#include "ImgurUpload.h"
-
-#include <QNetworkRequest>
-#include <QHttpMultiPart>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QHttpPart>
-#include <QFile>
-#include <QUrl>
-
-#include "BuildConfig.h"
-#include "Env.h"
-#include <QDebug>
-
-ImgurUpload::ImgurUpload(ScreenshotPtr shot) : NetAction(), m_shot(shot)
-{
- m_url = BuildConfig.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, BuildConfig.USER_AGENT_UNCACHED);
- request.setRawHeader("Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str());
- request.setRawHeader("Accept", "application/json");
-
- 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);
-
- 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)));
-}
-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);
-}
-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;
-}
-void ImgurUpload::downloadProgress(qint64 bytesReceived, qint64 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
deleted file mode 100644
index d79807f2..00000000
--- a/api/logic/screenshots/ImgurUpload.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-#include "net/NetAction.h"
-#include "Screenshot.h"
-
-#include "multimc_logic_export.h"
-
-typedef std::shared_ptr<class ImgurUpload> ImgurUploadPtr;
-class MULTIMC_LOGIC_EXPORT ImgurUpload : public NetAction
-{
-public:
- explicit ImgurUpload(ScreenshotPtr shot);
- static ImgurUploadPtr make(ScreenshotPtr shot)
- {
- return ImgurUploadPtr(new ImgurUpload(shot));
- }
-
-protected
-slots:
- virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
- virtual void downloadError(QNetworkReply::NetworkError error);
- virtual void downloadFinished();
- virtual void downloadReadyRead()
- {
- }
-
-public
-slots:
- virtual void start();
-
-private:
- ScreenshotPtr m_shot;
- bool finished = true;
-};
diff --git a/api/logic/screenshots/Screenshot.h b/api/logic/screenshots/Screenshot.h
deleted file mode 100644
index 9db3a8a1..00000000
--- a/api/logic/screenshots/Screenshot.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#include <QDateTime>
-#include <QString>
-#include <QFileInfo>
-#include <memory>
-
-struct ScreenShot
-{
- 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
deleted file mode 100644
index 6a3c801d..00000000
--- a/api/logic/settings/INIFile.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "settings/INIFile.h"
-#include <FileSystem.h>
-
-#include <QFile>
-#include <QTextStream>
-#include <QStringList>
-#include <QSaveFile>
-#include <QDebug>
-
-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 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 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 (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;
-}
-
-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.
- 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);
-}
-
-void INIFile::set(QString key, QVariant val)
-{
- this->operator[](key) = val;
-}
diff --git a/api/logic/settings/INIFile.h b/api/logic/settings/INIFile.h
deleted file mode 100644
index 9e8c68ea..00000000
--- a/api/logic/settings/INIFile.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QString>
-#include <QVariant>
-#include <QIODevice>
-
-#include "multimc_logic_export.h"
-
-// Sectionless INI parser (for instance config files)
-class MULTIMC_LOGIC_EXPORT INIFile : public QMap<QString, QVariant>
-{
-public:
- explicit INIFile();
-
- 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);
-};
diff --git a/api/logic/settings/INIFile_test.cpp b/api/logic/settings/INIFile_test.cpp
deleted file mode 100644
index 08c2155e..00000000
--- a/api/logic/settings/INIFile_test.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "settings/INIFile.h"
-
-class IniFileTest : public QObject
-{
- 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\"";
- 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)
-
-#include "INIFile_test.moc"
diff --git a/api/logic/settings/INISettingsObject.cpp b/api/logic/settings/INISettingsObject.cpp
deleted file mode 100644
index 12513403..00000000
--- a/api/logic/settings/INISettingsObject.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "INISettingsObject.h"
-#include "Setting.h"
-
-INISettingsObject::INISettingsObject(const QString &path, QObject *parent)
- : SettingsObject(parent)
-{
- m_filePath = path;
- m_ini.loadFile(path);
-}
-
-void INISettingsObject::setFilePath(const QString &filePath)
-{
- m_filePath = filePath;
-}
-
-bool INISettingsObject::reload()
-{
- return m_ini.loadFile(m_filePath) && SettingsObject::reload();
-}
-
-void INISettingsObject::suspendSave()
-{
- m_suspendSave = true;
-}
-
-void INISettingsObject::resumeSave()
-{
- 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();
- }
-}
-
-void INISettingsObject::doSave()
-{
- 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();
- }
-}
-
-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();
-}
diff --git a/api/logic/settings/INISettingsObject.h b/api/logic/settings/INISettingsObject.h
deleted file mode 100644
index 313f1512..00000000
--- a/api/logic/settings/INISettingsObject.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-
-#include "settings/INIFile.h"
-
-#include "settings/SettingsObject.h"
-
-#include "multimc_logic_export.h"
-
-/*!
- * \brief A settings object that stores its settings in an INIFile.
- */
-class MULTIMC_LOGIC_EXPORT INISettingsObject : public SettingsObject
-{
- Q_OBJECT
-public:
- 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 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;
-
- void suspendSave() override;
- void resumeSave() override;
-
-protected slots:
- 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();
-
-protected:
- INIFile m_ini;
- QString m_filePath;
-};
diff --git a/api/logic/settings/OverrideSetting.cpp b/api/logic/settings/OverrideSetting.cpp
deleted file mode 100644
index 4396a381..00000000
--- a/api/logic/settings/OverrideSetting.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "OverrideSetting.h"
-
-OverrideSetting::OverrideSetting(std::shared_ptr<Setting> other, std::shared_ptr<Setting> gate)
- : Setting(other->configKeys(), QVariant())
-{
- Q_ASSERT(other);
- Q_ASSERT(gate);
- m_other = other;
- m_gate = gate;
-}
-
-bool OverrideSetting::isOverriding() const
-{
- return m_gate->get().toBool();
-}
-
-QVariant OverrideSetting::defValue() const
-{
- return m_other->get();
-}
-
-QVariant OverrideSetting::get() const
-{
- if(isOverriding())
- {
- return Setting::get();
- }
- return m_other->get();
-}
-
-void OverrideSetting::reset()
-{
- Setting::reset();
-}
-
-void OverrideSetting::set(QVariant value)
-{
- Setting::set(value);
-}
diff --git a/api/logic/settings/OverrideSetting.h b/api/logic/settings/OverrideSetting.h
deleted file mode 100644
index 9f0c98b5..00000000
--- a/api/logic/settings/OverrideSetting.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <memory>
-
-#include "Setting.h"
-
-/*!
- * \brief A setting that 'overrides another.'
- * This means that the setting's default value will be the value of another setting.
- * The other setting can be (and usually is) a part of a different SettingsObject
- * than this one.
- */
-class OverrideSetting : public Setting
-{
- Q_OBJECT
-public:
- 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();
-
-private:
- bool isOverriding() const;
-
-protected:
- 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
deleted file mode 100644
index 8f93b251..00000000
--- a/api/logic/settings/PassthroughSetting.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "PassthroughSetting.h"
-
-PassthroughSetting::PassthroughSetting(std::shared_ptr<Setting> other, std::shared_ptr<Setting> gate)
- : Setting(other->configKeys(), QVariant())
-{
- Q_ASSERT(other);
- m_other = other;
- m_gate = gate;
-}
-
-bool PassthroughSetting::isOverriding() const
-{
- if(!m_gate)
- {
- return false;
- }
- return m_gate->get().toBool();
-}
-
-QVariant PassthroughSetting::defValue() const
-{
- if(isOverriding())
- {
- return m_other->get();
- }
- return m_other->defValue();
-}
-
-QVariant PassthroughSetting::get() const
-{
- if(isOverriding())
- {
- return Setting::get();
- }
- return m_other->get();
-}
-
-void PassthroughSetting::reset()
-{
- if(isOverriding())
- {
- Setting::reset();
- }
- m_other->reset();
-}
-
-void PassthroughSetting::set(QVariant value)
-{
- if(isOverriding())
- {
- Setting::set(value);
- }
- m_other->set(value);
-}
diff --git a/api/logic/settings/PassthroughSetting.h b/api/logic/settings/PassthroughSetting.h
deleted file mode 100644
index 22008f83..00000000
--- a/api/logic/settings/PassthroughSetting.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <memory>
-
-#include "Setting.h"
-
-/*!
- * \brief A setting that 'overrides another.' based on the value of a 'gate' setting
- * If 'gate' evaluates to true, the override stores and returns data
- * If 'gate' evaluates to false, the original does,
- */
-class PassthroughSetting : public Setting
-{
- Q_OBJECT
-public:
- 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();
-
-private:
- bool isOverriding() const;
-
-protected:
- 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
deleted file mode 100644
index cfe5a7f9..00000000
--- a/api/logic/settings/Setting.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Setting.h"
-#include "settings/SettingsObject.h"
-
-Setting::Setting(QStringList synonyms, QVariant 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;
- }
-}
-
-QVariant Setting::defValue() const
-{
- return m_defVal;
-}
-
-void Setting::set(QVariant value)
-{
- emit SettingChanged(*this, value);
-}
-
-void Setting::reset()
-{
- emit settingReset(*this);
-}
diff --git a/api/logic/settings/Setting.h b/api/logic/settings/Setting.h
deleted file mode 100644
index a31193ac..00000000
--- a/api/logic/settings/Setting.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QVariant>
-#include <QStringList>
-#include <memory>
-
-#include "multimc_logic_export.h"
-
-class SettingsObject;
-
-/*!
- *
- */
-class MULTIMC_LOGIC_EXPORT Setting : public QObject
-{
- 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());
-
- /*!
- * \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 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;
-
-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 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 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;
-};
diff --git a/api/logic/settings/SettingsObject.cpp b/api/logic/settings/SettingsObject.cpp
deleted file mode 100644
index 8a0bc045..00000000
--- a/api/logic/settings/SettingsObject.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "settings/SettingsObject.h"
-#include "settings/Setting.h"
-#include "settings/OverrideSetting.h"
-#include "PassthroughSetting.h"
-#include <QDebug>
-
-#include <QVariant>
-
-SettingsObject::SettingsObject(QObject *parent) : QObject(parent)
-{
-}
-
-SettingsObject::~SettingsObject()
-{
- m_settings.clear();
-}
-
-std::shared_ptr<Setting> SettingsObject::registerOverride(std::shared_ptr<Setting> original,
- 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;
-}
-
-std::shared_ptr<Setting> SettingsObject::registerPassthrough(std::shared_ptr<Setting> original,
- 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;
-}
-
-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;
-}
-
-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;
-
- return m_settings[id];
-}
-
-QVariant SettingsObject::get(const QString &id) const
-{
- 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;
- }
-}
-
-void SettingsObject::reset(const QString &id) const
-{
- auto setting = getSetting(id);
- if (setting)
- setting->reset();
-}
-
-bool SettingsObject::contains(const QString &id)
-{
- return m_settings.contains(id);
-}
-
-bool SettingsObject::reload()
-{
- 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(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
deleted file mode 100644
index 3ebdebdf..00000000
--- a/api/logic/settings/SettingsObject.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QMap>
-#include <QStringList>
-#include <QVariant>
-#include <memory>
-
-#include "multimc_logic_export.h"
-
-class Setting;
-class SettingsObject;
-
-typedef std::shared_ptr<SettingsObject> SettingsObjectPtr;
-
-/*!
- * \brief The SettingsObject handles communicating settings between the application and a
- *settings file.
- * The class keeps a list of Setting objects. Each Setting object represents one
- * of the application's settings. These Setting objects are registered with
- * a SettingsObject and can be managed similarly to the way a list works.
- *
- * \author Andrew Okin
- * \date 2/22/2013
- *
- * \sa Setting
- */
-class MULTIMC_LOGIC_EXPORT SettingsObject : public QObject
-{
- Q_OBJECT
-public:
- 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;
-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);
-
-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;
-
-protected:
- /*!
- * \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;
-
- friend class Setting;
-
-private:
- QMap<QString, std::shared_ptr<Setting>> m_settings;
-protected:
- bool m_suspendSave = false;
- bool m_doSave = false;
-};
diff --git a/api/logic/status/StatusChecker.cpp b/api/logic/status/StatusChecker.cpp
deleted file mode 100644
index 38fc2163..00000000
--- a/api/logic/status/StatusChecker.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "StatusChecker.h"
-
-#include <QByteArray>
-
-#include <QDebug>
-
-#include <BuildConfig.h>
-
-StatusChecker::StatusChecker()
-{
-
-}
-
-void StatusChecker::timerEvent(QTimerEvent *e)
-{
- QObject::timerEvent(e);
- reloadStatus();
-}
-
-void StatusChecker::reloadStatus()
-{
- if (isLoadingStatus())
- {
- // qDebug() << "Ignored request to reload status. Currently reloading already.";
- return;
- }
-
- // qDebug() << "Reloading status.";
-
- NetJob* job = new NetJob("Status JSON");
- job->addNetAction(Net::Download::makeByteArray(BuildConfig.MOJANG_STATUS_URL, &dataSink));
- QObject::connect(job, &NetJob::succeeded, this, &StatusChecker::statusDownloadFinished);
- QObject::connect(job, &NetJob::failed, this, &StatusChecker::statusDownloadFailed);
- m_statusNetJob.reset(job);
- emit statusLoading(true);
- job->start();
-}
-
-void StatusChecker::statusDownloadFinished()
-{
- qDebug() << "Finished loading status JSON.";
- m_statusEntries.clear();
- m_statusNetJob.reset();
-
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(dataSink, &jsonError);
-
- if (jsonError.error != QJsonParseError::NoError)
- {
- fail("Error parsing status JSON:" + jsonError.errorString());
- return;
- }
-
- if (!jsonDoc.isArray())
- {
- fail("Error parsing status JSON: JSON root is not an array");
- return;
- }
-
- QJsonArray root = jsonDoc.array();
-
- for(auto status = root.begin(); status != root.end(); ++status)
- {
- QVariantMap map = (*status).toObject().toVariantMap();
-
- for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
- {
- QString key = iter.key();
- QVariant value = iter.value();
-
- if(value.type() == QVariant::Type::String)
- {
- m_statusEntries.insert(key, value.toString());
- //qDebug() << "Status JSON object: " << key << m_statusEntries[key];
- }
- else
- {
- fail("Malformed status JSON: expected status type to be a string.");
- return;
- }
- }
- }
-
- succeed();
-}
-
-void StatusChecker::statusDownloadFailed(QString reason)
-{
- fail(tr("Failed to load status JSON:\n%1").arg(reason));
-}
-
-
-QMap<QString, QString> StatusChecker::getStatusEntries() const
-{
- return m_statusEntries;
-}
-
-bool StatusChecker::isLoadingStatus() const
-{
- return m_statusNetJob.get() != nullptr;
-}
-
-QString StatusChecker::getLastLoadErrorMsg() const
-{
- return m_lastLoadError;
-}
-
-void StatusChecker::succeed()
-{
- if(m_prevEntries != m_statusEntries)
- {
- emit statusChanged(m_statusEntries);
- m_prevEntries = m_statusEntries;
- }
- m_lastLoadError = "";
- qDebug() << "Status loading succeeded.";
- m_statusNetJob.reset();
- emit statusLoading(false);
-}
-
-void StatusChecker::fail(const QString& errorMsg)
-{
- if(m_prevEntries != m_statusEntries)
- {
- emit statusChanged(m_statusEntries);
- m_prevEntries = m_statusEntries;
- }
- m_lastLoadError = errorMsg;
- qDebug() << "Failed to load status:" << errorMsg;
- m_statusNetJob.reset();
- emit statusLoading(false);
-}
-
diff --git a/api/logic/status/StatusChecker.h b/api/logic/status/StatusChecker.h
deleted file mode 100644
index e9961aff..00000000
--- a/api/logic/status/StatusChecker.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QString>
-#include <QList>
-
-#include <net/NetJob.h>
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT StatusChecker : public QObject
-{
- Q_OBJECT
-public: /* con/des */
- StatusChecker();
-
-public: /* methods */
- QString getLastLoadErrorMsg() const;
- bool isLoadingStatus() const;
- QMap<QString, QString> getStatusEntries() const;
-
-signals:
- void statusLoading(bool loading);
- void statusChanged(QMap<QString, QString> newStatus);
-
-public slots:
- void reloadStatus();
-
-protected: /* methods */
- virtual void timerEvent(QTimerEvent *);
-
-protected slots:
- void statusDownloadFinished();
- void statusDownloadFailed(QString reason);
- void succeed();
- void fail(const QString& errorMsg);
-
-protected: /* data */
- QMap<QString, QString> m_prevEntries;
- QMap<QString, QString> m_statusEntries;
- NetJobPtr m_statusNetJob;
- QString m_lastLoadError;
- QByteArray dataSink;
-};
-
diff --git a/api/logic/tasks/SequentialTask.cpp b/api/logic/tasks/SequentialTask.cpp
deleted file mode 100644
index d0777132..00000000
--- a/api/logic/tasks/SequentialTask.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#include "SequentialTask.h"
-
-SequentialTask::SequentialTask(QObject *parent) : Task(parent), m_currentIndex(-1)
-{
-}
-
-void SequentialTask::addTask(std::shared_ptr<Task> task)
-{
- m_queue.append(task);
-}
-
-void SequentialTask::executeTask()
-{
- 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();
-}
-
-void SequentialTask::subTaskFailed(const QString &msg)
-{
- emitFailed(msg);
-}
-void SequentialTask::subTaskStatus(const QString &msg)
-{
- setStatus(msg);
-}
-void SequentialTask::subTaskProgress(qint64 current, qint64 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
deleted file mode 100644
index 2ca77c00..00000000
--- a/api/logic/tasks/SequentialTask.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include "Task.h"
-
-#include <QQueue>
-#include <memory>
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT SequentialTask : public Task
-{
- Q_OBJECT
-public:
- explicit SequentialTask(QObject *parent = 0);
- virtual ~SequentialTask() {};
-
- void addTask(std::shared_ptr<Task> task);
-
-protected:
- void executeTask();
-
-private
-slots:
- 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;
-};
diff --git a/api/logic/tasks/Task.cpp b/api/logic/tasks/Task.cpp
deleted file mode 100644
index d0ac7569..00000000
--- a/api/logic/tasks/Task.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Task.h"
-
-#include <QDebug>
-
-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);
- }
-}
-
-void Task::setProgress(qint64 current, qint64 total)
-{
- m_progress = current;
- m_progressTotal = total;
- emit progress(m_progress, m_progressTotal);
-}
-
-void Task::start()
-{
- 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 (!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 (!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 (!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;
-}
-
-bool Task::isRunning() const
-{
- return m_state == State::Running;
-}
-
-bool Task::isFinished() const
-{
- return m_state != State::Running && m_state != State::Inactive;
-}
-
-bool Task::wasSuccessful() const
-{
- return m_state == State::Succeeded;
-}
-
-QString Task::failReason() const
-{
- return m_failReason;
-}
-
-void Task::logWarning(const QString& line)
-{
- qWarning() << line;
- m_Warnings.append(line);
-}
-
-QStringList Task::warnings() const
-{
- return m_Warnings;
-}
diff --git a/api/logic/tasks/Task.h b/api/logic/tasks/Task.h
deleted file mode 100644
index 7ed7086c..00000000
--- a/api/logic/tasks/Task.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QString>
-#include <QStringList>
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT Task : public QObject
-{
- Q_OBJECT
-public:
- enum class State
- {
- Inactive,
- Running,
- Succeeded,
- Failed,
- AbortedByUser
- };
-
-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;
-
- virtual QStringList warnings() const;
-
- virtual bool canAbort() const { return false; }
-
- QString getStatus()
- {
- return m_status;
- }
-
- qint64 getProgress()
- {
- return m_progress;
- }
-
- qint64 getTotalProgress()
- {
- return m_progressTotal;
- }
-
-protected:
- void logWarning(const QString & line);
-
-private:
- QString describe();
-
-signals:
- 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; };
-
-protected:
- virtual void executeTask() = 0;
-
-protected slots:
- virtual void emitSucceeded();
- virtual void emitAborted();
- virtual void emitFailed(QString reason);
-
-public slots:
- void setStatus(const QString &status);
- void setProgress(qint64 current, qint64 total);
-
-private:
- 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/testdata/FileSystem-test_createShortcut-unix b/api/logic/testdata/FileSystem-test_createShortcut-unix
deleted file mode 100755
index 1ce3a2bd..00000000
--- a/api/logic/testdata/FileSystem-test_createShortcut-unix
+++ /dev/null
@@ -1,6 +0,0 @@
-[Desktop Entry]
-Type=Application
-TryExec=asdfDest
-Exec=asdfDest 'arg1' 'arg2'
-Name=asdf
-Icon=
diff --git a/api/logic/testdata/test_folder/assets/minecraft/textures/blah.txt b/api/logic/testdata/test_folder/assets/minecraft/textures/blah.txt
deleted file mode 100644
index 8d1c8b69..00000000
--- a/api/logic/testdata/test_folder/assets/minecraft/textures/blah.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/api/logic/testdata/test_folder/pack.mcmeta b/api/logic/testdata/test_folder/pack.mcmeta
deleted file mode 100644
index 67ee0434..00000000
--- a/api/logic/testdata/test_folder/pack.mcmeta
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "pack": {
- "pack_format": 1,
- "description": "Some resource pack maybe"
- }
-}
diff --git a/api/logic/testdata/test_folder/pack.nfo b/api/logic/testdata/test_folder/pack.nfo
deleted file mode 100644
index 8d1c8b69..00000000
--- a/api/logic/testdata/test_folder/pack.nfo
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/api/logic/tools/BaseExternalTool.cpp b/api/logic/tools/BaseExternalTool.cpp
deleted file mode 100644
index 38d81788..00000000
--- a/api/logic/tools/BaseExternalTool.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "BaseExternalTool.h"
-
-#include <QProcess>
-#include <QDir>
-
-#ifdef Q_OS_WIN
-#include <windows.h>
-#endif
-
-#include "BaseInstance.h"
-
-BaseExternalTool::BaseExternalTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent)
- : QObject(parent), m_instance(instance), globalSettings(settings)
-{
-}
-
-BaseExternalTool::~BaseExternalTool()
-{
-}
-
-BaseDetachedTool::BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent)
- : BaseExternalTool(settings, instance, parent)
-{
-
-}
-
-void BaseDetachedTool::run()
-{
- runImpl();
-}
-
-
-BaseExternalToolFactory::~BaseExternalToolFactory()
-{
-}
-
-BaseDetachedTool *BaseDetachedToolFactory::createDetachedTool(InstancePtr instance,
- QObject *parent)
-{
- return qobject_cast<BaseDetachedTool *>(createTool(instance, parent));
-}
diff --git a/api/logic/tools/BaseExternalTool.h b/api/logic/tools/BaseExternalTool.h
deleted file mode 100644
index b393b9ef..00000000
--- a/api/logic/tools/BaseExternalTool.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#pragma once
-
-#include <QObject>
-#include <BaseInstance.h>
-
-#include "multimc_logic_export.h"
-
-class BaseInstance;
-class SettingsObject;
-class QProcess;
-
-class MULTIMC_LOGIC_EXPORT BaseExternalTool : public QObject
-{
- Q_OBJECT
-public:
- explicit BaseExternalTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
- virtual ~BaseExternalTool();
-
-protected:
- InstancePtr m_instance;
- SettingsObjectPtr globalSettings;
-};
-
-class MULTIMC_LOGIC_EXPORT BaseDetachedTool : public BaseExternalTool
-{
- Q_OBJECT
-public:
- explicit BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
-
-public
-slots:
- void run();
-
-protected:
- virtual void runImpl() = 0;
-};
-
-class MULTIMC_LOGIC_EXPORT BaseExternalToolFactory
-{
-public:
- virtual ~BaseExternalToolFactory();
-
- virtual QString name() const = 0;
-
- virtual void registerSettings(SettingsObjectPtr settings) = 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;
-
-protected:
- SettingsObjectPtr globalSettings;
-};
-
-class MULTIMC_LOGIC_EXPORT BaseDetachedToolFactory : public BaseExternalToolFactory
-{
-public:
- virtual BaseDetachedTool *createDetachedTool(InstancePtr instance, QObject *parent = 0);
-};
diff --git a/api/logic/tools/BaseProfiler.cpp b/api/logic/tools/BaseProfiler.cpp
deleted file mode 100644
index 300d1a73..00000000
--- a/api/logic/tools/BaseProfiler.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "BaseProfiler.h"
-#include "QObjectPtr.h"
-
-#include <QProcess>
-
-BaseProfiler::BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent)
- : BaseExternalTool(settings, instance, parent)
-{
-}
-
-void BaseProfiler::beginProfiling(shared_qobject_ptr<LaunchTask> process)
-{
- beginProfilingImpl(process);
-}
-
-void BaseProfiler::abortProfiling()
-{
- abortProfilingImpl();
-}
-
-void BaseProfiler::abortProfilingImpl()
-{
- 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));
-}
diff --git a/api/logic/tools/BaseProfiler.h b/api/logic/tools/BaseProfiler.h
deleted file mode 100644
index da817f52..00000000
--- a/api/logic/tools/BaseProfiler.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include "BaseExternalTool.h"
-#include "QObjectPtr.h"
-
-#include "multimc_logic_export.h"
-
-class BaseInstance;
-class SettingsObject;
-class LaunchTask;
-class QProcess;
-
-class MULTIMC_LOGIC_EXPORT BaseProfiler : public BaseExternalTool
-{
- Q_OBJECT
-public:
- explicit BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
-
-public
-slots:
- void beginProfiling(shared_qobject_ptr<LaunchTask> process);
- void abortProfiling();
-
-protected:
- QProcess *m_profilerProcess;
-
- virtual void beginProfilingImpl(shared_qobject_ptr<LaunchTask> process) = 0;
- virtual void abortProfilingImpl();
-
-signals:
- 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);
-};
diff --git a/api/logic/tools/JProfiler.cpp b/api/logic/tools/JProfiler.cpp
deleted file mode 100644
index 1dc0d109..00000000
--- a/api/logic/tools/JProfiler.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-#include "JProfiler.h"
-
-#include <QDir>
-
-#include "settings/SettingsObject.h"
-#include "launch/LaunchTask.h"
-#include "BaseInstance.h"
-
-class JProfiler : public BaseProfiler
-{
- Q_OBJECT
-public:
- JProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
-
-private slots:
- void profilerStarted();
- void profilerFinished(int exit, QProcess::ExitStatus status);
-
-protected:
- void beginProfilingImpl(shared_qobject_ptr<LaunchTask> process);
-
-private:
- int listeningPort = 0;
-};
-
-JProfiler::JProfiler(SettingsObjectPtr settings, InstancePtr instance,
- QObject *parent)
- : BaseProfiler(settings, instance, parent)
-{
-}
-
-void JProfiler::profilerStarted()
-{
- 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;
- }
-}
-
-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();
-
-#ifdef Q_OS_WIN
- QString profilerProgram = QDir(basePath).absoluteFilePath("bin/jpenable.exe");
-#else
- QString profilerProgram = QDir(basePath).absoluteFilePath("bin/jpenable");
-#endif
-
- profiler->setArguments(profilerArgs);
- profiler->setProgram(profilerProgram);
-
- connect(profiler, SIGNAL(started()), SLOT(profilerStarted()));
- connect(profiler, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(profilerFinished(int,QProcess::ExitStatus)));
-
- m_profilerProcess = profiler;
- profiler->start();
-}
-
-void JProfilerFactory::registerSettings(SettingsObjectPtr settings)
-{
- settings->registerSetting("JProfilerPath");
- settings->registerSetting("JProfilerPort", 42042);
- globalSettings = settings;
-}
-
-BaseExternalTool *JProfilerFactory::createTool(InstancePtr instance, QObject *parent)
-{
- return new JProfiler(globalSettings, instance, parent);
-}
-
-bool JProfilerFactory::check(QString *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;
-}
-
-#include "JProfiler.moc"
diff --git a/api/logic/tools/JProfiler.h b/api/logic/tools/JProfiler.h
deleted file mode 100644
index f211ddbf..00000000
--- a/api/logic/tools/JProfiler.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include "BaseProfiler.h"
-
-#include "multimc_logic_export.h"
-
-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;
-};
diff --git a/api/logic/tools/JVisualVM.cpp b/api/logic/tools/JVisualVM.cpp
deleted file mode 100644
index b1acc3c0..00000000
--- a/api/logic/tools/JVisualVM.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "JVisualVM.h"
-
-#include <QDir>
-#include <QStandardPaths>
-
-#include "settings/SettingsObject.h"
-#include "launch/LaunchTask.h"
-#include "BaseInstance.h"
-
-class JVisualVM : public BaseProfiler
-{
- Q_OBJECT
-public:
- JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0);
-
-private slots:
- void profilerStarted();
- void profilerFinished(int exit, QProcess::ExitStatus status);
-
-protected:
- void beginProfilingImpl(shared_qobject_ptr<LaunchTask> process);
-};
-
-
-JVisualVM::JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject *parent)
- : BaseProfiler(settings, instance, parent)
-{
-}
-
-void JVisualVM::profilerStarted()
-{
- 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;
- }
-}
-
-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();
-
- profiler->setArguments(profilerArgs);
- profiler->setProgram(programPath);
-
- connect(profiler, SIGNAL(started()), SLOT(profilerStarted()));
- connect(profiler, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(profilerFinished(int,QProcess::ExitStatus)));
-
- 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;
-}
-
-BaseExternalTool *JVisualVMFactory::createTool(InstancePtr instance, QObject *parent)
-{
- return new JVisualVM(globalSettings, instance, parent);
-}
-
-bool JVisualVMFactory::check(QString *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;
-}
-
-#include "JVisualVM.moc"
diff --git a/api/logic/tools/JVisualVM.h b/api/logic/tools/JVisualVM.h
deleted file mode 100644
index 91d48d94..00000000
--- a/api/logic/tools/JVisualVM.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include "BaseProfiler.h"
-
-#include "multimc_logic_export.h"
-
-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;
-};
diff --git a/api/logic/tools/MCEditTool.cpp b/api/logic/tools/MCEditTool.cpp
deleted file mode 100644
index 880327c7..00000000
--- a/api/logic/tools/MCEditTool.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#include "MCEditTool.h"
-
-#include <QDir>
-#include <QProcess>
-#include <QUrl>
-
-#include "settings/SettingsObject.h"
-#include "BaseInstance.h"
-#include "minecraft/MinecraftInstance.h"
-
-MCEditTool::MCEditTool(SettingsObjectPtr settings)
-{
- settings->registerSetting("MCEditPath");
- m_settings = settings;
-}
-
-void MCEditTool::setPath(QString& path)
-{
- m_settings->set("MCEditPath", path);
-}
-
-QString MCEditTool::path() const
-{
- 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;
-}
-
-QString MCEditTool::getProgramPath()
-{
-#ifdef Q_OS_OSX
- return path();
-#else
- 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();
-#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();
-#endif
-#endif
-}
diff --git a/api/logic/tools/MCEditTool.h b/api/logic/tools/MCEditTool.h
deleted file mode 100644
index 1465494e..00000000
--- a/api/logic/tools/MCEditTool.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include <QString>
-#include "settings/SettingsObject.h"
-#include "multimc_logic_export.h"
-
-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();
-private:
- SettingsObjectPtr m_settings;
-};
diff --git a/api/logic/translations/POTranslator.cpp b/api/logic/translations/POTranslator.cpp
deleted file mode 100644
index 1ffcb9a4..00000000
--- a/api/logic/translations/POTranslator.cpp
+++ /dev/null
@@ -1,373 +0,0 @@
-#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
deleted file mode 100644
index 6d518560..00000000
--- a/api/logic/translations/POTranslator.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#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
deleted file mode 100644
index 29a952b0..00000000
--- a/api/logic/translations/TranslationsModel.cpp
+++ /dev/null
@@ -1,653 +0,0 @@
-#include "TranslationsModel.h"
-
-#include <QCoreApplication>
-#include <QTranslator>
-#include <QLocale>
-#include <QDir>
-#include <QLibraryInfo>
-#include <QDebug>
-#include <FileSystem.h>
-#include <net/NetJob.h>
-#include <net/ChecksumValidator.h>
-#include <Env.h>
-#include <BuildConfig.h>
-#include "Json.h"
-
-#include "POTranslator.h"
-
-const static QLatin1Literal defaultLangCode("en_US");
-
-enum class FileType
-{
- NONE,
- QM,
- PO
-};
-
-struct Language
-{
- 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 = {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);
- 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 toplevel_doc = Json::requireDocument(data);
- auto doc = Json::requireObject(toplevel_doc);
- 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(), 0, d->m_languages.size() + languages.size() - 1);
- for(auto & language: languages)
- {
- d->m_languages.append(language);
- }
- std::sort(d->m_languages.begin(), d->m_languages.end(), [](const Language& a, const Language& b) {
- return a.key.compare(b.key) < 0;
- });
- 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();
- 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 lang.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 lang.key;
- default:
- return QVariant();
- }
-}
-
-QVariant TranslationsModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
- 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);
-}
-
-int TranslationsModel::rowCount(const QModelIndex& parent) const
-{
- return d->m_languages.size();
-}
-
-int TranslationsModel::columnCount(const QModelIndex& parent) const
-{
- return 2;
-}
-
-Language * TranslationsModel::findLanguage(const QString& key)
-{
- 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;
- }
-}
-
-bool TranslationsModel::selectLanguage(QString key)
-{
- 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 = QLocale(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;
-}
-
-QModelIndex TranslationsModel::selectedIndex()
-{
- 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();
-}
-
-QString TranslationsModel::selectedLanguage()
-{
- return d->m_selectedLanguage;
-}
-
-void TranslationsModel::downloadIndex()
-{
- 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);
- }
-}
-
-void TranslationsModel::downloadTranslation(QString key)
-{
- 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(BuildConfig.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();
- }
-}
-
-void TranslationsModel::dlFailed(QString reason)
-{
- 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();
-}
-
-void TranslationsModel::indexFailed(QString reason)
-{
- 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
deleted file mode 100644
index 17e5b124..00000000
--- a/api/logic/translations/TranslationsModel.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QAbstractListModel>
-#include <memory>
-#include "multimc_logic_export.h"
-
-struct Language;
-
-class MULTIMC_LOGIC_EXPORT TranslationsModel : public QAbstractListModel
-{
- Q_OBJECT
-public:
- explicit TranslationsModel(QString path, QObject *parent = 0);
- virtual ~TranslationsModel();
-
- 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();
-
- void downloadIndex();
-
-private:
- 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;
-
-private slots:
- 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;
-};
diff --git a/api/logic/updater/DownloadTask.cpp b/api/logic/updater/DownloadTask.cpp
deleted file mode 100644
index 20b26ebb..00000000
--- a/api/logic/updater/DownloadTask.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "DownloadTask.h"
-
-#include "updater/UpdateChecker.h"
-#include "GoUpdate.h"
-#include "net/NetJob.h"
-
-#include <QFile>
-#include <QTemporaryDir>
-#include <QCryptographicHash>
-
-namespace GoUpdate
-{
-
-DownloadTask::DownloadTask(Status status, QString target, QObject *parent)
- : Task(parent), m_updateFilesDir(target)
-{
- m_status = status;
-
- m_updateFilesDir.setAutoRemove(false);
-}
-
-void DownloadTask::executeTask()
-{
- loadVersionInfo();
-}
-
-void DownloadTask::loadVersionInfo()
-{
- setStatus(tr("Loading version information..."));
-
- 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;
-
- 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;
- }
-
- // 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."));
-}
-
-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);
-
- 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();
-}
-
-void DownloadTask::fileDownloadFailed(QString 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);
-}
-
-QString DownloadTask::updateFilesDir()
-{
- return m_updateFilesDir.path();
-}
-
-OperationList DownloadTask::operations()
-{
- return m_operations;
-}
-
-} \ No newline at end of file
diff --git a/api/logic/updater/DownloadTask.h b/api/logic/updater/DownloadTask.h
deleted file mode 100644
index 88e60865..00000000
--- a/api/logic/updater/DownloadTask.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "tasks/Task.h"
-#include "net/NetJob.h"
-#include "GoUpdate.h"
-
-#include "multimc_logic_export.h"
-
-namespace GoUpdate
-{
-/*!
- * The DownloadTask is a task that takes a given version ID and repository URL,
- * downloads that version's files from the repository, and prepares to install them.
- */
-class MULTIMC_LOGIC_EXPORT DownloadTask : public Task
-{
- 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);
- virtual ~DownloadTask() {};
-
- /// Get the directory that will contain the update files.
- QString updateFilesDir();
-
- /// Get the list of operations that should be done
- OperationList operations();
-
- /// 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;
-
-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);
-};
-
-} \ No newline at end of file
diff --git a/api/logic/updater/DownloadTask_test.cpp b/api/logic/updater/DownloadTask_test.cpp
deleted file mode 100644
index 8d5375e8..00000000
--- a/api/logic/updater/DownloadTask_test.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-#include <QTest>
-#include <QSignalSpy>
-
-#include "TestUtil.h"
-
-#include "updater/GoUpdate.h"
-#include "updater/DownloadTask.h"
-#include "updater/UpdateChecker.h"
-#include <FileSystem.h>
-
-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});
-}
-
-Q_DECLARE_METATYPE(VersionFileList)
-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();
-}
-
-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();
-}
-
-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();
-}
-
-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();
-}
-
-class DownloadTaskTest : public QObject
-{
- 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);
- }
-};
-
-extern "C"
-{
- QTEST_GUILESS_MAIN(DownloadTaskTest)
-}
-
-#include "DownloadTask_test.moc"
diff --git a/api/logic/updater/GoUpdate.cpp b/api/logic/updater/GoUpdate.cpp
deleted file mode 100644
index 6167418e..00000000
--- a/api/logic/updater/GoUpdate.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-#include "GoUpdate.h"
-#include <QDebug>
-#include <QDomDocument>
-#include <QFile>
-#include <FileSystem.h>
-
-#include "net/Download.h"
-#include "net/ChecksumValidator.h"
-
-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();
-
- 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
-)
-{
- // 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;
-}
-}
diff --git a/api/logic/updater/GoUpdate.h b/api/logic/updater/GoUpdate.h
deleted file mode 100644
index 8f92bb99..00000000
--- a/api/logic/updater/GoUpdate.h
+++ /dev/null
@@ -1,127 +0,0 @@
-#pragma once
-#include <QByteArray>
-#include <net/NetJob.h>
-
-#include "multimc_logic_export.h"
-
-namespace GoUpdate
-{
-
-/**
- * A temporary object exchanged between updated checker and the actual update task
- */
-struct MULTIMC_LOGIC_EXPORT Status
-{
- bool updateAvailable = false;
-
- int newVersionId = -1;
- QString newRepoUrl;
-
- int currentVersionId = -1;
- QString currentRepoUrl;
-
- // path to the root of the application
- QString rootPath;
-};
-
-/**
- * Struct that describes an entry in a VersionFileEntry's `Sources` list.
- */
-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;
-};
-typedef QList<FileSource> FileSourceList;
-
-/**
- * Structure that describes an entry in a GoUpdate version's `Files` list.
- */
-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;
- }
-};
-typedef QList<VersionFileEntry> VersionFileList;
-
-/**
- * Structure that describes an operation to perform when installing updates.
- */
-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;
-};
-typedef QList<Operation> OperationList;
-
-/**
- * Loads the file list from the given version info JSON object into the given list.
- */
-bool MULTIMC_LOGIC_EXPORT parseVersionInfo(const QByteArray &data, VersionFileList& list, QString &error);
-
-/*!
- * Takes a list of file entries for the current version's files and the new version's files
- * and populates the downloadList and operationList with information about how to download and install the update.
- */
-bool MULTIMC_LOGIC_EXPORT processFileLists
-(
- const VersionFileList &currentVersion,
- const VersionFileList &newVersion,
- const QString &rootPath,
- const QString &tempPath,
- NetJobPtr job,
- OperationList &ops
-);
-
-}
-Q_DECLARE_METATYPE(GoUpdate::Status)
diff --git a/api/logic/updater/UpdateChecker.cpp b/api/logic/updater/UpdateChecker.cpp
deleted file mode 100644
index be33c73c..00000000
--- a/api/logic/updater/UpdateChecker.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "UpdateChecker.h"
-
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonValue>
-#include <QDebug>
-
-#define API_VERSION 0
-#define CHANLIST_FORMAT 0
-
-UpdateChecker::UpdateChecker(QString channelListUrl, QString currentChannel, int currentBuild)
-{
- m_channelListUrl = channelListUrl;
- m_currentChannel = currentChannel;
- m_currentBuild = currentBuild;
-}
-
-QList<UpdateChecker::ChannelListEntry> UpdateChecker::getChannelList() const
-{
- return m_channels;
-}
-
-bool UpdateChecker::hasChannels() const
-{
- 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();
-}
-
-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;
-}
-
-void UpdateChecker::updateCheckFailed()
-{
- 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();
-}
-
-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();
-}
-
-void UpdateChecker::chanListDownloadFailed(QString reason)
-{
- 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
deleted file mode 100644
index 20906207..00000000
--- a/api/logic/updater/UpdateChecker.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "net/NetJob.h"
-#include "GoUpdate.h"
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT UpdateChecker : public QObject
-{
- 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;
-
-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 the channel list finishes loading or fails to load.
- void channelListLoaded();
-
- void noUpdateFound();
-
-private slots:
- void updateCheckFinished(bool notifyNoUpdate);
- void updateCheckFailed();
-
- 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;
-};
-
diff --git a/api/logic/updater/UpdateChecker_test.cpp b/api/logic/updater/UpdateChecker_test.cpp
deleted file mode 100644
index 5702d9c6..00000000
--- a/api/logic/updater/UpdateChecker_test.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-#include <QTest>
-#include <QSignalSpy>
-
-#include "TestUtil.h"
-#include "updater/UpdateChecker.h"
-
-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 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();
-}
-
-QString findTestDataUrl(const char *file)
-{
- return QUrl::fromLocalFile(QFINDTESTDATA(file)).toString();
-}
-
-class UpdateCheckerTest : public QObject
-{
- Q_OBJECT
-private
-slots:
- 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)
-
-#include "UpdateChecker_test.moc"
diff --git a/api/logic/updater/testdata/1.json b/api/logic/updater/testdata/1.json
deleted file mode 100644
index 7af7e52d..00000000
--- a/api/logic/updater/testdata/1.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "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
deleted file mode 100644
index 96d430d5..00000000
--- a/api/logic/updater/testdata/2.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "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
deleted file mode 100644
index 5c6e42cb..00000000
--- a/api/logic/updater/testdata/channels.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "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
deleted file mode 100644
index a2cb2165..00000000
--- a/api/logic/updater/testdata/errorChannels.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "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/fileOneA b/api/logic/updater/testdata/fileOneA
deleted file mode 100644
index f2e41136..00000000
--- a/api/logic/updater/testdata/fileOneA
+++ /dev/null
@@ -1 +0,0 @@
-stuff
diff --git a/api/logic/updater/testdata/fileOneB b/api/logic/updater/testdata/fileOneB
deleted file mode 100644
index f9aba922..00000000
--- a/api/logic/updater/testdata/fileOneB
+++ /dev/null
@@ -1,3 +0,0 @@
-stuff
-
-more stuff that came in the new version
diff --git a/api/logic/updater/testdata/fileThree b/api/logic/updater/testdata/fileThree
deleted file mode 100644
index 6353ff16..00000000
--- a/api/logic/updater/testdata/fileThree
+++ /dev/null
@@ -1 +0,0 @@
-this is yet another file
diff --git a/api/logic/updater/testdata/fileTwo b/api/logic/updater/testdata/fileTwo
deleted file mode 100644
index aad9a93a..00000000
--- a/api/logic/updater/testdata/fileTwo
+++ /dev/null
@@ -1 +0,0 @@
-some other stuff
diff --git a/api/logic/updater/testdata/garbageChannels.json b/api/logic/updater/testdata/garbageChannels.json
deleted file mode 100644
index 34437451..00000000
--- a/api/logic/updater/testdata/garbageChannels.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "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
deleted file mode 100644
index 867bdcfb..00000000
--- a/api/logic/updater/testdata/index.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "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
deleted file mode 100644
index 76988982..00000000
--- a/api/logic/updater/testdata/noChannels.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "format_version": 0,
- "channels": [
- ]
-}
diff --git a/api/logic/updater/testdata/oneChannel.json b/api/logic/updater/testdata/oneChannel.json
deleted file mode 100644
index cc8ed255..00000000
--- a/api/logic/updater/testdata/oneChannel.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "format_version": 0,
- "channels": [
- {
- "id": "develop",
- "name": "Develop",
- "description": "The channel called \"develop\"",
- "url": "http://example.org/stuff"
- }
- ]
-}
diff --git a/api/logic/updater/testdata/tst_DownloadTask-test_writeInstallScript.xml b/api/logic/updater/testdata/tst_DownloadTask-test_writeInstallScript.xml
deleted file mode 100644
index 09c162ca..00000000
--- a/api/logic/updater/testdata/tst_DownloadTask-test_writeInstallScript.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<update version="3">
- <install>
- <file>
- <source>sourceOne</source>
- <dest>destOne</dest>
- <mode>0777</mode>
- </file>
- <file>
- <source>MultiMC.exe</source>
- <dest>M/u/l/t/i/M/C/e/x/e</dest>
- <mode>0644</mode>
- </file>
- </install>
- <uninstall>
- <file>toDelete.abc</file>
- </uninstall>
-</update>