Repository navigation
API Extensible Accessors
The Animation Accessor system is fully extensible.
Third-party mods may register custom animation accessor types, allowing them to define new animation structures, properties, and registration logic that can be loaded directly from animation JSON.
An accessor definition combines two independently extensible components:
- An animation accessor, which defines how the animation itself is constructed.
- An animation properties object, which defines additional properties applied to the resulting animation.
ExtendedDatapacksRegistry
│
▼
IAnimationAccessorType
│
├───────────────┐
▼ ▼
definitionCodec() accessorCodec()
│ │
│ ▼
│ IAnimationAccessor<T>
│ │
│ ▼
│ register(...)
│ │
│ ▼
│ DynamicAnimation
│
└──────► propertyCodec()
│
▼
IAnimationProperties<T>
│
▼
applyTo(...)
An accessor type therefore determines how its complete animation definition is decoded and registered.
An animation accessor implements:
IAnimationAccessor<T>where T is the DynamicAnimation type produced by the accessor.
The accessor is responsible for:
- Defining its serialized fields.
- Identifying its accessor type.
- Creating and registering the corresponding Epic Fight animation.
- Applying the decoded animation properties.
public record AttackAnimationAccessor(
float transitionTime,
String animationPath,
IArmatureType armatureType,
AnimationPhase... phases
) implements IAnimationAccessor<AttackAnimation> {
public static final MapCodec<AttackAnimationAccessor> CODEC =
RecordCodecBuilder.mapCodec(instance ->
instance.group(
Codec.FLOAT.fieldOf("transition_time")
.forGetter(AttackAnimationAccessor::transitionTime),
Codec.STRING.fieldOf("animation")
.forGetter(AttackAnimationAccessor::animationPath),
ArmatureTypeRegistry.CODEC.fieldOf("armature")
.forGetter(AttackAnimationAccessor::armatureType),
AnimationPhase.CODEC.codec()
.listOf()
.fieldOf("phases")
.forGetter(r -> Arrays.asList(r.phases()))
).apply(instance, (transition, path, armature, phases) ->
new AttackAnimationAccessor(
transition,
path,
armature,
phases.toArray(AnimationPhase[]::new)
)
)
);
@Override
public IAnimationAccessorType accessorType() {
return CombatAnimationAccessors.ATTACK;
}
@Override
public AnimationManager.AnimationAccessor<AttackAnimation> register(
AnimationManager.AnimationBuilder builder,
IAnimationProperties<AttackAnimation> property) {
return builder.nextAccessor(animationPath, accessor -> {
var animation = new AttackAnimation(
transitionTime,
accessor,
armatureType.armature(),
Arrays.stream(phases)
.map(phase ->
phase.parseToEpicFightPhases(armatureType))
.toArray(AttackAnimation.Phase[]::new)
);
property.applyTo(animation);
this.isSuccessful(accessor);
return animation;
});
}
}The register() method receives the decoded animation properties and is responsible for constructing the actual Epic Fight animation.
The second part of an accessor definition is its property object.
Properties implement:
IAnimationProperties<T>where T corresponds to the animation produced by the accessor.
For example, an attack animation can have properties such as:
{
"basis_attack_speed": 0.70,
"cancellable_movement": false
}The properties object is decoded independently from the accessor itself and is subsequently passed to:
property.applyTo(animation);This separation allows the same accessor architecture to support different property sets for different animation types.
Every accessor type uses:
AnimationAccessorDefinitionCodec<T>to associate its accessor codec with its property codec.
public record AnimationAccessorDefinitionCodec<T extends DynamicAnimation>(
MapCodec<? extends IAnimationAccessor<T>> accessorCodec,
MapCodec<? extends IAnimationProperties<T>> propertyCodec) {
@SuppressWarnings({"unchecked", "RedundantCast"})
public MapCodec<AnimationAccessorDefinition<T>> combined() {
MapCodec<IAnimationAccessor<T>> castedAccessor =
(MapCodec<IAnimationAccessor<T>>) (MapCodec<?>) accessorCodec;
MapCodec<IAnimationProperties<T>> castedProperty =
(MapCodec<IAnimationProperties<T>>) (MapCodec<?>) propertyCodec;
return RecordCodecBuilder.mapCodec(instance ->
instance.group(
castedAccessor.forGetter(
AnimationAccessorDefinition::accessor),
castedProperty.forGetter(
AnimationAccessorDefinition::properties)
).apply(instance, AnimationAccessorDefinition::new)
);
}
}Conceptually, this produces a definition containing:
AnimationAccessorDefinition<T>
│
├── accessor
│ └── IAnimationAccessor<T>
│
└── properties
└── IAnimationProperties<T>
This allows the animation accessor and its properties to be decoded as one complete animation definition.
Accessor types implement:
IAnimationAccessorTypeAn accessor type provides:
- Its unique identifier.
- The codec describing its complete animation definition.
public enum CombatAnimationAccessors
implements IAnimationAccessorType {
ATTACK(
"epicfight_edp:attack",
new AnimationAccessorDefinitionCodec<>(
AttackAnimationAccessor.CODEC,
AttackAnimationProperties.CODEC
)
),
COMBO_ATTACK(
"epicfight_edp:combo_attack",
new AnimationAccessorDefinitionCodec<>(
ComboAttackAccessor.CODEC,
ComboAttackAnimationProperties.CODEC
)
),
DASH_ATTACK(
"epicfight_edp:dash_attack",
new AnimationAccessorDefinitionCodec<>(
DashAttackAnimationAccessor.CODEC,
DashAttackAnimationProperties.CODEC
)
),
AIR_ATTACK(
"epicfight_edp:air_attack",
new AnimationAccessorDefinitionCodec<>(
AirAttackAnimationAccessor.CODEC,
AirAttackAnimationProperties.CODEC
)
),
AIM(
"epicfight_edp:aim",
new AnimationAccessorDefinitionCodec<>(
AimAnimationAccessor.CODEC,
AimAnimationProperties.CODEC
)
);
private final String definitionId;
private final AnimationAccessorDefinitionCodec<?> definitionCodec;
CombatAnimationAccessors(
String definitionId,
AnimationAccessorDefinitionCodec<?> codec) {
this.definitionId = definitionId;
this.definitionCodec = codec;
}
@Override
public String id() {
return definitionId;
}
@Override
public AnimationAccessorDefinitionCodec<?> definitionCodec() {
return definitionCodec;
}
}Unlike animation events and playback modifiers, accessor identifiers are explicitly defined by the accessor type through:
String id()This means the identifier does not have to be derived from the enum constant name.
For example:
epicfight_edp:attack
epicfight_edp:combo_attack
epicfight_edp:dash_attack
Accessor types are registered through the centralized:
ExtendedDatapacksRegistryusing:
ANIMATION_ACCESSORExtendedDatapacksRegistry.ANIMATION_ACCESSOR.register(
MOD_ID,
CombatAnimationAccessors.class
);Multiple enum classes can be registered in a single call:
ExtendedDatapacksRegistry.ANIMATION_ACCESSOR.register(
MOD_ID,
CombatAnimationAccessors.class,
HitAnimationAccessors.class,
InteractionAnimationAccessors.class,
LivingAnimationAccessors.class
);The registry automatically verifies that every supplied enum implements:
IAnimationAccessorTypeInvalid classes are rejected and a warning is emitted to the log.
Once an accessor type has been registered, it can be used directly inside animation JSON.
For example:
{
"mod_id": "efdg",
"type": "combo_attack",
"transition_time": 0.25,
"animation": "biped/combat/greatsword_dual_auto_1",
"armature": "epicfight:entity/biped",
"basis_attack_speed": 0.70,
"cancellable_movement": false,
"phases": [
{
"antic": 0.0,
"pre_delay": 0.2,
"contact": 0.4,
"recovery": 0.45,
"hand": "off_hand",
"properties": {
"damage": 0.85
},
"colliders": [
{
"joint": "Tool_L"
}
]
},
{
"antic": 0.45,
"pre_delay": 0.5,
"contact": 0.7,
"recovery": 1.4,
"hand": "main_hand",
"properties": {
"damage": 0.85
},
"colliders": [
{
"joint": "Tool_R"
}
]
}
]
}The value:
"type": "combo_attack"identifies the registered accessor type.
The corresponding accessor and property codecs then decode the remaining fields.
An accessor definition is conceptually decoded as:
JSON
│
▼
"type"
│
▼
IAnimationAccessorType
│
▼
AnimationAccessorDefinitionCodec
│
├── accessorCodec
│ │
│ ▼
│ IAnimationAccessor<T>
│
└── propertyCodec
│
▼
IAnimationProperties<T>
The resulting definition contains both components:
AnimationAccessorDefinition<T>
│
├── accessor
│
└── properties
The accessor can then construct the animation and apply the decoded properties to it.
Animation accessors must be registered before Extended Datapacks initializes and consumes its animation registries.
Attempting to register an accessor after the registry has been consumed will throw:
RegistryObjectException
This restriction exists because the animation definition codecs resolve the available accessor types during initialization.
Registration should therefore happen during mod initialization:
private static void registerAnimationAccessors() {
ExtendedDatapacksRegistry.ANIMATION_ACCESSOR.register(
MOD_ID,
CombatAnimationAccessors.class
);
}A third-party mod can therefore introduce an entirely new animation type without modifying the existing Extended Datapacks accessor implementations.
The basic structure is:
YourAccessor
│
├── CODEC
│
└── register(...)
│
▼
YourAnimation
YourProperties
│
├── CODEC
│
└── applyTo(...)
│
▼
YourAnimation
│
▼
YourAccessorType
│
▼
ExtendedDatapacksRegistry
This makes the accessor system an extension point rather than a fixed collection of predefined animation types.
- Custom animation accessors should implement
IAnimationAccessor<T>. - Custom animation properties should implement
IAnimationProperties<T>. - Custom accessor types must implement
IAnimationAccessorType. -
AnimationAccessorDefinitionCodeccombines the accessor and propertyMapCodecs into a single definition codec. - Accessor identifiers are explicitly supplied through
IAnimationAccessorType.id(). - Register accessor enum classes through
ExtendedDatapacksRegistry.ANIMATION_ACCESSOR. - Multiple accessor enum classes can be registered in a single call.
- The centralized
ExtendedDatapacksRegistryis the recommended registration API. - Registration must occur before Extended Datapacks initializes and consumes its animation registries.
- The accessor's
register()method is responsible for constructing the corresponding Epic Fight animation. - Animation properties are applied after the animation is constructed through
IAnimationProperties.applyTo(...). - Each accessor type defines its own JSON structure through its
AnimationAccessorDefinitionCodec.
EF: Extended Datapacks
📦 Data
🧰 Core
👁️ Visuals
- Zoom Types
- Weapon Models per Style
- Advanced Swing Trails
- Weapons Passive Particles
- Shader Effects I
- Shader Effects II
- Shader Effects III
⚔️ Combat
🗝️ Skill Builder
🛠️ Innate Builder
- Simple Innate Skills
- Holdable Innate Skills
- Conditional Innate Skills
- Conditional Data Innate Skills
- Conditional Holdable Innate Skills
- Sequential Innate Skills
- Per Combo Innate Skills
- Combo Innate Skills
🗡️ Animation Operations
- Animation Operations
- Animation Properties
⚙️ Third-Party