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.
Common Pattern
Section titled “Common Pattern”All registries follow the same pattern:
@Overrideprotected 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.
Available Registries
Section titled “Available Registries”| Method | Purpose | Detailed Docs |
|---|---|---|
getCommandRegistry() | Register commands | Creating Commands |
getEventRegistry() | Register event listeners | Listening 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 | — |
Commands
Section titled “Commands”getCommandRegistry().registerCommand(new MyCommand());See Creating Commands for full documentation.
Events
Section titled “Events”// listen to specific eventgetEventRegistry().register(PlayerJoinEvent.class, this::onPlayerJoin);
// with prioritygetEventRegistry().register(EventPriority.EARLY, PlayerJoinEvent.class, this::onPlayerJoin);
// global listener (receives all keys)getEventRegistry().registerGlobal(PlayerChatEvent.class, this::onAnyChat);
// async eventsgetEventRegistry().registerAsync(PlayerJoinEvent.class, future -> future.thenAccept(event -> { /* async handling */ }));See Listening to Events for full documentation.
Entity Store (ECS)
Section titled “Entity Store (ECS)”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.
Chunk Store (ECS)
Section titled “Chunk Store (ECS)”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 componentComponentType<ChunkStore, MyBlockData> type = getChunkStoreRegistry() .registerComponent(MyBlockData.class, "MyMod_Block", MyBlockData.CODEC);
// transient chunk componentComponentType<ChunkStore, MyCache> cache = getChunkStoreRegistry() .registerComponent(MyCache.class, MyCache::new);Block States
Section titled “Block States”Register custom block state types:
getBlockStateRegistry().registerBlockState( MyBlockState.class, // state class "MyBlockState", // unique key MyBlockState.CODEC // serialization codec);
// with associated datagetBlockStateRegistry().registerBlockState( MyBlockState.class, "MyBlockState", MyBlockState.CODEC, MyStateData.class, // data class MyStateData.CODEC // data codec);Entity Types
Section titled “Entity Types”Register custom entity types:
getEntityRegistry().registerEntity( "my_entity", // unique key MyEntity.class, // entity class world -> new MyEntity(world), // constructor MyEntity.CODEC // directDecodeCodec for serialization);Asset Stores
Section titled “Asset Stores”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.
Codec Registry
Section titled “Codec Registry”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 JSONgetCodecRegistry(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 typesBalanceAsset.CODEC- Custom balance/tuning assetsGameplayConfig.PLUGIN_CODEC- Custom gameplay config sectionsWorldConfig.PLUGIN_CODEC- Custom per-world config sections
See Assets for using existing types via JSON packs.
Task Registry
Section titled “Task Registry”Track async tasks for automatic cancellation on shutdown:
CompletableFuture<Void> task = CompletableFuture.runAsync(() -> { // background work});getTaskRegistry().registerTask(task);
// or for scheduled tasksScheduledFuture<Void> scheduled = HytaleServer.SCHEDULED_EXECUTOR .schedule(() -> { /* work */ }, 5, TimeUnit.SECONDS);getTaskRegistry().registerTask(scheduled);Client Features
Section titled “Client Features”Register features that require client support:
getClientFeatureRegistry().register(new MyClientFeature());
// register tags sent to clientsgetClientFeatureRegistry().registerClientTag("my_custom_tag");Automatic Cleanup
Section titled “Automatic Cleanup”All registrations are automatically unregistered when your mod shuts down. You don’t need to manually unregister anything in shutdown().
@Overrideprotected void shutdown() { // commands, events, components, etc. are already unregistered // only handle your own cleanup (save data, close connections)}