diff --git a/build.gradle.kts b/build.gradle.kts index ecfea96..9f67ea2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,9 +28,8 @@ dependencies { // Configure server testing runHytale { - // TODO: Update this URL when Hytale server is available - // Using Paper server as placeholder for testing the runServer functionality - jarUrl = "https://fill-data.papermc.io/v1/objects/d5f47f6393aa647759f101f02231fa8200e5bccd36081a3ee8b6a5fd96739057/paper-1.21.10-115.jar" + jarUrl = "./HytaleServer.jar" + assetsPath = "./Assets.zip" } tasks { diff --git a/buildSrc/src/main/kotlin/RunHytalePlugin.kt b/buildSrc/src/main/kotlin/RunHytalePlugin.kt index e6a3b10..8ea3f6c 100644 --- a/buildSrc/src/main/kotlin/RunHytalePlugin.kt +++ b/buildSrc/src/main/kotlin/RunHytalePlugin.kt @@ -5,6 +5,7 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskProvider import java.io.File +import java.io.InputStream import java.net.URI import java.security.MessageDigest @@ -14,30 +15,24 @@ import java.security.MessageDigest * Usage: * runHytale { * jarUrl = "https://example.com/hytale-server.jar" + * assetsPath = "Assets.zip" * } * * ./gradlew runServer */ open class RunHytalePlugin : Plugin { override fun apply(project: Project) { - // Create extension for configuration val extension = project.extensions.create("runHytale", RunHytaleExtension::class.java) - // Register the runServer task - val runTask: TaskProvider = project.tasks.register( - "runServer", - RunServerTask::class.java - ) { + val runTask = project.tasks.register("runServer", RunServerTask::class.java) { jarUrl.set(extension.jarUrl) + extension.assetsPath?.let { assetsPath.set(it) } group = "hytale" description = "Downloads and runs the Hytale server with your plugin" } - // Make runServer depend on shadowJar (build plugin first) project.tasks.findByName("shadowJar")?.let { - runTask.configure { - dependsOn(it) - } + runTask.configure { dependsOn(it) } } } } @@ -47,6 +42,7 @@ open class RunHytalePlugin : Plugin { */ open class RunHytaleExtension { var jarUrl: String = "https://example.com/hytale-server.jar" + var assetsPath: String? = null } /** @@ -56,6 +52,9 @@ open class RunServerTask : DefaultTask() { @Input val jarUrl = project.objects.property(String::class.java) + + @Input + val assetsPath = project.objects.property(String::class.java) @TaskAction fun run() { @@ -70,17 +69,26 @@ open class RunServerTask : DefaultTask() { "hytale-cache" ).apply { mkdirs() } - // Compute hash of URL for caching + // Normalize jarUrl to URI + val jarUrlStr = jarUrl.get() + val jarUri = when { + jarUrlStr.startsWith("file://") -> URI.create(jarUrlStr) + jarUrlStr.startsWith("http://") || jarUrlStr.startsWith("https://") -> URI.create(jarUrlStr) + File(jarUrlStr).isAbsolute -> File(jarUrlStr).toURI() + else -> File(project.projectDir, jarUrlStr).toURI() + } + + // Compute hash of URI for caching val urlHash = MessageDigest.getInstance("SHA-256") - .digest(jarUrl.get().toByteArray()) + .digest(jarUri.toString().toByteArray()) .joinToString("") { "%02x".format(it) } val cachedJar = File(cacheDir, "$urlHash.jar") // Download server JAR if not cached if (!cachedJar.exists()) { - println("Downloading Hytale server from ${jarUrl.get()}") + println("Downloading Hytale server from ${jarUri}") try { - URI.create(jarUrl.get()).toURL().openStream().use { input -> + jarUri.toURL().openStream().use { input -> cachedJar.outputStream().use { output -> input.copyTo(output) } @@ -108,6 +116,29 @@ open class RunServerTask : DefaultTask() { println("WARNING: Could not find shadowJar output") } + // Copy assets file (mandatory) + val assetsPathStr = assetsPath.orNull + ?: throw IllegalStateException( + "assetsPath is required but not set. " + + "Please configure it in build.gradle.kts: runHytale { assetsPath = \"Assets.zip\" }" + ) + + val sourceAssets = when { + assetsPathStr.startsWith("file://") -> File(URI.create(assetsPathStr)) + File(assetsPathStr).isAbsolute -> File(assetsPathStr) + else -> File(project.projectDir, assetsPathStr) + } + if (!sourceAssets.exists()) { + throw IllegalStateException( + "Assets file not found: ${sourceAssets.absolutePath}. " + + "Please ensure assetsPath is correctly configured in build.gradle.kts" + ) + } + + val assetsFile = File(runDir, sourceAssets.name) + sourceAssets.copyTo(assetsFile, overwrite = true) + println("Assets copied to: ${assetsFile.absolutePath}") + println("Starting Hytale server...") println("Press Ctrl+C to stop the server") @@ -121,6 +152,10 @@ open class RunServerTask : DefaultTask() { } javaArgs.addAll(listOf("-jar", jarFile.name)) + + // Add assets argument (mandatory) + javaArgs.add("--assets") + javaArgs.add(assetsFile.name) // Start the server process val process = ProcessBuilder("java", *javaArgs.toTypedArray()) @@ -135,20 +170,10 @@ open class RunServerTask : DefaultTask() { } } - // Forward stdout to console - Thread { - process.inputStream.bufferedReader().useLines { lines -> - lines.forEach { println(it) } - } - }.start() - - // Forward stderr to console - Thread { - process.errorStream.bufferedReader().useLines { lines -> - lines.forEach { System.err.println(it) } - } - }.start() - + // Forward process streams to console + forwardStream(process.inputStream) { println(it) } + forwardStream(process.errorStream) { System.err.println(it) } + // Forward stdin to server (for commands) Thread { System.`in`.bufferedReader().useLines { lines -> @@ -163,4 +188,15 @@ open class RunServerTask : DefaultTask() { val exitCode = process.waitFor() println("Server exited with code $exitCode") } + + /** + * Forwards an input stream to a consumer in a background thread. + */ + private fun forwardStream(inputStream: InputStream, consumer: (String) -> Unit) { + Thread { + inputStream.bufferedReader().useLines { lines -> + lines.forEach(consumer) + } + }.start() + } }