mirror of
https://github.com/packwiz/packwiz-installer.git
synced 2025-04-18 20:46:30 +02:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
ab3de9a246
@ -23,7 +23,7 @@ val r8 by configurations.creating
|
||||
dependencies {
|
||||
implementation("commons-cli:commons-cli:1.5.0")
|
||||
implementation("com.moandjiezana.toml:toml4j:0.7.2")
|
||||
implementation("com.google.code.gson:gson:2.8.9")
|
||||
implementation("com.google.code.gson:gson:2.9.0")
|
||||
implementation("com.squareup.okio:okio:3.0.0")
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
implementation("com.squareup.okhttp3:okhttp:4.9.3")
|
||||
|
108
src/main/kotlin/link/infra/packwiz/installer/LauncherUtils.kt
Normal file
108
src/main/kotlin/link/infra/packwiz/installer/LauncherUtils.kt
Normal file
@ -0,0 +1,108 @@
|
||||
package link.infra.packwiz.installer
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonIOException
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import link.infra.packwiz.installer.metadata.PackFile
|
||||
import link.infra.packwiz.installer.ui.IUserInterface
|
||||
import link.infra.packwiz.installer.util.Log
|
||||
import java.io.File
|
||||
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
|
||||
}
|
||||
|
||||
fun handleMultiMC(pf: PackFile, gson: Gson): LauncherStatus {
|
||||
// MultiMC MC and loader version checker
|
||||
val manifestPath = Paths.get(opts.multimcFolder, "mmc-pack.json").toString()
|
||||
val manifestFile = File(manifestPath)
|
||||
|
||||
if (!manifestFile.exists()) {
|
||||
return LauncherStatus.NotFound
|
||||
}
|
||||
|
||||
val multimcManifest = try {
|
||||
JsonParser.parseReader(manifestFile.reader())
|
||||
} 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")
|
||||
}
|
||||
|
||||
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 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 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 (version != pf.versions?.get(modLoader)) {
|
||||
manifestModified = true
|
||||
component.addProperty("version", pf.versions?.get(modLoader))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 }
|
||||
) {
|
||||
components.add(gson.toJsonTree(hashMapOf("uid" to modLoadersClasses.get(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 (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 newVers = pf.versions!!.map { Pair(it.key, it.value) }
|
||||
|
||||
|
||||
when (ui.showUpdateConfirmationDialog(oldVers, newVers)) {
|
||||
IUserInterface.UpdateConfirmationResult.CANCELLED -> {
|
||||
return LauncherStatus.Cancelled
|
||||
}
|
||||
IUserInterface.UpdateConfirmationResult.CONTINUE -> {
|
||||
return LauncherStatus.Succesful // Returning succesful as... Well, the user is telling us to continue
|
||||
}
|
||||
else -> {} // Compiler is giving warning about "non-exhaustive when", so i'll just add an empty one
|
||||
}
|
||||
|
||||
manifestFile.writeText(gson.toJson(multimcManifest))
|
||||
Log.info("Updated modpack Minecrafts and/or the modloaders version")
|
||||
|
||||
return LauncherStatus.Succesful
|
||||
}
|
||||
|
||||
return LauncherStatus.NoChanges
|
||||
}
|
||||
}
|
@ -71,6 +71,7 @@ class Main(args: Array<String>) {
|
||||
downloadURI = SpaceSafeURI(unparsedArgs[0]),
|
||||
side = cmd.getOptionValue("side")?.let((Side)::from),
|
||||
packFolder = cmd.getOptionValue("pack-folder"),
|
||||
multimcFolder = cmd.getOptionValue("multimc-folder"),
|
||||
manifestFile = cmd.getOptionValue("meta-file")
|
||||
)
|
||||
} catch (e: URISyntaxException) {
|
||||
@ -94,6 +95,7 @@ class Main(args: Array<String>) {
|
||||
options.addOption("s", "side", true, "Side to install mods from (client/server, defaults to client)")
|
||||
options.addOption(null, "title", true, "Title of the installer window")
|
||||
options.addOption(null, "pack-folder", true, "Folder to install the pack to (defaults to the JAR directory)")
|
||||
options.addOption(null, "multimc-folder", true, "The MultiMC pack folder (defaults to the parent of the pack directory)")
|
||||
options.addOption(null, "meta-file", true, "JSON file to store pack metadata, relative to the pack folder (defaults to packwiz.json)")
|
||||
}
|
||||
|
||||
|
@ -45,12 +45,13 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
|
||||
val downloadURI: SpaceSafeURI,
|
||||
val manifestFile: String,
|
||||
val packFolder: String,
|
||||
val multimcFolder: String,
|
||||
val side: Side
|
||||
) {
|
||||
// Horrible workaround for default params not working cleanly with nullable values
|
||||
companion object {
|
||||
fun construct(downloadURI: SpaceSafeURI, manifestFile: String?, packFolder: String?, side: Side?) =
|
||||
Options(downloadURI, manifestFile ?: "packwiz.json", packFolder ?: ".", side ?: Side.CLIENT)
|
||||
fun construct(downloadURI: SpaceSafeURI, manifestFile: String?, packFolder: String?, multimcFolder: String?, side: Side?) =
|
||||
Options(downloadURI, manifestFile ?: "packwiz.json", packFolder ?: ".", multimcFolder ?: "..", side ?: Side.CLIENT)
|
||||
}
|
||||
|
||||
}
|
||||
@ -97,6 +98,26 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
|
||||
handleCancellation()
|
||||
}
|
||||
|
||||
// Launcher checks
|
||||
val lu = LauncherUtils(opts, ui)
|
||||
|
||||
// MultiMC MC and loader version checker
|
||||
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")
|
||||
}
|
||||
handleCancellation()
|
||||
} catch (e: Exception) {
|
||||
ui.showErrorAndExit(e.message!!, e)
|
||||
}
|
||||
|
||||
if (ui.cancelButtonPressed) {
|
||||
showCancellationDialog()
|
||||
handleCancellation()
|
||||
}
|
||||
|
||||
ui.submitProgress(InstallProgress("Checking local files..."))
|
||||
|
||||
// Invalidation checking must be done here, as it must happen before pack/index hashes are checked
|
||||
@ -163,7 +184,6 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
|
||||
|
||||
handleCancellation()
|
||||
|
||||
// TODO: update MMC params, java args etc
|
||||
|
||||
// If there were errors, don't write the manifest/index hashes, to ensure they are rechecked later
|
||||
if (errorsOccurred) {
|
||||
|
@ -23,6 +23,8 @@ interface IUserInterface {
|
||||
|
||||
fun showCancellationDialog(): CancellationResult = CancellationResult.QUIT
|
||||
|
||||
fun showUpdateConfirmationDialog(oldVersions: List<Pair<String, String?>>, newVersions: List<Pair<String, String?>>): UpdateConfirmationResult = UpdateConfirmationResult.CANCELLED
|
||||
|
||||
fun awaitOptionalButton(showCancel: Boolean)
|
||||
|
||||
enum class ExceptionListResult {
|
||||
@ -33,6 +35,10 @@ interface IUserInterface {
|
||||
QUIT, CONTINUE
|
||||
}
|
||||
|
||||
enum class UpdateConfirmationResult {
|
||||
CANCELLED, CONTINUE, UPDATE
|
||||
}
|
||||
|
||||
var optionsButtonPressed: Boolean
|
||||
var cancelButtonPressed: Boolean
|
||||
|
||||
|
@ -166,6 +166,58 @@ class GUIHandler : IUserInterface {
|
||||
return future.get()
|
||||
}
|
||||
|
||||
override fun showUpdateConfirmationDialog(oldVersions: List<Pair<String, String?>>, newVersions: List<Pair<String, String?>>): IUserInterface.UpdateConfirmationResult {
|
||||
assert(newVersions.isNotEmpty())
|
||||
val future = CompletableFuture<IUserInterface.UpdateConfirmationResult>()
|
||||
EventQueue.invokeLater {
|
||||
val oldVersIndex = oldVersions.map { it.first to it.second }.toMap()
|
||||
val newVersIndex = newVersions.map { it.first to it.second }.toMap()
|
||||
val message = StringBuilder()
|
||||
message.append("<html>" +
|
||||
"This modpack uses newer versions of the following:<br>" +
|
||||
"<ul>")
|
||||
|
||||
for (oldVer in oldVersions) {
|
||||
val correspondingNewVer = newVersIndex[oldVer.first]
|
||||
message.append("<li>")
|
||||
message.append(oldVer.first.replaceFirstChar { it.uppercase() })
|
||||
message.append(": <font color=${if (oldVer.second != correspondingNewVer) "#ff0000" else "#000000"}>")
|
||||
message.append(oldVer.second ?: "Not found")
|
||||
message.append("</font></li>")
|
||||
}
|
||||
message.append("</ul>")
|
||||
|
||||
message.append("New versions:" +
|
||||
"<ul>")
|
||||
for (newVer in newVersions) {
|
||||
val correspondingOldVer = oldVersIndex[newVer.first]
|
||||
message.append("<li>")
|
||||
message.append(newVer.first.replaceFirstChar { it.uppercase() })
|
||||
message.append(": <font color=${if (newVer.second != correspondingOldVer) "#00ff00" else "#000000"}>")
|
||||
message.append(newVer.second ?: "Not found")
|
||||
message.append("</font></li>")
|
||||
}
|
||||
message.append("</ul><br>" +
|
||||
"Would you like to update the versions, launch without updating, or cancel the launch?")
|
||||
|
||||
|
||||
val options = arrayOf("Cancel", "Continue anyways", "Update")
|
||||
val result = JOptionPane.showOptionDialog(frmPackwizlauncher,
|
||||
message,
|
||||
"Updating MultiMC versions",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0])
|
||||
future.complete(
|
||||
when (result) {
|
||||
JOptionPane.CLOSED_OPTION, 0 -> IUserInterface.UpdateConfirmationResult.CANCELLED
|
||||
1 -> IUserInterface.UpdateConfirmationResult.CONTINUE
|
||||
2 -> IUserInterface.UpdateConfirmationResult.UPDATE
|
||||
else -> IUserInterface.UpdateConfirmationResult.CANCELLED
|
||||
}
|
||||
)
|
||||
}
|
||||
return future.get()
|
||||
}
|
||||
|
||||
override fun awaitOptionalButton(showCancel: Boolean) {
|
||||
EventQueue.invokeAndWait {
|
||||
frmPackwizlauncher.showOk(!showCancel)
|
||||
|
Loading…
x
Reference in New Issue
Block a user