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, andExileRegistry<T>. - [ ]
ExileRegistryTypedeclared with correct mod id, order, serializer, and sync time. - [ ]
Database.addRegistry(...)called inregisterDatabases(). - [ ] An empty/fallback entry registered whose GUID matches the
emptyDefaultargument. - [ ] If JSON-driven: JSON files placed under the correct datapack folder.
- [ ] If
ON_LOGIN: the client-sideClientSyncRegistrationcan handle deserialization (this is automatic forIAutoGsontypes).