Architecture Overview
This page explains how the mod is structured internally so you can navigate the codebase.
Project Structure
src/main/java/com/iafenvoy/origins/
├── data/
│ ├── action/ # Entity, Block, Item, Bi-entity action types
│ ├── badge/ # Badge system
│ ├── condition/ # Entity, Block, Item, Bi-entity, Damage, Biome, Fluid conditions
│ ├── layer/ # Layer registry and data
│ ├── origin/ # Origin registry and data
│ └── power/ # Power registry, base classes, built-in powers
│ ├── builtin/
│ │ ├── action/ # Action-triggered powers
│ │ ├── modify/ # Stat/damage modifier powers
│ │ ├── prevent/ # Blocking powers
│ │ └── regular/ # Standalone powers
│ └── component/ # Reusable power components (active, cooldown, toggle, resource, etc.)
├── registry/ # Block, item, entity, recipe registrations
├── attachment/ # Player data storage (OriginDataHolder)
├── command/ # In-game commands
├── config/ # Mod configuration
├── network/ # Network packet handling
└── render/ # Client-side rendering
Registry System
Origins uses Vanilla's DataPack Registry system. There are two kinds:
| Kind | Examples | Loaded from |
|---|---|---|
| Built-in (codec registries) | Action types, condition types, power types | DeferredRegister in Java code |
| Dynamic (data pack registries) | Origins, layers, powers, badges | data/<ns>/origins/<type>/ JSON files |
Dynamic registries are declared in OriginsRegistries.newDatapackRegistries(). Each has a ResourceKey:
origins:origin→OriginRegistries.ORIGIN_KEYorigins:layer→LayerRegistries.LAYER_KEYorigins:power→PowerRegistries.POWER_KEYorigins:badge→BadgeRegistries.BADGE_KEY
Power Lifecycle
When a power is granted to a player:
Power.grant(holder)is called. Granting is batched: for a whole origin (or aMultiplePowerplus its children) the power is registered first and only ticked once the entire grant batch completes.Power.tick(holder)is then called (once per batch). If theconditionfield is satisfied, the power becomes active viaPower.active(holder).- On specific intervals defined by
Power.tickInterval()(default is 1),Power.tick(holder)keeps running. - If the condition becomes false,
Power.inactive(holder)fires. - When the power is revoked,
Power.revoke(holder)fires.
Because activation is deferred until the end of the grant batch, a power may safely reference another power in the same origin (e.g. a resource power) even if that power is declared later in the powers list. All powers in the batch are registered before any of them is ticked.
Components (ActiveComponent, CooldownComponent, ToggleComponent, ResourceComponent, InventoryComponent, EntitySetComponent) are attached to each power instance and persist runtime state.
Adding Custom Types
To add a new power type:
- Extend
com.iafenvoy.origins.data.power.Power. - Provide a
MapCodecviacodec(). - Register it through
DeferredRegisteronPowerRegistries.POWER_TYPE.
To add a new condition or action:
- Implement the appropriate interface (
EntityCondition,BlockCondition,EntityAction, etc.). - Provide a
MapCodec. - Register through
ConditionRegistriesorActionRegistries.
Dependencies explanation
- Jupiter: Config library.
- Additional Entity Attributes (JIJ): For more entity attributes (e.g. lava vision), which replaced some power types.
- Caelus (JIJ): For attributs and better capability handling on fall flying related powers.
- Integration (JIJ): Provide better compatibility and entrypoints with other mods.
Differences from the Fabric Version
This NeoForge port consolidates what used to be separate libraries (Apoli, Calio) into a single mod. The namespace changed from apoli/calio to origins. The data path structure uses NeoForge's DataPack Registry system instead of Fabric's custom loading.
Class names and type IDs mostly match the Fabric version, but always check the source rather than assuming they're identical.
For how to port from Fabric to NeoForge, see this guide.