This guide describes the public API in the current source version 0.7.5. Use the dependency guidance below and check that the mod is loaded when integrating conditionally.

FFExhaustion — Modding API

Guide for other mods (and AI agents) integrating with FFExhaustion (ffexhaustion).

Minecraft1.21.1
LoaderNeoForge 21.1.x
Mod IDffexhaustion
API packagecom.fyxe.ffexhaustion.api
Exhaustion range0.0 – 1.0 (higher = more fatigued)

Dependency (optional):

[[dependencies.yourmod]]
    modId="ffexhaustion"
    type="optional"
    versionRange="[0,)"
    ordering="NONE"
    side="BOTH"

Use ModList.get().isLoaded("ffexhaustion") or optional compile-only dependency before calling API classes.


Core concepts


Reading exhaustion

These player-based queries read the server-owned attachment. Use them on the logical server only. Client player attachments are not updated by HUD packets and can report zero or stale values. Recovery delay, recovery state, and status queries are also server-only.

import com.fyxe.ffexhaustion.api.ExhaustionAPI;
import net.minecraft.world.entity.player.Player;

float x = ExhaustionAPI.getExhaustion(player);       // 0.0 – 1.0
int delay = ExhaustionAPI.getRecoveryDelayTicks(player);
boolean recovering = ExhaustionAPI.isRecovering(player);
boolean full = ExhaustionAPI.isFull(player);
boolean empty = ExhaustionAPI.isEmpty(player);
boolean tired = ExhaustionAPI.isAtLeast(player, 0.75f);

Client HUD / client-only code

import com.fyxe.ffexhaustion.api.client.ClientExhaustionAPI;

float local = ClientExhaustionAPI.getExhaustion();
boolean tired = ClientExhaustionAPI.isAtLeast(0.9f);

This is a synced cache, not authoritative. It may lag continuous server changes by up to five ticks. Do not use it for gameplay rules.


Changing exhaustion (server)

All of these fire the cancellable pre-change event. When the value actually changes, they also fire the changed event and sync the client HUD immediately. Non-finite inputs are ignored; finite values outside 0 to 1 are clamped.

import com.fyxe.ffexhaustion.api.ExhaustionAPI;

// Absolute set
ExhaustionAPI.setExhaustion(player, 0.5f, true);   // value, triggerRecoveryDelay

// Relative change (level gain multiplier applied on increase)
ExhaustionAPI.increaseExhaustion(player, 0.05f, true);
ExhaustionAPI.decreaseExhaustion(player, 0.05f, false);

// No recovery delay
ExhaustionAPI.increaseExhaustionNoDelay(player, 0.02f);
ExhaustionAPI.decreaseExhaustionNoDelay(player, 0.02f);

// Exact amount, ignore level multiplier, no delay
ExhaustionAPI.increaseExhaustionRaw(player, 0.1f);

// Full reset
ExhaustionAPI.resetExhaustion(player);

// Delay timer only
ExhaustionAPI.setRecoveryDelayTicks(player, 40);
MethodLevel multiplierTypical use
increaseExhaustion(p, amt, delay)YesPlayer actions, combat costs
increaseExhaustion(p, amt, delay, raw)If raw == falseSame, optional raw
increaseExhaustionRaw(p, amt)NoScripted / exact values
decreaseExhaustion(...)N/AItems, rest abilities
setExhaustion(...)N/AAbsolute control
resetExhaustion(p)N/ADeath effects, commands

Events (NeoForge event bus)

Register on NeoForge.EVENT_BUS (common / server).

ExhaustionChangeEvent (cancellable, before apply)

import com.fyxe.ffexhaustion.api.ExhaustionChangeEvent;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.neoforge.common.NeoForge;

@SubscribeEvent
public void onExhaustionChange(ExhaustionChangeEvent event) {
    // Cancel entirely
    if (event.getPlayer().isCreative()) {
        event.setCanceled(true);
        return;
    }
    // Soften increases
    if (event.getCause() == ExhaustionChangeEvent.Cause.INCREASE) {
        float mid = (event.getOldValue() + event.getNewValue()) * 0.5f;
        event.setNewValue(mid);
    }
    event.setTriggerDelay(false);
}
Getter / setterMeaning
getPlayer()ServerPlayer
getOldValue() / getNewValue()Before / proposed after
setNewValue(float)Clamps finite overrides; ignores NaN and infinities
isTriggerDelay() / setTriggerDelay(boolean)Recovery delay after apply
getCause()SET, INCREASE, DECREASE, RECOVERY, SYSTEM, OTHER
getDelta()new - old

ExhaustionChangedEvent (after apply, not cancellable)

@SubscribeEvent
public void onExhaustionChanged(ExhaustionChangedEvent event) {
    float x = event.getNewValue();
    // e.g. grant advancement, play sound, update capability
}

Note: Continuous built-in drains (sprint per tick, shield hold, glider) may update the attachment without posting these events every tick (performance). Discrete API calls and most one-shot actions (jump, attack, Better Combat swing) do post them. On the server, re-read ExhaustionAPI.getExhaustion(player) if you need the live value each tick.


Level helpers

int level = ExhaustionAPI.getEffectiveLevel(player);  // capped by config
float gainMult = ExhaustionAPI.getGainMultiplier(player);  // 1.0 if disabled
int delayTicks = ExhaustionAPI.getEffectiveDelayTicks(player);

These respect ServerConfig level-modifier toggles. They use vanilla experienceLevel only (no custom level store).


Attachment (advanced)

import com.fyxe.ffexhaustion.data.ModAttachments;
import com.fyxe.ffexhaustion.data.ExhaustionData;

ExhaustionData data = player.getData(ModAttachments.EXHAUSTION);
float v = data.getValue();

Prefer ExhaustionAPI for writes so events and client sync stay consistent.


Configurable status systems (potions + attributes)

Optional server features (all disabled by default). Other mods can query state without reading config classes.

boolean potionsOn = ExhaustionAPI.arePotionEffectsEnabled();
boolean thresholdAttrs = ExhaustionAPI.areThresholdAttributesEnabled();
boolean scalingAttrs = ExhaustionAPI.areScalingAttributesEnabled();

List<ExhaustionStatusApplier.PotionEntry> potions = ExhaustionAPI.getPotionEffectEntries();
List<ExhaustionStatusApplier.ThresholdAttrEntry> steps = ExhaustionAPI.getThresholdAttributeEntries();
List<ExhaustionStatusApplier.ScalingAttrEntry> scales = ExhaustionAPI.getScalingAttributeEntries();

boolean anyPotion = ExhaustionAPI.hasActivePotionThreshold(player);
boolean anyStep = ExhaustionAPI.hasActiveThresholdAttribute(player);

double amount = ExhaustionAPI.getScalingAmount(player, scales.get(0));
double atHalf = ExhaustionAPI.getScalingAmountAt(scales.get(0), 0.5f);

Parsed entry records live in com.fyxe.ffexhaustion.effect.ExhaustionStatusApplier and are part of the supported surface for read access.

Config entry formats (server TOML)

potion_effects

attributes.threshold

attributes.scaling

These are independent of built-in sprint / attack-speed decay modifiers.


Soft compatibility patterns

FFExhaustion uses optional soft compat (reflection / soft mixins) for:

Other mods should not depend on those packages. Integrate through ExhaustionAPI instead.


Config surface (for documentation only)

Server configs live under ffexhaustion-server.toml (names illustrative):

Do not hard-code config class field names in foreign mods; treat them as user-facing, not API.


Minimal integration example

@EventBusSubscriber(modid = "yourmod")
public class YourExhaustionHooks {
    @SubscribeEvent
    public static void onSpellCast(YourSpellEvent event) {
        if (!(event.getEntity() instanceof ServerPlayer player)) return;
        if (!ModList.get().isLoaded("ffexhaustion")) return;

        if (ExhaustionAPI.isAtLeast(player, 0.9f)) {
            event.setCanceled(true);
            return;
        }
        ExhaustionAPI.increaseExhaustion(player, 0.08f, true);
    }
}

Stability