Hytale Plugin Development

Plugins let you extend Hytale with Java code. You can add custom commands, handle events, create new game systems, and build complex features that packs alone can't do.

Early Access Notice

Hytale is in Early Access. The API may change and official documentation is incomplete. This guide is based on verified community research and official sources.

What You Need

  • Java 25 - Download from adoptium.net
  • IntelliJ IDEA - Free Community Edition from jetbrains.com
  • Basic Java knowledge - Variables, classes, methods, etc.

Quick Start

The fastest way to start is with the official template by Darkhax & Jared:

Terminal
git clone https://github.com/realBritakee/hytale-template-plugin.git
cd hytale-template-plugin
./gradlew build

The template includes:

  • Latest Hytale server files in classpath
  • IDE-integrated debugging with breakpoints
  • Asset bundling (editable via in-game Asset Editor)
  • Pre-configured Gradle build system
  • Example code and assets

Project Structure

A Hytale plugin project looks like this:

my-plugin/
src/main/java/
com/yourname/plugin/
MyPlugin.java
src/main/resources/
manifest.json
Common/
Server/
build.gradle
settings.gradle
gradle.properties

manifest.json

src/main/resources/manifest.json
{
    "Group": "com.yourname",
    "Name": "MyPlugin",
    "Version": "1.0.0",
    "Description": "My first Hytale plugin",
    "Authors": [
        {
            "Name": "Your Name",
            "Email": "your.email@example.com",
            "Url": "https://your-website.com"
        }
    ],
    "Website": "https://your-plugin-website.com",
    "ServerVersion": "*",
    "Dependencies": {},
    "OptionalDependencies": {},
    "DisabledByDefault": false
}

Gradle Configuration

build.gradle.kts
plugins {
    id("java")
    id("com.github.johnrengelman.shadow") version "8.1.1"
}

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(25))
    }
}

dependencies {
    compileOnly("com.hypixel.hytale:hytale-server:+")
}

tasks.shadowJar {
    archiveClassifier.set("")
}
settings.gradle
rootProject.name = 'my-plugin'
gradle.properties
hytale_version=latest
plugin_version=1.0.0

API Packages

PackagePurpose
com.hypixel.hytale.pluginPlugin base classes and initialization
com.hypixel.hytale.plugin.earlyBootstrap/early plugins and class transformers
com.hypixel.hytale.api.eventEvent system and EventBus
com.hypixel.hytale.api.commandCommand system and registration
com.hypixel.hytale.api.permissionPermission checking and management

Main Plugin Class

Your plugin must extend JavaPlugin and implement the lifecycle methods:

MyPlugin.java
package com.yourname.plugin;

import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import javax.annotation.Nonnull;

public class MyPlugin extends JavaPlugin {

    public MyPlugin(@Nonnull JavaPluginInit init) {
        super(init);
        getLogger().info("MyPlugin loaded!");
    }

    @Override
    public void setup() {
        // Called after constructor
        // Register systems and load configs here
        getLogger().info("MyPlugin setup complete!");
    }

    @Override
    public void start() {
        // Called when server is ready
        // Register commands and event listeners here
        getLogger().info("MyPlugin started!");
    }

    @Override
    public void shutdown() {
        // Called when server stops
        // Save data and cleanup here
        getLogger().info("MyPlugin shutting down!");
    }
}

Plugin Lifecycle

MethodWhen CalledUse For
constructorPlugin loadedInitial setup, load configs
setup()After constructorRegister systems, ECS stores
start()Server readyRegister events, commands
shutdown()Server stoppingSave data, cleanup

Handling Events

Hytale uses an EventBus for event handling. Events are registered using getEventRegistry().

Registering Event Handlers

Java
import com.hypixel.hytale.server.core.event.events.PlayerConnectEvent;
import com.hypixel.hytale.server.core.event.events.PlayerDisconnectEvent;
import com.hypixel.hytale.server.core.event.events.PlayerChatEvent;

@Override
public void start() {
    // Listen for player connect
    getEventRegistry().register(PlayerConnectEvent.class, event -> {
        var player = event.getPlayer();
        getLogger().info(player.getName() + " connected!");
    });

    // Listen for player disconnect
    getEventRegistry().register(PlayerDisconnectEvent.class, event -> {
        var player = event.getPlayer();
        getLogger().info(player.getName() + " disconnected!");
    });

    // Listen for chat (async event)
    getEventRegistry().registerAsync(PlayerChatEvent.class, future -> {
        return future.thenApply(event -> {
            String message = event.getMessage();
            getLogger().info("Chat: " + message);
            return event;
        });
    });
}

Event Priority

You can control when your handler runs relative to others:

Java
import com.hypixel.hytale.server.core.event.EventPriority;

// Run before other handlers
getEventRegistry().register(EventPriority.FIRST, PlayerConnectEvent.class, event -> {
    // This runs first
});

// Run after other handlers
getEventRegistry().register(EventPriority.LAST, PlayerConnectEvent.class, event -> {
    // This runs last
});
PriorityValueUse For
FIRST-21844Run before everything
EARLY-10922Run early
NORMAL0Default priority
LATE10922Run late
LAST21844Run after everything

Player Events

EventWhen It FiresCancellable
PlayerSetupConnectEventBefore player connectsYes
PlayerConnectEventPlayer connectsNo
PlayerReadyEventPlayer fully loadedNo
PlayerDisconnectEventPlayer disconnectsNo
PlayerChatEventPlayer sends messageYes (async)
PlayerInteractEventPlayer interactsYes
PlayerMouseButtonEventMouse button pressedYes
PlayerMouseMotionEventMouse movedYes
PlayerCraftEventPlayer crafts itemNo

Block Events (ECS)

EventWhen It FiresCancellable
BreakBlockEventBlock is brokenYes
PlaceBlockEventBlock is placedYes
DamageBlockEventBlock takes damageYes
UseBlockEvent.PreBefore using blockYes
UseBlockEvent.PostAfter using blockNo

Cancelling Events

Events that implement ICancellable can be cancelled:

Java
import com.hypixel.hytale.server.core.event.events.BreakBlockEvent;

getEventRegistry().register(BreakBlockEvent.class, event -> {
    var block = event.getBlock();

    // Prevent breaking bedrock
    if (block.getType().getName().equals("bedrock")) {
        event.setCancelled(true);
    }
});

Creating Commands

Commands are registered using getCommandRegistry().

Player Command

Java
import com.hypixel.hytale.server.core.command.AbstractPlayerCommand;
import com.hypixel.hytale.server.core.command.CommandContext;
import java.util.concurrent.CompletableFuture;

public class HealCommand extends AbstractPlayerCommand {

    public HealCommand() {
        super("heal", "Restore your health to maximum");
    }

    @Override
    public CompletableFuture<Void> execute(CommandContext context) {
        var player = context.getPlayer();
        player.setHealth(player.getMaxHealth());
        player.sendMessage("You have been healed!");
        return CompletableFuture.completedFuture(null);
    }
}

Async Command

For commands that do I/O or take time:

Java
import com.hypixel.hytale.server.core.command.AbstractAsyncCommand;

public class StatsCommand extends AbstractAsyncCommand {

    public StatsCommand() {
        super("stats", "View player statistics");
    }

    @Override
    public CompletableFuture<Void> execute(CommandContext context) {
        return CompletableFuture.runAsync(() -> {
            // Fetch data from database (async)
            var stats = fetchPlayerStats(context.getPlayer());
            context.getPlayer().sendMessage("Your stats: " + stats);
        });
    }
}

Registering Commands

Java
@Override
public void start() {
    getCommandRegistry().register(new HealCommand());
    getCommandRegistry().register(new StatsCommand());
}

Permissions

Check player permissions before allowing actions:

Java
if (player.hasPermission("myplugin.admin")) {
    // Player has admin permission
    player.sendMessage("You are an admin!");
} else {
    player.sendMessage("You don't have permission for that.");
}

Permission Events

Listen for permission changes:

EventWhen It Fires
PlayerPermissionChangeEvent.PermissionsAddedPermissions added to player
PlayerPermissionChangeEvent.PermissionsRemovedPermissions removed from player
PlayerPermissionChangeEvent.GroupAddedPlayer added to group
PlayerPermissionChangeEvent.GroupRemovedPlayer removed from group

Configuration Files

Load configuration in your constructor using withConfig():

Java
public class MyPlugin extends JavaPlugin {
    private MyConfig config;

    public MyPlugin(@Nonnull JavaPluginInit init) {
        super(init);

        // Load config file
        this.config = withConfig("config.json", MyConfigCodec.INSTANCE);
    }
}

Data Directory

Get your plugin's data folder:

Java
Path dataDir = getDataDirectory();
Path playerDataFile = dataDir.resolve("playerdata.json");

Task Scheduling

Schedule tasks using getTaskRegistry():

Java
// Run a task later
getTaskRegistry().runLater(() -> {
    getLogger().info("This runs after a delay!");
}, Duration.ofSeconds(5));

// Run a repeating task
getTaskRegistry().runRepeating(() -> {
    getLogger().info("This runs every 10 seconds!");
}, Duration.ofSeconds(10));

Available Registries

Your plugin has access to these registries:

RegistryMethodUse For
EventsgetEventRegistry()Event listeners
CommandsgetCommandRegistry()Chat commands
TasksgetTaskRegistry()Scheduled tasks
Entity StoresgetEntityStoreRegistry()ECS systems

Building Your Plugin

To create a JAR file:

Terminal
./gradlew build

The JAR appears in build/libs/.

Installation

Copy the JAR to your mods folder:

Windows
%appdata%\Hytale\UserData\Mods\
Full Path
C:\Users\YourUsername\AppData\Roaming\Hytale\UserData\Mods\

Debugging & Development Tips

The template includes a pre-configured debug setup:

  1. Open your project in IntelliJ IDEA
  2. Find the "HytaleServer" run configuration
  3. Click the bug icon to run with debugger
  4. Set breakpoints in your code
  5. Your breakpoints will pause execution!

Remote Debugging

Connect your IDE debugger to port 5005 for remote debugging. The server auto-reloads on file changes in development mode.

Useful Launch Arguments

ArgumentDescription
--disable-sentryDisable crash reporting (useful for development)
--assets <path>Path to assets zip file (required)
--bind <port>Custom port (default 5520)
--early-plugins <path>Custom path for bootstrap plugins
Development Tip

Use --disable-sentry during development to prevent crash reports from being sent. Logging methods: getLogger().info(), getLogger().warn(), getLogger().error().

Troubleshooting

ProblemSolution
Gradle sync failsVerify Java 25 is installed and configured in IntelliJ
Missing run configRe-import Gradle project (File → Sync Project)
Plugin not loadingCheck manifest.json is valid, check server logs
Breakpoints not workingMake sure you're running in Debug mode (bug icon)

Bootstrap/Early Plugins

For advanced low-level modifications, Bootstrap plugins run before the main server starts. They allow bytecode injection and class transformations.

Purpose

  • Class transformations and bytecode injection
  • Modify classes as they load
  • Runs before main server initialization
  • Can even modify HytaleServer class

Setup

  1. Create earlyplugins/ folder manually in server directory
  2. Or use --early-plugins <path> launch argument for custom paths
  3. Implement ClassTransformer interface
  4. Create service loader file at META-INF/services/

Creating a Class Transformer

ExampleTransformer.java
package com.example.early;

import com.hypixel.hytale.plugin.early.ClassTransformer;

public class ExampleTransformer implements ClassTransformer {
    @Override
    public byte[] transform(String className, byte[] classBytes) {
        // Modify bytecode here using ASM or Mixin
        return classBytes;
    }

    @Override
    public int getPriority() {
        return 0; // Higher values run first
    }
}

Service Loader File

Create a file at META-INF/services/com.hypixel.hytale.plugin.early.ClassTransformer:

META-INF/services/com.hypixel.hytale.plugin.early.ClassTransformer
com.example.early.ExampleTransformer
Advanced Feature

Bootstrap plugins can destabilize the game if used incorrectly. Use sparingly and only when regular plugins cannot achieve your goal. Some packages are restricted from modification. Use ASM or Mixin libraries for bytecode manipulation.