Skip to content

Registries

Registries are how mods register commands, events, components, and other extensions with the server. All registrations are automatically cleaned up when your mod shuts down.

All registries follow the same pattern:

@Override
protected void setup() {
// get registry from PluginBase, call register method
getCommandRegistry().registerCommand(new MyCommand());
getEventRegistry().register(PlayerJoinEvent.class, this::onJoin);
}

Most registrations should happen during setup(), but can also occur during start() or while running. The server prevents registration when the mod is disabled.

MethodPurposeDetailed Docs
getCommandRegistry()Register commandsCreating Commands
getEventRegistry()Register event listenersListening to Events
getEntityStoreRegistry()Register entity components, resources, systems
getChunkStoreRegistry()Register chunk components, resources, systems
getBlockStateRegistry()Register custom block states
getEntityRegistry()Register custom entity types
getAssetRegistry()Register custom asset stores
getCodecRegistry(mapCodec)Register polymorphic codec types
getTaskRegistry()Track async tasks for cleanup
getClientFeatureRegistry()Register client features and tags
getCommandRegistry().registerCommand(new MyCommand());

See Creating Commands for full documentation.

// listen to specific event
getEventRegistry().register(PlayerJoinEvent.class, this::onPlayerJoin);
// with priority
getEventRegistry().register(EventPriority.EARLY, PlayerJoinEvent.class, this::onPlayerJoin);
// global listener (receives all keys)
getEventRegistry().registerGlobal(PlayerChatEvent.class, this::onAnyChat);
// async events
getEventRegistry().registerAsync(PlayerJoinEvent.class, future ->
future.thenAccept(event -> { /* async handling */ }));

See Listening to Events for full documentation.

The Entity Component System registry for entity-level data. Components registered with a string ID and BuilderCodec are automatically persisted — player components save to players/{uuid}.json, other entity components save to chunk region files.

// persistent component (string ID + codec = saved to disk)
ComponentType<EntityStore, MyData> type = getEntityStoreRegistry()
.registerComponent(MyData.class, "MyMod_Data", MyData.CODEC);
// transient component (no ID, no codec = session only)
ComponentType<EntityStore, MyTemp> temp = getEntityStoreRegistry()
.registerComponent(MyTemp.class, MyTemp::new);
// system (logic that runs on entities each tick)
getEntityStoreRegistry().registerSystem(new MySystem());
// transient resource (singleton per world, not persisted)
ResourceType<EntityStore, MyResource> resource = getEntityStoreRegistry()
.registerResource(MyResource.class, MyResource::new);
// persistent resource (singleton per world, saved to disk)
ResourceType<EntityStore, MyResource> resource = getEntityStoreRegistry()
.registerResource(MyResource.class, "MyMod_Resource", MyResource.CODEC);

Systems implement ISystem<EntityStore>. See Entity Components for reading and writing components.

Same API as Entity Store, but for chunk-level data. Persistent chunk components are saved alongside block data in the chunk saving pipeline.

// persistent chunk component
ComponentType<ChunkStore, MyBlockData> type = getChunkStoreRegistry()
.registerComponent(MyBlockData.class, "MyMod_Block", MyBlockData.CODEC);
// transient chunk component
ComponentType<ChunkStore, MyCache> cache = getChunkStoreRegistry()
.registerComponent(MyCache.class, MyCache::new);

Register custom block state types:

getBlockStateRegistry().registerBlockState(
MyBlockState.class, // state class
"MyBlockState", // unique key
MyBlockState.CODEC // serialization codec
);
// with associated data
getBlockStateRegistry().registerBlockState(
MyBlockState.class,
"MyBlockState",
MyBlockState.CODEC,
MyStateData.class, // data class
MyStateData.CODEC // data codec
);

Register custom entity types:

getEntityRegistry().registerEntity(
"my_entity", // unique key
MyEntity.class, // entity class
world -> new MyEntity(world), // constructor
MyEntity.CODEC // directDecodeCodec for serialization
);

Register custom asset stores for JSON-defined content:

getAssetRegistry().register(
HytaleAssetStore.builder(MyAsset.class, new DefaultAssetMap())
.setPath("MyMod/MyAssets") // JSON folder path
.setCodec(MyAsset.CODEC) // builderCodec for parsing
.setKeyFunction(MyAsset::getId) // how to get asset ID
.build()
);

Asset stores extend AssetStore<K, T, M> and handle loading, inheritance, and hot-reloading of JSON assets.

Register new subtypes within existing polymorphic systems. Use this to extend systems that have a "Type" field in JSON.

Example: Adding a custom interaction type:

// register a new interaction type that can be used in JSON
getCodecRegistry(Interaction.CODEC)
.register(Priority.NORMAL, "MyCustomEffect", MyCustomEffect.class, MyCustomEffect.CODEC);

Now JSON can use your type:

{
"Type": "MyCustomEffect",
"MyCustomField": 42
}

Common polymorphic systems you can extend:

  • Interaction.CODEC - Custom interaction types
  • BalanceAsset.CODEC - Custom balance/tuning assets
  • GameplayConfig.PLUGIN_CODEC - Custom gameplay config sections
  • WorldConfig.PLUGIN_CODEC - Custom per-world config sections

See Assets for using existing types via JSON packs.

Track async tasks for automatic cancellation on shutdown:

CompletableFuture<Void> task = CompletableFuture.runAsync(() -> {
// background work
});
getTaskRegistry().registerTask(task);
// or for scheduled tasks
ScheduledFuture<Void> scheduled = HytaleServer.SCHEDULED_EXECUTOR
.schedule(() -> { /* work */ }, 5, TimeUnit.SECONDS);
getTaskRegistry().registerTask(scheduled);

Register features that require client support:

getClientFeatureRegistry().register(new MyClientFeature());
// register tags sent to clients
getClientFeatureRegistry().registerClientTag("my_custom_tag");

All registrations are automatically unregistered when your mod shuts down. You don’t need to manually unregister anything in shutdown().

@Override
protected void shutdown() {
// commands, events, components, etc. are already unregistered
// only handle your own cleanup (save data, close connections)
}