Assets & Registry
The Hytale asset system provides a powerful way to register, load, and manage custom game content.
Architecture
Section titled “Architecture”AssetRegistry (com.hypixel.hytale.assetstore.AssetRegistry)├── AssetStore<K, T, M>[] - Type-specific stores│ ├── AssetCodec - Serialization│ ├── AssetMap - Storage/lookup│ └── AssetPack[] - Content packs└── Tag indices - TAG_MAP / CLIENT_TAG_MAP (string → int)Asset Stores
Section titled “Asset Stores”Registering an Asset Store
Section titled “Registering an Asset Store”import com.hypixel.hytale.assetstore.codec.AssetCodec;import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;import com.hypixel.hytale.server.core.asset.HytaleAssetStore;
@Overrideprotected void setup() { getAssetRegistry().register( HytaleAssetStore.builder(MyAsset.class, new IndexedLookupTableAssetMap<>(MyAsset[]::new)) .setPath("MyAssets") .setCodec((AssetCodec) MyAsset.CODEC) .setKeyFunction(MyAsset::getId) .loadsAfter(OtherAsset.class) // Load dependencies .build() );}Asset Store Builder
Section titled “Asset Store Builder”HytaleAssetStore.builder(AssetClass.class, new IndexedLookupTableAssetMap<>(AssetClass[]::new)) .setPath("AssetDirectory") // JSON files location .setCodec((AssetCodec) Asset.CODEC) // Serialization codec .setKeyFunction(Asset::getId) // Key extraction function .loadsAfter(Dependency.class) // Load after this asset type .loadsBefore(Dependent.class) // Load before this asset type .build();Creating Assets
Section titled “Creating Assets”Asset Class
Section titled “Asset Class”import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;import com.hypixel.hytale.codec.builder.BuilderCodec;
public class MyAssetimplements JsonAssetWithMap<String, IndexedLookupTableAssetMap<String, MyAsset>> {
public static final BuilderCodec<MyAsset> ABSTRACT_CODEC = BuilderCodec.builder(MyAsset.class, MyAsset::new) .append(new KeyedCodec<>("Name", Codec.STRING), (obj, val) -> obj.name = val, obj -> obj.name) .add() .append(new KeyedCodec<>("Value", Codec.INTEGER), (obj, val) -> obj.value = val, obj -> obj.value) .add() .build();
private String id; private String name; private int value;
@Override public String getId() { return id; } public String getName() { return name; } public int getValue() { return value; }}Asset JSON File
Section titled “Asset JSON File”{ "Id": "my_asset_id", "Name": "My Asset", "Value": 42}Codec System
Section titled “Codec System”BuilderCodec
Section titled “BuilderCodec”Type-safe serialization for structured data:
public static final BuilderCodec<MyData> CODEC = BuilderCodec.builder(MyData.class, MyData::new) .append(new KeyedCodec<>("Name", Codec.STRING), (obj, val) -> obj.name = val, obj -> obj.name) .add() .append(new KeyedCodec<>("Count", Codec.INTEGER), (obj, val) -> obj.count = val, obj -> obj.count) .add() .build();Built-in Codecs
Section titled “Built-in Codecs”| Codec | Type |
|---|---|
Codec.STRING | String |
Codec.BOOLEAN | boolean |
Codec.INTEGER | int |
Codec.LONG | long |
Codec.FLOAT | float |
Codec.DOUBLE | double |
Codec.UUID_STRING | UUID |
Collection Codecs
Section titled “Collection Codecs”// List codecCodec<List<String>> stringList = Codec.list(Codec.STRING);
// Map codecCodec<Map<String, Integer>> stringIntMap = Codec.map(Codec.STRING, Codec.INTEGER);
// Optional codecCodec<Optional<String>> optionalString = Codec.optional(Codec.STRING);Polymorphic Codecs
Section titled “Polymorphic Codecs”For types with multiple implementations:
import com.hypixel.hytale.assetstore.codec.AssetCodecMapCodec;
public abstract class Interaction { public static final AssetCodecMapCodec<String, Interaction> CODEC = new AssetCodecMapCodec<>(...);}
// Register implementationsInteraction.CODEC.register("Click", ClickInteraction.class, ClickInteraction.CODEC);Registration in plugin:
@Overrideprotected void setup() { getCodecRegistry(Interaction.CODEC) .register("MyInteraction", MyInteraction.class, MyInteraction.CODEC);}Asset Access
Section titled “Asset Access”Get Asset by Key
Section titled “Get Asset by Key”DefaultAssetMap<String, MyAsset> map = MyAsset.getAssetMap();MyAsset asset = map.getAsset("my_asset_id");
if (asset != null) { // Use asset}Iterate All Assets
Section titled “Iterate All Assets”for (String key : map.getKeys()) { MyAsset asset = map.getAsset(key); // Process asset}Asset Events
Section titled “Asset Events”Listen for asset loading/unloading:
@Overrideprotected void setup() { getEventRegistry().register( LoadedAssetsEvent.class, MyAsset.class, this::onAssetsLoaded );}
private void onAssetsLoaded(LoadedAssetsEvent<MyAsset> event) { for (MyAsset asset : event.getAssets()) { getLogger().at(Level.INFO).log("Loaded asset: " + asset.getId()); }}Asset Inheritance
Section titled “Asset Inheritance”Assets can inherit from parent assets:
{ "Id": "my_child_asset", "Parent": "my_parent_asset", "Name": "Overridden Name"}Plugin Registries
Section titled “Plugin Registries”The PluginBase class provides several registry accessors:
// Codec registry for polymorphic typesgetCodecRegistry(ParentCodec.CODEC).register("Type", MyClass.class, MyClass.CODEC);
// Entity store registrygetEntityStoreRegistry().registerComponent(MyComponent.class, "Name", MyComponent.CODEC);getEntityStoreRegistry().registerSystem(new MySystem());
// Command registrygetCommandRegistry().registerCommand(new MyCommand());
// Event registrygetEventRegistry().register(SomeEvent.class, this::onEvent);Best Practices
Section titled “Best Practices”- Define clear codecs - Type-safe serialization prevents errors
- Use load ordering - Declare dependencies with loadsAfter/loadsBefore
- Validate assets - Add validators to catch invalid data
- Handle events - React to asset loading/unloading
- Use inheritance - Reduce duplication with parent assets
- Cache asset references - Store frequently accessed assets