This guide describes the public API in the current source version 0.6.4. Use the dependency guidance below and check that the mod is loaded when integrating conditionally.
FFItemWeight — Modding API
Guide for other mods (and AI agents) integrating with FFItemWeight (ffitemweight).
| Minecraft | 1.21.1 |
| Loader | NeoForge 21.1.x |
| Mod ID | ffitemweight |
| API package | com.fyxe.ffitemweight.api |
| Client helpers | com.fyxe.ffitemweight.api.client |
Optional dependency:
[[dependencies.yourmod]]
modId="ffitemweight"
type="optional"
versionRange="[0,)"
ordering="NONE"
side="BOTH"
Use ModList.get().isLoaded("ffitemweight") or a compile-only / optional dependency before calling API classes.
Core concepts
- Unit weight — mass of one item (no stack count). Assigned from datapack JSON, recipe derivation, or
defaultWeight. - Stack weight —
unit * count, optionally plus nested inventory contents (shulkers, bundles, backpacks). - Player total — sum of configured slots (main inventory, armour, offhand, Accessories, Curios).
- Encumbrance — ordered datapack bands compared by integer
level(default 0 Unencumbered through 5 Immobile). Display names are labels only. - Capacity — raises (or lowers) band capacities:
threshold' = capacity * levelMultiplier + flatBonus. - Server-authoritative weights — table computed on server start / datapack reload, synced as a slim non-default map to clients.
- Display — tooltips, slot highlights, HUD are client-only.
Dependency check
import net.neoforged.fml.ModList;
if (!ModList.get().isLoaded("ffitemweight")) {
return;
}
Reading item weights
import com.fyxe.ffitemweight.api.ItemWeightAPI;
import com.fyxe.ffitemweight.api.WeightCategory;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
double unit = ItemWeightAPI.getUnitWeight(item);
double stack = ItemWeightAPI.getWeight(stack); // includes nested if enabled
WeightCategory cat = ItemWeightAPI.getItemWeightCategory(stack);
String name = cat.name();
int rgb = cat.rgb();
double bandMax = cat.maxWeight();
List<WeightCategory> allItemBands = ItemWeightAPI.getItemWeightCategories();
| Method | Notes |
|---|---|
getUnitWeight(Item) | One item; default weight if unknown |
getWeight(ItemStack) | Count + nested contents |
getItemWeightCategory(double/stack) | Band from client weightCategories |
getItemWeightCategories() | Ordered immutable list |
Empty stacks return 0.0. Null item uses the configured default unit weight.
Player totals and encumbrance
import net.minecraft.world.entity.player.Player;
import com.fyxe.ffitemweight.api.EncumbranceLevel;
boolean tracking = ItemWeightAPI.isPlayerWeightTrackingEnabled();
double total = ItemWeightAPI.getPlayerTotalWeight(player);
EncumbranceLevel enc = ItemWeightAPI.getPlayerEncumbrance(player);
int level = ItemWeightAPI.getPlayerEncumbranceLevel(player);
boolean heavyOrWorse = ItemWeightAPI.isPlayerEncumbranceAtLeast(player, 3);
boolean pastMedium = ItemWeightAPI.isPlayerOverLevel(player, 2);
EncumbranceLevel band = ItemWeightAPI.getEncumbranceCategory(total, player);
List<EncumbranceLevel> loads = ItemWeightAPI.getEncumbranceLevels();
| Method | Notes |
|---|---|
getPlayerTotalWeight(Player) | Cached ~5 ticks; respects creative / slot toggles |
getPlayerEncumbrance(Player) | Current EncumbranceLevel after capacity scaling |
getPlayerEncumbranceLevel(Player) | Integer level from the datapack band |
isPlayerEncumbranceAtLeast(p, level) | Current level >= given level |
isPlayerOverLevel(p, level) | Total weight above that band's scaled capacity |
getEncumbranceLevels() | Base bands (capacities before scaling) |
Creative players return 0 total (and no HUD) unless applyInventoryWeightsInCreative is true.
Capacity
boolean capOn = ItemWeightAPI.isCapacityScalingEnabled();
double mult = ItemWeightAPI.getPlayerCapacityMultiplier(player); // level formula
double flat = ItemWeightAPI.getPlayerCapacityFlatBonus(player); // armour / accessories / enchants
double effectiveMax = ItemWeightAPI.scaleThreshold(2000.0, player); // Heavy Load example
double pieceBonus = ItemWeightAPI.getEquipmentCapacityBonus(chestStack);
boolean isGear = ItemWeightAPI.isCapacityEquipment(chestStack);
Formula used internally: threshold' = base * levelMultiplier + flatBonus.
Enchantment keys (datapack):
ffitemweight:burden_bearing— flat capacity bonus per levelffitemweight:crushing_load— curse; flat capacity penalty per level
Effects toggles (read-only)
boolean potions = ItemWeightAPI.areEncumbranceEffectsEnabled();
boolean attrs = ItemWeightAPI.areEncumbranceAttributesEnabled();
Actual potion / attribute application is internal (EncumbranceEffects). Query toggles only; do not depend on internal applicator classes.
Cache and readiness
// After you mutate inventory in a way this mod does not already listen for
ItemWeightAPI.invalidatePlayerCache(player);
ItemWeightAPI.invalidatePlayerCache(null); // all players
boolean ready = ItemWeightAPI.isWeightTableReady(); // server computed / client synced
Set<Item> stillDefault = ItemWeightAPI.getFallbackItems(); // server audit helper
Built-in invalidation already covers: equipment change, item pickup, container close, item destroy, logout.
Client-only helpers
import com.fyxe.ffitemweight.api.client.ClientItemWeightAPI;
double localTotal = ClientItemWeightAPI.getLocalPlayerTotalWeight();
EncumbranceLevel localEnc = ClientItemWeightAPI.getLocalPlayerEncumbrance();
boolean overloaded = ClientItemWeightAPI.isLocalPlayerEncumbranceAtLeast(4);
Use for HUD / UI only. Gameplay rules belong on the server with ItemWeightAPI and a real Player / ServerPlayer.
Datapack weights (for content packs)
Path: data/<namespace>/item_weights/<name>.json
{
"weights": [
{ "id": "mymod:widget", "weight": 2.5 },
{ "id": "mymod:.*_ore", "weight": 6.0, "regex": true },
{ "id": "mymod:boss_core", "weight": 50.0, "force": true }
]
}
| Field | Meaning |
|---|---|
id | Item id, or regex when regex is true |
weight | Unit weight |
regex | Treat id as a full-id Java regex |
force | Lock base; recipes cannot overwrite |
Prefer exact ids for anchors; leave craftable tools out of JSON so recipe derivation can fill them.
Datapack encumbrance
Path: data/<namespace>/ffitemweight/encumbrance/<id>.json
Override a built-in by shipping the same path (for example ffitemweight/encumbrance/heavy_load.json).
{
"level": 3,
"display_name": "Heavy Load",
"color": "#FFAA00",
"capacity": 2000,
"icon": "ffitemweight:textures/gui/encumbrance/heavy_load.png",
"shake": 1.5,
"effects": [{ "id": "minecraft:slowness", "amplifier": 0 }],
"attributes": [{ "id": "minecraft:generic.movement_speed", "operation": "multiply_total", "amount": -0.20 }]
}
Compare level in code. Treat display_name as a label only.
Events (NeoForge event bus)
Register on NeoForge.EVENT_BUS (common / server).
EncumbranceChangedEvent (after level change, not cancellable)
Fired when the player's integer encumbrance level changes (for example 2 to 3). Checked about every 10 ticks with potion/attribute application. First observation after join does not fire (avoids login spam).
import com.fyxe.ffitemweight.api.EncumbranceChangedEvent;
import net.neoforged.bus.api.SubscribeEvent;
@SubscribeEvent
public void onEncumbranceChanged(EncumbranceChangedEvent event) {
if (event.isIncrease()) {
// moved to a heavier band
}
int from = event.getOldLevel();
int to = event.getNewLevel();
double total = event.getTotalWeight();
EncumbranceLevel band = event.getNewBand();
}
| Getter | Meaning |
|---|---|
getPlayer() | ServerPlayer |
getOldLevel() / getNewLevel() | Previous / new integer bands (Integer.MIN_VALUE when tracking is off) |
getTotalWeight() | Carried weight at transition |
getOldBand() / getNewBand() | EncumbranceLevel snapshots (old may be null) |
isIncrease() / isDecrease() | Direction along integer levels |
Note: There is no pre-change cancellable event — weight is inventory-derived. Cancel gameplay side-effects in your own handlers instead.
Soft compatibility (do not hard-depend)
| Mod | Role |
|---|---|
| EMI | Tooltip weights via vanilla tooltip path |
| Accessories / Curios | Slot weight + capacity (Accessories preferred if both present) |
| Create | Config recipe modifiers / filters / nested handlers |
Integrate through ItemWeightAPI, not compat.* packages.
Stability
| Surface | Status |
|---|---|
com.fyxe.ffitemweight.api.* | Public API |
EncumbranceChangedEvent | Public API |
com.fyxe.ffitemweight.api.client.* | Public API (client) |
WeightManager / CapacityHelper public methods | Stable but prefer ItemWeightAPI |
Config fields / TOML keys | User-facing; do not hard-code as API |
EncumbranceEffects, compat.*, network internals | Internal |
Minimal integration example
@EventBusSubscriber(modid = "yourmod")
public class YourWeightHooks {
@SubscribeEvent
public static void onSpellCast(YourSpellEvent event) {
if (!(event.getEntity() instanceof ServerPlayer player)) return;
if (!ModList.get().isLoaded("ffitemweight")) return;
if (ItemWeightAPI.isPlayerEncumbranceAtLeast(player, 4)) {
event.setCanceled(true);
player.displayClientMessage(
Component.literal("Too encumbered to cast."), true);
return;
}
// Optional: react to carried mass
double mass = ItemWeightAPI.getPlayerTotalWeight(player);
if (mass > 3000.0) {
// apply your own mild penalty...
}
}
}
Datapack item weight references (0.6.4)
In data/<namespace>/item_weights/<name>.json, use an exact weight_from reference instead of a numeric weight:
{
"weights": [
{ "id": "minecraft:oxidized_copper", "weight_from": "minecraft:copper_block" }
]
}
The item always uses the source item's final unit weight, including recipe calculation, clamps, and fallback. References are locked regardless of force and take precedence over numeric regex bases. Do not combine weight_from with weight or regex: true. Chains are supported. Missing items and cycles, including self-references and dependent chains, log warnings and retain normal regex/recipe/fallback calculation. Recipe ingredients follow the source's current weight. Final weights sync to clients; nested contents are still counted separately.
Default data maps all 63 oxidation and wax variants of the nine copper shapes to their unwaxed, unoxidized equivalents. Replace the same resource path to override these defaults. Later loaded exact numeric/reference entries replace earlier ones. Reload with /reload; Java API signatures are unchanged.