Compare commits

..

8 Commits

Author SHA1 Message Date
comp500
fa9fe18215 Fix manual link resolution for files not visible in CF API (fixes #48) 2023-01-02 21:15:48 +00:00
comp500
01dcc09a78 Add main method so link.infra.packwiz.installer.Main can be launched directly
Useful for scripts or tools that already handle automatic updates of
packwiz-installer, such as docker-minecraft-server :)
Adds a message to suggest using the bootstrapper for automatic updates
2022-09-01 02:44:12 +01:00
comp500
a8f8444d45 Fix handling of old packwiz.json files with negative murmur2 values 2022-07-18 16:40:39 +01:00
comp500
d98baaf832 Change manifest file back to packwiz.json 2022-07-17 14:26:46 +01:00
comp500
783e35cf73 Fix OutOfBoundsException when parsing a single letter path component 2022-07-17 01:07:48 +01:00
comp500
ca172bdefc Fix order of modified MultiMC components when updating versions 2022-07-17 01:03:40 +01:00
comp500
b8cb9cc1aa Repack jar with Gradle so MANIFEST.MF is at the start
packwiz-installer-bootstrap relies on reading the manifest; breaking this means the update will run on every launch!
2022-07-17 00:30:22 +01:00
comp500
6f0beac1a1 Dry run fails the build when the release already exists; fix
Reverted to stop the snapshot build failing
2022-07-16 20:40:10 +01:00
6 changed files with 70 additions and 12 deletions

View File

@@ -22,7 +22,7 @@ repositories {
} }
val r8 by configurations.creating val r8 by configurations.creating
val shrinkJarOutput by configurations.creating { val distJarOutput by configurations.creating {
isCanBeResolved = false isCanBeResolved = false
isCanBeConsumed = true isCanBeConsumed = true
@@ -104,15 +104,27 @@ val shrinkJar by tasks.registering(JavaExec::class) {
) )
} }
// MANIFEST.MF must be one of the first 2 entries in the zip for JarInputStream to see it
// Gradle's JAR creation handles this whereas R8 doesn't, so the dist JAR is repacked
val distJar by tasks.registering(Jar::class) {
from(shrinkJar.map { zipTree(it.outputs.files.singleFile) })
archiveClassifier.set("all-repacked")
manifest {
from(shrinkJar.map { zipTree(it.outputs.files.singleFile).matching {
include("META-INF/MANIFEST.MF")
}.singleFile })
}
}
artifacts { artifacts {
add("shrinkJarOutput", shrinkJar) { add("distJarOutput", distJar) {
classifier = "dist" classifier = "dist"
} }
} }
// Used for vscode launch.json // Used for vscode launch.json
val copyJar by tasks.registering(Copy::class) { val copyJar by tasks.registering(Copy::class) {
from(shrinkJar) from(distJar)
rename("packwiz-installer-(.*)\\.jar", "packwiz-installer.jar") rename("packwiz-installer-(.*)\\.jar", "packwiz-installer.jar")
into(layout.buildDirectory.dir("dist")) into(layout.buildDirectory.dir("dist"))
outputs.file(layout.buildDirectory.dir("dist").map { it.file("packwiz-installer.jar") }) outputs.file(layout.buildDirectory.dir("dist").map { it.file("packwiz-installer.jar") })
@@ -130,12 +142,11 @@ githubRelease {
draft(true) draft(true)
token(findProperty("github.token") as String?) token(findProperty("github.token") as String?)
releaseAssets(layout.buildDirectory.dir("dist").map { it.file("packwiz-installer.jar") }.get()) releaseAssets(layout.buildDirectory.dir("dist").map { it.file("packwiz-installer.jar") }.get())
dryRun(project.findProperty("release") != "true")
} }
tasks.githubRelease { tasks.githubRelease {
dependsOn(copyJar) dependsOn(copyJar)
enabled = project.hasProperty("github.token") enabled = project.hasProperty("github.token") && project.findProperty("release") == "true"
} }
tasks.publish { tasks.publish {
@@ -156,7 +167,7 @@ tasks.compileTestKotlin {
} }
val javaComponent = components["java"] as AdhocComponentWithVariants val javaComponent = components["java"] as AdhocComponentWithVariants
javaComponent.addVariantsFromConfiguration(shrinkJarOutput) { javaComponent.addVariantsFromConfiguration(distJarOutput) {
mapToMavenScope("runtime") mapToMavenScope("runtime")
mapToOptional() mapToOptional()
} }

View File

@@ -50,7 +50,20 @@ class LauncherUtils internal constructor(private val opts: UpdateManager.Options
"net.minecraftforge" to "forge", "net.minecraftforge" to "forge",
"net.fabricmc.fabric-loader" to "fabric", "net.fabricmc.fabric-loader" to "fabric",
"org.quiltmc.quilt-loader" to "quilt", "org.quiltmc.quilt-loader" to "quilt",
"com.mumfrey.liteloader" to "liteloader") "com.mumfrey.liteloader" to "liteloader"
)
// MultiMC requires components to be sorted; this is defined in the MultiMC meta repo, but they seem to
// be the same for every version so they are just used directly here
val componentOrders = mapOf(
"net.minecraft" to -2,
"org.lwjgl" to -1,
"org.lwjgl3" to -1,
"net.minecraftforge" to 5,
"net.fabricmc.fabric-loader" to 10,
"org.quiltmc.quilt-loader" to 10,
"com.mumfrey.liteloader" to 10,
"net.fabricmc.intermediary" to 11
)
val modLoadersClasses = modLoaders.entries.associate{(k,v)-> v to k} val modLoadersClasses = modLoaders.entries.associate{(k,v)-> v to k}
val loaderVersionsFound = HashMap<String, String?>() val loaderVersionsFound = HashMap<String, String?>()
val outdatedLoaders = mutableSetOf<String>() val outdatedLoaders = mutableSetOf<String>()
@@ -92,6 +105,15 @@ class LauncherUtils internal constructor(private val opts: UpdateManager.Options
} }
if (manifestModified) { if (manifestModified) {
// Sort manifest by component order
val sortedComponents = components.sortedWith(nullsLast(compareBy {
if (it.isJsonObject) {
componentOrders[it.asJsonObject["uid"]?.asString]
} else { null }
}))
components.removeAll { true }
sortedComponents.forEach { components.add(it) }
// The manifest has been modified, so before saving it we'll ask the user // 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 // if they wanna update it, continue without updating it, or exit
val oldVers = loaderVersionsFound.map { Pair(it.key, it.value) } val oldVers = loaderVersionsFound.map { Pair(it.key, it.value) }

View File

@@ -101,7 +101,7 @@ class Main(args: Array<String>) {
cmd.getOptionValue("multimc-folder")?.let{ PackwizFilePath(it.toPath()) } ?: PackwizFilePath("..".toPath()) cmd.getOptionValue("multimc-folder")?.let{ PackwizFilePath(it.toPath()) } ?: PackwizFilePath("..".toPath())
} }
val manifestFile = ui.wrap("Invalid manifest file path") { val manifestFile = ui.wrap("Invalid manifest file path") {
packFolder / (cmd.getOptionValue("meta-file") ?: "manifest.json") packFolder / (cmd.getOptionValue("meta-file") ?: "packwiz.json")
} }
// Start update process! // Start update process!
@@ -135,6 +135,12 @@ class Main(args: Array<String>) {
options.addOption("g", "no-gui", false, "Don't display a GUI to show update progress") options.addOption("g", "no-gui", false, "Don't display a GUI to show update progress")
options.addOption("h", "help", false, "Display this message") // Implemented in packwiz-installer-bootstrap! options.addOption("h", "help", false, "Display this message") // Implemented in packwiz-installer-bootstrap!
} }
@JvmStatic
fun main(args: Array<String>) {
Log.info("packwiz-installer was started without packwiz-installer-bootstrap. Use the bootstrapper for automatic updates! (Disregard this message if you have your own update mechanism)")
Main(args)
}
} }
// Actual main() is in RequiresBootstrap! // Actual main() is in RequiresBootstrap!

View File

@@ -97,6 +97,16 @@ fun resolveCfMetadata(mods: List<IndexFile.File>, packFolder: PackwizFilePath, c
} }
} }
// Some file types don't show up in the API at all! (e.g. shaderpacks)
// Add unresolved files to manualDownloadMods
for ((fileId, file) in fileIdMap) {
if (file.linkedFile != null) {
if (file.linkedFile!!.resolvedUpdateData["curseforge"] == null) {
manualDownloadMods[(file.linkedFile!!.update["curseforge"] as CurseForgeUpdateData).projectId] = Pair(file, fileId)
}
}
}
if (manualDownloadMods.isNotEmpty()) { if (manualDownloadMods.isNotEmpty()) {
val reqModsData = GetModsRequest(manualDownloadMods.keys.toList()) val reqModsData = GetModsRequest(manualDownloadMods.keys.toList())
val reqMods = Request.Builder() val reqMods = Request.Builder()

View File

@@ -21,7 +21,14 @@ data class Hash<T>(val type: HashFormat<T>, val value: T) {
object UInt: Encoding<kotlin.UInt> { object UInt: Encoding<kotlin.UInt> {
override fun encodeToString(value: kotlin.UInt) = value.toString() override fun encodeToString(value: kotlin.UInt) = value.toString()
override fun decodeFromString(str: String) = str.toUInt() override fun decodeFromString(str: String) =
try {
str.toUInt()
} catch (e: NumberFormatException) {
// Old packwiz.json values are signed; if they are negative they should be parsed as signed integers
// and reinterpreted as unsigned integers
str.toInt().toUInt()
}
} }
} }

View File

@@ -43,9 +43,11 @@ abstract class PackwizPath<T: PackwizPath<T>>(path: String? = null) {
} else { } else {
canonicalised += componentNorm canonicalised += componentNorm
// Don't allow volume letters (allows traversal to the root on Windows) // Don't allow volume letters (allows traversal to the root on Windows)
if (componentNorm[0] in 'a'..'z' || componentNorm[0] in 'A'..'Z') { if (componentNorm.length == 2) {
if (componentNorm[1] == ':') { if (componentNorm[0] in 'a'..'z' || componentNorm[0] in 'A'..'Z') {
throw RequestException.Validation.PathContainsVolumeLetter(path) if (componentNorm[1] == ':') {
throw RequestException.Validation.PathContainsVolumeLetter(path)
}
} }
} }
} }