Improvements to MultiMC metadata updating

Reformatted and refactored names to fit Kotlin conventions
Closed manifest file reader properly
Fixed switching between loaders, improved intermediary logic
Improved error messages
This commit is contained in:
comp500 2022-06-29 03:26:50 +01:00
parent ab3de9a246
commit 02b01b90d7
3 changed files with 102 additions and 91 deletions

View File

@ -12,10 +12,10 @@ import java.nio.file.Paths
class LauncherUtils internal constructor(private val opts: UpdateManager.Options, val ui: IUserInterface) {
enum class LauncherStatus {
Succesful,
NoChanges,
Cancelled,
NotFound, // We'll use the NotFound as the neutral return type for now
SUCCESSFUL,
NO_CHANGES,
CANCELLED,
NOT_FOUND, // When there is no mmc-pack.json file found (i.e. MultiMC is not being used)
}
fun handleMultiMC(pf: PackFile, gson: Gson): LauncherStatus {
@ -24,85 +24,96 @@ class LauncherUtils internal constructor(private val opts: UpdateManager.Options
val manifestFile = File(manifestPath)
if (!manifestFile.exists()) {
return LauncherStatus.NotFound
return LauncherStatus.NOT_FOUND
}
val multimcManifest = try {
JsonParser.parseReader(manifestFile.reader())
val multimcManifest = manifestFile.reader().use {
try {
JsonParser.parseReader(it)
} catch (e: JsonIOException) {
throw Exception("Cannot read the MultiMC pack file", e)
} catch (e: JsonSyntaxException) {
throw Exception("Invalid MultiMC pack file", e)
}.asJsonObject
}
Log.info("Loaded MultiMC config")
// We only support format 1, if it gets updated in the future we'll have to handle that
// There's only version 1 for now tho, so that's good
if (multimcManifest["formatVersion"].asInt != 1) {
throw Exception("Invalid MultiMC format version")
if (multimcManifest["formatVersion"]?.asInt != 1) {
throw Exception("Unsupported MultiMC format version ${multimcManifest["formatVersion"]}")
}
var manifestModified = false
val modLoaders = hashMapOf("net.minecraft" to "minecraft", "net.minecraftforge" to "forge", "net.fabricmc.fabric-loader" to "fabric", "org.quiltmc.quilt-loader" to "quilt", "com.mumfrey.liteloader" to "liteloader")
val modLoaders = hashMapOf(
"net.minecraft" to "minecraft",
"net.minecraftforge" to "forge",
"net.fabricmc.fabric-loader" to "fabric",
"org.quiltmc.quilt-loader" to "quilt",
"com.mumfrey.liteloader" to "liteloader")
val modLoadersClasses = modLoaders.entries.associate{(k,v)-> v to k}
var modLoaderFound = false
val modLoadersFound = HashMap<String, String>() // Key: modLoader, Value: Version
val components = multimcManifest["components"].asJsonArray
for (componentObj in components) {
val component = componentObj.asJsonObject
val loaderVersionsFound = HashMap<String, String?>()
val outdatedLoaders = mutableSetOf<String>()
val components = multimcManifest["components"]?.asJsonArray ?: throw Exception("Invalid mmc-pack.json: no components key")
components.removeAll {
val component = it.asJsonObject
val version = component["version"].asString
val version = component["version"]?.asString
// If we find any of the modloaders we support, we save it and check the version
if (modLoaders.containsKey(component["uid"].asString)) {
val modLoader = modLoaders.getValue(component["uid"].asString)
if (modLoader != "minecraft")
modLoaderFound = true // Only set to true if modLoader isn't Minecraft
modLoadersFound[modLoader] = version
if (modLoaders.containsKey(component["uid"]?.asString)) {
val modLoader = modLoaders.getValue(component["uid"]!!.asString)
loaderVersionsFound[modLoader] = version
if (version != pf.versions?.get(modLoader)) {
manifestModified = true
component.addProperty("version", pf.versions?.get(modLoader))
}
outdatedLoaders.add(modLoader)
true // Delete component; cached metadata is invalid and will be re-added
} else {
false // Already up to date; cached metadata is valid
}
} else { false } // Not a known loader / MC
}
// If we can't find the mod loader in the MultiMC file, we add it
if (!modLoaderFound) {
// Using this filter and loop to handle multiple handlers
for ((_, loader) in modLoaders
.filter { it.value != "minecraft" && !modLoadersFound.containsKey(it.value) && pf.versions?.containsKey(it.value) == true }
.filter {
(!loaderVersionsFound.containsKey(it.value) || outdatedLoaders.contains(it.value))
&& pf.versions?.containsKey(it.value) == true }
) {
components.add(gson.toJsonTree(hashMapOf("uid" to modLoadersClasses.get(loader), "version" to pf.versions?.get(loader))))
}
manifestModified = true
components.add(gson.toJsonTree(
hashMapOf("uid" to modLoadersClasses[loader], "version" to pf.versions?.get(loader)))
)
}
// If mc version change detected, and fabric mappings are found, delete them, MultiMC will add and re-dl the correct one
if (modLoadersFound["minecraft"] != pf.versions?.getValue("minecraft"))
components.find { it.asJsonObject["uid"].asString == "net.fabricmc.intermediary" }?.asJsonObject?.let { components.remove(it) }
// If inconsistent Intermediary mappings version is found, delete it - MultiMC will add and re-dl the correct one
components.find { it.isJsonObject && it.asJsonObject["uid"]?.asString == "net.fabricmc.intermediary" }?.let {
if (it.asJsonObject["version"]?.asString != pf.versions?.get("minecraft")) {
components.remove(it)
manifestModified = true
}
}
if (manifestModified) {
// The manifest has been modified, so before saving it we'll ask the user
// if they wanna update it, continue without updating it, or exit
val oldVers = modLoadersFound.map { Pair(it.key, it.value) }
val oldVers = loaderVersionsFound.map { Pair(it.key, it.value) }
val newVers = pf.versions!!.map { Pair(it.key, it.value) }
when (ui.showUpdateConfirmationDialog(oldVers, newVers)) {
IUserInterface.UpdateConfirmationResult.CANCELLED -> {
return LauncherStatus.Cancelled
return LauncherStatus.CANCELLED
}
IUserInterface.UpdateConfirmationResult.CONTINUE -> {
return LauncherStatus.Succesful // Returning succesful as... Well, the user is telling us to continue
return LauncherStatus.SUCCESSFUL
}
else -> {} // Compiler is giving warning about "non-exhaustive when", so i'll just add an empty one
else -> {}
}
manifestFile.writeText(gson.toJson(multimcManifest))
Log.info("Updated modpack Minecrafts and/or the modloaders version")
Log.info("Successfully updated mmc-pack.json based on version metadata")
return LauncherStatus.Succesful
return LauncherStatus.SUCCESSFUL
}
return LauncherStatus.NoChanges
return LauncherStatus.NO_CHANGES
}
}

View File

@ -105,8 +105,9 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
ui.submitProgress(InstallProgress("Loading MultiMC pack file..."))
try {
when (lu.handleMultiMC(pf, gson)) {
LauncherUtils.LauncherStatus.Cancelled -> cancelled = true
LauncherUtils.LauncherStatus.NotFound -> Log.info("MultiMC not detected")
LauncherUtils.LauncherStatus.CANCELLED -> cancelled = true
LauncherUtils.LauncherStatus.NOT_FOUND -> Log.info("MultiMC not detected")
else -> {}
}
handleCancellation()
} catch (e: Exception) {

View File

@ -202,10 +202,9 @@ class GUIHandler : IUserInterface {
val options = arrayOf("Cancel", "Continue anyways", "Update")
val result = JOptionPane.showOptionDialog(frmPackwizlauncher,
message,
val result = JOptionPane.showOptionDialog(frmPackwizlauncher, message,
"Updating MultiMC versions",
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0])
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2])
future.complete(
when (result) {
JOptionPane.CLOSED_OPTION, 0 -> IUserInterface.UpdateConfirmationResult.CANCELLED