Files
hytale-server/build.gradle.kts
2026-02-19 16:43:06 +00:00

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")
}
}