How-to: Register a New Content Type

Use this guide when you need a completely new category of registerable content — not just new entries within an existing type (mob affix, relic, etc.), but a whole new ExileRegistryType with its own database container, serialization, and optional client sync.


1. Create the content class

Your class must implement ExileRegistry<T> (or JsonExileRegistry<T> for JSON-driven content) and IAutoGson<T> for Gson serialization:

public class MyContent implements JsonExileRegistry<MyContent>, IAutoGson<MyContent> {

    // Declared before the ExileRegistryType so it can be used as the serializer prototype.
    public static final MyContent SERIALIZER = new MyContent();

    // All fields listed here are serialized to/from JSON automatically.
    public String id = "";
    public int weight = 1000;
    public String my_field = "default_value";

    // ExileRegistry contract:
    @Override
    public String GUID() { return id; }

    @Override
    public int Weight() { return weight; }

    @Override
    public ExileRegistryType getExileRegistryType() {
        return MyDatabase.MY_CONTENT;
    }

    // IAutoGson contract:
    @Override
    public Class<MyContent> getClassForSerialization() {
        return MyContent.class;
    }
}

2. Declare the ExileRegistryType

Create a database init class (analogous to LibDatabase):

public class MyDatabase {

    public static final ExileRegistryType MY_CONTENT = ExileRegistryType.register(
        "my_mod",          // your mod id
        "my_content",      // short name — full id becomes "my_mod_my_content"
        10,                // load order (lower = earlier; use 0-50 range)
        MyContent.SERIALIZER,  // prototype for JSON deserialization; null = no JSON support
        SyncTime.ON_LOGIN  // or SyncTime.NEVER for server-only
    );
}

Call this initializer early — static field initialization is fine if MyDatabase is referenced before FMLCommonSetupEvent.


3. Register a database container

In registerDatabases() in your OrderedModConstructor:

@Override
public void registerDatabases() {
    Database.addRegistry(
        new ExileRegistryContainer<>(MyDatabase.MY_CONTENT, "my_mod:empty")
    );
}

The second argument is the GUID of the fallback "empty" entry. You must also register an entry with that GUID — see step 4.


4. Register an empty/fallback entry

public class MyContentHolder extends ExileKeyHolder<MyContent> {

    public MyContentHolder(String modid) {
        super(modid, MyDatabase.MY_CONTENT);
    }

    // The fallback — must match the emptyDefault GUID used in Database.addRegistry().
    public ExileKey<MyContent, KeyInfo> EMPTY = ExileKey.ofId(
        this, "my_mod:empty",
        x -> {
            var c = new MyContent();
            c.id = x.GUID();
            return c;
        }
    );
}

5. (Optional) Support JSON datapacks

If you passed a non-null serializer to ExileRegistryType.register, place JSON files at:

data/<your_namespace>/my_mod_my_content/<name>.json
{
  "id": "my_mod:example",
  "weight": 500,
  "my_field": "hello"
}

The loader key is the full ExileRegistryType id: "my_mod_my_content".


6. (Optional) Register a custom Gson adapter

If your content has a polymorphic serializer field (like mob affixes), register a type adapter during Gson construction:

ExileEvents.DATAPACK_GSON_ADAPTER_REGISTRY.register(new EventConsumer<DatapackGsonAdapterEvent>() {
    @Override
    public void accept(DatapackGsonAdapterEvent e) {
        e.registerAdapter(new MyContentGsonAdapter());
    }
});

7. Access entries at runtime

// Get a specific entry:
MyContent entry = Database.get(MyDatabase.MY_CONTENT, "my_mod:example");

// Get the container for bulk access:
ExileRegistryContainer<MyContent> container = Database.getRegistry(MyDatabase.MY_CONTENT);
List<MyContent> all = container.getList();
MyContent random = container.random();

Checklist

  • [ ] Content class implements IGUID, IWeighted, and ExileRegistry<T>.
  • [ ] ExileRegistryType declared with correct mod id, order, serializer, and sync time.
  • [ ] Database.addRegistry(...) called in registerDatabases().
  • [ ] An empty/fallback entry registered whose GUID matches the emptyDefault argument.
  • [ ] If JSON-driven: JSON files placed under the correct datapack folder.
  • [ ] If ON_LOGIN: the client-side ClientSyncRegistration can handle deserialization (this is automatic for IAutoGson types).