Skip to content

Creating a Java Plugin

This guide walks you through creating a minimal Java plugin for Hytale using Gradle. By the end you will have a plugin that loads on the server and logs a message on startup.

  1. Java 25 JDK installed. The game is built on Java 21 but runs on Java 25.
  2. A code editor of your choice.
  3. If your IDE doesnt include gradle project generation, install the Gradle CLI tool to create a gradle wrapper.
  4. Hytale installed via the official launcher (only needed to run a local dev server, not to build).

A Hytale plugin follows standard Gradle/Java conventions. The only addition is a manifest.json that describes your plugin to the game.

  • build.gradle.kts
  • settings.gradle.kts
  • gradle.properties
  • gradlew / gradlew.bat
  • .gitignore
  • Directorygradle/
    • Directorywrapper/
      • gradle-wrapper.jar
      • gradle-wrapper.properties
  • Directorysrc/
    • Directorymain/
      • Directoryjava/
        • Directorycom/example/myplugin/
          • MyPlugin.java plugin entry point
      • Directoryresources/
        • manifest.json plugin metadata
  • Directoryrun/ server working directory (gitignored)
  1. Create a new Gradle project or clone an existing template. The Gradle wrapper is included so you don’t need Gradle installed globally — just run ./gradlew from the project root.

  2. Configure settings.gradle.kts — set your project name. This becomes the base name for JAR files produced by Gradle.

    settings.gradle.kts
    rootProject.name = "MyPlugin"
  3. Configure gradle.properties — define your project version and build settings. These properties are referenced by build.gradle.kts.

    gradle.properties
    # The current version of your project. Please use semantic versioning!
    version=0.0.1
    # The Hytale server version to build against. Available versions can be found
    # at https://maven.hytale.com
    server_version=2026.01.24-6e2d4fc36
    # 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
  4. Set up build.gradle.kts — this is the core of the build system. It adds the Hytale server JAR to your classpath, auto-updates your manifest, and provides tasks for development.

    build.gradle.kts
    plugins {
    java
    }
    java {
    toolchain {
    languageVersion = JavaLanguageVersion.of(25)
    }
    }
    // Configuration for the Vineflower decompiler dependency.
    val vineflower by configurations.creating {
    isCanBeResolved = true
    isCanBeConsumed = false
    }
    repositories {
    mavenCentral()
    maven {
    url = uri("https://maven.hytale.com/release")
    }
    }
    dependencies {
    val server_version: String by project
    implementation("com.hypixel.hytale:Server:${server_version}")
    vineflower("org.vineflower:vineflower:1.10.1")
    }
    // Resolve the Hytale install directory. Set the HYTALE_HOME environment
    // variable to override the default path.
    val hytaleHome = System.getenv("HYTALE_HOME")
    ?: "${System.getProperty("user.home")}/AppData/Roaming/Hytale"
    // Updates the manifest.json file with the version from gradle.properties.
    tasks.register("updatePluginManifest") {
    val manifestFile = file("src/main/resources/manifest.json")
    doLast {
    if (!manifestFile.exists()) {
    throw GradleException("Could not find manifest.json at ${manifestFile.path}!")
    }
    val slurper = groovy.json.JsonSlurper()
    @Suppress("UNCHECKED_CAST")
    val manifestJson = slurper.parse(manifestFile) as MutableMap<String, Any>
    manifestJson["Version"] = project.version.toString()
    val jsonOutput = groovy.json.JsonOutput.toJson(manifestJson)
    val prettyJson = groovy.json.JsonOutput.prettyPrint(jsonOutput)
    manifestFile.writeText(prettyJson)
    println("Updated manifest.json version to ${project.version}")
    }
    }
    tasks.named("processResources") {
    dependsOn("updatePluginManifest")
    }
    tasks.register<JavaExec>("runServer") {
    dependsOn("processResources", "classes")
    mainClass.set("com.hypixel.hytale.Main")
    classpath = sourceSets["main"].runtimeClasspath
    val patchline: String by project
    val runDir = file("run")
    workingDir = runDir
    doFirst {
    runDir.mkdirs()
    }
    args = listOf(
    "--allow-op",
    "--assets=$hytaleHome/install/$patchline/package/game/latest/Assets.zip"
    )
    }
    // Vineflower decompiler tasks for generating readable source code.
    val genSourcesOutputDir = layout.buildDirectory.dir("generated/sources/vineflower")
    tasks.register<JavaExec>("genSources") {
    group = "decompilation"
    description = "Decompile the Hytale Server jar using Vineflower."
    mainClass.set("org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler")
    classpath = vineflower
    maxHeapSize = "12g"
    val runtimeClasspathConfig = configurations.runtimeClasspath
    dependsOn(runtimeClasspathConfig)
    doFirst {
    val outputDir = genSourcesOutputDir.get().asFile
    val serverJar = runtimeClasspathConfig.get().resolve().firstOrNull {
    it.name.startsWith("Server-") && it.extension == "jar"
    } ?: throw GradleException("Could not find Hytale Server jar in runtimeClasspath.")
    outputDir.deleteRecursively()
    outputDir.mkdirs()
    args = listOf(
    "-dgs=1", // Decompile generic signatures
    "-rsy=1", // Remove synthetic members
    "-ren=1", // Rename ambiguous members
    serverJar.absolutePath,
    outputDir.absolutePath
    )
    }
    outputs.dir(genSourcesOutputDir)
    }
    tasks.register<Jar>("genSourcesJar") {
    group = "decompilation"
    description = "Package decompiled sources into a sources jar."
    dependsOn("genSources")
    from(genSourcesOutputDir)
    archiveClassifier.set("sources")
    }

    Key things this does:

    • Pulls the Hytale server JAR from the official Maven repository as a compile dependency.
    • Defines an updatePluginManifest task that syncs Version from your Gradle properties into manifest.json every build.
    • Defines a runServer task that launches a local Hytale server with your plugin loaded.
    • Defines genSources and genSourcesJar tasks that use Vineflower to decompile the Hytale server for easier code exploration.
  5. Create manifest.json — this tells Hytale about your plugin. The Main field must point to your plugin’s entry class.

    src/main/resources/manifest.json
    {
    "Group": "MyGroup",
    "Name": "MyPlugin",
    "Description": "(The 'Group' field is your organization or team name, displayed to players. This is different from the 'maven_group' in gradle.properties, which is used for Maven publishing.)",
    "Version": "0.0.1",
    "Description": "My first Hytale plugin!",
    "Authors": [
    {
    "Name": "YourName"
    }
    ],
    "Website": "",
    "ServerVersion": "*",
    "Dependencies": {},
    "OptionalDependencies": {},
    "DisabledByDefault": false,
    "Main": "com.example.myplugin.MyPlugin",
    "IncludesAssetPack": false
    }
    FieldDescription
    GroupYour organization or team name — displayed to players as the plugin’s author group
    NamePlugin display name
    VersionSemantic version (auto-updated by Gradle)
    MainFully qualified class name of your JavaPlugin subclass
    IncludesAssetPackSet true if your plugin bundles game assets
    ServerVersionServer version constraint (* for any)
  6. Add a .gitignore to exclude build artifacts and the server runtime directory.

    .gitignore
    ### Gradle ###
    .gradle
    build/
    !gradle/wrapper/gradle-wrapper.jar
    !**/src/main/**/build/
    !**/src/test/**/build/
    ### Hytale ###
    run/
    ### IntelliJ IDEA ###
    .idea/
    *.iws
    *.iml
    *.ipr
    out/
    !**/src/main/**/out/
    !**/src/test/**/out/
    ### Eclipse ###
    .apt_generated
    .classpath
    .factorypath
    .project
    .settings
    .springBeans
    .sts4-cache
    bin/
    !**/src/main/**/bin/
    !**/src/test/**/bin/
    ### NetBeans ###
    /nbproject/private/
    /nbbuild/
    /dist/
    /nbdist/
    /.nb-gradle/
    ### VS Code ###
    .vscode/
    ### Mac OS ###
    .DS_Store

Every plugin needs a main class that extends JavaPlugin. This is the class referenced by Main in your manifest. The constructor runs when the plugin is loaded, and setup() is called when the server is ready for you to register functionality like commands and event listeners.

src/main/java/com/example/myplugin/MyPlugin.java
// Replace 'com.example' with your actual package namespace (e.g., com.yourcompany or io.github.yourusername)
package com.example.myplugin;
import javax.annotation.Nonnull;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
public class MyPlugin extends JavaPlugin {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
public MyPlugin(@Nonnull JavaPluginInit init) {
super(init);
LOGGER.atInfo().log("Hello from " + this.getName() + " v" + this.getManifest().getVersion());
}
@Override
protected void setup() {
LOGGER.atInfo().log(this.getName() + " is ready!");
}
}

That’s it — this plugin will log two messages to the server console when it loads.

  1. Start the server using the runServer Gradle task:

    Terminal window
    ./gradlew runServer
  2. Authenticate the server. You must do this before any client can connect. In the server terminal, run:

    auth login device

    This prints a URL — open it in your browser and sign in with your Hytale account. Then persist the session so you don’t have to re-authenticate every time:

    auth persistence Encrypted

    If you cannot type into the server terminal, you can trigger authentication from code temporarily:

    @Override
    protected void start() {
    CommandManager.get().handleCommand(ConsoleSender.INSTANCE, "auth login device");
    }

    Remove this code after your server is authenticated.

  3. Connect from the Hytale client. Open the game and connect to Local Server. If it doesn’t appear automatically, add 127.0.0.1 manually.

  4. Verify the plugin. Check the server console for your plugin’s log messages.

To build a shareable JAR file, run:

Terminal window
./gradlew build

The output JAR is written to build/libs/. Players can install your plugin by placing the JAR in their %APPDATA%/Hytale/UserData/Mods folder.

The build script includes tasks to decompile the Hytale server JAR using Vineflower, making it easier to explore the game’s code.

To generate decompiled sources:

Terminal window
./gradlew genSources

The decompiled Java files are written to build/generated/sources/vineflower/. You can also package them as a sources JAR for IDE integration:

Terminal window
./gradlew genSourcesJar

This creates a *-sources.jar in build/libs/ that your IDE can attach to the server dependency for inline source viewing.