Explanation: Architecture Overview
Library of Exile is a foundation mod. It does not add weapons, dungeons, or gameplay by itself. Instead it provides the infrastructure that other mods in the Mine and Slash ecosystem build on top of. Understanding its shape makes it easier to know where to look when adding features or debugging.
The registry layer
The most important abstraction is ExileRegistryType. Think of it as a typed channel — a named
slot in a global map. Each content category (mob affixes, relic types, mob lists, etc.) has one
ExileRegistryType instance. All entries of that category go into the corresponding
ExileRegistryContainer stored in Database.
This is separate from Forge's own registry system. Forge registries handle Item, Block,
EntityType, etc. — Minecraft's built-in object types. The ExileRegistryType system handles
game design data: the stat definitions, spawn weights, map generation rules, and similar
content that is meant to be datapack-editable and often needs to be synced to clients.
The two systems coexist: a RelicType entry (in the exile registry) holds the string id of a
Forge-registered Item, bridging the two worlds.
Content lifecycle
Content enters the system in one of two ways: hardcoded Java registration or JSON datapack
loading. In both cases the result ends up in the same ExileRegistryContainer.
Hardcoded entries are registered during FMLCommonSetupEvent when the library fires
ExileEvents.EXILE_REGISTRY_GATHER for each type. Mods listen to this event and call
e.add(entry, info) to register their entries. The ExileKeyHolder + ExileKey pattern
automates this wiring.
JSON entries are loaded by BaseDataPackLoader, which is registered as a Forge reload
listener. On every /reload and on world load, the loader reads all JSON files from the
correct datapack folder for each type and calls e.add(parsed_entry, info).
After both sources have contributed, Database.checkGuidValidity() validates all entries and
unregisterInvalidEntries() removes bad ones. Then each ExileRegistryContainer caches its
ON_LOGIN sync packet bytes.
The sync model
Clients need some registry data (relic stats, affix names, rarity info) to display things correctly. They do not need server-authoritative data like mob spawn lists or map generation rules.
The SyncTime enum controls this split at the type level:
- ON_LOGIN — the library automatically sends all JSON-originated entries to every connecting
client.
- NEVER — entries stay on the server.
This means the division between client and server knowledge is declared once, at type registration time, and is then fully automatic. Mods do not need to write packet handlers for registry data.
The event layer
Library of Exile exposes game hooks through ExileEvents. These are thin wrappers around
internal logic (mixins, Forge events) that give dependent mods a stable surface to attach to.
For example, the GRAB_MOB_AFFIXES event is fired by a tick listener (registered in
MobAffixEvents.init()). Any number of mods can register listeners on that single event to
contribute affixes for any entity. The library collects all responses and applies them.
This pattern — one library event, many mod listeners — keeps mods decoupled from each other. Two mods that both add affixes to Zombies never need to know about each other.
The mixin layer
Some hooks require bytecode injection into vanilla classes. The library handles all of these
in its own mixin package, exposing the results through ExileEvents or through the capability
system. Dependent mods should never need to write their own mixins for features the library
already covers.
Active mixin targets:
- LootTable — chest loot generation, block drop hooks.
- LivingEntity — damage pipeline hook.
- Mob — spawn finalization, for storing spawn reason.
- PlayerList — player login hook.
- ServerPlayer, Entity, ItemStack — various small hooks.
- Level, Commands — dimension and command hooks.
The capability system
Player, entity, chunk, and map data that must persist across sessions is stored using Forge
capabilities. Library of Exile defines:
- PlayerDataCapability / PlayerCapabilities — player-level persistent data.
- LibMapCap / LibChunkCap — map and chunk data.
- EntityInfoComponent / EntityDmgStatsData — per-entity stats and damage tracking.
Dependent mods can read these capabilities to share data. The capability instances are
registered in Capabilities.reg(), called from commonSetupEvent.
OrderedModConstructor as the integration contract
The primary integration point for a dependent mod is OrderedModConstructor. Subclassing it
and calling register() from your @Mod constructor guarantees that your Forge deferred
registers, your exile registry databases, and your content entries all initialize in the right
order relative to the library and to each other.
The six abstract methods form a checklist that covers every initialization phase:
1. Register Forge DeferredRegister instances with the event bus.
2. Register items/blocks into those registers.
3. Declare new ExileRegistryType containers (if any).
4. Register hardcoded entries into those containers.
5. Declare ExileRegistryEventClass handlers for batch content registration.
6. Declare ExileKeyHolder instances for individual named entries.