read first
Most issues come from threading violations or incorrect manifest configuration. Check these first before diving deeper.
Threading Violations
| symptom | cause | fix |
|---|---|---|
| IllegalStateException: Not on world thread | Entity modification outside world.execute() | Wrap in player.getWorld().execute(() -> {...}) |
| ConcurrentModificationException | Modifying collections during iteration | Copy collection before modifying or use execute() |
| Silent failure (code runs but nothing happens) | Thread-unsafe component access | Always use world.execute() for component changes |
| Random NullPointerException | Entity reference became invalid | Get fresh references inside execute() |
1// WRONG - Will crash or fail silently2getEventRegistry().register(PlayerConnectEvent.class, event -> {3 Player player = event.getPlayer();4 player.setModel("Mouse"); // CRASH!5});67// CORRECT - Safe execution8getEventRegistry().register(PlayerConnectEvent.class, event -> {9 Player player = event.getPlayer();10 player.getWorld().execute(() -> {11 player.setModel("Mouse"); // Safe!12 });13});Manifest Issues
| symptom | cause | fix |
|---|---|---|
| Plugin not loading at all | Wrong filename | Use manifest.json, NOT plugin.json |
| ClassNotFoundException | Wrong Main class path | Check Main matches full package path |
| Plugin loads but start() not called | Constructor doesn't call super() | Add super(init) to constructor |
| NoSuchMethodException | Wrong constructor signature | Must be public MyPlugin(JavaPluginInit init) |
manifest.jsonjson
1{2 "Group": "com.example",3 "Name": "my-plugin",4 "Version": "1.0.0",5 "Main": "com.example.MyPlugin"6}common mistake
The file MUST be named
manifest.json. Using plugin.jsonwill cause the plugin to silently fail to load.MaybeBool Enum
Uses PascalCase, not SCREAMING_CASE:
1// CORRECT2MaybeBool.True3MaybeBool.False45// WRONG - Will not compile6MaybeBool.TRUE // Error!7MaybeBool.FALSE // Error!Deprecated Methods
Many methods show deprecation warnings but still work. Use them until replacements are documented:
| method | status | notes |
|---|---|---|
| player.getUuid() | Deprecated but works | Safe to use |
| player.getPlayerConnection() | Deprecated but works | Required for packets |
| player.getTransformComponent() | Deprecated but works | Use for position |
| world.getPlayers() | Deprecated but works | Returns all players |
1// These warnings are safe to ignore for now2@SuppressWarnings("deprecation")3public void myMethod(Player player) {4 UUID uuid = player.getUuid(); // Works fine5 PlayerConnection conn = player.getPlayerConnection(); // Works fine6}Compilation Errors
| error | cause | fix |
|---|---|---|
| cannot find symbol: class Player | Missing import | import com.hypixel.hytale.server.core.entity.entities.Player |
| incompatible types: double cannot be converted to float | Wrong number type | Add f suffix: 1.0f instead of 1.0 |
| method does not override or implement | Wrong method signature | Check exact parameter types |
1# Compilation command2javac --release 21 \3 -cp "/path/to/HytaleServer.jar" \4 -d build/classes \5 src/com/example/*.javaEvent Issues
| symptom | cause | fix |
|---|---|---|
| Event handler never called | Registered in setup() instead of start() | Move registration to start() |
| Event called multiple times | Registered multiple times | Only register once in start() |
| event.setCancelled() not working | Wrong event priority | Use higher priority or check isCancelled() |
| Compilation error: wrong register() signature | Using wrong registration method | See keyed vs non-keyed events below |
1// WRONG - setup() is too early2@Override3protected void setup() {4 getEventRegistry().register(...); // May not work!5}67// CORRECT - start() is the right place8@Override9protected void start() {10 getEventRegistry().register(...); // Works!11}Keyed vs Non-Keyed Events
critical gotcha
Events in Hytale use different registration signatures depending on whether they have a "key" type. Using the wrong one causes compilation errors.
Check if the event extends IBaseEvent<Void> (non-keyed) orIBaseEvent<SomeType> (keyed):
| event type | key type | registration |
|---|---|---|
| PlayerConnectEvent | Void (non-keyed) | register(Class, Consumer) |
| PlayerReadyEvent | Object (keyed) | register(Class, Object, Consumer) |
| PlayerChatEvent | keyed | register(Class, key, Consumer) |
| PlayerInteractEvent | keyed | register(Class, key, Consumer) |
1// NON-KEYED EVENT (like PlayerConnectEvent)2// Simple registration - just class and handler3getEventRegistry().register(PlayerConnectEvent.class, event -> {4 Player player = event.getPlayer();5 // handle connect6});78// KEYED EVENT (like PlayerReadyEvent) 9// Requires a key parameter in the middle!10getEventRegistry().register(PlayerReadyEvent.class, new Object(), event -> {11 Player player = event.getPlayer();12 // handle ready13});1415// WRONG - This will NOT compile for keyed events:16getEventRegistry().register(PlayerReadyEvent.class, event -> {17 // ERROR: wrong method signature!18});If you get a compilation error about register() not accepting your lambda, the event is probably keyed. Check the API docs for the event's key type.
Packet Issues
| symptom | cause | fix |
|---|---|---|
| Title not showing | Wrong packet parameters | Check fadeIn, fadeOut, duration > 0 |
| Particles not visible | Wrong position or scale | Check spawn position is near player |
| Speed change not applying | Missing fields | Set ALL speed multiplier fields |
Logger (Flogger)
Hytale uses Google's Flogger library, NOT standard Java logging. The syntax is different:
1// WRONG - Standard Java logging syntax2getLogger().info("Player joined"); // Error!3getLogger().warning("Something went wrong"); // Error!45// CORRECT - Flogger syntax6import java.util.logging.Level;78getLogger().at(Level.INFO).log("Player joined");9getLogger().at(Level.WARNING).log("Something went wrong");10getLogger().at(Level.SEVERE).log("Critical error!");1112// With formatting13getLogger().at(Level.INFO).log("Player %s joined", player.getName());common mistake
If you see
getLogger().info() in examples, it's wrong. Always use getLogger().at(Level.INFO).log().Debugging Tips
add logging
Use
getLogger().at(Level.INFO).log() liberally to trace execution flow.check server console
Exceptions are logged to the server console - always check it.
isolate the problem
Comment out code sections to find which part causes issues.
test threading
If something "doesn't work" but has no errors, it's probably threading.
Getting Help
When asking for help, provide:
- Full stack trace (if any)
- Relevant code snippets
- What you expected vs what happened
- Server version
- Steps to reproduce
Most problems are solved by reading this documentation carefully. Search for your error message in the docs before asking.