This guide describes the public API in the current source version 0.9.1. Use the dependency guidance below and check that the mod is loaded when integrating conditionally.
FFInjury — Modding API
Use this guide when integrating another mod with FFInjury (ffinjury).
| Minecraft | 1.20.1, 1.20.6, 1.21.1, 1.21.11, 26.1, 26.1.1, 26.1.2, 26.2, 26.3 |
| Loader | Fabric (all versions), Forge 47.1 (1.20.1), NeoForge (1.20.6, 1.21.1, 1.21.11, 26.1.2, 26.2) |
| Mod ID | ffinjury |
| API package | com.fyxe.ffinjury.api |
| Source version | 0.9.1 |
Dependency (optional), NeoForge:
[[dependencies.yourmod]]
modId="ffinjury"
type="optional"
versionRange="[0.6.0,)"
ordering="NONE"
side="BOTH"
On Fabric, list "ffinjury": "*" under suggests (or depends when required) in fabric.mod.json. The first Fabric build is 0.8.0, so use a version range starting there when you need it. Forge 1.20.1 (mods.toml) uses mandatory=false in place of type="optional"; builds for Forge, 1.20.x and 1.21.11 start at 0.9.0.
Use ModList.get().isLoaded("ffinjury") on NeoForge, FabricLoader.getInstance().isModLoaded("ffinjury") on Fabric, or FFUtilities' Mods.isLoaded, together with an optional compile-only dependency, before calling API classes. Each target is published as com.fyxe.ffinjury:ffinjury-<minecraft>-<loader>:<version> (for example ffinjury-1.21.1-fabric, ffinjury-1.21.11-neoforge or ffinjury-1.20.1-forge).
Core concepts
- Server-authoritative. Mutations only apply on the logical server (
ServerPlayer). Client query methods work; client mutations are no-ops. - Persisted on the player through a loader attachment (
ffinjury:injury_data), read withInjuryAttachments.get(player). NeoForge and Fabric (1.20.5 and newer) use their attachment types; 1.20.1 keeps the data in overworld saved data by player UUID. The client copy is filled from a sync packet (NeoForge 1.21 and newer syncs the attachment itself). - Cleared on death. Non-death clones keep state.
- Adrenaline suppresses injury potion effects and attribute modifiers while active.
- Treated injuries (bandaged) suppress their own effects/attributes until treatment expires. Increasing an injury's tier clears treated + heal timers.
- Injury types are dynamic — datapack JSON at
data/<ns>/ffinjury/injuries/and/orInjuryAPI.registerInjury. - Hurt level is the highest active injury tier. Rules live in
data/<ns>/ffinjury/hurt_levels/and are off unlesshurt_level.enabledis true. - Creative suppression. If
general.enableInjuriesInCreativeis false, creative/instabuild players: do not gain injuries, do not heal them, hide HUD/inventory icons, and skip injury/adrenaline effects. Query withInjuryAPI.isGameplaySuppressed(player).
Registering custom injuries
Prefer a datapack file so names, icons, effects, and damage weights exist on both sides:
data/mymod/ffinjury/injuries/fracture.json
Id becomes mymod:fracture. See README.md for the full JSON schema.
Code fallback (overridden by datapack JSON with the same id):
import com.fyxe.ffinjury.api.InjuryAPI;
InjuryAPI.registerInjury("mymod:fracture", "Bone Fracture", "minecraft:bone");
All injuries share general.defaultMaxTier. Per-injury max tiers are gone.
Queries
import com.fyxe.ffinjury.api.InjuryAPI;
import com.fyxe.ffinjury.injury.InjuryType;
import net.minecraft.world.entity.player.Player;
boolean any = InjuryAPI.hasAnyInjury(player);
int count = InjuryAPI.getInjuryCount(player);
int hurtLevel = InjuryAPI.getHurtLevel(player); // max tier, 0 if none
InjuryType type = InjuryAPI.getInjuryType("bleeding").orElse(null);
boolean has = InjuryAPI.hasInjury(player, type);
int tier = InjuryAPI.getTier(player, type);
boolean treated = InjuryAPI.isTreated(player, type);
boolean healing = InjuryAPI.isHealing(player, type);
boolean adrenaline = InjuryAPI.isAdrenalineActive(player);
long expireTick = InjuryAPI.getAdrenalineExpireTick(player);
Optional<ActiveInjury> ai = InjuryAPI.getActiveInjury(player, type);
List<ActiveInjury> list = InjuryAPI.getInjuries(player); // unmodifiable
Collection<InjuryType> allTypes = InjuryAPI.getRegisteredInjuryTypes();
boolean suppressed = InjuryAPI.isGameplaySuppressed(player);
boolean hurtOn = InjuryAPI.isHurtLevelEnabled();
double regenMul = InjuryAPI.getHurtLevelRegenMultiplier(player); // 1.0 if feature off
Config mirrors:
boolean creative = InjuryAPI.areInjuriesEnabledInCreative();
int maxActive = InjuryAPI.getMaxActiveInjuries();
int maxTier = InjuryAPI.getDefaultMaxTier();
Built-in convenience constants still exist: InjuryType.BLEEDING, InjuryType.BURN, etc.
Mutations (server only)
InjuryAPI.setInjuryTier(player, type, 2); // 0 removes
int newTier = InjuryAPI.addOrIncreaseInjury(player, type);
InjuryAPI.reduceInjury(player, type, 1);
int healed = InjuryAPI.healTiers(player, 2);
InjuryAPI.treatInjury(player, type, 60); // treat 60s + start heal timer
InjuryAPI.clearAllInjuries(player);
InjuryAPI.grantAdrenaline(player, 20 * 15);
All mutations refresh potion effects, attribute modifiers, sync the client HUD, and fire the relevant API events.
Raising a tier via setInjuryTier / addOrIncreaseInjury clears that injury's treated and heal timers.
Events
Listen on every loader with InjuryApiEvents.register(EventClass.class, listener). Listeners run on the server thread, in registration order. On NeoForge the same events are also posted on NeoForge.EVENT_BUS (they extend NeoForge's Event), so @SubscribeEvent listeners keep working. Fabric has no separate bus, so use InjuryApiEvents there. Every event extends InjuryEvent; the cancellable ones implement Cancellable (NeoForge's ICancellableEvent on NeoForge).
| Event | Cancellable | When |
|---|---|---|
InjurySelectEvent | Yes | After damage-table selection, before apply. Can change type. |
InjuryAppliedEvent | No | After an injury is added or tier increased. |
InjuryHealedEvent | No | When tiers are reduced (sleep, apple, bandage heal, API, tick heal). |
InjuryTreatedEvent | No | When a bandage or InjuryAPI.treatInjury starts treatment. getTreatmentItem() may be empty for API calls. |
AdrenalineGrantedEvent | Yes | Before adrenaline is applied; can adjust duration. |
Package: com.fyxe.ffinjury.api.event.
Example (any loader):
InjuryApiEvents.register(InjurySelectEvent.class, e -> {
if (e.getInjuryType().getId().equals("bleeding")) {
e.setCanceled(true); // block bleeding from this hit
}
});
The same listener written for NeoForge only:
@SubscribeEvent
public void onSelect(InjurySelectEvent e) {
if (e.getInjuryType().getId().equals("bleeding")) {
e.setCanceled(true);
}
}
Injury types
Resolve by id: InjuryType.fromId("bleeding") or InjuryType.of("bleeding") (fallback handle).
| Default ID | Display |
|---|---|
twisted_ankle | Twisted Ankle |
ankle_fracture | Ankle Fracture |
broken_leg | Broken Leg |
bruised_ribs | Bruised Ribs |
broken_ribs | Broken Ribs |
concussion | Concussion |
bleeding | Bleeding |
burn | Burn |
InjuryType live-resolves display name, description, icon, effects, and attributes from the current datapack definition.
ActiveInjury
ActiveInjury ai = ...;
InjuryType type = ai.getType();
int tier = ai.getTier();
boolean treated = ai.isTreated(currentGameTick);
boolean healing = ai.isHealing();
long treatedUntil = ai.getTreatedUntilTick();
long healAt = ai.getHealAtTick();
Do not call setTier / timer setters from other mods unless you also refresh status and sync. Prefer InjuryAPI.
Hurt level (datapack)
Path: data/<namespace>/ffinjury/hurt_levels/<id>.json
{
"min_hurt_level": 2,
"stop_regen": false,
"regen_multiplier": 0.5,
"effects": [
{ "effect": "minecraft:slowness", "amplifier": 0 }
],
"attributes": [
{ "attribute": "minecraft:generic.movement_speed", "amount": -0.05, "operation": "add_multiplied_total" }
]
}
Matching rules (hurt >= min_hurt_level) combine: lowest regen multiplier wins, any stop_regen stops regen, effects/attributes stack. Feature toggle: hurt_level.enabled (default false).
Attribute modifiers
- Per-injury modifiers live on the injury JSON (
attributesarray). - Hurt-level modifiers live on hurt-level JSON (
attributesarray), not in server config.
Operations: add_value, add_multiplied_base, add_multiplied_total.
Applied by InjuryStatusApplier and suppressed by adrenaline / treatment / creative gameplay suppression.
Notes for integrators
- Prefer
InjuryAPIover direct attachment access so status (effects + attributes) stays consistent. - Check
isGameplaySuppressedbefore applying your own injury-related effects in creative. - Damage chance is config (
damage_sources.chances); weighted injury picks come from JSONdamage_weights. Intercept selection withInjurySelectEvent. - Custom injuries defined only on the server datapack may show fallback icons/names on clients that lack the same JSON.
- Bandages refuse to start their use animation when they cannot apply (no injuries, or all treated for non-ethereal).