Skip to content

Plugin System

The Hytale server uses a powerful plugin system that allows you to extend server functionality.

Every plugin requires a manifest.json file in the root of your JAR:

{
"Group": "com.example",
"Name": "MyPlugin",
"Version": "1.0.0",
"Description": "A sample plugin",
"Main": "com.example.MyPlugin",
"Authors": [
{
"Name": "Your Name",
"Email": "you@example.com",
"Url": "https://example.com"
}
],
"Website": "https://example.com/myplugin",
"ServerVersion": ">=0.0.1",
"Dependencies": {
"Hytale:SomePlugin": ">=1.0.0"
},
"OptionalDependencies": {
"Hytale:OptionalPlugin": "*"
},
"LoadBefore": {
"Hytale:AnotherPlugin": "*"
},
"DisabledByDefault": false,
"IncludesAssetPack": false
}
FieldRequiredDescription
GroupYesThe plugin’s group/namespace (e.g., com.example)
NameYesThe plugin name (used with Group to form identifier)
VersionYesSemantic version string (e.g., 1.0.0)
DescriptionNoShort description of the plugin
MainYesFully qualified class name of the main plugin class
AuthorsNoArray of author objects with Name, Email, and Url fields (all optional)
WebsiteNoPlugin website URL
ServerVersionNoRequired server version range (e.g., >=0.1.0)
DependenciesNoMap of required plugin identifiers to version ranges
OptionalDependenciesNoMap of optional plugin identifiers to version ranges
LoadBeforeNoMap of plugins that should load after this plugin
DisabledByDefaultNoIf true, plugin won’t load unless explicitly enabled
IncludesAssetPackNoIf true, registers the JAR as an asset pack
SubPluginsNoArray of nested plugin manifests (for multi-plugin JARs)

The plugin identifier is formed as Group:Name (e.g., com.example:MyPlugin).

package com.example;
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 {
private static MyPlugin instance;
public MyPlugin(@Nonnull JavaPluginInit init) {
super(init);
}
public static MyPlugin get() {
return instance;
}
@Override
protected void setup() {
instance = this;
getLogger().at(Level.INFO).log("Plugin setup complete!");
}
@Override
protected void start() {
getLogger().at(Level.INFO).log("Plugin started!");
}
@Override
protected void shutdown() {
getLogger().at(Level.INFO).log("Plugin shutting down!");
}
}
MethodWhen CalledPurpose
setup()During server initializationRegister components, commands, events
start()After all plugins are set upStart background tasks, initialize state
shutdown()When server is stoppingClean up resources, save data

The plugin system fires events at each lifecycle stage:

import com.hypixel.hytale.server.core.event.events.BootEvent;
// In setup()
getEventRegistry().register(BootEvent.class, event -> {
getLogger().at(Level.INFO).log("Server booted!");
});

Each plugin has access to several registries for registering various components:

RegistryGetter MethodPurpose
CommandRegistrygetCommandRegistry()Register custom commands
EventRegistrygetEventRegistry()Register event listeners
TaskRegistrygetTaskRegistry()Register async tasks
EntityRegistrygetEntityRegistry()Register custom entities
BlockStateRegistrygetBlockStateRegistry()Register block states
AssetRegistrygetAssetRegistry()Register custom assets
ClientFeatureRegistrygetClientFeatureRegistry()Register client features
EntityStoreRegistrygetEntityStoreRegistry()Register entity storage components
ChunkStoreRegistrygetChunkStoreRegistry()Register chunk storage components
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
@Override
protected void setup() {
// Commands
getCommandRegistry().registerCommand(new MyCommand());
// Events - use PlayerConnectEvent for player connection handling
getEventRegistry().register(PlayerConnectEvent.class, this::onPlayerConnect);
// Tasks - register a CompletableFuture<Void> for async operations
getTaskRegistry().registerTask(myFuture);
// Logging
getLogger().at(Level.INFO).log("Setup complete!");
}

Plugins can be reloaded at runtime. To support hot reloading:

  1. Clean up in shutdown() - Unregister listeners, stop tasks
  2. Use registries - Registrations are automatically cleaned up
  3. Avoid static state - Use the plugin instance pattern
private static MyPlugin instance;
public static MyPlugin get() {
return instance;
}
@Override
protected void setup() {
instance = this;
// Safe: instance is refreshed on reload
}
plugins {
id 'java'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
dependencies {
compileOnly files('path/to/HytaleServer.jar')
}
jar {
from('src/main/resources') {
include 'manifest.json'
}
}
  1. Build your JAR file
  2. Place it in the mods/ directory
  3. Restart the server (or use hot reload if supported)

Plugins are loaded from multiple locations in this order:

  1. Core plugins - Built-in server functionality
  2. Builtin directory - builtin/ next to the server JAR
  3. Classpath - Plugins bundled with the server
  4. Mods directory - mods/ (user plugins)
  5. Additional directories - Specified via --mods-directories option

Dependencies are resolved to ensure correct loading order within each stage.