Skip to content

Commit ee01abc

Browse files
committed
feat(forge): reuse processor V2 engine for verified Forge installer
1 parent 8066594 commit ee01abc

2 files changed

Lines changed: 46 additions & 13 deletions

File tree

‎launcher-core/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/core/loader/InstallerLoaderDiscovery.kt‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,28 @@ class NeoForgeLoaderProvider(http: HttpClient, private val downloads: DownloadMa
7676
}
7777
}
7878

79-
class ForgeLoaderProvider(http: HttpClient) : InstallerBackedLoaderProvider(
79+
class ForgeLoaderProvider(http: HttpClient, private val downloader: DownloadManager? = null) : InstallerBackedLoaderProvider(
8080
http, "https://maven.minecraftforge.net/net/minecraftforge/forge/maven-metadata.xml",
81-
) {
81+
), PreparedModLoaderProvider {
8282
override val id = "forge"
83+
override suspend fun install(
84+
base: ResolvedMinecraftVersion,
85+
version: String,
86+
layout: MinecraftInstallationLayout,
87+
runtime: JavaRuntime,
88+
): ResolvedMinecraftVersion {
89+
ModLoaderSelection("forge", version)
90+
if (compatibleVersions(base.id).none { it.version == version }) {
91+
throw IncompatibleModLoaderException(
92+
"Forge " + version + " is not compatible with Minecraft " + base.id,
93+
)
94+
}
95+
val downloads = downloader
96+
?: throw ModLoaderException("Forge installer download infrastructure is unavailable.")
97+
return ProcessorInstallerV2Engine(
98+
http, downloads, flavor = InstallerV2Flavor.FORGE,
99+
).install(base, version, layout, runtime)
100+
}
83101
override suspend fun compatibleVersions(minecraftVersionId: String): List<ModLoaderVersion> {
84102
require(minecraftVersionId.matches(Regex("[A-Za-z0-9._-]{1,80}")))
85103
val prefix = minecraftVersionId + "-"

‎launcher-core/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/core/loader/NeoForgeInstallerEngine.kt‎

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,14 @@ import pl.syntaxdevteam.syntaxlauncher.core.storage.MinecraftInstallationLayout
4141
* Executes NeoForge V2 client processors in a dedicated child JVM and staging tree.
4242
* No instance game directory is written. Existing shared cache artifacts are never replaced.
4343
*/
44-
class NeoForgeInstallerEngine(
44+
enum class InstallerV2Flavor { NEOFORGE, FORGE }
45+
46+
/** Shared V2 processor pipeline. Only the verified installer acquisition is loader-specific. */
47+
class ProcessorInstallerV2Engine(
4548
private val http: HttpClient,
4649
private val downloads: DownloadManager,
4750
private val preflight: NeoForgeInstallerPreflight = NeoForgeInstallerPreflight(http, downloads),
51+
private val flavor: InstallerV2Flavor = InstallerV2Flavor.NEOFORGE,
4852
) {
4953
private val json = Json { ignoreUnknownKeys = true }
5054
private val coordinate = Regex("""[A-Za-z0-9_.-]+:[A-Za-z0-9_.-]+:[A-Za-z0-9._+~-]+(?::[A-Za-z0-9_.-]+)?(?:@[A-Za-z0-9]+)?""")
@@ -60,18 +64,26 @@ class NeoForgeInstallerEngine(
6064
runtime: JavaRuntime,
6165
): ResolvedMinecraftVersion {
6266
check(Files.isRegularFile(layout.versionJar(base.id), LinkOption.NOFOLLOW_LINKS)) {
63-
"Verified Vanilla client must be prepared before NeoForge installation."
67+
"Verified Vanilla client must be prepared before Forge-family installation."
68+
}
69+
val (installerJar, inspection) = when (flavor) {
70+
InstallerV2Flavor.NEOFORGE -> preflight.inspect(
71+
base.id, loaderVersion, layout.versionsDir,
72+
).let { it.installerJar to it.inspection }
73+
InstallerV2Flavor.FORGE -> ForgeInstallerPreflight(http, downloads).inspect(
74+
base.id, loaderVersion, layout.versionsDir,
75+
).let { it.installerJar to it.inspection }
6476
}
65-
val manifest = preflight.inspect(base.id, loaderVersion, layout.versionsDir)
66-
val profile = readJson(manifest.installerJar, "install_profile.json")
67-
val version = readJson(manifest.installerJar, "version.json")
77+
val profile = readJson(installerJar, "install_profile.json")
78+
val version = readJson(installerJar, "version.json")
6879
val mainClass = str(version, "mainClass")
6980
?: throw ModLoaderException("NeoForge runtime profile has no main class.")
7081
require(mainClass.matches(Regex("""[A-Za-z_$][A-Za-z0-9_$.]*""")))
71-
require(str(version, "id") == manifest.inspection.launcherVersionId)
82+
require(str(version, "id") == inspection.launcherVersionId)
7283

7384
return withContext(Dispatchers.IO) {
74-
val staging = layout.versionsDir.resolve(".neoforge-" + loaderVersion + "-" + UUID.randomUUID())
85+
val staging = layout.versionsDir.resolve("." + flavor.name.lowercase() + "-" +
86+
loaderVersion + "-" + UUID.randomUUID())
7587
val libraryRoot = staging.resolve("libraries")
7688
Files.createDirectories(libraryRoot)
7789
try {
@@ -94,8 +106,8 @@ class NeoForgeInstallerEngine(
94106
sources[name] = url
95107
val target = libraryRoot.resolve(relative)
96108
val embedded = "maven/" + relative
97-
if (hasEntry(manifest.installerJar, embedded)) {
98-
extract(manifest.installerJar, embedded, target)
109+
if (hasEntry(installerJar, embedded)) {
110+
extract(installerJar, embedded, target)
99111
str(artifact, "sha1")?.let { expected ->
100112
require(hash(target).equals(expected, ignoreCase = true)) {
101113
"Embedded NeoForge library checksum mismatch: " + name
@@ -116,7 +128,7 @@ class NeoForgeInstallerEngine(
116128
"MINECRAFT_JAR" to layout.versionJar(base.id).toString(),
117129
"MINECRAFT_VERSION" to base.id,
118130
"ROOT" to staging.toString(),
119-
"INSTALLER" to manifest.installerJar.toString(),
131+
"INSTALLER" to installerJar.toString(),
120132
"LIBRARY_DIR" to libraryRoot.toString(),
121133
)
122134
profile["data"].obj().orEmpty().forEach { (key, sided) ->
@@ -135,7 +147,7 @@ class NeoForgeInstallerEngine(
135147
}
136148
val target = staging.resolve(embedded).normalize()
137149
require(target.startsWith(staging))
138-
extract(manifest.installerJar, embedded, target)
150+
extract(installerJar, embedded, target)
139151
target.toString()
140152
}
141153
else -> throw ModLoaderException("Unsupported NeoForge data value.")
@@ -352,3 +364,6 @@ class NeoForgeInstallerEngine(
352364
private fun array(value: JsonObject, key: String): List<JsonElement> =
353365
(value[key] as? JsonArray)?.toList().orEmpty()
354366
}
367+
368+
/** Keep the existing NeoForge integration and its regression tests source compatible. */
369+
typealias NeoForgeInstallerEngine = ProcessorInstallerV2Engine

0 commit comments

Comments
 (0)