From b7b84b47bae0572c96139902933ed832ac496ad4 Mon Sep 17 00:00:00 2001 From: Cody Adam Date: Tue, 13 Jan 2026 18:48:52 +0100 Subject: [PATCH 1/2] feat: allow to pass in relative paths for input files and add assets file --- build.gradle.kts | 5 +- buildSrc/src/main/kotlin/RunHytalePlugin.kt | 92 ++++++++++++++------- 2 files changed, 66 insertions(+), 31 deletions(-) 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() + } } From 2e98ce84572fcc14c42879a59ad7e6a0e6caf842 Mon Sep 17 00:00:00 2001 From: Cody Adam Date: Wed, 14 Jan 2026 10:28:58 +0100 Subject: [PATCH 2/2] fix: update paths for Hytale server and assets in build configuration Adjusted the paths in build.gradle.kts to reference the correct locations for HytaleServer.jar and Assets.zip. Updated README to reflect changes in the server's mods folder instead of plugins folder for clarity. --- README.md | 22 +++++++++++++++------ build.gradle.kts | 6 +++--- buildSrc/src/main/kotlin/RunHytalePlugin.kt | 4 ++-- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f99daa2..e1ed6d7 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A minimal, ready-to-use template for creating Hytale plugins with modern build t ✅ **Java 25** - Latest Java features ✅ **ShadowJar** - Automatic dependency bundling ✅ **CI/CD Ready** - GitHub Actions workflow included -✅ **Minimal Structure** - Only essential files, write your own code +✅ **Minimal Structure** - Only essential files, write your own code --- @@ -52,11 +52,13 @@ Your plugin JAR will be in: `build/libs/TemplatePlugin-1.0.0.jar` When ready to customize, edit these files: **`settings.gradle.kts`:** + ```kotlin rootProject.name = "your-plugin-name" ``` **`gradle.properties`:** + ```properties pluginGroup=com.yourname pluginVersion=1.0.0 @@ -64,6 +66,7 @@ pluginDescription=Your plugin description ``` **`src/main/resources/manifest.json`:** + ```json { "Group": "YourName", @@ -73,6 +76,7 @@ pluginDescription=Your plugin description ``` **Rename the main plugin class:** + - Rename `src/main/java/com/example/templateplugin/TemplatePlugin.java` - Update package name to match your `pluginGroup` @@ -91,6 +95,7 @@ Your plugin JAR will be in: `build/libs/YourPluginName-1.0.0.jar` ### 5. Implement Your Plugin Write your plugin code in `src/main/java/`: + - Commands - Event listeners - Services @@ -110,9 +115,10 @@ gradlew.bat runServer ``` This will: + 1. Download the Hytale server (cached for future runs) 2. Build your plugin -3. Copy it to the server's plugins folder +3. Copy it to the server's mods folder 4. Start the server with interactive console --- @@ -141,6 +147,7 @@ TemplatePlugin/ ``` **Note:** This is a minimal template. Create your own folder structure: + - `commands/` - For command implementations - `listeners/` - For event listeners - `services/` - For business logic @@ -198,11 +205,11 @@ Edit `build.gradle.kts`: ```kotlin dependencies { // Hytale API (provided by server) - compileOnly(files("libs/hytale-server.jar")) - + compileOnly(files("./HytaleServer.jar")) + // Your dependencies (will be bundled) implementation("com.google.code.gson:gson:2.10.1") - + // Test dependencies testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") } @@ -210,7 +217,7 @@ dependencies { ### Configuring Server Testing -**Run Hytale Server** - A Gradle plugin to download and run a Hytale server for development and testing purposes. The server files will be located in the `run/` directory of the project. Before starting the server it will compile (shadowJar task) and copy the plugin jar to the server's `plugins/` folder. +**Run Hytale Server** - A Gradle plugin to download and run a Hytale server for development and testing purposes. The server files will be located in the `run/` directory of the project. Before starting the server it will compile (shadowJar task) and copy the plugin jar to the server's `mods/` folder. **Usage:** @@ -233,6 +240,7 @@ gradlew.bat runServer ``` **Features:** + - ✅ Automatic server JAR download and caching - ✅ Compiles and deploys your plugin automatically - ✅ Starts server with interactive console @@ -242,6 +250,7 @@ gradlew.bat runServer ### Implementing Your Plugin **Recommended folder structure:** + ``` src/main/java/com/yourname/yourplugin/ ├── YourPlugin.java # Main class @@ -254,6 +263,7 @@ src/main/java/com/yourname/yourplugin/ ``` **See our documentation for examples:** + - [Getting Started with Plugins](../Documentation/07-getting-started-with-plugins.md) - [Advanced Plugin Patterns](../Documentation/12-advanced-plugin-patterns.md) - [Common Plugin Features](../Documentation/14-common-plugin-features.md) diff --git a/build.gradle.kts b/build.gradle.kts index 9f67ea2..372dbb0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ repositories { dependencies { // Hytale Server API (provided by server at runtime) - compileOnly(files("libs/hytale-server.jar")) + compileOnly(files("./libs/HytaleServer.jar")) // Common dependencies (will be bundled in JAR) implementation("com.google.code.gson:gson:2.10.1") @@ -28,8 +28,8 @@ dependencies { // Configure server testing runHytale { - jarUrl = "./HytaleServer.jar" - assetsPath = "./Assets.zip" + jarUrl = "./libs/HytaleServer.jar" + assetsPath = "./libs/Assets.zip" } tasks { diff --git a/buildSrc/src/main/kotlin/RunHytalePlugin.kt b/buildSrc/src/main/kotlin/RunHytalePlugin.kt index 8ea3f6c..8ca5208 100644 --- a/buildSrc/src/main/kotlin/RunHytalePlugin.kt +++ b/buildSrc/src/main/kotlin/RunHytalePlugin.kt @@ -60,7 +60,7 @@ open class RunServerTask : DefaultTask() { fun run() { // Create directories val runDir = File(project.projectDir, "run").apply { mkdirs() } - val pluginsDir = File(runDir, "plugins").apply { mkdirs() } + val pluginsDir = File(runDir, "mods").apply { mkdirs() } val jarFile = File(runDir, "server.jar") // Cache directory for downloaded server JARs @@ -107,7 +107,7 @@ open class RunServerTask : DefaultTask() { // Copy server JAR to run directory cachedJar.copyTo(jarFile, overwrite = true) - // Copy plugin JAR to plugins folder + // Copy plugin JAR to mods folder project.tasks.findByName("shadowJar")?.outputs?.files?.firstOrNull()?.let { shadowJar -> val targetFile = File(pluginsDir, shadowJar.name) shadowJar.copyTo(targetFile, overwrite = true)