150 lines
4.7 KiB
Kotlin
150 lines
4.7 KiB
Kotlin
import java.util.zip.CRC32
|
|
import java.util.zip.ZipEntry
|
|
import java.util.zip.ZipFile
|
|
import java.util.zip.ZipOutputStream
|
|
|
|
// Text file extensions for LF normalization
|
|
val textExtensions = setOf(".kt", ".java", ".properties", ".json", ".toml", ".xml", ".txt", ".MF")
|
|
fun isTextFile(name: String) = textExtensions.any { name.endsWith(it) } || name.startsWith("META-INF/services/")
|
|
|
|
fun normalizeContent(name: String, content: ByteArray): ByteArray {
|
|
if (!isTextFile(name)) return content
|
|
return content.toString(Charsets.UTF_8)
|
|
.replace("\r\n", "\n").replace("\r", "\n")
|
|
.toByteArray(Charsets.UTF_8)
|
|
}
|
|
|
|
fun writeStoredEntry(output: ZipOutputStream, name: String, content: ByteArray) {
|
|
val normalized = normalizeContent(name, content)
|
|
val entry = ZipEntry(name).apply {
|
|
time = 0
|
|
size = normalized.size.toLong()
|
|
compressedSize = normalized.size.toLong()
|
|
crc = CRC32().apply { update(normalized) }.value
|
|
method = ZipEntry.STORED
|
|
}
|
|
output.putNextEntry(entry)
|
|
output.write(normalized)
|
|
output.closeEntry()
|
|
}
|
|
|
|
plugins {
|
|
id("java")
|
|
}
|
|
|
|
java {
|
|
toolchain {
|
|
languageVersion.set(JavaLanguageVersion.of(21))
|
|
}
|
|
}
|
|
|
|
repositories {
|
|
mavenCentral()
|
|
maven {
|
|
name = "hytale-release"
|
|
url = uri("https://maven.hytale.com/release")
|
|
}
|
|
maven {
|
|
name = "hytale-pre-release"
|
|
url = uri("https://maven.hytale.com/pre-release")
|
|
}
|
|
}
|
|
|
|
val hytaleServer: Configuration by configurations.creating
|
|
|
|
val patchedJar = rootDir.resolve("repo/applications/HytaleServerPatched.jar")
|
|
|
|
dependencies {
|
|
hytaleServer("com.hypixel.hytale:Server:2026.02.19-1a311a592")
|
|
}
|
|
|
|
configurations {
|
|
compileOnly.get().extendsFrom(hytaleServer)
|
|
}
|
|
|
|
tasks.withType<JavaCompile> {
|
|
options.compilerArgs.addAll(listOf(
|
|
"--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED"
|
|
))
|
|
}
|
|
|
|
sourceSets {
|
|
main {
|
|
java {
|
|
// Patch files go in runtime/hytale-server/src/
|
|
// Use the same package structure as the original JAR
|
|
// e.g., src/com/hypixel/hytale/server/SomeClass.java
|
|
srcDir("src")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task to create the patched JAR by overlaying compiled classes on top of original
|
|
tasks.register("patchJar") {
|
|
group = "coldfusion"
|
|
description = "Creates a patched HytaleServer JAR with compiled modifications"
|
|
|
|
dependsOn(tasks.compileJava)
|
|
|
|
inputs.files(hytaleServer)
|
|
inputs.files(tasks.compileJava.get().outputs.files).optional()
|
|
outputs.file(patchedJar)
|
|
|
|
doLast {
|
|
val compiledClassesDir = tasks.compileJava.get().destinationDirectory.get().asFile
|
|
val originalJar = hytaleServer.singleFile
|
|
|
|
// Collect all compiled class files with their relative paths
|
|
val patchedFiles = mutableMapOf<String, ByteArray>()
|
|
if (compiledClassesDir.exists()) {
|
|
compiledClassesDir.walkTopDown()
|
|
.filter { it.isFile }
|
|
.forEach { file ->
|
|
val relativePath = file.relativeTo(compiledClassesDir).path.replace(File.separatorChar, '/')
|
|
patchedFiles[relativePath] = file.readBytes()
|
|
}
|
|
}
|
|
|
|
logger.lifecycle("Patching JAR with ${patchedFiles.size} compiled files")
|
|
|
|
// Collect all entries: patched files override original
|
|
val allEntries = mutableMapOf<String, ByteArray?>() // null = directory
|
|
|
|
ZipFile(originalJar).use { original ->
|
|
original.entries().asSequence().forEach { entry ->
|
|
allEntries[entry.name] = if (entry.isDirectory) null else original.getInputStream(entry).readBytes()
|
|
}
|
|
}
|
|
|
|
// Override with patched files
|
|
patchedFiles.forEach { (path, content) ->
|
|
logger.lifecycle(" Patching: $path")
|
|
allEntries[path] = content
|
|
}
|
|
|
|
// Write sorted entries with STORED compression and LF normalization
|
|
patchedJar.outputStream().buffered().let { ZipOutputStream(it) }.use { output ->
|
|
output.setMethod(ZipOutputStream.STORED)
|
|
|
|
allEntries.keys.sorted().forEach { name ->
|
|
val content = allEntries[name]
|
|
if (content == null) {
|
|
// Directory
|
|
val entry = ZipEntry(name).apply {
|
|
time = 0
|
|
size = 0
|
|
compressedSize = 0
|
|
crc = 0
|
|
method = ZipEntry.STORED
|
|
}
|
|
output.putNextEntry(entry)
|
|
output.closeEntry()
|
|
} else {
|
|
writeStoredEntry(output, name, content)
|
|
}
|
|
}
|
|
}
|
|
|
|
logger.lifecycle("Created patched JAR: $patchedJar")
|
|
}
|
|
} |