diff --git a/build.gradle.kts b/build.gradle.kts
index 0ba42d5..b751379 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -31,7 +31,7 @@ repositories {
 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")
diff --git a/src/main/kotlin/link/infra/packwiz/installer/LauncherUtils.kt b/src/main/kotlin/link/infra/packwiz/installer/LauncherUtils.kt
new file mode 100644
index 0000000..1e9eb05
--- /dev/null
+++ b/src/main/kotlin/link/infra/packwiz/installer/LauncherUtils.kt
@@ -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
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/link/infra/packwiz/installer/Main.kt b/src/main/kotlin/link/infra/packwiz/installer/Main.kt
index f22632b..2991205 100644
--- a/src/main/kotlin/link/infra/packwiz/installer/Main.kt
+++ b/src/main/kotlin/link/infra/packwiz/installer/Main.kt
@@ -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)")
 		}
 
diff --git a/src/main/kotlin/link/infra/packwiz/installer/UpdateManager.kt b/src/main/kotlin/link/infra/packwiz/installer/UpdateManager.kt
index 3cb2e3d..425e538 100644
--- a/src/main/kotlin/link/infra/packwiz/installer/UpdateManager.kt
+++ b/src/main/kotlin/link/infra/packwiz/installer/UpdateManager.kt
@@ -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) {
diff --git a/src/main/kotlin/link/infra/packwiz/installer/ui/IUserInterface.kt b/src/main/kotlin/link/infra/packwiz/installer/ui/IUserInterface.kt
index 55c1ead..ecd7bd6 100644
--- a/src/main/kotlin/link/infra/packwiz/installer/ui/IUserInterface.kt
+++ b/src/main/kotlin/link/infra/packwiz/installer/ui/IUserInterface.kt
@@ -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
 
diff --git a/src/main/kotlin/link/infra/packwiz/installer/ui/gui/GUIHandler.kt b/src/main/kotlin/link/infra/packwiz/installer/ui/gui/GUIHandler.kt
index 36a46f6..508e3cd 100644
--- a/src/main/kotlin/link/infra/packwiz/installer/ui/gui/GUIHandler.kt
+++ b/src/main/kotlin/link/infra/packwiz/installer/ui/gui/GUIHandler.kt
@@ -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)