From 41dd40e48bbc57cdfc1262070302fd4e45603d2c Mon Sep 17 00:00:00 2001 From: Britakee <127439938+realBritakee@users.noreply.github.com> Date: Fri, 16 Jan 2026 07:24:15 +0100 Subject: [PATCH] Migrate to new Hytale plugin template structure Replaces the previous Kotlin-based Gradle build and template files with a new Java-based Gradle build system and updated example plugin code. Removes old build scripts, plugin template, and configuration, and introduces a new ExamplePlugin with updated manifest, command, and recipe example. Updates documentation and project configuration to match the new structure and usage. --- .gitattributes | 2 - .gitignore | 32 +- LICENSE | 21 - README.md | 442 ++++-------------- build.gradle | 131 ++++++ build.gradle.kts | 88 ---- buildSrc/build.gradle.kts | 17 - buildSrc/src/main/kotlin/RunHytalePlugin.kt | 166 ------- gradle.properties | 40 +- gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 18 +- gradlew.bat | 15 +- settings.gradle | 1 + settings.gradle.kts | 1 - .../templateplugin/TemplatePlugin.java | 54 --- .../org/example/plugin/ExampleCommand.java | 29 ++ .../org/example/plugin/ExamplePlugin.java | 27 ++ .../Server/Item/Recipes/Example_Recipe.json | 22 + src/main/resources/config.json | 5 - src/main/resources/manifest.json | 39 +- 20 files changed, 367 insertions(+), 786 deletions(-) delete mode 100644 .gitattributes delete mode 100644 LICENSE create mode 100644 build.gradle delete mode 100644 build.gradle.kts delete mode 100644 buildSrc/build.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/RunHytalePlugin.kt create mode 100644 settings.gradle delete mode 100644 settings.gradle.kts delete mode 100644 src/main/java/com/example/templateplugin/TemplatePlugin.java create mode 100644 src/main/java/org/example/plugin/ExampleCommand.java create mode 100644 src/main/java/org/example/plugin/ExamplePlugin.java create mode 100644 src/main/resources/Server/Item/Recipes/Example_Recipe.json delete mode 100644 src/main/resources/config.json diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/.gitignore b/.gitignore index 020e258..0c3389b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,14 @@ -# Gradle -.gradle/ +### Gradle ### +.gradle build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ -.kotlin/ -# Server testing directory +### Hytale ### run/ -# IntelliJ IDEA +### IntelliJ IDEA ### .idea/ *.iws *.iml @@ -18,7 +17,7 @@ out/ !**/src/main/**/out/ !**/src/test/**/out/ -# Eclipse +### Eclipse ### .apt_generated .classpath .factorypath @@ -30,28 +29,15 @@ bin/ !**/src/main/**/bin/ !**/src/test/**/bin/ -# NetBeans +### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ -# VS Code +### VS Code ### .vscode/ -# Mac OS -.DS_Store - -# Windows -Thumbs.db -desktop.ini - -# Logs -*.log - -# Temporary files -*.tmp -*.bak -*.swp -*~ +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 1c63704..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Hytale Modding Community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index f99daa2..71a1229 100644 --- a/README.md +++ b/README.md @@ -1,371 +1,99 @@ -# Hytale Plugin Template +# Hytale Example Plugin -A minimal, ready-to-use template for creating Hytale plugins with modern build tools and automated testing. +An example project that can build and run plugins for the game Hytale! -> **✨ Builds immediately without any changes!** Clone and run `./gradlew shadowJar` to get a working plugin JAR. +> **⚠️ Warning: Early Access** +> The game Hytale is in early access, and so is this project! Features may be +> incomplete, unstable, or change frequently. Please be patient and understanding as development +> continues. -## Features +## Introduction +This project contains a Gradle project that can be imported into IDEA and used +as the foundation for custom Hytale plugins. The template will add the Hytale +server to your classpath and create a run configuration that can be used to +run your plugin on the server. It can also be used to build a sharable JAR file +that contains your plugin. -✅ **Modern Build System** - Gradle with Kotlin DSL -✅ **Automated Testing** - Custom Gradle plugin for one-command server testing -✅ **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 +## Requirements +Please ensure all the requirements are met before getting started. ---- +1. Download Hytale using the official launcher. +2. Have Intellij IDEA installed. Community edition is fine. +3. Download Java 25 and set it as the SDK in IDEA. -## Quick Start +## Configuring Template +It is important to configure the project before using it as a template. Doing +this before importing the project will help avoid running into caching issues +later on. -### Prerequisites +### 1: Project Name +Set the name of the project in `settings.gradle`. This should be the name of +your plugin. We recommend capitalizing your project name and avoiding +whitespace and most special characters. This will be used as the base name for +any files produced by Gradle, like the sharable JAR file. -- **Java 25 JDK** - [Download here](https://www.oracle.com/java/technologies/downloads/) -- **IntelliJ IDEA** - [Download here](https://www.jetbrains.com/idea/download/) (Community Edition is fine) -- **Git** - [Download here](https://git-scm.com/) +### 2: Gradle Properties +Review the properties defined in `gradle.properties`. You should change the +`maven_group` to match your project. You should also change the `version` +property before making a new release, or set up CI/CD to automate it. -### 1. Clone or Download +### 3: Manifest +The manifest file provides important information about your plugin to Hytale. +You should update every property in this file to reflect your project. The +most important property to set is `Main` which tells the game which class +file to load as the entry point for your plugin. The file can be found at +`src/main/resources/manifest.json`. -```bash -git clone https://github.com/yourusername/hytale-plugin-template.git -cd hytale-plugin-template +**This template has configured Gradle to automatically update the `Version` and +`IncludesAssetPack` property to reflect your Gradle properties every time you +run the game in development, or build the plugin. This is a workaround to allow +the in-game asset editor to be used when working on your project.** + +## Importing into IDEA +When opening the project in IDEA it should automatically create the +`HytaleServer` run configuration and a `./run` folder. When you run the game it +will generate all the relevant files in there. It will also load the default +assets from the games. + +**If you do not see the `HytaleServer` run configuration, you may need to open +the dropdown or click `Edit Configurations...` once to unhide it.** + +## Importing into VSCode +While VSCode is not officially supported, you can generate launch configs by +running `./gradlew generateVSCodeLaunch`. + +## Connecting to Server +Once the server is running in IDEA you should be able to connect to +`Local Server` using your standard Hytale client. If the server does not show +up automatically, add the IP as `127.0.0.1` manually. + +### You MUST authenticate your test server! +In order to connect to the test server, you must authenticate it with Hytale. +This is done by running the `auth login device` command in the server terminal. +This command will print a URL that you can use to authenticate the server using +your Hytale account. Once authenticated, you can run the +`auth persistence Encrypted` command to keep your server authenticated after +restarting it. + +**Never share your encrypted auth file!** + +If you are unable to run commands from the IDEA terminal, you can also run the +command from code like this. Make sure to remove the code after your server is +authenticated. + +```java + @Override + protected void start() { + CommandManager.get().handleCommand(ConsoleSender.INSTANCE, "auth login device"); + } ``` -**The template builds immediately without any changes!** -You can customize it later when you're ready to develop your plugin. -### 2. Build Immediately (No Changes Needed!) +## Verifying The Example Plugin +You can verify the Example plugin has loaded by running the `/test` command +in game. It will print the name and version of your plugin. This is for +demonstration purposes, and should **NOT** be included in your final build. -The template works out-of-the-box: - -```bash -# Windows -gradlew.bat shadowJar - -# Linux/Mac -./gradlew shadowJar -``` - -Your plugin JAR will be in: `build/libs/TemplatePlugin-1.0.0.jar` - -### 3. Customize Your Plugin (Optional) - -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 -pluginDescription=Your plugin description -``` - -**`src/main/resources/manifest.json`:** -```json -{ - "Group": "YourName", - "Name": "YourPluginName", - "Main": "com.yourname.yourplugin.YourPlugin" -} -``` - -**Rename the main plugin class:** -- Rename `src/main/java/com/example/templateplugin/TemplatePlugin.java` -- Update package name to match your `pluginGroup` - -### 4. Build Your Plugin - -```bash -# Windows -gradlew.bat shadowJar - -# Linux/Mac -./gradlew shadowJar -``` - -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 -- Storage -- Utilities - -See our [documentation](../Documentation/) for examples and patterns. - -### 6. Test Your Plugin (Automated!) - -```bash -# Windows -gradlew.bat runServer - -# Linux/Mac -./gradlew 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 -4. Start the server with interactive console - ---- - -## Project Structure - -``` -TemplatePlugin/ -├── .github/workflows/ -│ └── build.yml # CI/CD workflow -├── buildSrc/ -│ ├── build.gradle.kts # Custom plugin configuration -│ └── src/main/kotlin/ -│ └── RunHytalePlugin.kt # Automated server testing -├── src/main/ -│ ├── java/com/example/templateplugin/ -│ │ └── TemplatePlugin.java # Minimal main class (example) -│ └── resources/ -│ └── manifest.json # Plugin metadata -├── .gitignore # Git ignore rules -├── build.gradle.kts # Build configuration -├── gradle.properties # Project properties -├── settings.gradle.kts # Project settings -├── LICENSE # MIT License -└── README.md # This file -``` - -**Note:** This is a minimal template. Create your own folder structure: -- `commands/` - For command implementations -- `listeners/` - For event listeners -- `services/` - For business logic -- `storage/` - For data persistence -- `utils/` - For utility classes -- `config/` - For configuration management - ---- - -## Development Workflow - -### Building - -```bash -# Compile only -./gradlew compileJava - -# Build plugin JAR -./gradlew shadowJar - -# Clean and rebuild -./gradlew clean shadowJar -``` - -### Testing - -```bash -# Run server with your plugin -./gradlew runServer - -# Run unit tests -./gradlew test - -# Clean test server -rm -rf run/ -``` - -### Debugging - -```bash -# Run server in debug mode -./gradlew runServer -Pdebug - -# Then connect your IDE debugger to localhost:5005 -``` - ---- - -## Customization - -### Adding Dependencies - -Edit `build.gradle.kts`: - -```kotlin -dependencies { - // Hytale API (provided by server) - compileOnly(files("libs/hytale-server.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") -} -``` - -### 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. - -**Usage:** - -Edit `build.gradle.kts`: - -```kotlin -runHytale { - jarUrl = "url to hytale server jar" -} -``` - -Run the server with: - -```bash -# Windows -gradlew.bat runServer - -# Linux/Mac -./gradlew runServer -``` - -**Features:** -- ✅ Automatic server JAR download and caching -- ✅ Compiles and deploys your plugin automatically -- ✅ Starts server with interactive console -- ✅ One-command workflow: `./gradlew runServer` -- ✅ Server files in `run/` directory (gitignored) - -### Implementing Your Plugin - -**Recommended folder structure:** -``` -src/main/java/com/yourname/yourplugin/ -├── YourPlugin.java # Main class -├── commands/ # Commands -├── listeners/ # Event listeners -├── services/ # Business logic -├── storage/ # Data persistence -├── config/ # Configuration -└── utils/ # Utilities -``` - -**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) - ---- - -## CI/CD - -This template includes a GitHub Actions workflow that: - -1. ✅ Builds your plugin on every push -2. ✅ Runs tests -3. ✅ Uploads artifacts -4. ✅ Creates releases (when you tag) - -### Creating a Release - -```bash -git tag v1.0.0 -git push origin v1.0.0 -``` - -GitHub Actions will automatically build and create a release with your plugin JAR. - ---- - -## Best Practices - -### ✅ DO: - -- Use the Service-Storage pattern for data management -- Write unit tests for your business logic -- Use structured logging (not `System.out.println`) -- Handle errors gracefully -- Document your public API -- Version your releases semantically (1.0.0, 1.1.0, etc.) - -### ❌ DON'T: - -- Hardcode configuration values -- Block the main thread with heavy operations -- Ignore exceptions -- Use deprecated APIs -- Commit sensitive data (API keys, passwords) - ---- - -## Troubleshooting - -### Build Fails - -```bash -# Clean and rebuild -./gradlew clean build --refresh-dependencies -``` - -### Server Won't Start - -1. Check that `jarUrl` in `build.gradle.kts` is correct -2. Verify Java 25 is installed: `java -version` -3. Check logs in `run/logs/` - -### Plugin Not Loading - -1. Verify `manifest.json` has correct `Main` class -2. Check server logs for errors -3. Ensure all dependencies are bundled in JAR - ---- - -## Documentation - -For detailed guides on plugin development, see: - -- [Hytale Modding Documentation](https://github.com/yourusername/hytale-modding/tree/main/Documentation) -- [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) - ---- - -## Contributing - -Contributions are welcome! Please: - -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Submit a pull request - ---- - -## License - -This template is released under the MIT License. You are free to use it for any purpose. - ---- - -## Support - -- **Issues:** [GitHub Issues](https://github.com/yourusername/hytale-plugin-template/issues) -- **Documentation:** [Hytale Modding Docs](https://github.com/yourusername/hytale-modding) -- **Community:** Join the Hytale modding community - ---- - -## Credits - -Created by the Hytale modding community. - -Based on best practices from production Hytale plugins. - ---- - -**Happy Modding! 🎮** +The example plugin also includes a recipe defined by an asset pack. This recipe +allows you to craft 10 dirt into 1 dirt using the crafting window. This is also +an example and should not be removed before you release the plugin. \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..8df6db3 --- /dev/null +++ b/build.gradle @@ -0,0 +1,131 @@ +plugins { + id 'java' + id 'org.jetbrains.gradle.plugin.idea-ext' version '1.3' +} + +import org.gradle.internal.os.OperatingSystem + +ext { + if (project.hasProperty('hytale_home')) { + hytaleHome = project.findProperty('hytale_home') + } + else { + def os = OperatingSystem.current() + if (os.isWindows()) { + hytaleHome = "${System.getProperty("user.home")}/AppData/Roaming/Hytale" + } + else if (os.isMacOsX()) { + hytaleHome = "${System.getProperty("user.home")}/Library/Application Support/Hytale" + } + else if (os.isLinux()) { + hytaleHome = "${System.getProperty("user.home")}/.var/app/com.hypixel.HytaleLauncher/data/Hytale" + if (!file(hytaleHome).exists()) { + hytaleHome = "${System.getProperty("user.home")}/.local/share/Hytale" + } + } + } +} + +if (!project.hasProperty('hytaleHome')) { + throw new GradleException('Your Hytale install could not be detected automatically. If you are on an unsupported platform or using a custom install location, please define the install location using the hytale_home property.'); +} +else if (!file(project.findProperty('hytaleHome')).exists()) { + throw new GradleException("Failed to find Hytale at the expected location. Please make sure you have installed the game. The expected location can be changed using the hytale_home property. Currently looking in ${project.findProperty('hytaleHome')}") +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(java_version) + withSourcesJar() + withJavadocJar() +} + +// Quiet warnings about missing Javadocs. +javadoc { + options.addStringOption('Xdoclint:-missing', '-quiet') +} + +// Adds the Hytale server as a build dependency, allowing you to reference and +// compile against their code. This requires you to have Hytale installed using +// the official launcher for now. +dependencies { + implementation(files("$hytaleHome/install/$patchline/package/game/latest/Server/HytaleServer.jar")) +} + +// Create the working directory to run the server if it does not already exist. +def serverRunDir = file("$projectDir/run") +if (!serverRunDir.exists()) { + serverRunDir.mkdirs() +} + +// Updates the manifest.json file with the latest properties defined in the +// build.properties file. Currently we update the version and if packs are +// included with the plugin. +tasks.register('updatePluginManifest') { + def manifestFile = file('src/main/resources/manifest.json') + doLast { + if (!manifestFile.exists()) { + throw new GradleException("Could not find manifest.json at ${manifestFile.path}!") + } + def manifestJson = new groovy.json.JsonSlurper().parseText(manifestFile.text) + manifestJson.Version = version + manifestJson.IncludesAssetPack = includes_pack.toBoolean() + manifestFile.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(manifestJson)) + } +} + +// Makes sure the plugin manifest is up to date. +tasks.named('processResources') { + dependsOn 'updatePluginManifest' +} + +def createServerRunArguments(String srcDir) { + def programParameters = '--allow-op --disable-sentry --assets="' + "${hytaleHome}/install/$patchline/package/game/latest/Assets.zip" + '"' + def modPaths = [] + if (includes_pack.toBoolean()) { + modPaths << srcDir + } + if (load_user_mods.toBoolean()) { + modPaths << "${hytaleHome}/UserData/Mods" + } + if (!modPaths.isEmpty()) { + programParameters += ' --mods="' + modPaths.join(',') + '"' + } + return programParameters +} + +// Creates a run configuration in IDEA that will run the Hytale server with +// your plugin and the default assets. +idea.project.settings.runConfigurations { + 'HytaleServer'(org.jetbrains.gradle.ext.Application) { + mainClass = 'com.hypixel.hytale.Main' + moduleName = project.idea.module.name + '.main' + programParameters = createServerRunArguments(sourceSets.main.java.srcDirs.first().parentFile.absolutePath) + workingDirectory = serverRunDir.absolutePath + } +} + +// Creates a launch.json file for VSCode with the same configuration +tasks.register('generateVSCodeLaunch') { + def vscodeDir = file("$projectDir/.vscode") + def launchFile = file("$vscodeDir/launch.json") + doLast { + if (!vscodeDir.exists()) { + vscodeDir.mkdirs() + } + def programParams = createServerRunArguments("\${workspaceFolder}") + def launchConfig = [ + version: "0.2.0", + configurations: [ + [ + type: "java", + name: "HytaleServer", + request: "launch", + mainClass: "com.hypixel.hytale.Main", + args: programParams, + cwd: "\${workspaceFolder}/run" + ] + ] + ] + launchFile.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(launchConfig)) + } +} diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index ecfea96..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,88 +0,0 @@ -plugins { - id("java-library") - id("com.gradleup.shadow") version "9.3.1" - id("run-hytale") -} - -group = findProperty("pluginGroup") as String? ?: "com.example" -version = findProperty("pluginVersion") as String? ?: "1.0.0" -description = findProperty("pluginDescription") as String? ?: "A Hytale plugin template" - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - // Hytale Server API (provided by server at runtime) - compileOnly(files("libs/hytale-server.jar")) - - // Common dependencies (will be bundled in JAR) - implementation("com.google.code.gson:gson:2.10.1") - implementation("org.jetbrains:annotations:24.1.0") - - // Test dependencies - testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} - -// 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" -} - -tasks { - // Configure Java compilation - compileJava { - options.encoding = Charsets.UTF_8.name() - options.release = 25 - } - - // Configure resource processing - processResources { - filteringCharset = Charsets.UTF_8.name() - - // Replace placeholders in manifest.json - val props = mapOf( - "group" to project.group, - "version" to project.version, - "description" to project.description - ) - inputs.properties(props) - - filesMatching("manifest.json") { - expand(props) - } - } - - // Configure ShadowJar (bundle dependencies) - shadowJar { - archiveBaseName.set(rootProject.name) - archiveClassifier.set("") - - // Relocate dependencies to avoid conflicts - relocate("com.google.gson", "com.yourplugin.libs.gson") - - // Minimize JAR size (removes unused classes) - minimize() - } - - // Configure tests - test { - useJUnitPlatform() - } - - // Make build depend on shadowJar - build { - dependsOn(shadowJar) - } -} - -// Configure Java toolchain -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(25)) - } -} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100644 index 4aef118..0000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -plugins { - `kotlin-dsl` - `java-gradle-plugin` -} - -gradlePlugin { - plugins { - create("runHytale") { - id = "run-hytale" - implementationClass = "RunHytalePlugin" - } - } -} - -repositories { - mavenCentral() -} diff --git a/buildSrc/src/main/kotlin/RunHytalePlugin.kt b/buildSrc/src/main/kotlin/RunHytalePlugin.kt deleted file mode 100644 index e6a3b10..0000000 --- a/buildSrc/src/main/kotlin/RunHytalePlugin.kt +++ /dev/null @@ -1,166 +0,0 @@ -import org.gradle.api.DefaultTask -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.TaskProvider -import java.io.File -import java.net.URI -import java.security.MessageDigest - -/** - * Custom Gradle plugin for automated Hytale server testing. - * - * Usage: - * runHytale { - * jarUrl = "https://example.com/hytale-server.jar" - * } - * - * ./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 - ) { - jarUrl.set(extension.jarUrl) - 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) - } - } - } -} - -/** - * Extension for configuring the RunHytale plugin. - */ -open class RunHytaleExtension { - var jarUrl: String = "https://example.com/hytale-server.jar" -} - -/** - * Task that downloads, sets up, and runs a Hytale server with the plugin. - */ -open class RunServerTask : DefaultTask() { - - @Input - val jarUrl = project.objects.property(String::class.java) - - @TaskAction - fun run() { - // Create directories - val runDir = File(project.projectDir, "run").apply { mkdirs() } - val pluginsDir = File(runDir, "plugins").apply { mkdirs() } - val jarFile = File(runDir, "server.jar") - - // Cache directory for downloaded server JARs - val cacheDir = File( - project.layout.buildDirectory.asFile.get(), - "hytale-cache" - ).apply { mkdirs() } - - // Compute hash of URL for caching - val urlHash = MessageDigest.getInstance("SHA-256") - .digest(jarUrl.get().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()}") - try { - URI.create(jarUrl.get()).toURL().openStream().use { input -> - cachedJar.outputStream().use { output -> - input.copyTo(output) - } - } - println("Server JAR downloaded and cached") - } catch (e: Exception) { - println("ERROR: Failed to download server JAR") - println("Make sure the jarUrl in build.gradle.kts is correct") - println("Error: ${e.message}") - return - } - } else { - println("Using cached server JAR") - } - - // Copy server JAR to run directory - cachedJar.copyTo(jarFile, overwrite = true) - - // Copy plugin JAR to plugins folder - project.tasks.findByName("shadowJar")?.outputs?.files?.firstOrNull()?.let { shadowJar -> - val targetFile = File(pluginsDir, shadowJar.name) - shadowJar.copyTo(targetFile, overwrite = true) - println("Plugin copied to: ${targetFile.absolutePath}") - } ?: run { - println("WARNING: Could not find shadowJar output") - } - - println("Starting Hytale server...") - println("Press Ctrl+C to stop the server") - - // Check if debug mode is enabled - val debugMode = project.hasProperty("debug") - val javaArgs = mutableListOf() - - if (debugMode) { - javaArgs.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005") - println("Debug mode enabled. Connect debugger to port 5005") - } - - javaArgs.addAll(listOf("-jar", jarFile.name)) - - // Start the server process - val process = ProcessBuilder("java", *javaArgs.toTypedArray()) - .directory(runDir) - .start() - - // Handle graceful shutdown - project.gradle.buildFinished { - if (process.isAlive) { - println("\nStopping server...") - process.destroy() - } - } - - // 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 stdin to server (for commands) - Thread { - System.`in`.bufferedReader().useLines { lines -> - lines.forEach { - process.outputStream.write((it + "\n").toByteArray()) - process.outputStream.flush() - } - } - }.start() - - // Wait for server to exit - val exitCode = process.waitFor() - println("Server exited with code $exitCode") - } -} diff --git a/gradle.properties b/gradle.properties index 7d8e6db..e0e99eb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,32 @@ -# Project Information -pluginGroup=com.example -pluginVersion=1.0.0 -pluginDescription=A Hytale plugin template with best practices +# The current version of your project. Please use semantic versioning! +version=0.0.2 -# Gradle Configuration -org.gradle.parallel=true -org.gradle.caching=true -org.gradle.daemon=true -org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m +# The group ID used for maven publishing. Usually the same as your package name +# but not the same as your plugin group! +maven_group=org.example + +# The version of Java used by your plugin. The game is built on Java 21 but +# actually runs on Java 25. +java_version=25 + +# Determines if your plugin should also be loaded as an asset pack. If your +# pack contains assets, or you intend to use the in-game asset editor, you +# want this to be true. +includes_pack=true + +# The release channel your plugin should be built and ran against. This is +# usually release or pre-release. You can verify your settings in the +# official launcher. +patchline=release + +# Determines if the development server should also load mods from the user's +# standard mods folder. This lets you test mods by installing them where a +# normal player would, instead of adding them as dependencies or adding them +# to the development server manually. +load_user_mods=false + +# If Hytale was installed to a custom location, you must set the home path +# manually. You may also want to use a custom path if you are building in +# a non-standard environment like a build server. The home path should +# the folder that contains the install and UserData folder. +# hytale_home=./test-file \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bad7c24..4d1957b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,6 @@ +#Tue Nov 25 02:23:10 MST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip -networkTimeout=10000 -validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 79a61d4..1b6c787 100644 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,11 +80,11 @@ do esac done -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,16 +143,12 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -209,12 +205,6 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%"=="" @echo off +@if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,8 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused +if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -41,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd +if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..1cd81f0 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'ExamplePlugin' \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index c4dd3b3..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "TemplatePlugin" diff --git a/src/main/java/com/example/templateplugin/TemplatePlugin.java b/src/main/java/com/example/templateplugin/TemplatePlugin.java deleted file mode 100644 index 72d21a7..0000000 --- a/src/main/java/com/example/templateplugin/TemplatePlugin.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.example.templateplugin; - -/** - * Main plugin class. - * - * TODO: Implement your plugin logic here. - * - * @author YourName - * @version 1.0.0 - */ -public class TemplatePlugin { - - private static TemplatePlugin instance; - - /** - * Constructor - Called when plugin is loaded. - */ - public TemplatePlugin() { - instance = this; - System.out.println("[TemplatePlugin] Plugin loaded!"); - } - - /** - * Called when plugin is enabled. - */ - public void onEnable() { - System.out.println("[TemplatePlugin] Plugin enabled!"); - - // TODO: Initialize your plugin here - // - Load configuration - // - Register event listeners - // - Register commands - // - Start services - } - - /** - * Called when plugin is disabled. - */ - public void onDisable() { - System.out.println("[TemplatePlugin] Plugin disabled!"); - - // TODO: Cleanup your plugin here - // - Save data - // - Stop services - // - Close connections - } - - /** - * Get plugin instance. - */ - public static TemplatePlugin getInstance() { - return instance; - } -} diff --git a/src/main/java/org/example/plugin/ExampleCommand.java b/src/main/java/org/example/plugin/ExampleCommand.java new file mode 100644 index 0000000..6c1b119 --- /dev/null +++ b/src/main/java/org/example/plugin/ExampleCommand.java @@ -0,0 +1,29 @@ +package org.example.plugin; + +import com.hypixel.hytale.protocol.GameMode; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase; + +import javax.annotation.Nonnull; + +/** + * This is an example command that will simply print the name of the plugin in chat when used. + */ +public class ExampleCommand extends CommandBase { + + private final String pluginName; + private final String pluginVersion; + + public ExampleCommand(String pluginName, String pluginVersion) { + super("test", "Prints a test message from the " + pluginName + " plugin."); + this.setPermissionGroup(GameMode.Adventure); // Allows the command to be used by anyone, not just OP + this.pluginName = pluginName; + this.pluginVersion = pluginVersion; + } + + @Override + protected void executeSync(@Nonnull CommandContext ctx) { + ctx.sendMessage(Message.raw("Hello from the " + pluginName + " v" + pluginVersion + " plugin!")); + } +} \ No newline at end of file diff --git a/src/main/java/org/example/plugin/ExamplePlugin.java b/src/main/java/org/example/plugin/ExamplePlugin.java new file mode 100644 index 0000000..e7c7c9e --- /dev/null +++ b/src/main/java/org/example/plugin/ExamplePlugin.java @@ -0,0 +1,27 @@ +package org.example.plugin; + +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.server.core.plugin.JavaPlugin; +import com.hypixel.hytale.server.core.plugin.JavaPluginInit; + +import javax.annotation.Nonnull; + +/** + * This class serves as the entrypoint for your plugin. Use the setup method to register into game registries or add + * event listeners. + */ +public class ExamplePlugin extends JavaPlugin { + + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + + public ExamplePlugin(@Nonnull JavaPluginInit init) { + super(init); + LOGGER.atInfo().log("Hello from " + this.getName() + " version " + this.getManifest().getVersion().toString()); + } + + @Override + protected void setup() { + LOGGER.atInfo().log("Setting up plugin " + this.getName()); + this.getCommandRegistry().registerCommand(new ExampleCommand(this.getName(), this.getManifest().getVersion().toString())); + } +} \ No newline at end of file diff --git a/src/main/resources/Server/Item/Recipes/Example_Recipe.json b/src/main/resources/Server/Item/Recipes/Example_Recipe.json new file mode 100644 index 0000000..64c3aef --- /dev/null +++ b/src/main/resources/Server/Item/Recipes/Example_Recipe.json @@ -0,0 +1,22 @@ +{ + "Input": [ + { + "ItemId": "Soil_Dirt", + "Quantity": 10 + } + ], + "PrimaryOutput": { + "ItemId": "Soil_Dirt", + "Quantity": 1 + }, + "BenchRequirement": [ + { + "Id": "Fieldcraft", + "Type": "Crafting", + "Categories": [ + "Tools" + ] + } + ], + "Seconds": 1 +} \ No newline at end of file diff --git a/src/main/resources/config.json b/src/main/resources/config.json deleted file mode 100644 index d61bc16..0000000 --- a/src/main/resources/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "pluginName": "TemplatePlugin", - "version": "1.0.0", - "debugMode": false -} diff --git a/src/main/resources/manifest.json b/src/main/resources/manifest.json index a5c8015..169a0cc 100644 --- a/src/main/resources/manifest.json +++ b/src/main/resources/manifest.json @@ -1,19 +1,22 @@ { - "Group": "TemplatePlugin", - "Name": "TemplatePlugin", - "Version": "1.0.0", - "Description": "A Hytale plugin template", - "Authors": [ - { - "Name": "YourName", - "Email": "your.email@example.com", - "Url": "https://your-website.com" - } - ], - "Website": "https://github.com/yourusername/hytale-plugin-template", - "Main": "com.example.templateplugin.TemplatePlugin", - "ServerVersion": "*", - "Dependencies": {}, - "OptionalDependencies": {}, - "DisabledByDefault": false -} + "Group": "Example", + "Name": "ExamplePlugin", + "Version": "0.0.2", + "Description": "An example plugin for HyTale!", + "Authors": [ + { + "Name": "It's you!" + } + ], + "Website": "example.org", + "ServerVersion": "*", + "Dependencies": { + + }, + "OptionalDependencies": { + + }, + "DisabledByDefault": false, + "Main": "org.example.plugin.ExamplePlugin", + "IncludesAssetPack": true +} \ No newline at end of file