aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt253
-rw-r--r--MultiMC.cpp29
-rw-r--r--MultiMC.h15
-rw-r--r--depends/launcher/CMakeLists.txt2
-rw-r--r--depends/launcher/net/minecraft/Launcher.java15
-rw-r--r--depends/launcher/org/multimc/LegacyFrame.java (renamed from depends/launcher/org/multimc/legacy/LegacyFrame.java)2
-rw-r--r--depends/launcher/org/multimc/legacy/LegacyLauncher.java5
-rw-r--r--depends/launcher/org/multimc/onesix/OneSixLauncher.java331
-rw-r--r--gui/MainWindow.cpp189
-rw-r--r--gui/MainWindow.h17
-rw-r--r--gui/MainWindow.ui90
-rw-r--r--gui/dialogs/CopyInstanceDialog.cpp1
-rw-r--r--gui/dialogs/EditNotesDialog.cpp43
-rw-r--r--gui/dialogs/EditNotesDialog.ui77
-rw-r--r--gui/dialogs/InstanceSettings.cpp243
-rw-r--r--gui/dialogs/LegacyModEditDialog.cpp393
-rw-r--r--gui/dialogs/LegacyModEditDialog.h78
-rw-r--r--gui/dialogs/LegacyModEditDialog.ui321
-rw-r--r--gui/dialogs/LwjglSelectDialog.cpp2
-rw-r--r--gui/dialogs/ModEditDialogCommon.cpp21
-rw-r--r--gui/dialogs/ModEditDialogCommon.h21
-rw-r--r--gui/dialogs/NewInstanceDialog.cpp10
-rw-r--r--gui/dialogs/NewInstanceDialog.h2
-rw-r--r--gui/dialogs/OneSixModEditDialog.cpp399
-rw-r--r--gui/dialogs/OneSixModEditDialog.ui310
-rw-r--r--gui/dialogs/SettingsDialog.cpp16
-rw-r--r--gui/dialogs/SettingsDialog.h2
-rw-r--r--gui/dialogs/VersionSelectDialog.cpp2
-rw-r--r--gui/groupview/InstanceDelegate.cpp2
-rw-r--r--gui/pagedialog/PageDialog.cpp199
-rw-r--r--gui/pagedialog/PageDialog.h56
-rw-r--r--gui/pagedialog/PageDialog_p.h106
-rw-r--r--gui/pages/BasePage.h (renamed from logic/NostalgiaInstance.h)35
-rw-r--r--gui/pages/BasePageProvider.h (renamed from gui/dialogs/EditNotesDialog.h)26
-rw-r--r--gui/pages/InstanceSettingsPage.cpp223
-rw-r--r--gui/pages/InstanceSettingsPage.h (renamed from gui/dialogs/InstanceSettings.h)47
-rw-r--r--gui/pages/InstanceSettingsPage.ui (renamed from gui/dialogs/InstanceSettings.ui)353
-rw-r--r--gui/pages/LegacyJarModPage.cpp203
-rw-r--r--gui/pages/LegacyJarModPage.h62
-rw-r--r--gui/pages/LegacyJarModPage.ui158
-rw-r--r--gui/pages/LegacyUpgradePage.cpp33
-rw-r--r--gui/pages/LegacyUpgradePage.h47
-rw-r--r--gui/pages/LegacyUpgradePage.ui58
-rw-r--r--gui/pages/ModFolderPage.cpp141
-rw-r--r--gui/pages/ModFolderPage.h61
-rw-r--r--gui/pages/ModFolderPage.ui118
-rw-r--r--gui/pages/NotesPage.cpp35
-rw-r--r--gui/pages/NotesPage.h45
-rw-r--r--gui/pages/NotesPage.ui46
-rw-r--r--gui/pages/ResourcePackPage.h22
-rw-r--r--gui/pages/TexturePackPage.h20
-rw-r--r--gui/pages/VersionPage.cpp389
-rw-r--r--gui/pages/VersionPage.h (renamed from gui/dialogs/OneSixModEditDialog.h)42
-rw-r--r--gui/pages/VersionPage.ui197
-rw-r--r--gui/widgets/LineSeparator.h4
-rw-r--r--gui/widgets/ServerStatus.cpp2
-rw-r--r--logic/BaseInstaller.cpp9
-rw-r--r--logic/BaseInstaller.h2
-rw-r--r--logic/BaseInstance.cpp4
-rw-r--r--logic/BaseInstance.h24
-rw-r--r--logic/BaseVersion.h4
-rw-r--r--logic/BaseVersionList.cpp (renamed from logic/lists/BaseVersionList.cpp)2
-rw-r--r--logic/BaseVersionList.h (renamed from logic/lists/BaseVersionList.h)0
-rw-r--r--logic/InstanceFactory.cpp79
-rw-r--r--logic/InstanceLauncher.cpp2
-rw-r--r--logic/InstanceList.cpp (renamed from logic/lists/InstanceList.cpp)16
-rw-r--r--logic/InstanceList.h (renamed from logic/lists/InstanceList.h)0
-rw-r--r--logic/LegacyFTBInstance.cpp5
-rw-r--r--logic/LegacyFTBInstance.h1
-rw-r--r--logic/LegacyInstance.cpp55
-rw-r--r--logic/LegacyInstance.h14
-rw-r--r--logic/LegacyUpdate.cpp85
-rw-r--r--logic/LegacyUpdate.h9
-rw-r--r--logic/LwjglVersionList.cpp (renamed from logic/lists/LwjglVersionList.cpp)0
-rw-r--r--logic/LwjglVersionList.h (renamed from logic/lists/LwjglVersionList.h)0
-rw-r--r--logic/MMCJson.cpp24
-rw-r--r--logic/MMCJson.h19
-rw-r--r--logic/MinecraftVersion.h89
-rw-r--r--logic/ModList.cpp1
-rw-r--r--logic/NostalgiaInstance.cpp36
-rw-r--r--logic/OneSixFTBInstance.cpp16
-rw-r--r--logic/OneSixFTBInstance.h1
-rw-r--r--logic/OneSixInstance.cpp299
-rw-r--r--logic/OneSixInstance.h44
-rw-r--r--logic/OneSixInstance_p.h13
-rw-r--r--logic/OneSixUpdate.cpp357
-rw-r--r--logic/OneSixUpdate.h21
-rw-r--r--logic/OneSixVersionBuilder.cpp252
-rw-r--r--logic/OneSixVersionBuilder.h47
-rw-r--r--logic/URNResolver.cpp98
-rw-r--r--logic/URNResolver.h18
-rw-r--r--logic/VersionFile.h127
-rw-r--r--logic/VersionFilterData.cpp68
-rw-r--r--logic/VersionFilterData.h26
-rw-r--r--logic/VersionFinal.cpp377
-rw-r--r--logic/forge/ForgeInstaller.cpp (renamed from logic/ForgeInstaller.cpp)194
-rw-r--r--logic/forge/ForgeInstaller.h (renamed from logic/ForgeInstaller.h)22
-rw-r--r--logic/forge/ForgeMirror.h (renamed from logic/net/ForgeMirror.h)0
-rw-r--r--logic/forge/ForgeMirrors.cpp (renamed from logic/net/ForgeMirrors.cpp)0
-rw-r--r--logic/forge/ForgeMirrors.h (renamed from logic/net/ForgeMirrors.h)8
-rw-r--r--logic/forge/ForgeVersion.cpp55
-rw-r--r--logic/forge/ForgeVersion.h33
-rw-r--r--logic/forge/ForgeVersionList.cpp (renamed from logic/lists/ForgeVersionList.cpp)28
-rw-r--r--logic/forge/ForgeVersionList.h (renamed from logic/lists/ForgeVersionList.h)55
-rw-r--r--logic/forge/ForgeXzDownload.cpp (renamed from logic/net/ForgeXzDownload.cpp)0
-rw-r--r--logic/forge/ForgeXzDownload.h (renamed from logic/net/ForgeXzDownload.h)4
-rw-r--r--logic/forge/LegacyForge.cpp (renamed from logic/LegacyForge.cpp)0
-rw-r--r--logic/forge/LegacyForge.h (renamed from logic/LegacyForge.h)2
-rw-r--r--logic/java/JavaChecker.cpp (renamed from logic/JavaChecker.cpp)0
-rw-r--r--logic/java/JavaChecker.h (renamed from logic/JavaChecker.h)0
-rw-r--r--logic/java/JavaCheckerJob.cpp (renamed from logic/JavaCheckerJob.cpp)0
-rw-r--r--logic/java/JavaCheckerJob.h (renamed from logic/JavaCheckerJob.h)0
-rw-r--r--logic/java/JavaUtils.cpp (renamed from logic/JavaUtils.cpp)8
-rw-r--r--logic/java/JavaUtils.h (renamed from logic/JavaUtils.h)2
-rw-r--r--logic/java/JavaVersionList.cpp (renamed from logic/lists/JavaVersionList.cpp)10
-rw-r--r--logic/java/JavaVersionList.h (renamed from logic/lists/JavaVersionList.h)4
-rw-r--r--logic/lists/MinecraftVersionList.cpp290
-rw-r--r--logic/liteloader/LiteLoaderInstaller.cpp (renamed from logic/LiteLoaderInstaller.cpp)8
-rw-r--r--logic/liteloader/LiteLoaderInstaller.h (renamed from logic/LiteLoaderInstaller.h)5
-rw-r--r--logic/liteloader/LiteLoaderVersionList.cpp (renamed from logic/lists/LiteLoaderVersionList.cpp)0
-rw-r--r--logic/liteloader/LiteLoaderVersionList.h (renamed from logic/lists/LiteLoaderVersionList.h)6
-rw-r--r--logic/minecraft/InstanceVersion.cpp537
-rw-r--r--logic/minecraft/InstanceVersion.h (renamed from logic/VersionFinal.h)73
-rw-r--r--logic/minecraft/JarMod.cpp56
-rw-r--r--logic/minecraft/JarMod.h18
-rw-r--r--logic/minecraft/MinecraftVersion.cpp143
-rw-r--r--logic/minecraft/MinecraftVersion.h103
-rw-r--r--logic/minecraft/MinecraftVersionList.cpp602
-rw-r--r--logic/minecraft/MinecraftVersionList.h (renamed from logic/lists/MinecraftVersionList.h)42
-rw-r--r--logic/minecraft/OneSixLibrary.cpp (renamed from logic/OneSixLibrary.cpp)79
-rw-r--r--logic/minecraft/OneSixLibrary.h (renamed from logic/OneSixLibrary.h)34
-rw-r--r--logic/minecraft/OneSixRule.cpp (renamed from logic/OneSixRule.cpp)17
-rw-r--r--logic/minecraft/OneSixRule.h (renamed from logic/OneSixRule.h)8
-rw-r--r--logic/minecraft/OpSys.cpp (renamed from logic/OpSys.cpp)0
-rw-r--r--logic/minecraft/OpSys.h (renamed from logic/OpSys.h)0
-rw-r--r--logic/minecraft/ParseUtils.cpp24
-rw-r--r--logic/minecraft/ParseUtils.h14
-rw-r--r--logic/minecraft/RawLibrary.cpp205
-rw-r--r--logic/minecraft/RawLibrary.h64
-rw-r--r--logic/minecraft/VersionBuildError.h58
-rw-r--r--logic/minecraft/VersionBuilder.cpp349
-rw-r--r--logic/minecraft/VersionBuilder.h56
-rw-r--r--logic/minecraft/VersionFile.cpp (renamed from logic/VersionFile.cpp)378
-rw-r--r--logic/minecraft/VersionFile.h103
-rw-r--r--logic/minecraft/VersionPatch.h31
-rw-r--r--logic/minecraft/VersionSource.h9
-rw-r--r--logic/net/NetJob.h1
-rw-r--r--logic/tasks/ProgressProvider.h1
-rw-r--r--main.cpp1
-rw-r--r--resources/multimc/16x16/plugin-blue.pngbin0 -> 731 bytes
-rw-r--r--resources/multimc/16x16/plugin-green.pngbin0 -> 702 bytes
-rw-r--r--resources/multimc/16x16/plugin-red.pngbin0 -> 693 bytes
-rw-r--r--resources/multimc/16x16/resourcepacks.pngbin0 -> 1207 bytes
-rw-r--r--resources/multimc/24x24/plugin-blue.pngbin0 -> 1240 bytes
-rw-r--r--resources/multimc/24x24/plugin-green.pngbin0 -> 1281 bytes
-rw-r--r--resources/multimc/24x24/plugin-red.pngbin0 -> 1170 bytes
-rw-r--r--resources/multimc/24x24/resourcepacks.pngbin0 -> 2000 bytes
-rw-r--r--resources/multimc/32x32/plugin-blue.pngbin0 -> 1708 bytes
-rw-r--r--resources/multimc/32x32/plugin-green.pngbin0 -> 1758 bytes
-rw-r--r--resources/multimc/32x32/plugin-red.pngbin0 -> 1566 bytes
-rw-r--r--resources/multimc/32x32/resourcepacks.pngbin0 -> 2818 bytes
-rw-r--r--resources/multimc/64x64/plugin-blue.pngbin0 -> 4406 bytes
-rw-r--r--resources/multimc/64x64/plugin-green.pngbin0 -> 5036 bytes
-rw-r--r--resources/multimc/64x64/plugin-red.pngbin0 -> 4003 bytes
-rw-r--r--resources/multimc/64x64/resourcepacks.pngbin0 -> 6805 bytes
-rw-r--r--resources/multimc/multimc.qrc24
-rw-r--r--resources/versions/LWJGL/2.9.0.json45
-rw-r--r--resources/versions/LWJGL/2.9.1-nightly-20130708-debug3.json45
-rw-r--r--resources/versions/LWJGL/2.9.1.json45
-rw-r--r--resources/versions/minecraft.json668
-rw-r--r--resources/versions/versions.qrc11
171 files changed, 7691 insertions, 4750 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 345bb366..af8dca4b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -291,48 +291,61 @@ SET(MULTIMC_SOURCES
gui/ConsoleWindow.h
gui/ConsoleWindow.cpp
+ # GUI - page dialog and pages
+ gui/pagedialog/PageDialog.cpp
+ gui/pagedialog/PageDialog.h
+ gui/pagedialog/PageDialog_p.h
+ gui/pages/VersionPage.cpp
+ gui/pages/VersionPage.h
+ gui/pages/TexturePackPage.h
+ gui/pages/ResourcePackPage.h
+ gui/pages/ModFolderPage.cpp
+ gui/pages/ModFolderPage.h
+ gui/pages/NotesPage.cpp
+ gui/pages/NotesPage.h
+ gui/pages/LegacyUpgradePage.cpp
+ gui/pages/LegacyUpgradePage.h
+ gui/pages/LegacyJarModPage.cpp
+ gui/pages/LegacyJarModPage.h
+ gui/pages/InstanceSettingsPage.cpp
+ gui/pages/InstanceSettingsPage.h
+
# GUI - dialogs
- gui/dialogs/SettingsDialog.h
- gui/dialogs/SettingsDialog.cpp
- gui/dialogs/CopyInstanceDialog.h
- gui/dialogs/CopyInstanceDialog.cpp
- gui/dialogs/NewInstanceDialog.cpp
- gui/dialogs/ProgressDialog.h
- gui/dialogs/ProgressDialog.cpp
- gui/dialogs/AboutDialog.h
gui/dialogs/AboutDialog.cpp
- gui/dialogs/VersionSelectDialog.h
- gui/dialogs/VersionSelectDialog.cpp
- gui/dialogs/LwjglSelectDialog.h
- gui/dialogs/LwjglSelectDialog.cpp
- gui/dialogs/InstanceSettings.h
- gui/dialogs/InstanceSettings.cpp
- gui/dialogs/IconPickerDialog.h
- gui/dialogs/IconPickerDialog.cpp
- gui/dialogs/LegacyModEditDialog.h
- gui/dialogs/LegacyModEditDialog.cpp
- gui/dialogs/OneSixModEditDialog.h
- gui/dialogs/OneSixModEditDialog.cpp
- gui/dialogs/ModEditDialogCommon.h
- gui/dialogs/ModEditDialogCommon.cpp
- gui/dialogs/EditNotesDialog.h
- gui/dialogs/EditNotesDialog.cpp
- gui/dialogs/CustomMessageBox.h
+ gui/dialogs/AboutDialog.h
+ gui/dialogs/AccountListDialog.cpp
+ gui/dialogs/AccountListDialog.h
+ gui/dialogs/AccountSelectDialog.cpp
+ gui/dialogs/AccountSelectDialog.h
+ gui/dialogs/CopyInstanceDialog.cpp
+ gui/dialogs/CopyInstanceDialog.h
gui/dialogs/CustomMessageBox.cpp
- gui/dialogs/EditAccountDialog.h
+ gui/dialogs/CustomMessageBox.h
gui/dialogs/EditAccountDialog.cpp
- gui/dialogs/LoginDialog.h
+ gui/dialogs/EditAccountDialog.h
+ gui/dialogs/IconPickerDialog.cpp
+ gui/dialogs/IconPickerDialog.h
gui/dialogs/LoginDialog.cpp
- gui/dialogs/AccountListDialog.h
- gui/dialogs/AccountListDialog.cpp
- gui/dialogs/AccountSelectDialog.h
- gui/dialogs/AccountSelectDialog.cpp
- gui/dialogs/UpdateDialog.h
- gui/dialogs/UpdateDialog.cpp
- gui/dialogs/ScreenshotDialog.h
- gui/dialogs/ScreenshotDialog.cpp
- gui/dialogs/NotificationDialog.h
+ gui/dialogs/LoginDialog.h
+ gui/dialogs/LwjglSelectDialog.cpp
+ gui/dialogs/LwjglSelectDialog.h
+ gui/dialogs/ModEditDialogCommon.cpp
+ gui/dialogs/ModEditDialogCommon.h
+ gui/dialogs/NewInstanceDialog.cpp
+ gui/dialogs/NewInstanceDialog.h
gui/dialogs/NotificationDialog.cpp
+ gui/dialogs/NotificationDialog.h
+ gui/dialogs/ProgressDialog.cpp
+ gui/dialogs/ProgressDialog.h
+ gui/dialogs/ScreenshotDialog.cpp
+ gui/dialogs/ScreenshotDialog.h
+ gui/dialogs/SettingsDialog.cpp
+ gui/dialogs/SettingsDialog.h
+ gui/dialogs/UpdateDialog.cpp
+ gui/dialogs/UpdateDialog.h
+ gui/dialogs/VersionSelectDialog.cpp
+ gui/dialogs/VersionSelectDialog.h
+
# GUI - widgets
gui/widgets/Common.cpp
@@ -363,25 +376,43 @@ SET(MULTIMC_SOURCES
gui/groupview/InstanceDelegate.cpp
gui/groupview/InstanceDelegate.h
- # Base classes and infrastructure
+ # LOGIC - Base classes and infrastructure
logic/BaseVersion.h
- logic/MinecraftVersion.h
logic/InstanceFactory.h
logic/InstanceFactory.cpp
logic/BaseInstance.h
logic/BaseInstance.cpp
logic/BaseInstance_p.h
-
- logic/MinecraftProcess.h
- logic/MinecraftProcess.cpp
logic/Mod.h
logic/Mod.cpp
logic/ModList.h
logic/ModList.cpp
+
+ # sets and maps for deciding based on versions
+ logic/VersionFilterData.h
+ logic/VersionFilterData.cpp
- # Basic instance launcher for starting from terminal
+ # Instance launch
logic/InstanceLauncher.h
logic/InstanceLauncher.cpp
+ logic/MinecraftProcess.h
+ logic/MinecraftProcess.cpp
+
+ # URN parser/resolver
+ logic/URNResolver.cpp
+ logic/URNResolver.h
+
+ # Annoying nag screen logic
+ logic/NagUtils.h
+ logic/NagUtils.cpp
+
+ # Player skin utilities
+ logic/SkinUtils.h
+ logic/SkinUtils.cpp
+
+ # misc model filter
+ logic/EnabledItemFilter.h
+ logic/EnabledItemFilter.cpp
# JSON parsing helpers
logic/MMCJson.h
@@ -395,10 +426,6 @@ SET(MULTIMC_SOURCES
logic/net/ByteArrayDownload.cpp
logic/net/CacheDownload.h
logic/net/CacheDownload.cpp
- logic/net/ForgeMirrors.h
- logic/net/ForgeMirrors.cpp
- logic/net/ForgeXzDownload.h
- logic/net/ForgeXzDownload.cpp
logic/net/NetJob.h
logic/net/NetJob.cpp
logic/net/HttpMetaCache.h
@@ -449,9 +476,6 @@ SET(MULTIMC_SOURCES
logic/LegacyUpdate.h
logic/LegacyUpdate.cpp
- logic/LegacyForge.h
- logic/LegacyForge.cpp
-
# OneSix instances
logic/OneSixUpdate.h
logic/OneSixUpdate.cpp
@@ -460,30 +484,42 @@ SET(MULTIMC_SOURCES
logic/OneSixInstance_p.h
# OneSix version json infrastructure
- logic/OneSixVersionBuilder.h
- logic/OneSixVersionBuilder.cpp
- logic/VersionFile.h
- logic/VersionFile.cpp
- logic/VersionFinal.h
- logic/VersionFinal.cpp
- logic/OneSixLibrary.h
- logic/OneSixLibrary.cpp
- logic/OneSixRule.h
- logic/OneSixRule.cpp
- logic/OpSys.h
- logic/OpSys.cpp
-
- # Mod installers
+ logic/minecraft/InstanceVersion.cpp
+ logic/minecraft/InstanceVersion.h
+ logic/minecraft/JarMod.cpp
+ logic/minecraft/JarMod.h
+ logic/minecraft/MinecraftVersion.cpp
+ logic/minecraft/MinecraftVersion.h
+ logic/minecraft/MinecraftVersionList.cpp
+ logic/minecraft/MinecraftVersionList.h
+ logic/minecraft/OneSixLibrary.cpp
+ logic/minecraft/OneSixLibrary.h
+ logic/minecraft/OneSixRule.cpp
+ logic/minecraft/OneSixRule.h
+ logic/minecraft/OpSys.cpp
+ logic/minecraft/OpSys.h
+ logic/minecraft/ParseUtils.cpp
+ logic/minecraft/ParseUtils.h
+ logic/minecraft/RawLibrary.cpp
+ logic/minecraft/RawLibrary.h
+ logic/minecraft/VersionBuilder.cpp
+ logic/minecraft/VersionBuilder.h
+ logic/minecraft/VersionBuildError.h
+ logic/minecraft/VersionFile.cpp
+ logic/minecraft/VersionFile.h
+ logic/minecraft/VersionPatch.h
+ logic/minecraft/VersionSource.h
+
+ # Various base classes
logic/BaseInstaller.h
logic/BaseInstaller.cpp
- logic/ForgeInstaller.h
- logic/ForgeInstaller.cpp
- logic/LiteLoaderInstaller.h
- logic/LiteLoaderInstaller.cpp
+ logic/BaseVersionList.h
+ logic/BaseVersionList.cpp
- # Nostalgia
- logic/NostalgiaInstance.h
- logic/NostalgiaInstance.cpp
+ logic/InstanceList.h
+ logic/InstanceList.cpp
+ logic/LwjglVersionList.h
+ logic/LwjglVersionList.cpp
# FTB
logic/OneSixFTBInstance.h
@@ -491,22 +527,6 @@ SET(MULTIMC_SOURCES
logic/LegacyFTBInstance.h
logic/LegacyFTBInstance.cpp
- # Lists
- logic/lists/InstanceList.h
- logic/lists/InstanceList.cpp
- logic/lists/BaseVersionList.h
- logic/lists/BaseVersionList.cpp
- logic/lists/MinecraftVersionList.h
- logic/lists/MinecraftVersionList.cpp
- logic/lists/LwjglVersionList.h
- logic/lists/LwjglVersionList.cpp
- logic/lists/ForgeVersionList.h
- logic/lists/ForgeVersionList.cpp
- logic/lists/JavaVersionList.h
- logic/lists/JavaVersionList.cpp
- logic/lists/LiteLoaderVersionList.h
- logic/lists/LiteLoaderVersionList.cpp
-
# the screenshots feature
logic/screenshots/Screenshot.h
logic/screenshots/Screenshot.cpp
@@ -523,11 +543,6 @@ SET(MULTIMC_SOURCES
logic/icons/IconList.h
logic/icons/IconList.cpp
-
- # misc model/view
- logic/EnabledItemFilter.h
- logic/EnabledItemFilter.cpp
-
# Tasks
logic/tasks/ProgressProvider.h
logic/tasks/Task.h
@@ -537,17 +552,15 @@ SET(MULTIMC_SOURCES
logic/tasks/SequentialTask.h
logic/tasks/SequentialTask.cpp
- # Utilities
- logic/JavaChecker.h
- logic/JavaChecker.cpp
- logic/JavaUtils.h
- logic/JavaUtils.cpp
- logic/NagUtils.h
- logic/NagUtils.cpp
- logic/SkinUtils.h
- logic/SkinUtils.cpp
- logic/JavaCheckerJob.h
- logic/JavaCheckerJob.cpp
+ # Java related code
+ logic/java/JavaChecker.h
+ logic/java/JavaChecker.cpp
+ logic/java/JavaUtils.h
+ logic/java/JavaUtils.cpp
+ logic/java/JavaVersionList.h
+ logic/java/JavaVersionList.cpp
+ logic/java/JavaCheckerJob.h
+ logic/java/JavaCheckerJob.cpp
# Assets
logic/assets/AssetsMigrateTask.h
@@ -566,6 +579,27 @@ SET(MULTIMC_SOURCES
logic/tools/JProfiler.cpp
logic/tools/JVisualVM.h
logic/tools/JVisualVM.cpp
+
+ # Forge and all things forge related
+ logic/forge/ForgeVersion.h
+ logic/forge/ForgeVersion.cpp
+ logic/forge/ForgeVersionList.h
+ logic/forge/ForgeVersionList.cpp
+ logic/forge/ForgeMirror.h
+ logic/forge/ForgeMirrors.h
+ logic/forge/ForgeMirrors.cpp
+ logic/forge/ForgeXzDownload.h
+ logic/forge/ForgeXzDownload.cpp
+ logic/forge/LegacyForge.h
+ logic/forge/LegacyForge.cpp
+ logic/forge/ForgeInstaller.h
+ logic/forge/ForgeInstaller.cpp
+
+ # Liteloader and related things
+ logic/liteloader/LiteLoaderInstaller.h
+ logic/liteloader/LiteLoaderInstaller.cpp
+ logic/liteloader/LiteLoaderVersionList.h
+ logic/liteloader/LiteLoaderVersionList.cpp
)
@@ -575,6 +609,14 @@ SET(MULTIMC_UIS
gui/MainWindow.ui
gui/ConsoleWindow.ui
+ # Option pages
+ gui/pages/VersionPage.ui
+ gui/pages/ModFolderPage.ui
+ gui/pages/LegacyUpgradePage.ui
+ gui/pages/LegacyJarModPage.ui
+ gui/pages/InstanceSettingsPage.ui
+ gui/pages/NotesPage.ui
+
# Dialogs
gui/dialogs/SettingsDialog.ui
gui/dialogs/CopyInstanceDialog.ui
@@ -582,12 +624,8 @@ SET(MULTIMC_UIS
gui/dialogs/AboutDialog.ui
gui/dialogs/VersionSelectDialog.ui
gui/dialogs/LwjglSelectDialog.ui
- gui/dialogs/InstanceSettings.ui
gui/dialogs/ProgressDialog.ui
gui/dialogs/IconPickerDialog.ui
- gui/dialogs/LegacyModEditDialog.ui
- gui/dialogs/OneSixModEditDialog.ui
- gui/dialogs/EditNotesDialog.ui
gui/dialogs/AccountListDialog.ui
gui/dialogs/AccountSelectDialog.ui
gui/dialogs/EditAccountDialog.ui
@@ -615,6 +653,7 @@ set(MULTIMC_QRCS
resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc
resources/instances/instances.qrc
+ resources/versions/versions.qrc
)
@@ -795,8 +834,6 @@ include(CPack)
include(Coverity)
-include_directories(${PROJECT_BINARY_DIR}/include)
-
# Translations
add_subdirectory(translations)
diff --git a/MultiMC.cpp b/MultiMC.cpp
index c1fc089c..6d01e4af 100644
--- a/MultiMC.cpp
+++ b/MultiMC.cpp
@@ -12,13 +12,14 @@
#include <QDesktopServices>
#include "gui/dialogs/VersionSelectDialog.h"
-#include "logic/lists/InstanceList.h"
+#include "logic/InstanceList.h"
#include "logic/auth/MojangAccountList.h"
#include "logic/icons/IconList.h"
-#include "logic/lists/LwjglVersionList.h"
-#include "logic/lists/MinecraftVersionList.h"
-#include "logic/lists/ForgeVersionList.h"
-#include "logic/lists/LiteLoaderVersionList.h"
+#include "logic/LwjglVersionList.h"
+#include "logic/minecraft/MinecraftVersionList.h"
+#include "logic/liteloader/LiteLoaderVersionList.h"
+
+#include "logic/forge/ForgeVersionList.h"
#include "logic/news/NewsChecker.h"
@@ -28,7 +29,7 @@
#include "logic/net/HttpMetaCache.h"
#include "logic/net/URLConstants.h"
-#include "logic/JavaUtils.h"
+#include "logic/java/JavaUtils.h"
#include "logic/updater/UpdateChecker.h"
#include "logic/updater/NotificationChecker.h"
@@ -37,6 +38,8 @@
#include "logic/tools/JVisualVM.h"
#include "logic/tools/MCEditTool.h"
+#include "logic/URNResolver.h"
+
#include "pathutils.h"
#include "cmdutils.h"
#include <inisettingsobject.h>
@@ -336,7 +339,7 @@ void MultiMC::initLogger()
QsLogging::Logger &logger = QsLogging::Logger::instance();
logger.setLoggingLevel(QsLogging::TraceLevel);
m_fileDestination = QsLogging::DestinationFactory::MakeFileDestination(logBase.arg(0));
- m_debugDestination = QsLogging::DestinationFactory::MakeQDebugDestination();
+ m_debugDestination = QsLogging::DestinationFactory::MakeDebugOutputDestination();
logger.addDestination(m_fileDestination.get());
logger.addDestination(m_debugDestination.get());
// log all the things
@@ -504,6 +507,8 @@ void MultiMC::initGlobalSettings()
m_settings->registerSetting("ConsoleWindowGeometry", "");
m_settings->registerSetting("SettingsGeometry", "");
+
+ m_settings->registerSetting("PagedGeometry", "");
}
void MultiMC::initHttpMetaCache()
@@ -643,6 +648,16 @@ std::shared_ptr<JavaVersionList> MultiMC::javalist()
return m_javalist;
}
+std::shared_ptr<URNResolver> MultiMC::resolver()
+{
+ if (!m_resolver)
+ {
+ m_resolver.reset(new URNResolver());
+ }
+ return m_resolver;
+}
+
+
void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags)
{
// if we are going to update on exit, save the params now
diff --git a/MultiMC.h b/MultiMC.h
index 0fd60b7d..ce3f9238 100644
--- a/MultiMC.h
+++ b/MultiMC.h
@@ -23,21 +23,13 @@ class NewsChecker;
class StatusChecker;
class BaseProfilerFactory;
class BaseDetachedToolFactory;
+class URNResolver;
#if defined(MMC)
#undef MMC
#endif
#define MMC (static_cast<MultiMC *>(QCoreApplication::instance()))
-// FIXME: possibly move elsewhere
-enum InstSortMode
-{
- // Sort alphabetically by name.
- Sort_Name,
- // Sort by which instance was launched most recently.
- Sort_LastLaunch
-};
-
enum UpdateFlag
{
None = 0x0,
@@ -128,6 +120,8 @@ public:
std::shared_ptr<JavaVersionList> javalist();
+ std::shared_ptr<URNResolver> resolver();
+
QMap<QString, std::shared_ptr<BaseProfilerFactory>> profilers()
{
return m_profilers;
@@ -214,8 +208,11 @@ private:
std::shared_ptr<LiteLoaderVersionList> m_liteloaderlist;
std::shared_ptr<MinecraftVersionList> m_minecraftlist;
std::shared_ptr<JavaVersionList> m_javalist;
+ std::shared_ptr<URNResolver> m_resolver;
+
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> m_tools;
+
QsLogging::DestinationPtr m_fileDestination;
QsLogging::DestinationPtr m_debugDestination;
diff --git a/depends/launcher/CMakeLists.txt b/depends/launcher/CMakeLists.txt
index ad06fa7b..42c77a89 100644
--- a/depends/launcher/CMakeLists.txt
+++ b/depends/launcher/CMakeLists.txt
@@ -18,7 +18,7 @@ set(SRC
# The launcher has to be there for silly FML/Forge relauncher.
net/minecraft/Launcher.java
org/multimc/legacy/LegacyLauncher.java
- org/multimc/legacy/LegacyFrame.java
+ org/multimc/LegacyFrame.java
# onesix launcher
org/multimc/onesix/OneSixLauncher.java
diff --git a/depends/launcher/net/minecraft/Launcher.java b/depends/launcher/net/minecraft/Launcher.java
index c9b137e1..a53501ec 100644
--- a/depends/launcher/net/minecraft/Launcher.java
+++ b/depends/launcher/net/minecraft/Launcher.java
@@ -24,6 +24,7 @@ import java.awt.BorderLayout;
import java.awt.Graphics;
import java.applet.Applet;
import java.applet.AppletStub;
+import java.net.MalformedURLException;
public class Launcher extends Applet implements AppletStub
{
@@ -130,13 +131,23 @@ public class Launcher extends Applet implements AppletStub
@Override
public URL getCodeBase() {
- return wrappedApplet.getCodeBase();
+ try {
+ return new URL("http://www.minecraft.net/game/");
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ return null;
}
@Override
public URL getDocumentBase()
{
- return documentBase;
+ try {
+ return new URL("http://www.minecraft.net/game/");
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ return null;
}
@Override
diff --git a/depends/launcher/org/multimc/legacy/LegacyFrame.java b/depends/launcher/org/multimc/LegacyFrame.java
index c3c0cafc..a081f3ae 100644
--- a/depends/launcher/org/multimc/legacy/LegacyFrame.java
+++ b/depends/launcher/org/multimc/LegacyFrame.java
@@ -1,4 +1,4 @@
-package org.multimc.legacy;/*
+package org.multimc;/*
* Copyright 2012-2014 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/depends/launcher/org/multimc/legacy/LegacyLauncher.java b/depends/launcher/org/multimc/legacy/LegacyLauncher.java
index 1ca37c4a..347bb1a2 100644
--- a/depends/launcher/org/multimc/legacy/LegacyLauncher.java
+++ b/depends/launcher/org/multimc/legacy/LegacyLauncher.java
@@ -14,10 +14,7 @@ package org.multimc.legacy;/*
* limitations under the License.
*/
-import org.multimc.Launcher;
-import org.multimc.NotFoundException;
-import org.multimc.ParamBucket;
-import org.multimc.Utils;
+import org.multimc.*;
import java.applet.Applet;
import java.awt.*;
diff --git a/depends/launcher/org/multimc/onesix/OneSixLauncher.java b/depends/launcher/org/multimc/onesix/OneSixLauncher.java
index 28f8e6ee..b9eb4d66 100644
--- a/depends/launcher/org/multimc/onesix/OneSixLauncher.java
+++ b/depends/launcher/org/multimc/onesix/OneSixLauncher.java
@@ -17,128 +17,185 @@ package org.multimc.onesix;
import org.multimc.*;
+import java.applet.Applet;
import java.io.File;
+import java.awt.*;
import java.io.IOException;
import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
public class OneSixLauncher implements Launcher
{
- @Override
- public int launch(ParamBucket params)
+ // parameters, separated from ParamBucket
+ private List<String> libraries;
+ private List<String> extlibs;
+ private List<String> mcparams;
+ private List<String> mods;
+ private List<String> traits;
+ private String appletClass;
+ private String mainClass;
+ private String natives;
+ private String userName, sessionId;
+ private String windowTitle;
+ private String windowParams;
+
+ // secondary parameters
+ private Dimension winSize;
+ private boolean maximize;
+ private String cwd;
+
+ // the much abused system classloader, for convenience (for further abuse)
+ private ClassLoader cl;
+
+ private void processParams(ParamBucket params) throws NotFoundException
{
- // get and process the launch script params
- List<String> libraries;
- List<String> extlibs;
- List<String> mcparams;
- List<String> mods;
- String mainClass;
- String natives;
- final String windowTitle;
- String windowParams;
- try
- {
- libraries = params.all("cp");
- extlibs = params.all("ext");
- mcparams = params.all("param");
- mainClass = params.first("mainClass");
- mods = params.allSafe("mods", new ArrayList<String>());
- natives = params.first("natives");
+ libraries = params.all("cp");
+ extlibs = params.all("ext");
+ mcparams = params.allSafe("param", new ArrayList<String>() );
+ mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft");
+ appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet");
+ mods = params.allSafe("mods", new ArrayList<String>());
+ traits = params.allSafe("traits", new ArrayList<String>());
+ natives = params.first("natives");
- windowTitle = params.first("windowTitle");
- // windowParams = params.first("windowParams");
- } catch (NotFoundException e)
+ userName = params.first("userName");
+ sessionId = params.first("sessionId");
+ windowTitle = params.firstSafe("windowTitle", "Minecraft");
+ windowParams = params.firstSafe("windowParams", "854x480");
+
+ cwd = System.getProperty("user.dir");
+ winSize = new Dimension(854, 480);
+ maximize = false;
+
+ String[] dimStrings = windowParams.split("x");
+
+ if (windowParams.equalsIgnoreCase("max"))
{
- System.err.println("Not enough arguments.");
- e.printStackTrace(System.err);
- return -1;
+ maximize = true;
}
+ else if (dimStrings.length == 2)
+ {
+ try
+ {
+ winSize = new Dimension(Integer.parseInt(dimStrings[0]), Integer.parseInt(dimStrings[1]));
+ } catch (NumberFormatException ignored) {}
+ }
+ }
+
+ private void printStats()
+ {
+ Utils.log("Main Class:");
+ Utils.log(" " + mainClass);
+ Utils.log();
- List<String> allJars = new ArrayList<String>();
- allJars.addAll(mods);
- allJars.addAll(libraries);
+ Utils.log("Native path:");
+ Utils.log(" " + natives);
+ Utils.log();
- if(!Utils.addToClassPath(allJars))
+ Utils.log("Traits:");
+ Utils.log(" " + traits);
+ Utils.log();
+
+ Utils.log("Libraries:");
+ for (String s : libraries)
{
- System.err.println("Halting launch due to previous errors.");
- return -1;
+ Utils.log(" " + s);
}
+ Utils.log();
- // print the pretty things
+ if(mods.size() > 0)
{
- Utils.log("Main Class:");
- Utils.log(" " + mainClass);
- Utils.log();
-
- Utils.log("Native path:");
- Utils.log(" " + natives);
- Utils.log();
-
- Utils.log("Libraries:");
- for (String s : libraries)
+ Utils.log("Class Path Mods:");
+ for (String s : mods)
{
Utils.log(" " + s);
}
Utils.log();
+ }
+
+ Utils.log("Params:");
+ Utils.log(" " + mcparams.toString());
+ Utils.log();
+ }
+
+ int legacyLaunch()
+ {
+ // Get the Minecraft Class and set the base folder
+ Class<?> mc;
+ try
+ {
+ mc = cl.loadClass(mainClass);
- if(mods.size() > 0)
+ Field f = Utils.getMCPathField(mc);
+
+ if (f == null)
{
- Utils.log("Class Path Mods:");
- for (String s : mods)
- {
- Utils.log(" " + s);
- }
- Utils.log();
+ System.err.println("Could not find Minecraft path field.");
}
-
- Utils.log("Params:");
- Utils.log(" " + mcparams.toString());
- Utils.log();
+ else
+ {
+ f.setAccessible(true);
+ f.set(null, new File(cwd));
+ }
+ } catch (Exception e)
+ {
+ System.err.println("Could not set base folder. Failed to find/access Minecraft main class:");
+ e.printStackTrace(System.err);
+ return -1;
}
- // set up the natives path(s).
- Utils.log("Preparing native libraries...");
- String property = System.getProperty("os.arch");
- boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64");
- for(String extlib: extlibs)
+ System.setProperty("minecraft.applet.TargetDirectory", cwd);
+
+ String[] mcArgs = new String[2];
+ mcArgs[0] = userName;
+ mcArgs[1] = sessionId;
+
+ Utils.log("Launching with applet wrapper...");
+ try
+ {
+ Class<?> MCAppletClass = cl.loadClass(appletClass);
+ Applet mcappl = (Applet) MCAppletClass.newInstance();
+ LegacyFrame mcWindow = new LegacyFrame(windowTitle);
+ mcWindow.start(mcappl, userName, sessionId, winSize, maximize);
+ } catch (Exception e)
{
+ Utils.log("Applet wrapper failed:", "Error");
+ e.printStackTrace(System.err);
+ Utils.log();
+ Utils.log("Falling back to compatibility mode.");
try
{
- String cleanlib = extlib.replace("${arch}", is_64 ? "64" : "32");
- File cleanlibf = new File(cleanlib);
- Utils.log("Extracting " + cleanlibf.getName());
- Utils.unzip(cleanlibf, new File(natives));
- } catch (IOException e)
+ mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs);
+ } catch (Exception e1)
{
- System.err.println("Failed to extract native library:");
- e.printStackTrace(System.err);
+ Utils.log("Failed to invoke the Minecraft main class:", "Fatal");
+ e1.printStackTrace(System.err);
return -1;
}
}
- Utils.log();
-
- System.setProperty("java.library.path", natives);
- Field fieldSysPath;
- try
+ return 0;
+ }
+
+ int launchWithMainClass()
+ {
+ // window size, title and state, onesix
+ if (maximize)
{
- fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
- fieldSysPath.setAccessible( true );
- fieldSysPath.set( null, null );
- } catch (Exception e)
+ // FIXME: there is no good way to maximize the minecraft window in onesix.
+ // the following often breaks linux screen setups
+ // mcparams.add("--fullscreen");
+ }
+ else
{
- System.err.println("Failed to set the native library path:");
- e.printStackTrace(System.err);
- return -1;
+ mcparams.add("--width");
+ mcparams.add(Integer.toString(winSize.width));
+ mcparams.add("--height");
+ mcparams.add(Integer.toString(winSize.height));
}
-
+
// Get the Minecraft Class.
- final ClassLoader cl = ClassLoader.getSystemClassLoader();
Class<?> mc;
try
{
@@ -161,9 +218,7 @@ public class OneSixLauncher implements Launcher
e.printStackTrace(System.err);
return -1;
}
-
- // FIXME: works only on linux, we need a better solution
-/*
+ /*
final java.nio.ByteBuffer[] icons = IconLoader.load("icon.png");
new Thread() {
public void run() {
@@ -174,18 +229,21 @@ public class OneSixLauncher implements Launcher
Method isCreated;
Method setTitle;
Method setIcon;
-
+ Field fieldWindowCreated;
+ Boolean created = false;
Display = cl.loadClass("org.lwjgl.opengl.Display");
- isCreated = Display.getMethod("isCreated");
+ fieldWindowCreated = Display.getDeclaredField("window_created");
+ fieldWindowCreated.setAccessible( true );
setTitle = Display.getMethod("setTitle", String.class);
setIcon = Display.getMethod("setIcon", java.nio.ByteBuffer[].class);
-
+ created = (Boolean) fieldWindowCreated.get( null );
// set the window title? Maybe?
- while(!(Boolean) isCreated.invoke(null))
+ while(!created)
{
try
{
Thread.sleep(150);
+ created = (Boolean) fieldWindowCreated.get( null );
} catch (InterruptedException ignored) {}
}
// Give it a bit more time ;)
@@ -206,12 +264,13 @@ public class OneSixLauncher implements Launcher
}
}
.start();
-*/
- // start Minecraft
- String[] paramsArray = mcparams.toArray(new String[mcparams.size()]); // init params accordingly
+ */
+ // init params for the main method to chomp on.
+ String[] paramsArray = mcparams.toArray(new String[mcparams.size()]);
try
{
- meth.invoke(null, (Object) paramsArray); // static method doesn't have an instance
+ // static method doesn't have an instance
+ meth.invoke(null, (Object) paramsArray);
} catch (Exception e)
{
System.err.println("Failed to start Minecraft:");
@@ -220,4 +279,88 @@ public class OneSixLauncher implements Launcher
}
return 0;
}
+
+ @Override
+ public int launch(ParamBucket params)
+ {
+ // get and process the launch script params
+ try
+ {
+ processParams(params);
+ } catch (NotFoundException e)
+ {
+ System.err.println("Not enough arguments.");
+ e.printStackTrace(System.err);
+ return -1;
+ }
+
+ // do some horrible black magic with the classpath
+ {
+ List<String> allJars = new ArrayList<String>();
+ allJars.addAll(mods);
+ allJars.addAll(libraries);
+
+ if(!Utils.addToClassPath(allJars))
+ {
+ System.err.println("Halting launch due to previous errors.");
+ return -1;
+ }
+ }
+
+ // print the pretty things
+ printStats();
+
+ // extract native libs (depending on platform here... java!)
+ Utils.log("Preparing native libraries...");
+ String property = System.getProperty("os.arch");
+ boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64");
+ for(String extlib: extlibs)
+ {
+ try
+ {
+ String cleanlib = extlib.replace("${arch}", is_64 ? "64" : "32");
+ File cleanlibf = new File(cleanlib);
+ Utils.log("Extracting " + cleanlibf.getName());
+ Utils.unzip(cleanlibf, new File(natives));
+ } catch (IOException e)
+ {
+ System.err.println("Failed to extract native library:");
+ e.printStackTrace(System.err);
+ return -1;
+ }
+ }
+ Utils.log();
+
+ // set the native libs path... the brute force way
+ try
+ {
+ System.setProperty("java.library.path", natives);
+ System.setProperty("org.lwjgl.librarypath", natives);
+ System.setProperty("net.java.games.input.librarypath", natives);
+ // by the power of reflection, initialize native libs again. DIRTY!
+ // this is SO BAD. imagine doing that to ld
+ Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
+ fieldSysPath.setAccessible( true );
+ fieldSysPath.set( null, null );
+ } catch (Exception e)
+ {
+ System.err.println("Failed to set the native library path:");
+ e.printStackTrace(System.err);
+ return -1;
+ }
+
+ // grab the system classloader and ...
+ cl = ClassLoader.getSystemClassLoader();
+
+ if (traits.contains("legacyLaunch") || traits.contains("alphaLaunch") )
+ {
+ // legacy launch uses the applet wrapper
+ return legacyLaunch();
+ }
+ else
+ {
+ // normal launch just calls main()
+ return launchWithMainClass();
+ }
+ }
}
diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp
index 5ba05b2a..b8f42dc4 100644
--- a/gui/MainWindow.cpp
+++ b/gui/MainWindow.cpp
@@ -56,9 +56,7 @@
#include "gui/dialogs/VersionSelectDialog.h"
#include "gui/dialogs/CustomMessageBox.h"
#include "gui/dialogs/LwjglSelectDialog.h"
-#include "gui/dialogs/InstanceSettings.h"
#include "gui/dialogs/IconPickerDialog.h"
-#include "gui/dialogs/EditNotesDialog.h"
#include "gui/dialogs/CopyInstanceDialog.h"
#include "gui/dialogs/AccountListDialog.h"
#include "gui/dialogs/AccountSelectDialog.h"
@@ -68,12 +66,13 @@
#include "dialogs/ScreenshotDialog.h"
#include "gui/ConsoleWindow.h"
+#include "pagedialog/PageDialog.h"
-#include "logic/lists/InstanceList.h"
-#include "logic/lists/MinecraftVersionList.h"
-#include "logic/lists/LwjglVersionList.h"
+#include "logic/InstanceList.h"
+#include "logic/minecraft/MinecraftVersionList.h"
+#include "logic/LwjglVersionList.h"
#include "logic/icons/IconList.h"
-#include "logic/lists/JavaVersionList.h"
+#include "logic/java/JavaVersionList.h"
#include "logic/auth/flows/AuthenticateTask.h"
#include "logic/auth/flows/RefreshTask.h"
@@ -91,7 +90,7 @@
#include "logic/InstanceFactory.h"
#include "logic/MinecraftProcess.h"
#include "logic/OneSixUpdate.h"
-#include "logic/JavaUtils.h"
+#include "logic/java/JavaUtils.h"
#include "logic/NagUtils.h"
#include "logic/SkinUtils.h"
@@ -870,17 +869,14 @@ void MainWindow::updateInstanceToolIcon(QString new_icon)
void MainWindow::setSelectedInstanceById(const QString &id)
{
- QModelIndex selectionIndex = proxymodel->index(0, 0);
- if (!id.isNull())
+ if (id.isNull())
+ return;
+ const QModelIndex index = MMC->instances()->getInstanceIndexById(id);
+ if (index.isValid())
{
- const QModelIndex index = MMC->instances()->getInstanceIndexById(id);
- if (index.isValid())
- {
- selectionIndex = proxymodel->mapFromSource(index);
- }
+ QModelIndex selectionIndex = proxymodel->mapFromSource(index);
+ view->selectionModel()->setCurrentIndex(selectionIndex, QItemSelectionModel::ClearAndSelect);
}
- view->selectionModel()->setCurrentIndex(selectionIndex,
- QItemSelectionModel::ClearAndSelect);
}
void MainWindow::on_actionChangeInstGroup_triggered()
@@ -944,6 +940,31 @@ void MainWindow::on_actionSettings_triggered()
updateToolsMenu();
}
+template <typename T>
+void ShowPageDialog(T raw_provider, QWidget * parent, QString open_page = QString())
+{
+ auto provider = std::dynamic_pointer_cast<BasePageProvider>(raw_provider);
+ if(!provider)
+ return;
+ PageDialog dlg(provider, open_page, parent);
+ dlg.exec();
+}
+
+void MainWindow::on_actionInstanceSettings_triggered()
+{
+ ShowPageDialog(m_selectedInstance, this, "settings");
+}
+
+void MainWindow::on_actionEditInstNotes_triggered()
+{
+ ShowPageDialog(m_selectedInstance, this, "notes");
+}
+
+void MainWindow::on_actionEditInstance_triggered()
+{
+ ShowPageDialog(m_selectedInstance, this);
+}
+
void MainWindow::on_actionManageAccounts_triggered()
{
AccountListDialog dialog(this);
@@ -1032,17 +1053,6 @@ void MainWindow::on_actionViewSelectedInstFolder_triggered()
}
}
-void MainWindow::on_actionEditInstMods_triggered()
-{
- if (m_selectedInstance)
- {
- auto dialog = m_selectedInstance->createModEditDialog(this);
- if (dialog)
- dialog->exec();
- dialog->deleteLater();
- }
-}
-
void MainWindow::closeEvent(QCloseEvent *event)
{
// Save the window state and geometry.
@@ -1339,119 +1349,26 @@ void MainWindow::startTask(Task *task)
task->start();
}
-// Create A Desktop Shortcut
-void MainWindow::on_actionMakeDesktopShortcut_triggered()
-{
- QString name("Test");
- name = QInputDialog::getText(this, tr("MultiMC Shortcut"), tr("Enter a Shortcut Name."),
- QLineEdit::Normal, name);
-
- Util::createShortCut(Util::getDesktopDir(), QApplication::instance()->applicationFilePath(),
- QStringList() << "-dl" << QDir::currentPath() << "test", name,
- "application-x-octet-stream");
-
- CustomMessageBox::selectable(
- this, tr("Not useful"),
- tr("A Dummy Shortcut was created. it will not do anything productive"),
- QMessageBox::Warning)->show();
-}
-
// BrowserDialog
void MainWindow::openWebPage(QUrl url)
{
QDesktopServices::openUrl(url);
}
-void MainWindow::on_actionChangeInstMCVersion_triggered()
+void MainWindow::instanceChanged(const QModelIndex &current, const QModelIndex &previous)
{
- if (view->selectionModel()->selectedIndexes().count() < 1)
- return;
-
- VersionSelectDialog vselect(m_selectedInstance->versionList().get(),
- tr("Change Minecraft version"), this);
- vselect.setFuzzyFilter(1, "*OneSix*");
- if (!vselect.exec() || !vselect.selectedVersion())
- return;
-
- if (!MMC->accounts()->anyAccountIsValid())
+ if(!current.isValid())
{
- CustomMessageBox::selectable(
- this, tr("Error"),
- tr("MultiMC cannot download Minecraft or update instances unless you have at least "
- "one account added.\nPlease add your Mojang or Minecraft account."),
- QMessageBox::Warning)->show();
+ MMC->settings()->set("SelectedInstance", QString());
+ selectionBad();
return;
}
-
- if (m_selectedInstance->versionIsCustom())
- {
- auto result = CustomMessageBox::selectable(
- this, tr("Are you sure?"),
- tr("This will remove any library/version customization you did previously. "
- "This includes things like Forge install and similar."),
- QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort,
- QMessageBox::Abort)->exec();
-
- if (result != QMessageBox::Ok)
- return;
- }
- m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor());
-
- auto updateTask = m_selectedInstance->doUpdate();
- if (!updateTask)
- {
- return;
- }
- ProgressDialog tDialog(this);
- connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
- tDialog.exec(updateTask.get());
-}
-
-void MainWindow::on_actionChangeInstLWJGLVersion_triggered()
-{
- if (!m_selectedInstance)
- return;
-
- LWJGLSelectDialog lselect(this);
- lselect.exec();
- if (lselect.result() == QDialog::Accepted)
- {
- auto ptr = std::dynamic_pointer_cast<LegacyInstance>(m_selectedInstance);
- if(ptr)
- ptr->setLWJGLVersion(lselect.selectedVersion());
- }
-}
-
-void MainWindow::on_actionInstanceSettings_triggered()
-{
- if (view->selectionModel()->selectedIndexes().count() < 1)
- return;
-
- InstanceSettings settings(&m_selectedInstance->settings(), this);
- settings.setWindowTitle(tr("Instance settings"));
- settings.exec();
-}
-
-void MainWindow::instanceChanged(const QModelIndex &current, const QModelIndex &previous)
-{
- if(!current.isValid())
- {
- selectionBad();
- MMC->settings()->set("SelectedInstance", QString());
- return;
- }
- QString id = current.data(InstanceList::InstanceIDRole).toString();
- m_selectedInstance = MMC->instances()->getInstanceById(id);
+ QString id = current.data(InstanceList::InstanceIDRole).toString();
+ m_selectedInstance = MMC->instances()->getInstanceById(id);
if ( m_selectedInstance )
{
ui->instanceToolBar->setEnabled(m_selectedInstance->canLaunch());
renameButton->setText(m_selectedInstance->name());
- ui->actionChangeInstLWJGLVersion->setEnabled(
- m_selectedInstance->menuActionEnabled("actionChangeInstLWJGLVersion"));
- ui->actionEditInstMods->setEnabled(
- m_selectedInstance->menuActionEnabled("actionEditInstMods"));
- ui->actionChangeInstMCVersion->setEnabled(
- m_selectedInstance->menuActionEnabled("actionChangeInstMCVersion"));
m_statusLeft->setText(m_selectedInstance->getStatusbarDescription());
updateInstanceToolIcon(m_selectedInstance->iconKey());
@@ -1461,9 +1378,9 @@ void MainWindow::instanceChanged(const QModelIndex &current, const QModelIndex &
}
else
{
- selectionBad();
- MMC->settings()->set("SelectedInstance", QString());
- return;
+ MMC->settings()->set("SelectedInstance", QString());
+ selectionBad();
+ return;
}
}
@@ -1481,20 +1398,6 @@ void MainWindow::selectionBad()
setSelectedInstanceById(MMC->settings()->get("SelectedInstance").toString());
}
-void MainWindow::on_actionEditInstNotes_triggered()
-{
- if (!m_selectedInstance)
- return;
-
- EditNotesDialog noteedit(m_selectedInstance->notes(), m_selectedInstance->name(), this);
- noteedit.exec();
- if (noteedit.result() == QDialog::Accepted)
- {
-
- m_selectedInstance->setNotes(noteedit.getText());
- }
-}
-
void MainWindow::instanceEnded()
{
this->show();
diff --git a/gui/MainWindow.h b/gui/MainWindow.h
index 69cf11b0..182e9c0c 100644
--- a/gui/MainWindow.h
+++ b/gui/MainWindow.h
@@ -19,11 +19,10 @@
#include <QProcess>
#include <QTimer>
-#include "logic/lists/InstanceList.h"
+#include "logic/InstanceList.h"
#include "logic/BaseInstance.h"
-
#include "logic/auth/MojangAccount.h"
-#include <logic/net/NetJob.h>
+#include "logic/net/NetJob.h"
class QToolButton;
class LabeledToolButton;
@@ -81,6 +80,8 @@ slots:
void on_actionSettings_triggered();
+ void on_actionInstanceSettings_triggered();
+
void on_actionManageAccounts_triggered();
void on_actionReportBug_triggered();
@@ -103,11 +104,7 @@ slots:
void on_actionRenameInstance_triggered();
- void on_actionMakeDesktopShortcut_triggered();
-
- void on_actionChangeInstMCVersion_triggered();
-
- void on_actionEditInstMods_triggered();
+ void on_actionEditInstance_triggered();
void on_actionEditInstNotes_triggered();
@@ -135,12 +132,8 @@ slots:
void taskStart();
void taskEnd();
- void on_actionChangeInstLWJGLVersion_triggered();
-
void instanceEnded();
- void on_actionInstanceSettings_triggered();
-
// called when an icon is changed in the icon model.
void iconUpdated(QString);
diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui
index 1d7fbec9..c79dc948 100644
--- a/gui/MainWindow.ui
+++ b/gui/MainWindow.ui
@@ -74,7 +74,7 @@
<addaction name="actionReportBug"/>
<addaction name="actionAbout"/>
<addaction name="separator"/>
- <addaction name="actionPatreon"/>
+ <addaction name="actionPatreon"/>
<addaction name="actionCAT"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
@@ -115,10 +115,8 @@
<addaction name="separator"/>
<addaction name="actionScreenshots"/>
<addaction name="separator"/>
+ <addaction name="actionEditInstance"/>
<addaction name="actionInstanceSettings"/>
- <addaction name="actionChangeInstMCVersion"/>
- <addaction name="actionChangeInstLWJGLVersion"/>
- <addaction name="actionEditInstMods"/>
<addaction name="actionViewSelectedInstFolder"/>
<addaction name="actionConfig_Folder"/>
<addaction name="separator"/>
@@ -284,7 +282,7 @@
<property name="statusTip">
<string>Open the MultiMC Patreon page.</string>
</property>
- </action>
+ </action>
<action name="actionMoreNews">
<property name="icon">
<iconset theme="news">
@@ -388,82 +386,18 @@
<string>Edit the notes for the selected instance.</string>
</property>
</action>
- <action name="actionInstanceSettings">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="text">
- <string>Settings</string>
- </property>
- <property name="toolTip">
- <string>Change settings for the selected instance.</string>
- </property>
- <property name="statusTip">
- <string>Change settings for the selected instance.</string>
- </property>
- </action>
- <action name="actionMakeDesktopShortcut">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="text">
- <string>Make Shortcut</string>
- </property>
- <property name="toolTip">
- <string>Make a shortcut on the desktop for the selected instance.</string>
- </property>
- <property name="statusTip">
- <string>Make a shortcut on the desktop for the selected instance.</string>
- </property>
- </action>
- <action name="actionManageInstSaves">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="text">
- <string>Manage Saves</string>
- </property>
- <property name="toolTip">
- <string>Manage saves for the selected instance.</string>
- </property>
- <property name="statusTip">
- <string>Manage saves for the selected instance.</string>
- </property>
- </action>
- <action name="actionEditInstMods">
+ <action name="actionEditInstance">
<property name="text">
<string>Edit Mods</string>
</property>
- <property name="toolTip">
- <string>Edit the mods for the selected instance.</string>
- </property>
- <property name="statusTip">
- <string>Edit the mods for the selected instance.</string>
- </property>
- </action>
- <action name="actionChangeInstMCVersion">
- <property name="text">
- <string>Change Version</string>
- </property>
- <property name="toolTip">
- <string>Change the selected instance's Minecraft version.</string>
- </property>
- <property name="statusTip">
- <string>Change the selected instance's Minecraft version.</string>
- </property>
- </action>
- <action name="actionChangeInstLWJGLVersion">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="text">
- <string>Change LWJGL</string>
+ <property name="iconText">
+ <string>Edit Instance</string>
</property>
<property name="toolTip">
- <string>Change the version of LWJGL for the selected instance to use.</string>
+ <string>Change the instance settings, mods and versions.</string>
</property>
<property name="statusTip">
- <string>Change the version of LWJGL for the selected instance to use.</string>
+ <string>Change the instance settings, mods and versions.</string>
</property>
</action>
<action name="actionViewSelectedInstFolder">
@@ -555,6 +489,14 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;View and upload screenshots for this instance&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</action>
+ <action name="actionInstanceSettings">
+ <property name="text">
+ <string>Instance Settings</string>
+ </property>
+ <property name="toolTip">
+ <string>Change the settings specific to the instance</string>
+ </property>
+ </action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
diff --git a/gui/dialogs/CopyInstanceDialog.cpp b/gui/dialogs/CopyInstanceDialog.cpp
index 71429367..188cf274 100644
--- a/gui/dialogs/CopyInstanceDialog.cpp
+++ b/gui/dialogs/CopyInstanceDialog.cpp
@@ -28,7 +28,6 @@
#include "logic/InstanceFactory.h"
#include "logic/BaseVersion.h"
#include "logic/icons/IconList.h"
-#include "logic/lists/MinecraftVersionList.h"
#include "logic/tasks/Task.h"
#include "logic/BaseInstance.h"
diff --git a/gui/dialogs/EditNotesDialog.cpp b/gui/dialogs/EditNotesDialog.cpp
deleted file mode 100644
index f2aa029f..00000000
--- a/gui/dialogs/EditNotesDialog.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright 2013 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 "EditNotesDialog.h"
-#include "ui_EditNotesDialog.h"
-#include "gui/Platform.h"
-
-#include <QIcon>
-#include <QApplication>
-
-EditNotesDialog::EditNotesDialog(QString notes, QString name, QWidget *parent)
- : QDialog(parent), ui(new Ui::EditNotesDialog), m_instance_name(name),
- m_instance_notes(notes)
-{
- MultiMCPlatform::fixWM_CLASS(this);
- ui->setupUi(this);
- ui->noteEditor->setText(notes);
- setWindowTitle(tr("Edit notes of %1").arg(m_instance_name));
- // connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
-}
-
-EditNotesDialog::~EditNotesDialog()
-{
- delete ui;
-}
-
-QString EditNotesDialog::getText()
-{
- QString test = ui->noteEditor->toPlainText();
- return test;
-}
diff --git a/gui/dialogs/EditNotesDialog.ui b/gui/dialogs/EditNotesDialog.ui
deleted file mode 100644
index 487dfb84..00000000
--- a/gui/dialogs/EditNotesDialog.ui
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>EditNotesDialog</class>
- <widget class="QDialog" name="EditNotesDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>459</width>
- <height>399</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Edit Notes</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QTextEdit" name="noteEditor">
- <property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOn</enum>
- </property>
- <property name="acceptRichText">
- <bool>false</bool>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>EditNotesDialog</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>248</x>
- <y>254</y>
- </hint>
- <hint type="destinationlabel">
- <x>157</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>EditNotesDialog</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>316</x>
- <y>260</y>
- </hint>
- <hint type="destinationlabel">
- <x>286</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- </connections>
-</ui>
diff --git a/gui/dialogs/InstanceSettings.cpp b/gui/dialogs/InstanceSettings.cpp
deleted file mode 100644
index edb4a921..00000000
--- a/gui/dialogs/InstanceSettings.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-/* Copyright 2013 MultiMC Contributors
- *
- * Authors: Andrew Okin
- * Peterix
- * 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 "MultiMC.h"
-#include "InstanceSettings.h"
-#include "ui_InstanceSettings.h"
-#include "gui/Platform.h"
-#include "gui/dialogs/VersionSelectDialog.h"
-
-#include "logic/JavaUtils.h"
-#include "logic/NagUtils.h"
-#include "logic/lists/JavaVersionList.h"
-#include "logic/JavaChecker.h"
-
-#include <QFileDialog>
-#include <QMessageBox>
-
-InstanceSettings::InstanceSettings(SettingsObject *obj, QWidget *parent)
- : QDialog(parent), ui(new Ui::InstanceSettings), m_obj(obj)
-{
- MultiMCPlatform::fixWM_CLASS(this);
- ui->setupUi(this);
-
- restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("SettingsGeometry").toByteArray()));
-
- loadSettings();
-}
-
-InstanceSettings::~InstanceSettings()
-{
- delete ui;
-}
-
-void InstanceSettings::showEvent(QShowEvent *ev)
-{
- QDialog::showEvent(ev);
-}
-
-void InstanceSettings::closeEvent(QCloseEvent *ev)
-{
- MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
-
- QDialog::closeEvent(ev);
-}
-
-void InstanceSettings::on_customCommandsGroupBox_toggled(bool state)
-{
- ui->labelCustomCmdsDescription->setEnabled(state);
-}
-
-void InstanceSettings::on_buttonBox_accepted()
-{
- MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
-
- applySettings();
- accept();
-}
-
-void InstanceSettings::on_buttonBox_rejected()
-{
- MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
-
- reject();
-}
-
-void InstanceSettings::applySettings()
-{
- // Console
- bool console = ui->consoleSettingsBox->isChecked();
- m_obj->set("OverrideConsole", console);
- if (console)
- {
- m_obj->set("ShowConsole", ui->showConsoleCheck->isChecked());
- m_obj->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked());
- }
- else
- {
- m_obj->reset("ShowConsole");
- m_obj->reset("AutoCloseConsole");
- }
-
- // Window Size
- bool window = ui->windowSizeGroupBox->isChecked();
- m_obj->set("OverrideWindow", window);
- if (window)
- {
- m_obj->set("LaunchMaximized", ui->maximizedCheckBox->isChecked());
- m_obj->set("MinecraftWinWidth", ui->windowWidthSpinBox->value());
- m_obj->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
- }
- else
- {
- m_obj->reset("LaunchMaximized");
- m_obj->reset("MinecraftWinWidth");
- m_obj->reset("MinecraftWinHeight");
- }
-
- // Memory
- bool memory = ui->memoryGroupBox->isChecked();
- m_obj->set("OverrideMemory", memory);
- if (memory)
- {
- m_obj->set("MinMemAlloc", ui->minMemSpinBox->value());
- m_obj->set("MaxMemAlloc", ui->maxMemSpinBox->value());
- m_obj->set("PermGen", ui->permGenSpinBox->value());
- }
- else
- {
- m_obj->reset("MinMemAlloc");
- m_obj->reset("MaxMemAlloc");
- m_obj->reset("PermGen");
- }
-
- // Java Settings
- bool java = ui->javaSettingsGroupBox->isChecked();
- m_obj->set("OverrideJava", java);
- if (java)
- {
- m_obj->set("JavaPath", ui->javaPathTextBox->text());
- m_obj->set("JvmArgs", ui->jvmArgsTextBox->text());
-
- NagUtils::checkJVMArgs(m_obj->get("JvmArgs").toString(), this->parentWidget());
- }
- else
- {
- m_obj->reset("JavaPath");
- m_obj->reset("JvmArgs");
- }
-
- // Custom Commands
- bool custcmd = ui->customCommandsGroupBox->isChecked();
- m_obj->set("OverrideCommands", custcmd);
- if (custcmd)
- {
- m_obj->set("PreLaunchCommand", ui->preLaunchCmdTextBox->text());
- m_obj->set("PostExitCommand", ui->postExitCmdTextBox->text());
- }
- else
- {
- m_obj->reset("PreLaunchCommand");
- m_obj->reset("PostExitCommand");
- }
-}
-
-void InstanceSettings::loadSettings()
-{
- // Console
- ui->consoleSettingsBox->setChecked(m_obj->get("OverrideConsole").toBool());
- ui->showConsoleCheck->setChecked(m_obj->get("ShowConsole").toBool());
- ui->autoCloseConsoleCheck->setChecked(m_obj->get("AutoCloseConsole").toBool());
-
- // Window Size
- ui->windowSizeGroupBox->setChecked(m_obj->get("OverrideWindow").toBool());
- ui->maximizedCheckBox->setChecked(m_obj->get("LaunchMaximized").toBool());
- ui->windowWidthSpinBox->setValue(m_obj->get("MinecraftWinWidth").toInt());
- ui->windowHeightSpinBox->setValue(m_obj->get("MinecraftWinHeight").toInt());
-
- // Memory
- ui->memoryGroupBox->setChecked(m_obj->get("OverrideMemory").toBool());
- ui->minMemSpinBox->setValue(m_obj->get("MinMemAlloc").toInt());
- ui->maxMemSpinBox->setValue(m_obj->get("MaxMemAlloc").toInt());
- ui->permGenSpinBox->setValue(m_obj->get("PermGen").toInt());
-
- // Java Settings
- ui->javaSettingsGroupBox->setChecked(m_obj->get("OverrideJava").toBool());
- ui->javaPathTextBox->setText(m_obj->get("JavaPath").toString());
- ui->jvmArgsTextBox->setText(m_obj->get("JvmArgs").toString());
-
- // Custom Commands
- ui->customCommandsGroupBox->setChecked(m_obj->get("OverrideCommands").toBool());
- ui->preLaunchCmdTextBox->setText(m_obj->get("PreLaunchCommand").toString());
- ui->postExitCmdTextBox->setText(m_obj->get("PostExitCommand").toString());
-}
-
-void InstanceSettings::on_javaDetectBtn_clicked()
-{
- JavaVersionPtr java;
-
- VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this, true);
- vselect.setResizeOn(2);
- vselect.exec();
-
- if (vselect.result() == QDialog::Accepted && vselect.selectedVersion())
- {
- java = std::dynamic_pointer_cast<JavaVersion>(vselect.selectedVersion());
- ui->javaPathTextBox->setText(java->path);
- }
-}
-
-void InstanceSettings::on_javaBrowseBtn_clicked()
-{
- QString dir = QFileDialog::getOpenFileName(this, tr("Find Java executable"));
- if (!dir.isNull())
- {
- ui->javaPathTextBox->setText(dir);
- }
-}
-
-void InstanceSettings::on_javaTestBtn_clicked()
-{
- checker.reset(new JavaChecker());
- connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
- SLOT(checkFinished(JavaCheckResult)));
- checker->path = ui->javaPathTextBox->text();
- checker->performCheck();
-}
-
-void InstanceSettings::checkFinished(JavaCheckResult result)
-{
- if (result.valid)
- {
- QString text;
- text += "Java test succeeded!\n";
- if (result.is_64bit)
- text += "Using 64bit java.\n";
- text += "\n";
- text += "Platform reported: " + result.realPlatform;
- QMessageBox::information(this, tr("Java test success"), text);
- }
- else
- {
- QMessageBox::warning(
- this, tr("Java test failure"),
- tr("The specified java binary didn't work. You should use the auto-detect feature, "
- "or set the path to the java executable."));
- }
-}
diff --git a/gui/dialogs/LegacyModEditDialog.cpp b/gui/dialogs/LegacyModEditDialog.cpp
deleted file mode 100644
index e5039c02..00000000
--- a/gui/dialogs/LegacyModEditDialog.cpp
+++ /dev/null
@@ -1,393 +0,0 @@
-/* Copyright 2013 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 "MultiMC.h"
-#include "LegacyModEditDialog.h"
-#include "ModEditDialogCommon.h"
-#include "VersionSelectDialog.h"
-#include "ProgressDialog.h"
-#include "ui_LegacyModEditDialog.h"
-#include "logic/ModList.h"
-#include "logic/lists/ForgeVersionList.h"
-#include "gui/Platform.h"
-
-#include <pathutils.h>
-#include <QFileDialog>
-//#include <QMessageBox>
-#include <QDebug>
-#include <QEvent>
-#include <QKeyEvent>
-
-LegacyModEditDialog::LegacyModEditDialog(LegacyInstance *inst, QWidget *parent)
- : QDialog(parent), ui(new Ui::LegacyModEditDialog), m_inst(inst)
-{
- MultiMCPlatform::fixWM_CLASS(this);
- ui->setupUi(this);
-
- // Jar mods
- {
- ensureFolderPathExists(m_inst->jarModsDir());
- m_jarmods = m_inst->jarModList();
- ui->jarModsTreeView->setModel(m_jarmods.get());
-#ifndef Q_OS_LINUX
- // FIXME: internal DnD causes segfaults later
- ui->jarModsTreeView->setDragDropMode(QAbstractItemView::DragDrop);
- // FIXME: DnD is glitched with contiguous (we move only first item in selection)
- ui->jarModsTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
-#endif
- ui->jarModsTreeView->installEventFilter(this);
- m_jarmods->startWatching();
- auto smodel = ui->jarModsTreeView->selectionModel();
- connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
- SLOT(jarCurrent(QModelIndex, QModelIndex)));
- }
- // Core mods
- {
- ensureFolderPathExists(m_inst->coreModsDir());
- m_coremods = m_inst->coreModList();
- ui->coreModsTreeView->setModel(m_coremods.get());
- ui->coreModsTreeView->installEventFilter(this);
- m_coremods->startWatching();
- auto smodel = ui->coreModsTreeView->selectionModel();
- connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
- SLOT(coreCurrent(QModelIndex, QModelIndex)));
- }
- // Loader mods
- {
- ensureFolderPathExists(m_inst->loaderModsDir());
- m_mods = m_inst->loaderModList();
- ui->loaderModTreeView->setModel(m_mods.get());
- ui->loaderModTreeView->installEventFilter(this);
- m_mods->startWatching();
- auto smodel = ui->loaderModTreeView->selectionModel();
- connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
- SLOT(loaderCurrent(QModelIndex, QModelIndex)));
- }
- // texture packs
- {
- ensureFolderPathExists(m_inst->texturePacksDir());
- m_texturepacks = m_inst->texturePackList();
- ui->texPackTreeView->setModel(m_texturepacks.get());
- ui->texPackTreeView->installEventFilter(this);
- m_texturepacks->startWatching();
- }
-}
-
-LegacyModEditDialog::~LegacyModEditDialog()
-{
- m_mods->stopWatching();
- m_coremods->stopWatching();
- m_jarmods->stopWatching();
- m_texturepacks->stopWatching();
- delete ui;
-}
-
-bool LegacyModEditDialog::coreListFilter(QKeyEvent *keyEvent)
-{
- switch (keyEvent->key())
- {
- case Qt::Key_Delete:
- on_rmCoreBtn_clicked();
- return true;
- case Qt::Key_Plus:
- on_addCoreBtn_clicked();
- return true;
- default:
- break;
- }
- return QDialog::eventFilter(ui->coreModsTreeView, keyEvent);
-}
-
-bool LegacyModEditDialog::jarListFilter(QKeyEvent *keyEvent)
-{
- switch (keyEvent->key())
- {
- case Qt::Key_Up:
- {
- if (keyEvent->modifiers() & Qt::ControlModifier)
- {
- on_moveJarUpBtn_clicked();
- return true;
- }
- break;
- }
- case Qt::Key_Down:
- {
- if (keyEvent->modifiers() & Qt::ControlModifier)
- {
- on_moveJarDownBtn_clicked();
- return true;
- }
- break;
- }
- case Qt::Key_Delete:
- on_rmJarBtn_clicked();
- return true;
- case Qt::Key_Plus:
- on_addJarBtn_clicked();
- return true;
- default:
- break;
- }
- return QDialog::eventFilter(ui->jarModsTreeView, keyEvent);
-}
-
-bool LegacyModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
-{
- switch (keyEvent->key())
- {
- case Qt::Key_Delete:
- on_rmModBtn_clicked();
- return true;
- case Qt::Key_Plus:
- on_addModBtn_clicked();
- return true;
- default:
- break;
- }
- return QDialog::eventFilter(ui->loaderModTreeView, keyEvent);
-}
-
-bool LegacyModEditDialog::texturePackListFilter(QKeyEvent *keyEvent)
-{
- switch (keyEvent->key())
- {
- case Qt::Key_Delete:
- on_rmTexPackBtn_clicked();
- return true;
- case Qt::Key_Plus:
- on_addTexPackBtn_clicked();
- return true;
- default:
- break;
- }
- return QDialog::eventFilter(ui->texPackTreeView, keyEvent);
-}
-
-bool LegacyModEditDialog::eventFilter(QObject *obj, QEvent *ev)
-{
- if (ev->type() != QEvent::KeyPress)
- {
- return QDialog::eventFilter(obj, ev);
- }
- QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
- if (obj == ui->jarModsTreeView)
- return jarListFilter(keyEvent);
- if (obj == ui->coreModsTreeView)
- return coreListFilter(keyEvent);
- if (obj == ui->loaderModTreeView)
- return loaderListFilter(keyEvent);
- if (obj == ui->texPackTreeView)
- return texturePackListFilter(keyEvent);
- return QDialog::eventFilter(obj, ev);
-}
-
-void LegacyModEditDialog::on_addCoreBtn_clicked()
-{
- //: Title of core mod selection dialog
- QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Core Mods"));
- for (auto filename : fileNames)
- {
- m_coremods->stopWatching();
- m_coremods->installMod(QFileInfo(filename));
- m_coremods->startWatching();
- }
-}
-void LegacyModEditDialog::on_addForgeBtn_clicked()
-{
- VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
- vselect.setExactFilter(1, m_inst->intendedVersionId());
- if (vselect.exec() && vselect.selectedVersion())
- {
- ForgeVersionPtr forge =
- std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
- if (!forge)
- return;
- auto entry = MMC->metacache()->resolveEntry("minecraftforge", forge->filename);
- if (entry->stale)
- {
- NetJob *fjob = new NetJob("Forge download");
- fjob->addNetAction(CacheDownload::make(forge->universal_url, entry));
- ProgressDialog dlg(this);
- dlg.exec(fjob);
- if (dlg.result() == QDialog::Accepted)
- {
- m_jarmods->stopWatching();
- m_jarmods->installMod(QFileInfo(entry->getFullPath()));
- m_jarmods->startWatching();
- }
- else
- {
- // failed to download forge :/
- }
- }
- else
- {
- m_jarmods->stopWatching();
- m_jarmods->installMod(QFileInfo(entry->getFullPath()));
- m_jarmods->startWatching();
- }
- }
-}
-void LegacyModEditDialog::on_addJarBtn_clicked()
-{
- //: Title of jar mod selection dialog
- QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Jar Mods"));
- for (auto filename : fileNames)
- {
- m_jarmods->stopWatching();
- m_jarmods->installMod(QFileInfo(filename));
- m_jarmods->startWatching();
- }
-}
-void LegacyModEditDialog::on_addModBtn_clicked()
-{
- //: Title of regular mod selection dialog
- QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Loader Mods"));
- for (auto filename : fileNames)
- {
- m_mods->stopWatching();
- m_mods->installMod(QFileInfo(filename));
- m_mods->startWatching();
- }
-}
-void LegacyModEditDialog::on_addTexPackBtn_clicked()
-{
- //: Title of texture pack selection dialog
- QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Texture Packs"));
- for (auto filename : fileNames)
- {
- m_texturepacks->stopWatching();
- m_texturepacks->installMod(QFileInfo(filename));
- m_texturepacks->startWatching();
- }
-}
-
-void LegacyModEditDialog::on_moveJarDownBtn_clicked()
-{
- int first, last;
- auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
-
- if (!lastfirst(list, first, last))
- return;
-
- m_jarmods->moveModsDown(first, last);
-}
-void LegacyModEditDialog::on_moveJarUpBtn_clicked()
-{
- int first, last;
- auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
-
- if (!lastfirst(list, first, last))
- return;
- m_jarmods->moveModsUp(first, last);
-}
-void LegacyModEditDialog::on_rmCoreBtn_clicked()
-{
- int first, last;
- auto list = ui->coreModsTreeView->selectionModel()->selectedRows();
-
- if (!lastfirst(list, first, last))
- return;
- m_coremods->stopWatching();
- m_coremods->deleteMods(first, last);
- m_coremods->startWatching();
-}
-void LegacyModEditDialog::on_rmJarBtn_clicked()
-{
- int first, last;
- auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
-
- if (!lastfirst(list, first, last))
- return;
- m_jarmods->stopWatching();
- m_jarmods->deleteMods(first, last);
- m_jarmods->startWatching();
-}
-void LegacyModEditDialog::on_rmModBtn_clicked()
-{
- int first, last;
- auto list = ui->loaderModTreeView->selectionModel()->selectedRows();
-
- if (!lastfirst(list, first, last))
- return;
- m_mods->stopWatching();
- m_mods->deleteMods(first, last);
- m_mods->startWatching();
-}
-void LegacyModEditDialog::on_rmTexPackBtn_clicked()
-{
- int first, last;
- auto list = ui->texPackTreeView->selectionModel()->selectedRows();
-
- if (!lastfirst(list, first, last))
- return;
- m_texturepacks->stopWatching();
- m_texturepacks->deleteMods(first, last);
- m_texturepacks->startWatching();
-}
-void LegacyModEditDialog::on_viewCoreBtn_clicked()
-{
- openDirInDefaultProgram(m_inst->coreModsDir(), true);
-}
-void LegacyModEditDialog::on_viewModBtn_clicked()
-{
- openDirInDefaultProgram(m_inst->loaderModsDir(), true);
-}
-void LegacyModEditDialog::on_viewTexPackBtn_clicked()
-{
- openDirInDefaultProgram(m_inst->texturePacksDir(), true);
-}
-
-void LegacyModEditDialog::on_buttonBox_rejected()
-{
- close();
-}
-
-void LegacyModEditDialog::jarCurrent(QModelIndex current, QModelIndex previous)
-{
- if (!current.isValid())
- {
- ui->jarMIFrame->clear();
- return;
- }
- int row = current.row();
- Mod &m = m_jarmods->operator[](row);
- ui->jarMIFrame->updateWithMod(m);
-}
-
-void LegacyModEditDialog::coreCurrent(QModelIndex current, QModelIndex previous)
-{
- if (!current.isValid())
- {
- ui->coreMIFrame->clear();
- return;
- }
- int row = current.row();
- Mod &m = m_coremods->operator[](row);
- ui->coreMIFrame->updateWithMod(m);
-}
-
-void LegacyModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previous)
-{
- if (!current.isValid())
- {
- ui->loaderMIFrame->clear();
- return;
- }
- int row = current.row();
- Mod &m = m_mods->operator[](row);
- ui->loaderMIFrame->updateWithMod(m);
-}
diff --git a/gui/dialogs/LegacyModEditDialog.h b/gui/dialogs/LegacyModEditDialog.h
deleted file mode 100644
index d5582aef..00000000
--- a/gui/dialogs/LegacyModEditDialog.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Copyright 2013 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 <QDialog>
-#include "logic/LegacyInstance.h"
-#include <logic/net/NetJob.h>
-
-namespace Ui
-{
-class LegacyModEditDialog;
-}
-
-class LegacyModEditDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit LegacyModEditDialog(LegacyInstance *inst, QWidget *parent = 0);
- ~LegacyModEditDialog();
-
-private
-slots:
-
- void on_addJarBtn_clicked();
- void on_rmJarBtn_clicked();
- void on_addForgeBtn_clicked();
- void on_moveJarUpBtn_clicked();
- void on_moveJarDownBtn_clicked();
-
- void on_addCoreBtn_clicked();
- void on_rmCoreBtn_clicked();
- void on_viewCoreBtn_clicked();
-
- void on_addModBtn_clicked();
- void on_rmModBtn_clicked();
- void on_viewModBtn_clicked();
-
- void on_addTexPackBtn_clicked();
- void on_rmTexPackBtn_clicked();
- void on_viewTexPackBtn_clicked();
-
- // Questionable: SettingsDialog doesn't need this for some reason?
- void on_buttonBox_rejected();
-
- void jarCurrent(QModelIndex current, QModelIndex previous);
- void coreCurrent(QModelIndex current, QModelIndex previous);
- void loaderCurrent(QModelIndex current, QModelIndex previous);
-
-protected:
- bool eventFilter(QObject *obj, QEvent *ev);
- bool jarListFilter(QKeyEvent *ev);
- bool coreListFilter(QKeyEvent *ev);
- bool loaderListFilter(QKeyEvent *ev);
- bool texturePackListFilter(QKeyEvent *ev);
-
-private:
- Ui::LegacyModEditDialog *ui;
- std::shared_ptr<ModList> m_mods;
- std::shared_ptr<ModList> m_coremods;
- std::shared_ptr<ModList> m_jarmods;
- std::shared_ptr<ModList> m_texturepacks;
- LegacyInstance *m_inst;
- NetJobPtr forgeJob;
-};
diff --git a/gui/dialogs/LegacyModEditDialog.ui b/gui/dialogs/LegacyModEditDialog.ui
deleted file mode 100644
index 0662c712..00000000
--- a/gui/dialogs/LegacyModEditDialog.ui
+++ /dev/null
@@ -1,321 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>LegacyModEditDialog</class>
- <widget class="QDialog" name="LegacyModEditDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>540</width>
- <height>420</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Edit Mods</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QTabWidget" name="tabWidget">
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="jarTab">
- <attribute name="title">
- <string>Jar Mods</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="ModListView" name="jarModsTreeView">
- <property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOn</enum>
- </property>
- <property name="horizontalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QVBoxLayout" name="jarModsButtonBox">
- <item>
- <widget class="QPushButton" name="addJarBtn">
- <property name="text">
- <string>&amp;Add</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="rmJarBtn">
- <property name="text">
- <string>&amp;Remove</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="addForgeBtn">
- <property name="text">
- <string>MCForge</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="jarModsButtonSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="moveJarUpBtn">
- <property name="text">
- <string>Move &amp;Up</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="moveJarDownBtn">
- <property name="text">
- <string>Move &amp;Down</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </item>
- <item>
- <widget class="MCModInfoFrame" name="jarMIFrame">
- <property name="frameShadow">
- <enum>QFrame::Plain</enum>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="coreTab">
- <attribute name="title">
- <string>Core Mods</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="ModListView" name="coreModsTreeView">
- <property name="dragDropMode">
- <enum>QAbstractItemView::DropOnly</enum>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QVBoxLayout" name="coreModsButtonBox">
- <item>
- <widget class="QPushButton" name="addCoreBtn">
- <property name="text">
- <string>&amp;Add</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="rmCoreBtn">
- <property name="text">
- <string>&amp;Remove</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="coreModsButtonSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="viewCoreBtn">
- <property name="text">
- <string>&amp;View Folder</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </item>
- <item>
- <widget class="MCModInfoFrame" name="coreMIFrame">
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="modTab">
- <attribute name="title">
- <string>Loader Mods</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_4">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="ModListView" name="loaderModTreeView">
- <property name="acceptDrops">
- <bool>true</bool>
- </property>
- <property name="dragDropMode">
- <enum>QAbstractItemView::DropOnly</enum>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QVBoxLayout" name="mlModsButtonBox">
- <item>
- <widget class="QPushButton" name="addModBtn">
- <property name="text">
- <string>&amp;Add</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="rmModBtn">
- <property name="text">
- <string>&amp;Remove</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="mlModsButtonSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="viewModBtn">
- <property name="text">
- <string>&amp;View Folder</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </item>
- <item>
- <widget class="MCModInfoFrame" name="loaderMIFrame">
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="texPackTab">
- <property name="acceptDrops">
- <bool>false</bool>
- </property>
- <attribute name="title">
- <string>Texture Packs</string>
- </attribute>
- <layout class="QHBoxLayout" name="horizontalLayout_4">
- <item>
- <widget class="ModListView" name="texPackTreeView">
- <property name="acceptDrops">
- <bool>true</bool>
- </property>
- <property name="dragDropMode">
- <enum>QAbstractItemView::DropOnly</enum>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QVBoxLayout" name="texturePacksButtonBox">
- <item>
- <widget class="QPushButton" name="addTexPackBtn">
- <property name="text">
- <string>&amp;Add</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="rmTexPackBtn">
- <property name="text">
- <string>&amp;Remove</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="texturePacksButtonSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="viewTexPackBtn">
- <property name="text">
- <string>&amp;View Folder</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </widget>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Close</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>ModListView</class>
- <extends>QTreeView</extends>
- <header>gui/widgets/ModListView.h</header>
- </customwidget>
- <customwidget>
- <class>MCModInfoFrame</class>
- <extends>QFrame</extends>
- <header>gui/widgets/MCModInfoFrame.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
diff --git a/gui/dialogs/LwjglSelectDialog.cpp b/gui/dialogs/LwjglSelectDialog.cpp
index 046a4e2e..e64228b2 100644
--- a/gui/dialogs/LwjglSelectDialog.cpp
+++ b/gui/dialogs/LwjglSelectDialog.cpp
@@ -18,7 +18,7 @@
#include "ui_LwjglSelectDialog.h"
#include "gui/Platform.h"
-#include "logic/lists/LwjglVersionList.h"
+#include "logic/LwjglVersionList.h"
LWJGLSelectDialog::LWJGLSelectDialog(QWidget *parent)
: QDialog(parent), ui(new Ui::LWJGLSelectDialog)
diff --git a/gui/dialogs/ModEditDialogCommon.cpp b/gui/dialogs/ModEditDialogCommon.cpp
index eee42e5e..35942374 100644
--- a/gui/dialogs/ModEditDialogCommon.cpp
+++ b/gui/dialogs/ModEditDialogCommon.cpp
@@ -1,24 +1,7 @@
-/* Copyright 2013 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 "ModEditDialogCommon.h"
#include "CustomMessageBox.h"
-#include <QDesktopServices>
-#include <QMessageBox>
-#include <QString>
#include <QUrl>
+
bool lastfirst(QModelIndexList &list, int &first, int &last)
{
if (!list.size())
@@ -54,4 +37,4 @@ void showWebsiteForMod(QWidget *parentDlg, Mod &m)
QObject::tr("The mod author didn't provide a website link for this mod."),
QMessageBox::Warning);
}
-}
+} \ No newline at end of file
diff --git a/gui/dialogs/ModEditDialogCommon.h b/gui/dialogs/ModEditDialogCommon.h
index a226d5a9..3ccfbf6b 100644
--- a/gui/dialogs/ModEditDialogCommon.h
+++ b/gui/dialogs/ModEditDialogCommon.h
@@ -1,22 +1,9 @@
-/* Copyright 2013 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 <QAbstractItemModel>
+#include <QModelIndex>
+#include <QDesktopServices>
+#include <QWidget>
#include <logic/Mod.h>
bool lastfirst(QModelIndexList &list, int &first, int &last);
-void showWebsiteForMod(QWidget *parentDlg, Mod &m); \ No newline at end of file
+void showWebsiteForMod(QWidget *parentDlg, Mod &m);
diff --git a/gui/dialogs/NewInstanceDialog.cpp b/gui/dialogs/NewInstanceDialog.cpp
index c7b273af..41ae329c 100644
--- a/gui/dialogs/NewInstanceDialog.cpp
+++ b/gui/dialogs/NewInstanceDialog.cpp
@@ -20,7 +20,7 @@
#include "logic/InstanceFactory.h"
#include "logic/BaseVersion.h"
#include "logic/icons/IconList.h"
-#include "logic/lists/MinecraftVersionList.h"
+#include "logic/minecraft/MinecraftVersionList.h"
#include "logic/tasks/Task.h"
#include "gui/Platform.h"
@@ -47,7 +47,7 @@ NewInstanceDialog::NewInstanceDialog(QWidget *parent)
taskDlg->exec(loadTask);
}
*/
- setSelectedVersion(MMC->minecraftlist()->getLatestStable());
+ setSelectedVersion(MMC->minecraftlist()->getLatestStable(), true);
InstIconKey = "infinity";
ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
}
@@ -63,13 +63,17 @@ void NewInstanceDialog::updateDialogState()
->setEnabled(!instName().isEmpty() && m_selectedVersion);
}
-void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version)
+void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version, bool initial)
{
m_selectedVersion = version;
if (m_selectedVersion)
{
ui->versionTextBox->setText(version->name());
+ if(ui->instNameTextBox->text().isEmpty() && !initial)
+ {
+ ui->instNameTextBox->setText(version->name());
+ }
}
else
{
diff --git a/gui/dialogs/NewInstanceDialog.h b/gui/dialogs/NewInstanceDialog.h
index 4357c28d..17045ec0 100644
--- a/gui/dialogs/NewInstanceDialog.h
+++ b/gui/dialogs/NewInstanceDialog.h
@@ -33,7 +33,7 @@ public:
void updateDialogState();
- void setSelectedVersion(BaseVersionPtr version);
+ void setSelectedVersion(BaseVersionPtr version, bool initial = false);
void loadVersionList();
diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp
deleted file mode 100644
index a3598eb9..00000000
--- a/gui/dialogs/OneSixModEditDialog.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-/* Copyright 2013 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 "MultiMC.h"
-
-#include <pathutils.h>
-#include <QFileDialog>
-#include <QMessageBox>
-#include <QDebug>
-#include <QEvent>
-#include <QKeyEvent>
-#include <QDesktopServices>
-
-#include "OneSixModEditDialog.h"
-#include "ModEditDialogCommon.h"
-#include "ui_OneSixModEditDialog.h"
-
-#include "gui/Platform.h"
-#include "gui/dialogs/CustomMessageBox.h"
-#include "gui/dialogs/VersionSelectDialog.h"
-
-#include "gui/dialogs/ProgressDialog.h"
-
-#include "logic/ModList.h"
-#include "logic/VersionFinal.h"
-#include "logic/EnabledItemFilter.h"
-#include "logic/lists/ForgeVersionList.h"
-#include "logic/lists/LiteLoaderVersionList.h"
-#include "logic/ForgeInstaller.h"
-#include "logic/LiteLoaderInstaller.h"
-#include "logic/OneSixVersionBuilder.h"
-
-OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
- : QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst)
-{
- MultiMCPlatform::fixWM_CLASS(this);
- ui->setupUi(this);
- // libraries!
-
- m_version = m_inst->getFullVersion();
- if (m_version)
- {
- main_model = new EnabledItemFilter(this);
- main_model->setActive(true);
- main_model->setSourceModel(m_version.get());
- ui->libraryTreeView->setModel(main_model);
- ui->libraryTreeView->installEventFilter(this);
- connect(ui->libraryTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
- this, &OneSixModEditDialog::versionCurrent);
- updateVersionControls();
- }
- else
- {
- disableVersionControls();
- }
- // Loader mods
- {
- ensureFolderPathExists(m_inst->loaderModsDir());
- m_mods = m_inst->loaderModList();
- ui->loaderModTreeView->setModel(m_mods.get());
- ui->loaderModTreeView->installEventFilter(this);
- m_mods->startWatching();
- auto smodel = ui->loaderModTreeView->selectionModel();
- connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
- SLOT(loaderCurrent(QModelIndex, QModelIndex)));
- }
- // resource packs
- {
- ensureFolderPathExists(m_inst->resourcePacksDir());
- m_resourcepacks = m_inst->resourcePackList();
- ui->resPackTreeView->setModel(m_resourcepacks.get());
- ui->resPackTreeView->installEventFilter(this);
- m_resourcepacks->startWatching();
- }
-
- connect(m_inst, &OneSixInstance::versionReloaded, this,
- &OneSixModEditDialog::updateVersionControls);
-}
-
-OneSixModEditDialog::~OneSixModEditDialog()
-{
- m_mods->stopWatching();
- m_resourcepacks->stopWatching();
- delete ui;
-}
-
-void OneSixModEditDialog::updateVersionControls()
-{
- ui->forgeBtn->setEnabled(true);
- ui->liteloaderBtn->setEnabled(true);
-}
-
-void OneSixModEditDialog::disableVersionControls()
-{
- ui->forgeBtn->setEnabled(false);
- ui->liteloaderBtn->setEnabled(false);
- ui->reloadLibrariesBtn->setEnabled(false);
- ui->removeLibraryBtn->setEnabled(false);
-}
-
-bool OneSixModEditDialog::reloadInstanceVersion()
-{
- try
- {
- m_inst->reloadVersion();
- return true;
- }
- catch (MMCError &e)
- {
- QMessageBox::critical(this, tr("Error"), e.cause());
- return false;
- }
- catch (...)
- {
- QMessageBox::critical(
- this, tr("Error"),
- tr("Failed to load the version description file for reasons unknown."));
- return false;
- }
-}
-
-void OneSixModEditDialog::on_reloadLibrariesBtn_clicked()
-{
- reloadInstanceVersion();
-}
-
-void OneSixModEditDialog::on_removeLibraryBtn_clicked()
-{
- if (ui->libraryTreeView->currentIndex().isValid())
- {
- // FIXME: use actual model, not reloading.
- if (!m_version->remove(ui->libraryTreeView->currentIndex().row()))
- {
- QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
- }
- }
-}
-
-void OneSixModEditDialog::on_resetLibraryOrderBtn_clicked()
-{
- try
- {
- m_version->resetOrder();
- }
- catch (MMCError &e)
- {
- QMessageBox::critical(this, tr("Error"), e.cause());
- }
-}
-
-void OneSixModEditDialog::on_moveLibraryUpBtn_clicked()
-{
- if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
- {
- return;
- }
- try
- {
- const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
- const int newRow = 0;m_version->move(row, VersionFinal::MoveUp);
- //ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), QItemSelectionModel::ClearAndSelect);
- }
- catch (MMCError &e)
- {
- QMessageBox::critical(this, tr("Error"), e.cause());
- }
-}
-
-void OneSixModEditDialog::on_moveLibraryDownBtn_clicked()
-{
- if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
- {
- return;
- }
- try
- {
- const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
- const int newRow = 0;m_version->move(row, VersionFinal::MoveDown);
- //ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), QItemSelectionModel::ClearAndSelect);
- }
- catch (MMCError &e)
- {
- QMessageBox::critical(this, tr("Error"), e.cause());
- }
-}
-
-void OneSixModEditDialog::on_forgeBtn_clicked()
-{
- // FIXME: use actual model, not reloading. Move logic to model.
- if (m_version->hasFtbPack())
- {
- if (QMessageBox::question(this, tr("Revert?"),
- tr("This action will remove the FTB pack version patch. Continue?")) !=
- QMessageBox::Yes)
- {
- return;
- }
- m_version->removeFtbPack();
- reloadInstanceVersion();
- }
- if (m_version->isCustom())
- {
- if (QMessageBox::question(this, tr("Revert?"),
- tr("This action will remove your custom.json. Continue?")) !=
- QMessageBox::Yes)
- {
- return;
- }
- m_version->revertToBase();
- reloadInstanceVersion();
- }
- VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
- vselect.setExactFilter(1, m_inst->currentVersionId());
- vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
- m_inst->currentVersionId());
- if (vselect.exec() && vselect.selectedVersion())
- {
- ProgressDialog dialog(this);
- dialog.exec(ForgeInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
- }
-}
-
-void OneSixModEditDialog::on_liteloaderBtn_clicked()
-{
- if (m_version->hasFtbPack())
- {
- if (QMessageBox::question(this, tr("Revert?"),
- tr("This action will remove the FTB pack version patch. Continue?")) !=
- QMessageBox::Yes)
- {
- return;
- }
- m_version->removeFtbPack();
- reloadInstanceVersion();
- }
- if (m_version->isCustom())
- {
- if (QMessageBox::question(this, tr("Revert?"),
- tr("This action will remove your custom.json. Continue?")) !=
- QMessageBox::Yes)
- {
- return;
- }
- m_version->revertToBase();
- reloadInstanceVersion();
- }
- VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"),
- this);
- vselect.setExactFilter(1, m_inst->currentVersionId());
- vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") +
- m_inst->currentVersionId());
- if (vselect.exec() && vselect.selectedVersion())
- {
- ProgressDialog dialog(this);
- dialog.exec(LiteLoaderInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
- }
-}
-
-bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
-{
- switch (keyEvent->key())
- {
- case Qt::Key_Delete:
- on_rmModBtn_clicked();
- return true;
- case Qt::Key_Plus:
- on_addModBtn_clicked();
- return true;
- default:
- break;
- }
- return QDialog::eventFilter(ui->loaderModTreeView, keyEvent);
-}
-
-bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent)
-{
- switch (keyEvent->key())
- {
- case Qt::Key_Delete:
- on_rmResPackBtn_clicked();
- return true;
- case Qt::Key_Plus:
- on_addResPackBtn_clicked();
- return true;
- default:
- break;
- }
- return QDialog::eventFilter(ui->resPackTreeView, keyEvent);
-}
-
-bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev)
-{
- if (ev->type() != QEvent::KeyPress)
- {
- return QDialog::eventFilter(obj, ev);
- }
- QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
- if (obj == ui->loaderModTreeView)
- return loaderListFilter(keyEvent);
- if (obj == ui->resPackTreeView)
- return resourcePackListFilter(keyEvent);
- return QDialog::eventFilter(obj, ev);
-}
-
-void OneSixModEditDialog::on_buttonBox_rejected()
-{
- close();
-}
-
-void OneSixModEditDialog::on_addModBtn_clicked()
-{
- QStringList fileNames = QFileDialog::getOpenFileNames(
- this, QApplication::translate("LegacyModEditDialog", "Select Loader Mods"));
- for (auto filename : fileNames)
- {
- m_mods->stopWatching();
- m_mods->installMod(QFileInfo(filename));
- m_mods->startWatching();
- }
-}
-void OneSixModEditDialog::on_rmModBtn_clicked()
-{
- int first, last;
- auto list = ui->loaderModTreeView->selectionModel()->selectedRows();
-
- if (!lastfirst(list, first, last))
- return;
- m_mods->stopWatching();
- m_mods->deleteMods(first, last);
- m_mods->startWatching();
-}
-void OneSixModEditDialog::on_viewModBtn_clicked()
-{
- openDirInDefaultProgram(m_inst->loaderModsDir(), true);
-}
-
-void OneSixModEditDialog::on_addResPackBtn_clicked()
-{
- QStringList fileNames = QFileDialog::getOpenFileNames(
- this, QApplication::translate("LegacyModEditDialog", "Select Resource Packs"));
- for (auto filename : fileNames)
- {
- m_resourcepacks->stopWatching();
- m_resourcepacks->installMod(QFileInfo(filename));
- m_resourcepacks->startWatching();
- }
-}
-void OneSixModEditDialog::on_rmResPackBtn_clicked()
-{
- int first, last;
- auto list = ui->resPackTreeView->selectionModel()->selectedRows();
-
- if (!lastfirst(list, first, last))
- return;
- m_resourcepacks->stopWatching();
- m_resourcepacks->deleteMods(first, last);
- m_resourcepacks->startWatching();
-}
-void OneSixModEditDialog::on_viewResPackBtn_clicked()
-{
- openDirInDefaultProgram(m_inst->resourcePacksDir(), true);
-}
-
-void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previous)
-{
- if (!current.isValid())
- {
- ui->frame->clear();
- return;
- }
- int row = current.row();
- Mod &m = m_mods->operator[](row);
- ui->frame->updateWithMod(m);
-}
-
-void OneSixModEditDialog::versionCurrent(const QModelIndex &current,
- const QModelIndex &previous)
-{
- if (!current.isValid())
- {
- ui->removeLibraryBtn->setDisabled(true);
- }
- else
- {
- ui->removeLibraryBtn->setEnabled(m_version->canRemove(current.row()));
- }
-}
diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui
deleted file mode 100644
index 2c9f70bb..00000000
--- a/gui/dialogs/OneSixModEditDialog.ui
+++ /dev/null
@@ -1,310 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>OneSixModEditDialog</class>
- <widget class="QDialog" name="OneSixModEditDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>555</width>
- <height>463</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Manage Mods</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QTabWidget" name="tabWidget">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="libTab">
- <attribute name="title">
- <string>Version</string>
- </attribute>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_10">
- <item>
- <widget class="ModListView" name="libraryTreeView">
- <property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOn</enum>
- </property>
- <property name="horizontalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- <attribute name="headerVisible">
- <bool>false</bool>
- </attribute>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_4">
- <item>
- <widget class="QPushButton" name="forgeBtn">
- <property name="toolTip">
- <string>Replace any current custom version with Minecraft Forge</string>
- </property>
- <property name="text">
- <string>Install Forge</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="liteloaderBtn">
- <property name="text">
- <string>Install LiteLoader</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="Line" name="line">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="reloadLibrariesBtn">
- <property name="text">
- <string>Reload</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="removeLibraryBtn">
- <property name="text">
- <string>Remove</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="Line" name="line_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="moveLibraryUpBtn">
- <property name="toolTip">
- <string>This isn't implemented yet.</string>
- </property>
- <property name="text">
- <string>Move up</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="moveLibraryDownBtn">
- <property name="toolTip">
- <string>This isn't implemented yet.</string>
- </property>
- <property name="text">
- <string>Move down</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="resetLibraryOrderBtn">
- <property name="toolTip">
- <string>This isn't implemented yet.</string>
- </property>
- <property name="text">
- <string>Reset order</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_7">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="modTab">
- <attribute name="title">
- <string>Loader Mods</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_6">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="ModListView" name="loaderModTreeView">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="acceptDrops">
- <bool>true</bool>
- </property>
- <property name="dragDropMode">
- <enum>QAbstractItemView::DropOnly</enum>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QPushButton" name="addModBtn">
- <property name="text">
- <string>&amp;Add</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="rmModBtn">
- <property name="text">
- <string>&amp;Remove</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="viewModBtn">
- <property name="text">
- <string>&amp;View Folder</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </item>
- <item>
- <widget class="MCModInfoFrame" name="frame">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="resPackTab">
- <attribute name="title">
- <string>Resource Packs</string>
- </attribute>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="ModListView" name="resPackTreeView">
- <property name="acceptDrops">
- <bool>true</bool>
- </property>
- <property name="dragDropMode">
- <enum>QAbstractItemView::DropOnly</enum>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <widget class="QPushButton" name="addResPackBtn">
- <property name="text">
- <string>&amp;Add</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="rmResPackBtn">
- <property name="text">
- <string>&amp;Remove</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="viewResPackBtn">
- <property name="text">
- <string>&amp;View Folder</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Close</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>ModListView</class>
- <extends>QTreeView</extends>
- <header>gui/widgets/ModListView.h</header>
- </customwidget>
- <customwidget>
- <class>MCModInfoFrame</class>
- <extends>QFrame</extends>
- <header>gui/widgets/MCModInfoFrame.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
diff --git a/gui/dialogs/SettingsDialog.cpp b/gui/dialogs/SettingsDialog.cpp
index 2dd19077..f7333055 100644
--- a/gui/dialogs/SettingsDialog.cpp
+++ b/gui/dialogs/SettingsDialog.cpp
@@ -22,10 +22,11 @@
#include "gui/dialogs/VersionSelectDialog.h"
#include "gui/dialogs/CustomMessageBox.h"
-#include "logic/JavaUtils.h"
#include "logic/NagUtils.h"
-#include "logic/lists/JavaVersionList.h"
-#include <logic/JavaChecker.h>
+
+#include "logic/java/JavaUtils.h"
+#include "logic/java/JavaVersionList.h"
+#include "logic/java/JavaChecker.h"
#include "logic/updater/UpdateChecker.h"
@@ -37,6 +38,15 @@
#include <QMessageBox>
#include <QDir>
+// FIXME: possibly move elsewhere
+enum InstSortMode
+{
+ // Sort alphabetically by name.
+ Sort_Name,
+ // Sort by which instance was launched most recently.
+ Sort_LastLaunch
+};
+
SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SettingsDialog)
{
MultiMCPlatform::fixWM_CLASS(this);
diff --git a/gui/dialogs/SettingsDialog.h b/gui/dialogs/SettingsDialog.h
index d8495fdd..c65e9fb5 100644
--- a/gui/dialogs/SettingsDialog.h
+++ b/gui/dialogs/SettingsDialog.h
@@ -18,7 +18,7 @@
#include <memory>
#include <QDialog>
-#include "logic/JavaChecker.h"
+#include "logic/java/JavaChecker.h"
class SettingsObject;
diff --git a/gui/dialogs/VersionSelectDialog.cpp b/gui/dialogs/VersionSelectDialog.cpp
index cae5a732..fd8b569d 100644
--- a/gui/dialogs/VersionSelectDialog.cpp
+++ b/gui/dialogs/VersionSelectDialog.cpp
@@ -24,7 +24,7 @@
#include "gui/Platform.h"
#include <logic/BaseVersion.h>
-#include <logic/lists/BaseVersionList.h>
+#include <logic/BaseVersionList.h>
#include <logic/tasks/Task.h>
VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent,
diff --git a/gui/groupview/InstanceDelegate.cpp b/gui/groupview/InstanceDelegate.cpp
index cd26ddaa..64dc31d2 100644
--- a/gui/groupview/InstanceDelegate.cpp
+++ b/gui/groupview/InstanceDelegate.cpp
@@ -22,7 +22,7 @@
#include "GroupView.h"
#include "logic/BaseInstance.h"
-#include "logic/lists/InstanceList.h"
+#include "logic/InstanceList.h"
QCache<QString, QPixmap> ListViewDelegate::m_pixmapCache;
diff --git a/gui/pagedialog/PageDialog.cpp b/gui/pagedialog/PageDialog.cpp
new file mode 100644
index 00000000..4db5cd28
--- /dev/null
+++ b/gui/pagedialog/PageDialog.cpp
@@ -0,0 +1,199 @@
+/* Copyright 2014 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 "PageDialog.h"
+#include "gui/Platform.h"
+#include <QStackedLayout>
+#include <QPushButton>
+#include <QSortFilterProxyModel>
+#include <QUrl>
+#include "MultiMC.h"
+#include <QStyledItemDelegate>
+#include <QListView>
+#include <QLineEdit>
+#include <QLabel>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QDesktopServices>
+#include <settingsobject.h>
+
+#include "PageDialog_p.h"
+#include <gui/widgets/IconLabel.h>
+
+class PageEntryFilterModel : public QSortFilterProxyModel
+{
+public:
+ explicit PageEntryFilterModel(QObject *parent = 0) : QSortFilterProxyModel(parent)
+ {
+ }
+
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+ {
+ const QString pattern = filterRegExp().pattern();
+ const auto model = static_cast<PageModel *>(sourceModel());
+ const auto page = model->pages().at(sourceRow);
+ if(!page->shouldDisplay())
+ return false;
+ // Regular contents check, then check page-filter.
+ return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
+ }
+};
+
+PageDialog::PageDialog(BasePageProviderPtr pageProvider, QString defaultId, QWidget *parent) : QDialog(parent)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ createUI();
+ setWindowTitle(pageProvider->dialogTitle());
+ restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("PagedGeometry").toByteArray()));
+
+ m_model = new PageModel(this);
+ m_proxyModel = new PageEntryFilterModel(this);
+ int firstIndex = -1;
+ int counter = 0;
+ auto pages = pageProvider->getPages();
+ for(auto page: pages)
+ {
+ page->stackIndex = m_pageStack->addWidget(dynamic_cast<QWidget *>(page));
+ page->listIndex = counter;
+ counter++;
+ if(firstIndex == -1)
+ {
+ firstIndex = page->stackIndex;
+ }
+ }
+ m_model->setPages(pages);
+
+ m_proxyModel->setSourceModel(m_model);
+ m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+
+ m_pageList->setIconSize(QSize(pageIconSize, pageIconSize));
+ m_pageList->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_pageList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ m_pageList->setModel(m_proxyModel);
+ connect(m_pageList->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
+ this, SLOT(currentChanged(QModelIndex)));
+ m_pageStack->setStackingMode(QStackedLayout::StackOne);
+ m_pageList->setFocus();
+ // now find what we want to have selected...
+ auto page = m_model->findPageEntryById(defaultId);
+ QModelIndex index;
+ if(page)
+ {
+ index = m_proxyModel->mapFromSource(m_model->index(page->listIndex));
+ }
+ else
+ {
+ index = m_proxyModel->index(0,0);
+ }
+ if(index.isValid())
+ m_pageList->setCurrentIndex(index);
+}
+
+void PageDialog::createUI()
+{
+ m_pageStack = new QStackedLayout;
+ m_filter = new QLineEdit;
+ m_pageList = new PageView;
+ m_header = new QLabel();
+ m_iconHeader = new IconLabel(this, QIcon(), QSize(24,24));
+
+ QFont headerLabelFont = m_header->font();
+ headerLabelFont.setBold(true);
+ const int pointSize = headerLabelFont.pointSize();
+ if (pointSize > 0)
+ headerLabelFont.setPointSize(pointSize + 2);
+ m_header->setFont(headerLabelFont);
+
+ QHBoxLayout *headerHLayout = new QHBoxLayout;
+ const int leftMargin = MMC->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
+ headerHLayout->addSpacerItem(
+ new QSpacerItem(leftMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
+ headerHLayout->addWidget(m_header);
+ headerHLayout->addSpacerItem(
+ new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
+ headerHLayout->addWidget(m_iconHeader);
+
+ m_pageStack->setMargin(0);
+ m_pageStack->addWidget(new QWidget(this));
+
+ QDialogButtonBox *buttons =
+ new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close);
+ buttons->button(QDialogButtonBox::Close)->setDefault(true);
+ connect(buttons->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(close()));
+ connect(buttons->button(QDialogButtonBox::Help), SIGNAL(clicked()), this, SLOT(help()));
+
+ QGridLayout *mainGridLayout = new QGridLayout;
+ mainGridLayout->addLayout(headerHLayout, 0, 1, 1, 1);
+ mainGridLayout->addWidget(m_pageList, 0, 0, 2, 1);
+ mainGridLayout->addLayout(m_pageStack, 1, 1, 1, 1);
+ mainGridLayout->addWidget(buttons, 2, 0, 1, 2);
+ mainGridLayout->setColumnStretch(1, 4);
+ setLayout(mainGridLayout);
+}
+
+void PageDialog::showPage(int row)
+{
+ if(row != -1)
+ {
+ m_currentPage = m_model->pages().at(row);
+ }
+ else
+ {
+ m_currentPage = nullptr;
+ }
+ if(m_currentPage)
+ {
+ m_pageStack->setCurrentIndex(m_currentPage->stackIndex);
+ m_header->setText(m_currentPage->displayName());
+ m_iconHeader->setIcon(m_currentPage->icon());
+ }
+ else
+ {
+ m_pageStack->setCurrentIndex(0);
+ m_header->setText(QString());
+ m_iconHeader->setIcon(QIcon::fromTheme("bug"));
+ }
+}
+
+void PageDialog::help()
+{
+ if(m_currentPage)
+ {
+ QString pageId = m_currentPage->helpPage();
+ if(pageId.isEmpty())
+ return;
+ QDesktopServices::openUrl(QUrl("https://github.com/MultiMC/MultiMC5/wiki/" + pageId));
+ }
+}
+
+void PageDialog::currentChanged(const QModelIndex &current)
+{
+ showPage(current.isValid() ? m_proxyModel->mapToSource(current).row() : -1);
+}
+
+void PageDialog::closeEvent(QCloseEvent * event)
+{
+ bool accepted = true;
+ for(auto page: m_model->pages())
+ {
+ accepted &= page->apply();
+ }
+ if(accepted)
+ {
+ MMC->settings()->set("PagedGeometry", saveGeometry().toBase64());
+ QDialog::closeEvent(event);
+ }
+}
diff --git a/gui/pagedialog/PageDialog.h b/gui/pagedialog/PageDialog.h
new file mode 100644
index 00000000..88926ef1
--- /dev/null
+++ b/gui/pagedialog/PageDialog.h
@@ -0,0 +1,56 @@
+/* Copyright 2014 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 <QDialog>
+#include <QModelIndex>
+#include <gui/pages/BasePageProvider.h>
+
+class IconLabel;
+class QSortFilterProxyModel;
+class PageModel;
+class QLabel;
+class QListView;
+class QLineEdit;
+class QStackedLayout;
+
+class PageDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit PageDialog(BasePageProviderPtr pageProvider, QString defaultId = QString(),
+ QWidget *parent = 0);
+ virtual ~PageDialog() {};
+
+private:
+ void createUI();
+private
+slots:
+ void currentChanged(const QModelIndex &current);
+ void showPage(int row);
+ void help();
+ virtual void closeEvent(QCloseEvent *event);
+
+
+private:
+ BasePage * m_currentPage;
+ QSortFilterProxyModel *m_proxyModel;
+ PageModel *m_model;
+ QStackedLayout *m_pageStack;
+ QLineEdit *m_filter;
+ QListView *m_pageList;
+ QLabel *m_header;
+ IconLabel *m_iconHeader;
+};
diff --git a/gui/pagedialog/PageDialog_p.h b/gui/pagedialog/PageDialog_p.h
new file mode 100644
index 00000000..f10e8f2c
--- /dev/null
+++ b/gui/pagedialog/PageDialog_p.h
@@ -0,0 +1,106 @@
+#pragma once
+#include <QListView>
+#include <QStyledItemDelegate>
+#include <QEvent>
+#include <QScrollBar>
+
+class BasePage;
+const int pageIconSize = 24;
+
+class PageViewDelegate : public QStyledItemDelegate
+{
+public:
+ PageViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
+ {
+ }
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+ {
+ QSize size = QStyledItemDelegate::sizeHint(option, index);
+ size.setHeight(qMax(size.height(), 32));
+ return size;
+ }
+};
+
+class PageModel : public QAbstractListModel
+{
+public:
+ PageModel(QObject *parent = 0) : QAbstractListModel(parent)
+ {
+ QPixmap empty(pageIconSize, pageIconSize);
+ empty.fill(Qt::transparent);
+ m_emptyIcon = QIcon(empty);
+ }
+ virtual ~PageModel() {};
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const
+ {
+ return parent.isValid() ? 0 : m_pages.size();
+ }
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
+ {
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ return m_pages.at(index.row())->displayName();
+ case Qt::DecorationRole:
+ {
+ QIcon icon = m_pages.at(index.row())->icon();
+ if (icon.isNull())
+ icon = m_emptyIcon;
+ return icon;
+ }
+ }
+ return QVariant();
+ }
+
+ void setPages(const QList<BasePage *> &pages)
+ {
+ beginResetModel();
+ m_pages = pages;
+ endResetModel();
+ }
+ const QList<BasePage *> &pages() const
+ {
+ return m_pages;
+ }
+
+ BasePage * findPageEntryById(QString id)
+ {
+ for(auto page: m_pages)
+ {
+ if (page->id() == id)
+ return page;
+ }
+ return nullptr;
+ }
+
+ QList<BasePage *> m_pages;
+ QIcon m_emptyIcon;
+};
+
+class PageView : public QListView
+{
+public:
+ PageView(QWidget *parent = 0) : QListView(parent)
+ {
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
+ setItemDelegate(new PageViewDelegate(this));
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ }
+
+ virtual QSize sizeHint() const
+ {
+ int width = sizeHintForColumn(0) + frameWidth() * 2 + 5;
+ if (verticalScrollBar()->isVisible())
+ width += verticalScrollBar()->width();
+ return QSize(width, 100);
+ }
+
+ virtual bool eventFilter(QObject *obj, QEvent *event)
+ {
+ if (obj == verticalScrollBar() &&
+ (event->type() == QEvent::Show || event->type() == QEvent::Hide))
+ updateGeometry();
+ return QListView::eventFilter(obj, event);
+ }
+};
diff --git a/logic/NostalgiaInstance.h b/gui/pages/BasePage.h
index f95531d2..bba4996d 100644
--- a/logic/NostalgiaInstance.h
+++ b/gui/pages/BasePage.h
@@ -1,4 +1,4 @@
-/* Copyright 2013 MultiMC Contributors
+/* Copyright 2014 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,31 @@
*/
#pragma once
+#include <QString>
+#include <QIcon>
+#include <memory>
-#include "OneSixInstance.h"
-
-class NostalgiaInstance : public OneSixInstance
+class BasePage
{
- Q_OBJECT
public:
- explicit NostalgiaInstance(const QString &rootDir, SettingsObject *settings,
- QObject *parent = 0);
- virtual ~NostalgiaInstance() {};
- virtual QString getStatusbarDescription();
- virtual bool menuActionEnabled(QString action_name) const;
+ virtual ~BasePage(){};
+ virtual QString id() = 0;
+ virtual QString displayName() = 0;
+ virtual QIcon icon() = 0;
+ virtual bool apply()
+ {
+ return true;
+ }
+ virtual bool shouldDisplay()
+ {
+ return true;
+ }
+ virtual QString helpPage()
+ {
+ return QString();
+ }
+ int stackIndex = -1;
+ int listIndex = -1;
};
+
+typedef std::shared_ptr<BasePage> BasePagePtr;
diff --git a/gui/dialogs/EditNotesDialog.h b/gui/pages/BasePageProvider.h
index b74558c4..cff9c8e7 100644
--- a/gui/dialogs/EditNotesDialog.h
+++ b/gui/pages/BasePageProvider.h
@@ -1,4 +1,4 @@
-/* Copyright 2013 MultiMC Contributors
+/* Copyright 2014 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,24 +15,14 @@
#pragma once
-#include <QDialog>
+#include "BasePage.h"
+#include <memory>
-namespace Ui
+class BasePageProvider
{
-class EditNotesDialog;
-}
-
-class EditNotesDialog : public QDialog
-{
- Q_OBJECT
-
public:
- explicit EditNotesDialog(QString notes, QString name, QWidget *parent = 0);
- ~EditNotesDialog();
- QString getText();
-
-private:
- Ui::EditNotesDialog *ui;
- QString m_instance_name;
- QString m_instance_notes;
+ virtual QList<BasePage *> getPages() = 0;
+ virtual QString dialogTitle() = 0;
};
+
+typedef std::shared_ptr<BasePageProvider> BasePageProviderPtr;
diff --git a/gui/pages/InstanceSettingsPage.cpp b/gui/pages/InstanceSettingsPage.cpp
new file mode 100644
index 00000000..b4a6405f
--- /dev/null
+++ b/gui/pages/InstanceSettingsPage.cpp
@@ -0,0 +1,223 @@
+#include "InstanceSettingsPage.h"
+#include <gui/dialogs/VersionSelectDialog.h>
+#include "logic/NagUtils.h"
+#include <logic/java/JavaVersionList.h>
+#include "MultiMC.h"
+#include <QDialog>
+#include <QFileDialog>
+#include <QMessageBox>
+#include "ui_InstanceSettingsPage.h"
+
+QString InstanceSettingsPage::displayName()
+{
+ return tr("Settings");
+}
+
+QIcon InstanceSettingsPage::icon()
+{
+ return QIcon::fromTheme("settings");
+}
+
+QString InstanceSettingsPage::id()
+{
+ return "settings";
+}
+
+InstanceSettingsPage::InstanceSettingsPage(SettingsObject *s, QWidget *parent)
+ : QWidget(parent), ui(new Ui::InstanceSettingsPage), m_settings(s)
+{
+ ui->setupUi(this);
+ loadSettings();
+}
+
+InstanceSettingsPage::~InstanceSettingsPage()
+{
+ delete ui;
+}
+
+bool InstanceSettingsPage::apply()
+{
+ applySettings();
+ return true;
+}
+
+void InstanceSettingsPage::applySettings()
+{
+ // Console
+ bool console = ui->consoleSettingsBox->isChecked();
+ m_settings->set("OverrideConsole", console);
+ if (console)
+ {
+ m_settings->set("ShowConsole", ui->showConsoleCheck->isChecked());
+ m_settings->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked());
+ }
+ else
+ {
+ m_settings->reset("ShowConsole");
+ m_settings->reset("AutoCloseConsole");
+ }
+
+ // Window Size
+ bool window = ui->windowSizeGroupBox->isChecked();
+ m_settings->set("OverrideWindow", window);
+ if (window)
+ {
+ m_settings->set("LaunchMaximized", ui->maximizedCheckBox->isChecked());
+ m_settings->set("MinecraftWinWidth", ui->windowWidthSpinBox->value());
+ m_settings->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
+ }
+ else
+ {
+ m_settings->reset("LaunchMaximized");
+ m_settings->reset("MinecraftWinWidth");
+ m_settings->reset("MinecraftWinHeight");
+ }
+
+ // Memory
+ bool memory = ui->memoryGroupBox->isChecked();
+ m_settings->set("OverrideMemory", memory);
+ if (memory)
+ {
+ m_settings->set("MinMemAlloc", ui->minMemSpinBox->value());
+ m_settings->set("MaxMemAlloc", ui->maxMemSpinBox->value());
+ m_settings->set("PermGen", ui->permGenSpinBox->value());
+ }
+ else
+ {
+ m_settings->reset("MinMemAlloc");
+ m_settings->reset("MaxMemAlloc");
+ m_settings->reset("PermGen");
+ }
+
+ // Java Install Settings
+ bool javaInstall = ui->javaSettingsGroupBox->isChecked();
+ m_settings->set("OverrideJavaLocation", javaInstall);
+ if (javaInstall)
+ {
+ m_settings->set("JavaPath", ui->javaPathTextBox->text());
+ }
+ else
+ {
+ m_settings->reset("JavaPath");
+ }
+
+ // Java arguments
+ bool javaArgs = ui->javaArgumentsGroupBox->isChecked();
+ m_settings->set("OverrideJavaArgs", javaArgs);
+ if(javaArgs)
+ {
+ m_settings->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " "));
+ NagUtils::checkJVMArgs(m_settings->get("JvmArgs").toString(), this->parentWidget());
+ }
+ else
+ {
+ m_settings->reset("JvmArgs");
+ }
+
+ // old generic 'override both' is removed.
+ m_settings->reset("OverrideJava");
+
+ // Custom Commands
+ bool custcmd = ui->customCommandsGroupBox->isChecked();
+ m_settings->set("OverrideCommands", custcmd);
+ if (custcmd)
+ {
+ m_settings->set("PreLaunchCommand", ui->preLaunchCmdTextBox->text());
+ m_settings->set("PostExitCommand", ui->postExitCmdTextBox->text());
+ }
+ else
+ {
+ m_settings->reset("PreLaunchCommand");
+ m_settings->reset("PostExitCommand");
+ }
+}
+
+void InstanceSettingsPage::loadSettings()
+{
+ // Console
+ ui->consoleSettingsBox->setChecked(m_settings->get("OverrideConsole").toBool());
+ ui->showConsoleCheck->setChecked(m_settings->get("ShowConsole").toBool());
+ ui->autoCloseConsoleCheck->setChecked(m_settings->get("AutoCloseConsole").toBool());
+
+ // Window Size
+ ui->windowSizeGroupBox->setChecked(m_settings->get("OverrideWindow").toBool());
+ ui->maximizedCheckBox->setChecked(m_settings->get("LaunchMaximized").toBool());
+ ui->windowWidthSpinBox->setValue(m_settings->get("MinecraftWinWidth").toInt());
+ ui->windowHeightSpinBox->setValue(m_settings->get("MinecraftWinHeight").toInt());
+
+ // Memory
+ ui->memoryGroupBox->setChecked(m_settings->get("OverrideMemory").toBool());
+ ui->minMemSpinBox->setValue(m_settings->get("MinMemAlloc").toInt());
+ ui->maxMemSpinBox->setValue(m_settings->get("MaxMemAlloc").toInt());
+ ui->permGenSpinBox->setValue(m_settings->get("PermGen").toInt());
+
+ // Java Settings
+ bool overrideJava = m_settings->get("OverrideJava").toBool();
+ bool overrideLocation = m_settings->get("OverrideJavaLocation").toBool() || overrideJava;
+ bool overrideArgs = m_settings->get("OverrideJavaArgs").toBool() || overrideJava;
+
+ ui->javaSettingsGroupBox->setChecked(overrideLocation);
+ ui->javaPathTextBox->setText(m_settings->get("JavaPath").toString());
+
+ ui->javaArgumentsGroupBox->setChecked(overrideArgs);
+ ui->jvmArgsTextBox->setPlainText(m_settings->get("JvmArgs").toString());
+
+ // Custom Commands
+ ui->customCommandsGroupBox->setChecked(m_settings->get("OverrideCommands").toBool());
+ ui->preLaunchCmdTextBox->setText(m_settings->get("PreLaunchCommand").toString());
+ ui->postExitCmdTextBox->setText(m_settings->get("PostExitCommand").toString());
+}
+
+void InstanceSettingsPage::on_javaDetectBtn_clicked()
+{
+ JavaVersionPtr java;
+
+ VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this, true);
+ vselect.setResizeOn(2);
+ vselect.exec();
+
+ if (vselect.result() == QDialog::Accepted && vselect.selectedVersion())
+ {
+ java = std::dynamic_pointer_cast<JavaVersion>(vselect.selectedVersion());
+ ui->javaPathTextBox->setText(java->path);
+ }
+}
+
+void InstanceSettingsPage::on_javaBrowseBtn_clicked()
+{
+ QString dir = QFileDialog::getOpenFileName(this, tr("Find Java executable"));
+ if (!dir.isNull())
+ {
+ ui->javaPathTextBox->setText(dir);
+ }
+}
+
+void InstanceSettingsPage::on_javaTestBtn_clicked()
+{
+ checker.reset(new JavaChecker());
+ connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
+ SLOT(checkFinished(JavaCheckResult)));
+ checker->path = ui->javaPathTextBox->text();
+ checker->performCheck();
+}
+
+void InstanceSettingsPage::checkFinished(JavaCheckResult result)
+{
+ if (result.valid)
+ {
+ QString text;
+ text += "Java test succeeded!\n";
+ if (result.is_64bit)
+ text += "Using 64bit java.\n";
+ text += "\n";
+ text += "Platform reported: " + result.realPlatform;
+ QMessageBox::information(this, tr("Java test success"), text);
+ }
+ else
+ {
+ QMessageBox::warning(
+ this, tr("Java test failure"),
+ tr("The specified java binary didn't work. You should use the auto-detect feature, "
+ "or set the path to the java executable."));
+ }
+}
diff --git a/gui/dialogs/InstanceSettings.h b/gui/pages/InstanceSettingsPage.h
index e296db4c..db37d8d8 100644
--- a/gui/dialogs/InstanceSettings.h
+++ b/gui/pages/InstanceSettingsPage.h
@@ -1,4 +1,4 @@
-/* Copyright 2013 MultiMC Contributors
+/* Copyright 2014 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,38 +14,34 @@
*/
#pragma once
+#include <QWidget>
-#include <QDialog>
-#include "settingsobject.h"
-#include "logic/JavaChecker.h"
+#include <logic/OneSixInstance.h>
+#include <logic/net/NetJob.h>
+#include <logic/java/JavaChecker.h>
+#include "BasePage.h"
+class JavaChecker;
namespace Ui
{
-class InstanceSettings;
+class InstanceSettingsPage;
}
-class InstanceSettings : public QDialog
+class InstanceSettingsPage : public QWidget, public BasePage
{
Q_OBJECT
public:
- explicit InstanceSettings(SettingsObject *s, QWidget *parent = 0);
- ~InstanceSettings();
-
+ explicit InstanceSettingsPage(SettingsObject *s, QWidget *parent = 0);
+ virtual ~InstanceSettingsPage();
+ virtual QString displayName() override;
+ virtual QIcon icon() override;
+ virtual QString id() override;
+ virtual bool apply();
+ virtual QString helpPage() override { return "Instance-settings"; };
+private:
void updateCheckboxStuff();
-
- void applySettings();
- void loadSettings();
-
-protected:
- virtual void showEvent(QShowEvent *);
- virtual void closeEvent(QCloseEvent *);
-private
-slots:
- void on_customCommandsGroupBox_toggled(bool arg1);
- void on_buttonBox_accepted();
- void on_buttonBox_rejected();
-
+private slots:
void on_javaDetectBtn_clicked();
void on_javaTestBtn_clicked();
@@ -53,8 +49,11 @@ slots:
void on_javaBrowseBtn_clicked();
void checkFinished(JavaCheckResult result);
+
+ void applySettings();
+ void loadSettings();
private:
- Ui::InstanceSettings *ui;
- SettingsObject *m_obj;
+ Ui::InstanceSettingsPage *ui;
+ SettingsObject *m_settings;
std::shared_ptr<JavaChecker> checker;
};
diff --git a/gui/dialogs/InstanceSettings.ui b/gui/pages/InstanceSettingsPage.ui
index 9c7e1757..b8af6c60 100644
--- a/gui/dialogs/InstanceSettings.ui
+++ b/gui/pages/InstanceSettingsPage.ui
@@ -1,19 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>InstanceSettings</class>
- <widget class="QDialog" name="InstanceSettings">
+ <class>InstanceSettingsPage</class>
+ <widget class="QWidget" name="InstanceSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>526</width>
- <height>637</height>
+ <width>458</width>
+ <height>426</height>
</rect>
</property>
<property name="windowTitle">
- <string>Instance Settings</string>
+ <string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
<item>
<widget class="QTabWidget" name="settingsTabs">
<property name="tabShape">
@@ -24,16 +36,16 @@
</property>
<widget class="QWidget" name="minecraftTab">
<attribute name="title">
- <string>Minecraft</string>
+ <string>Java</string>
</attribute>
- <layout class="QVBoxLayout" name="verticalLayout_3">
+ <layout class="QVBoxLayout" name="verticalLayout_5">
<item>
- <widget class="QGroupBox" name="windowSizeGroupBox">
+ <widget class="QGroupBox" name="javaSettingsGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
- <string>Window Size</string>
+ <string>Java installation</string>
</property>
<property name="checkable">
<bool>true</bool>
@@ -41,90 +53,28 @@
<property name="checked">
<bool>false</bool>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_4">
- <item>
- <widget class="QCheckBox" name="maximizedCheckBox">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0" colspan="3">
+ <widget class="QLineEdit" name="javaPathTextBox"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QPushButton" name="javaDetectBtn">
<property name="text">
- <string>Start Minecraft maximized?</string>
+ <string>Auto-detect...</string>
</property>
</widget>
</item>
- <item>
- <layout class="QGridLayout" name="gridLayoutWindowSize">
- <item row="1" column="0">
- <widget class="QLabel" name="labelWindowHeight">
- <property name="text">
- <string>Window height:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="labelWindowWidth">
- <property name="text">
- <string>Window width:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QSpinBox" name="windowWidthSpinBox">
- <property name="minimum">
- <number>854</number>
- </property>
- <property name="maximum">
- <number>65536</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="value">
- <number>854</number>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QSpinBox" name="windowHeightSpinBox">
- <property name="minimum">
- <number>480</number>
- </property>
- <property name="maximum">
- <number>65536</number>
- </property>
- <property name="value">
- <number>480</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="consoleSettingsBox">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="title">
- <string>Console Settings</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="checked">
- <bool>false</bool>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QCheckBox" name="showConsoleCheck">
+ <item row="1" column="1">
+ <widget class="QPushButton" name="javaBrowseBtn">
<property name="text">
- <string>Show console while the game is running?</string>
+ <string>Browse...</string>
</property>
</widget>
</item>
- <item>
- <widget class="QCheckBox" name="autoCloseConsoleCheck">
+ <item row="1" column="2">
+ <widget class="QPushButton" name="javaTestBtn">
<property name="text">
- <string>Automatically close console when the game quits?</string>
+ <string>Test</string>
</property>
</widget>
</item>
@@ -132,26 +82,6 @@
</widget>
</item>
<item>
- <spacer name="verticalSpacerMinecraft">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="javaTab">
- <attribute name="title">
- <string>Java</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <item>
<widget class="QGroupBox" name="memoryGroupBox">
<property name="enabled">
<bool>true</bool>
@@ -257,12 +187,12 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="javaSettingsGroupBox">
+ <widget class="QGroupBox" name="javaArgumentsGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
- <string>Java Settings</string>
+ <string>Java arguments</string>
</property>
<property name="checkable">
<bool>true</bool>
@@ -270,45 +200,131 @@
<property name="checked">
<bool>false</bool>
</property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="2" column="4">
- <widget class="QPushButton" name="javaTestBtn">
- <property name="text">
- <string>Test</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="labelJavaPath">
- <property name="text">
- <string>Java path:</string>
- </property>
- </widget>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="1" column="1">
+ <widget class="QPlainTextEdit" name="jvmArgsTextBox"/>
</item>
- <item row="3" column="0">
- <widget class="QLabel" name="labelJVMArgs">
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerMinecraft">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="javaTab">
+ <attribute name="title">
+ <string>Game windows</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QGroupBox" name="windowSizeGroupBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Game Window</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QCheckBox" name="maximizedCheckBox">
<property name="text">
- <string>JVM arguments:</string>
+ <string>Start Minecraft maximized?</string>
</property>
</widget>
</item>
- <item row="3" column="2" colspan="3">
- <widget class="QLineEdit" name="jvmArgsTextBox"/>
- </item>
- <item row="0" column="2" colspan="3">
- <widget class="QLineEdit" name="javaPathTextBox"/>
+ <item>
+ <layout class="QGridLayout" name="gridLayoutWindowSize">
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelWindowHeight">
+ <property name="text">
+ <string>Window height:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelWindowWidth">
+ <property name="text">
+ <string>Window width:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="windowWidthSpinBox">
+ <property name="minimum">
+ <number>854</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>854</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="windowHeightSpinBox">
+ <property name="minimum">
+ <number>480</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="value">
+ <number>480</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
- <item row="2" column="3">
- <widget class="QPushButton" name="javaBrowseBtn">
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="consoleSettingsBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Console Settings</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="showConsoleCheck">
<property name="text">
- <string>Browse...</string>
+ <string>Show console while the game is running?</string>
</property>
</widget>
</item>
- <item row="2" column="2">
- <widget class="QPushButton" name="javaDetectBtn">
+ <item>
+ <widget class="QCheckBox" name="autoCloseConsoleCheck">
<property name="text">
- <string>Auto-detect...</string>
+ <string>Automatically close console when the game quits?</string>
</property>
</widget>
</item>
@@ -316,6 +332,26 @@
</widget>
</item>
<item>
+ <spacer name="verticalSpacerMinecraft_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>88</width>
+ <height>125</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Custom commands</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
<widget class="QGroupBox" name="customCommandsGroupBox">
<property name="enabled">
<bool>true</bool>
@@ -358,12 +394,6 @@
<property name="enabled">
<bool>false</bool>
</property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="text">
<string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string>
</property>
@@ -378,42 +408,25 @@
</property>
</widget>
</item>
+ <item>
+ <spacer name="verticalSpacerMinecraft_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>88</width>
+ <height>186</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</widget>
</item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
</layout>
</widget>
- <tabstops>
- <tabstop>settingsTabs</tabstop>
- <tabstop>buttonBox</tabstop>
- <tabstop>windowSizeGroupBox</tabstop>
- <tabstop>maximizedCheckBox</tabstop>
- <tabstop>windowWidthSpinBox</tabstop>
- <tabstop>windowHeightSpinBox</tabstop>
- <tabstop>consoleSettingsBox</tabstop>
- <tabstop>showConsoleCheck</tabstop>
- <tabstop>autoCloseConsoleCheck</tabstop>
- <tabstop>memoryGroupBox</tabstop>
- <tabstop>minMemSpinBox</tabstop>
- <tabstop>maxMemSpinBox</tabstop>
- <tabstop>permGenSpinBox</tabstop>
- <tabstop>javaSettingsGroupBox</tabstop>
- <tabstop>jvmArgsTextBox</tabstop>
- <tabstop>customCommandsGroupBox</tabstop>
- <tabstop>preLaunchCmdTextBox</tabstop>
- <tabstop>postExitCmdTextBox</tabstop>
- </tabstops>
<resources/>
<connections/>
</ui>
diff --git a/gui/pages/LegacyJarModPage.cpp b/gui/pages/LegacyJarModPage.cpp
new file mode 100644
index 00000000..f0f3d753
--- /dev/null
+++ b/gui/pages/LegacyJarModPage.cpp
@@ -0,0 +1,203 @@
+/* Copyright 2013 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 "LegacyJarModPage.h"
+#include "ui_LegacyJarModPage.h"
+#include "gui/dialogs/VersionSelectDialog.h"
+#include "gui/dialogs/ProgressDialog.h"
+#include "gui/dialogs/ModEditDialogCommon.h"
+#include "logic/ModList.h"
+#include "logic/LegacyInstance.h"
+#include "logic/forge/ForgeVersion.h"
+#include "logic/forge/ForgeVersionList.h"
+#include "MultiMC.h"
+#include <pathutils.h>
+#include <QtGui/QKeyEvent>
+#include <QFileDialog>
+#include <QKeyEvent>
+
+LegacyJarModPage::LegacyJarModPage(LegacyInstance *inst, QWidget *parent)
+ : QWidget(parent), ui(new Ui::LegacyJarModPage), m_inst(inst)
+{
+ ui->setupUi(this);
+ m_jarmods = m_inst->jarModList();
+ ui->jarModsTreeView->setModel(m_jarmods.get());
+ ui->jarModsTreeView->setDragDropMode(QAbstractItemView::DragDrop);
+ ui->jarModsTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui->jarModsTreeView->installEventFilter(this);
+ m_jarmods->startWatching();
+ auto smodel = ui->jarModsTreeView->selectionModel();
+ connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ SLOT(jarCurrent(QModelIndex, QModelIndex)));
+}
+
+LegacyJarModPage::~LegacyJarModPage()
+{
+ m_jarmods->stopWatching();
+ delete ui;
+}
+
+QString LegacyJarModPage::displayName()
+{
+ return tr("Jar Mods");
+}
+
+QIcon LegacyJarModPage::icon()
+{
+ return QIcon::fromTheme("plugin-red");
+}
+
+QString LegacyJarModPage::id()
+{
+ return "jarmods";
+}
+
+bool LegacyJarModPage::eventFilter(QObject *obj, QEvent *ev)
+{
+ if (ev->type() != QEvent::KeyPress || obj != ui->jarModsTreeView)
+ {
+ return QWidget::eventFilter(obj, ev);
+ }
+
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Up:
+ {
+ if (keyEvent->modifiers() & Qt::ControlModifier)
+ {
+ on_moveJarUpBtn_clicked();
+ return true;
+ }
+ break;
+ }
+ case Qt::Key_Down:
+ {
+ if (keyEvent->modifiers() & Qt::ControlModifier)
+ {
+ on_moveJarDownBtn_clicked();
+ return true;
+ }
+ break;
+ }
+ case Qt::Key_Delete:
+ on_rmJarBtn_clicked();
+ return true;
+ case Qt::Key_Plus:
+ on_addJarBtn_clicked();
+ return true;
+ default:
+ break;
+ }
+ return QWidget::eventFilter(obj, ev);
+}
+
+void LegacyJarModPage::on_addForgeBtn_clicked()
+{
+ VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
+ vselect.setExactFilter(1, m_inst->intendedVersionId());
+ if (vselect.exec() && vselect.selectedVersion())
+ {
+ ForgeVersionPtr forge =
+ std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
+ if (!forge)
+ return;
+ auto entry = MMC->metacache()->resolveEntry("minecraftforge", forge->filename());
+ if (entry->stale)
+ {
+ NetJob *fjob = new NetJob("Forge download");
+ fjob->addNetAction(CacheDownload::make(forge->universal_url, entry));
+ ProgressDialog dlg(this);
+ dlg.exec(fjob);
+ if (dlg.result() == QDialog::Accepted)
+ {
+ m_jarmods->stopWatching();
+ m_jarmods->installMod(QFileInfo(entry->getFullPath()));
+ m_jarmods->startWatching();
+ }
+ else
+ {
+ // failed to download forge :/
+ }
+ }
+ else
+ {
+ m_jarmods->stopWatching();
+ m_jarmods->installMod(QFileInfo(entry->getFullPath()));
+ m_jarmods->startWatching();
+ }
+ }
+}
+void LegacyJarModPage::on_addJarBtn_clicked()
+{
+ //: Title of jar mod selection dialog
+ QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Jar Mods"));
+ for (auto filename : fileNames)
+ {
+ m_jarmods->stopWatching();
+ m_jarmods->installMod(QFileInfo(filename));
+ m_jarmods->startWatching();
+ }
+}
+
+void LegacyJarModPage::on_moveJarDownBtn_clicked()
+{
+ int first, last;
+ auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+
+ m_jarmods->moveModsDown(first, last);
+}
+
+void LegacyJarModPage::on_moveJarUpBtn_clicked()
+{
+ int first, last;
+ auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_jarmods->moveModsUp(first, last);
+}
+
+void LegacyJarModPage::on_rmJarBtn_clicked()
+{
+ int first, last;
+ auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_jarmods->stopWatching();
+ m_jarmods->deleteMods(first, last);
+ m_jarmods->startWatching();
+}
+
+void LegacyJarModPage::on_viewJarBtn_clicked()
+{
+ openDirInDefaultProgram(m_inst->jarModsDir(), true);
+}
+
+void LegacyJarModPage::jarCurrent(QModelIndex current, QModelIndex previous)
+{
+ if (!current.isValid())
+ {
+ ui->jarMIFrame->clear();
+ return;
+ }
+ int row = current.row();
+ Mod &m = m_jarmods->operator[](row);
+ ui->jarMIFrame->updateWithMod(m);
+}
diff --git a/gui/pages/LegacyJarModPage.h b/gui/pages/LegacyJarModPage.h
new file mode 100644
index 00000000..0b28777b
--- /dev/null
+++ b/gui/pages/LegacyJarModPage.h
@@ -0,0 +1,62 @@
+/* Copyright 2013 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 <QDialog>
+#include <logic/net/NetJob.h>
+#include "BasePage.h"
+
+class ModList;
+class LegacyInstance;
+namespace Ui
+{
+class LegacyJarModPage;
+}
+
+class LegacyJarModPage : public QWidget, public BasePage
+{
+ Q_OBJECT
+
+public:
+ explicit LegacyJarModPage(LegacyInstance *inst, QWidget *parent = 0);
+ virtual ~LegacyJarModPage();
+
+ virtual QString displayName();
+ virtual QIcon icon();
+ virtual QString id();
+ virtual QString helpPage() override { return "Legacy-jar-mods"; };
+
+private
+slots:
+
+ void on_addJarBtn_clicked();
+ void on_rmJarBtn_clicked();
+ void on_addForgeBtn_clicked();
+ void on_moveJarUpBtn_clicked();
+ void on_moveJarDownBtn_clicked();
+ void on_viewJarBtn_clicked();
+
+ void jarCurrent(QModelIndex current, QModelIndex previous);
+
+protected:
+ virtual bool eventFilter(QObject *obj, QEvent *ev) override;
+
+private:
+ Ui::LegacyJarModPage *ui;
+ std::shared_ptr<ModList> m_jarmods;
+ LegacyInstance *m_inst;
+ NetJobPtr forgeJob;
+};
diff --git a/gui/pages/LegacyJarModPage.ui b/gui/pages/LegacyJarModPage.ui
new file mode 100644
index 00000000..a1da2b20
--- /dev/null
+++ b/gui/pages/LegacyJarModPage.ui
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LegacyJarModPage</class>
+ <widget class="QWidget" name="LegacyJarModPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>378</width>
+ <height>324</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>LegacyJarModPage</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="ModListView" name="jarModsTreeView">
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOn</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="jarModsButtonBox">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Selection</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="rmJarBtn">
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="moveJarUpBtn">
+ <property name="text">
+ <string>Move &amp;Up</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="moveJarDownBtn">
+ <property name="text">
+ <string>Move &amp;Down</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="LineSeparator" name="separator" native="true"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Install</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addJarBtn">
+ <property name="text">
+ <string>&amp;Add jar mod</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addForgeBtn">
+ <property name="text">
+ <string>Install Forge</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="viewJarBtn">
+ <property name="text">
+ <string>&amp;View Folder</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="MCModInfoFrame" name="jarMIFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ModListView</class>
+ <extends>QTreeView</extends>
+ <header>gui/widgets/ModListView.h</header>
+ </customwidget>
+ <customwidget>
+ <class>MCModInfoFrame</class>
+ <extends>QFrame</extends>
+ <header>gui/widgets/MCModInfoFrame.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>LineSeparator</class>
+ <extends>QWidget</extends>
+ <header>gui/widgets/LineSeparator.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/pages/LegacyUpgradePage.cpp b/gui/pages/LegacyUpgradePage.cpp
new file mode 100644
index 00000000..02729c79
--- /dev/null
+++ b/gui/pages/LegacyUpgradePage.cpp
@@ -0,0 +1,33 @@
+#include "LegacyUpgradePage.h"
+#include "ui_LegacyUpgradePage.h"
+
+QString LegacyUpgradePage::displayName()
+{
+ return tr("Upgrade");
+}
+
+QIcon LegacyUpgradePage::icon()
+{
+ return QIcon::fromTheme("checkupdate");
+}
+
+QString LegacyUpgradePage::id()
+{
+ return "upgrade";
+}
+
+LegacyUpgradePage::LegacyUpgradePage(LegacyInstance *inst, QWidget *parent)
+ : QWidget(parent), ui(new Ui::LegacyUpgradePage), m_inst(inst)
+{
+ ui->setupUi(this);
+}
+
+LegacyUpgradePage::~LegacyUpgradePage()
+{
+ delete ui;
+}
+
+void LegacyUpgradePage::on_upgradeButton_clicked()
+{
+ // now what?
+}
diff --git a/gui/pages/LegacyUpgradePage.h b/gui/pages/LegacyUpgradePage.h
new file mode 100644
index 00000000..4f287e95
--- /dev/null
+++ b/gui/pages/LegacyUpgradePage.h
@@ -0,0 +1,47 @@
+/* Copyright 2014 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <QWidget>
+
+#include <logic/OneSixInstance.h>
+#include <logic/net/NetJob.h>
+#include "BasePage.h"
+
+class EnabledItemFilter;
+namespace Ui
+{
+class LegacyUpgradePage;
+}
+
+class LegacyUpgradePage : public QWidget, public BasePage
+{
+ Q_OBJECT
+
+public:
+ explicit LegacyUpgradePage(LegacyInstance *inst, QWidget *parent = 0);
+ virtual ~LegacyUpgradePage();
+ virtual QString displayName() override;
+ virtual QIcon icon() override;
+ virtual QString id() override;
+ virtual QString helpPage() override { return "Legacy-upgrade"; };
+private
+slots:
+ void on_upgradeButton_clicked();
+
+private:
+ Ui::LegacyUpgradePage *ui;
+ LegacyInstance *m_inst;
+};
diff --git a/gui/pages/LegacyUpgradePage.ui b/gui/pages/LegacyUpgradePage.ui
new file mode 100644
index 00000000..8d676eae
--- /dev/null
+++ b/gui/pages/LegacyUpgradePage.ui
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LegacyUpgradePage</class>
+ <widget class="QWidget" name="LegacyUpgradePage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>546</width>
+ <height>405</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Upgrade</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QTextBrowser" name="textBrowser">
+ <property name="html">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt; &lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:18pt; font-weight:600;&quot;&gt;New format is available&lt;/span&gt; &lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;MultiMC now supports old Minecraft versions in the new (OneSix) instance format. The old format won't be getting any new features and only the most critical bugfixes. As a consequence, you should upgrade this instance. &lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The upgrade will create a new instance with the same contents as the current one, in the new format. The original instance will remain untouched, in case anything goes wrong in the process. &lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please report any issues on our &lt;a href=&quot;https://github.com/MultiMC/MultiMC5/issues&quot;&gt;&lt;img src=&quot;:/icons/multimc/22x22/bug.png&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/MultiMC/MultiMC5/issues&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#68a0df;&quot;&gt;github issues page&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCommandLinkButton" name="upgradeButton">
+ <property name="text">
+ <string>Start the upgrade! (Not Yet Implemented, Coming Soonâ„¢)</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/pages/ModFolderPage.cpp b/gui/pages/ModFolderPage.cpp
new file mode 100644
index 00000000..6d5c6226
--- /dev/null
+++ b/gui/pages/ModFolderPage.cpp
@@ -0,0 +1,141 @@
+/* Copyright 2014 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 "MultiMC.h"
+
+#include <pathutils.h>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QDebug>
+#include <QEvent>
+#include <QKeyEvent>
+#include <QDesktopServices>
+#include <QAbstractItemModel>
+
+#include "ModFolderPage.h"
+#include "ui_ModFolderPage.h"
+
+#include "gui/dialogs/CustomMessageBox.h"
+#include "gui/dialogs/ModEditDialogCommon.h"
+
+#include "logic/ModList.h"
+#include "logic/Mod.h"
+
+QString ModFolderPage::displayName()
+{
+ return m_displayName;
+}
+
+QIcon ModFolderPage::icon()
+{
+ return QIcon::fromTheme(m_iconName);
+}
+
+QString ModFolderPage::id()
+{
+ return m_id;
+}
+
+ModFolderPage::ModFolderPage(std::shared_ptr<ModList> mods, QString id, QString iconName,
+ QString displayName, QString helpPage, QWidget *parent)
+ : QWidget(parent), ui(new Ui::ModFolderPage)
+{
+ ui->setupUi(this);
+ m_mods = mods;
+ m_id = id;
+ m_displayName = displayName;
+ m_iconName = iconName;
+ m_helpName = helpPage;
+ ui->modTreeView->setModel(m_mods.get());
+ ui->modTreeView->installEventFilter(this);
+ m_mods->startWatching();
+ auto smodel = ui->modTreeView->selectionModel();
+ connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ SLOT(modCurrent(QModelIndex, QModelIndex)));
+}
+
+ModFolderPage::~ModFolderPage()
+{
+ m_mods->stopWatching();
+ delete ui;
+}
+
+bool ModFolderPage::modListFilter(QKeyEvent *keyEvent)
+{
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Delete:
+ on_rmModBtn_clicked();
+ return true;
+ case Qt::Key_Plus:
+ on_addModBtn_clicked();
+ return true;
+ default:
+ break;
+ }
+ return QWidget::eventFilter(ui->modTreeView, keyEvent);
+}
+
+bool ModFolderPage::eventFilter(QObject *obj, QEvent *ev)
+{
+ if (ev->type() != QEvent::KeyPress)
+ {
+ return QWidget::eventFilter(obj, ev);
+ }
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
+ if (obj == ui->modTreeView)
+ return modListFilter(keyEvent);
+ return QWidget::eventFilter(obj, ev);
+}
+
+void ModFolderPage::on_addModBtn_clicked()
+{
+ QStringList fileNames = QFileDialog::getOpenFileNames(
+ this, QApplication::translate("ModFolderPage", "Select Loader Mods"));
+ for (auto filename : fileNames)
+ {
+ m_mods->stopWatching();
+ m_mods->installMod(QFileInfo(filename));
+ m_mods->startWatching();
+ }
+}
+void ModFolderPage::on_rmModBtn_clicked()
+{
+ int first, last;
+ auto list = ui->modTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_mods->stopWatching();
+ m_mods->deleteMods(first, last);
+ m_mods->startWatching();
+}
+
+void ModFolderPage::on_viewModBtn_clicked()
+{
+ openDirInDefaultProgram(m_mods->dir().absolutePath(), true);
+}
+
+void ModFolderPage::modCurrent(const QModelIndex &current, const QModelIndex &previous)
+{
+ if (!current.isValid())
+ {
+ ui->frame->clear();
+ return;
+ }
+ int row = current.row();
+ Mod &m = m_mods->operator[](row);
+ ui->frame->updateWithMod(m);
+}
diff --git a/gui/pages/ModFolderPage.h b/gui/pages/ModFolderPage.h
new file mode 100644
index 00000000..b4e05928
--- /dev/null
+++ b/gui/pages/ModFolderPage.h
@@ -0,0 +1,61 @@
+/* Copyright 2014 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <QWidget>
+
+#include <logic/OneSixInstance.h>
+#include <logic/net/NetJob.h>
+#include "BasePage.h"
+
+class EnabledItemFilter;
+class ModList;
+namespace Ui
+{
+class ModFolderPage;
+}
+
+class ModFolderPage : public QWidget, public BasePage
+{
+ Q_OBJECT
+
+public:
+ explicit ModFolderPage(std::shared_ptr<ModList> mods, QString id, QString iconName,
+ QString displayName, QString helpPage = "" , QWidget *parent = 0);
+ virtual ~ModFolderPage();
+ virtual QString displayName() override;
+ virtual QIcon icon() override;
+ virtual QString id() override;
+ virtual QString helpPage() override { return m_helpName; };
+protected:
+ bool eventFilter(QObject *obj, QEvent *ev);
+ bool modListFilter(QKeyEvent *ev);
+
+private:
+ Ui::ModFolderPage *ui;
+ std::shared_ptr<ModList> m_mods;
+ QString m_iconName;
+ QString m_id;
+ QString m_displayName;
+ QString m_helpName;
+
+public slots:
+ void modCurrent(const QModelIndex &current, const QModelIndex &previous);
+
+private slots:
+ void on_addModBtn_clicked();
+ void on_rmModBtn_clicked();
+ void on_viewModBtn_clicked();
+};
diff --git a/gui/pages/ModFolderPage.ui b/gui/pages/ModFolderPage.ui
new file mode 100644
index 00000000..eb29a2c0
--- /dev/null
+++ b/gui/pages/ModFolderPage.ui
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ModFolderPage</class>
+ <widget class="QWidget" name="ModFolderPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>723</width>
+ <height>532</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Mods</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="ModListView" name="modTreeView">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DropOnly</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QPushButton" name="addModBtn">
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="rmModBtn">
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="viewModBtn">
+ <property name="text">
+ <string>&amp;View Folder</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="MCModInfoFrame" name="frame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ModListView</class>
+ <extends>QTreeView</extends>
+ <header>gui/widgets/ModListView.h</header>
+ </customwidget>
+ <customwidget>
+ <class>MCModInfoFrame</class>
+ <extends>QFrame</extends>
+ <header>gui/widgets/MCModInfoFrame.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/pages/NotesPage.cpp b/gui/pages/NotesPage.cpp
new file mode 100644
index 00000000..b4746a77
--- /dev/null
+++ b/gui/pages/NotesPage.cpp
@@ -0,0 +1,35 @@
+#include "NotesPage.h"
+#include "ui_NotesPage.h"
+
+QString NotesPage::displayName()
+{
+ return tr("Notes");
+}
+
+QIcon NotesPage::icon()
+{
+ return QIcon::fromTheme("news");
+}
+
+QString NotesPage::id()
+{
+ return "notes";
+}
+
+NotesPage::NotesPage(BaseInstance *inst, QWidget *parent)
+ : QWidget(parent), ui(new Ui::NotesPage), m_inst(inst)
+{
+ ui->setupUi(this);
+ ui->noteEditor->setText(m_inst->notes());
+}
+
+NotesPage::~NotesPage()
+{
+ delete ui;
+}
+
+bool NotesPage::apply()
+{
+ m_inst->setNotes(ui->noteEditor->toPlainText());
+ return true;
+}
diff --git a/gui/pages/NotesPage.h b/gui/pages/NotesPage.h
new file mode 100644
index 00000000..fe916f21
--- /dev/null
+++ b/gui/pages/NotesPage.h
@@ -0,0 +1,45 @@
+/* Copyright 2014 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <QWidget>
+
+#include <logic/BaseInstance.h>
+#include <logic/net/NetJob.h>
+#include "BasePage.h"
+
+class EnabledItemFilter;
+namespace Ui
+{
+class NotesPage;
+}
+
+class NotesPage : public QWidget, public BasePage
+{
+ Q_OBJECT
+
+public:
+ explicit NotesPage(BaseInstance *inst, QWidget *parent = 0);
+ virtual ~NotesPage();
+ virtual QString displayName() override;
+ virtual QIcon icon() override;
+ virtual QString id() override;
+ virtual bool apply();
+ virtual QString helpPage() override { return "Notes"; };
+
+private:
+ Ui::NotesPage *ui;
+ BaseInstance *m_inst;
+};
diff --git a/gui/pages/NotesPage.ui b/gui/pages/NotesPage.ui
new file mode 100644
index 00000000..ab33ffd3
--- /dev/null
+++ b/gui/pages/NotesPage.ui
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NotesPage</class>
+ <widget class="QWidget" name="NotesPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QTextEdit" name="noteEditor">
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOn</enum>
+ </property>
+ <property name="acceptRichText">
+ <bool>false</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/pages/ResourcePackPage.h b/gui/pages/ResourcePackPage.h
new file mode 100644
index 00000000..9332a6fa
--- /dev/null
+++ b/gui/pages/ResourcePackPage.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "ModFolderPage.h"
+
+class ResourcePackPage : public ModFolderPage
+{
+public:
+ explicit ResourcePackPage(BaseInstance *instance, QWidget *parent = 0)
+ : ModFolderPage(instance->resourcePackList(), "resourcepacks", "resourcepacks",
+ tr("Resource packs"), "Resource-packs", parent)
+ {
+ m_inst = instance;
+ }
+
+ virtual ~ResourcePackPage() {};
+ virtual bool shouldDisplay() override
+ {
+ return !m_inst->traits().contains("no-texturepacks") &&
+ !m_inst->traits().contains("texturepacks");
+ }
+private:
+ BaseInstance *m_inst;
+};
diff --git a/gui/pages/TexturePackPage.h b/gui/pages/TexturePackPage.h
new file mode 100644
index 00000000..b1a2f544
--- /dev/null
+++ b/gui/pages/TexturePackPage.h
@@ -0,0 +1,20 @@
+#pragma once
+#include "ModFolderPage.h"
+
+class TexturePackPage : public ModFolderPage
+{
+public:
+ explicit TexturePackPage(BaseInstance *instance, QWidget *parent = 0)
+ : ModFolderPage(instance->texturePackList(), "texturepacks", "resourcepacks",
+ tr("Texture packs"), "Texture-packs", parent)
+ {
+ m_inst = instance;
+ }
+ virtual ~TexturePackPage() {};
+ virtual bool shouldDisplay() override
+ {
+ return m_inst->traits().contains("texturepacks");
+ }
+private:
+ BaseInstance *m_inst;
+};
diff --git a/gui/pages/VersionPage.cpp b/gui/pages/VersionPage.cpp
new file mode 100644
index 00000000..3fd98d27
--- /dev/null
+++ b/gui/pages/VersionPage.cpp
@@ -0,0 +1,389 @@
+/* Copyright 2014 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 "MultiMC.h"
+
+#include <pathutils.h>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QDebug>
+#include <QEvent>
+#include <QKeyEvent>
+
+#include "VersionPage.h"
+#include "ui_VersionPage.h"
+
+#include "gui/Platform.h"
+#include "gui/dialogs/CustomMessageBox.h"
+#include "gui/dialogs/VersionSelectDialog.h"
+#include "gui/dialogs/ModEditDialogCommon.h"
+
+#include "gui/dialogs/ProgressDialog.h"
+
+#include "logic/ModList.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/EnabledItemFilter.h"
+#include "logic/forge/ForgeVersionList.h"
+#include "logic/forge/ForgeInstaller.h"
+#include "logic/liteloader/LiteLoaderVersionList.h"
+#include "logic/liteloader/LiteLoaderInstaller.h"
+#include "logic/minecraft/VersionBuilder.h"
+#include "logic/auth/MojangAccountList.h"
+
+#include <QAbstractItemModel>
+#include <logic/Mod.h>
+#include <logic/icons/IconList.h>
+
+#include <QMessageBox>
+#include <QListView>
+#include <QString>
+#include <QUrl>
+
+QString VersionPage::displayName()
+{
+ return tr("Version");
+}
+
+QIcon VersionPage::icon()
+{
+ return MMC->icons()->getIcon(m_inst->iconKey());
+}
+
+QString VersionPage::id()
+{
+ return "version";
+}
+
+VersionPage::VersionPage(OneSixInstance *inst, QWidget *parent)
+ : QWidget(parent), ui(new Ui::VersionPage), m_inst(inst)
+{
+ ui->setupUi(this);
+ // libraries!
+
+ m_version = m_inst->getFullVersion();
+ if (m_version)
+ {
+ main_model = new EnabledItemFilter(this);
+ main_model->setActive(true);
+ main_model->setSourceModel(m_version.get());
+ ui->libraryTreeView->setModel(main_model);
+ ui->libraryTreeView->installEventFilter(this);
+ ui->libraryTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
+ connect(ui->libraryTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
+ this, &VersionPage::versionCurrent);
+ updateVersionControls();
+ // select first item.
+ auto index = main_model->index(0,0);
+ if(index.isValid())
+ ui->libraryTreeView->setCurrentIndex(index);
+ }
+ else
+ {
+ disableVersionControls();
+ }
+ connect(m_inst, &OneSixInstance::versionReloaded, this,
+ &VersionPage::updateVersionControls);
+}
+
+VersionPage::~VersionPage()
+{
+ delete ui;
+}
+
+void VersionPage::updateVersionControls()
+{
+ ui->forgeBtn->setEnabled(true);
+ ui->liteloaderBtn->setEnabled(true);
+}
+
+void VersionPage::disableVersionControls()
+{
+ ui->forgeBtn->setEnabled(false);
+ ui->liteloaderBtn->setEnabled(false);
+ ui->reloadLibrariesBtn->setEnabled(false);
+ ui->removeLibraryBtn->setEnabled(false);
+}
+
+bool VersionPage::reloadInstanceVersion()
+{
+ try
+ {
+ m_inst->reloadVersion();
+ return true;
+ }
+ catch (MMCError &e)
+ {
+ QMessageBox::critical(this, tr("Error"), e.cause());
+ return false;
+ }
+ catch (...)
+ {
+ QMessageBox::critical(
+ this, tr("Error"),
+ tr("Failed to load the version description file for reasons unknown."));
+ return false;
+ }
+}
+
+void VersionPage::on_reloadLibrariesBtn_clicked()
+{
+ reloadInstanceVersion();
+}
+
+void VersionPage::on_removeLibraryBtn_clicked()
+{
+ if (ui->libraryTreeView->currentIndex().isValid())
+ {
+ // FIXME: use actual model, not reloading.
+ if (!m_version->remove(ui->libraryTreeView->currentIndex().row()))
+ {
+ QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
+ }
+ }
+}
+
+void VersionPage::on_jarmodBtn_clicked()
+{
+ QFileDialog w;
+ QSet<QString> locations;
+ QString modsFolder = MMC->settings()->get("CentralModsDir").toString();
+ auto f = [&](QStandardPaths::StandardLocation l)
+ {
+ QString location = QStandardPaths::writableLocation(l);
+ QFileInfo finfo(location);
+ if (!finfo.exists())
+ return;
+ locations.insert(location);
+ };
+ f(QStandardPaths::DesktopLocation);
+ f(QStandardPaths::DocumentsLocation);
+ f(QStandardPaths::DownloadLocation);
+ f(QStandardPaths::HomeLocation);
+ QList<QUrl> urls;
+ for (auto location : locations)
+ {
+ urls.append(QUrl::fromLocalFile(location));
+ }
+ urls.append(QUrl::fromLocalFile(modsFolder));
+
+ w.setFileMode(QFileDialog::ExistingFiles);
+ w.setAcceptMode(QFileDialog::AcceptOpen);
+ w.setNameFilter(tr("Minecraft jar mods (*.zip *.jar)"));
+ w.setDirectory(modsFolder);
+ w.setSidebarUrls(urls);
+
+ if (w.exec())
+ m_version->installJarMods(w.selectedFiles());
+}
+
+void VersionPage::on_resetLibraryOrderBtn_clicked()
+{
+ try
+ {
+ m_version->resetOrder();
+ }
+ catch (MMCError &e)
+ {
+ QMessageBox::critical(this, tr("Error"), e.cause());
+ }
+}
+
+void VersionPage::on_moveLibraryUpBtn_clicked()
+{
+ if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
+ {
+ return;
+ }
+ try
+ {
+ const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
+ const int newRow = 0;
+ m_version->move(row, InstanceVersion::MoveUp);
+ // ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow),
+ // QItemSelectionModel::ClearAndSelect);
+ }
+ catch (MMCError &e)
+ {
+ QMessageBox::critical(this, tr("Error"), e.cause());
+ }
+}
+
+void VersionPage::on_moveLibraryDownBtn_clicked()
+{
+ if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
+ {
+ return;
+ }
+ try
+ {
+ const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
+ const int newRow = 0;
+ m_version->move(row, InstanceVersion::MoveDown);
+ // ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow),
+ // QItemSelectionModel::ClearAndSelect);
+ }
+ catch (MMCError &e)
+ {
+ QMessageBox::critical(this, tr("Error"), e.cause());
+ }
+}
+
+void VersionPage::on_changeMCVersionBtn_clicked()
+{
+ VersionSelectDialog vselect(m_inst->versionList().get(), tr("Change Minecraft version"),
+ this);
+ if (!vselect.exec() || !vselect.selectedVersion())
+ return;
+
+ if (!MMC->accounts()->anyAccountIsValid())
+ {
+ CustomMessageBox::selectable(
+ this, tr("Error"),
+ tr("MultiMC cannot download Minecraft or update instances unless you have at least "
+ "one account added.\nPlease add your Mojang or Minecraft account."),
+ QMessageBox::Warning)->show();
+ return;
+ }
+
+ if (m_inst->versionIsCustom())
+ {
+ auto result = CustomMessageBox::selectable(
+ this, tr("Are you sure?"),
+ tr("This will remove any library/version customization you did previously. "
+ "This includes things like Forge install and similar."),
+ QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort,
+ QMessageBox::Abort)->exec();
+
+ if (result != QMessageBox::Ok)
+ return;
+ m_version->revertToVanilla();
+ reloadInstanceVersion();
+ }
+ m_inst->setIntendedVersionId(vselect.selectedVersion()->descriptor());
+
+ auto updateTask = m_inst->doUpdate();
+ if (!updateTask)
+ {
+ return;
+ }
+ ProgressDialog tDialog(this);
+ connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
+ tDialog.exec(updateTask.get());
+}
+
+void VersionPage::on_forgeBtn_clicked()
+{
+ // FIXME: use actual model, not reloading. Move logic to model.
+ if (m_version->hasFtbPack())
+ {
+ if (QMessageBox::question(
+ this, tr("Revert?"),
+ tr("This action will remove the FTB pack version patch. Continue?")) !=
+ QMessageBox::Yes)
+ {
+ return;
+ }
+ m_version->removeFtbPack();
+ reloadInstanceVersion();
+ }
+ if (m_version->hasDeprecatedVersionFiles())
+ {
+ if (QMessageBox::question(this, tr("Revert?"),
+ tr("This action will remove deprecated version files "
+ "(custom.json and version.json). Continue?")) !=
+ QMessageBox::Yes)
+ {
+ return;
+ }
+ m_version->removeDeprecatedVersionFiles();
+ reloadInstanceVersion();
+ }
+ VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
+ vselect.setExactFilter(1, m_inst->currentVersionId());
+ vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
+ m_inst->currentVersionId());
+ if (vselect.exec() && vselect.selectedVersion())
+ {
+ ProgressDialog dialog(this);
+ dialog.exec(
+ ForgeInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
+ }
+}
+
+void VersionPage::on_liteloaderBtn_clicked()
+{
+ if (m_version->hasFtbPack())
+ {
+ if (QMessageBox::question(
+ this, tr("Revert?"),
+ tr("This action will remove the FTB pack version patch. Continue?")) !=
+ QMessageBox::Yes)
+ {
+ return;
+ }
+ m_version->removeFtbPack();
+ reloadInstanceVersion();
+ }
+ if (m_version->hasDeprecatedVersionFiles())
+ {
+ if (QMessageBox::question(this, tr("Revert?"),
+ tr("This action will remove deprecated version files "
+ "(custom.json and version.json). Continue?")) !=
+ QMessageBox::Yes)
+ {
+ return;
+ }
+ m_version->removeDeprecatedVersionFiles();
+ reloadInstanceVersion();
+ }
+ VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"),
+ this);
+ vselect.setExactFilter(1, m_inst->currentVersionId());
+ vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") +
+ m_inst->currentVersionId());
+ if (vselect.exec() && vselect.selectedVersion())
+ {
+ ProgressDialog dialog(this);
+ dialog.exec(
+ LiteLoaderInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
+ }
+}
+
+void VersionPage::versionCurrent(const QModelIndex &current, const QModelIndex &previous)
+{
+ if (!current.isValid())
+ {
+ ui->removeLibraryBtn->setDisabled(true);
+ ui->moveLibraryDownBtn->setDisabled(true);
+ ui->moveLibraryUpBtn->setDisabled(true);
+ }
+ else
+ {
+ bool enabled = m_version->canRemove(current.row());
+ ui->removeLibraryBtn->setEnabled(enabled);
+ ui->moveLibraryDownBtn->setEnabled(enabled);
+ ui->moveLibraryUpBtn->setEnabled(enabled);
+ }
+ QString selectedId = m_version->versionFileId(current.row());
+ if (selectedId == "net.minecraft" || selectedId == "org.multimc.custom.json" ||
+ selectedId == "org.multimc.version.json")
+ {
+ ui->changeMCVersionBtn->setEnabled(true);
+ }
+ else
+ {
+ ui->changeMCVersionBtn->setEnabled(false);
+ }
+}
diff --git a/gui/dialogs/OneSixModEditDialog.h b/gui/pages/VersionPage.h
index e106c6fe..a8bc1515 100644
--- a/gui/dialogs/OneSixModEditDialog.h
+++ b/gui/pages/VersionPage.h
@@ -1,4 +1,4 @@
-/* Copyright 2013 MultiMC Contributors
+/* Copyright 2014 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,35 +14,33 @@
*/
#pragma once
-#include <QDialog>
+#include <QWidget>
#include <logic/OneSixInstance.h>
+#include <logic/net/NetJob.h>
+#include "BasePage.h"
class EnabledItemFilter;
namespace Ui
{
-class OneSixModEditDialog;
+class VersionPage;
}
-class OneSixModEditDialog : public QDialog
+class VersionPage : public QWidget, public BasePage
{
Q_OBJECT
public:
- explicit OneSixModEditDialog(OneSixInstance *inst, QWidget *parent = 0);
- virtual ~OneSixModEditDialog();
-
+ explicit VersionPage(OneSixInstance *inst, QWidget *parent = 0);
+ virtual ~VersionPage();
+ virtual QString displayName() override;
+ virtual QIcon icon() override;
+ virtual QString id() override;
+ virtual QString helpPage() override { return "Instance-version"; };
private
slots:
- void on_addModBtn_clicked();
- void on_rmModBtn_clicked();
- void on_viewModBtn_clicked();
- void on_addResPackBtn_clicked();
- void on_rmResPackBtn_clicked();
- void on_viewResPackBtn_clicked();
- // Questionable: SettingsDialog doesn't need this for some reason?
- void on_buttonBox_rejected();
+ // version tab
void on_forgeBtn_clicked();
void on_liteloaderBtn_clicked();
void on_reloadLibrariesBtn_clicked();
@@ -50,26 +48,24 @@ slots:
void on_resetLibraryOrderBtn_clicked();
void on_moveLibraryUpBtn_clicked();
void on_moveLibraryDownBtn_clicked();
+ void on_jarmodBtn_clicked();
+
void updateVersionControls();
void disableVersionControls();
+ void on_changeMCVersionBtn_clicked();
protected:
- bool eventFilter(QObject *obj, QEvent *ev);
- bool loaderListFilter(QKeyEvent *ev);
- bool resourcePackListFilter(QKeyEvent *ev);
/// FIXME: this shouldn't be necessary!
bool reloadInstanceVersion();
private:
- Ui::OneSixModEditDialog *ui;
- std::shared_ptr<VersionFinal> m_version;
- std::shared_ptr<ModList> m_mods;
- std::shared_ptr<ModList> m_resourcepacks;
+ Ui::VersionPage *ui;
+ std::shared_ptr<InstanceVersion> m_version;
EnabledItemFilter *main_model;
OneSixInstance *m_inst;
+ NetJobPtr forgeJob;
public
slots:
- void loaderCurrent(QModelIndex current, QModelIndex previous);
void versionCurrent(const QModelIndex &current, const QModelIndex &previous);
};
diff --git a/gui/pages/VersionPage.ui b/gui/pages/VersionPage.ui
new file mode 100644
index 00000000..f770df55
--- /dev/null
+++ b/gui/pages/VersionPage.ui
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>VersionPage</class>
+ <widget class="QWidget" name="VersionPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>475</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Version</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_10">
+ <item>
+ <widget class="ModListView" name="libraryTreeView">
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOn</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="headerHidden">
+ <bool>false</bool>
+ </property>
+ <attribute name="headerVisible">
+ <bool>true</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Selection</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="changeMCVersionBtn">
+ <property name="text">
+ <string>Change version</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="moveLibraryUpBtn">
+ <property name="toolTip">
+ <string>This isn't implemented yet.</string>
+ </property>
+ <property name="text">
+ <string>Move up</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="moveLibraryDownBtn">
+ <property name="toolTip">
+ <string>This isn't implemented yet.</string>
+ </property>
+ <property name="text">
+ <string>Move down</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeLibraryBtn">
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="LineSeparator" name="separator" native="true"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Install</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="forgeBtn">
+ <property name="toolTip">
+ <string>Replace any current custom version with Minecraft Forge</string>
+ </property>
+ <property name="text">
+ <string>Install Forge</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="liteloaderBtn">
+ <property name="text">
+ <string>Install LiteLoader</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="jarmodBtn">
+ <property name="text">
+ <string>Add jar mod</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="LineSeparator" name="widget" native="true"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>List</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="resetLibraryOrderBtn">
+ <property name="toolTip">
+ <string>This isn't implemented yet.</string>
+ </property>
+ <property name="text">
+ <string>Reset order</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="reloadLibrariesBtn">
+ <property name="text">
+ <string>Reload</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ModListView</class>
+ <extends>QTreeView</extends>
+ <header>gui/widgets/ModListView.h</header>
+ </customwidget>
+ <customwidget>
+ <class>LineSeparator</class>
+ <extends>QWidget</extends>
+ <header>gui/widgets/LineSeparator.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/widgets/LineSeparator.h b/gui/widgets/LineSeparator.h
index 376f2056..9546e747 100644
--- a/gui/widgets/LineSeparator.h
+++ b/gui/widgets/LineSeparator.h
@@ -9,10 +9,10 @@ class LineSeparator : public QWidget
public:
/// Create a line separator. orientation is the orientation of the line.
- explicit LineSeparator(QWidget *parent, Qt::Orientation orientation = Qt::Vertical);
+ explicit LineSeparator(QWidget *parent, Qt::Orientation orientation = Qt::Horizontal);
QSize sizeHint() const;
void paintEvent(QPaintEvent *);
void initStyleOption(QStyleOption *option) const;
private:
- Qt::Orientation m_orientation = Qt::Vertical;
+ Qt::Orientation m_orientation = Qt::Horizontal;
};
diff --git a/gui/widgets/ServerStatus.cpp b/gui/widgets/ServerStatus.cpp
index e540a301..10ed79fb 100644
--- a/gui/widgets/ServerStatus.cpp
+++ b/gui/widgets/ServerStatus.cpp
@@ -59,7 +59,7 @@ void ServerStatus::reloadStatus()
void ServerStatus::addLine()
{
- layout->addWidget(new LineSeparator(this));
+ layout->addWidget(new LineSeparator(this, Qt::Vertical));
}
void ServerStatus::addStatus(QString key, QString name)
diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp
index 669fd0ac..5660eb07 100644
--- a/logic/BaseInstaller.cpp
+++ b/logic/BaseInstaller.cpp
@@ -13,15 +13,10 @@
* limitations under the License.
*/
-#include "BaseInstaller.h"
-
#include <QFile>
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
-#include "OneSixInstance.h"
-
-#include "cmdutils.h"
+#include "logic/BaseInstaller.h"
+#include "logic/OneSixInstance.h"
BaseInstaller::BaseInstaller()
{
diff --git a/logic/BaseInstaller.h b/logic/BaseInstaller.h
index d59833cc..9531fbff 100644
--- a/logic/BaseInstaller.h
+++ b/logic/BaseInstaller.h
@@ -29,7 +29,7 @@ class BaseInstaller
{
public:
BaseInstaller();
-
+ virtual ~BaseInstaller(){};
bool isApplied(OneSixInstance *on);
virtual bool add(OneSixInstance *to);
diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp
index 5fa62593..0088530f 100644
--- a/logic/BaseInstance.cpp
+++ b/logic/BaseInstance.cpp
@@ -27,7 +27,7 @@
#include "pathutils.h"
#include <cmdutils.h>
-#include "lists/MinecraftVersionList.h"
+#include "logic/minecraft/MinecraftVersionList.h"
#include "logic/icons/IconList.h"
BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
@@ -58,6 +58,8 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
// Java Settings
settings().registerSetting("OverrideJava", false);
+ settings().registerSetting("OverrideJavaLocation", false);
+ settings().registerSetting("OverrideJavaArgs", false);
settings().registerOverride(globalSettings->getSetting("JavaPath"));
settings().registerOverride(globalSettings->getSetting("JvmArgs"));
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h
index 16791592..9598549b 100644
--- a/logic/BaseInstance.h
+++ b/logic/BaseInstance.h
@@ -22,9 +22,10 @@
#include <settingsobject.h>
#include "inifile.h"
-#include "lists/BaseVersionList.h"
+#include "logic/BaseVersionList.h"
#include "logic/auth/MojangAccount.h"
+class ModList;
class QDialog;
class QDir;
class Task;
@@ -89,7 +90,7 @@ public:
void setGroupInitial(QString val);
void setGroupPost(QString val);
- QStringList extraArguments() const;
+ virtual QStringList extraArguments() const;
virtual QString intendedVersionId() const = 0;
virtual bool setIntendedVersionId(QString version) = 0;
@@ -110,6 +111,19 @@ public:
virtual bool shouldUpdate() const = 0;
virtual void setShouldUpdate(bool val) = 0;
+ ////// Mod Lists //////
+ virtual std::shared_ptr<ModList> resourcePackList()
+ {
+ return nullptr;
+ }
+ virtual std::shared_ptr<ModList> texturePackList()
+ {
+ return nullptr;
+ }
+
+ /// Traits. Normally inside the version, depends on instance implementation.
+ virtual QSet <QString> traits() = 0;
+
/// Get the curent base jar of this instance. By default, it's the
/// versions/$version/$version.jar
QString baseJar() const;
@@ -169,12 +183,6 @@ public:
/// 'prepareForLaunch'
virtual void cleanupAfterRun() = 0;
- /// create a mod edit dialog for the instance
- virtual QDialog *createModEditDialog(QWidget *parent) = 0;
-
- /// is a particular action enabled with this instance selected?
- virtual bool menuActionEnabled(QString action_name) const = 0;
-
virtual QString getStatusbarDescription() = 0;
/// FIXME: this really should be elsewhere...
diff --git a/logic/BaseVersion.h b/logic/BaseVersion.h
index 43f5942a..ed63f551 100644
--- a/logic/BaseVersion.h
+++ b/logic/BaseVersion.h
@@ -16,6 +16,8 @@
#pragma once
#include <memory>
+#include <QString>
+#include <QMetaType>
/*!
* An abstract base class for versions.
@@ -52,4 +54,4 @@ struct BaseVersion
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
-Q_DECLARE_METATYPE(BaseVersionPtr) \ No newline at end of file
+Q_DECLARE_METATYPE(BaseVersionPtr)
diff --git a/logic/lists/BaseVersionList.cpp b/logic/BaseVersionList.cpp
index 6e2c5282..b34750b5 100644
--- a/logic/lists/BaseVersionList.cpp
+++ b/logic/BaseVersionList.cpp
@@ -13,7 +13,7 @@
* limitations under the License.
*/
-#include "logic/lists/BaseVersionList.h"
+#include "logic/BaseVersionList.h"
#include "logic/BaseVersion.h"
BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
diff --git a/logic/lists/BaseVersionList.h b/logic/BaseVersionList.h
index 21b44e8d..21b44e8d 100644
--- a/logic/lists/BaseVersionList.h
+++ b/logic/BaseVersionList.h
diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp
index 95fd855b..c0a392e0 100644
--- a/logic/InstanceFactory.cpp
+++ b/logic/InstanceFactory.cpp
@@ -13,28 +13,26 @@
* limitations under the License.
*/
-#include "InstanceFactory.h"
-
#include <QDir>
#include <QFileInfo>
-
-#include "BaseInstance.h"
-#include "LegacyInstance.h"
-#include "LegacyFTBInstance.h"
-#include "OneSixInstance.h"
-#include "OneSixFTBInstance.h"
-#include "NostalgiaInstance.h"
-#include "OneSixInstance.h"
-#include "BaseVersion.h"
-#include "MinecraftVersion.h"
-
-#include "inifile.h"
+#include <inifile.h>
#include <inisettingsobject.h>
#include <setting.h>
-#include "pathutils.h"
+#include <pathutils.h>
#include "logger/QsLog.h"
+#include "logic/InstanceFactory.h"
+
+#include "logic/BaseInstance.h"
+#include "logic/LegacyInstance.h"
+#include "logic/LegacyFTBInstance.h"
+#include "logic/OneSixInstance.h"
+#include "logic/OneSixFTBInstance.h"
+#include "logic/OneSixInstance.h"
+#include "logic/BaseVersion.h"
+#include "logic/minecraft/MinecraftVersion.h"
+
InstanceFactory InstanceFactory::loader;
InstanceFactory::InstanceFactory() : QObject(NULL)
@@ -51,7 +49,7 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(InstancePtr &inst,
QString inst_type = m_settings->get("InstanceType").toString();
// FIXME: replace with a map lookup, where instance classes register their types
- if (inst_type == "OneSix")
+ if (inst_type == "OneSix" || inst_type == "Nostalgia")
{
inst.reset(new OneSixInstance(instDir, m_settings, this));
}
@@ -59,10 +57,6 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(InstancePtr &inst,
{
inst.reset(new LegacyInstance(instDir, m_settings, this));
}
- else if (inst_type == "Nostalgia")
- {
- inst.reset(new NostalgiaInstance(instDir, m_settings, this));
- }
else if (inst_type == "LegacyFTB")
{
inst.reset(new LegacyFTBInstance(instDir, m_settings, this));
@@ -98,55 +92,26 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(InstancePtr &in
if (type == NormalInst)
{
- switch (mcVer->type)
- {
- case MinecraftVersion::Legacy:
- // TODO new instance type
- m_settings->set("InstanceType", "Legacy");
- inst.reset(new LegacyInstance(instDir, m_settings, this));
- inst->setIntendedVersionId(version->descriptor());
- inst->setShouldUseCustomBaseJar(false);
- break;
- case MinecraftVersion::OneSix:
- m_settings->set("InstanceType", "OneSix");
- inst.reset(new OneSixInstance(instDir, m_settings, this));
- inst->setIntendedVersionId(version->descriptor());
- inst->setShouldUseCustomBaseJar(false);
- break;
- case MinecraftVersion::Nostalgia:
- m_settings->set("InstanceType", "Nostalgia");
- inst.reset(new NostalgiaInstance(instDir, m_settings, this));
- inst->setIntendedVersionId(version->descriptor());
- inst->setShouldUseCustomBaseJar(false);
- break;
- default:
- {
- delete m_settings;
- return InstanceFactory::NoSuchVersion;
- }
- }
+ m_settings->set("InstanceType", "OneSix");
+ inst.reset(new OneSixInstance(instDir, m_settings, this));
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
}
else if (type == FTBInstance)
{
- switch (mcVer->type)
+ if(mcVer->usesLegacyLauncher())
{
- case MinecraftVersion::Legacy:
m_settings->set("InstanceType", "LegacyFTB");
inst.reset(new LegacyFTBInstance(instDir, m_settings, this));
inst->setIntendedVersionId(version->descriptor());
inst->setShouldUseCustomBaseJar(false);
- break;
- case MinecraftVersion::OneSix:
+ }
+ else
+ {
m_settings->set("InstanceType", "OneSixFTB");
inst.reset(new OneSixFTBInstance(instDir, m_settings, this));
inst->setIntendedVersionId(version->descriptor());
inst->setShouldUseCustomBaseJar(false);
- break;
- default:
- {
- delete m_settings;
- return InstanceFactory::NoSuchVersion;
- }
}
}
else
diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp
index c0079d80..9170c87f 100644
--- a/logic/InstanceLauncher.cpp
+++ b/logic/InstanceLauncher.cpp
@@ -22,7 +22,7 @@
#include "gui/dialogs/ProgressDialog.h"
#include "logic/MinecraftProcess.h"
-#include "logic/lists/InstanceList.h"
+#include "logic/InstanceList.h"
InstanceLauncher::InstanceLauncher(QString instId) : QObject(), instId(instId)
{
diff --git a/logic/lists/InstanceList.cpp b/logic/InstanceList.cpp
index 8808d6b5..5a988fd3 100644
--- a/logic/lists/InstanceList.cpp
+++ b/logic/InstanceList.cpp
@@ -27,13 +27,13 @@
#include <pathutils.h>
#include "MultiMC.h"
-#include "logic/lists/InstanceList.h"
+#include "logic/InstanceList.h"
#include "logic/icons/IconList.h"
-#include "logic/lists/MinecraftVersionList.h"
+#include "logic/minecraft/MinecraftVersionList.h"
#include "logic/BaseInstance.h"
#include "logic/InstanceFactory.h"
#include "logger/QsLog.h"
-#include <gui/groupview/GroupView.h>
+#include "gui/groupview/GroupView.h"
const static int GROUP_FILE_FORMAT_VERSION = 1;
@@ -46,16 +46,6 @@ InstanceList::InstanceList(const QString &instDir, QObject *parent)
{
QDir::current().mkpath(m_instDir);
}
-
- /*
- * FIXME HACK: instances sometimes need to be created at launch. They need the versions for
- * that.
- *
- * Remove this. it has no business of reloading the whole list. The instances which
- * need it should track such events themselves and CHANGE THEIR DATA ONLY!
- */
- connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this,
- &InstanceList::loadList);
}
InstanceList::~InstanceList()
diff --git a/logic/lists/InstanceList.h b/logic/InstanceList.h
index f0bbb7ec..f0bbb7ec 100644
--- a/logic/lists/InstanceList.h
+++ b/logic/InstanceList.h
diff --git a/logic/LegacyFTBInstance.cpp b/logic/LegacyFTBInstance.cpp
index 3c3356c9..73a1f73d 100644
--- a/logic/LegacyFTBInstance.cpp
+++ b/logic/LegacyFTBInstance.cpp
@@ -14,11 +14,6 @@ QString LegacyFTBInstance::getStatusbarDescription()
return "Legacy FTB: " + intendedVersionId();
}
-bool LegacyFTBInstance::menuActionEnabled(QString action_name) const
-{
- return false;
-}
-
QString LegacyFTBInstance::id() const
{
return "FTB/" + BaseInstance::id();
diff --git a/logic/LegacyFTBInstance.h b/logic/LegacyFTBInstance.h
index 70f60535..a2fe1ead 100644
--- a/logic/LegacyFTBInstance.h
+++ b/logic/LegacyFTBInstance.h
@@ -9,6 +9,5 @@ public:
explicit LegacyFTBInstance(const QString &rootDir, SettingsObject *settings,
QObject *parent = 0);
virtual QString getStatusbarDescription();
- virtual bool menuActionEnabled(QString action_name) const;
virtual QString id() const;
};
diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp
index 6648e059..4b856b0d 100644
--- a/logic/LegacyInstance.cpp
+++ b/logic/LegacyInstance.cpp
@@ -28,8 +28,12 @@
#include "logic/MinecraftProcess.h"
#include "logic/LegacyUpdate.h"
#include "logic/icons/IconList.h"
-
-#include "gui/dialogs/LegacyModEditDialog.h"
+#include "gui/pages/LegacyUpgradePage.h"
+#include "gui/pages/ModFolderPage.h"
+#include "gui/pages/LegacyJarModPage.h"
+#include <gui/pages/TexturePackPage.h>
+#include <gui/pages/InstanceSettingsPage.h>
+#include <gui/pages/NotesPage.h>
LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings,
QObject *parent)
@@ -42,6 +46,26 @@ LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings,
settings->registerSetting("IntendedJarVersion", "");
}
+QList<BasePage *> LegacyInstance::getPages()
+{
+ QList<BasePage *> values;
+ values.append(new LegacyUpgradePage(this));
+ values.append(new LegacyJarModPage(this));
+ values.append(new ModFolderPage(loaderModList(), "mods", "plugin-blue", tr("Loader mods"),
+ "Loader-mods"));
+ values.append(new ModFolderPage(coreModList(), "coremods", "plugin-green", tr("Core mods"),
+ "Core-mods"));
+ values.append(new TexturePackPage(this));
+ values.append(new NotesPage(this));
+ values.append(new InstanceSettingsPage(&settings()));
+ return values;
+}
+
+QString LegacyInstance::dialogTitle()
+{
+ return tr("Edit Instance (%1)").arg(name());
+}
+
std::shared_ptr<Task> LegacyInstance::doUpdate()
{
// make sure the jar mods list is initialized by asking for it.
@@ -50,7 +74,7 @@ std::shared_ptr<Task> LegacyInstance::doUpdate()
return std::shared_ptr<Task>(new LegacyUpdate(this, this));
}
-bool LegacyInstance::prepareForLaunch(AuthSessionPtr account, QString & launchScript)
+bool LegacyInstance::prepareForLaunch(AuthSessionPtr account, QString &launchScript)
{
QIcon icon = MMC->icons()->getIcon(iconKey());
auto pixmap = icon.pixmap(128, 128);
@@ -136,11 +160,6 @@ std::shared_ptr<ModList> LegacyInstance::texturePackList()
return d->texture_pack_list;
}
-QDialog *LegacyInstance::createModEditDialog(QWidget *parent)
-{
- return new LegacyModEditDialog(this, parent);
-}
-
QString LegacyInstance::jarModsDir() const
{
return PathCombine(instanceRoot(), "instMods");
@@ -263,27 +282,11 @@ QString LegacyInstance::defaultCustomBaseJar() const
return PathCombine(binDir(), "mcbackup.jar");
}
-bool LegacyInstance::menuActionEnabled(QString action_name) const
-{
- if (flags().contains(VersionBrokenFlag))
- {
- return false;
- }
- if (action_name == "actionChangeInstMCVersion")
- {
- return false;
- }
- return true;
-}
-
QString LegacyInstance::getStatusbarDescription()
{
if (flags().contains(VersionBrokenFlag))
{
- return "Legacy : " + intendedVersionId() + " (broken)";
+ return tr("Legacy : %1 (broken)").arg(intendedVersionId());
}
- if (shouldUpdate())
- return "Legacy : " + currentVersionId() + " -> " + intendedVersionId();
- else
- return "Legacy : " + currentVersionId();
+ return tr("Legacy : %1").arg(intendedVersionId());
}
diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h
index aa80968e..76a8c24d 100644
--- a/logic/LegacyInstance.h
+++ b/logic/LegacyInstance.h
@@ -16,11 +16,12 @@
#pragma once
#include "BaseInstance.h"
+#include "gui/pages/BasePageProvider.h"
class ModList;
class Task;
-class LegacyInstance : public BaseInstance
+class LegacyInstance : public BaseInstance, public BasePageProvider
{
Q_OBJECT
public:
@@ -34,6 +35,10 @@ public:
//! Path to the instance's modlist file.
QString modListFile() const;
+ ////// Edit Instance Dialog stuff //////
+ virtual QList<BasePage *> getPages();
+ virtual QString dialogTitle();
+
////// Mod Lists //////
std::shared_ptr<ModList> jarModList();
std::shared_ptr<ModList> coreModList();
@@ -75,18 +80,21 @@ public:
return false;
}
+ virtual QSet<QString> traits()
+ {
+ return {"legacy-instance", "texturepacks"};
+ };
+
virtual bool shouldUpdate() const override;
virtual void setShouldUpdate(bool val) override;
virtual std::shared_ptr<Task> doUpdate() override;
virtual bool prepareForLaunch(AuthSessionPtr account, QString & launchScript) override;
virtual void cleanupAfterRun() override;
- virtual QDialog *createModEditDialog(QWidget *parent) override;
virtual QString defaultBaseJar() const override;
virtual QString defaultCustomBaseJar() const override;
- bool menuActionEnabled(QString action_name) const;
virtual QString getStatusbarDescription() override;
protected
diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp
index 15c99234..00ee795d 100644
--- a/logic/LegacyUpdate.cpp
+++ b/logic/LegacyUpdate.cpp
@@ -13,69 +13,27 @@
* limitations under the License.
*/
-#include "LegacyUpdate.h"
-#include "lists/LwjglVersionList.h"
-#include "lists/MinecraftVersionList.h"
-#include "BaseInstance.h"
-#include "LegacyInstance.h"
-#include "MultiMC.h"
-#include "ModList.h"
+#include <QStringList>
+
#include <pathutils.h>
#include <quazip.h>
#include <quazipfile.h>
#include <JlCompress.h>
+
+#include "logic/LegacyUpdate.h"
+#include "logic/LwjglVersionList.h"
+#include "logic/minecraft/MinecraftVersionList.h"
+#include "logic/BaseInstance.h"
+#include "logic/LegacyInstance.h"
+#include "MultiMC.h"
+#include "logic/ModList.h"
+
#include "logger/QsLog.h"
#include "logic/net/URLConstants.h"
-#include <QStringList>
+
LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
{
- // 1.3 - 1.3.2
- auto libs13 = QList<FMLlib>{
- {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}};
-
- fmlLibsMapping["1.3.2"] = libs13;
-
- auto libs14 = QList<FMLlib>{
- {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false},
- {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb", false}};
-
- fmlLibsMapping["1.4"] = libs14;
- fmlLibsMapping["1.4.1"] = libs14;
- fmlLibsMapping["1.4.2"] = libs14;
- fmlLibsMapping["1.4.3"] = libs14;
- fmlLibsMapping["1.4.4"] = libs14;
- fmlLibsMapping["1.4.5"] = libs14;
- fmlLibsMapping["1.4.6"] = libs14;
- fmlLibsMapping["1.4.7"] = libs14;
-
- fmlLibsMapping["1.5"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
- {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8", false},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
-
- fmlLibsMapping["1.5.1"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
- {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6", false},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
-
- fmlLibsMapping["1.5.2"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
- {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9", false},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
}
void LegacyUpdate::executeTask()
@@ -110,6 +68,7 @@ void LegacyUpdate::fmllibsStart()
bool forge_present = false;
QString version = inst->intendedVersionId();
+ auto & fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
if (!fmlLibsMapping.contains(version))
{
lwjglStart();
@@ -152,7 +111,7 @@ void LegacyUpdate::fmllibsStart()
// now check the lib folder inside the instance for files.
for (auto &lib : libList)
{
- QFileInfo libInfo(PathCombine(inst->libDir(), lib.name));
+ QFileInfo libInfo(PathCombine(inst->libDir(), lib.filename));
if (libInfo.exists())
continue;
fmlLibsToProcess.append(lib);
@@ -171,9 +130,9 @@ void LegacyUpdate::fmllibsStart()
auto metacache = MMC->metacache();
for (auto &lib : fmlLibsToProcess)
{
- auto entry = metacache->resolveEntry("fmllibs", lib.name);
- QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.name
- : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.name;
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename
+ : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename;
dljob->addNetAction(CacheDownload::make(QUrl(urlString), entry));
}
@@ -196,16 +155,16 @@ void LegacyUpdate::fmllibsFinished()
for (auto &lib : fmlLibsToProcess)
{
progress(index, fmlLibsToProcess.size());
- auto entry = metacache->resolveEntry("fmllibs", lib.name);
- auto path = PathCombine(inst->libDir(), lib.name);
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ auto path = PathCombine(inst->libDir(), lib.filename);
if(!ensureFilePathExists(path))
{
emitFailed(tr("Failed creating FML library folder inside the instance."));
return;
}
- if (!QFile::copy(entry->getFullPath(), PathCombine(inst->libDir(), lib.name)))
+ if (!QFile::copy(entry->getFullPath(), PathCombine(inst->libDir(), lib.filename)))
{
- emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.name));
+ emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename));
return;
}
index++;
@@ -265,8 +224,6 @@ void LegacyUpdate::lwjglStart()
connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
connect(worker.get(), SIGNAL(finished(QNetworkReply *)),
SLOT(lwjglFinished(QNetworkReply *)));
- // connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
- // SLOT(downloadError(QNetworkReply::NetworkError)));
}
void LegacyUpdate::lwjglFinished(QNetworkReply *reply)
diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h
index 5b073cb7..140ee1e3 100644
--- a/logic/LegacyUpdate.h
+++ b/logic/LegacyUpdate.h
@@ -21,19 +21,13 @@
#include "logic/net/NetJob.h"
#include "logic/tasks/Task.h"
+#include "logic/VersionFilterData.h"
class MinecraftVersion;
class BaseInstance;
class QuaZip;
class Mod;
-struct FMLlib
-{
- QString name;
- QString checksum;
- bool ours;
-};
-
class LegacyUpdate : public Task
{
Q_OBJECT
@@ -84,5 +78,4 @@ private:
NetJobPtr legacyDownloadJob;
BaseInstance *m_inst = nullptr;
QList<FMLlib> fmlLibsToProcess;
- QMap<QString, QList<FMLlib>> fmlLibsMapping;
};
diff --git a/logic/lists/LwjglVersionList.cpp b/logic/LwjglVersionList.cpp
index df46d7be..df46d7be 100644
--- a/logic/lists/LwjglVersionList.cpp
+++ b/logic/LwjglVersionList.cpp
diff --git a/logic/lists/LwjglVersionList.h b/logic/LwjglVersionList.h
index fa57e8eb..fa57e8eb 100644
--- a/logic/lists/LwjglVersionList.h
+++ b/logic/LwjglVersionList.h
diff --git a/logic/MMCJson.cpp b/logic/MMCJson.cpp
index 65423436..8de88b6b 100644
--- a/logic/MMCJson.cpp
+++ b/logic/MMCJson.cpp
@@ -1,5 +1,6 @@
#include "MMCJson.h"
#include <QString>
+#include <QStringList>
#include <math.h>
bool MMCJson::ensureBoolean(const QJsonValue val, const QString what)
@@ -11,7 +12,7 @@ bool MMCJson::ensureBoolean(const QJsonValue val, const QString what)
QJsonValue MMCJson::ensureExists(QJsonValue val, const QString what)
{
- if(val.isNull())
+ if(val.isUndefined() || val.isUndefined())
throw JSONValidationError(what + " does not exist");
return val;
}
@@ -59,3 +60,24 @@ QString MMCJson::ensureString(const QJsonValue val, const QString what)
return val.toString();
}
+void MMCJson::writeString(QJsonObject &to, QString key, QString value)
+{
+ if(value.size())
+ {
+ to.insert(key, value);
+ }
+}
+
+void MMCJson::writeStringList(QJsonObject &to, QString key, QStringList values)
+{
+ if(values.size())
+ {
+ QJsonArray array;
+ for(auto value: values)
+ {
+ array.append(value);
+ }
+ to.insert(key, array);
+ }
+}
+
diff --git a/logic/MMCJson.h b/logic/MMCJson.h
index 71ded435..8408f29b 100644
--- a/logic/MMCJson.h
+++ b/logic/MMCJson.h
@@ -43,4 +43,23 @@ int ensureInteger(const QJsonValue val, QString what = "value");
/// make sure the value is converted into a double precision floating number. throw otherwise.
double ensureDouble(const QJsonValue val, QString what = "value");
+
+void writeString(QJsonObject & to, QString key, QString value);
+
+void writeStringList (QJsonObject & to, QString key, QStringList values);
+
+template <typename T>
+void writeObjectList (QJsonObject & to, QString key, QList<T> values)
+{
+ if(values.size())
+ {
+ QJsonArray array;
+ for(auto value: values)
+ {
+ array.append(value->toJson());
+ }
+ to.insert(key, array);
+ }
+}
}
+
diff --git a/logic/MinecraftVersion.h b/logic/MinecraftVersion.h
deleted file mode 100644
index 504381a8..00000000
--- a/logic/MinecraftVersion.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* Copyright 2013 Andrew Okin
- *
- * 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 <QStringList>
-
-struct MinecraftVersion : public BaseVersion
-{
- /*!
- * Gets the version's timestamp.
- * This is primarily used for sorting versions in a list.
- */
- qint64 timestamp;
-
- /// The URL that this version will be downloaded from. maybe.
- QString download_url;
-
- /// This version's type. Used internally to identify what kind of version this is.
- enum VersionType
- {
- OneSix,
- Legacy,
- Nostalgia
- } type;
-
- /// is this the latest version?
- bool is_latest = false;
-
- /// is this a snapshot?
- bool is_snapshot = false;
-
- QString m_name;
-
- QString m_descriptor;
-
- virtual QString descriptor()
- {
- return m_descriptor;
- }
-
- virtual QString name()
- {
- return m_name;
- }
-
- virtual QString typeString() const
- {
- QStringList pre_final;
- if (is_latest == true)
- {
- pre_final.append("Latest");
- }
- switch (type)
- {
- case OneSix:
- pre_final.append("OneSix");
- break;
- case Legacy:
- pre_final.append("Legacy");
- break;
- case Nostalgia:
- pre_final.append("Nostalgia");
- break;
-
- default:
- pre_final.append(QString("Type(%1)").arg(type));
- break;
- }
- if (is_snapshot == true)
- {
- pre_final.append("Snapshot");
- }
- return pre_final.join(' ');
- }
-};
diff --git a/logic/ModList.cpp b/logic/ModList.cpp
index 79b56986..f7770920 100644
--- a/logic/ModList.cpp
+++ b/logic/ModList.cpp
@@ -26,6 +26,7 @@
ModList::ModList(const QString &dir, const QString &list_file)
: QAbstractListModel(), m_dir(dir), m_list_file(list_file)
{
+ 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);
diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp
deleted file mode 100644
index 52820725..00000000
--- a/logic/NostalgiaInstance.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright 2013 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 "NostalgiaInstance.h"
-
-NostalgiaInstance::NostalgiaInstance(const QString &rootDir, SettingsObject *settings,
- QObject *parent)
- : OneSixInstance(rootDir, settings, parent)
-{
-}
-
-QString NostalgiaInstance::getStatusbarDescription()
-{
- if (flags().contains(VersionBrokenFlag))
- {
- return "Nostalgia : " + intendedVersionId() + " (broken)";
- }
- return "Nostalgia : " + intendedVersionId();
-}
-
-bool NostalgiaInstance::menuActionEnabled(QString action_name) const
-{
- return false;
-}
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
index 172830bb..ef951987 100644
--- a/logic/OneSixFTBInstance.cpp
+++ b/logic/OneSixFTBInstance.cpp
@@ -1,12 +1,12 @@
#include "OneSixFTBInstance.h"
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/minecraft/VersionBuilder.h"
#include "tasks/SequentialTask.h"
-#include "ForgeInstaller.h"
-#include "lists/ForgeVersionList.h"
+#include "forge/ForgeInstaller.h"
+#include "forge/ForgeVersionList.h"
#include "OneSixInstance_p.h"
-#include "OneSixVersionBuilder.h"
#include "MultiMC.h"
#include "pathutils.h"
@@ -100,6 +100,7 @@ QDir OneSixFTBInstance::librariesPath() const
{
return QDir(MMC->settings()->get("FTBRoot").toString() + "/libraries");
}
+
QDir OneSixFTBInstance::versionsPath() const
{
return QDir(MMC->settings()->get("FTBRoot").toString() + "/versions");
@@ -107,7 +108,6 @@ QDir OneSixFTBInstance::versionsPath() const
QStringList OneSixFTBInstance::externalPatches() const
{
- I_D(OneSixInstance);
return QStringList() << versionsPath().absoluteFilePath(intendedVersionId() + "/" + intendedVersionId() + ".json")
<< minecraftRoot() + "/pack.json";
}
@@ -125,10 +125,6 @@ QString OneSixFTBInstance::getStatusbarDescription()
}
return "OneSix FTB: " + intendedVersionId();
}
-bool OneSixFTBInstance::menuActionEnabled(QString action_name) const
-{
- return false;
-}
std::shared_ptr<Task> OneSixFTBInstance::doUpdate()
{
diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h
index 440dc9f1..ecfa2231 100644
--- a/logic/OneSixFTBInstance.h
+++ b/logic/OneSixFTBInstance.h
@@ -16,7 +16,6 @@ public:
void copy(const QDir &newDir) override;
virtual QString getStatusbarDescription();
- virtual bool menuActionEnabled(QString action_name) const;
virtual std::shared_ptr<Task> doUpdate() override;
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
index 6f3018cb..98244831 100644
--- a/logic/OneSixInstance.cpp
+++ b/logic/OneSixInstance.cpp
@@ -13,52 +13,82 @@
* limitations under the License.
*/
-#include "OneSixInstance.h"
-
#include <QIcon>
-
-#include "OneSixInstance_p.h"
-#include "OneSixUpdate.h"
-#include "VersionFinal.h"
-#include "pathutils.h"
+#include <pathutils.h>
#include "logger/QsLog.h"
-#include "assets/AssetsUtils.h"
#include "MultiMC.h"
-#include "icons/IconList.h"
-#include "MinecraftProcess.h"
-#include "gui/dialogs/OneSixModEditDialog.h"
-#include <MMCError.h>
+#include "MMCError.h"
+
+#include "logic/OneSixInstance.h"
-OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
+#include "logic/OneSixInstance_p.h"
+#include "logic/OneSixUpdate.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "minecraft/VersionBuildError.h"
+
+#include "logic/assets/AssetsUtils.h"
+#include "icons/IconList.h"
+#include "logic/MinecraftProcess.h"
+#include "gui/pagedialog/PageDialog.h"
+#include "gui/pages/VersionPage.h"
+#include <gui/pages/ModFolderPage.h>
+#include <gui/pages/ResourcePackPage.h>
+#include <gui/pages/TexturePackPage.h>
+#include <gui/pages/InstanceSettingsPage.h>
+#include <gui/pages/NotesPage.h>
+
+OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent)
: BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent)
{
I_D(OneSixInstance);
d->m_settings->registerSetting("IntendedVersion", "");
- d->m_settings->registerSetting("ShouldUpdate", false);
- d->version.reset(new VersionFinal(this, this));
- d->vanillaVersion.reset(new VersionFinal(this, this));
+ d->version.reset(new InstanceVersion(this, this));
}
void OneSixInstance::init()
{
- // FIXME: why is this decided here? what does this even mean?
- if (QDir(instanceRoot()).exists("version.json"))
+ try
{
- try
- {
- reloadVersion();
- }
- catch(MMCError & e)
- {
- // QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
- }
+ reloadVersion();
}
- else
+ catch (MMCError &e)
{
- clearVersion();
+ QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
}
}
+QList<BasePage *> OneSixInstance::getPages()
+{
+ QList<BasePage *> values;
+ values.append(new VersionPage(this));
+ values.append(new ModFolderPage(loaderModList(), "mods", "plugin-blue", tr("Loader mods"),
+ "Loader-mods"));
+ values.append(new ModFolderPage(coreModList(), "coremods", "plugin-green", tr("Core mods"),
+ "Core-mods"));
+ values.append(new ResourcePackPage(this));
+ values.append(new TexturePackPage(this));
+ values.append(new NotesPage(this));
+ values.append(new InstanceSettingsPage(&settings()));
+ return values;
+}
+
+QString OneSixInstance::dialogTitle()
+{
+ return tr("Edit Instance (%1)").arg(name());
+}
+
+QSet<QString> OneSixInstance::traits()
+{
+ auto version = getFullVersion();
+ if (!version)
+ {
+ return {"version-incomplete"};
+ }
+ else
+ return version->traits;
+}
+
std::shared_ptr<Task> OneSixInstance::doUpdate()
{
return std::shared_ptr<Task>(new OneSixUpdate(this));
@@ -88,7 +118,7 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with)
return result;
}
-QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version)
+QDir OneSixInstance::reconstructAssets(std::shared_ptr<InstanceVersion> version)
{
QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes"));
@@ -126,7 +156,7 @@ QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version)
QString original_path =
PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash);
QFile original(original_path);
- if(!original.exists())
+ if (!original.exists())
continue;
if (!target.exists())
{
@@ -138,7 +168,7 @@ QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version)
bool couldCopy = original.copy(target_path);
QLOG_DEBUG() << " Copying" << original_path << "to" << target_path
- << QString::number(couldCopy); // << original.errorString();
+ << QString::number(couldCopy); // << original.errorString();
}
}
@@ -189,7 +219,7 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
return parts;
}
-bool OneSixInstance::prepareForLaunch(AuthSessionPtr account, QString &launchScript)
+bool OneSixInstance::prepareForLaunch(AuthSessionPtr session, QString &launchScript)
{
I_D(OneSixInstance);
@@ -200,44 +230,79 @@ bool OneSixInstance::prepareForLaunch(AuthSessionPtr account, QString &launchScr
auto version = d->version;
if (!version)
return nullptr;
+
+ // libraries and class path.
{
auto libs = version->getActiveNormalLibs();
for (auto lib : libs)
{
launchScript += "cp " + librariesPath().absoluteFilePath(lib->storagePath()) + "\n";
}
- QString targetstr = version->id + "/" + version->id + ".jar";
- launchScript += "cp " + versionsPath().absoluteFilePath(targetstr) + "\n";
+ QString minecraftjarpath;
+ if (version->hasJarMods())
+ {
+ for (auto jarmod : version->jarMods)
+ {
+ launchScript += "cp " + jarmodsPath().absoluteFilePath(jarmod->name) + "\n";
+ }
+ minecraftjarpath = version->id + "/" + version->id + "-stripped.jar";
+ }
+ else
+ {
+ minecraftjarpath = version->id + "/" + version->id + ".jar";
+ }
+ launchScript += "cp " + versionsPath().absoluteFilePath(minecraftjarpath) + "\n";
+ }
+ if (!version->mainClass.isEmpty())
+ {
+ launchScript += "mainClass " + version->mainClass + "\n";
+ }
+ if (!version->appletClass.isEmpty())
+ {
+ launchScript += "appletClass " + version->appletClass + "\n";
}
- launchScript += "mainClass " + version->mainClass + "\n";
- for (auto param : processMinecraftArgs(account))
+ // generic minecraft params
+ for (auto param : processMinecraftArgs(session))
{
launchScript += "param " + param + "\n";
}
- // Set the width and height for 1.6 instances
- bool maximize = settings().get("LaunchMaximized").toBool();
- if (maximize)
+ // window size, title and state, legacy
{
- // this is probably a BAD idea
- // launchScript += "param --fullscreen\n";
+ 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";
}
- else
+
+ // legacy auth
+ {
+ launchScript += "userName " + session->player_name + "\n";
+ launchScript += "sessionId " + session->session + "\n";
+ }
+
+ // native libraries (mostly LWJGL)
{
- launchScript +=
- "param --width\nparam " + settings().get("MinecraftWinWidth").toString() + "\n";
- launchScript +=
- "param --height\nparam " + settings().get("MinecraftWinHeight").toString() + "\n";
+ QDir natives_dir(PathCombine(instanceRoot(), "natives/"));
+ for (auto native : version->getActiveNativeLibs())
+ {
+ QFileInfo finfo(PathCombine("libraries", native->storagePath()));
+ launchScript += "ext " + finfo.absoluteFilePath() + "\n";
+ }
+ launchScript += "natives " + natives_dir.absolutePath() + "\n";
}
- QDir natives_dir(PathCombine(instanceRoot(), "natives/"));
- launchScript += "windowTitle " + windowTitle() + "\n";
- for(auto native: version->getActiveNativeLibs())
+
+ // traits. including legacyLaunch and others ;)
+ for (auto trait : version->traits)
{
- QFileInfo finfo(PathCombine("libraries", native->storagePath()));
- launchScript += "ext " + finfo.absoluteFilePath() + "\n";
+ launchScript += "traits " + trait + "\n";
}
- launchScript += "natives " + natives_dir.absolutePath() + "\n";
launchScript += "launcher onesix\n";
return true;
}
@@ -260,6 +325,17 @@ std::shared_ptr<ModList> OneSixInstance::loaderModList()
return d->loader_mod_list;
}
+std::shared_ptr<ModList> OneSixInstance::coreModList()
+{
+ I_D(OneSixInstance);
+ if (!d->core_mod_list)
+ {
+ d->core_mod_list.reset(new ModList(coreModsDir()));
+ }
+ d->core_mod_list->update();
+ return d->core_mod_list;
+}
+
std::shared_ptr<ModList> OneSixInstance::resourcePackList()
{
I_D(OneSixInstance);
@@ -271,15 +347,20 @@ std::shared_ptr<ModList> OneSixInstance::resourcePackList()
return d->resource_pack_list;
}
-QDialog *OneSixInstance::createModEditDialog(QWidget *parent)
+std::shared_ptr<ModList> OneSixInstance::texturePackList()
{
- return new OneSixModEditDialog(this, parent);
+ I_D(OneSixInstance);
+ if (!d->texture_pack_list)
+ {
+ d->texture_pack_list.reset(new ModList(texturePacksDir()));
+ }
+ d->texture_pack_list->update();
+ return d->texture_pack_list;
}
bool OneSixInstance::setIntendedVersionId(QString version)
{
settings().set("IntendedVersion", version);
- setShouldUpdate(true);
QFile::remove(PathCombine(instanceRoot(), "version.json"));
clearVersion();
return true;
@@ -290,34 +371,31 @@ QString OneSixInstance::intendedVersionId() const
return settings().get("IntendedVersion").toString();
}
-void OneSixInstance::setShouldUpdate(bool val)
+void OneSixInstance::setShouldUpdate(bool)
{
- settings().set("ShouldUpdate", val);
}
bool OneSixInstance::shouldUpdate() const
{
- QVariant var = settings().get("ShouldUpdate");
- if (!var.isValid() || var.toBool() == false)
- {
- return intendedVersionId() != currentVersionId();
- }
return true;
}
bool OneSixInstance::versionIsCustom()
{
- QDir patches(PathCombine(instanceRoot(), "patches/"));
- return (patches.exists() && patches.count() >= 0)
- || QFile::exists(PathCombine(instanceRoot(), "custom.json"))
- || QFile::exists(PathCombine(instanceRoot(), "user.json"));
+ I_D(const OneSixInstance);
+ auto ver = d->version;
+ if (ver)
+ {
+ return !ver->isVanilla();
+ }
+ return false;
}
bool OneSixInstance::versionIsFTBPack()
{
I_D(const OneSixInstance);
auto ver = d->version;
- if(ver)
+ if (ver)
{
return ver->hasFtbPack();
}
@@ -335,17 +413,18 @@ void OneSixInstance::reloadVersion()
try
{
- d->version->reload(false, externalPatches());
- d->vanillaVersion->reload(true, externalPatches());
+ d->version->reload(externalPatches());
d->m_flags.remove(VersionBrokenFlag);
emit versionReloaded();
}
- catch(MMCError & error)
+ catch (VersionIncomplete &error)
+ {
+ }
+ catch (MMCError &error)
{
d->version->clear();
- d->vanillaVersion->clear();
d->m_flags.insert(VersionBrokenFlag);
- //TODO: rethrow to show some error message(s)?
+ // TODO: rethrow to show some error message(s)?
emit versionReloaded();
throw;
}
@@ -355,22 +434,15 @@ void OneSixInstance::clearVersion()
{
I_D(OneSixInstance);
d->version->clear();
- d->vanillaVersion->clear();
emit versionReloaded();
}
-std::shared_ptr<VersionFinal> OneSixInstance::getFullVersion() const
+std::shared_ptr<InstanceVersion> OneSixInstance::getFullVersion() const
{
I_D(const OneSixInstance);
return d->version;
}
-std::shared_ptr<VersionFinal> OneSixInstance::getVanillaVersion() const
-{
- I_D(const OneSixInstance);
- return d->vanillaVersion;
-}
-
QString OneSixInstance::defaultBaseJar() const
{
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
@@ -381,37 +453,38 @@ QString OneSixInstance::defaultCustomBaseJar() const
return PathCombine(instanceRoot(), "custom.jar");
}
-bool OneSixInstance::menuActionEnabled(QString action_name) const
+QString OneSixInstance::getStatusbarDescription()
{
- if (flags().contains(VersionBrokenFlag))
+ QStringList traits;
+ if (versionIsCustom())
{
- return false;
+ traits.append(tr("custom"));
}
- if (action_name == "actionChangeInstLWJGLVersion")
+ if (flags().contains(VersionBrokenFlag))
{
- return false;
+ traits.append(tr("broken"));
}
- return true;
-}
-QString OneSixInstance::getStatusbarDescription()
-{
- QString descr = "OneSix : " + intendedVersionId();
- if (versionIsCustom())
+ if (traits.size())
{
- descr += " (custom)";
+ return tr("Minecraft %1 (%2)").arg(intendedVersionId()).arg(traits.join(", "));
}
- if (flags().contains(VersionBrokenFlag))
+ else
{
- descr += " (broken)";
+ return tr("Minecraft %1").arg(intendedVersionId());
}
- return descr;
}
QDir OneSixInstance::librariesPath() const
{
return QDir::current().absoluteFilePath("libraries");
}
+
+QDir OneSixInstance::jarmodsPath() const
+{
+ return QDir(jarModsDir());
+}
+
QDir OneSixInstance::versionsPath() const
{
return QDir::current().absoluteFilePath("versions");
@@ -429,7 +502,7 @@ bool OneSixInstance::providesVersionFile() const
bool OneSixInstance::reload()
{
- if(BaseInstance::reload())
+ if (BaseInstance::reload())
{
try
{
@@ -449,12 +522,46 @@ QString OneSixInstance::loaderModsDir() const
return PathCombine(minecraftRoot(), "mods");
}
+QString OneSixInstance::coreModsDir() const
+{
+ return PathCombine(minecraftRoot(), "coremods");
+}
+
QString OneSixInstance::resourcePacksDir() const
{
return PathCombine(minecraftRoot(), "resourcepacks");
}
+QString OneSixInstance::texturePacksDir() const
+{
+ return PathCombine(minecraftRoot(), "texturepacks");
+}
+
QString OneSixInstance::instanceConfigFolder() const
{
return PathCombine(minecraftRoot(), "config");
}
+
+QString OneSixInstance::jarModsDir() const
+{
+ return PathCombine(instanceRoot(), "jarmods");
+}
+
+QString OneSixInstance::libDir() const
+{
+ return PathCombine(minecraftRoot(), "lib");
+}
+
+QStringList OneSixInstance::extraArguments() const
+{
+ auto list = BaseInstance::extraArguments();
+ auto version = getFullVersion();
+ if (!version)
+ return list;
+ if (version->hasJarMods())
+ {
+ list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true",
+ "-Dfml.ignorePatchDiscrepancies=true"});
+ }
+ return list;
+}
diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h
index 13a392c2..75caef1f 100644
--- a/logic/OneSixInstance.h
+++ b/logic/OneSixInstance.h
@@ -17,10 +17,11 @@
#include "BaseInstance.h"
-#include "VersionFinal.h"
-#include "ModList.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/ModList.h"
+#include "gui/pages/BasePageProvider.h"
-class OneSixInstance : public BaseInstance
+class OneSixInstance : public BaseInstance, public BasePageProvider
{
Q_OBJECT
public:
@@ -30,13 +31,25 @@ public:
virtual void init() override;
+ ////// Edit Instance Dialog stuff //////
+ virtual QList<BasePage *> getPages();
+ virtual QString dialogTitle();
+
////// Mod Lists //////
std::shared_ptr<ModList> loaderModList();
- std::shared_ptr<ModList> resourcePackList();
-
- ////// Directories //////
+ std::shared_ptr<ModList> coreModList();
+ std::shared_ptr<ModList> resourcePackList() override;
+ std::shared_ptr<ModList> texturePackList() override;
+
+ virtual QSet<QString> traits();
+
+ ////// Directories and files //////
+ QString jarModsDir() const;
QString resourcePacksDir() const;
+ QString texturePacksDir() const;
QString loaderModsDir() const;
+ QString coreModsDir() const;
+ QString libDir() const;
virtual QString instanceConfigFolder() const override;
virtual std::shared_ptr<Task> doUpdate() override;
@@ -52,42 +65,43 @@ public:
virtual bool shouldUpdate() const override;
virtual void setShouldUpdate(bool val) override;
- virtual QDialog *createModEditDialog(QWidget *parent) override;
-
/**
- * reload the full version json files. return true on success!
+ * reload the full version json files.
*
* throws various exceptions :3
*/
void reloadVersion();
+
/// clears all version information in preparation for an update
void clearVersion();
+
/// get the current full version info
- std::shared_ptr<VersionFinal> getFullVersion() const;
- /// gets the current version info, but only for version.json
- std::shared_ptr<VersionFinal> getVanillaVersion() const;
+ std::shared_ptr<InstanceVersion> getFullVersion() const;
+
/// is the current version original, or custom?
virtual bool versionIsCustom() override;
+
/// does this instance have an FTB pack patch inside?
bool versionIsFTBPack();
virtual QString defaultBaseJar() const override;
virtual QString defaultCustomBaseJar() const override;
- virtual bool menuActionEnabled(QString action_name) const override;
virtual QString getStatusbarDescription() override;
+ virtual QDir jarmodsPath() const;
virtual QDir librariesPath() const;
virtual QDir versionsPath() const;
virtual QStringList externalPatches() const;
virtual bool providesVersionFile() const;
bool reload() override;
-
+ virtual QStringList extraArguments() const override;
+
signals:
void versionReloaded();
private:
QStringList processMinecraftArgs(AuthSessionPtr account);
- QDir reconstructAssets(std::shared_ptr<VersionFinal> version);
+ QDir reconstructAssets(std::shared_ptr<InstanceVersion> version);
};
diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h
index c70de07c..3c4ef324 100644
--- a/logic/OneSixInstance_p.h
+++ b/logic/OneSixInstance_p.h
@@ -15,16 +15,19 @@
#pragma once
-#include "BaseInstance_p.h"
-#include "VersionFinal.h"
-#include "ModList.h"
+#include "logic/BaseInstance_p.h"
+
+class ModList;
+class InstanceVersion;
class OneSixInstancePrivate : public BaseInstancePrivate
{
public:
virtual ~OneSixInstancePrivate() {};
- std::shared_ptr<VersionFinal> version;
- std::shared_ptr<VersionFinal> vanillaVersion;
+ std::shared_ptr<InstanceVersion> version;
+ std::shared_ptr<ModList> jar_mod_list;
std::shared_ptr<ModList> loader_mod_list;
+ std::shared_ptr<ModList> core_mod_list;
std::shared_ptr<ModList> resource_pack_list;
+ std::shared_ptr<ModList> texture_pack_list;
};
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index d083c2ba..abc5f9c1 100644
--- a/logic/OneSixUpdate.cpp
+++ b/logic/OneSixUpdate.cpp
@@ -22,28 +22,24 @@
#include <QFileInfo>
#include <QTextStream>
#include <QDataStream>
-
-#include "BaseInstance.h"
-#include "lists/MinecraftVersionList.h"
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
-#include "OneSixInstance.h"
-#include "net/ForgeMirrors.h"
-#include "net/URLConstants.h"
-#include "assets/AssetsUtils.h"
-
-#include "pathutils.h"
+#include <pathutils.h>
#include <JlCompress.h>
-OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent)
- : Task(parent), m_inst(inst)
+#include "logic/BaseInstance.h"
+#include "logic/minecraft/MinecraftVersionList.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/OneSixInstance.h"
+#include "logic/forge/ForgeMirrors.h"
+#include "logic/net/URLConstants.h"
+#include "logic/assets/AssetsUtils.h"
+
+OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
{
}
void OneSixUpdate::executeTask()
{
- QString intendedVersion = m_inst->intendedVersionId();
-
// Make directories
QDir mcDir(m_inst->minecraftRoot());
if (!mcDir.exists() && !mcDir.mkpath("."))
@@ -52,102 +48,44 @@ void OneSixUpdate::executeTask()
return;
}
- if (m_inst->shouldUpdate())
+ // Get a pointer to the version object that corresponds to the instance's version.
+ targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
+ MMC->minecraftlist()->findVersion(m_inst->intendedVersionId()));
+ if (targetVersion == nullptr)
{
- // Get a pointer to the version object that corresponds to the instance's version.
- targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
- MMC->minecraftlist()->findVersion(intendedVersion));
- if (targetVersion == nullptr)
- {
- // don't do anything if it was invalid
- emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
- return;
- }
- versionFileStart();
+ // don't do anything if it was invalid
+ emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
+ return;
}
- else
+ if (m_inst->providesVersionFile() || !targetVersion->needsUpdate())
{
jarlibStart();
+ return;
}
-}
-
-void OneSixUpdate::versionFileStart()
-{
- if (m_inst->providesVersionFile())
+ versionUpdateTask = MMC->minecraftlist()->createUpdateTask(m_inst->intendedVersionId());
+ if (!versionUpdateTask)
{
jarlibStart();
return;
}
- QLOG_INFO() << m_inst->name() << ": getting version file.";
- setStatus(tr("Getting the version files from Mojang..."));
-
- QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS +
- targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json";
- auto job = new NetJob("Version index");
- job->addNetAction(ByteArrayDownload::make(QUrl(urlstr)));
- specificVersionDownloadJob.reset(job);
- connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished()));
- connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed()));
- connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ connect(versionUpdateTask.get(), SIGNAL(succeeded()), SLOT(jarlibStart()));
+ connect(versionUpdateTask.get(), SIGNAL(failed(QString)), SLOT(versionUpdateFailed(QString)));
+ connect(versionUpdateTask.get(), SIGNAL(progress(qint64, qint64)),
SIGNAL(progress(qint64, qint64)));
- specificVersionDownloadJob->start();
-}
-
-void OneSixUpdate::versionFileFinished()
-{
- NetActionPtr DlJob = specificVersionDownloadJob->first();
-
- QString version_id = targetVersion->descriptor();
- QString inst_dir = m_inst->instanceRoot();
- // save the version file in $instanceId/version.json
- {
- QString version1 = PathCombine(inst_dir, "/version.json");
- ensureFilePathExists(version1);
- // FIXME: detect errors here, download to a temp file, swap
- QSaveFile vfile1(version1);
- if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
- {
- emitFailed(tr("Can't open %1 for writing.").arg(version1));
- return;
- }
- auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data;
- qint64 actual = 0;
- if ((actual = vfile1.write(data)) != data.size())
- {
- emitFailed(tr("Failed to write into %1. Written %2 out of %3.").arg(version1).arg(actual).arg(data.size()));
- return;
- }
- if (!vfile1.commit())
- {
- emitFailed(tr("Can't commit changes to %1").arg(version1));
- return;
- }
- }
-
- // the version is downloaded safely. update is 'done' at this point
- m_inst->setShouldUpdate(false);
-
- // delete any custom version inside the instance (it's no longer relevant, we did an update)
- QString custom = PathCombine(inst_dir, "/custom.json");
- QFile finfo(custom);
- if (finfo.exists())
- {
- finfo.remove();
- }
- // NOTE: Version is reloaded in jarlibStart
- jarlibStart();
+ setStatus(tr("Getting the version files from Mojang..."));
+ versionUpdateTask->start();
}
-void OneSixUpdate::versionFileFailed()
+void OneSixUpdate::versionUpdateFailed(QString reason)
{
- emitFailed(tr("Failed to download the version description. Try again."));
+ emitFailed(reason);
}
void OneSixUpdate::assetIndexStart()
{
setStatus(tr("Updating assets index..."));
OneSixInstance *inst = (OneSixInstance *)m_inst;
- std::shared_ptr<VersionFinal> version = inst->getFullVersion();
+ std::shared_ptr<InstanceVersion> version = inst->getFullVersion();
QString assetName = version->assets;
QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json";
QString localPath = assetName + ".json";
@@ -171,7 +109,7 @@ void OneSixUpdate::assetIndexFinished()
AssetsIndex index;
OneSixInstance *inst = (OneSixInstance *)m_inst;
- std::shared_ptr<VersionFinal> version = inst->getFullVersion();
+ std::shared_ptr<InstanceVersion> version = inst->getFullVersion();
QString assetName = version->assets;
QString asset_fname = "assets/indexes/" + assetName + ".json";
@@ -235,19 +173,19 @@ void OneSixUpdate::jarlibStart()
{
inst->reloadVersion();
}
- catch(MMCError & e)
+ catch (MMCError &e)
{
emitFailed(e.cause());
return;
}
- catch(...)
+ catch (...)
{
emitFailed(tr("Failed to load the version description file for reasons unknown."));
return;
}
// Build a list of URLs that will need to be downloaded.
- std::shared_ptr<VersionFinal> version = inst->getFullVersion();
+ std::shared_ptr<InstanceVersion> version = inst->getFullVersion();
// minecraft.jar for this version
{
QString version_id = version->id;
@@ -259,7 +197,7 @@ void OneSixUpdate::jarlibStart()
auto metacache = MMC->metacache();
auto entry = metacache->resolveEntry("versions", localPath);
job->addNetAction(CacheDownload::make(QUrl(urlstr), entry));
-
+ jarHashOnEntry = entry->md5sum;
jarlibDownloadJob.reset(job);
}
@@ -274,7 +212,7 @@ void OneSixUpdate::jarlibStart()
{
if (lib->hint() == "local")
{
- if(!lib->filesExist(m_inst->librariesPath()))
+ if (!lib->filesExist(m_inst->librariesPath()))
brokenLocalLibs.append(lib);
continue;
}
@@ -311,16 +249,19 @@ void OneSixUpdate::jarlibStart()
f(raw_storage, raw_dl);
}
}
- if(!brokenLocalLibs.empty())
+ if (!brokenLocalLibs.empty())
{
jarlibDownloadJob.reset();
QStringList failed;
- for(auto brokenLib : brokenLocalLibs)
+ for (auto brokenLib : brokenLocalLibs)
{
failed.append(brokenLib->files());
}
QString failed_all = failed.join("\n");
- emitFailed(tr("Some libraries marked as 'local' are missing their jar files:\n%1\n\nYou'll have to correct this problem manually. If this is an externally tracked instance, make sure to run it at least once outside of MultiMC.").arg(failed_all));
+ emitFailed(tr("Some libraries marked as 'local' are missing their jar "
+ "files:\n%1\n\nYou'll have to correct this problem manually. If this is "
+ "an externally tracked instance, make sure to run it at least once "
+ "outside of MultiMC.").arg(failed_all));
return;
}
// TODO: think about how to propagate this from the original json file... or IF AT ALL
@@ -341,12 +282,222 @@ void OneSixUpdate::jarlibStart()
void OneSixUpdate::jarlibFinished()
{
- assetIndexStart();
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+ std::shared_ptr<InstanceVersion> version = inst->getFullVersion();
+
+ // create stripped jar, if needed
+ if (version->hasJarMods())
+ {
+ // FIXME: good candidate for moving elsewhere (jar location resolving/version caching).
+ QString version_id = version->id;
+ QString localPath = version_id + "/" + version_id + ".jar";
+ QString strippedPath = version_id + "/" + version_id + "-stripped.jar";
+ auto metacache = MMC->metacache();
+ auto entry = metacache->resolveEntry("versions", localPath);
+ auto entryStripped = metacache->resolveEntry("versions", strippedPath);
+
+ QString fullJarPath = entry->getFullPath();
+ QString fullStrippedJarPath = entryStripped->getFullPath();
+ QFileInfo finfo(fullStrippedJarPath);
+ if (entry->md5sum != jarHashOnEntry || !finfo.exists())
+ {
+ stripJar(fullJarPath, fullStrippedJarPath);
+ }
+ }
+ if (version->traits.contains("legacyFML"))
+ {
+ fmllibsStart();
+ }
+ else
+ {
+ assetIndexStart();
+ }
}
void OneSixUpdate::jarlibFailed()
{
QStringList failed = jarlibDownloadJob->getFailedFiles();
QString failed_all = failed.join("\n");
- emitFailed(tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all));
+ emitFailed(
+ tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all));
+}
+
+void OneSixUpdate::stripJar(QString origPath, QString newPath)
+{
+ QFileInfo runnableJar(newPath);
+ if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
+ {
+ emitFailed("Failed to delete old minecraft.jar");
+ return;
+ }
+
+ // TaskStep(); // STEP 1
+ setStatus(tr("Creating stripped jar: Opening minecraft.jar ..."));
+
+ QuaZip zipOut(runnableJar.filePath());
+ if (!zipOut.open(QuaZip::mdCreate))
+ {
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to open the minecraft.jar for stripping");
+ return;
+ }
+ // Modify the jar
+ setStatus(tr("Creating stripped jar: Adding files..."));
+ if (!MergeZipFiles(&zipOut, origPath))
+ {
+ zipOut.close();
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to add " + origPath + " to the jar.");
+ return;
+ }
+}
+
+bool OneSixUpdate::MergeZipFiles(QuaZip *into, QString from)
+{
+ setStatus(tr("Installing mods: Adding ") + from + " ...");
+
+ QuaZip modZip(from);
+ modZip.open(QuaZip::mdUnzip);
+
+ QuaZipFile fileInsideMod(&modZip);
+ QuaZipFile zipOutFile(into);
+ for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
+ {
+ QString filename = modZip.getCurrentFileName();
+ if (filename.contains("META-INF"))
+ {
+ QLOG_INFO() << "Skipping META-INF " << filename << " from " << from;
+ continue;
+ }
+ QLOG_INFO() << "Adding file " << filename << " from " << from;
+
+ if (!fileInsideMod.open(QIODevice::ReadOnly))
+ {
+ QLOG_ERROR() << "Failed to open " << filename << " from " << from;
+ return false;
+ }
+ /*
+ QuaZipFileInfo old_info;
+ fileInsideMod.getFileInfo(&old_info);
+ */
+ QuaZipNewInfo info_out(fileInsideMod.getActualFileName());
+ /*
+ info_out.externalAttr = old_info.externalAttr;
+ */
+ if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
+ {
+ QLOG_ERROR() << "Failed to open " << filename << " in the jar";
+ fileInsideMod.close();
+ return false;
+ }
+ if (!JlCompress::copyData(fileInsideMod, zipOutFile))
+ {
+ zipOutFile.close();
+ fileInsideMod.close();
+ QLOG_ERROR() << "Failed to copy data of " << filename << " into the jar";
+ return false;
+ }
+ zipOutFile.close();
+ fileInsideMod.close();
+ }
+ return true;
+}
+
+void OneSixUpdate::fmllibsStart()
+{
+ // Get the mod list
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+ std::shared_ptr<InstanceVersion> fullversion = inst->getFullVersion();
+ bool forge_present = false;
+
+ QString version = inst->intendedVersionId();
+ auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
+ if (!fmlLibsMapping.contains(version))
+ {
+ assetIndexStart();
+ return;
+ }
+
+ auto &libList = fmlLibsMapping[version];
+
+ // determine if we need some libs for FML or forge
+ setStatus(tr("Checking for FML libraries..."));
+ forge_present = (fullversion->versionPatch("net.minecraftforge") != nullptr);
+ // we don't...
+ if (!forge_present)
+ {
+ assetIndexStart();
+ return;
+ }
+
+ // now check the lib folder inside the instance for files.
+ for (auto &lib : libList)
+ {
+ QFileInfo libInfo(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())
+ {
+ assetIndexStart();
+ return;
+ }
+
+ // download missing libs to our place
+ setStatus(tr("Dowloading FML libraries..."));
+ auto dljob = new NetJob("FML libraries");
+ auto metacache = MMC->metacache();
+ for (auto &lib : fmlLibsToProcess)
+ {
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename
+ : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename;
+ dljob->addNetAction(CacheDownload::make(QUrl(urlString), entry));
+ }
+
+ connect(dljob, SIGNAL(succeeded()), SLOT(fmllibsFinished()));
+ connect(dljob, SIGNAL(failed()), SLOT(fmllibsFailed()));
+ connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
+ legacyDownloadJob.reset(dljob);
+ legacyDownloadJob->start();
+}
+
+void OneSixUpdate::fmllibsFinished()
+{
+ legacyDownloadJob.reset();
+ if (!fmlLibsToProcess.isEmpty())
+ {
+ setStatus(tr("Copying FML libraries into the instance..."));
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+ auto metacache = MMC->metacache();
+ int index = 0;
+ for (auto &lib : fmlLibsToProcess)
+ {
+ progress(index, fmlLibsToProcess.size());
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ auto path = PathCombine(inst->libDir(), lib.filename);
+ if (!ensureFilePathExists(path))
+ {
+ emitFailed(tr("Failed creating FML library folder inside the instance."));
+ return;
+ }
+ if (!QFile::copy(entry->getFullPath(), PathCombine(inst->libDir(), lib.filename)))
+ {
+ emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename));
+ return;
+ }
+ index++;
+ }
+ progress(index, fmlLibsToProcess.size());
+ }
+ assetIndexStart();
+}
+
+void OneSixUpdate::fmllibsFailed()
+{
+ emitFailed("Game update failed: it was impossible to fetch the required FML libraries.");
+ return;
}
diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h
index eac882b5..139143db 100644
--- a/logic/OneSixUpdate.h
+++ b/logic/OneSixUpdate.h
@@ -21,6 +21,8 @@
#include "logic/net/NetJob.h"
#include "logic/tasks/Task.h"
+#include "logic/VersionFilterData.h"
+#include <quazip.h>
class MinecraftVersion;
class OneSixInstance;
@@ -34,14 +36,16 @@ public:
private
slots:
- void versionFileStart();
- void versionFileFinished();
- void versionFileFailed();
+ void versionUpdateFailed(QString reason);
void jarlibStart();
void jarlibFinished();
void jarlibFailed();
+ void fmllibsStart();
+ void fmllibsFinished();
+ void fmllibsFailed();
+
void assetIndexStart();
void assetIndexFinished();
void assetIndexFailed();
@@ -49,11 +53,18 @@ slots:
void assetsFinished();
void assetsFailed();
+ void stripJar(QString origPath, QString newPath);
+ bool MergeZipFiles(QuaZip *into, QString from);
private:
- NetJobPtr specificVersionDownloadJob;
NetJobPtr jarlibDownloadJob;
+ NetJobPtr legacyDownloadJob;
- // target version, determined during this task
+ /// target version, determined during this task
std::shared_ptr<MinecraftVersion> targetVersion;
+ /// the task that is spawned for version updates
+ std::shared_ptr<Task> versionUpdateTask;
+
OneSixInstance *m_inst = nullptr;
+ QString jarHashOnEntry;
+ QList<FMLlib> fmlLibsToProcess;
};
diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp
deleted file mode 100644
index be3a7da4..00000000
--- a/logic/OneSixVersionBuilder.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-/* Copyright 2013 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 "OneSixVersionBuilder.h"
-
-#include <QList>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QFile>
-#include <QFileInfo>
-#include <QMessageBox>
-#include <QObject>
-#include <QDir>
-#include <QDebug>
-
-#include "VersionFinal.h"
-#include "OneSixInstance.h"
-#include "OneSixRule.h"
-#include "VersionFile.h"
-#include "MMCJson.h"
-#include "modutils.h"
-#include "logger/QsLog.h"
-
-OneSixVersionBuilder::OneSixVersionBuilder()
-{
-}
-
-void OneSixVersionBuilder::build(VersionFinal *version, OneSixInstance *instance,
- const bool onlyVanilla, const QStringList &external)
-{
- OneSixVersionBuilder builder;
- builder.m_version = version;
- builder.m_instance = instance;
- builder.buildInternal(onlyVanilla, external);
-}
-
-void OneSixVersionBuilder::readJsonAndApplyToVersion(VersionFinal *version,
- const QJsonObject &obj)
-{
- OneSixVersionBuilder builder;
- builder.m_version = version;
- builder.m_instance = 0;
- builder.readJsonAndApply(obj);
-}
-
-void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringList &external)
-{
- m_version->versionFiles.clear();
-
- QDir root(m_instance->instanceRoot());
- QDir patches(root.absoluteFilePath("patches/"));
-
- // if we do external files, do just those.
- if (!external.isEmpty())
- {
- int externalOrder = -1;
- for (auto fileName : external)
- {
- QLOG_INFO() << "Reading" << fileName;
- auto file =
- parseJsonFile(QFileInfo(fileName), false, fileName.endsWith("pack.json"));
- file->name = QFileInfo(fileName).fileName();
- file->fileId = "org.multimc.external." + file->name;
- file->order = (externalOrder += 1);
- file->version = QString();
- file->mcVersion = QString();
- m_version->versionFiles.append(file);
- }
- }
- // else, if there's custom json, we just do that.
- else if (QFile::exists(root.absoluteFilePath("custom.json")))
- {
- QLOG_INFO() << "Reading custom.json";
- auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("custom.json")), false);
- file->name = "custom.json";
- file->filename = "custom.json";
- file->fileId = "org.multimc.custom.json";
- file->order = -1;
- file->version = QString();
- m_version->versionFiles.append(file);
- // QObject::tr("The version descriptors of this instance are not compatible with the
- // current version of MultiMC"));
- // QObject::tr("Error while applying %1. Please check MultiMC-0.log for more info.")
- }
- // version.json -> patches/*.json -> user.json
- else
- do
- {
- // version.json
- QLOG_INFO() << "Reading version.json";
- auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("version.json")), false);
- file->name = "Minecraft";
- file->fileId = "org.multimc.version.json";
- file->order = -1;
- file->version = m_instance->intendedVersionId();
- file->mcVersion = m_instance->intendedVersionId();
- m_version->versionFiles.append(file);
- // QObject::tr("Error while applying %1. Please check MultiMC-0.log for more
- // info.").arg(root.absoluteFilePath("version.json")));
-
- if (onlyVanilla)
- break;
-
- // patches/
- // load all, put into map for ordering, apply in the right order
-
- QMap<int, QPair<QString, VersionFilePtr>> files;
- for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
- {
- QLOG_INFO() << "Reading" << info.fileName();
- auto file = parseJsonFile(info, true);
- if (files.contains(file->order))
- {
- throw VersionBuildError(QObject::tr("%1 has the same order as %2").arg(
- file->fileId, files[file->order].second->fileId));
- }
- files.insert(file->order, qMakePair(info.fileName(), file));
- }
- for (auto order : files.keys())
- {
- auto &filePair = files[order];
- m_version->versionFiles.append(filePair.second);
- }
- } while (0);
-
- // some final touches
- m_version->finalize();
-}
-
-
-
-void OneSixVersionBuilder::readJsonAndApply(const QJsonObject &obj)
-{
- m_version->clear();
-
- auto file = VersionFile::fromJson(QJsonDocument(obj), QString(), false);
- // QObject::tr("Error while reading. Please check MultiMC-0.log for more info."));
-
- file->applyTo(m_version);
- m_version->versionFiles.append(file);
- // QObject::tr("Error while applying. Please check MultiMC-0.log for more info."));
- // QObject::tr("The version descriptors of this instance are not compatible with the current
- // version of MultiMC"));
-}
-
-VersionFilePtr OneSixVersionBuilder::parseJsonFile(const QFileInfo &fileInfo,
- const bool requireOrder, bool isFTB)
-{
- QFile file(fileInfo.absoluteFilePath());
- if (!file.open(QFile::ReadOnly))
- {
- throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
- .arg(fileInfo.fileName(), file.errorString()));
- }
- QJsonParseError error;
- QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
- if (error.error != QJsonParseError::NoError)
- {
- throw JSONValidationError(QObject::tr("Unable to process the version file %1: %2 at %3.")
- .arg(fileInfo.fileName(), error.errorString())
- .arg(error.offset));
- }
- return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB);
- // QObject::tr("Error while reading %1. Please check MultiMC-0.log for more
- // info.").arg(file.fileName());
-}
-
-QMap<QString, int> OneSixVersionBuilder::readOverrideOrders(OneSixInstance *instance)
-{
- QMap<QString, int> out;
-
- // make sure the order file exists
- if (!QDir(instance->instanceRoot()).exists("order.json"))
- return out;
-
- // and it can be opened
- QFile orderFile(instance->instanceRoot() + "/order.json");
- if (!orderFile.open(QFile::ReadOnly))
- {
- QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
- << " for reading:" << orderFile.errorString();
- QLOG_WARN() << "Ignoring overriden order";
- return out;
- }
-
- // and it's valid JSON
- QJsonParseError error;
- QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
- if (error.error != QJsonParseError::NoError)
- {
- QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString();
- QLOG_WARN() << "Ignoring overriden order";
- return out;
- }
-
- // and then read it and process it if all above is true.
- try
- {
- auto obj = MMCJson::ensureObject(doc);
- for (auto it = obj.begin(); it != obj.end(); ++it)
- {
- if (it.key().startsWith("org.multimc."))
- {
- continue;
- }
- out.insert(it.key(), MMCJson::ensureInteger(it.value()));
- }
- }
- catch (JSONValidationError &err)
- {
- QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
- QLOG_WARN() << "Ignoring overriden order";
- return out;
- }
- return out;
-}
-
-bool OneSixVersionBuilder::writeOverrideOrders(const QMap<QString, int> &order,
- OneSixInstance *instance)
-{
- QJsonObject obj;
- for (auto it = order.cbegin(); it != order.cend(); ++it)
- {
- if (it.key().startsWith("org.multimc."))
- {
- continue;
- }
- obj.insert(it.key(), it.value());
- }
- QFile orderFile(instance->instanceRoot() + "/order.json");
- if (!orderFile.open(QFile::WriteOnly))
- {
- QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
- << "for writing:" << orderFile.errorString();
- return false;
- }
- orderFile.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
- return true;
-}
diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h
deleted file mode 100644
index 7a799e5b..00000000
--- a/logic/OneSixVersionBuilder.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright 2013 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 "VersionFile.h"
-
-class VersionFinal;
-class OneSixInstance;
-class QJsonObject;
-class QFileInfo;
-
-class OneSixVersionBuilder
-{
- OneSixVersionBuilder();
-public:
- static void build(VersionFinal *version, OneSixInstance *instance, const bool onlyVanilla,
- const QStringList &external);
- static void readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj);
-
- static QMap<QString, int> readOverrideOrders(OneSixInstance *instance);
- static bool writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance);
-
-private:
- VersionFinal *m_version;
- OneSixInstance *m_instance;
-
- void buildInternal(const bool onlyVanilla, const QStringList &external);
- void readJsonAndApply(const QJsonObject &obj);
-
- VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
- bool isFTB = false);
-};
diff --git a/logic/URNResolver.cpp b/logic/URNResolver.cpp
new file mode 100644
index 00000000..b6bdcf41
--- /dev/null
+++ b/logic/URNResolver.cpp
@@ -0,0 +1,98 @@
+#include "URNResolver.h"
+#include <logger/QsLog.h>
+#include "MultiMC.h"
+#include "logic/forge/ForgeVersionList.h"
+#include "logic/forge/ForgeVersion.h"
+
+QString unescapeNSS(QString RawNSS)
+{
+ QString NSS;
+ NSS.reserve(RawNSS.size());
+ enum
+ {
+ Normal,
+ FirstHex,
+ SecondHex
+ } ParseState = Normal;
+
+ QByteArray translator(" ");
+
+ for (auto ch : RawNSS)
+ {
+ if(ParseState == Normal)
+ {
+ if(ch == '%')
+ {
+ ParseState = FirstHex;
+ continue;
+ }
+ else
+ {
+ NSS.append(ch);
+ }
+ }
+ if(ParseState == FirstHex)
+ {
+ translator[0] = ch.toLower().unicode();
+ ParseState = SecondHex;
+ }
+ else if(ParseState == SecondHex)
+ {
+ translator[1] = ch.toLower().unicode();
+ auto result = QByteArray::fromHex(translator);
+ if (result[0] == '\0')
+ return NSS;
+ NSS.append(result);
+ ParseState = Normal;
+ }
+ }
+ return NSS;
+}
+
+bool URNResolver::parse(const QString &URN, QString &NID, QString &NSS)
+{
+ QRegExp URNPattern(
+ "^urn:([a-z0-9][a-z0-9-]{0,31}):(([a-z0-9()+,\\-.:=@;$_!*']|%[0-9a-f]{2})+).*",
+ Qt::CaseInsensitive);
+ if (URNPattern.indexIn(URN) == -1)
+ return false;
+ auto captures = URNPattern.capturedTexts();
+ QString RawNID = captures[1];
+ QString RawNSS = captures[2];
+
+ NID = RawNID.toLower();
+ NSS = unescapeNSS(RawNSS);
+ return true;
+}
+
+URNResolver::URNResolver()
+{
+}
+
+QVariant URNResolver::resolve(QString URN)
+{
+ QString NID, NSS;
+ parse(URN, NID, NSS);
+
+ if(NID != "x-mmc")
+ return QVariant();
+ auto parts = NSS.split(":");
+ if(parts.size() < 1)
+ return QVariant();
+ unsigned int version = parts[0].toUInt();
+ switch(version)
+ {
+ case 1:
+ return resolveV1(parts.mid(1));
+ default:
+ return QVariant();
+ }
+}
+
+/**
+ * TODO: implement.
+ */
+QVariant URNResolver::resolveV1(QStringList parts)
+{
+ return QVariant();
+}
diff --git a/logic/URNResolver.h b/logic/URNResolver.h
new file mode 100644
index 00000000..dfc7f5eb
--- /dev/null
+++ b/logic/URNResolver.h
@@ -0,0 +1,18 @@
+#pragma once
+#include <QString>
+#include <QMap>
+#include <memory>
+#include <QVariant>
+
+class URNResolver;
+typedef std::shared_ptr<URNResolver> URNResolverPtr;
+
+class URNResolver
+{
+public:
+ URNResolver();
+ QVariant resolve (QString URN);
+ static bool parse (const QString &URN, QString &NID, QString &NSS);
+private:
+ QVariant resolveV1 (QStringList parts);
+};
diff --git a/logic/VersionFile.h b/logic/VersionFile.h
deleted file mode 100644
index 169a2066..00000000
--- a/logic/VersionFile.h
+++ /dev/null
@@ -1,127 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QStringList>
-#include <memory>
-#include "logic/OpSys.h"
-#include "logic/OneSixRule.h"
-#include "MMCError.h"
-
-class VersionFinal;
-
-class VersionBuildError : public MMCError
-{
-public:
- VersionBuildError(QString cause) : MMCError(cause) {};
- virtual ~VersionBuildError() noexcept {}
-};
-
-/**
- * the base version file was meant for a newer version of the vanilla launcher than we support
- */
-class LauncherVersionError : public VersionBuildError
-{
-public:
- LauncherVersionError(int actual, int supported)
- : VersionBuildError(QObject::tr(
- "The base version file of this instance was meant for a newer (%1) "
- "version of the vanilla launcher than this version of MultiMC supports (%2).")
- .arg(actual)
- .arg(supported)) {};
- virtual ~LauncherVersionError() noexcept {}
-};
-
-/**
- * some patch was intended for a different version of minecraft
- */
-class MinecraftVersionMismatch : public VersionBuildError
-{
-public:
- MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion)
- : VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft "
- "(%2) than that of the instance (%3).")
- .arg(fileId)
- .arg(mcVersion)
- .arg(parentMcVersion)) {};
- virtual ~MinecraftVersionMismatch() noexcept {}
-};
-
-struct RawLibrary;
-typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
-struct RawLibrary
-{
- QString name;
- QString url;
- QString hint;
- QString absoluteUrl;
- bool applyExcludes = false;
- QStringList excludes;
- bool applyNatives = false;
- QList<QPair<OpSys, QString>> natives;
- bool applyRules = false;
- QList<std::shared_ptr<Rule>> rules;
-
- // user for '+' libraries
- enum InsertType
- {
- Apply,
- Append,
- Prepend,
- Replace
- };
- InsertType insertType = Append;
- QString insertData;
- enum DependType
- {
- Soft,
- Hard
- };
- DependType dependType = Soft;
-
- static RawLibraryPtr fromJson(const QJsonObject &libObj, const QString &filename);
-};
-
-struct VersionFile;
-typedef std::shared_ptr<VersionFile> VersionFilePtr;
-struct VersionFile
-{
-public: /* methods */
- static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename,
- const bool requireOrder, const bool isFTB = false);
-
- static OneSixLibraryPtr createLibrary(RawLibraryPtr lib);
- int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle);
- void applyTo(VersionFinal *version);
-
-public: /* data */
- int order = 0;
- QString name;
- QString fileId;
- QString version;
- // TODO use the mcVersion to determine if a version file should be removed on update
- QString mcVersion;
- QString filename;
- // TODO requirements
- // QMap<QString, QString> requirements;
- QString id;
- QString mainClass;
- QString overwriteMinecraftArguments;
- QString addMinecraftArguments;
- QString removeMinecraftArguments;
- QString processArguments;
- QString type;
- QString releaseTime;
- QString time;
- QString assets;
- int minimumLauncherVersion = -1;
-
- bool shouldOverwriteTweakers = false;
- QStringList overwriteTweakers;
- QStringList addTweakers;
- QStringList removeTweakers;
-
- bool shouldOverwriteLibs = false;
- QList<RawLibraryPtr> overwriteLibs;
- QList<RawLibraryPtr> addLibs;
- QList<QString> removeLibs;
-};
diff --git a/logic/VersionFilterData.cpp b/logic/VersionFilterData.cpp
new file mode 100644
index 00000000..8b521266
--- /dev/null
+++ b/logic/VersionFilterData.cpp
@@ -0,0 +1,68 @@
+#include "VersionFilterData.h"
+#include "minecraft/ParseUtils.h"
+
+extern VersionFilterData g_VersionFilterData = VersionFilterData();
+
+VersionFilterData::VersionFilterData()
+{
+ // 1.3.*
+ auto libs13 =
+ QList<FMLlib>{{"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
+ {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
+ {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}};
+
+ fmlLibsMapping["1.3.2"] = libs13;
+
+ // 1.4.*
+ auto libs14 = QList<FMLlib>{
+ {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
+ {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
+ {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false},
+ {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb", false}};
+
+ fmlLibsMapping["1.4"] = libs14;
+ fmlLibsMapping["1.4.1"] = libs14;
+ fmlLibsMapping["1.4.2"] = libs14;
+ fmlLibsMapping["1.4.3"] = libs14;
+ fmlLibsMapping["1.4.4"] = libs14;
+ fmlLibsMapping["1.4.5"] = libs14;
+ fmlLibsMapping["1.4.6"] = libs14;
+ fmlLibsMapping["1.4.7"] = libs14;
+
+ // 1.5
+ fmlLibsMapping["1.5"] = QList<FMLlib>{
+ {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
+ {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
+ {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
+ {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
+ {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8", false},
+ {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
+
+ // 1.5.1
+ fmlLibsMapping["1.5.1"] = QList<FMLlib>{
+ {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
+ {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
+ {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
+ {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
+ {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6", false},
+ {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
+
+ // 1.5.2
+ fmlLibsMapping["1.5.2"] = QList<FMLlib>{
+ {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
+ {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
+ {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
+ {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
+ {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9", false},
+ {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
+
+ // don't use installers for those.
+ forgeInstallerBlacklist = QSet<QString>({"1.5.2"});
+ // these won't show up in version lists because they are extremely bad and dangerous
+ legacyBlacklist = QSet<QString>({"rd-160052"});
+ /*
+ * nothing older than this will be accepted from Mojang servers
+ * (these versions need to be tested by us first)
+ */
+ legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00");
+}
diff --git a/logic/VersionFilterData.h b/logic/VersionFilterData.h
new file mode 100644
index 00000000..e010adc7
--- /dev/null
+++ b/logic/VersionFilterData.h
@@ -0,0 +1,26 @@
+#pragma once
+#include <QMap>
+#include <QString>
+#include <QSet>
+#include <QDateTime>
+
+struct FMLlib
+{
+ QString filename;
+ QString checksum;
+ bool ours;
+};
+
+struct VersionFilterData
+{
+ VersionFilterData();
+ // mapping between minecraft versions and FML libraries required
+ QMap<QString, QList<FMLlib>> fmlLibsMapping;
+ // set of minecraft versions for which using forge installers is blacklisted
+ QSet<QString> forgeInstallerBlacklist;
+ // set of 'legacy' versions that will not show up in the version lists.
+ QSet<QString> legacyBlacklist;
+ // no new versions below this date will be accepted from Mojang servers
+ QDateTime legacyCutoffDate;
+};
+extern VersionFilterData g_VersionFilterData;
diff --git a/logic/VersionFinal.cpp b/logic/VersionFinal.cpp
deleted file mode 100644
index dedf2ce5..00000000
--- a/logic/VersionFinal.cpp
+++ /dev/null
@@ -1,377 +0,0 @@
-/* Copyright 2013 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 "VersionFinal.h"
-
-#include <QDebug>
-#include <QFile>
-#include <QDir>
-
-#include "OneSixVersionBuilder.h"
-#include "OneSixInstance.h"
-
-template <typename A, typename B> QMap<A, B> invert(const QMap<B, A> &in)
-{
- QMap<A, B> out;
- for (auto it = in.begin(); it != in.end(); ++it)
- {
- out.insert(it.value(), it.key());
- }
- return out;
-}
-
-VersionFinal::VersionFinal(OneSixInstance *instance, QObject *parent)
- : QAbstractListModel(parent), m_instance(instance)
-{
- clear();
-}
-
-void VersionFinal::reload(const bool onlyVanilla, const QStringList &external)
-{
- //FIXME: source of epic failure.
- beginResetModel();
- OneSixVersionBuilder::build(this, m_instance, onlyVanilla, external);
- reapply(true);
- endResetModel();
-}
-
-void VersionFinal::clear()
-{
- id.clear();
- time.clear();
- releaseTime.clear();
- type.clear();
- assets.clear();
- processArguments.clear();
- minecraftArguments.clear();
- minimumLauncherVersion = 0xDEADBEAF;
- mainClass.clear();
- libraries.clear();
- tweakers.clear();
-}
-
-bool VersionFinal::canRemove(const int index) const
-{
- if (index < versionFiles.size())
- {
- return versionFiles.at(index)->fileId != "org.multimc.version.json";
- }
- return false;
-}
-
-bool VersionFinal::remove(const int index)
-{
- if (canRemove(index) && QFile::remove(versionFiles.at(index)->filename))
- {
- beginResetModel();
- versionFiles.removeAt(index);
- reapply(true);
- endResetModel();
- return true;
- }
- return false;
-}
-
-bool VersionFinal::remove(const QString id)
-{
- int i = 0;
- for (auto file : versionFiles)
- {
- if (file->fileId == id)
- {
- return remove(i);
- }
- i++;
- }
- return false;
-}
-
-QString VersionFinal::versionFileId(const int index) const
-{
- if (index < 0 || index >= versionFiles.size())
- {
- return QString();
- }
- return versionFiles.at(index)->fileId;
-}
-
-VersionFilePtr VersionFinal::versionFile(const QString &id)
-{
- for (auto file : versionFiles)
- {
- if (file->fileId == id)
- {
- return file;
- }
- }
- return 0;
-}
-
-bool VersionFinal::hasFtbPack()
-{
- return versionFile("org.multimc.ftb.pack.json") != nullptr;
-}
-
-bool VersionFinal::removeFtbPack()
-{
- return remove("org.multimc.ftb.pack.json");
-}
-
-QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNormalLibs()
-{
- QList<std::shared_ptr<OneSixLibrary> > output;
- for (auto lib : libraries)
- {
- if (lib->isActive() && !lib->isNative())
- {
- output.append(lib);
- }
- }
- return output;
-}
-QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNativeLibs()
-{
- QList<std::shared_ptr<OneSixLibrary> > output;
- for (auto lib : libraries)
- {
- if (lib->isActive() && lib->isNative())
- {
- output.append(lib);
- }
- }
- return output;
-}
-
-std::shared_ptr<VersionFinal> VersionFinal::fromJson(const QJsonObject &obj)
-{
- std::shared_ptr<VersionFinal> version(new VersionFinal(0));
- try
- {
- OneSixVersionBuilder::readJsonAndApplyToVersion(version.get(), obj);
- }
- catch(MMCError & err)
- {
- return 0;
- }
- return version;
-}
-
-QVariant VersionFinal::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return QVariant();
-
- int row = index.row();
- int column = index.column();
-
- if (row < 0 || row >= versionFiles.size())
- return QVariant();
-
- if (role == Qt::DisplayRole)
- {
- switch (column)
- {
- case 0:
- return versionFiles.at(row)->name;
- case 1:
- return versionFiles.at(row)->version;
- default:
- return QVariant();
- }
- }
- return QVariant();
-}
-QVariant VersionFinal::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if (orientation == Qt::Horizontal)
- {
- if (role == Qt::DisplayRole)
- {
- switch (section)
- {
- case 0:
- return tr("Name");
- case 1:
- return tr("Version");
- default:
- return QVariant();
- }
- }
- }
- return QVariant();
-}
-Qt::ItemFlags VersionFinal::flags(const QModelIndex &index) const
-{
- if (!index.isValid())
- return Qt::NoItemFlags;
- return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
-}
-
-int VersionFinal::rowCount(const QModelIndex &parent) const
-{
- return versionFiles.size();
-}
-
-int VersionFinal::columnCount(const QModelIndex &parent) const
-{
- return 2;
-}
-
-bool VersionFinal::isCustom()
-{
- return QDir(m_instance->instanceRoot()).exists("custom.json");
-}
-bool VersionFinal::revertToBase()
-{
- return QDir(m_instance->instanceRoot()).remove("custom.json");
-}
-
-QMap<QString, int> VersionFinal::getExistingOrder() const
-{
-
- QMap<QString, int> order;
- // default
- {
- for (auto file : versionFiles)
- {
- order.insert(file->fileId, file->order);
- }
- }
- // overriden
- {
- QMap<QString, int> overridenOrder = OneSixVersionBuilder::readOverrideOrders(m_instance);
- for (auto id : order.keys())
- {
- if (overridenOrder.contains(id))
- {
- order[id] = overridenOrder[id];
- }
- }
- }
- return order;
-}
-
-void VersionFinal::move(const int index, const MoveDirection direction)
-{
- int theirIndex;
- if (direction == MoveUp)
- {
- theirIndex = index - 1;
- }
- else
- {
- theirIndex = index + 1;
- }
- if (theirIndex < 0 || theirIndex >= versionFiles.size())
- {
- return;
- }
- const QString ourId = versionFileId(index);
- const QString theirId = versionFileId(theirIndex);
- if (ourId.isNull() || ourId.startsWith("org.multimc.") ||
- theirId.isNull() || theirId.startsWith("org.multimc."))
- {
- return;
- }
-
- VersionFilePtr we = versionFiles[index];
- VersionFilePtr them = versionFiles[theirIndex];
- if (!we || !them)
- {
- return;
- }
- beginMoveRows(QModelIndex(), index, index, QModelIndex(), theirIndex);
- versionFiles.replace(theirIndex, we);
- versionFiles.replace(index, them);
- endMoveRows();
-
- auto order = getExistingOrder();
- order[ourId] = theirIndex;
- order[theirId] = index;
-
- if (!OneSixVersionBuilder::writeOverrideOrders(order, m_instance))
- {
- throw MMCError(tr("Couldn't save the new order"));
- }
- else
- {
- reapply();
- }
-}
-void VersionFinal::resetOrder()
-{
- QDir(m_instance->instanceRoot()).remove("order.json");
- reapply();
-}
-
-void VersionFinal::reapply(const bool alreadyReseting)
-{
- if (!alreadyReseting)
- {
- beginResetModel();
- }
-
- clear();
-
- auto existingOrders = getExistingOrder();
- QList<int> orders = existingOrders.values();
- std::sort(orders.begin(), orders.end());
- QList<VersionFilePtr> newVersionFiles;
- for (auto order : orders)
- {
- auto file = versionFile(existingOrders.key(order));
- newVersionFiles.append(file);
- file->applyTo(this);
- }
- versionFiles.swap(newVersionFiles);
- finalize();
- if (!alreadyReseting)
- {
- endResetModel();
- }
-}
-
-void VersionFinal::finalize()
-{
- // HACK: deny april fools. my head hurts enough already.
- QDate now = QDate::currentDate();
- bool isAprilFools = now.month() == 4 && now.day() == 1;
- if (assets.endsWith("_af") && !isAprilFools)
- {
- assets = assets.left(assets.length() - 3);
- }
- if (assets.isEmpty())
- {
- assets = "legacy";
- }
- if (minecraftArguments.isEmpty())
- {
- QString toCompare = processArguments.toLower();
- if (toCompare == "legacy")
- {
- minecraftArguments = " ${auth_player_name} ${auth_session}";
- }
- else if (toCompare == "username_session")
- {
- minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
- }
- else if (toCompare == "username_session_version")
- {
- minecraftArguments = "--username ${auth_player_name} "
- "--session ${auth_session} "
- "--version ${profile_name}";
- }
- }
-}
diff --git a/logic/ForgeInstaller.cpp b/logic/forge/ForgeInstaller.cpp
index 94b3f319..7f952408 100644
--- a/logic/ForgeInstaller.cpp
+++ b/logic/forge/ForgeInstaller.cpp
@@ -14,9 +14,15 @@
*/
#include "ForgeInstaller.h"
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
-#include "net/HttpMetaCache.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/net/HttpMetaCache.h"
+#include "logic/tasks/Task.h"
+#include "logic/OneSixInstance.h"
+#include "logic/forge/ForgeVersionList.h"
+#include "logic/VersionFilterData.h"
+#include "gui/dialogs/ProgressDialog.h"
+
#include <quazip.h>
#include <quazipfile.h>
#include <pathutils.h>
@@ -24,23 +30,17 @@
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include "MultiMC.h"
-#include "tasks/Task.h"
-#include "OneSixInstance.h"
-#include "lists/ForgeVersionList.h"
-#include "gui/dialogs/ProgressDialog.h"
-
#include <QJsonDocument>
#include <QJsonArray>
#include <QSaveFile>
#include <QCryptographicHash>
-ForgeInstaller::ForgeInstaller()
- : BaseInstaller()
+ForgeInstaller::ForgeInstaller() : BaseInstaller()
{
}
void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl)
{
- std::shared_ptr<VersionFinal> newVersion;
+ std::shared_ptr<InstanceVersion> newVersion;
m_universal_url = universalUrl;
QuaZip zip(filename);
@@ -73,7 +73,7 @@ void ForgeInstaller::prepare(const QString &filename, const QString &universalUr
// read the forge version info
{
- newVersion = VersionFinal::fromJson(versionInfoVal.toObject());
+ newVersion = InstanceVersion::fromJson(versionInfoVal.toObject());
if (!newVersion)
return;
}
@@ -115,8 +115,8 @@ void ForgeInstaller::prepare(const QString &filename, const QString &universalUr
}
file.close();
- m_forge_version = newVersion;
- realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
+ m_forge_json = newVersion;
+ realVersionId = m_forge_json->id = installObj.value("minecraft").toString();
}
bool ForgeInstaller::add(OneSixInstance *to)
{
@@ -128,7 +128,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
QJsonObject obj;
obj.insert("order", 5);
- if (!m_forge_version)
+ if (!m_forge_json)
return false;
int sliding_insert_window = 0;
{
@@ -136,7 +136,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
// for each library in the version we are adding (except for the blacklisted)
QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"};
- for (auto lib : m_forge_version->libraries)
+ for (auto lib : m_forge_json->libraries)
{
QString libName = lib->name();
// WARNING: This could actually break.
@@ -157,7 +157,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
bool found = false;
bool equals = false;
// find an entry that matches this one
- for (auto tolib : to->getVanillaVersion()->libraries)
+ for (auto tolib : to->getFullVersion()->vanillaLibraries)
{
if (tolib->name() != libName)
continue;
@@ -187,8 +187,8 @@ bool ForgeInstaller::add(OneSixInstance *to)
librariesPlus.prepend(libObj);
}
obj.insert("+libraries", librariesPlus);
- obj.insert("mainClass", m_forge_version->mainClass);
- QString args = m_forge_version->minecraftArguments;
+ obj.insert("mainClass", m_forge_json->mainClass);
+ QString args = m_forge_json->minecraftArguments;
QStringList tweakers;
{
QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)");
@@ -200,7 +200,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
match = expression.match(args);
}
}
- if (!args.isEmpty() && args != to->getVanillaVersion()->minecraftArguments)
+ if (!args.isEmpty() && args != to->getFullVersion()->vanillaMinecraftArguments)
{
obj.insert("minecraftArguments", args);
}
@@ -208,10 +208,10 @@ bool ForgeInstaller::add(OneSixInstance *to)
{
obj.insert("+tweakers", QJsonArray::fromStringList(tweakers));
}
- if (!m_forge_version->processArguments.isEmpty() &&
- m_forge_version->processArguments != to->getVanillaVersion()->processArguments)
+ if (!m_forge_json->processArguments.isEmpty() &&
+ m_forge_json->processArguments != to->getFullVersion()->vanillaProcessArguments)
{
- obj.insert("processArguments", m_forge_version->processArguments);
+ obj.insert("processArguments", m_forge_json->processArguments);
}
}
@@ -233,11 +233,65 @@ bool ForgeInstaller::add(OneSixInstance *to)
return true;
}
+bool ForgeInstaller::addLegacy(OneSixInstance *to)
+{
+ if (!BaseInstaller::add(to))
+ {
+ return false;
+ }
+ auto entry = MMC->metacache()->resolveEntry("minecraftforge", m_forge_version->filename());
+ finalPath = PathCombine(to->jarModsDir(), m_forge_version->filename());
+ if (!ensureFilePathExists(finalPath))
+ {
+ return false;
+ }
+ if (!QFile::copy(entry->getFullPath(),finalPath))
+ {
+ return false;
+ }
+ QJsonObject obj;
+ obj.insert("order", 5);
+ {
+ QJsonArray jarmodsPlus;
+ {
+ QJsonObject libObj;
+ libObj.insert("name", m_forge_version->universal_filename);
+ jarmodsPlus.append(libObj);
+ }
+ obj.insert("+jarMods", jarmodsPlus);
+ }
+
+ obj.insert("name", QString("Forge"));
+ obj.insert("fileId", id());
+ obj.insert("version", m_forge_version->jobbuildver);
+ obj.insert("mcVersion", to->intendedVersionId());
+ if (g_VersionFilterData.fmlLibsMapping.contains(m_forge_version->mcver))
+ {
+ QJsonArray traitsPlus;
+ traitsPlus.append(QString("legacyFML"));
+ obj.insert("+traits", traitsPlus);
+ }
+ auto fullversion = to->getFullVersion();
+ fullversion->remove("net.minecraftforge");
+
+ QFile file(filename(to->instanceRoot()));
+ if (!file.open(QFile::WriteOnly))
+ {
+ QLOG_ERROR() << "Error opening" << file.fileName()
+ << "for reading:" << file.errorString();
+ return false;
+ }
+ file.write(QJsonDocument(obj).toJson());
+ file.close();
+ return true;
+}
+
class ForgeInstallTask : public Task
{
Q_OBJECT
public:
- ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance, BaseVersionPtr version, QObject *parent = 0)
+ ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance,
+ BaseVersionPtr version, QObject *parent = 0)
: Task(parent), m_installer(installer), m_instance(instance), m_version(version)
{
}
@@ -245,57 +299,59 @@ public:
protected:
void executeTask() override
{
+ setStatus(tr("Installing forge..."));
+ ForgeVersionPtr forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(m_version);
+ if (!forgeVersion)
{
- setStatus(tr("Installing forge..."));
- ForgeVersionPtr forgeVersion =
- std::dynamic_pointer_cast<ForgeVersion>(m_version);
- if (!forgeVersion)
- {
- emitFailed(tr("Unknown error occured"));
- return;
- }
- auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
- if (entry->stale)
+ emitFailed(tr("Unknown error occured"));
+ return;
+ }
+ prepare(forgeVersion);
+ }
+ void prepare(ForgeVersionPtr forgeVersion)
+ {
+ auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename());
+ auto installFunction = [this, entry, forgeVersion]()
+ {
+ if (!install(entry, forgeVersion))
{
- NetJob *fjob = new NetJob("Forge download");
- fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
- connect(fjob, &NetJob::progress, [this](qint64 current, qint64 total){setProgress(100 * current / qMax((qint64)1, total));});
- connect(fjob, &NetJob::status, [this](const QString &msg){setStatus(msg);});
- connect(fjob, &NetJob::failed, [this](){emitFailed(tr("Failure to download forge"));});
- connect(fjob, &NetJob::succeeded, [this, entry, forgeVersion]()
- {
- if (!install(entry, forgeVersion))
- {
- QLOG_ERROR() << "Failure installing forge";
- emitFailed(tr("Failure to install forge"));
- }
- else
- {
- reload();
- }
- });
- fjob->start();
+ QLOG_ERROR() << "Failure installing forge";
+ emitFailed(tr("Failure to install forge"));
}
else
{
- if (!install(entry, forgeVersion))
- {
- QLOG_ERROR() << "Failure installing forge";
- emitFailed(tr("Failure to install forge"));
- }
- else
- {
- reload();
- }
+ reload();
}
+ };
+
+ if (entry->stale)
+ {
+ NetJob *fjob = new NetJob("Forge download");
+ fjob->addNetAction(CacheDownload::make(forgeVersion->url(), entry));
+ connect(fjob, &NetJob::progress, [this](qint64 current, qint64 total)
+ { setProgress(100 * current / qMax((qint64)1, total)); });
+ connect(fjob, &NetJob::status, [this](const QString & msg)
+ { setStatus(msg); });
+ connect(fjob, &NetJob::failed, [this]()
+ { emitFailed(tr("Failure to download forge")); });
+ connect(fjob, &NetJob::succeeded, installFunction);
+ fjob->start();
+ }
+ else
+ {
+ installFunction();
}
}
-
bool install(const std::shared_ptr<MetaEntry> &entry, const ForgeVersionPtr &forgeVersion)
{
- QString forgePath = entry->getFullPath();
- m_installer->prepare(forgePath, forgeVersion->universal_url);
- return m_installer->add(m_instance);
+ if (forgeVersion->usesInstaller())
+ {
+ QString forgePath = entry->getFullPath();
+ m_installer->prepare(forgePath, forgeVersion->universal_url);
+ return m_installer->add(m_instance);
+ }
+ else
+ return m_installer->addLegacy(m_instance);
}
void reload()
{
@@ -320,8 +376,14 @@ private:
BaseVersionPtr m_version;
};
-ProgressProvider *ForgeInstaller::createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent)
+ProgressProvider *ForgeInstaller::createInstallTask(OneSixInstance *instance,
+ BaseVersionPtr version, QObject *parent)
{
+ if (!version)
+ {
+ return nullptr;
+ }
+ m_forge_version = std::dynamic_pointer_cast<ForgeVersion>(version);
return new ForgeInstallTask(this, instance, version, parent);
}
diff --git a/logic/ForgeInstaller.h b/logic/forge/ForgeInstaller.h
index 05cc994b..14aeeb51 100644
--- a/logic/ForgeInstaller.h
+++ b/logic/forge/ForgeInstaller.h
@@ -15,28 +15,34 @@
#pragma once
-#include "BaseInstaller.h"
+#include "logic/BaseInstaller.h"
#include <QString>
#include <memory>
-class VersionFinal;
+class InstanceVersion;
+class ForgeInstallTask;
+class ForgeVersion;
class ForgeInstaller : public BaseInstaller
{
+ friend class ForgeInstallTask;
public:
ForgeInstaller();
+ virtual ~ForgeInstaller(){};
+ virtual ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override;
+protected:
+ virtual QString id() const override { return "net.minecraftforge"; }
void prepare(const QString &filename, const QString &universalUrl);
bool add(OneSixInstance *to) override;
-
- QString id() const override { return "net.minecraftforge"; }
-
- ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override;
+ bool addLegacy(OneSixInstance *to);
private:
- // the version, read from the installer
- std::shared_ptr<VersionFinal> m_forge_version;
+ // the parsed version json, read from the installer
+ std::shared_ptr<InstanceVersion> m_forge_json;
+ // the actual forge version
+ std::shared_ptr<ForgeVersion> m_forge_version;
QString internalPath;
QString finalPath;
QString realVersionId;
diff --git a/logic/net/ForgeMirror.h b/logic/forge/ForgeMirror.h
index 2518dffe..2518dffe 100644
--- a/logic/net/ForgeMirror.h
+++ b/logic/forge/ForgeMirror.h
diff --git a/logic/net/ForgeMirrors.cpp b/logic/forge/ForgeMirrors.cpp
index b224306f..b224306f 100644
--- a/logic/net/ForgeMirrors.cpp
+++ b/logic/forge/ForgeMirrors.cpp
diff --git a/logic/net/ForgeMirrors.h b/logic/forge/ForgeMirrors.h
index 6784fba1..d25762db 100644
--- a/logic/net/ForgeMirrors.h
+++ b/logic/forge/ForgeMirrors.h
@@ -15,10 +15,10 @@
#pragma once
-#include "NetAction.h"
-#include "HttpMetaCache.h"
-#include "ForgeXzDownload.h"
-#include "NetJob.h"
+#include "logic/net/NetAction.h"
+#include "logic/net/HttpMetaCache.h"
+#include "logic/net/NetJob.h"
+#include "logic/forge/ForgeXzDownload.h"
#include <QFile>
#include <QTemporaryFile>
typedef std::shared_ptr<class ForgeMirrors> ForgeMirrorsPtr;
diff --git a/logic/forge/ForgeVersion.cpp b/logic/forge/ForgeVersion.cpp
new file mode 100644
index 00000000..3131ec39
--- /dev/null
+++ b/logic/forge/ForgeVersion.cpp
@@ -0,0 +1,55 @@
+#include "ForgeVersion.h"
+#include "logic/VersionFilterData.h"
+#include <QObject>
+
+QString ForgeVersion::name()
+{
+ return "Forge " + jobbuildver;
+}
+
+QString ForgeVersion::descriptor()
+{
+ return universal_filename;
+}
+
+QString ForgeVersion::typeString() const
+{
+ if (is_recommended)
+ return QObject::tr("Recommended");
+ return QString();
+}
+
+bool ForgeVersion::operator<(BaseVersion &a)
+{
+ ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
+ if (!pa)
+ return true;
+ return m_buildnr < pa->m_buildnr;
+}
+
+bool ForgeVersion::operator>(BaseVersion &a)
+{
+ ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
+ if (!pa)
+ return false;
+ return m_buildnr > pa->m_buildnr;
+}
+
+bool ForgeVersion::usesInstaller()
+{
+ if(installer_url.isEmpty())
+ return false;
+ if(g_VersionFilterData.forgeInstallerBlacklist.contains(mcver))
+ return false;
+ return true;
+}
+
+QString ForgeVersion::filename()
+{
+ return usesInstaller() ? installer_filename : universal_filename;
+}
+
+QString ForgeVersion::url()
+{
+ return usesInstaller() ? installer_url : universal_url;
+}
diff --git a/logic/forge/ForgeVersion.h b/logic/forge/ForgeVersion.h
new file mode 100644
index 00000000..466f46bd
--- /dev/null
+++ b/logic/forge/ForgeVersion.h
@@ -0,0 +1,33 @@
+#pragma once
+#include <QString>
+#include <memory>
+#include "logic/BaseVersion.h"
+
+struct ForgeVersion;
+typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr;
+
+struct ForgeVersion : public BaseVersion
+{
+ virtual QString descriptor() override;
+ virtual QString name() override;
+ virtual QString typeString() const override;
+ virtual bool operator<(BaseVersion &a) override;
+ virtual bool operator>(BaseVersion &a) override;
+
+ QString filename();
+ QString url();
+
+ bool usesInstaller();
+
+ int m_buildnr = 0;
+ QString branch;
+ QString universal_url;
+ QString changelog_url;
+ QString installer_url;
+ QString jobbuildver;
+ QString mcver;
+ QString mcver_sane;
+ QString universal_filename;
+ QString installer_filename;
+ bool is_recommended = false;
+};
diff --git a/logic/lists/ForgeVersionList.cpp b/logic/forge/ForgeVersionList.cpp
index 50899d24..efb30ba6 100644
--- a/logic/lists/ForgeVersionList.cpp
+++ b/logic/forge/ForgeVersionList.cpp
@@ -13,9 +13,10 @@
* limitations under the License.
*/
-#include "ForgeVersionList.h"
-#include <logic/net/NetJob.h>
-#include <logic/net/URLConstants.h>
+#include "logic/forge/ForgeVersionList.h"
+#include "logic/forge/ForgeVersion.h"
+#include "logic/net/NetJob.h"
+#include "logic/net/URLConstants.h"
#include "MultiMC.h"
#include <QtNetwork>
@@ -236,7 +237,7 @@ bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out)
if (!build_nr)
continue;
QJsonArray files = obj.value("files").toArray();
- QString url, jobbuildver, mcver, buildtype, filename;
+ QString url, jobbuildver, mcver, buildtype, universal_filename;
QString changelog_url, installer_url;
QString installer_filename;
bool valid = false;
@@ -254,7 +255,7 @@ bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out)
url = file.value("url").toString();
jobbuildver = file.value("jobbuildver").toString();
int lastSlash = url.lastIndexOf('/');
- filename = url.mid(lastSlash + 1);
+ universal_filename = url.mid(lastSlash + 1);
valid = true;
}
else if (buildtype == "changelog")
@@ -282,14 +283,8 @@ bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out)
fVersion->installer_url = installer_url;
fVersion->jobbuildver = jobbuildver;
fVersion->mcver = fVersion->mcver_sane = mcver;
- if (installer_filename.isEmpty())
- {
- fVersion->filename = filename;
- }
- else
- {
- fVersion->filename = installer_filename;
- }
+ fVersion->installer_filename = installer_filename;
+ fVersion->universal_filename = universal_filename;
fVersion->m_buildnr = build_nr;
out.append(fVersion);
}
@@ -343,12 +338,13 @@ bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out)
fVersion->jobbuildver = number.value("version").toString();
fVersion->branch = number.value("branch").toString("");
fVersion->mcver = number.value("mcversion").toString();
+ fVersion->universal_filename = "";
+ fVersion->installer_filename = "";
// HACK: here, we fix the minecraft version used by forge.
// HACK: this will inevitably break (later)
// FIXME: replace with a dictionary
fVersion->mcver_sane = fVersion->mcver;
fVersion->mcver_sane.replace("_pre", "-pre");
- fVersion->filename = "";
QString universal_filename, installer_filename;
QJsonArray files = number.value("files").toArray();
@@ -397,7 +393,8 @@ bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out)
{
continue;
}
- fVersion->filename = fVersion->installer_url.isEmpty() ? universal_filename : installer_filename;
+ fVersion->universal_filename = universal_filename;
+ fVersion->installer_filename = installer_filename;
out.append(fVersion);
}
return true;
@@ -441,6 +438,7 @@ void ForgeListLoadTask::listFailed()
QLOG_ERROR() << "Getting forge version list failed for reasons unknown.";
}
}
+
void ForgeListLoadTask::gradleListFailed()
{
auto reply = gradleListDownload->m_reply;
diff --git a/logic/lists/ForgeVersionList.h b/logic/forge/ForgeVersionList.h
index 091072e4..477edb3d 100644
--- a/logic/lists/ForgeVersionList.h
+++ b/logic/forge/ForgeVersionList.h
@@ -18,59 +18,12 @@
#include <QObject>
#include <QAbstractListModel>
#include <QUrl>
-
#include <QNetworkReply>
-#include "BaseVersionList.h"
+
+#include "logic/BaseVersionList.h"
#include "logic/tasks/Task.h"
#include "logic/net/NetJob.h"
-
-class ForgeVersion;
-typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr;
-
-struct ForgeVersion : public BaseVersion
-{
- virtual QString descriptor() override
- {
- return filename;
- }
- ;
- virtual QString name() override
- {
- return "Forge " + jobbuildver;
- }
- ;
- virtual QString typeString() const override
- {
- if (installer_url.isEmpty())
- return "Universal";
- else
- return "Installer";
- }
-
- virtual bool operator<(BaseVersion &a) override
- {
- ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
- if(!pa)
- return true;
- return m_buildnr < pa->m_buildnr;
- }
- virtual bool operator>(BaseVersion &a) override
- {
- ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
- if(!pa)
- return false;
- return m_buildnr > pa->m_buildnr;
- }
- int m_buildnr = 0;
- QString universal_url;
- QString changelog_url;
- QString installer_url;
- QString jobbuildver;
- QString mcver;
- QString filename;
- QString branch;
- QString mcver_sane;
-};
+#include "logic/forge/ForgeVersion.h"
class ForgeVersionList : public BaseVersionList
{
@@ -88,6 +41,8 @@ public:
virtual BaseVersionPtr getLatestStable() const;
+ ForgeVersionPtr findVersionByVersionNr(QString version);
+
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual int columnCount(const QModelIndex &parent) const;
diff --git a/logic/net/ForgeXzDownload.cpp b/logic/forge/ForgeXzDownload.cpp
index 359ad858..359ad858 100644
--- a/logic/net/ForgeXzDownload.cpp
+++ b/logic/forge/ForgeXzDownload.cpp
diff --git a/logic/net/ForgeXzDownload.h b/logic/forge/ForgeXzDownload.h
index 7bdfb6d9..f2564380 100644
--- a/logic/net/ForgeXzDownload.h
+++ b/logic/forge/ForgeXzDownload.h
@@ -15,8 +15,8 @@
#pragma once
-#include "NetAction.h"
-#include "HttpMetaCache.h"
+#include "logic/net/NetAction.h"
+#include "logic/net/HttpMetaCache.h"
#include <QFile>
#include <QTemporaryFile>
#include "ForgeMirror.h"
diff --git a/logic/LegacyForge.cpp b/logic/forge/LegacyForge.cpp
index 94212ae4..94212ae4 100644
--- a/logic/LegacyForge.cpp
+++ b/logic/forge/LegacyForge.cpp
diff --git a/logic/LegacyForge.h b/logic/forge/LegacyForge.h
index f4165ffa..ec49f63c 100644
--- a/logic/LegacyForge.h
+++ b/logic/forge/LegacyForge.h
@@ -15,7 +15,7 @@
#pragma once
-#include "Mod.h"
+#include "logic/Mod.h"
class MinecraftForge : public Mod
{
diff --git a/logic/JavaChecker.cpp b/logic/java/JavaChecker.cpp
index b87ee3d5..b87ee3d5 100644
--- a/logic/JavaChecker.cpp
+++ b/logic/java/JavaChecker.cpp
diff --git a/logic/JavaChecker.h b/logic/java/JavaChecker.h
index e19895f7..e19895f7 100644
--- a/logic/JavaChecker.h
+++ b/logic/java/JavaChecker.h
diff --git a/logic/JavaCheckerJob.cpp b/logic/java/JavaCheckerJob.cpp
index b0aea758..b0aea758 100644
--- a/logic/JavaCheckerJob.cpp
+++ b/logic/java/JavaCheckerJob.cpp
diff --git a/logic/JavaCheckerJob.h b/logic/java/JavaCheckerJob.h
index 132a92d4..132a92d4 100644
--- a/logic/JavaCheckerJob.h
+++ b/logic/java/JavaCheckerJob.h
diff --git a/logic/JavaUtils.cpp b/logic/java/JavaUtils.cpp
index 3a3046bd..1be93a9b 100644
--- a/logic/JavaUtils.cpp
+++ b/logic/java/JavaUtils.cpp
@@ -16,7 +16,6 @@
#include <QStringList>
#include <QString>
#include <QDir>
-#include <QMessageBox>
#include <QStringList>
#include <setting.h>
@@ -24,11 +23,10 @@
#include "MultiMC.h"
-#include "JavaUtils.h"
#include "logger/QsLog.h"
-#include "gui/dialogs/VersionSelectDialog.h"
-#include "JavaCheckerJob.h"
-#include "lists/JavaVersionList.h"
+#include "logic/java/JavaUtils.h"
+#include "logic/java/JavaCheckerJob.h"
+#include "logic/java/JavaVersionList.h"
JavaUtils::JavaUtils()
{
diff --git a/logic/JavaUtils.h b/logic/java/JavaUtils.h
index 22a68ef3..af92100f 100644
--- a/logic/JavaUtils.h
+++ b/logic/java/JavaUtils.h
@@ -21,7 +21,7 @@
#include <osutils.h>
#include "JavaCheckerJob.h"
#include "JavaChecker.h"
-#include "lists/JavaVersionList.h"
+#include "JavaVersionList.h"
#if WINDOWS
#include <windows.h>
diff --git a/logic/lists/JavaVersionList.cpp b/logic/java/JavaVersionList.cpp
index 4fd0bc19..dcb6ced6 100644
--- a/logic/lists/JavaVersionList.cpp
+++ b/logic/java/JavaVersionList.cpp
@@ -13,16 +13,16 @@
* limitations under the License.
*/
-#include "JavaVersionList.h"
-#include "MultiMC.h"
-
#include <QtNetwork>
#include <QtXml>
#include <QRegExp>
+#include "MultiMC.h"
#include "logger/QsLog.h"
-#include "logic/JavaCheckerJob.h"
-#include "logic/JavaUtils.h"
+
+#include "logic/java/JavaVersionList.h"
+#include "logic/java/JavaCheckerJob.h"
+#include "logic/java/JavaUtils.h"
JavaVersionList::JavaVersionList(QObject *parent) : BaseVersionList(parent)
{
diff --git a/logic/lists/JavaVersionList.h b/logic/java/JavaVersionList.h
index e6cc8e5f..a46f33a2 100644
--- a/logic/lists/JavaVersionList.h
+++ b/logic/java/JavaVersionList.h
@@ -18,9 +18,9 @@
#include <QObject>
#include <QAbstractListModel>
-#include "BaseVersionList.h"
+#include "logic/BaseVersionList.h"
#include "logic/tasks/Task.h"
-#include "logic/JavaCheckerJob.h"
+#include "logic/java/JavaCheckerJob.h"
class JavaListLoadTask;
diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp
deleted file mode 100644
index b9d60c61..00000000
--- a/logic/lists/MinecraftVersionList.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-/* Copyright 2013 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 "MinecraftVersionList.h"
-#include "MultiMC.h"
-#include "logic/net/URLConstants.h"
-
-#include <QtXml>
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonValue>
-#include <QJsonParseError>
-
-#include <QtAlgorithms>
-
-#include <QtNetwork>
-
-MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
-{
-}
-
-Task *MinecraftVersionList::getLoadTask()
-{
- return new MCVListLoadTask(this);
-}
-
-bool MinecraftVersionList::isLoaded()
-{
- return m_loaded;
-}
-
-const BaseVersionPtr MinecraftVersionList::at(int i) const
-{
- return m_vlist.at(i);
-}
-
-int MinecraftVersionList::count() const
-{
- return m_vlist.count();
-}
-
-static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
-{
- auto left = std::dynamic_pointer_cast<MinecraftVersion>(first);
- auto right = std::dynamic_pointer_cast<MinecraftVersion>(second);
- return left->timestamp > right->timestamp;
-}
-
-void MinecraftVersionList::sortInternal()
-{
- qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
-}
-
-void MinecraftVersionList::sort()
-{
- beginResetModel();
- sortInternal();
- endResetModel();
-}
-
-BaseVersionPtr MinecraftVersionList::getLatestStable() const
-{
- for (int i = 0; i < m_vlist.length(); i++)
- {
- auto ver = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist.at(i));
- if (ver->is_latest && !ver->is_snapshot)
- {
- return m_vlist.at(i);
- }
- }
- return BaseVersionPtr();
-}
-
-void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions)
-{
- beginResetModel();
- m_vlist = versions;
- m_loaded = true;
- sortInternal();
- endResetModel();
-}
-
-inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
-{
- QDomNodeList elementList = parent.elementsByTagName(tagname);
- if (elementList.count())
- return elementList.at(0).toElement();
- else
- return QDomElement();
-}
-
-inline QDateTime timeFromS3Time(QString str)
-{
- return QDateTime::fromString(str, Qt::ISODate);
-}
-
-MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
-{
- m_list = vlist;
- m_currentStable = NULL;
- vlistReply = nullptr;
- legacyWhitelist.insert("1.5.2");
- legacyWhitelist.insert("1.5.1");
- legacyWhitelist.insert("1.5");
- legacyWhitelist.insert("1.4.7");
- legacyWhitelist.insert("1.4.6");
- legacyWhitelist.insert("1.4.5");
- legacyWhitelist.insert("1.4.4");
- legacyWhitelist.insert("1.4.3");
- legacyWhitelist.insert("1.4.2");
- legacyWhitelist.insert("1.4.1");
- legacyWhitelist.insert("1.4");
- legacyWhitelist.insert("1.3.2");
- legacyWhitelist.insert("1.3.1");
- legacyWhitelist.insert("1.3");
- legacyWhitelist.insert("1.2.5");
- legacyWhitelist.insert("1.2.4");
- legacyWhitelist.insert("1.2.3");
- legacyWhitelist.insert("1.2.2");
- legacyWhitelist.insert("1.2.1");
- legacyWhitelist.insert("1.1");
- legacyWhitelist.insert("1.0.1");
- legacyWhitelist.insert("1.0");
-}
-
-MCVListLoadTask::~MCVListLoadTask()
-{
-}
-
-void MCVListLoadTask::executeTask()
-{
- setStatus(tr("Loading instance version list..."));
- auto worker = MMC->qnam();
- vlistReply = worker->get(QNetworkRequest(QUrl("http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + "versions.json")));
- connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded()));
-}
-
-void MCVListLoadTask::list_downloaded()
-{
- if (vlistReply->error() != QNetworkReply::NoError)
- {
- vlistReply->deleteLater();
- emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString());
- return;
- }
-
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
- vlistReply->deleteLater();
-
- if (jsonError.error != QJsonParseError::NoError)
- {
- emitFailed("Error parsing version list JSON:" + jsonError.errorString());
- return;
- }
-
- if (!jsonDoc.isObject())
- {
- emitFailed("Error parsing version list JSON: jsonDoc is not an object");
- return;
- }
-
- QJsonObject root = jsonDoc.object();
-
- // Get the ID of the latest release and the latest snapshot.
- if (!root.value("latest").isObject())
- {
- emitFailed("Error parsing version list JSON: version list is missing 'latest' object");
- return;
- }
-
- QJsonObject latest = root.value("latest").toObject();
-
- QString latestReleaseID = latest.value("release").toString("");
- QString latestSnapshotID = latest.value("snapshot").toString("");
- if (latestReleaseID.isEmpty())
- {
- emitFailed("Error parsing version list JSON: latest release field is missing");
- return;
- }
- if (latestSnapshotID.isEmpty())
- {
- emitFailed("Error parsing version list JSON: latest snapshot field is missing");
- return;
- }
-
- // Now, get the array of versions.
- if (!root.value("versions").isArray())
- {
- emitFailed(
- "Error parsing version list JSON: version list object is missing 'versions' array");
- return;
- }
- QJsonArray versions = root.value("versions").toArray();
-
- QList<BaseVersionPtr> tempList;
- for (int i = 0; i < versions.count(); i++)
- {
- bool is_snapshot = false;
- bool is_latest = false;
-
- // Load the version info.
- if (!versions[i].isObject())
- {
- // FIXME: log this somewhere
- continue;
- }
- QJsonObject version = versions[i].toObject();
- QString versionID = version.value("id").toString("");
- QString versionTimeStr = version.value("releaseTime").toString("");
- QString versionTypeStr = version.value("type").toString("");
- if (versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty())
- {
- // FIXME: log this somewhere
- continue;
- }
-
- // Parse the timestamp.
- QDateTime versionTime = timeFromS3Time(versionTimeStr);
- if (!versionTime.isValid())
- {
- // FIXME: log this somewhere
- continue;
- }
- // Parse the type.
- MinecraftVersion::VersionType versionType;
- // OneSix or Legacy. use filter to determine type
- if (versionTypeStr == "release")
- {
- versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
- : MinecraftVersion::OneSix;
- is_latest = (versionID == latestReleaseID);
- is_snapshot = false;
- }
- else if (versionTypeStr == "snapshot") // It's a snapshot... yay
- {
- versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
- : MinecraftVersion::OneSix;
- is_latest = (versionID == latestSnapshotID);
- is_snapshot = true;
- }
- else if (versionTypeStr == "old_alpha")
- {
- versionType = MinecraftVersion::Nostalgia;
- is_latest = false;
- is_snapshot = false;
- }
- else if (versionTypeStr == "old_beta")
- {
- versionType = MinecraftVersion::Legacy;
- is_latest = false;
- is_snapshot = false;
- }
- else
- {
- // FIXME: log this somewhere
- continue;
- }
- // Get the download URL.
- QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
-
- // Now, we construct the version object and add it to the list.
- std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
- mcVersion->m_name = mcVersion->m_descriptor = versionID;
- mcVersion->timestamp = versionTime.toMSecsSinceEpoch();
- mcVersion->download_url = dlUrl;
- mcVersion->is_latest = is_latest;
- mcVersion->is_snapshot = is_snapshot;
- mcVersion->type = versionType;
- tempList.append(mcVersion);
- }
- m_list->updateListData(tempList);
-
- emitSucceeded();
- return;
-}
diff --git a/logic/LiteLoaderInstaller.cpp b/logic/liteloader/LiteLoaderInstaller.cpp
index 99cc5643..ea1a4396 100644
--- a/logic/LiteLoaderInstaller.cpp
+++ b/logic/liteloader/LiteLoaderInstaller.cpp
@@ -20,11 +20,11 @@
#include "logger/QsLog.h"
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
-#include "OneSixInstance.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/OneSixInstance.h"
#include "MultiMC.h"
-#include "lists/LiteLoaderVersionList.h"
+#include "logic/liteloader/LiteLoaderVersionList.h"
LiteLoaderInstaller::LiteLoaderInstaller() : BaseInstaller()
{
diff --git a/logic/LiteLoaderInstaller.h b/logic/liteloader/LiteLoaderInstaller.h
index 3ab5acb2..43ad6b83 100644
--- a/logic/LiteLoaderInstaller.h
+++ b/logic/liteloader/LiteLoaderInstaller.h
@@ -15,12 +15,11 @@
#pragma once
-#include "BaseInstaller.h"
-
#include <QString>
#include <QMap>
-#include "logic/lists/LiteLoaderVersionList.h"
+#include "logic/BaseInstaller.h"
+#include "logic/liteloader/LiteLoaderVersionList.h"
class LiteLoaderInstaller : public BaseInstaller
{
diff --git a/logic/lists/LiteLoaderVersionList.cpp b/logic/liteloader/LiteLoaderVersionList.cpp
index ef95eefd..ef95eefd 100644
--- a/logic/lists/LiteLoaderVersionList.cpp
+++ b/logic/liteloader/LiteLoaderVersionList.cpp
diff --git a/logic/lists/LiteLoaderVersionList.h b/logic/liteloader/LiteLoaderVersionList.h
index bfc913e5..0aecc3e1 100644
--- a/logic/lists/LiteLoaderVersionList.h
+++ b/logic/liteloader/LiteLoaderVersionList.h
@@ -19,10 +19,10 @@
#include <QString>
#include <QStringList>
-#include "BaseVersionList.h"
-#include "logic/tasks/Task.h"
#include "logic/BaseVersion.h"
-#include <logic/net/NetJob.h>
+#include "logic/BaseVersionList.h"
+#include "logic/tasks/Task.h"
+#include "logic/net/NetJob.h"
class LLListLoadTask;
class QNetworkReply;
diff --git a/logic/minecraft/InstanceVersion.cpp b/logic/minecraft/InstanceVersion.cpp
new file mode 100644
index 00000000..ca0e3796
--- /dev/null
+++ b/logic/minecraft/InstanceVersion.cpp
@@ -0,0 +1,537 @@
+/* Copyright 2013 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 <QDebug>
+#include <QFile>
+#include <QDir>
+#include <QUuid>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <pathutils.h>
+
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/VersionBuilder.h"
+#include "logic/OneSixInstance.h"
+
+InstanceVersion::InstanceVersion(OneSixInstance *instance, QObject *parent)
+ : QAbstractListModel(parent), m_instance(instance)
+{
+ clear();
+}
+
+void InstanceVersion::reload(const QStringList &external)
+{
+ m_externalPatches = external;
+ beginResetModel();
+ VersionBuilder::build(this, m_instance, m_externalPatches);
+ reapply(true);
+ endResetModel();
+}
+
+void InstanceVersion::clear()
+{
+ id.clear();
+ m_updateTimeString.clear();
+ m_updateTime = QDateTime();
+ m_releaseTimeString.clear();
+ m_releaseTime = QDateTime();
+ type.clear();
+ assets.clear();
+ processArguments.clear();
+ minecraftArguments.clear();
+ minimumLauncherVersion = 0xDEADBEAF;
+ mainClass.clear();
+ appletClass.clear();
+ libraries.clear();
+ tweakers.clear();
+ jarMods.clear();
+ traits.clear();
+}
+
+bool InstanceVersion::canRemove(const int index) const
+{
+ return VersionPatches.at(index)->isMoveable();
+}
+
+bool InstanceVersion::preremove(VersionPatchPtr patch)
+{
+ bool ok = true;
+ for(auto & jarmod: patch->getJarMods())
+ {
+ QString fullpath =PathCombine(m_instance->jarModsDir(), jarmod->name);
+ QFileInfo finfo (fullpath);
+ if(finfo.exists())
+ ok &= QFile::remove(fullpath);
+ }
+ return ok;
+}
+
+bool InstanceVersion::remove(const int index)
+{
+ if (!canRemove(index))
+ return false;
+ if(!preremove(VersionPatches[index]))
+ {
+ return false;
+ }
+ if(!QFile::remove(VersionPatches.at(index)->getPatchFilename()))
+ return false;
+ beginRemoveRows(QModelIndex(), index, index);
+ VersionPatches.removeAt(index);
+ endRemoveRows();
+ reapply(true);
+ saveCurrentOrder();
+ return true;
+}
+
+bool InstanceVersion::remove(const QString id)
+{
+ int i = 0;
+ for (auto patch : VersionPatches)
+ {
+ if (patch->getPatchID() == id)
+ {
+ return remove(i);
+ }
+ i++;
+ }
+ return false;
+}
+
+QString InstanceVersion::versionFileId(const int index) const
+{
+ if (index < 0 || index >= VersionPatches.size())
+ {
+ return QString();
+ }
+ return VersionPatches.at(index)->getPatchID();
+}
+
+VersionPatchPtr InstanceVersion::versionPatch(const QString &id)
+{
+ for (auto file : VersionPatches)
+ {
+ if (file->getPatchID() == id)
+ {
+ return file;
+ }
+ }
+ return 0;
+}
+
+VersionPatchPtr InstanceVersion::versionPatch(int index)
+{
+ if(index < 0 || index >= VersionPatches.size())
+ return 0;
+ return VersionPatches[index];
+}
+
+
+bool InstanceVersion::hasJarMods()
+{
+ return !jarMods.isEmpty();
+}
+
+bool InstanceVersion::hasFtbPack()
+{
+ return versionPatch("org.multimc.ftb.pack.json") != nullptr;
+}
+
+bool InstanceVersion::removeFtbPack()
+{
+ return remove("org.multimc.ftb.pack.json");
+}
+
+bool InstanceVersion::isVanilla()
+{
+ QDir patches(PathCombine(m_instance->instanceRoot(), "patches/"));
+ if(VersionPatches.size() > 1)
+ return false;
+ if(QFile::exists(PathCombine(m_instance->instanceRoot(), "custom.json")))
+ return false;
+ if(QFile::exists(PathCombine(m_instance->instanceRoot(), "version.json")))
+ return false;
+ return true;
+}
+
+bool InstanceVersion::revertToVanilla()
+{
+ beginResetModel();
+ // remove custom.json, if present
+ QString customPath = PathCombine(m_instance->instanceRoot(), "custom.json");
+ if(QFile::exists(customPath))
+ {
+ if(!QFile::remove(customPath))
+ {
+ endResetModel();
+ return false;
+ }
+ }
+ // remove version.json, if present
+ QString versionPath = PathCombine(m_instance->instanceRoot(), "version.json");
+ if(QFile::exists(versionPath))
+ {
+ if(!QFile::remove(versionPath))
+ {
+ endResetModel();
+ return false;
+ }
+ }
+ // remove patches, if present
+ auto it = VersionPatches.begin();
+ while (it != VersionPatches.end())
+ {
+ if ((*it)->isMoveable())
+ {
+ if(!preremove(*it))
+ {
+ endResetModel();
+ saveCurrentOrder();
+ return false;
+ }
+ if(!QFile::remove((*it)->getPatchFilename()))
+ {
+ endResetModel();
+ saveCurrentOrder();
+ return false;
+ }
+ it = VersionPatches.erase(it);
+ }
+ else
+ it++;
+ }
+ reapply(true);
+ endResetModel();
+ saveCurrentOrder();
+ return true;
+}
+
+bool InstanceVersion::hasDeprecatedVersionFiles()
+{
+ if(QFile::exists(PathCombine(m_instance->instanceRoot(), "custom.json")))
+ return true;
+ if(QFile::exists(PathCombine(m_instance->instanceRoot(), "version.json")))
+ return true;
+ return false;
+}
+
+bool InstanceVersion::removeDeprecatedVersionFiles()
+{
+ beginResetModel();
+ // remove custom.json, if present
+ QString customPath = PathCombine(m_instance->instanceRoot(), "custom.json");
+ if(QFile::exists(customPath))
+ {
+ if(!QFile::remove(customPath))
+ {
+ endResetModel();
+ return false;
+ }
+ }
+ // remove version.json, if present
+ QString versionPath = PathCombine(m_instance->instanceRoot(), "version.json");
+ if(QFile::exists(versionPath))
+ {
+ if(!QFile::remove(versionPath))
+ {
+ endResetModel();
+ return false;
+ }
+ }
+ endResetModel();
+ return true;
+}
+
+QList<std::shared_ptr<OneSixLibrary> > InstanceVersion::getActiveNormalLibs()
+{
+ QList<std::shared_ptr<OneSixLibrary> > output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && !lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
+}
+QList<std::shared_ptr<OneSixLibrary> > InstanceVersion::getActiveNativeLibs()
+{
+ QList<std::shared_ptr<OneSixLibrary> > output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
+}
+
+std::shared_ptr<InstanceVersion> InstanceVersion::fromJson(const QJsonObject &obj)
+{
+ std::shared_ptr<InstanceVersion> version(new InstanceVersion(0));
+ try
+ {
+ VersionBuilder::readJsonAndApplyToVersion(version.get(), obj);
+ }
+ catch(MMCError & err)
+ {
+ return 0;
+ }
+ return version;
+}
+
+QVariant InstanceVersion::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+ int column = index.column();
+
+ if (row < 0 || row >= VersionPatches.size())
+ return QVariant();
+
+ if (role == Qt::DisplayRole)
+ {
+ switch (column)
+ {
+ case 0:
+ return VersionPatches.at(row)->getPatchName();
+ case 1:
+ return VersionPatches.at(row)->getPatchVersion();
+ default:
+ return QVariant();
+ }
+ }
+ return QVariant();
+}
+QVariant InstanceVersion::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal)
+ {
+ if (role == Qt::DisplayRole)
+ {
+ switch (section)
+ {
+ case 0:
+ return tr("Name");
+ case 1:
+ return tr("Version");
+ default:
+ return QVariant();
+ }
+ }
+ }
+ return QVariant();
+}
+Qt::ItemFlags InstanceVersion::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+}
+
+int InstanceVersion::rowCount(const QModelIndex &parent) const
+{
+ return VersionPatches.size();
+}
+
+int InstanceVersion::columnCount(const QModelIndex &parent) const
+{
+ return 2;
+}
+
+void InstanceVersion::saveCurrentOrder() const
+{
+ PatchOrder order;
+ for(auto item: VersionPatches)
+ {
+ if(!item->isMoveable())
+ continue;
+ order.append(item->getPatchID());
+ }
+ VersionBuilder::writeOverrideOrders(m_instance, order);
+}
+
+void InstanceVersion::move(const int index, const MoveDirection direction)
+{
+ int theirIndex;
+ if (direction == MoveUp)
+ {
+ theirIndex = index - 1;
+ }
+ else
+ {
+ theirIndex = index + 1;
+ }
+
+ if (index < 0 || index >= VersionPatches.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 = versionPatch(index);
+ auto to = versionPatch(theirIndex);
+
+ if (!from || !to || !to->isMoveable() || !from->isMoveable())
+ {
+ return;
+ }
+ beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap);
+ VersionPatches.swap(index, theirIndex);
+ endMoveRows();
+ saveCurrentOrder();
+ reapply();
+}
+void InstanceVersion::resetOrder()
+{
+ QDir(m_instance->instanceRoot()).remove("order.json");
+ reload(m_externalPatches);
+}
+
+void InstanceVersion::reapply(const bool alreadyReseting)
+{
+ clear();
+ for(auto file: VersionPatches)
+ {
+ file->applyTo(this);
+ }
+ finalize();
+}
+
+void InstanceVersion::finalize()
+{
+ // HACK: deny april fools. my head hurts enough already.
+ QDate now = QDate::currentDate();
+ bool isAprilFools = now.month() == 4 && now.day() == 1;
+ if (assets.endsWith("_af") && !isAprilFools)
+ {
+ assets = assets.left(assets.length() - 3);
+ }
+ if (assets.isEmpty())
+ {
+ assets = "legacy";
+ }
+ auto finalizeArguments = [&]( QString & minecraftArguments, const QString & processArguments ) -> void
+ {
+ if (!minecraftArguments.isEmpty())
+ return;
+ QString toCompare = processArguments.toLower();
+ if (toCompare == "legacy")
+ {
+ minecraftArguments = " ${auth_player_name} ${auth_session}";
+ }
+ else if (toCompare == "username_session")
+ {
+ minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
+ }
+ else if (toCompare == "username_session_version")
+ {
+ minecraftArguments = "--username ${auth_player_name} "
+ "--session ${auth_session} "
+ "--version ${profile_name}";
+ }
+ };
+ finalizeArguments(vanillaMinecraftArguments, vanillaProcessArguments);
+ finalizeArguments(minecraftArguments, processArguments);
+}
+
+void InstanceVersion::installJarMods(QStringList selectedFiles)
+{
+ for(auto filename: selectedFiles)
+ {
+ installJarModByFilename(filename);
+ }
+}
+
+void InstanceVersion::installJarModByFilename(QString filepath)
+{
+ QString patchDir = PathCombine(m_instance->instanceRoot(), "patches");
+ if(!ensureFolderPathExists(patchDir))
+ {
+ // THROW...
+ return;
+ }
+
+ if (!ensureFolderPathExists(m_instance->jarModsDir()))
+ {
+ // THROW...
+ return;
+ }
+
+ 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 = PathCombine(m_instance->jarModsDir(), target_filename);
+
+ QFileInfo targetInfo(finalPath);
+ if(targetInfo.exists())
+ {
+ // THROW
+ return;
+ }
+
+ if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath()))
+ {
+ // THROW
+ return;
+ }
+
+ auto f = std::make_shared<VersionFile>();
+ auto jarMod = std::make_shared<Jarmod>();
+ jarMod->name = target_filename;
+ f->jarMods.append(jarMod);
+ f->name = target_name;
+ f->fileId = target_id;
+ f->order = getFreeOrderNumber();
+
+ QFile file(PathCombine(patchDir, target_id + ".json"));
+ if (!file.open(QFile::WriteOnly))
+ {
+ QLOG_ERROR() << "Error opening" << file.fileName()
+ << "for reading:" << file.errorString();
+ return;
+ // THROW
+ }
+ file.write(f->toJson(true).toJson());
+ file.close();
+ int index = VersionPatches.size();
+ beginInsertRows(QModelIndex(), index, index);
+ VersionPatches.append(f);
+ endInsertRows();
+ saveCurrentOrder();
+}
+
+int InstanceVersion::getFreeOrderNumber()
+{
+ int largest = 100;
+ // yes, I do realize this is dumb. The order thing itself is dumb. and to be removed next.
+ for(auto thing: VersionPatches)
+ {
+ int order = thing->getOrder();
+ if(order > largest)
+ largest = order;
+ }
+ return largest + 1;
+}
diff --git a/logic/VersionFinal.h b/logic/minecraft/InstanceVersion.h
index 41fd23bd..6b69ab47 100644
--- a/logic/VersionFinal.h
+++ b/logic/minecraft/InstanceVersion.h
@@ -23,14 +23,15 @@
#include "OneSixLibrary.h"
#include "VersionFile.h"
+#include "JarMod.h"
class OneSixInstance;
-class VersionFinal : public QAbstractListModel
+class InstanceVersion : public QAbstractListModel
{
Q_OBJECT
public:
- explicit VersionFinal(OneSixInstance *instance, QObject *parent = 0);
+ explicit InstanceVersion(OneSixInstance *instance, QObject *parent = 0);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
@@ -38,22 +39,32 @@ public:
virtual int columnCount(const QModelIndex &parent) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
- void reload(const bool onlyVanilla = false, const QStringList &external = QStringList());
+ void reload(const QStringList &external = QStringList());
void clear();
bool canRemove(const int index) const;
QString versionFileId(const int index) const;
- // does this instance have an all overriding custom.json
- bool isCustom();
- // remove custom.json
- bool revertToBase();
- // does this instance have an FTB pack patch file?
+ // is this version unmodded vanilla minecraft?
+ bool isVanilla();
+ // remove any customizations on top of vanilla
+ bool revertToVanilla();
+
+ // does this version consist of obsolete files?
+ bool hasDeprecatedVersionFiles();
+ // remove obsolete files
+ bool removeDeprecatedVersionFiles();
+
+ // does this version have an FTB pack patch file?
bool hasFtbPack();
// remove FTB pack
bool removeFtbPack();
+ // does this version have any jar mods?
+ bool hasJarMods();
+ void installJarMods(QStringList selectedFiles);
+ void installJarModByFilename(QString filepath);
enum MoveDirection { MoveUp, MoveDown };
void move(const int index, const MoveDirection direction);
@@ -72,16 +83,24 @@ public:
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
- static std::shared_ptr<VersionFinal> fromJson(const QJsonObject &obj);
+ static std::shared_ptr<InstanceVersion> fromJson(const QJsonObject &obj);
+private:
+ bool preremove(VersionPatchPtr patch);
+
// data members
public:
/// the ID - determines which jar to use! ACTUALLY IMPORTANT!
QString id;
- /// Last updated time - as a string
- QString time;
- /// Release time - as a string
- QString releaseTime;
+
+ /// the time this version was actually released by Mojang, as string and as QDateTime
+ QString m_releaseTimeString;
+ QDateTime m_releaseTime;
+
+ /// the time this version was last updated by Mojang, as string and as QDateTime
+ QString m_updateTimeString;
+ QDateTime m_updateTime;
+
/// Release type - "release" or "snapshot"
QString type;
/// Assets type - "legacy" or a version ID
@@ -91,6 +110,8 @@ public:
* ex: "username_session_version"
*/
QString processArguments;
+ /// Same as above, but only for vanilla
+ QString vanillaProcessArguments;
/**
* arguments that should be used for launching minecraft
*
@@ -98,6 +119,8 @@ public:
* --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}"
*/
QString minecraftArguments;
+ /// Same as above, but only for vanilla
+ QString vanillaMinecraftArguments;
/**
* the minimum launcher version required by this version ... current is 4 (at point of
* writing)
@@ -111,9 +134,22 @@ public:
* The main class to load first
*/
QString mainClass;
-
+ /**
+ * The applet class, for some very old minecraft releases
+ */
+ QString appletClass;
+
/// the list of libs - both active and inactive, native and java
QList<std::shared_ptr<OneSixLibrary>> libraries;
+
+ /// same, but only vanilla.
+ QList<std::shared_ptr<OneSixLibrary>> vanillaLibraries;
+
+ /// traits, collected from all the version files (version files can only add)
+ QSet<QString> traits;
+
+ /// A list of jar mods. version files can add those.
+ QList<JarmodPtr> jarMods;
/*
FIXME: add support for those rules here? Looks like a pile of quick hacks to me though.
@@ -136,10 +172,13 @@ public:
*/
// QList<Rule> rules;
- QList<VersionFilePtr> versionFiles;
- VersionFilePtr versionFile(const QString &id);
+ QList<VersionPatchPtr> VersionPatches;
+ VersionPatchPtr versionPatch(const QString &id);
+ VersionPatchPtr versionPatch(int index);
private:
+ QStringList m_externalPatches;
OneSixInstance *m_instance;
- QMap<QString, int> getExistingOrder() const;
+ void saveCurrentOrder() const;
+ int getFreeOrderNumber();
};
diff --git a/logic/minecraft/JarMod.cpp b/logic/minecraft/JarMod.cpp
new file mode 100644
index 00000000..18a9411c
--- /dev/null
+++ b/logic/minecraft/JarMod.cpp
@@ -0,0 +1,56 @@
+#include "JarMod.h"
+#include "logic/MMCJson.h"
+using namespace MMCJson;
+
+JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename)
+{
+ JarmodPtr out(new Jarmod());
+ if (!libObj.contains("name"))
+ {
+ throw JSONValidationError(filename +
+ "contains a jarmod that doesn't have a 'name' field");
+ }
+ out->name = libObj.value("name").toString();
+
+ auto readString = [libObj, filename](const QString & key, QString & variable)
+ {
+ if (libObj.contains(key))
+ {
+ QJsonValue val = libObj.value(key);
+ if (!val.isString())
+ {
+ QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
+ }
+ else
+ {
+ variable = val.toString();
+ }
+ }
+ };
+
+ readString("url", out->baseurl);
+ readString("MMC-hint", out->hint);
+ readString("MMC-absoluteUrl", out->absoluteUrl);
+ if(!out->baseurl.isEmpty() && out->absoluteUrl.isEmpty())
+ {
+ out->absoluteUrl = out->baseurl + out->name;
+ }
+ return out;
+}
+
+QJsonObject Jarmod::toJson()
+{
+ QJsonObject out;
+ writeString(out, "name", name);
+ writeString(out, "url", baseurl);
+ writeString(out, "MMC-absoluteUrl", absoluteUrl);
+ writeString(out, "MMC-hint", hint);
+ return out;
+}
+
+QString Jarmod::url()
+{
+ if(!absoluteUrl.isEmpty())
+ return absoluteUrl;
+ else return baseurl + name;
+}
diff --git a/logic/minecraft/JarMod.h b/logic/minecraft/JarMod.h
new file mode 100644
index 00000000..c438dbcd
--- /dev/null
+++ b/logic/minecraft/JarMod.h
@@ -0,0 +1,18 @@
+#pragma once
+#include <QString>
+#include <QJsonObject>
+#include <memory>
+class Jarmod;
+typedef std::shared_ptr<Jarmod> JarmodPtr;
+class Jarmod
+{
+public: /* methods */
+ static JarmodPtr fromJson(const QJsonObject &libObj, const QString &filename);
+ QJsonObject toJson();
+ QString url();
+public: /* data */
+ QString name;
+ QString baseurl;
+ QString hint;
+ QString absoluteUrl;
+};
diff --git a/logic/minecraft/MinecraftVersion.cpp b/logic/minecraft/MinecraftVersion.cpp
new file mode 100644
index 00000000..488a180a
--- /dev/null
+++ b/logic/minecraft/MinecraftVersion.cpp
@@ -0,0 +1,143 @@
+#include "MinecraftVersion.h"
+#include "InstanceVersion.h"
+#include "VersionBuildError.h"
+#include "VersionBuilder.h"
+
+bool MinecraftVersion::usesLegacyLauncher()
+{
+ return m_traits.contains("legacyLaunch") || m_traits.contains("aplhaLaunch");
+}
+
+QString MinecraftVersion::descriptor()
+{
+ return m_descriptor;
+}
+
+QString MinecraftVersion::name()
+{
+ return m_name;
+}
+
+QString MinecraftVersion::typeString() const
+{
+ if(m_type == "snapshot")
+ {
+ return QObject::tr("Snapshot");
+ }
+ else if (m_type == "release")
+ {
+ return QObject::tr("Regular release");
+ }
+ else if (m_type == "old_alpha")
+ {
+ return QObject::tr("Alpha");
+ }
+ else if (m_type == "old_beta")
+ {
+ return QObject::tr("Beta");
+ }
+ else
+ {
+ return QString();
+ }
+}
+
+bool MinecraftVersion::hasJarMods()
+{
+ return false;
+}
+
+bool MinecraftVersion::isMinecraftVersion()
+{
+ return true;
+}
+
+// 1. assume the local file is good. load, check. If it's good, apply.
+// 2. if discrepancies are found, fall out and fail (impossible to apply incomplete version).
+void MinecraftVersion::applyFileTo(InstanceVersion *version)
+{
+ QFileInfo versionFile(QString("versions/%1/%1.dat").arg(m_descriptor));
+
+ auto versionObj = VersionBuilder::parseBinaryJsonFile(versionFile);
+ versionObj->applyTo(version);
+}
+
+void MinecraftVersion::applyTo(InstanceVersion *version)
+{
+ // do we have this one cached?
+ if (m_versionSource == Local)
+ {
+ applyFileTo(version);
+ return;
+ }
+ // if not builtin, do not proceed any further.
+ if (m_versionSource != Builtin)
+ {
+ throw VersionIncomplete(QObject::tr(
+ "Minecraft version %1 could not be applied: version files are missing.").arg(m_descriptor));
+ }
+ if (!m_descriptor.isNull())
+ {
+ version->id = m_descriptor;
+ }
+ if (!m_mainClass.isNull())
+ {
+ version->mainClass = m_mainClass;
+ }
+ if (!m_appletClass.isNull())
+ {
+ version->appletClass = m_appletClass;
+ }
+ if (!m_processArguments.isNull())
+ {
+ version->vanillaProcessArguments = m_processArguments;
+ version->processArguments = m_processArguments;
+ }
+ if (!m_type.isNull())
+ {
+ version->type = m_type;
+ }
+ if (!m_releaseTimeString.isNull())
+ {
+ version->m_releaseTimeString = m_releaseTimeString;
+ version->m_releaseTime = m_releaseTime;
+ }
+ if (!m_updateTimeString.isNull())
+ {
+ version->m_updateTimeString = m_updateTimeString;
+ version->m_updateTime = m_updateTime;
+ }
+ version->traits.unite(m_traits);
+}
+
+int MinecraftVersion::getOrder()
+{
+ return order;
+}
+
+void MinecraftVersion::setOrder(int order)
+{
+ this->order = order;
+}
+
+QList<JarmodPtr> MinecraftVersion::getJarMods()
+{
+ return QList<JarmodPtr>();
+}
+
+QString MinecraftVersion::getPatchName()
+{
+ return "Minecraft";
+}
+QString MinecraftVersion::getPatchVersion()
+{
+ return m_descriptor;
+}
+QString MinecraftVersion::getPatchID()
+{
+ return "net.minecraft";
+}
+QString MinecraftVersion::getPatchFilename()
+{
+ return QString();
+}
diff --git a/logic/minecraft/MinecraftVersion.h b/logic/minecraft/MinecraftVersion.h
new file mode 100644
index 00000000..82073a97
--- /dev/null
+++ b/logic/minecraft/MinecraftVersion.h
@@ -0,0 +1,103 @@
+/* Copyright 2013 Andrew Okin
+ *
+ * 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 <QSet>
+#include <QDateTime>
+
+#include "logic/BaseVersion.h"
+#include "VersionPatch.h"
+#include "VersionFile.h"
+#include "VersionSource.h"
+
+class InstanceVersion;
+class MinecraftVersion;
+typedef std::shared_ptr<MinecraftVersion> MinecraftVersionPtr;
+
+class MinecraftVersion : public BaseVersion, public VersionPatch
+{
+public: /* methods */
+ bool usesLegacyLauncher();
+ virtual QString descriptor() override;
+ virtual QString name() override;
+ virtual QString typeString() const override;
+ virtual bool hasJarMods() override;
+ virtual bool isMinecraftVersion() override;
+ virtual void applyTo(InstanceVersion *version) override;
+ virtual int getOrder();
+ virtual void setOrder(int order);
+ virtual QList<JarmodPtr> getJarMods() override;
+ virtual QString getPatchID() override;
+ virtual QString getPatchVersion() override;
+ virtual QString getPatchName() override;
+ virtual QString getPatchFilename() override;
+ bool needsUpdate()
+ {
+ return m_versionSource == Remote;
+ }
+ bool hasUpdate()
+ {
+ return m_versionSource == Remote || (m_versionSource == Local && upstreamUpdate);
+ }
+
+private: /* methods */
+ void applyFileTo(InstanceVersion *version);
+
+public: /* data */
+ /// The URL that this version will be downloaded from. maybe.
+ QString download_url;
+
+ VersionSource m_versionSource = Builtin;
+
+ /// the human readable version name
+ QString m_name;
+
+ /// the version ID.
+ QString m_descriptor;
+
+ /// version traits. added by MultiMC
+ QSet<QString> m_traits;
+
+ /// The main class this version uses (if any, can be empty).
+ QString m_mainClass;
+
+ /// The applet class this version uses (if any, can be empty).
+ QString m_appletClass;
+
+ /// The process arguments used by this version
+ QString m_processArguments;
+
+ /// The type of this release
+ QString m_type;
+
+ /// the time this version was actually released by Mojang, as string and as QDateTime
+ QString m_releaseTimeString;
+ QDateTime m_releaseTime;
+
+ /// the time this version was last updated by Mojang, as string and as QDateTime
+ QString m_updateTimeString;
+ QDateTime m_updateTime;
+
+ /// MD5 hash of the minecraft jar
+ QString m_jarChecksum;
+
+ /// order of this file... default = -2
+ int order = -2;
+
+ /// an update available from Mojang
+ MinecraftVersionPtr upstreamUpdate;
+};
diff --git a/logic/minecraft/MinecraftVersionList.cpp b/logic/minecraft/MinecraftVersionList.cpp
new file mode 100644
index 00000000..3aa1ac01
--- /dev/null
+++ b/logic/minecraft/MinecraftVersionList.cpp
@@ -0,0 +1,602 @@
+/* Copyright 2013 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 <QtXml>
+#include "logic/MMCJson.h"
+#include <QtAlgorithms>
+#include <QtNetwork>
+
+#include "MultiMC.h"
+#include "MMCError.h"
+
+#include "MinecraftVersionList.h"
+#include "logic/net/URLConstants.h"
+
+#include "ParseUtils.h"
+#include "VersionBuilder.h"
+#include <logic/VersionFilterData.h>
+#include <pathutils.h>
+
+static const char * localVersionCache = "versions/versions.dat";
+
+class ListLoadError : public MMCError
+{
+public:
+ ListLoadError(QString cause) : MMCError(cause) {};
+ virtual ~ListLoadError() noexcept
+ {
+ }
+};
+
+MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
+{
+ loadBuiltinList();
+ loadCachedList();
+}
+
+Task *MinecraftVersionList::getLoadTask()
+{
+ return new MCVListLoadTask(this);
+}
+
+bool MinecraftVersionList::isLoaded()
+{
+ return m_loaded;
+}
+
+const BaseVersionPtr MinecraftVersionList::at(int i) const
+{
+ return m_vlist.at(i);
+}
+
+int MinecraftVersionList::count() const
+{
+ return m_vlist.count();
+}
+
+static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
+{
+ auto left = std::dynamic_pointer_cast<MinecraftVersion>(first);
+ auto right = std::dynamic_pointer_cast<MinecraftVersion>(second);
+ return left->m_releaseTime > right->m_releaseTime;
+}
+
+void MinecraftVersionList::sortInternal()
+{
+ qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
+}
+
+void MinecraftVersionList::loadCachedList()
+{
+ QFile localIndex(localVersionCache);
+ if (!localIndex.exists())
+ {
+ return;
+ }
+ if (!localIndex.open(QIODevice::ReadOnly))
+ {
+ // FIXME: this is actually a very bad thing! How do we deal with this?
+ QLOG_ERROR() << "The minecraft version cache can't be read.";
+ return;
+ }
+ auto data = localIndex.readAll();
+ try
+ {
+ localIndex.close();
+ QJsonDocument jsonDoc = QJsonDocument::fromBinaryData(data);
+ if (jsonDoc.isNull())
+ {
+ throw ListLoadError(tr("Error reading the version list."));
+ }
+ loadMojangList(jsonDoc, Local);
+ }
+ catch (MMCError &e)
+ {
+ // the cache has gone bad for some reason... flush it.
+ QLOG_ERROR() << "The minecraft version cache is corrupted. Flushing cache.";
+ localIndex.remove();
+ return;
+ }
+ m_hasLocalIndex = true;
+}
+
+void MinecraftVersionList::loadBuiltinList()
+{
+ QLOG_INFO() << "Loading builtin version list.";
+ // grab the version list data from internal resources.
+ QResource versionList(":/versions/minecraft.json");
+ QFile filez(versionList.absoluteFilePath());
+ filez.open(QIODevice::ReadOnly);
+ auto data = filez.readAll();
+
+ // parse the data as json
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ QJsonObject root = jsonDoc.object();
+
+ // parse all the versions
+ for (const auto version : MMCJson::ensureArray(root.value("versions")))
+ {
+ QJsonObject versionObj = version.toObject();
+ QString versionID = versionObj.value("id").toString("");
+ QString versionTypeStr = versionObj.value("type").toString("");
+ if (versionID.isEmpty() || versionTypeStr.isEmpty())
+ {
+ QLOG_ERROR() << "Parsed version is missing ID or type";
+ continue;
+ }
+
+ if (g_VersionFilterData.legacyBlacklist.contains(versionID))
+ {
+ QLOG_WARN() << "Blacklisted legacy version ignored: " << versionID;
+ continue;
+ }
+
+ // Now, we construct the version object and add it to the list.
+ std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
+ mcVersion->m_name = mcVersion->m_descriptor = versionID;
+
+ // Parse the timestamp.
+ if (!parse_timestamp(versionObj.value("releaseTime").toString(""),
+ mcVersion->m_releaseTimeString, mcVersion->m_releaseTime))
+ {
+ QLOG_ERROR() << "Error while parsing version" << versionID
+ << ": invalid version timestamp";
+ continue;
+ }
+
+ // Get the download URL.
+ mcVersion->download_url =
+ "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
+
+ mcVersion->m_versionSource = Builtin;
+ mcVersion->m_type = versionTypeStr;
+ mcVersion->m_appletClass = versionObj.value("appletClass").toString("");
+ mcVersion->m_mainClass = versionObj.value("mainClass").toString("");
+ mcVersion->m_jarChecksum = versionObj.value("checksum").toString("");
+ mcVersion->m_processArguments = versionObj.value("processArguments").toString("legacy");
+ if (versionObj.contains("+traits"))
+ {
+ for (auto traitVal : MMCJson::ensureArray(versionObj.value("+traits")))
+ {
+ mcVersion->m_traits.insert(MMCJson::ensureString(traitVal));
+ }
+ }
+ m_lookup[versionID] = mcVersion;
+ m_vlist.append(mcVersion);
+ }
+}
+
+void MinecraftVersionList::loadMojangList(QJsonDocument jsonDoc, VersionSource source)
+{
+ QLOG_INFO() << "Loading" << ((source == Remote) ? "remote" : "local") << "version list.";
+
+ if (!jsonDoc.isObject())
+ {
+ throw ListLoadError(tr("Error parsing version list JSON: jsonDoc is not an object"));
+ }
+
+ QJsonObject root = jsonDoc.object();
+
+ try
+ {
+ QJsonObject latest = MMCJson::ensureObject(root.value("latest"));
+ m_latestReleaseID = MMCJson::ensureString(latest.value("release"));
+ m_latestSnapshotID = MMCJson::ensureString(latest.value("snapshot"));
+ }
+ catch (MMCError &err)
+ {
+ QLOG_ERROR()
+ << tr("Error parsing version list JSON: couldn't determine latest versions");
+ }
+
+ // Now, get the array of versions.
+ if (!root.value("versions").isArray())
+ {
+ throw ListLoadError(tr("Error parsing version list JSON: version list object is "
+ "missing 'versions' array"));
+ }
+ QJsonArray versions = root.value("versions").toArray();
+
+ QList<BaseVersionPtr> tempList;
+ for (auto version : versions)
+ {
+ // Load the version info.
+ if (!version.isObject())
+ {
+ QLOG_ERROR() << "Error while parsing version list : invalid JSON structure";
+ continue;
+ }
+
+ QJsonObject versionObj = version.toObject();
+ QString versionID = versionObj.value("id").toString("");
+ if (versionID.isEmpty())
+ {
+ QLOG_ERROR() << "Error while parsing version : version ID is missing";
+ continue;
+ }
+
+ if (g_VersionFilterData.legacyBlacklist.contains(versionID))
+ {
+ QLOG_WARN() << "Blacklisted legacy version ignored: " << versionID;
+ continue;
+ }
+
+ // Now, we construct the version object and add it to the list.
+ std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
+ mcVersion->m_name = mcVersion->m_descriptor = versionID;
+
+ if (!parse_timestamp(versionObj.value("releaseTime").toString(""),
+ mcVersion->m_releaseTimeString, mcVersion->m_releaseTime))
+ {
+ QLOG_ERROR() << "Error while parsing version" << versionID
+ << ": invalid release timestamp";
+ continue;
+ }
+ if (!parse_timestamp(versionObj.value("time").toString(""),
+ mcVersion->m_updateTimeString, mcVersion->m_updateTime))
+ {
+ QLOG_ERROR() << "Error while parsing version" << versionID
+ << ": invalid update timestamp";
+ continue;
+ }
+
+ if (mcVersion->m_releaseTime < g_VersionFilterData.legacyCutoffDate)
+ {
+ continue;
+ }
+
+ // depends on where we load the version from -- network request or local file?
+ mcVersion->m_versionSource = source;
+
+ QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
+ mcVersion->download_url = dlUrl;
+ QString versionTypeStr = versionObj.value("type").toString("");
+ if (versionTypeStr.isEmpty())
+ {
+ // FIXME: log this somewhere
+ continue;
+ }
+ // OneSix or Legacy. use filter to determine type
+ if (versionTypeStr == "release")
+ {
+ }
+ else if (versionTypeStr == "snapshot") // It's a snapshot... yay
+ {
+ }
+ else if (versionTypeStr == "old_alpha")
+ {
+ }
+ else if (versionTypeStr == "old_beta")
+ {
+ }
+ else
+ {
+ // FIXME: log this somewhere
+ continue;
+ }
+ mcVersion->m_type = versionTypeStr;
+ tempList.append(mcVersion);
+ }
+ updateListData(tempList);
+ if(source == Remote)
+ {
+ m_loaded = true;
+ }
+}
+
+void MinecraftVersionList::sort()
+{
+ beginResetModel();
+ sortInternal();
+ endResetModel();
+}
+
+BaseVersionPtr MinecraftVersionList::getLatestStable() const
+{
+ if(m_lookup.contains(m_latestReleaseID))
+ return m_lookup[m_latestReleaseID];
+ return BaseVersionPtr();
+}
+
+void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions)
+{
+ beginResetModel();
+ for (auto version : versions)
+ {
+ auto descr = version->descriptor();
+
+ if (!m_lookup.contains(descr))
+ {
+ m_lookup[version->descriptor()] = version;
+ m_vlist.append(version);
+ continue;
+ }
+ auto orig = std::dynamic_pointer_cast<MinecraftVersion>(m_lookup[descr]);
+ auto added = std::dynamic_pointer_cast<MinecraftVersion>(version);
+ // updateListData is called after Mojang list loads. those can be local or remote
+ // remote comes always after local
+ // any other options are ignored
+ if (orig->m_versionSource != Local || added->m_versionSource != Remote)
+ {
+ continue;
+ }
+ // is it actually an update?
+ if (orig->m_updateTime >= added->m_updateTime)
+ {
+ // nope.
+ continue;
+ }
+ // alright, it's an update. put it inside the original, for further processing.
+ orig->upstreamUpdate = added;
+ }
+ sortInternal();
+ endResetModel();
+}
+
+inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
+{
+ QDomNodeList elementList = parent.elementsByTagName(tagname);
+ if (elementList.count())
+ return elementList.at(0).toElement();
+ else
+ return QDomElement();
+}
+
+MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
+{
+ m_list = vlist;
+ m_currentStable = NULL;
+ vlistReply = nullptr;
+}
+
+void MCVListLoadTask::executeTask()
+{
+ setStatus(tr("Loading instance version list..."));
+ auto worker = MMC->qnam();
+ vlistReply = worker->get(QNetworkRequest(
+ QUrl("http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + "versions.json")));
+ connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded()));
+}
+
+void MCVListLoadTask::list_downloaded()
+{
+ if (vlistReply->error() != QNetworkReply::NoError)
+ {
+ vlistReply->deleteLater();
+ emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString());
+ return;
+ }
+
+ auto data = vlistReply->readAll();
+ vlistReply->deleteLater();
+ try
+ {
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ throw ListLoadError(
+ tr("Error parsing version list JSON: %1").arg(jsonError.errorString()));
+ }
+ m_list->loadMojangList(jsonDoc, Remote);
+ }
+ catch (MMCError &e)
+ {
+ emitFailed(e.cause());
+ return;
+ }
+
+ emitSucceeded();
+ return;
+}
+
+MCVListVersionUpdateTask::MCVListVersionUpdateTask(MinecraftVersionList *vlist,
+ QString updatedVersion)
+ : Task()
+{
+ m_list = vlist;
+ versionToUpdate = updatedVersion;
+}
+
+void MCVListVersionUpdateTask::executeTask()
+{
+ QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionToUpdate + "/" +
+ versionToUpdate + ".json";
+ auto job = new NetJob("Version index");
+ job->addNetAction(ByteArrayDownload::make(QUrl(urlstr)));
+ specificVersionDownloadJob.reset(job);
+ connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(json_downloaded()));
+ connect(specificVersionDownloadJob.get(), SIGNAL(failed(QString)), SIGNAL(failed(QString)));
+ connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
+ specificVersionDownloadJob->start();
+}
+
+void MCVListVersionUpdateTask::json_downloaded()
+{
+ NetActionPtr DlJob = specificVersionDownloadJob->first();
+ auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data;
+ specificVersionDownloadJob.reset();
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ emitFailed(tr("The download version file is not valid."));
+ return;
+ }
+ VersionFilePtr file;
+ try
+ {
+ file = VersionFile::fromJson(jsonDoc, "net.minecraft.json", false);
+ }
+ catch (MMCError &e)
+ {
+ emitFailed(tr("Couldn't process version file: %1").arg(e.cause()));
+ return;
+ }
+ QList<RawLibraryPtr> filteredLibs;
+ QList<RawLibraryPtr> lwjglLibs;
+ QSet<QString> lwjglFilter = {
+ "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"};
+ for (auto lib : file->overwriteLibs)
+ {
+ if (lwjglFilter.contains(lib->fullname()))
+ {
+ lwjglLibs.append(lib);
+ }
+ else
+ {
+ filteredLibs.append(lib);
+ }
+ }
+ file->overwriteLibs = filteredLibs;
+
+ // TODO: recognize and add LWJGL versions here.
+
+ file->fileId = "net.minecraft";
+
+ // now dump the file to disk
+ auto doc = file->toJson(false);
+ auto newdata = doc.toBinaryData();
+ QLOG_INFO() << newdata;
+ QString targetPath = "versions/" + versionToUpdate + "/" + versionToUpdate + ".dat";
+ ensureFilePathExists(targetPath);
+ QSaveFile vfile1(targetPath);
+ if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
+ {
+ emitFailed(tr("Can't open %1 for writing.").arg(targetPath));
+ return;
+ }
+ qint64 actual = 0;
+ if ((actual = vfile1.write(newdata)) != newdata.size())
+ {
+ emitFailed(tr("Failed to write into %1. Written %2 out of %3.")
+ .arg(targetPath)
+ .arg(actual)
+ .arg(newdata.size()));
+ return;
+ }
+ if (!vfile1.commit())
+ {
+ emitFailed(tr("Can't commit changes to %1").arg(targetPath));
+ return;
+ }
+
+ m_list->finalizeUpdate(versionToUpdate);
+ emitSucceeded();
+}
+
+std::shared_ptr<Task> MinecraftVersionList::createUpdateTask(QString version)
+{
+ return std::shared_ptr<Task>(new MCVListVersionUpdateTask(this, version));
+}
+
+void MinecraftVersionList::saveCachedList()
+{
+ // FIXME: throw.
+ if (!ensureFilePathExists(localVersionCache))
+ return;
+ QSaveFile tfile(localVersionCache);
+ if (!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate))
+ return;
+ QJsonObject toplevel;
+ QJsonArray entriesArr;
+ for (auto version : m_vlist)
+ {
+ auto mcversion = std::dynamic_pointer_cast<MinecraftVersion>(version);
+ // do not save the remote versions.
+ if (mcversion->m_versionSource != Local)
+ continue;
+ QJsonObject entryObj;
+
+ entryObj.insert("id", mcversion->descriptor());
+ entryObj.insert("time", mcversion->m_updateTimeString);
+ entryObj.insert("releaseTime", mcversion->m_releaseTimeString);
+ entryObj.insert("type", mcversion->m_type);
+ entriesArr.append(entryObj);
+ }
+ toplevel.insert("versions", entriesArr);
+
+ {
+ bool someLatest = false;
+ QJsonObject latestObj;
+ if(!m_latestReleaseID.isNull())
+ {
+ latestObj.insert("release", m_latestReleaseID);
+ someLatest = true;
+ }
+ if(!m_latestSnapshotID.isNull())
+ {
+ latestObj.insert("snapshot", m_latestSnapshotID);
+ someLatest = true;
+ }
+ if(someLatest)
+ {
+ toplevel.insert("latest", latestObj);
+ }
+ }
+
+ QJsonDocument doc(toplevel);
+ QByteArray jsonData = doc.toBinaryData();
+ qint64 result = tfile.write(jsonData);
+ if (result == -1)
+ return;
+ if (result != jsonData.size())
+ return;
+ tfile.commit();
+}
+
+void MinecraftVersionList::finalizeUpdate(QString version)
+{
+ int idx = -1;
+ for (int i = 0; i < m_vlist.size(); i++)
+ {
+ if (version == m_vlist[i]->descriptor())
+ {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ {
+ return;
+ }
+
+ auto updatedVersion = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist[idx]);
+
+ if (updatedVersion->m_versionSource == Builtin)
+ return;
+
+ if (updatedVersion->upstreamUpdate)
+ {
+ auto updatedWith = updatedVersion->upstreamUpdate;
+ updatedWith->m_versionSource = Local;
+ m_vlist[idx] = updatedWith;
+ m_lookup[version] = updatedWith;
+ }
+ else
+ {
+ updatedVersion->m_versionSource = Local;
+ }
+
+ dataChanged(index(idx), index(idx));
+
+ saveCachedList();
+}
diff --git a/logic/lists/MinecraftVersionList.h b/logic/minecraft/MinecraftVersionList.h
index 167f4d11..4753ce05 100644
--- a/logic/lists/MinecraftVersionList.h
+++ b/logic/minecraft/MinecraftVersionList.h
@@ -19,11 +19,13 @@
#include <QList>
#include <QSet>
-#include "BaseVersionList.h"
+#include "logic/BaseVersionList.h"
#include "logic/tasks/Task.h"
-#include "logic/MinecraftVersion.h"
+#include "logic/minecraft/MinecraftVersion.h"
+#include <logic/net/NetJob.h>
class MCVListLoadTask;
+class MCVListVersionUpdateTask;
class QNetworkReply;
class MinecraftVersionList : public BaseVersionList
@@ -31,11 +33,19 @@ class MinecraftVersionList : public BaseVersionList
Q_OBJECT
private:
void sortInternal();
+ void loadBuiltinList();
+ void loadMojangList(QJsonDocument jsonDoc, VersionSource source);
+ void loadCachedList();
+ void saveCachedList();
+ void finalizeUpdate(QString version);
public:
friend class MCVListLoadTask;
+ friend class MCVListVersionUpdateTask;
explicit MinecraftVersionList(QObject *parent = 0);
+ std::shared_ptr<Task> createUpdateTask(QString version);
+
virtual Task *getLoadTask();
virtual bool isLoaded();
virtual const BaseVersionPtr at(int i) const;
@@ -46,8 +56,12 @@ public:
protected:
QList<BaseVersionPtr> m_vlist;
+ QMap<QString, BaseVersionPtr> m_lookup;
bool m_loaded = false;
+ bool m_hasLocalIndex = false;
+ QString m_latestReleaseID = "INVALID";
+ QString m_latestSnapshotID = "INVALID";
protected
slots:
@@ -60,9 +74,9 @@ class MCVListLoadTask : public Task
public:
explicit MCVListLoadTask(MinecraftVersionList *vlist);
- ~MCVListLoadTask();
+ virtual ~MCVListLoadTask() override{};
- virtual void executeTask();
+ virtual void executeTask() override;
protected
slots:
@@ -72,5 +86,23 @@ protected:
QNetworkReply *vlistReply;
MinecraftVersionList *m_list;
MinecraftVersion *m_currentStable;
- QSet<QString> legacyWhitelist;
+};
+
+class MCVListVersionUpdateTask : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit MCVListVersionUpdateTask(MinecraftVersionList *vlist, QString updatedVersion);
+ virtual ~MCVListVersionUpdateTask() override{};
+ virtual void executeTask() override;
+
+protected
+slots:
+ void json_downloaded();
+
+protected:
+ NetJobPtr specificVersionDownloadJob;
+ QString versionToUpdate;
+ MinecraftVersionList *m_list;
};
diff --git a/logic/OneSixLibrary.cpp b/logic/minecraft/OneSixLibrary.cpp
index 45fa169e..7f69d9f8 100644
--- a/logic/OneSixLibrary.cpp
+++ b/logic/minecraft/OneSixLibrary.cpp
@@ -23,6 +23,23 @@
#include <JlCompress.h>
#include "logger/QsLog.h"
+OneSixLibrary::OneSixLibrary(RawLibraryPtr base)
+{
+ m_name = base->m_name;
+ m_base_url = base->m_base_url;
+ m_hint = base->m_hint;
+ m_absolute_url = base->m_absolute_url;
+ extract_excludes = base->extract_excludes;
+ m_native_suffixes = base->m_native_suffixes;
+ m_rules = base->m_rules;
+ finalize();
+}
+
+OneSixLibraryPtr OneSixLibrary::fromRawLibrary(RawLibraryPtr lib)
+{
+ return OneSixLibraryPtr(new OneSixLibrary(lib));
+}
+
void OneSixLibrary::finalize()
{
QStringList parts = m_name.split(':');
@@ -30,7 +47,7 @@ void OneSixLibrary::finalize()
relative.replace('.', '/');
relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2];
- if (!m_is_native)
+ if (!isNative())
relative += ".jar";
else
{
@@ -65,7 +82,7 @@ void OneSixLibrary::finalize()
}
m_is_active = (result == Allow);
}
- if (m_is_native)
+ if (isNative())
{
m_is_active = m_is_active && m_native_suffixes.contains(currentSystem);
m_decenttype = "Native";
@@ -84,13 +101,8 @@ void OneSixLibrary::setBaseUrl(const QString &base_url)
{
m_base_url = base_url;
}
-void OneSixLibrary::setIsNative()
-{
- m_is_native = true;
-}
void OneSixLibrary::addNative(OpSys os, const QString &suffix)
{
- m_is_native = true;
m_native_suffixes[os] = suffix;
}
void OneSixLibrary::clearSuffixes()
@@ -105,10 +117,6 @@ bool OneSixLibrary::isActive() const
{
return m_is_active;
}
-bool OneSixLibrary::isNative() const
-{
- return m_is_native;
-}
QString OneSixLibrary::downloadUrl() const
{
if (m_absolute_url.size())
@@ -223,52 +231,3 @@ bool OneSixLibrary::extractTo(QString target_dir)
}
return true;
}
-
-QJsonObject OneSixLibrary::toJson()
-{
- QJsonObject libRoot;
- libRoot.insert("name", m_name);
- if (m_absolute_url.size())
- libRoot.insert("MMC-absoluteUrl", m_absolute_url);
- if (m_hint.size())
- libRoot.insert("MMC-hint", m_hint);
- if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
- m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
- m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty())
- {
- libRoot.insert("url", m_base_url);
- }
- if (isNative() && m_native_suffixes.size())
- {
- QJsonObject nativeList;
- auto iter = m_native_suffixes.begin();
- while (iter != m_native_suffixes.end())
- {
- nativeList.insert(OpSys_toString(iter.key()), iter.value());
- iter++;
- }
- libRoot.insert("natives", nativeList);
- }
- if (isNative() && extract_excludes.size())
- {
- QJsonArray excludes;
- QJsonObject extract;
- for (auto exclude : extract_excludes)
- {
- excludes.append(exclude);
- }
- extract.insert("exclude", excludes);
- libRoot.insert("extract", extract);
- }
- if (m_rules.size())
- {
- QJsonArray allRules;
- for (auto &rule : m_rules)
- {
- QJsonObject ruleObj = rule->toJson();
- allRules.append(ruleObj);
- }
- libRoot.insert("rules", allRules);
- }
- return libRoot;
-}
diff --git a/logic/OneSixLibrary.h b/logic/minecraft/OneSixLibrary.h
index 61d4c8e2..3d38985b 100644
--- a/logic/OneSixLibrary.h
+++ b/logic/minecraft/OneSixLibrary.h
@@ -23,28 +23,17 @@
#include <memory>
#include "logic/net/URLConstants.h"
-#include "OpSys.h"
+#include "logic/minecraft/OpSys.h"
+#include "logic/minecraft/RawLibrary.h"
class Rule;
class OneSixLibrary;
typedef std::shared_ptr<OneSixLibrary> OneSixLibraryPtr;
-class OneSixLibrary
+class OneSixLibrary : public RawLibrary
{
private:
- // basic values used internally (so far)
- QString m_name;
- QString m_base_url = "https://" + URLConstants::LIBRARY_BASE;
- QList<std::shared_ptr<Rule>> m_rules;
-
- // custom values
- /// absolute URL. takes precedence over m_download_path, if defined
- QString m_absolute_url;
- /// download hint - how to actually get the library
- QString m_hint;
-
- // derived values used for real things
/// a decent name fit for display
QString m_decentname;
/// a decent version fit for display
@@ -57,13 +46,9 @@ private:
QString m_download_url;
/// is this lib actually active on the current OS?
bool m_is_active = false;
- /// is the library a native?
- bool m_is_native = false;
- /// native suffixes per OS
- QMap<OpSys, QString> m_native_suffixes;
public:
- QStringList extract_excludes;
+
QString minVersion;
enum DependType
@@ -80,15 +65,16 @@ public:
m_name = name;
dependType = type;
}
-
+ /// Constructor
+ OneSixLibrary(RawLibraryPtr base);
+ static OneSixLibraryPtr fromRawLibrary(RawLibraryPtr lib);
+
/// Returns the raw name field
QString rawName() const
{
return m_name;
}
- QJsonObject toJson();
-
/**
* finalize the library, processing the input values into derived values and state
*
@@ -116,8 +102,6 @@ public:
/// Set the url base for downloads
void setBaseUrl(const QString &base_url);
- /// Call this to mark the library as 'native' (it's a zip archive with DLLs)
- void setIsNative();
/// Attach a name suffix to the specified OS native
void addNative(OpSys os, const QString &suffix);
/// Clears all suffixes
@@ -127,8 +111,6 @@ public:
/// Returns true if the library should be loaded (or extracted, in case of natives)
bool isActive() const;
- /// Returns true if the library is native
- bool isNative() const;
/// Get the URL to download the library from
QString downloadUrl() const;
/// Get the relative path where the library should be saved
diff --git a/logic/OneSixRule.cpp b/logic/minecraft/OneSixRule.cpp
index d8d13b50..93c49e5e 100644
--- a/logic/OneSixRule.cpp
+++ b/logic/minecraft/OneSixRule.cpp
@@ -18,6 +18,15 @@
#include "OneSixRule.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;
@@ -79,11 +88,3 @@ QJsonObject OsRule::toJson()
return ruleObj;
}
-RuleAction RuleAction_fromString(QString name)
-{
- if (name == "allow")
- return Allow;
- if (name == "disallow")
- return Disallow;
- return Defer;
-}
diff --git a/logic/OneSixRule.h b/logic/minecraft/OneSixRule.h
index 426e2886..a18093b0 100644
--- a/logic/OneSixRule.h
+++ b/logic/minecraft/OneSixRule.h
@@ -16,8 +16,13 @@
#pragma once
#include <QString>
+#include <QList>
+#include <QJsonObject>
+#include <memory>
+#include "OpSys.h"
-#include "logic/OneSixLibrary.h"
+class OneSixLibrary;
+class Rule;
enum RuleAction
{
@@ -26,7 +31,6 @@ enum RuleAction
Defer
};
-RuleAction RuleAction_fromString(QString);
QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules);
class Rule
diff --git a/logic/OpSys.cpp b/logic/minecraft/OpSys.cpp
index e001b7f3..e001b7f3 100644
--- a/logic/OpSys.cpp
+++ b/logic/minecraft/OpSys.cpp
diff --git a/logic/OpSys.h b/logic/minecraft/OpSys.h
index 363c87d7..363c87d7 100644
--- a/logic/OpSys.h
+++ b/logic/minecraft/OpSys.h
diff --git a/logic/minecraft/ParseUtils.cpp b/logic/minecraft/ParseUtils.cpp
new file mode 100644
index 00000000..f94de6ff
--- /dev/null
+++ b/logic/minecraft/ParseUtils.cpp
@@ -0,0 +1,24 @@
+#include <QDateTime>
+#include <QString>
+#include "ParseUtils.h"
+#include <logic/MMCJson.h>
+
+QDateTime timeFromS3Time(QString str)
+{
+ return QDateTime::fromString(str, Qt::ISODate);
+}
+
+bool parse_timestamp (const QString & raw, QString & save_here, QDateTime & parse_here)
+{
+ save_here = raw;
+ if (save_here.isEmpty())
+ {
+ return false;
+ }
+ parse_here = timeFromS3Time(save_here);
+ if (!parse_here.isValid())
+ {
+ return false;
+ }
+ return true;
+}
diff --git a/logic/minecraft/ParseUtils.h b/logic/minecraft/ParseUtils.h
new file mode 100644
index 00000000..bd2f6ffb
--- /dev/null
+++ b/logic/minecraft/ParseUtils.h
@@ -0,0 +1,14 @@
+#pragma once
+#include <QString>
+#include <QDateTime>
+
+/**
+ * parse the S3 timestamp in 'raw' and fill the forwarded variables.
+ * return true/false for success/failure
+ */
+bool parse_timestamp (const QString &raw, QString &save_here, QDateTime &parse_here);
+
+/**
+ * take the timestamp used by S3 and turn it into QDateTime
+ */
+QDateTime timeFromS3Time(QString str);
diff --git a/logic/minecraft/RawLibrary.cpp b/logic/minecraft/RawLibrary.cpp
new file mode 100644
index 00000000..7e0ebff0
--- /dev/null
+++ b/logic/minecraft/RawLibrary.cpp
@@ -0,0 +1,205 @@
+#include "logic/MMCJson.h"
+using namespace MMCJson;
+
+#include "RawLibrary.h"
+
+RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &filename)
+{
+ RawLibraryPtr out(new RawLibrary());
+ if (!libObj.contains("name"))
+ {
+ throw JSONValidationError(filename +
+ "contains a library that doesn't have a 'name' field");
+ }
+ out->m_name = libObj.value("name").toString();
+
+ auto readString = [libObj, filename](const QString & key, QString & variable)
+ {
+ if (libObj.contains(key))
+ {
+ QJsonValue val = libObj.value(key);
+ if (!val.isString())
+ {
+ QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
+ }
+ else
+ {
+ variable = val.toString();
+ }
+ }
+ };
+
+ readString("url", out->m_base_url);
+ readString("MMC-hint", out->m_hint);
+ readString("MMC-absulute_url", out->m_absolute_url);
+ readString("MMC-absoluteUrl", out->m_absolute_url);
+ if (libObj.contains("extract"))
+ {
+ out->applyExcludes = true;
+ auto extractObj = ensureObject(libObj.value("extract"));
+ for (auto excludeVal : ensureArray(extractObj.value("exclude")))
+ {
+ out->extract_excludes.append(ensureString(excludeVal));
+ }
+ }
+ if (libObj.contains("natives"))
+ {
+ QJsonObject nativesObj = ensureObject(libObj.value("natives"));
+ for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
+ {
+ if (!it.value().isString())
+ {
+ QLOG_WARN() << filename << "contains an invalid native (skipping)";
+ }
+ OpSys opSys = OpSys_fromString(it.key());
+ if (opSys != Os_Other)
+ {
+ out->m_native_suffixes[opSys] = it.value().toString();
+ }
+ }
+ }
+ if (libObj.contains("rules"))
+ {
+ out->applyRules = true;
+ out->m_rules = rulesFromJsonV4(libObj);
+ }
+ return out;
+}
+
+RawLibraryPtr RawLibrary::fromJsonPlus(const QJsonObject &libObj, const QString &filename)
+{
+ auto lib = RawLibrary::fromJson(libObj, filename);
+ if (libObj.contains("insert"))
+ {
+ QJsonValue insertVal = ensureExists(libObj.value("insert"), "library insert rule");
+ QString insertString;
+ {
+ if (insertVal.isString())
+ {
+ insertString = insertVal.toString();
+ }
+ else if (insertVal.isObject())
+ {
+ QJsonObject insertObj = insertVal.toObject();
+ if (insertObj.isEmpty())
+ {
+ throw JSONValidationError("One library has an empty insert object in " +
+ filename);
+ }
+ insertString = insertObj.keys().first();
+ lib->insertData = insertObj.value(insertString).toString();
+ }
+ }
+ if (insertString == "apply")
+ {
+ lib->insertType = RawLibrary::Apply;
+ }
+ else if (insertString == "prepend")
+ {
+ lib->insertType = RawLibrary::Prepend;
+ }
+ else if (insertString == "append")
+ {
+ lib->insertType = RawLibrary::Append;
+ }
+ else if (insertString == "replace")
+ {
+ lib->insertType = RawLibrary::Replace;
+ }
+ else
+ {
+ throw JSONValidationError("A '+' library in " + filename +
+ " contains an invalid insert type");
+ }
+ }
+ if (libObj.contains("MMC-depend"))
+ {
+ const QString dependString = ensureString(libObj.value("MMC-depend"));
+ if (dependString == "hard")
+ {
+ lib->dependType = RawLibrary::Hard;
+ }
+ else if (dependString == "soft")
+ {
+ lib->dependType = RawLibrary::Soft;
+ }
+ else
+ {
+ throw JSONValidationError("A '+' library in " + filename +
+ " contains an invalid depend type");
+ }
+ }
+ return lib;
+}
+
+bool RawLibrary::isNative() const
+{
+ return m_native_suffixes.size() != 0;
+}
+
+QJsonObject RawLibrary::toJson()
+{
+ QJsonObject libRoot;
+ libRoot.insert("name", m_name);
+ if (m_absolute_url.size())
+ libRoot.insert("MMC-absoluteUrl", m_absolute_url);
+ if (m_hint.size())
+ libRoot.insert("MMC-hint", m_hint);
+ if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
+ m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
+ m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty())
+ {
+ libRoot.insert("url", m_base_url);
+ }
+ if (isNative())
+ {
+ QJsonObject nativeList;
+ auto iter = m_native_suffixes.begin();
+ while (iter != m_native_suffixes.end())
+ {
+ nativeList.insert(OpSys_toString(iter.key()), iter.value());
+ iter++;
+ }
+ libRoot.insert("natives", nativeList);
+ if (extract_excludes.size())
+ {
+ QJsonArray excludes;
+ QJsonObject extract;
+ for (auto exclude : extract_excludes)
+ {
+ excludes.append(exclude);
+ }
+ extract.insert("exclude", excludes);
+ libRoot.insert("extract", extract);
+ }
+ }
+ if (m_rules.size())
+ {
+ QJsonArray allRules;
+ for (auto &rule : m_rules)
+ {
+ QJsonObject ruleObj = rule->toJson();
+ allRules.append(ruleObj);
+ }
+ libRoot.insert("rules", allRules);
+ }
+ return libRoot;
+}
+
+QString RawLibrary::fullname()
+{
+ QStringList parts = m_name.split(':');
+ return parts[0] + ":" + parts[1];
+}
+
+QString RawLibrary::group()
+{
+ QStringList parts = m_name.split(':');
+ return parts[0];
+}
+
+QString RawLibrary::version()
+{
+ QStringList parts = m_name.split(':');
+ return parts[2];
+}
diff --git a/logic/minecraft/RawLibrary.h b/logic/minecraft/RawLibrary.h
new file mode 100644
index 00000000..f5a28c61
--- /dev/null
+++ b/logic/minecraft/RawLibrary.h
@@ -0,0 +1,64 @@
+#pragma once
+#include <QString>
+#include <QPair>
+#include <QList>
+#include <QStringList>
+#include <QMap>
+#include <memory>
+
+#include "logic/minecraft/OneSixRule.h"
+#include "logic/minecraft/OpSys.h"
+#include "logic/net/URLConstants.h"
+
+class RawLibrary;
+typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
+
+class RawLibrary
+{
+public: /* methods */
+ /// read and create a basic library
+ static RawLibraryPtr fromJson(const QJsonObject &libObj, const QString &filename);
+ /// read and create a MultiMC '+' library. Those have some extra fields.
+ static RawLibraryPtr fromJsonPlus(const QJsonObject &libObj, const QString &filename);
+ QJsonObject toJson();
+
+ QString fullname();
+ QString version();
+ QString group();
+
+public: /* data */
+ QString m_name;
+ QString m_base_url = "https://" + URLConstants::LIBRARY_BASE;
+ /// type hint - modifies how the library is treated
+ QString m_hint;
+ /// absolute URL. takes precedence over m_download_path, if defined
+ QString m_absolute_url;
+
+ bool applyExcludes = false;
+ QStringList extract_excludes;
+
+ /// Returns true if the library is native
+ bool isNative() const;
+ /// native suffixes per OS
+ QMap<OpSys, QString> m_native_suffixes;
+
+ bool applyRules = false;
+ QList<std::shared_ptr<Rule>> m_rules;
+
+ // used for '+' libraries
+ enum InsertType
+ {
+ Apply,
+ Append,
+ Prepend,
+ Replace
+ } insertType = Append;
+ QString insertData;
+
+ // soft or hard dependency? hard means 'needs equal', soft means 'needs equal or newer'
+ enum DependType
+ {
+ Soft,
+ Hard
+ } dependType = Soft;
+};
diff --git a/logic/minecraft/VersionBuildError.h b/logic/minecraft/VersionBuildError.h
new file mode 100644
index 00000000..ae479851
--- /dev/null
+++ b/logic/minecraft/VersionBuildError.h
@@ -0,0 +1,58 @@
+#include "MMCError.h"
+
+class VersionBuildError : public MMCError
+{
+public:
+ VersionBuildError(QString cause) : MMCError(cause) {};
+ virtual ~VersionBuildError() noexcept
+ {
+ }
+};
+
+/**
+ * the base version file was meant for a newer version of the vanilla launcher than we support
+ */
+class LauncherVersionError : public VersionBuildError
+{
+public:
+ LauncherVersionError(int actual, int supported)
+ : VersionBuildError(QObject::tr(
+ "The base version file of this instance was meant for a newer (%1) "
+ "version of the vanilla launcher than this version of MultiMC supports (%2).")
+ .arg(actual)
+ .arg(supported)) {};
+ virtual ~LauncherVersionError() noexcept
+ {
+ }
+};
+
+/**
+ * some patch was intended for a different version of minecraft
+ */
+class MinecraftVersionMismatch : public VersionBuildError
+{
+public:
+ MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion)
+ : VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft "
+ "(%2) than that of the instance (%3).")
+ .arg(fileId)
+ .arg(mcVersion)
+ .arg(parentMcVersion)) {};
+ virtual ~MinecraftVersionMismatch() noexcept
+ {
+ }
+};
+
+/**
+ * files required for the version are not (yet?) present
+ */
+class VersionIncomplete : public VersionBuildError
+{
+public:
+ VersionIncomplete(QString missingPatch)
+ : VersionBuildError(QObject::tr("Version is incomplete: missing %1.")
+ .arg(missingPatch)) {};
+ virtual ~VersionIncomplete() noexcept
+ {
+ }
+}; \ No newline at end of file
diff --git a/logic/minecraft/VersionBuilder.cpp b/logic/minecraft/VersionBuilder.cpp
new file mode 100644
index 00000000..ddcf6333
--- /dev/null
+++ b/logic/minecraft/VersionBuilder.cpp
@@ -0,0 +1,349 @@
+/* Copyright 2013 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 <QList>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QFile>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <QObject>
+#include <QDir>
+#include <QDebug>
+#include <qresource.h>
+#include <modutils.h>
+
+#include "MultiMC.h"
+#include "logic/minecraft/VersionBuilder.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixRule.h"
+#include "logic/minecraft/VersionPatch.h"
+#include "logic/minecraft/VersionFile.h"
+#include "VersionBuildError.h"
+#include "MinecraftVersionList.h"
+
+#include "logic/OneSixInstance.h"
+#include "logic/MMCJson.h"
+
+#include "logger/QsLog.h"
+
+VersionBuilder::VersionBuilder()
+{
+}
+
+void VersionBuilder::build(InstanceVersion *version, OneSixInstance *instance,
+ const QStringList &external)
+{
+ VersionBuilder builder;
+ builder.m_version = version;
+ builder.m_instance = instance;
+ builder.external_patches = external;
+ builder.buildInternal();
+}
+
+void VersionBuilder::readJsonAndApplyToVersion(InstanceVersion *version, const QJsonObject &obj)
+{
+ VersionBuilder builder;
+ builder.m_version = version;
+ builder.m_instance = 0;
+ builder.readJsonAndApply(obj);
+}
+
+void VersionBuilder::buildFromCustomJson()
+{
+ QLOG_INFO() << "Building version from custom.json within the instance.";
+ QLOG_INFO() << "Reading custom.json";
+ auto file = parseJsonFile(QFileInfo(instance_root.absoluteFilePath("custom.json")), false);
+ file->name = "custom.json";
+ file->filename = "custom.json";
+ file->fileId = "org.multimc.custom.json";
+ file->order = -1;
+ file->version = QString();
+ m_version->VersionPatches.append(file);
+ m_version->finalize();
+ return;
+}
+
+void VersionBuilder::buildFromVersionJson()
+{
+ QLOG_INFO() << "Building version from version.json and patches within the instance.";
+ QLOG_INFO() << "Reading version.json";
+ auto file = parseJsonFile(QFileInfo(instance_root.absoluteFilePath("version.json")), false);
+ file->name = "Minecraft";
+ file->fileId = "org.multimc.version.json";
+ file->order = -1;
+ file->version = m_instance->intendedVersionId();
+ file->mcVersion = m_instance->intendedVersionId();
+ m_version->VersionPatches.append(file);
+
+ // load all patches, put into map for ordering, apply in the right order
+ readInstancePatches();
+
+ // some final touches
+ m_version->finalize();
+}
+
+void VersionBuilder::readInstancePatches()
+{
+ PatchOrder userOrder;
+ readOverrideOrders(m_instance, userOrder);
+ QDir patches(instance_root.absoluteFilePath("patches/"));
+
+ // first, load things by sort order.
+ for (auto id : userOrder)
+ {
+ // ignore builtins
+ if (id == "net.minecraft")
+ continue;
+ if (id == "org.lwjgl")
+ continue;
+ // parse the file
+ QString filename = patches.absoluteFilePath(id + ".json");
+ QLOG_INFO() << "Reading" << filename << "by user order";
+ auto file = parseJsonFile(QFileInfo(filename), false);
+ // sanity check. prevent tampering with files.
+ if (file->fileId != id)
+ {
+ throw VersionBuildError(
+ QObject::tr("load id %1 does not match internal id %2").arg(id, file->fileId));
+ }
+ m_version->VersionPatches.append(file);
+ }
+ // now load the rest by internal preference.
+ QMap<int, QPair<QString, VersionFilePtr>> files;
+ for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
+ {
+ // parse the file
+ QLOG_INFO() << "Reading" << info.fileName();
+ auto file = parseJsonFile(info, true);
+ // ignore builtins
+ if (file->fileId == "net.minecraft")
+ continue;
+ if (file->fileId == "org.lwjgl")
+ continue;
+ // do not load what we already loaded in the first pass
+ if (userOrder.contains(file->fileId))
+ continue;
+ if (files.contains(file->order))
+ {
+ // FIXME: do not throw?
+ throw VersionBuildError(QObject::tr("%1 has the same order as %2")
+ .arg(file->fileId, files[file->order].second->fileId));
+ }
+ files.insert(file->order, qMakePair(info.fileName(), file));
+ }
+ for (auto order : files.keys())
+ {
+ auto &filePair = files[order];
+ m_version->VersionPatches.append(filePair.second);
+ }
+}
+
+void VersionBuilder::buildFromExternalPatches()
+{
+ QLOG_INFO() << "Building version from external files.";
+ int externalOrder = -1;
+ for (auto fileName : external_patches)
+ {
+ QLOG_INFO() << "Reading" << fileName;
+ auto file = parseJsonFile(QFileInfo(fileName), false, fileName.endsWith("pack.json"));
+ file->name = QFileInfo(fileName).fileName();
+ file->fileId = "org.multimc.external." + file->name;
+ file->order = (externalOrder += 1);
+ file->version = QString();
+ file->mcVersion = QString();
+ m_version->VersionPatches.append(file);
+ }
+ // some final touches
+ m_version->finalize();
+}
+
+void VersionBuilder::buildFromMultilayer()
+{
+ QLOG_INFO() << "Building version from multilayered sources.";
+ // just the builtin stuff for now
+ auto minecraftList = MMC->minecraftlist();
+ auto mcversion = minecraftList->findVersion(m_instance->intendedVersionId());
+ auto minecraftPatch = std::dynamic_pointer_cast<VersionPatch>(mcversion);
+ if (!minecraftPatch)
+ {
+ throw VersionIncomplete("net.minecraft");
+ }
+ minecraftPatch->setOrder(-2);
+ m_version->VersionPatches.append(minecraftPatch);
+
+ // TODO: this is obviously fake.
+ QResource LWJGL(":/versions/LWJGL/2.9.1.json");
+ auto lwjgl = parseJsonFile(LWJGL.absoluteFilePath(), false, false);
+ auto lwjglPatch = std::dynamic_pointer_cast<VersionPatch>(lwjgl);
+ if (!lwjglPatch)
+ {
+ throw VersionIncomplete("org.lwjgl");
+ }
+ lwjglPatch->setOrder(-1);
+ m_version->VersionPatches.append(lwjglPatch);
+
+ // load all patches, put into map for ordering, apply in the right order
+ readInstancePatches();
+
+ m_version->finalize();
+}
+
+void VersionBuilder::buildInternal()
+{
+ m_version->VersionPatches.clear();
+ instance_root = QDir(m_instance->instanceRoot());
+ // if we do external files, do just those.
+ if (!external_patches.isEmpty())
+ {
+ buildFromExternalPatches();
+ }
+ // else, if there's custom json, we just do that.
+ else if (QFile::exists(instance_root.absoluteFilePath("custom.json")))
+ {
+ buildFromCustomJson();
+ }
+ // version.json -> patches/*.json
+ else if (QFile::exists(instance_root.absoluteFilePath("version.json")))
+ {
+ buildFromVersionJson();
+ }
+ else
+ {
+ buildFromMultilayer();
+ }
+}
+
+void VersionBuilder::readJsonAndApply(const QJsonObject &obj)
+{
+ m_version->clear();
+
+ auto file = VersionFile::fromJson(QJsonDocument(obj), QString(), false);
+
+ file->applyTo(m_version);
+ m_version->VersionPatches.append(file);
+}
+
+VersionFilePtr VersionBuilder::parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
+ bool isFTB)
+{
+ QFile file(fileInfo.absoluteFilePath());
+ if (!file.open(QFile::ReadOnly))
+ {
+ throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
+ .arg(fileInfo.fileName(), file.errorString()));
+ }
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
+ if (error.error != QJsonParseError::NoError)
+ {
+ throw JSONValidationError(
+ QObject::tr("Unable to process the version file %1: %2 at %3.")
+ .arg(fileInfo.fileName(), error.errorString())
+ .arg(error.offset));
+ }
+ return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB);
+}
+
+VersionFilePtr VersionBuilder::parseBinaryJsonFile(const QFileInfo &fileInfo)
+{
+ QFile file(fileInfo.absoluteFilePath());
+ if (!file.open(QFile::ReadOnly))
+ {
+ throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
+ .arg(fileInfo.fileName(), file.errorString()));
+ }
+ 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 VersionFile::fromJson(doc, file.fileName(), false, false);
+}
+
+static const int currentOrderFileVersion = 1;
+
+bool VersionBuilder::readOverrideOrders(OneSixInstance *instance, PatchOrder &order)
+{
+ QFile orderFile(instance->instanceRoot() + "/order.json");
+ if (!orderFile.open(QFile::ReadOnly))
+ {
+ QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
+ << " for reading:" << orderFile.errorString();
+ QLOG_WARN() << "Ignoring overriden order";
+ return false;
+ }
+
+ // and it's valid JSON
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
+ if (error.error != QJsonParseError::NoError)
+ {
+ QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString();
+ QLOG_WARN() << "Ignoring overriden order";
+ return false;
+ }
+
+ // and then read it and process it if all above is true.
+ try
+ {
+ auto obj = MMCJson::ensureObject(doc);
+ // check order file version.
+ auto version = MMCJson::ensureInteger(obj.value("version"), "version");
+ if (version != currentOrderFileVersion)
+ {
+ throw JSONValidationError(QObject::tr("Invalid order file version, expected %1")
+ .arg(currentOrderFileVersion));
+ }
+ auto orderArray = MMCJson::ensureArray(obj.value("order"));
+ for(auto item: orderArray)
+ {
+ order.append(MMCJson::ensureString(item));
+ }
+ }
+ catch (JSONValidationError &err)
+ {
+ QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
+ QLOG_WARN() << "Ignoring overriden order";
+ order.clear();
+ return false;
+ }
+ return true;
+}
+
+bool VersionBuilder::writeOverrideOrders(OneSixInstance *instance, const PatchOrder &order)
+{
+ QJsonObject obj;
+ obj.insert("version", currentOrderFileVersion);
+ QJsonArray orderArray;
+ for(auto str: order)
+ {
+ orderArray.append(str);
+ }
+ obj.insert("order", orderArray);
+ QFile orderFile(instance->instanceRoot() + "/order.json");
+ if (!orderFile.open(QFile::WriteOnly))
+ {
+ QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
+ << "for writing:" << orderFile.errorString();
+ return false;
+ }
+ orderFile.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
+ return true;
+}
diff --git a/logic/minecraft/VersionBuilder.h b/logic/minecraft/VersionBuilder.h
new file mode 100644
index 00000000..350179b9
--- /dev/null
+++ b/logic/minecraft/VersionBuilder.h
@@ -0,0 +1,56 @@
+/* Copyright 2013 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 "VersionFile.h"
+
+class InstanceVersion;
+class OneSixInstance;
+class QJsonObject;
+class QFileInfo;
+
+typedef QStringList PatchOrder;
+
+class VersionBuilder
+{
+ VersionBuilder();
+public:
+ static void build(InstanceVersion *version, OneSixInstance *instance, const QStringList &external);
+ static void readJsonAndApplyToVersion(InstanceVersion *version, const QJsonObject &obj);
+ static VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, bool isFTB = false);
+ static VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo);
+
+ bool readOverrideOrders(OneSixInstance *instance, PatchOrder &order);
+ static bool writeOverrideOrders(OneSixInstance *instance, const PatchOrder &order);
+
+private:
+ InstanceVersion *m_version;
+ OneSixInstance *m_instance;
+ QStringList external_patches;
+ QDir instance_root;
+
+ void buildInternal();
+ void buildFromExternalPatches();
+ void buildFromCustomJson();
+ void buildFromVersionJson();
+ void buildFromMultilayer();
+
+ void readInstancePatches();
+
+ void readJsonAndApply(const QJsonObject &obj);
+};
diff --git a/logic/VersionFile.cpp b/logic/minecraft/VersionFile.cpp
index cd2a4f9c..93f57116 100644
--- a/logic/VersionFile.cpp
+++ b/logic/minecraft/VersionFile.cpp
@@ -1,84 +1,41 @@
#include <QJsonArray>
#include <QJsonDocument>
-
#include <modutils.h>
#include "logger/QsLog.h"
-#include "logic/VersionFile.h"
-#include "logic/OneSixLibrary.h"
-#include "logic/VersionFinal.h"
-#include "MMCJson.h"
+#include "logic/minecraft/VersionFile.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/JarMod.h"
+#include "ParseUtils.h"
+
+#include "logic/MMCJson.h"
using namespace MMCJson;
+#include "VersionBuildError.h"
+
#define CURRENT_MINIMUM_LAUNCHER_VERSION 14
-RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &filename)
+int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle)
{
- RawLibraryPtr out(new RawLibrary());
- if (!libObj.contains("name"))
- {
- throw JSONValidationError(filename +
- "contains a library that doesn't have a 'name' field");
- }
- out->name = libObj.value("name").toString();
-
- auto readString = [libObj, filename](const QString & key, QString & variable)
- {
- if (libObj.contains(key))
- {
- QJsonValue val = libObj.value(key);
- if (!val.isString())
- {
- QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
- }
- else
- {
- variable = val.toString();
- }
- }
- };
-
- readString("url", out->url);
- readString("MMC-hint", out->hint);
- readString("MMC-absulute_url", out->absoluteUrl);
- readString("MMC-absoluteUrl", out->absoluteUrl);
- if (libObj.contains("extract"))
- {
- out->applyExcludes = true;
- auto extractObj = ensureObject(libObj.value("extract"));
- for (auto excludeVal : ensureArray(extractObj.value("exclude")))
- {
- out->excludes.append(ensureString(excludeVal));
- }
- }
- if (libObj.contains("natives"))
+ int retval = -1;
+ for (int i = 0; i < haystack.size(); ++i)
{
- out->applyNatives = true;
- QJsonObject nativesObj = ensureObject(libObj.value("natives"));
- for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
+ QString chunk = haystack.at(i)->rawName();
+ if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(chunk) != -1)
{
- if (!it.value().isString())
- {
- QLOG_WARN() << filename << "contains an invalid native (skipping)";
- }
- OpSys opSys = OpSys_fromString(it.key());
- if (opSys != Os_Other)
- {
- out->natives.append(qMakePair(opSys, it.value().toString()));
- }
+ // only one is allowed.
+ if (retval != -1)
+ return -1;
+ retval = i;
}
}
- if (libObj.contains("rules"))
- {
- out->applyRules = true;
- out->rules = rulesFromJsonV4(libObj);
- }
- return out;
+ return retval;
}
VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &filename,
- const bool requireOrder, const bool isFTB)
+ const bool requireOrder, const bool isFTB)
{
VersionFilePtr out(new VersionFile());
if (doc.isEmpty() || doc.isNull())
@@ -87,7 +44,6 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
if (!doc.isObject())
{
- throw JSONValidationError("The root of " + filename + " is not an object");
}
QJsonObject root = doc.object();
@@ -111,7 +67,7 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
out->mcVersion = root.value("mcVersion").toString();
out->filename = filename;
- auto readString = [root, filename](const QString & key, QString & variable)
+ auto readString = [root](const QString & key, QString & variable)
{
if (root.contains(key))
{
@@ -119,6 +75,16 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
};
+ auto readStringRet = [root](const QString & key)->QString
+ {
+ if (root.contains(key))
+ {
+ return ensureString(root.value(key));
+ }
+ return QString();
+ }
+ ;
+
// FIXME: This should be ignored when applying.
if (!isFTB)
{
@@ -126,13 +92,16 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
readString("mainClass", out->mainClass);
+ readString("appletClass", out->appletClass);
readString("processArguments", out->processArguments);
readString("minecraftArguments", out->overwriteMinecraftArguments);
readString("+minecraftArguments", out->addMinecraftArguments);
readString("-minecraftArguments", out->removeMinecraftArguments);
readString("type", out->type);
- readString("releaseTime", out->releaseTime);
- readString("time", out->time);
+
+ parse_timestamp(readStringRet("releaseTime"), out->m_releaseTimeString, out->m_releaseTime);
+ parse_timestamp(readStringRet("time"), out->m_updateTimeString, out->m_updateTime);
+
readString("assets", out->assets);
if (root.contains("minimumLauncherVersion"))
@@ -165,6 +134,14 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
}
+ if (root.contains("+traits"))
+ {
+ for (auto tweakerVal : ensureArray(root.value("+traits")))
+ {
+ out->traits.insert(ensureString(tweakerVal));
+ }
+ }
+
if (root.contains("libraries"))
{
// FIXME: This should be done when applying.
@@ -177,7 +154,7 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
// FIXME: This should be done when applying.
if (isFTB)
{
- lib->hint = "local";
+ lib->m_hint = "local";
lib->insertType = RawLibrary::Prepend;
out->addLibs.prepend(lib);
}
@@ -188,76 +165,29 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
}
+ if (root.contains("+jarMods"))
+ {
+ for (auto libVal : ensureArray(root.value("+jarMods")))
+ {
+ QJsonObject libObj = ensureObject(libVal);
+ // parse the jarmod
+ auto lib = Jarmod::fromJson(libObj, filename);
+ // and add to jar mods
+ out->jarMods.append(lib);
+ }
+ }
+
if (root.contains("+libraries"))
{
for (auto libVal : ensureArray(root.value("+libraries")))
{
QJsonObject libObj = ensureObject(libVal);
- QJsonValue insertVal = ensureExists(libObj.value("insert"));
-
// parse the library
- auto lib = RawLibrary::fromJson(libObj, filename);
-
- // TODO: utility functions for handling this case. templates?
- QString insertString;
- {
- if (insertVal.isString())
- {
- insertString = insertVal.toString();
- }
- else if (insertVal.isObject())
- {
- QJsonObject insertObj = insertVal.toObject();
- if (insertObj.isEmpty())
- {
- throw JSONValidationError("One library has an empty insert object in " +
- filename);
- }
- insertString = insertObj.keys().first();
- lib->insertData = insertObj.value(insertString).toString();
- }
- }
- if (insertString == "apply")
- {
- lib->insertType = RawLibrary::Apply;
- }
- else if (insertString == "prepend")
- {
- lib->insertType = RawLibrary::Prepend;
- }
- else if (insertString == "append")
- {
- lib->insertType = RawLibrary::Prepend;
- }
- else if (insertString == "replace")
- {
- lib->insertType = RawLibrary::Replace;
- }
- else
- {
- throw JSONValidationError("A '+' library in " + filename +
- " contains an invalid insert type");
- }
- if (libObj.contains("MMC-depend"))
- {
- const QString dependString = ensureString(libObj.value("MMC-depend"));
- if (dependString == "hard")
- {
- lib->dependType = RawLibrary::Hard;
- }
- else if (dependString == "soft")
- {
- lib->dependType = RawLibrary::Soft;
- }
- else
- {
- throw JSONValidationError("A '+' library in " + filename +
- " contains an invalid depend type");
- }
- }
+ auto lib = RawLibrary::fromJsonPlus(libObj, filename);
out->addLibs.append(lib);
}
}
+
if (root.contains("-libraries"))
{
for (auto libVal : ensureArray(root.value("-libraries")))
@@ -269,53 +199,81 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
return out;
}
-OneSixLibraryPtr VersionFile::createLibrary(RawLibraryPtr lib)
+QJsonDocument VersionFile::toJson(bool saveOrder)
{
- std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib->name));
- if (!lib->url.isEmpty())
+ QJsonObject root;
+ if (saveOrder)
+ {
+ root.insert("order", order);
+ }
+ writeString(root, "name", name);
+ writeString(root, "fileId", fileId);
+ writeString(root, "version", version);
+ writeString(root, "mcVersion", mcVersion);
+ writeString(root, "id", id);
+ writeString(root, "mainClass", mainClass);
+ writeString(root, "appletClass", appletClass);
+ writeString(root, "processArguments", processArguments);
+ writeString(root, "minecraftArguments", overwriteMinecraftArguments);
+ writeString(root, "+minecraftArguments", addMinecraftArguments);
+ writeString(root, "-minecraftArguments", removeMinecraftArguments);
+ writeString(root, "type", type);
+ writeString(root, "assets", assets);
+ if (isMinecraftVersion())
+ {
+ writeString(root, "releaseTime", m_releaseTimeString);
+ writeString(root, "time", m_updateTimeString);
+ }
+ if (minimumLauncherVersion != -1)
{
- out->setBaseUrl(lib->url);
+ root.insert("minimumLauncherVersion", minimumLauncherVersion);
}
- out->setHint(lib->hint);
- if (!lib->absoluteUrl.isEmpty())
+ writeStringList(root, "tweakers", overwriteTweakers);
+ writeStringList(root, "+tweakers", addTweakers);
+ writeStringList(root, "-tweakers", removeTweakers);
+ writeStringList(root, "+traits", traits.toList());
+ writeObjectList(root, "libraries", overwriteLibs);
+ writeObjectList(root, "+libraries", addLibs);
+ writeObjectList(root, "+jarMods", jarMods);
+ // FIXME: removed libs are special snowflakes.
+ if (removeLibs.size())
{
- out->setAbsoluteUrl(lib->absoluteUrl);
+ QJsonArray array;
+ for (auto lib : removeLibs)
+ {
+ QJsonObject rmlibobj;
+ rmlibobj.insert("name", lib);
+ array.append(rmlibobj);
+ }
+ root.insert("-libraries", array);
}
- out->setAbsoluteUrl(lib->absoluteUrl);
- out->extract_excludes = lib->excludes;
- for (auto native : lib->natives)
+ // write the contents to a json document.
{
- out->addNative(native.first, native.second);
+ QJsonDocument out;
+ out.setObject(root);
+ return out;
}
- out->setRules(lib->rules);
- out->finalize();
- return out;
}
-int VersionFile::findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle)
+bool VersionFile::isMinecraftVersion()
{
- int retval = -1;
- for (int i = 0; i < haystack.size(); ++i)
- {
- QString chunk = haystack.at(i)->rawName();
- if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(chunk) != -1)
- {
- // only one is allowed.
- if(retval != -1)
- return -1;
- retval = i;
- }
- }
- return retval;
+ return (fileId == "org.multimc.version.json") || (fileId == "net.minecraft") ||
+ (fileId == "org.multimc.custom.json");
+}
+
+bool VersionFile::hasJarMods()
+{
+ return !jarMods.isEmpty();
}
-void VersionFile::applyTo(VersionFinal *version)
+void VersionFile::applyTo(InstanceVersion *version)
{
if (minimumLauncherVersion != -1)
{
if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
{
- throw LauncherVersionError(minimumLauncherVersion, CURRENT_MINIMUM_LAUNCHER_VERSION);
+ throw LauncherVersionError(minimumLauncherVersion,
+ CURRENT_MINIMUM_LAUNCHER_VERSION);
}
}
@@ -336,21 +294,34 @@ void VersionFile::applyTo(VersionFinal *version)
{
version->mainClass = mainClass;
}
- if (!processArguments.isNull())
+ if (!appletClass.isNull())
{
- version->processArguments = processArguments;
+ version->appletClass = appletClass;
}
- if (!type.isNull())
- {
- version->type = type;
- }
- if (!releaseTime.isNull())
+ if (!processArguments.isNull())
{
- version->releaseTime = releaseTime;
+ if (isMinecraftVersion())
+ {
+ version->vanillaProcessArguments = processArguments;
+ }
+ version->processArguments = processArguments;
}
- if (!time.isNull())
+ if (isMinecraftVersion())
{
- version->time = time;
+ if (!type.isNull())
+ {
+ version->type = type;
+ }
+ if (!m_releaseTimeString.isNull())
+ {
+ version->m_releaseTimeString = m_releaseTimeString;
+ version->m_releaseTime = m_releaseTime;
+ }
+ if (!m_updateTimeString.isNull())
+ {
+ version->m_updateTimeString = m_updateTimeString;
+ version->m_updateTime = m_updateTime;
+ }
}
if (!assets.isNull())
{
@@ -358,10 +329,15 @@ void VersionFile::applyTo(VersionFinal *version)
}
if (minimumLauncherVersion >= 0)
{
- version->minimumLauncherVersion = minimumLauncherVersion;
+ if (version->minimumLauncherVersion < minimumLauncherVersion)
+ version->minimumLauncherVersion = minimumLauncherVersion;
}
if (!overwriteMinecraftArguments.isNull())
{
+ if (isMinecraftVersion())
+ {
+ version->vanillaMinecraftArguments = overwriteMinecraftArguments;
+ }
version->minecraftArguments = overwriteMinecraftArguments;
}
if (!addMinecraftArguments.isNull())
@@ -384,13 +360,20 @@ void VersionFile::applyTo(VersionFinal *version)
{
version->tweakers.removeAll(tweaker);
}
+ version->jarMods.append(jarMods);
+ version->traits.unite(traits);
if (shouldOverwriteLibs)
{
- version->libraries.clear();
+ QList<OneSixLibraryPtr> libs;
for (auto lib : overwriteLibs)
{
- version->libraries.append(createLibrary(lib));
+ libs.append(OneSixLibrary::fromRawLibrary(lib));
+ }
+ if (isMinecraftVersion())
+ {
+ version->vanillaLibraries = libs;
}
+ version->libraries = libs;
}
for (auto lib : addLibs)
{
@@ -399,43 +382,46 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Apply:
{
// QLOG_INFO() << "Applying lib " << lib->name;
- int index = findLibrary(version->libraries, lib->name);
+ int index = findLibrary(version->libraries, lib->m_name);
if (index >= 0)
{
auto library = version->libraries[index];
- if (!lib->url.isNull())
+ if (!lib->m_base_url.isNull())
{
- library->setBaseUrl(lib->url);
+ library->setBaseUrl(lib->m_base_url);
}
- if (!lib->hint.isNull())
+ if (!lib->m_hint.isNull())
{
- library->setHint(lib->hint);
+ library->setHint(lib->m_hint);
}
- if (!lib->absoluteUrl.isNull())
+ if (!lib->m_absolute_url.isNull())
{
- library->setAbsoluteUrl(lib->absoluteUrl);
+ library->setAbsoluteUrl(lib->m_absolute_url);
}
if (lib->applyExcludes)
{
- library->extract_excludes = lib->excludes;
+ library->extract_excludes = lib->extract_excludes;
}
- if (lib->applyNatives)
+ if (lib->isNative())
{
- library->clearSuffixes();
+ // library->clearSuffixes();
+ library->m_native_suffixes = lib->m_native_suffixes;
+ /*
for (auto native : lib->natives)
{
library->addNative(native.first, native.second);
}
+ */
}
if (lib->applyRules)
{
- library->setRules(lib->rules);
+ library->setRules(lib->m_rules);
}
library->finalize();
}
else
{
- QLOG_WARN() << "Couldn't find" << lib->name << "(skipping)";
+ QLOG_WARN() << "Couldn't find" << lib->m_name << "(skipping)";
}
break;
}
@@ -443,24 +429,24 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Prepend:
{
// QLOG_INFO() << "Adding lib " << lib->name;
- const int startOfVersion = lib->name.lastIndexOf(':') + 1;
+ const int startOfVersion = lib->m_name.lastIndexOf(':') + 1;
const int index = findLibrary(
- version->libraries, QString(lib->name).replace(startOfVersion, INT_MAX, '*'));
+ version->libraries, QString(lib->m_name).replace(startOfVersion, INT_MAX, '*'));
if (index < 0)
{
if (lib->insertType == RawLibrary::Append)
{
- version->libraries.append(createLibrary(lib));
+ version->libraries.append(OneSixLibrary::fromRawLibrary(lib));
}
else
{
- version->libraries.prepend(createLibrary(lib));
+ version->libraries.prepend(OneSixLibrary::fromRawLibrary(lib));
}
}
else
{
auto otherLib = version->libraries.at(index);
- const Util::Version ourVersion = lib->name.mid(startOfVersion, INT_MAX);
+ const Util::Version ourVersion = lib->m_name.mid(startOfVersion, INT_MAX);
const Util::Version otherVersion = otherLib->version();
// if the existing version is a hard dependency we can either use it or
// fail, but we can't change it
@@ -474,7 +460,7 @@ void VersionFile::applyTo(VersionFinal *version)
throw VersionBuildError(
QObject::tr(
"Error resolving library dependencies between %1 and %2 in %3.")
- .arg(otherLib->rawName(), lib->name, filename));
+ .arg(otherLib->rawName(), lib->m_name, filename));
}
else
{
@@ -486,7 +472,7 @@ void VersionFile::applyTo(VersionFinal *version)
// if we are higher it means we should update
if (ourVersion > otherVersion)
{
- auto library = createLibrary(lib);
+ auto library = OneSixLibrary::fromRawLibrary(lib);
if (Util::Version(otherLib->minVersion) < ourVersion)
{
library->minVersion = ourVersion.toString();
@@ -501,7 +487,7 @@ void VersionFile::applyTo(VersionFinal *version)
{
throw VersionBuildError(QObject::tr(
"Error resolving library dependencies between %1 and %2 in %3.")
- .arg(otherLib->rawName(), lib->name,
+ .arg(otherLib->rawName(), lib->m_name,
filename));
}
}
@@ -512,10 +498,10 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Replace:
{
QString toReplace;
- if(lib->insertData.isEmpty())
+ if (lib->insertData.isEmpty())
{
- const int startOfVersion = lib->name.lastIndexOf(':') + 1;
- toReplace = QString(lib->name).replace(startOfVersion, INT_MAX, '*');
+ const int startOfVersion = lib->m_name.lastIndexOf(':') + 1;
+ toReplace = QString(lib->m_name).replace(startOfVersion, INT_MAX, '*');
}
else
toReplace = lib->insertData;
@@ -523,7 +509,7 @@ void VersionFile::applyTo(VersionFinal *version)
int index = findLibrary(version->libraries, toReplace);
if (index >= 0)
{
- version->libraries.replace(index, createLibrary(lib));
+ version->libraries.replace(index, OneSixLibrary::fromRawLibrary(lib));
}
else
{
diff --git a/logic/minecraft/VersionFile.h b/logic/minecraft/VersionFile.h
new file mode 100644
index 00000000..186f4335
--- /dev/null
+++ b/logic/minecraft/VersionFile.h
@@ -0,0 +1,103 @@
+#pragma once
+
+#include <QString>
+#include <QStringList>
+#include <QDateTime>
+#include <memory>
+#include "logic/minecraft/OpSys.h"
+#include "logic/minecraft/OneSixRule.h"
+#include "VersionPatch.h"
+#include "MMCError.h"
+#include "OneSixLibrary.h"
+#include "JarMod.h"
+
+class InstanceVersion;
+struct VersionFile;
+
+typedef std::shared_ptr<VersionFile> VersionFilePtr;
+class VersionFile : public VersionPatch
+{
+public: /* methods */
+ static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename,
+ const bool requireOrder, const bool isFTB = false);
+ QJsonDocument toJson(bool saveOrder);
+
+ virtual void applyTo(InstanceVersion *version) override;
+ virtual bool isMinecraftVersion() override;
+ virtual bool hasJarMods() override;
+ virtual int getOrder() override
+ {
+ return order;
+ }
+ virtual void setOrder(int order) override
+ {
+ this->order = order;
+ }
+ virtual QList<JarmodPtr> getJarMods() override
+ {
+ return jarMods;
+ }
+ virtual QString getPatchID() override
+ {
+ return fileId;
+ }
+ virtual QString getPatchName() override
+ {
+ return name;
+ }
+ virtual QString getPatchVersion() override
+ {
+ return version;
+ }
+ virtual QString getPatchFilename() override
+ {
+ return filename;
+ }
+
+public: /* data */
+ int order = 0;
+ QString name;
+ QString fileId;
+ QString version;
+ // TODO use the mcVersion to determine if a version file should be removed on update
+ QString mcVersion;
+ QString filename;
+ // TODO requirements
+ // QMap<QString, QString> requirements;
+ QString id;
+ QString mainClass;
+ QString appletClass;
+ QString overwriteMinecraftArguments;
+ QString addMinecraftArguments;
+ QString removeMinecraftArguments;
+ QString processArguments;
+ QString type;
+
+ /// the time this version was actually released by Mojang, as string and as QDateTime
+ QString m_releaseTimeString;
+ QDateTime m_releaseTime;
+
+ /// the time this version was last updated by Mojang, as string and as QDateTime
+ QString m_updateTimeString;
+ QDateTime m_updateTime;
+
+ /// asset group used by this ... thing.
+ QString assets;
+ int minimumLauncherVersion = -1;
+
+ bool shouldOverwriteTweakers = false;
+ QStringList overwriteTweakers;
+ QStringList addTweakers;
+ QStringList removeTweakers;
+
+ bool shouldOverwriteLibs = false;
+ QList<RawLibraryPtr> overwriteLibs;
+ QList<RawLibraryPtr> addLibs;
+ QList<QString> removeLibs;
+
+ QSet<QString> traits;
+
+ QList<JarmodPtr> jarMods;
+};
+
+
diff --git a/logic/minecraft/VersionPatch.h b/logic/minecraft/VersionPatch.h
new file mode 100644
index 00000000..1dd30e79
--- /dev/null
+++ b/logic/minecraft/VersionPatch.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <memory>
+#include <QList>
+#include "JarMod.h"
+
+class InstanceVersion;
+class VersionPatch
+{
+public:
+ virtual ~VersionPatch(){};
+ virtual void applyTo(InstanceVersion *version) = 0;
+
+ virtual bool isMinecraftVersion() = 0;
+ virtual bool hasJarMods() = 0;
+ virtual QList<JarmodPtr> getJarMods() = 0;
+
+ virtual bool isMoveable()
+ {
+ return getOrder() >= 0;
+ }
+ virtual void setOrder(int order) = 0;
+ virtual int getOrder() = 0;
+
+ virtual QString getPatchID() = 0;
+ virtual QString getPatchName() = 0;
+ virtual QString getPatchVersion() = 0;
+ virtual QString getPatchFilename() = 0;
+};
+
+typedef std::shared_ptr<VersionPatch> VersionPatchPtr;
diff --git a/logic/minecraft/VersionSource.h b/logic/minecraft/VersionSource.h
new file mode 100644
index 00000000..75b2c24b
--- /dev/null
+++ b/logic/minecraft/VersionSource.h
@@ -0,0 +1,9 @@
+#pragma once
+
+/// where is a version from?
+enum VersionSource
+{
+ Builtin, //!< version loaded from the internal resources.
+ Local, //!< version loaded from a file in the cache.
+ Remote, //!< incomplete version on a remote server.
+}; \ No newline at end of file
diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h
index 2df8428b..d05e7b6f 100644
--- a/logic/net/NetJob.h
+++ b/logic/net/NetJob.h
@@ -21,7 +21,6 @@
#include "MD5EtagDownload.h"
#include "CacheDownload.h"
#include "HttpMetaCache.h"
-#include "ForgeXzDownload.h"
#include "logic/tasks/ProgressProvider.h"
class NetJob;
diff --git a/logic/tasks/ProgressProvider.h b/logic/tasks/ProgressProvider.h
index 15e453a3..dcb71139 100644
--- a/logic/tasks/ProgressProvider.h
+++ b/logic/tasks/ProgressProvider.h
@@ -32,6 +32,7 @@ signals:
void status(QString status);
public:
+ virtual ~ProgressProvider() {};
virtual QString getStatus() const = 0;
virtual void getProgress(qint64 &current, qint64 &total) = 0;
virtual bool isRunning() const = 0;
diff --git a/main.cpp b/main.cpp
index a79cc014..b33b02f2 100644
--- a/main.cpp
+++ b/main.cpp
@@ -28,6 +28,7 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(instances);
Q_INIT_RESOURCE(multimc);
Q_INIT_RESOURCE(backgrounds);
+ Q_INIT_RESOURCE(versions);
#ifdef HANDLE_SEGV
// Register signal handler for generating crash reports.
diff --git a/resources/multimc/16x16/plugin-blue.png b/resources/multimc/16x16/plugin-blue.png
new file mode 100644
index 00000000..b5ab3fce
--- /dev/null
+++ b/resources/multimc/16x16/plugin-blue.png
Binary files differ
diff --git a/resources/multimc/16x16/plugin-green.png b/resources/multimc/16x16/plugin-green.png
new file mode 100644
index 00000000..af0f1166
--- /dev/null
+++ b/resources/multimc/16x16/plugin-green.png
Binary files differ
diff --git a/resources/multimc/16x16/plugin-red.png b/resources/multimc/16x16/plugin-red.png
new file mode 100644
index 00000000..1a97c9c0
--- /dev/null
+++ b/resources/multimc/16x16/plugin-red.png
Binary files differ
diff --git a/resources/multimc/16x16/resourcepacks.png b/resources/multimc/16x16/resourcepacks.png
new file mode 100644
index 00000000..d862f5ca
--- /dev/null
+++ b/resources/multimc/16x16/resourcepacks.png
Binary files differ
diff --git a/resources/multimc/24x24/plugin-blue.png b/resources/multimc/24x24/plugin-blue.png
new file mode 100644
index 00000000..250a6260
--- /dev/null
+++ b/resources/multimc/24x24/plugin-blue.png
Binary files differ
diff --git a/resources/multimc/24x24/plugin-green.png b/resources/multimc/24x24/plugin-green.png
new file mode 100644
index 00000000..90603d24
--- /dev/null
+++ b/resources/multimc/24x24/plugin-green.png
Binary files differ
diff --git a/resources/multimc/24x24/plugin-red.png b/resources/multimc/24x24/plugin-red.png
new file mode 100644
index 00000000..68cb8e9d
--- /dev/null
+++ b/resources/multimc/24x24/plugin-red.png
Binary files differ
diff --git a/resources/multimc/24x24/resourcepacks.png b/resources/multimc/24x24/resourcepacks.png
new file mode 100644
index 00000000..68359d39
--- /dev/null
+++ b/resources/multimc/24x24/resourcepacks.png
Binary files differ
diff --git a/resources/multimc/32x32/plugin-blue.png b/resources/multimc/32x32/plugin-blue.png
new file mode 100644
index 00000000..c4ca12e2
--- /dev/null
+++ b/resources/multimc/32x32/plugin-blue.png
Binary files differ
diff --git a/resources/multimc/32x32/plugin-green.png b/resources/multimc/32x32/plugin-green.png
new file mode 100644
index 00000000..770d695e
--- /dev/null
+++ b/resources/multimc/32x32/plugin-green.png
Binary files differ
diff --git a/resources/multimc/32x32/plugin-red.png b/resources/multimc/32x32/plugin-red.png
new file mode 100644
index 00000000..5cda173a
--- /dev/null
+++ b/resources/multimc/32x32/plugin-red.png
Binary files differ
diff --git a/resources/multimc/32x32/resourcepacks.png b/resources/multimc/32x32/resourcepacks.png
new file mode 100644
index 00000000..c14759ef
--- /dev/null
+++ b/resources/multimc/32x32/resourcepacks.png
Binary files differ
diff --git a/resources/multimc/64x64/plugin-blue.png b/resources/multimc/64x64/plugin-blue.png
new file mode 100644
index 00000000..24618fd0
--- /dev/null
+++ b/resources/multimc/64x64/plugin-blue.png
Binary files differ
diff --git a/resources/multimc/64x64/plugin-green.png b/resources/multimc/64x64/plugin-green.png
new file mode 100644
index 00000000..668be334
--- /dev/null
+++ b/resources/multimc/64x64/plugin-green.png
Binary files differ
diff --git a/resources/multimc/64x64/plugin-red.png b/resources/multimc/64x64/plugin-red.png
new file mode 100644
index 00000000..55d1a42a
--- /dev/null
+++ b/resources/multimc/64x64/plugin-red.png
Binary files differ
diff --git a/resources/multimc/64x64/resourcepacks.png b/resources/multimc/64x64/resourcepacks.png
new file mode 100644
index 00000000..fb874e7d
--- /dev/null
+++ b/resources/multimc/64x64/resourcepacks.png
Binary files differ
diff --git a/resources/multimc/multimc.qrc b/resources/multimc/multimc.qrc
index 1df22c29..887e94ac 100644
--- a/resources/multimc/multimc.qrc
+++ b/resources/multimc/multimc.qrc
@@ -102,6 +102,30 @@
<file>32x32/status-good.png</file>
<file>48x48/status-good.png</file>
<file>64x64/status-good.png</file>
+
+ <!-- Plugin (blue recolor), CC-BY-SA 3.0, Oxygen icons. -->
+ <file>16x16/plugin-blue.png</file>
+ <file>24x24/plugin-blue.png</file>
+ <file>32x32/plugin-blue.png</file>
+ <file>64x64/plugin-blue.png</file>
+
+ <!-- Plugin (red recolor), CC-BY-SA 3.0, Oxygen icons. -->
+ <file>16x16/plugin-red.png</file>
+ <file>24x24/plugin-red.png</file>
+ <file>32x32/plugin-red.png</file>
+ <file>64x64/plugin-red.png</file>
+
+ <!-- Plugin (green original), CC-BY-SA 3.0, Oxygen icons. -->
+ <file>16x16/plugin-green.png</file>
+ <file>24x24/plugin-green.png</file>
+ <file>32x32/plugin-green.png</file>
+ <file>64x64/plugin-green.png</file>
+
+ <!-- Resource packs, CC-BY-SA 3.0, Oxygen icons. -->
+ <file>16x16/resourcepacks.png</file>
+ <file>24x24/resourcepacks.png</file>
+ <file>32x32/resourcepacks.png</file>
+ <file>64x64/resourcepacks.png</file>
<!-- Refresh, CC-BY-SA 3.0, Oxygen icons. -->
<file>16x16/refresh.png</file>
diff --git a/resources/versions/LWJGL/2.9.0.json b/resources/versions/LWJGL/2.9.0.json
new file mode 100644
index 00000000..5dbd624e
--- /dev/null
+++ b/resources/versions/LWJGL/2.9.0.json
@@ -0,0 +1,45 @@
+ {
+ "fileId": "org.lwjgl",
+ "name": "LWJGL",
+ "version": "2.9.0",
+ "+libraries": [
+ {
+ "name": "net.java.jinput:jinput:2.0.5"
+ },
+ {
+ "name": "net.java.jinput:jinput-platform:2.0.5",
+ "natives": {
+ "linux": "natives-linux",
+ "windows": "natives-windows",
+ "osx": "natives-osx"
+ },
+ "extract": {
+ "exclude": [
+ "META-INF/"
+ ]
+ }
+ },
+ {
+ "name": "net.java.jutils:jutils:1.0.0"
+ },
+ {
+ "name": "org.lwjgl.lwjgl:lwjgl:2.9.0"
+ },
+ {
+ "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.0"
+ },
+ {
+ "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.0",
+ "natives": {
+ "linux": "natives-linux",
+ "windows": "natives-windows",
+ "osx": "natives-osx"
+ },
+ "extract": {
+ "exclude": [
+ "META-INF/"
+ ]
+ }
+ }
+ ]
+}
diff --git a/resources/versions/LWJGL/2.9.1-nightly-20130708-debug3.json b/resources/versions/LWJGL/2.9.1-nightly-20130708-debug3.json
new file mode 100644
index 00000000..7265b3b0
--- /dev/null
+++ b/resources/versions/LWJGL/2.9.1-nightly-20130708-debug3.json
@@ -0,0 +1,45 @@
+{
+ "fileId": "org.lwjgl",
+ "name": "LWJGL",
+ "version": "2.9.1-nightly-20130708-debug3",
+ "+libraries": [
+ {
+ "name": "net.java.jinput:jinput:2.0.5"
+ },
+ {
+ "name": "net.java.jinput:jinput-platform:2.0.5",
+ "natives": {
+ "linux": "natives-linux",
+ "windows": "natives-windows",
+ "osx": "natives-osx"
+ },
+ "extract": {
+ "exclude": [
+ "META-INF/"
+ ]
+ }
+ },
+ {
+ "name": "net.java.jutils:jutils:1.0.0"
+ },
+ {
+ "name": "org.lwjgl.lwjgl:lwjgl:2.9.1-nightly-20130708-debug3"
+ },
+ {
+ "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.1-nightly-20130708-debug3"
+ },
+ {
+ "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.1-nightly-20130708-debug3",
+ "natives": {
+ "linux": "natives-linux",
+ "windows": "natives-windows",
+ "osx": "natives-osx"
+ },
+ "extract": {
+ "exclude": [
+ "META-INF/"
+ ]
+ }
+ }
+ ]
+}
diff --git a/resources/versions/LWJGL/2.9.1.json b/resources/versions/LWJGL/2.9.1.json
new file mode 100644
index 00000000..e7f5e947
--- /dev/null
+++ b/resources/versions/LWJGL/2.9.1.json
@@ -0,0 +1,45 @@
+ {
+ "fileId": "org.lwjgl",
+ "name": "LWJGL",
+ "version": "2.9.1",
+ "+libraries": [
+ {
+ "name": "net.java.jinput:jinput:2.0.5"
+ },
+ {
+ "name": "net.java.jinput:jinput-platform:2.0.5",
+ "natives": {
+ "linux": "natives-linux",
+ "windows": "natives-windows",
+ "osx": "natives-osx"
+ },
+ "extract": {
+ "exclude": [
+ "META-INF/"
+ ]
+ }
+ },
+ {
+ "name": "net.java.jutils:jutils:1.0.0"
+ },
+ {
+ "name": "org.lwjgl.lwjgl:lwjgl:2.9.1"
+ },
+ {
+ "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.1"
+ },
+ {
+ "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.1",
+ "natives": {
+ "linux": "natives-linux",
+ "windows": "natives-windows",
+ "osx": "natives-osx"
+ },
+ "extract": {
+ "exclude": [
+ "META-INF/"
+ ]
+ }
+ }
+ ]
+}
diff --git a/resources/versions/minecraft.json b/resources/versions/minecraft.json
new file mode 100644
index 00000000..58b153c2
--- /dev/null
+++ b/resources/versions/minecraft.json
@@ -0,0 +1,668 @@
+{
+"versions": [
+ {
+ "id": "1.5.2",
+ "checksum": "6897c3287fb971c9f362eb3ab20f5ddd",
+ "releaseTime": "2013-04-25T17:45:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.5.1",
+ "checksum": "5c1219d869b87d233de3033688ec7567",
+ "releaseTime": "2013-03-20T12:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.5",
+ "checksum": "fd11cbc5b01aae1d62cff0145171f3d9",
+ "releaseTime": "2013-03-07T00:00:00+02:00",
+ "type": "snapshot",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.4.7",
+ "checksum": "8e80fb01b321c6b3c7efca397a3eea35",
+ "releaseTime": "2012-12-28T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.4.6",
+ "checksum": "48677dc4c2b98c29918722b5ab27b4fd",
+ "releaseTime": "2012-12-20T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.4.5",
+ "checksum": "b15e2b2b6b4629f0d99a95b6b44412a0",
+ "releaseTime": "2012-11-20T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.4.4",
+ "checksum": "7aa46c8058cba2f38e9d2ddddcc77c72",
+ "releaseTime": "2012-11-14T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.4.3",
+ "checksum": "9cc3295931edb6339f22989fe1b612a6",
+ "releaseTime": "2012-11-01T00:00:00+02:00",
+ "type": "snapshot",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.4.2",
+ "checksum": "771175c01778ea67395bc6919a5a9dc5",
+ "releaseTime": "2012-10-25T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.4.1",
+ "checksum": "542621a5298659dc65f383f35170fc4c",
+ "releaseTime": "2012-10-23T00:00:00+02:00",
+ "type": "snapshot",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.4",
+ "checksum": "32a654388b54d3e4bb29c1a46e7d6a12",
+ "releaseTime": "2012-10-19T00:00:00+02:00",
+ "type": "snapshot",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.3.2",
+ "checksum": "969699f13e5bbe7f12e40ac4f32b7d9a",
+ "releaseTime": "2012-08-16T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.3.1",
+ "checksum": "266ccbc9798afd2eadf3d6c01b4c562a",
+ "releaseTime": "2012-08-01T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.3",
+ "checksum": "a6effac1eaccf5d429aae340cf95ed5d",
+ "releaseTime": "2012-07-26T00:00:00+02:00",
+ "type": "snapshot",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.2.5",
+ "checksum": "8e8778078a175a33603a585257f28563",
+ "releaseTime": "2012-03-30T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.2.4",
+ "checksum": "25423eab6d8707f96cc6ad8a21a7250a",
+ "releaseTime": "2012-03-22T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.2.3",
+ "checksum": "12f6c4b1bdcc63f029e3c088a364b8e4",
+ "releaseTime": "2012-03-02T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.2.2",
+ "checksum": "6189e96efaea11e5164b4a4755574324",
+ "releaseTime": "2012-03-01T00:00:01+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.2.1",
+ "checksum": "97067a603eba2b6eb75d3194f81f6bcd",
+ "releaseTime": "2012-03-01T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.1",
+ "checksum": "e92302d2acdba7c97e0d8df1e10d2006",
+ "releaseTime": "2012-01-12T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "1.0",
+ "checksum": "3820d222b95d0b8c520d9596a756a6e6",
+ "releaseTime": "2011-11-18T00:00:00+02:00",
+ "type": "release",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.8.1",
+ "checksum": "f8c5a2ccd3bc996792bbe436d8cc08bc",
+ "releaseTime": "2011-09-19T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.8",
+ "checksum": "a59a9fd4c726a573b0a2bdd10d857f59",
+ "releaseTime": "2011-09-15T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.7.3",
+ "checksum": "eae3353fdaa7e10a59b4cb5b45bfa10d",
+ "releaseTime": "2011-07-08T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.7.2",
+ "checksum": "dd9215ab1141170d4871f42bff4ab302",
+ "releaseTime": "2011-07-01T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.7",
+ "checksum": "682419e9ed1a236c3067822d53cda1e4",
+ "releaseTime": "2011-06-30T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.6.6",
+ "checksum": "ce80072464433cd5b05d505aa8ff29d1",
+ "releaseTime": "2011-05-31T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.6.5",
+ "checksum": "2aba888864b32038c8d22ee5df71b7c8",
+ "releaseTime": "2011-05-28T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.6.4",
+ "checksum": "5c4df6f120336f113180698613853dba",
+ "releaseTime": "2011-05-26T00:00:04+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.6.3",
+ "checksum": "efc2becca965e4f8feb5b4210c6a4fd1",
+ "releaseTime": "2011-05-26T00:00:03+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.6.2",
+ "checksum": "01330b1c930102a683a4dd8d792e632e",
+ "releaseTime": "2011-05-26T00:00:02+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.6.1",
+ "checksum": "a7e82c441a57ef4068c533f4d777336a",
+ "releaseTime": "2011-05-26T00:00:01+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.6",
+ "checksum": "d531e221227a65392259d3141893280d",
+ "releaseTime": "2011-05-26T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.5_01",
+ "checksum": "d02fa9998e30693d8d989d5f88cf0040",
+ "releaseTime": "2011-04-20T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.5",
+ "checksum": "24289130902822d73f8722b52bc07cdb",
+ "releaseTime": "2011-04-19T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.4_01",
+ "checksum": "9379e54b581ba4ef3acc3e326e87db91",
+ "releaseTime": "2011-04-05T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.4",
+ "checksum": "71e64b61175b371ed148b385f2d14ebf",
+ "releaseTime": "2011-03-31T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.3_01",
+ "checksum": "4203826f35e1036f089919032c3d19d1",
+ "releaseTime": "2011-02-23T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.3b",
+ "checksum": "de2164df461d028229ed2e101181bbd4",
+ "releaseTime": "2011-02-22T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.2_02",
+ "checksum": "1736c5ba4f63a981220c2a18a4120180",
+ "releaseTime": "2011-01-21T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.2_01",
+ "checksum": "486d83ec00554b45ffa21af2faa0116a",
+ "releaseTime": "2011-01-14T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.2",
+ "checksum": "6426223efe23c3931a4ef89685be3349",
+ "releaseTime": "2011-01-13T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.1_02",
+ "checksum": "7d547e495a770c62054ef136add43034",
+ "releaseTime": "2010-12-22T00:00:01+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.1_01",
+ "checksum": "1f9331f2bfca81b6ce2acdfc1f105837",
+ "releaseTime": "2010-12-22T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.0.2",
+ "checksum": "d200c465b8c167cc8df6537531fc9a48",
+ "releaseTime": "2010-12-21T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.0_01",
+ "checksum": "03bd20b870dbbd121de5dca98af4e1ce",
+ "releaseTime": "2010-12-20T00:00:01+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "b1.0",
+ "checksum": "5f8733dbbf09b4e7c874661a3c29c239",
+ "releaseTime": "2010-12-20T00:00:00+02:00",
+ "type": "old_beta",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.6",
+ "checksum": "ddd5e39467f28d1ea1a03b4d9e790867",
+ "releaseTime": "2010-12-03T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.5",
+ "checksum": "7d3a43037190970ff2e11153b5718b74",
+ "releaseTime": "2010-12-01T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.4_01",
+ "checksum": "0a1cc8c668faa6dc93fc418e8b4b097a",
+ "releaseTime": "2010-11-30T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.3_04",
+ "checksum": "b2c25a753c82a1cd228ce71469829dc1",
+ "releaseTime": "2010-11-26T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.3_02",
+ "checksum": "3ad4808ef2ac3b65d10305315260da03",
+ "releaseTime": "2010-11-25T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.3_01",
+ "checksum": "70cbab762b17c5b11fefea9b12564119",
+ "releaseTime": "2010-11-24T00:00:01+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.3",
+ "checksum": "25f053114e34b915e675f82d58f08711",
+ "releaseTime": "2010-11-24T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.2b",
+ "checksum": "6250fb17f8898c4d970d6bd03c229177",
+ "releaseTime": "2010-11-10T00:00:01+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.2a",
+ "checksum": "0f9fe018b344fd9dd849005f9bdca803",
+ "releaseTime": "2010-11-10T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "texturepacks"]
+ },
+ {
+ "id": "a1.2.1_01",
+ "checksum": "0a496e44a7b4e2f493b5893d8e5845bd",
+ "releaseTime": "2010-11-05T00:00:01+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.2.1",
+ "checksum": "0a496e44a7b4e2f493b5893d8e5845bd",
+ "releaseTime": "2010-11-05T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.2.0_02",
+ "checksum": "f5bcb4d0c0e78bc220f164b89ae9bd60",
+ "releaseTime": "2010-11-04T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.2.0_01",
+ "checksum": "b2e9333e967cb89488884c2e5c715d4f",
+ "releaseTime": "2010-10-31T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.2.0",
+ "checksum": "44c384dae02390f700458b95d82c3e2a",
+ "releaseTime": "2010-10-30T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.1.2_01",
+ "checksum": "94346e1b8f6ad0e4a284314f0e29207b",
+ "releaseTime": "2010-09-23T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.1.2",
+ "checksum": "72ba1f834327805cb44164a42b331522",
+ "releaseTime": "2010-09-20T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.1.0",
+ "checksum": "891fd93e04f5daaf35d73c58e45c01b1",
+ "releaseTime": "2010-09-13T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.0.17_04",
+ "checksum": "16ed7dc58244772847991e504afcf02f",
+ "releaseTime": "2010-08-23T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.0.17_02",
+ "checksum": "d89760b0871ef61a55c9f336c0439d58",
+ "releaseTime": "2010-08-20T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.0.16",
+ "checksum": "6bbde02c13aed5766275f4398ede6aae",
+ "releaseTime": "2010-08-12T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.0.15",
+ "checksum": "ade257d2080d56fa983763f9c701fa14",
+ "releaseTime": "2010-08-04T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.0.14",
+ "checksum": "227d0c6fa896a231de6269a074c9a458",
+ "releaseTime": "2010-07-30T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.0.11",
+ "checksum": "6f1b1dd157fa0df39760f5be3eab01b0",
+ "releaseTime": "2010-07-23T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.0.5_01",
+ "checksum": "ae5f606caa18222e7568819c910ee423",
+ "releaseTime": "2010-07-13T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "y",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "a1.0.4",
+ "checksum": "13ce7935c3670e7494e26b2704bfa3e9",
+ "releaseTime": "2010-07-09T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "ax",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "inf-20100618",
+ "checksum": "f5f5aa34760facc10486e906a7c60196",
+ "releaseTime": "2010-06-16T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "net.minecraft.client.d",
+ "appletClass": "net.minecraft.client.MinecraftApplet",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "c0.30_01c",
+ "checksum": "fcfd7f83a6b27503cf48202381a5adf2",
+ "releaseTime": "2009-12-22T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "com.mojang.minecraft.l",
+ "appletClass": "com.mojang.minecraft.MinecraftApplet",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "c0.0.13a_03",
+ "checksum": "a9527cb5aef198e0f53e235ebe13dc75",
+ "releaseTime": "2009-05-22T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "com.mojang.minecraft.c",
+ "appletClass": "com.mojang.minecraft.MinecraftApplet",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "c0.0.13a",
+ "checksum": "3617fbf5fbfd2b837ebf5ceb63584908",
+ "releaseTime": "2009-05-31T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "com.mojang.minecraft.Minecraft",
+ "appletClass": "com.mojang.minecraft.MinecraftApplet",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "c0.0.11a",
+ "checksum": "a6e03c2eaf74709facc0d2477648e999",
+ "releaseTime": "2009-05-17T00:00:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "com.mojang.minecraft.Minecraft",
+ "appletClass": "com.mojang.minecraft.MinecraftApplet",
+ "+traits": ["legacyLaunch", "no-texturepacks"]
+ },
+ {
+ "id": "rd-161348",
+ "checksum": "80882b8936a5c8d91500838a6660b504",
+ "releaseTime": "2009-05-16T13:48:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "com.mojang.minecraft.RubyDung",
+ "+traits": ["no-texturepacks"]
+ },
+ {
+ "id": "rd-160052",
+ "checksum": "24c5cc99a2a612697ed2f7d5d04242fe",
+ "releaseTime": "2009-05-16T00:52:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "com.mojang.rubydung.RubyDung",
+ "+traits": ["no-texturepacks"]
+ },
+ {
+ "id": "rd-132328",
+ "checksum": "70e33a81c541b13a477e68c1207124eb",
+ "releaseTime": "2009-05-13T23:28:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "com.mojang.rubydung.RubyDung",
+ "+traits": ["no-texturepacks"]
+ },
+ {
+ "id": "rd-132211",
+ "checksum": "99fdaea10c494b9c3c3254636b98b799",
+ "releaseTime": "2009-05-13T22:11:00+02:00",
+ "type": "old_alpha",
+ "processArguments": "legacy",
+ "mainClass": "com.mojang.rubydung.RubyDung",
+ "+traits": ["no-texturepacks"]
+ }
+]
+}
diff --git a/resources/versions/versions.qrc b/resources/versions/versions.qrc
new file mode 100644
index 00000000..6ba05744
--- /dev/null
+++ b/resources/versions/versions.qrc
@@ -0,0 +1,11 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource prefix="/versions">
+ <!-- builtin legacy versions -->
+ <file>minecraft.json</file>
+ <!-- LWJGL -->
+ <file>LWJGL/2.9.0.json</file>
+ <file>LWJGL/2.9.1-nightly-20130708-debug3.json</file>
+ <file>LWJGL/2.9.1.json</file>
+ </qresource>
+</RCC>