This guide describes the public API in the current source version 0.2.2. Use the dependency guidance below and check that the mod is loaded when integrating conditionally.
FF Nutrition — Modding API
This document describes the public API other mods (and agents) can use to interact with FF Nutrition on Minecraft 1.21.1 / NeoForge.
Primary entry point: com.fyxeinc.ffnutrition.api.FFNutritionAPI
Events live under com.fyxeinc.ffnutrition.api.event.
Dependency
Optional (recommended):
[[dependencies.yourmod]]
modId="ffnutrition"
type="optional"
versionRange="[0.2,)"
ordering="NONE"
side="BOTH"
Always prefer the API over writing the nutrition_data attachment or attribute base value directly. The API keeps events, the max-health modifier, and the Metabolic Gauge client sync consistent.
Max-health compatibility
FF Nutrition no longer calls AttributeInstance#setBaseValue on Attributes.MAX_HEALTH.
It applies a permanent AttributeModifier with a stable id:
ffnutrition:nutrition_max_health
Exposed as FFNutritionMod.NUTRITION_MAX_HEALTH_MODIFIER_ID and FFNutritionAPI.getMaxHealthModifierId().
Behaviour:
- Remove any previous instance of our modifier.
- Measure the attribute value without us.
- Add an
ADD_VALUEmodifier so the final value equals the nutrition target (same absolute result as the old base-value overwrite). - Clamp current health downward if it exceeds the new max.
Other mods can:
- Detect our contribution via
AttributeInstance#getModifier(FFNutritionAPI.getMaxHealthModifierId()). - Remove it with
AttributeInstance#removeModifier(...). - Adjust the target before application by listening to
ComputeMaxHealthEvent.
Queries (safe on both sides when the attachment exists)
| Method | Returns |
|---|---|
getNutritionData(LivingEntity) | Attachment instance (creates defaults if needed) |
getNutritionScore(Player) | Score in [0.0, 1.0] |
getCategoryValue(Player, String) | Category fill in [0.0, 100.0] |
getCategoryValues(Player) | Unmodifiable category map |
getCategories() | Ordered datapack category names |
hasCategory(String) | Whether the category is currently loaded |
getItemNutritionValues(Item) | Raw datapack vector (unscaled) |
hasItemNutrition(Item) | Whether a datapack entry exists |
getDefaultTargetMaxHealth(Player) | Built-in formula target (before events) |
getMaxHealthModifierId() | ResourceLocation of our permanent modifier |
getMaxHealthModifierAmount(Player) | Optional<Double> of current modifier amount |
Mutations (server only; no-ops on client)
All mutation helpers:
- Post a cancellable
NutritionChangeEvent. - Apply the change (with clamping).
- Refresh the max-health modifier (fires
ComputeMaxHealthEvent+MaxHealthAppliedEvent). - Sync Metabolic Gauge state to the owning client.
- Post
NutritionChangedEvent.
| Method | Purpose |
|---|---|
addToCategory(Player, String, double) | Add delta to one category |
addToCategory(..., Cause) | Same with explicit cause |
setCategory(Player, String, double) | Absolute set (clamped) |
setCategory(..., Cause) | Same with explicit cause |
setAllCategories(Player, double) | Set every category to the same value (clamped) |
setAllCategories(..., Cause) | Same with explicit cause |
addNutritionVector(Player, double[], Cause) | Ordered vector add (food-style) |
applyFoodNutrition(ServerPlayer, Item, double[], usedFallback, fromPlacedBlock) | Food path + FoodNutritionAppliedEvent |
applyMaxHealth(Player) | Force modifier refresh only |
Cause values: FOOD, DECAY, COMMAND, API, DEATH_CLONE, OTHER.
Events (NeoForge bus, server-side)
| Event | Cancellable | When |
|---|---|---|
NutritionChangeEvent | Yes | Before a category mutation |
NutritionChangedEvent | No | After a successful mutation |
ComputeMaxHealthEvent | No (adjust target instead) | Before the max-health modifier is written |
MaxHealthAppliedEvent | No | After the modifier is applied |
FoodNutritionAppliedEvent | No | After food (item or placed block) contributed values |
Example — override max health from another mod
@SubscribeEvent
public static void onComputeMaxHealth(ComputeMaxHealthEvent event) {
// e.g. give a bonus heart while a custom effect is active
if (event.getPlayer().hasEffect(YOUR_EFFECT)) {
event.setTargetMaxHealth(event.getTargetMaxHealth() + 2.0);
}
}
Example — block nutrition gain from a specific food
@SubscribeEvent
public static void onNutritionChange(NutritionChangeEvent event) {
if (event.getCause() == NutritionChangeEvent.Cause.FOOD
&& event.getOperation() == NutritionChangeEvent.Operation.ADD_VECTOR) {
// cancel or mutate event.getVector()
event.setCanceled(true);
}
}
Example — react after score changes
@SubscribeEvent
public static void onNutritionChanged(NutritionChangedEvent event) {
if (event.getNewScore() >= 0.9) {
// grant achievement, particle, etc.
}
}
Notes for agents / integrators
- Category names and order come from the datapack (
nutrition_categories.json). Do not hard-code indices without also readinggetCategories(). - Item vectors are raw composition; the server scales them by vanilla food nutrition/saturation and the common-config multipliers before applying.
- Decay runs every server level tick and deliberately does not spam
NutritionChangeEventevery tick. Max-health events still fire when the applied max actually changes. setCategory/setAllCategoriesvia the API (and commands) now clamp correctly; the old command path that accidentally added instead of set has been fixed.- Prefer
FFNutritionAPIover direct attachment writes so client gauge sync and events stay correct.
Package layout
com.fyxeinc.ffnutrition.api
├── FFNutritionAPI.java
└── event
├── NutritionChangeEvent.java
├── NutritionChangedEvent.java
├── ComputeMaxHealthEvent.java
├── MaxHealthAppliedEvent.java
└── FoodNutritionAppliedEvent.java