Skip to content

Commit 056d90a

Browse files
committed
feat: library rewriting, directly download libs from mojank, bundler extraction
currently works for 1.21.3, 1.8 launches with errors, 1.18.2 has a mismatched vanilla hash, potentially need to look into that
1 parent ebd1d5f commit 056d90a

File tree

6 files changed

+168
-24
lines changed

6 files changed

+168
-24
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package io.github.sploonmc.bundler
2+
3+
import java.io.FileInputStream
4+
import java.io.FileOutputStream
5+
import java.nio.file.Path
6+
import java.util.jar.JarFile
7+
import java.util.zip.ZipInputStream
8+
import kotlin.io.path.copyTo
9+
import kotlin.io.path.createDirectories
10+
import kotlin.io.path.notExists
11+
import kotlin.io.path.readText
12+
13+
private const val JAR_VERSIONS_PATH = "META-INF/versions"
14+
private const val JAR_VERSIONS_LIST_PATH = "META-INF/versions.list"
15+
16+
fun JarFile.needsExtraction() = getJarEntry(JAR_VERSIONS_PATH) != null
17+
18+
fun extractJar(jarFile: Path, outputDirectory: Path) {
19+
if (outputDirectory.notExists()) {
20+
outputDirectory.createDirectories()
21+
}
22+
23+
FileInputStream(jarFile.toFile()).use { fileStream ->
24+
ZipInputStream(fileStream).use { zipStream ->
25+
var entry = zipStream.nextEntry
26+
while (entry != null) {
27+
val outputPath = outputDirectory.resolve(entry.name)
28+
29+
if (entry.isDirectory) {
30+
outputPath.createDirectories()
31+
} else {
32+
outputPath.parent.createDirectories()
33+
34+
FileOutputStream(outputPath.toFile()).use { fileOutStream ->
35+
zipStream.copyTo(fileOutStream)
36+
}
37+
}
38+
39+
entry = zipStream.nextEntry
40+
}
41+
}
42+
}
43+
}
44+
45+
fun extractServer(jarFile: Path, extractDir: Path, target: Path) {
46+
extractJar(jarFile, extractDir)
47+
48+
val extractionPath = extractDir.resolve(JAR_VERSIONS_PATH)
49+
val versionsFilePath = extractDir.resolve(JAR_VERSIONS_LIST_PATH)
50+
val content = runCatching(versionsFilePath::readText).getOrNull()
51+
52+
val jar = if (content == null) {
53+
println("Failed reading versions.list. Using $jarFile instead.")
54+
jarFile
55+
} else {
56+
extractionPath.resolve(content.split("\t")[2])
57+
}
58+
59+
if (target.notExists()) {
60+
target.parent.createDirectories()
61+
}
62+
63+
jar.copyTo(target)
64+
}

src/main/kotlin/io/github/sploonmc/bundler/SploonBundler.kt

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import java.nio.file.SimpleFileVisitor
1818
import java.nio.file.attribute.BasicFileAttributes
1919
import java.util.jar.JarFile
2020
import kotlin.io.path.ExperimentalPathApi
21+
import kotlin.io.path.copyTo
2122
import kotlin.io.path.createDirectories
2223
import kotlin.io.path.createDirectory
2324
import kotlin.io.path.exists
@@ -98,16 +99,22 @@ class SploonBundler(val minecraftVersion: String, workDir: Path, val serverArgs:
9899
val deps = MavenDependency.parseLines(libs.lines())
99100
val spigotJarPath = setupVanilla()
100101
deps.forEach { dep ->
101-
dep.download(false)
102+
dep.rewrite().download(false)
102103
}
103104

104105
// Handle mojang deps to cover everything
105-
versionMeta.libraries.map(PistonLibrary::name).forEach { lib ->
106-
MavenDependency
107-
.parseLine(lib)
108-
.download(false)
109-
.map(Path::toUri)
110-
.map(URI::toURL)
106+
versionMeta.libraries.forEach { lib ->
107+
if (lib.downloads.artifact != null) {
108+
downloadUri(URI(lib.downloads.artifact.url), librariesDir.resolve(lib.downloads.artifact.path))
109+
} else if (lib.downloads.classifiers != null) {
110+
// we assume this is a native library only required for the client
111+
} else {
112+
MavenDependency
113+
.parseLine(lib.name)
114+
.rewrite()
115+
.also(::println)
116+
.download(false)
117+
}
111118
}
112119

113120
val classpath = buildList {
@@ -147,13 +154,12 @@ class SploonBundler(val minecraftVersion: String, workDir: Path, val serverArgs:
147154
}
148155

149156
if (vanillaServer.notExists()) {
150-
JarFile(vanillaBundler.toFile()).use { zip ->
151-
val entry = zip.getEntry("META-INF/versions/$minecraftVersion/server-$minecraftVersion.jar")
152-
zip.getInputStream(entry).use { input ->
153-
vanillaServer.outputStream().use { output ->
154-
input.transferTo(output)
155-
}
156-
}
157+
val needsExtraction = JarFile(vanillaBundler.toFile()).use(JarFile::needsExtraction)
158+
159+
if (!needsExtraction) {
160+
vanillaBundler.copyTo(vanillaServer)
161+
} else {
162+
extractServer(vanillaBundler, bundlerDir.resolve("$minecraftVersion-extracted"), vanillaServer)
157163
}
158164
}
159165

@@ -181,7 +187,7 @@ class SploonBundler(val minecraftVersion: String, workDir: Path, val serverArgs:
181187
private set
182188

183189
val TRANSFORMERS = mapOf<String, (ClassVisitor) -> ClassVisitor>(
184-
DedicatedServerTransformer.TARGET to { visitor -> DedicatedServerTransformer(visitor) }
190+
DedicatedServerTransformer.TARGET to ::DedicatedServerTransformer
185191
)
186192
}
187193
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.github.sploonmc.bundler.library
2+
3+
interface LibraryRewriter {
4+
fun isApplicable(dependency: MavenDependency): Boolean
5+
6+
fun rewrite(dependency: MavenDependency): MavenDependency
7+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.github.sploonmc.bundler.library
2+
3+
object LibraryRewriters {
4+
val REWRITERS = listOf(VecMathRewriter, )
5+
6+
object VecMathRewriter : LibraryRewriter {
7+
override fun isApplicable(dependency: MavenDependency) = dependency.artifactId == "vecmath"
8+
9+
override fun rewrite(dependency: MavenDependency) = dependency.copy(groupId = "javax.vecmath")
10+
}
11+
12+
object LwjglPlatformRewrier : LibraryRewriter {
13+
override fun isApplicable(dependency: MavenDependency) = dependency.artifactId == "lwjgl-platform"
14+
15+
override fun rewrite(dependency: MavenDependency) = dependency.copy(groupId = "org.lwjgl")
16+
}
17+
}

src/main/kotlin/io/github/sploonmc/bundler/library/MavenDependency.kt

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ data class MavenDependency(val groupId: String, val artifactId: String, val vers
2727
return true
2828
}
2929

30+
fun rewrite(): MavenDependency {
31+
var result = this
32+
33+
LibraryRewriters.REWRITERS.forEach { rewriter ->
34+
if (!rewriter.isApplicable(result)) return@forEach
35+
result = rewriter.rewrite(result)
36+
}
37+
38+
return result
39+
}
3040

3141
companion object {
3242
private fun download(dependency: MavenDependency, override: Boolean): List<Path> {
@@ -39,14 +49,24 @@ data class MavenDependency(val groupId: String, val artifactId: String, val vers
3949
val gav =
4050
"${dependency.groupId.replace(".", "/")}/${dependency.artifactId}/${dependency.version}/"
4151

42-
val repository = Repository.parseRepository(dependency.groupId)
52+
val repository = Repository.parseRepository(dependency)
4353

4454
var version = dependency.version
4555
if (dependency.version.contains("SNAPSHOT"))
4656
version = latestSnapshotVersion(repository.url + gav + "maven-metadata.xml")
4757

4858
val suffix = gav + "${dependency.artifactId}-$version.jar"
49-
val url = URI(repository.url + suffix)
59+
var url = URI(repository.url + suffix)
60+
61+
val isBungeeChatNonSnapshot = dependency.artifactId == "bungeecord-chat" && !dependency.version.endsWith("-SNAPSHOT")
62+
63+
if (isBungeeChatNonSnapshot) {
64+
val metaUrl =
65+
repository.url + "${dependency.groupId.replace(".", "/")}/${dependency.artifactId}/${dependency.version}-SNAPSHOT/maven-metadata.xml"
66+
67+
val latestSnapshot = latestSnapshotVersion(metaUrl)
68+
url = URI(repository.url + "${dependency.groupId.replace(".", "/")}/${dependency.artifactId}/$version-SNAPSHOT/${dependency.artifactId}-$latestSnapshot.jar")
69+
}
5070

5171
val outputList = mutableListOf<Path>()
5272
outputList.addAll(locateTransitiveDeps(dependency, repository, version).flatMap { it.download(false) }.toMutableList())
@@ -86,7 +106,15 @@ data class MavenDependency(val groupId: String, val artifactId: String, val vers
86106
}
87107

88108
private fun locateTransitiveDeps(parent: MavenDependency, repository: Repository, version: String): List<MavenDependency> {
89-
val gavWithPom = "${parent.groupId.replace(".", "/")}/${parent.artifactId}/${parent.version}/${parent.artifactId}-$version.pom"
109+
val isBungeeChatNonSnapshot = parent.artifactId == "bungeecord-chat" && !parent.version.endsWith("-SNAPSHOT")
110+
var gavWithPom = "${parent.groupId.replace(".", "/")}/${parent.artifactId}/${parent.version}/${parent.artifactId}-$version.pom"
111+
112+
if (isBungeeChatNonSnapshot) {
113+
val metaUrl =
114+
repository.url + "${parent.groupId.replace(".", "/")}/${parent.artifactId}/${parent.version}-SNAPSHOT/maven-metadata.xml"
115+
val latestSnapshot = latestSnapshotVersion(metaUrl)
116+
gavWithPom = "${parent.groupId.replace(".", "/")}/${parent.artifactId}/$version-SNAPSHOT/${parent.artifactId}-$latestSnapshot.pom"
117+
}
90118

91119
val parser = XmlParser()
92120
val url = URI(repository.url + gavWithPom).toURL()
@@ -127,13 +155,22 @@ data class MavenDependency(val groupId: String, val artifactId: String, val vers
127155

128156
private enum class Repository(val url: String) {
129157
MOJANG("https://libraries.minecraft.net/"),
130-
SPIGOT("https://hub.spigotmc.org/nexus/repository/snapshots/"),
131-
CENTRAL("https://repo1.maven.org/maven2/");
158+
SPIGOT_SNAPSHOTS("https://hub.spigotmc.org/nexus/repository/snapshots/"),
159+
SPIGOT("https://hub.spigotmc.org/nexus/repository/public/"),
160+
CENTRAL("https://repo1.maven.org/maven2/"),
161+
VELOCITY("https://nexus.velocitypowered.com/repository/maven-public/");
132162

133163
companion object {
134-
fun parseRepository(group: String): Repository {
135-
if (group.endsWith("mojang")) return MOJANG
136-
if (group.endsWith("spigotmc")) return SPIGOT
164+
fun parseRepository(dependency: MavenDependency): Repository {
165+
if (dependency.groupId.endsWith("mojang")
166+
|| dependency.artifactId.endsWith("mojang")
167+
|| dependency.groupId.endsWith("lwjgl")
168+
&& dependency.artifactId != "lwjgl-platform"
169+
) return MOJANG
170+
if (dependency.artifactId == "lwjgl-platform" && "nightly" in dependency.version) return MOJANG
171+
if (dependency.groupId.endsWith("md-5")) return SPIGOT
172+
if (dependency.groupId.endsWith("spigotmc")) return SPIGOT_SNAPSHOTS
173+
if (dependency.groupId.endsWith("paulscode")) return VELOCITY
137174
return CENTRAL
138175
}
139176
}

src/main/kotlin/io/github/sploonmc/bundler/piston/pistonSchemas.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
package io.github.sploonmc.bundler.piston
22

3-
import kotlinx.serialization.SerialName
43
import kotlinx.serialization.Serializable
4+
import kotlinx.serialization.json.JsonObject
55

66
@Serializable
77
data class PistonVersion(
88
val id: String,
99
val url: String,
1010
)
1111

12+
@Serializable
13+
data class PistonLibraryArtifact(
14+
val url: String,
15+
val path: String
16+
)
17+
18+
@Serializable
19+
data class PistonLibraryDownloads(
20+
val artifact: PistonLibraryArtifact? = null,
21+
val classifiers: JsonObject? = null
22+
)
23+
1224
@Serializable
1325
data class PistonLibrary(
1426
val name: String,
27+
val downloads: PistonLibraryDownloads
1528
)
1629

1730
@Serializable

0 commit comments

Comments
 (0)