Codecs
Codecs are Hytale’s serialization layer for reading and writing JSON (and BSON). They’re used everywhere: configuration files, asset definitions, entity persistence, and network packets.
Built-in Codecs
Section titled “Built-in Codecs”Primitives
Section titled “Primitives”| Codec | Java Type |
|---|---|
Codec.STRING | String |
Codec.BOOLEAN | boolean |
Codec.BYTE | byte |
Codec.SHORT | short |
Codec.INTEGER | int |
Codec.LONG | long |
Codec.FLOAT | float |
Codec.DOUBLE | double |
Arrays
Section titled “Arrays”| Codec | Java Type |
|---|---|
Codec.STRING_ARRAY | String[] |
Codec.INT_ARRAY | int[] |
Codec.LONG_ARRAY | long[] |
Codec.FLOAT_ARRAY | float[] |
Codec.DOUBLE_ARRAY | double[] |
Codec.BYTE_ARRAY | byte[] |
Transformed Types
Section titled “Transformed Types”These wrap primitive codecs with conversion functions:
| Codec | JSON Type | Java Type |
|---|---|---|
Codec.PATH | string | java.nio.file.Path |
Codec.UUID_STRING | string | UUID |
Codec.UUID_BINARY | binary | UUID |
Codec.INSTANT | string | java.time.Instant |
Codec.DURATION | string | java.time.Duration |
Codec.DURATION_SECONDS | number | Duration (from seconds) |
Codec.LOG_LEVEL | string | java.util.logging.Level |
KeyedCodec
Section titled “KeyedCodec”KeyedCodec associates a codec with a JSON field name:
new KeyedCodec<>("MaxPlayers", Codec.INTEGER) // "MaxPlayers": 100new KeyedCodec<>("Name", Codec.STRING) // "Name": "Alice"Convention: JSON keys start with uppercase (MaxPlayers, not maxPlayers).
KeyedCodec is used with BuilderCodec to define class fields.
BuilderCodec
Section titled “BuilderCodec”BuilderCodec creates codecs for Java classes using a fluent builder pattern. See Configuration for practical usage with mod configs.
public static final BuilderCodec<MyData> CODEC = BuilderCodec.builder( MyData.class, MyData::new ) .append(new KeyedCodec<>("Name", Codec.STRING), (data, val) -> data.name = val, // setter data -> data.name) // getter .add() .append(new KeyedCodec<>("Count", Codec.INTEGER), (data, val) -> data.count = val, data -> data.count) .add() .build();Validation
Section titled “Validation”Add .addValidator() between .append() and .add() to validate field values. See Configuration - Validation for common validators.
Inheritance
Section titled “Inheritance”For class hierarchies, pass the parent codec:
// base classpublic static final BuilderCodec<Animal> CODEC = BuilderCodec.abstractBuilder(Animal.class) .append(new KeyedCodec<>("Name", Codec.STRING), ...) .build();
// subclass inherits parent fieldspublic static final BuilderCodec<Dog> CODEC = BuilderCodec.builder( Dog.class, Dog::new, Animal.CODEC) .append(new KeyedCodec<>("Breed", Codec.STRING), ...) .build();Custom Codecs
Section titled “Custom Codecs”FunctionCodec
Section titled “FunctionCodec”Wrap an existing codec with conversion functions:
// string codec that converts to/from PathCodec<Path> PATH_CODEC = new FunctionCodec<>( Codec.STRING, str -> Paths.get(str), // string to Path path -> path.toString() // path to string);This is how Codec.PATH, Codec.UUID_STRING, and Codec.INSTANT are implemented.
EnumCodec
Section titled “EnumCodec”For enum types:
public enum Priority { LOW, NORMAL, HIGH }
public static final Codec<Priority> CODEC = new EnumCodec<>(Priority.class);JSON uses the enum name: "Priority": "HIGH"
ArrayCodec
Section titled “ArrayCodec”For arrays of custom types:
// array of custom objectsCodec<MyItem[]> ITEMS_CODEC = new ArrayCodec<>( MyItem.CODEC, MyItem[]::new);MapCodec
Section titled “MapCodec”For Map<String, V> types:
Codec<Map<String, Integer>> SCORES_CODEC = new MapCodec<>( Codec.INTEGER, HashMap::new);JSON:
{ "Scores": { "Alice": 100, "Bob": 85 }}Polymorphic Types
Section titled “Polymorphic Types”CodecMapCodec handles polymorphic JSON where a "Type" field determines which concrete class to use.
Defining the Base Type
Section titled “Defining the Base Type”public abstract class Reward { // polymorphic codec - "Type" field selects concrete class public static final CodecMapCodec<Reward> CODEC = new CodecMapCodec<>("Type");
// shared fields in abstract builder public static final BuilderCodec<Reward> ABSTRACT_CODEC = BuilderCodec.abstractBuilder(Reward.class) .append(new KeyedCodec<>("Description", Codec.STRING), ...) .build();}Defining Concrete Types
Section titled “Defining Concrete Types”public class ItemReward extends Reward { public static final BuilderCodec<ItemReward> CODEC = BuilderCodec.builder( ItemReward.class, ItemReward::new, Reward.ABSTRACT_CODEC) .append(new KeyedCodec<>("ItemId", Codec.STRING), ...) .append(new KeyedCodec<>("Count", Codec.INTEGER), ...) .build();}
public class XpReward extends Reward { public static final BuilderCodec<XpReward> CODEC = BuilderCodec.builder( XpReward.class, XpReward::new, Reward.ABSTRACT_CODEC) .append(new KeyedCodec<>("Amount", Codec.INTEGER), ...) .build();}Registering Types
Section titled “Registering Types”Register concrete types during setup():
@Overrideprotected void setup() { // direct registration on the codec Reward.CODEC.register("Item", ItemReward.class, ItemReward.CODEC); Reward.CODEC.register("Xp", XpReward.class, XpReward.CODEC);
// or via registry (auto-cleanup on mod shutdown) this.getCodecRegistry(Reward.CODEC) .register(Priority.NORMAL, "Item", ItemReward.class, ItemReward.CODEC);}JSON Usage
Section titled “JSON Usage”Once registered, JSON can use your types:
{ "Type": "Item", "Description": "Starter sword", "ItemId": "iron_sword", "Count": 1}{ "Type": "Xp", "Description": "Quest completion bonus", "Amount": 500}Extending Built-in Types
Section titled “Extending Built-in Types”You can add new subtypes to existing Hytale systems:
// add custom interaction typethis.getCodecRegistry(Interaction.CODEC) .register(Priority.NORMAL, "MyCustomEffect", MyCustomEffect.class, MyCustomEffect.CODEC);Now your types can be used in JSON asset files alongside built-in types.