Getting Started with Plugin Development
Welcome to Hytale plugin development! This guide will help you create your first plugin.
Prerequisites
Required Tools
- Java Development Kit (JDK) 21+ - Hytale uses modern Java features
- IntelliJ IDEA or Eclipse - Recommended IDEs
- Gradle - Build system (included in project)
- Git - Version control
Knowledge Requirements
- Java programming fundamentals
- Basic understanding of object-oriented programming
- Familiarity with event-driven architecture (helpful)
Alternative Languages
Kotlin Support: Kotlin is fully compatible with Hytale plugins since both Java and Kotlin compile to JVM bytecode.
“If it supports Java, it also supports Kotlin” - Community consensus
Benefits of Kotlin:
- Null safety
- Extension functions
- Less boilerplate
- Modern syntax
Important: When using Kotlin, you must shade the Kotlin standard library into your JAR:
// build.gradle.kts
plugins {
kotlin("jvm") version "1.9.0"
id("com.github.johnrengelman.shadow") version "8.1.1"
}
tasks.shadowJar {
// Include Kotlin runtime
}Development Environment Setup
1. Install JDK 21
Windows:
# Download from Oracle or use Chocolatey
choco install openjdk21Mac:
# Using Homebrew
brew install openjdk@21Linux:
# Ubuntu/Debian
sudo apt install openjdk-21-jdk2. Set Up IDE
IntelliJ IDEA (Recommended):
- Download IntelliJ IDEA Community Edition
- Install Gradle plugin
- Configure JDK 21 in Project Structure
Eclipse:
- Download Eclipse IDE for Java Developers
- Install Buildship Gradle plugin
- Configure JDK 21 in Installed JREs
3. Create Hytale Server
Download Hytale Server from official sources:
# Extract server files
unzip hytale-server.zip
# Run server once to generate files
cd hytale-server
./start.shCreating Your First Plugin
Project Structure
MyPlugin/
├── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── myplugin/
│ │ └── MyPlugin.java
│ └── resources/
│ └── manifest.json
├── build.gradle
└── settings.gradle1. Initialize Gradle Project
build.gradle:
plugins {
id 'java'
}
group = 'com.example'
version = '1.0.0'
repositories {
mavenCentral()
// Hytale repository (when available)
maven { url 'https://repo.hytale.com/releases' }
}
dependencies {
// Hytale API
compileOnly 'com.hytale:hytale-api:1.0.0'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
jar {
archiveBaseName = 'MyPlugin'
archiveVersion = project.version
}settings.gradle:
rootProject.name = 'MyPlugin'2. Create Plugin Manifest
The manifest.json file is required in your plugin JAR root. This format was confirmed from the decompiled server source code.
src/main/resources/manifest.json:
{
"Group": "com.example",
"Name": "MyPlugin",
"Version": "1.0.0",
"Description": "My first Hytale plugin",
"Authors": [
{
"Name": "YourName"
}
],
"Main": "com.example.myplugin.MyPlugin",
"ServerVersion": "*",
"Dependencies": {},
"IncludesAssetPack": false
}Manifest Fields Explained:
| Field | Required | Description |
|---|---|---|
Group | Yes | Plugin group/namespace (e.g., com.example) |
Name | Yes | Plugin name |
Version | Yes | Semantic version (e.g., 1.0.0) |
Description | No | Plugin description |
Authors | No | Array of author objects with Name field |
Main | Yes | Fully qualified main class name |
ServerVersion | No | Server version requirements (* for any) |
Dependencies | No | Map of plugin dependencies with version ranges |
OptionalDependencies | No | Optional plugin dependencies |
LoadBefore | No | Plugins this should load before |
DisabledByDefault | No | If true, plugin must be enabled manually |
IncludesAssetPack | No | Set to true if plugin includes custom assets |
Example with Dependencies:
{
"Group": "com.example",
"Name": "MyPlugin",
"Version": "1.0.0",
"Main": "com.example.myplugin.MyPlugin",
"ServerVersion": "2026.1.*",
"Dependencies": {
"Hytale:DamageModule": "*"
},
"OptionalDependencies": {
"SomePlugin:Economy": ">=1.0.0"
}
}3. Create Main Plugin Class
src/main/java/com/example/myplugin/MyPlugin.java:
package com.example.myplugin;
import com.hytale.api.plugin.JavaPlugin;
import com.hytale.api.event.player.PlayerReadyEvent;
import com.hytale.api.message.Message;
public class MyPlugin extends JavaPlugin {
@Override
protected void setup() {
getLogger().atInfo().log("Setting up MyPlugin...");
// Register event listener
getEventRegistry().registerGlobal(PlayerReadyEvent.class, this::onPlayerReady);
// Register command
getCommandRegistry().registerCommand(new HelloCommand());
}
@Override
protected void start() {
getLogger().atInfo().log("MyPlugin started successfully!");
}
@Override
protected void shutdown() {
getLogger().atInfo().log("MyPlugin shutting down...");
}
private void onPlayerReady(PlayerReadyEvent event) {
event.getPlayer().sendMessage(
Message.raw("Welcome to the server!")
);
}
}4. Create a Simple Command
HelloCommand.java:
package com.example.myplugin;
import com.hytale.api.command.CommandBase;
import com.hytale.api.command.CommandSender;
import com.hytale.api.entity.player.Player;
import com.hytale.api.message.Message;
public class HelloCommand extends CommandBase {
@Override
public String getName() {
return "hello";
}
@Override
public String getDescription() {
return "Say hello!";
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage("Only players can use this command");
return;
}
Player player = (Player) sender;
player.sendMessage(Message.raw("Hello, " + player.getName() + "!"));
}
}Building Your Plugin
Using Gradle
# Build the plugin
./gradlew build
# Output location
# build/libs/MyPlugin-1.0.0.jarManual Build (IntelliJ)
- Build > Build Project
- Find JAR in
out/artifacts/orbuild/libs/
Installing Your Plugin
1. Copy to Server
# Copy JAR to server mods directory
cp build/libs/MyPlugin-1.0.0.jar /path/to/hytale-server/mods/2. Start Server
cd /path/to/hytale-server
./start.sh3. Verify Plugin Loaded
Check server console for:
[INFO] Setting up MyPlugin...
[INFO] MyPlugin started successfully!Testing Your Plugin
1. Join Server
Connect to your server using Hytale client:
localhost:55202. Test Welcome Message
When you join, you should see:
Welcome to the server!3. Test Command
In game chat:
/helloExpected output:
Hello, YourName!Development Workflow
Hot Reload Setup
Install the HotPluginReload plugin for faster development:
# Copy HotPluginReload to mods
cp HotPluginReload.jar /path/to/server/mods/Workflow:
# 1. Make code changes
# 2. Build
./gradlew build
# 3. Copy to server
cp build/libs/MyPlugin-1.0.0.jar /path/to/server/mods/
# 4. HotPluginReload automatically detects and reloadsDebugging
IntelliJ Remote Debugging:
- Add to server start script:
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar hytale-server.jar- In IntelliJ: Run > Edit Configurations > Add Remote JVM Debug
- Set port to 5005
- Start debugging session
Common First Plugin Ideas
1. Welcome Plugin
- Welcome message on join
- Configurable message
- Permission-based VIP message
2. Simple Teleport
- /spawn command
- /sethome and /home commands
- Save locations to file
3. Player Stats
- Track kills/deaths
- Display on command
- Save to database
4. Chat Features
- Custom chat format
- Colored names by rank
- Chat filter
5. Admin Tools
- /heal command
- /feed command
- /fly command
Plugin Architecture Best Practices
1. Separate Concerns
myplugin/
├── MyPlugin.java # Main class
├── commands/ # All commands
│ ├── HelloCommand.java
│ └── SpawnCommand.java
├── listeners/ # Event handlers
│ └── PlayerListener.java
├── data/ # Data management
│ ├── DataStore.java
│ └── PlayerData.java
└── config/ # Configuration
└── ConfigManager.java2. Use Services
@Override
protected void setup() {
// Register services for other plugins to use
registerService(new EconomyService());
registerService(new PermissionService());
}3. Handle Errors Gracefully
@Override
public void execute(CommandSender sender, String[] args) {
try {
// Command logic
} catch (Exception e) {
sender.sendMessage("An error occurred");
getLogger().atSevere().log("Command error", e);
}
}4. Use Async for Heavy Operations
CompletableFuture.runAsync(() -> {
// Load data from database
}).thenAccept(data -> {
// Update player on main thread
});Next Steps
Now that you have your first plugin running:
- Learn Event System - Event System
- Create Commands - Commands
- Understand ECS - ECS Architecture
- Add Configuration - Configuration
- Implement Permissions - Permissions
- Study Examples - Complete Examples
Troubleshooting
Plugin Not Loading
Check manifest.json:
- Correct main class path
- Valid JSON format
- Dependencies listed correctly
Check logs:
tail -f logs/latest.logClassNotFoundException
- Ensure all dependencies in build.gradle
- Verify main class package matches manifest
Events Not Firing
- Check event is registered in setup()
- Verify correct event type
- Check if dependencies required (e.g., DamageModule)
Community Resources
- Discord - Active community for questions
- GitHub - Example plugins and libraries
- Documentation - This site!
Development Tips
- Start Simple - Don’t try to build everything at once
- Test Frequently - Use hot reload to iterate quickly
- Read Examples - Study existing plugins
- Ask Questions - Community is helpful
- Version Control - Use Git from the start
- Document Code - Future you will thank you
Example Development Session
# 1. Create project
mkdir MyPlugin && cd MyPlugin
gradle init
# 2. Add dependencies to build.gradle
# 3. Create manifest.json
# 4. Write MyPlugin.java
# 5. Build
./gradlew build
# 6. Copy to server
cp build/libs/MyPlugin-1.0.0.jar ~/hytale-server/mods/
# 7. Test
cd ~/hytale-server
./start.sh
# 8. Verify in logs
tail -f logs/latest.log | grep MyPluginReady to dive deeper? Check out the Event System to learn about handling game events!