Content Builder
Create Minecraft items, blocks, recipes, and entities in a few lines of code — no boilerplate subclasses required.
How It Works
In standard Forge development, creating a new item requires extending Item, writing a model
JSON, registering the item in multiple events, and wiring up recipes manually. The Content
Builder module removes all of that ceremony.
Instead of subclasses and boilerplate, you describe what you want using a fluent chain
and call .register() at the end. EriAPI handles everything else: Forge event registration,
model binding, creative tab placement, and recipe injection.
EriItem.create("mymod", "ruby")
.maxStackSize(16)
.rarity(EnumRarity.RARE)
.glowing(true)
.tooltip("&7A precious gem")
.tooltip("&cVery rare")
.onRightClick(ctx -> ctx.getPlayer().heal(2f))
.creativeTab(CreativeTabs.MATERIALS)
.register();
The Fluent API — writing code like a sentence
Every method returns this, which means you can keep chaining. Think of it as building a
sentence: "Create an item named 'ruby' in 'mymod', max stack of 16, rare rarity, glowing, with two
tooltip lines, healing the player on right-click, in the Materials creative tab — then register."
You never have to extend Item or Block yourself. Sub-builders (like
.food(), .tool(), .armor()) let you describe specialised
behaviour inline and return you to the parent builder with .done().
Always end every builder chain with .register(). Without it, the item or block is
never submitted to Forge and will not appear in-game.
Package
All classes described on this page live in the fr.eri.eriapi.content package. Import
whichever class you need at the top of your file.
import fr.eri.eriapi.content.EriItem;
import fr.eri.eriapi.content.EriBlock;
import fr.eri.eriapi.content.EriRecipe;
import fr.eri.eriapi.content.ContentRegistry;
import fr.eri.eriapi.content.ItemActionContext;
import fr.eri.eriapi.content.BlockActionContext;
Where to register your content
Call your builder chains from the @Mod.EventHandler preInit phase of your
main mod class — before Forge fires the registry events. EriAPI queues everything internally and
flushes the queue at the right moment through ContentRegistry.
@Mod(modid = "mymod", version = "1.0")
public class MyMod {
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
// Register your items and blocks here
EriItem.create("mymod", "ruby")
.maxStackSize(16)
.rarity(EnumRarity.RARE)
.register();
EriBlock.create("mymod", "ruby_ore")
.material(Material.ROCK)
.hardness(3.0f)
.register();
}
}
EriItem
EriItem is the entry point for creating any item. Start with the static factory method
EriItem.create(modid, name) and chain the methods you need. The name
becomes the item's registry name (e.g. mymod:ruby) and is also used as the translation
key and model filename.
EriItem.create("modid", "ruby")
.maxStackSize(16)
.rarity(EnumRarity.RARE)
.glowing(true)
.tooltip("&7A precious gem")
.tooltip("&cVery rare")
.onRightClick(ctx -> ctx.getPlayer().heal(2f))
.creativeTab(CreativeTabs.MATERIALS)
.register();
Methods
-
STATICEriItem.create(String modid, String name)Static factory. Creates a new item builder.
modidis your mod's ID;nameis the registry name (lowercase, underscores). Must be called first. -
FLUENTmaxStackSize(int size)Sets the maximum number of items that can stack in one inventory slot. Default: 64.
-
FLUENTmaxDamage(int damage)Sets the maximum durability of the item. Items with durability automatically show the durability bar. Setting this implicitly sets maxStackSize to 1.
-
FLUENTrarity(EnumRarity rarity)Sets the item rarity, which controls the color of its name in tooltips. Values:
COMMON(white),UNCOMMON(yellow),RARE(aqua),EPIC(light purple). -
FLUENTcreativeTab(CreativeTabs tab)Places the item in a creative mode tab. Use any of Minecraft's built-in tabs (
CreativeTabs.MATERIALS,CreativeTabs.TOOLS, etc.) or pass a custom tab instance. -
FLUENTglowing(boolean glow)When
true, applies the enchantment glint visual effect to the item regardless of whether it is actually enchanted. -
FLUENTtooltip(String line)Adds a line to the item tooltip shown when hovering in an inventory. Supports Minecraft color codes with
&(e.g.&7gray text). Call multiple times for multiple lines. -
EVENTonRightClick(Consumer<ItemActionContext> action)Registers a callback fired when the player right-clicks while holding this item. Receives an
ItemActionContextwith the player, world, hand, and position. -
EVENTonLeftClick(Consumer<ItemActionContext> action)Registers a callback fired when the player left-clicks (attacks) while holding this item. Receives an
ItemActionContext. -
SUB-BUILDERfood()Opens a
FoodBuildersub-builder for configuring food properties. Call.done()on the sub-builder to return here. -
SUB-BUILDERtool()Opens a
ToolBuildersub-builder for configuring tool properties. Call.done()to return. -
SUB-BUILDERarmor()Opens an
ArmorBuildersub-builder for configuring armor properties. Call.done()to return. -
FLUENTanimatedModel(String modelId)Configures the item to use an animated 3D Blockbench model when held or in inventory. Rendering is delegated to an internal
TileEntityItemStackRenderer.modelIduses the format"modid:item/name"(e.g."eriniumfaction:item/faction_sword"). The Blockbench JSON file must be atassets/{modid}/models/item/{name}.json. -
TERMINALregister()Finalizes the item and submits it to EriAPI's
ContentRegistryfor Forge registration. Must be the last call in every chain.
If you need to reference the item later (e.g. to use it in a recipe), assign the result of
EriItem.create() to a field before calling .register().
The builder object itself doubles as a handle to the registered item.
public static EriItem RUBY = EriItem.create("mymod", "ruby")
.maxStackSize(16)
.register();
Simple item — model and texture
For a plain 2D item, you must provide two files inside
src/main/resources/assets/mymod/: the JSON model and the PNG texture. EriAPI registers
the model's resource location with Forge through ModelLoader.setCustomModelResourceLocation(),
but the JSON files (models and blockstates) must be provided by the developer in
src/main/resources/. Those files are bundled into the mod JAR at compile time.
EriItem.create("mymod", "ruby")
.maxStackSize(64)
.register();
// -> Expected texture: assets/mymod/textures/items/ruby.png (16x16 or 64x64)
// -> Expected JSON model: assets/mymod/models/item/ruby.json
Model file to create manually at src/main/resources/assets/mymod/models/item/ruby.json:
{
"parent": "item/generated",
"textures": {
"layer0": "mymod:items/ruby"
}
}
For items that must be held properly in the hand (tool-style rendering), use
"parent": "item/handheld" instead of "item/generated" inside the model
JSON.
Animated 3D item — animatedModel() + AnimatedItemController
The .animatedModel(String modelId) method lets you render the item with a
3D Blockbench model (Java Block/Item project type) instead of a flat 2D texture.
Rendering is delegated to an internal TileEntityItemStackRenderer, and animations are
driven client-side by AnimatedItemController.
Required files
assets/modid/models/item/name.json— exported Blockbench model (Java Block/Item format)assets/modid/animations/item/name.erianim.json— animation file (optional if no animation is needed)- All textures referenced from the Blockbench JSON, placed at the expected path
import fr.eri.eriapi.anim.AnimatedItemController;
import fr.eri.eriapi.content.EriItem;
import net.minecraft.item.EnumRarity;
// Declaration
EriItem.create("mymod", "animated_sword")
.maxStackSize(1)
.rarity(EnumRarity.EPIC)
.animatedModel("mymod:item/animated_sword") // 3D Blockbench model
.onRightClick(ctx -> {
if (ctx.world.isRemote) {
// Plays the "use" animation from mymod:item/animated_sword.erianim.json
AnimatedItemController.play("mymod:item/animated_sword", "use");
}
})
.register();
// Play an animation (animFileId = modelId by default)
AnimatedItemController.play("mymod:item/animated_sword", "idle");
// Play with an explicit animFileId (2nd parameter)
AnimatedItemController.play("mymod:item/animated_sword", "mymod:item/sword_anims", "swing");
// Stop the current animation
AnimatedItemController.stop("mymod:item/animated_sword");
// Check if an animation is currently playing
boolean playing = AnimatedItemController.isPlaying("mymod:item/animated_sword");
animFileId resolutionThe optional 2nd parameter of play() resolves to
assets/modid/animations/path.erianim.json. When omitted, EriAPI uses
modelId as the animFileId. Playback state is shared per
modelId: every item rendered with the same model plays the same animation
at the same time.
FoodBuilder
FoodBuilder is a sub-builder accessed via EriItem.create(...).food().
It lets you define all food properties inline. When you are done, call .done() to
return to the parent EriItem builder and continue chaining.
EriItem.create("modid", "golden_apple_custom")
.food()
.hunger(6)
.saturation(1.2f)
.alwaysEdible(true)
.effect(new PotionEffect(MobEffects.REGENERATION, 100, 1), 1.0f)
.done()
.register();
Methods
-
FLUENThunger(int amount)How many hunger points are restored when eaten. Each full shank icon = 2 points; a full hunger bar = 20 points.
-
FLUENTsaturation(float modifier)The saturation modifier added on top of the hunger points. Higher values mean the player stays fed longer. Bread is ~0.6, golden apple is ~1.2.
-
FLUENTalwaysEdible(boolean value)When
true, the item can be eaten even when the player's hunger bar is full (like golden apples). Default:false. -
FLUENTeffect(PotionEffect effect, float probability)Applies a potion effect when the food is eaten.
probabilityis a float from0.0(never) to1.0(always). Call multiple times for multiple effects. -
RETURNdone()Closes the
FoodBuilderand returns the parentEriItembuilder so you can continue chaining.
ToolBuilder
ToolBuilder is accessed via EriItem.create(...).tool(). It configures
the item as a sword, pickaxe, axe, shovel, or hoe by setting the tool type, attack damage,
durability, and attack speed. Call .done() when finished.
EriItem.create("modid", "ruby_sword")
.tool()
.type(ToolType.SWORD)
.damage(8f)
.durability(1500)
.speed(1.6f)
.done()
.register();
ToolType enum values
| Value | Description |
|---|---|
| ToolType.SWORD | Melee weapon — increases attack damage, sweeping |
| ToolType.PICKAXE | Mines stone-type blocks faster, required harvest tool |
| ToolType.AXE | Chops wood faster, deals extra damage on first hit |
| ToolType.SHOVEL | Digs dirt/gravel faster |
| ToolType.HOE | Tills farmland |
Methods
-
FLUENTtype(ToolType type)Determines which kind of tool behavior is applied. See table above.
-
FLUENTdamage(float attackDamage)The amount of attack damage dealt. A stone sword deals 5.0; a diamond sword deals 7.0.
-
FLUENTdurability(int uses)How many uses before the item breaks. A diamond sword has 1561 uses; an iron sword has 251.
-
FLUENTspeed(float attackSpeed)The attack speed modifier. Higher values mean faster attack cooldown recovery. A sword is ~1.6; an axe is ~0.9.
-
RETURNdone()Closes the
ToolBuilderand returns the parentEriItembuilder.
ArmorBuilder
ArmorBuilder is accessed via EriItem.create(...).armor(). It configures
which armor slot the item occupies, how much protection it provides, and which material name to
use for the armor texture. Call .done() when finished.
EriItem.create("modid", "ruby_chestplate")
.armor()
.slot(ArmorSlot.CHEST)
.protection(8)
.toughness(2.0f)
.materialName("ruby")
.done()
.register();
ArmorSlot enum values
| Value | Slot | Standard protection (diamond) |
|---|---|---|
| ArmorSlot.HEAD | Helmet (head) | 3 |
| ArmorSlot.CHEST | Chestplate (torso) | 8 |
| ArmorSlot.LEGS | Leggings (legs) | 6 |
| ArmorSlot.FEET | Boots (feet) | 3 |
Methods
-
FLUENTslot(ArmorSlot slot)Which armor slot this item equips into. See table above.
-
FLUENTprotection(int points)Number of armor points granted. Each full shield icon in the HUD = 2 points; maximum total from all four pieces is 20.
-
FLUENTtoughness(float value)Armor toughness reduces the damage-scaling penalty from strong hits. Diamond armor has 2.0 per piece; most other materials have 0.0.
-
FLUENTmaterialName(String name)The base name used to look up the armor layer textures. For
"ruby", Minecraft will look fortextures/models/armor/ruby_layer_1.pngandruby_layer_2.png. -
RETURNdone()Closes the
ArmorBuilderand returns the parentEriItembuilder.
EriBlock
EriBlock is the entry point for creating any block. Works exactly like
EriItem — start with the static factory, chain the properties you need, and end
with .register().
The name parameter becomes the block's registry name (e.g. mymod:ruby_ore)
and is also used for the block state JSON and model filename lookups.
EriBlock.create("modid", "ruby_ore")
.material(Material.ROCK)
.hardness(3.0f)
.resistance(15.0f)
.harvestTool("pickaxe", 2)
.soundType(SoundType.STONE)
.drops(Items.DIAMOND, 1, 3)
.creativeTab(CreativeTabs.BUILDING_BLOCKS)
.onBreak(ctx -> System.out.println("Broken!"))
.register();
Methods
-
STATICEriBlock.create(String modid, String name)Static factory. Creates a new block builder. Must be called first.
-
FLUENTmaterial(Material material)Sets the block's material, which determines default properties like flammability, piston behavior, and map color. Common values:
Material.ROCK,Material.WOOD,Material.IRON,Material.GROUND. -
FLUENThardness(float value)How long it takes to mine the block without the correct tool. Dirt is 0.5, stone is 1.5, obsidian is 50.0. Use
-1.0ffor unbreakable. -
FLUENTresistance(float value)Explosion resistance. Stone is 10.0, obsidian is 6000.0. Generally set this higher than hardness.
-
FLUENTharvestTool(String toolClass, int level)Specifies which tool is required to harvest drops, and at which harvest level. Tool class values:
"pickaxe","axe","shovel". Levels: 0=wood, 1=stone, 2=iron, 3=diamond. -
FLUENTsoundType(SoundType soundType)The set of sounds played when walking on, placing, or breaking the block. Common values:
SoundType.STONE,SoundType.WOOD,SoundType.SAND,SoundType.METAL,SoundType.GLASS. -
FLUENTdrops(Item item, int min, int max)Sets what the block drops when broken.
minandmaxdefine the random drop count range. For a block that drops itself, omit this method. -
FLUENTcreativeTab(CreativeTabs tab)Places the block's item form in a creative mode tab.
-
EVENTonBreak(Consumer<BlockActionContext> action)Registers a callback fired when a player breaks this block. Receives a
BlockActionContextwith the world, position, and player. -
EVENTonPlace(Consumer<BlockActionContext> action)Registers a callback fired when a player places this block. The callback is triggered in
onBlockPlacedBy, which guarantees thatctx.getPlayer()always returns the player who placed the block (nevernull). -
EVENTonWalk(Consumer<BlockActionContext> action)Registers a callback fired every tick an entity walks on top of this block.
-
FLUENThitbox(AxisAlignedBB box)Sets a single custom hitbox (selection + collision). Replaces the default full-block hitbox. Automatically sets
isFullCubeandisOpaquetofalse. Coordinates are in blocks: 1.0 = 1 block = 16 pixels. Example:new AxisAlignedBB(0, 0, 0, 1, 0.5, 1)= half-block height. -
FLUENThitboxes(AxisAlignedBB... boxes)Defines multiple independent collision boxes. Each box is tested separately for entity collision. The selection outline (hover highlight) shows the union of all boxes. Automatically sets
isFullCubeandisOpaquetofalse. Recommended: keep under 6-8 boxes to avoid TPS impact on busy servers. -
FLUENTrenderLayer(BlockRenderLayer layer)Sets the client render layer for the block. Used for translucent, alpha-tested, or transparent blocks. Common values:
SOLID(default) — opaque full blockCUTOUT— binary alpha (plants, grates)CUTOUT_MIPPED— binary alpha with mipmaps (leaves)TRANSLUCENT— continuous alpha transparency (stained glass, ice)
-
FLUENTonEntityWalk(TriConsumer<World, BlockPos, Entity> action)Callback invoked every tick an entity walks on top of the block (equivalent to Forge's
onEntityWalk). UnlikeonWalk(Consumer<BlockActionContext>), this variant gives direct access to the world, position, and entity - useful for movement effects or zone damage. -
FLUENTonEntityCollision(TriConsumer<World, BlockPos, Entity> action)Callback invoked every tick an entity passes through the block (entity inside the hitbox). Equivalent to Forge's
onEntityCollision. Useful for plants that damage on contact, slowdown blocks, or trigger traps. -
FLUENTlogLike()Turns the block into a tree log (
BlockRotatedPillar) with theAXISproperty (X, Y, Z). The block orients itself based on the face it is placed on (upright, lying north/south, lying east/west). The blockstate JSON must handle the three variantsaxis=x,axis=y,axis=z. -
FLUENTleavesLike(Block sapling)Turns the block into tree leaves (
BlockLeaves) with the vanillaCHECK_DECAYandDECAYABLEproperties. Leaves automatically decay when disconnected from a log. Forces theCUTOUT_MIPPEDrender layer. Thesaplingargument is dropped with 5% probability during natural decay (may benullto disable).
Overload:leavesLike()is equivalent toleavesLike(null). -
FLUENTplantLike()Turns the block into a decorative plant:
- Reduced hitbox (
0.15, 0, 0.15to0.85, 0.9, 0.85) - No collision (entities pass through)
- Automatic
CUTOUTrender layer isOpaque=false,isFullCube=false
block/crossmodel for the X-shaped display (like vanilla flowers). - Reduced hitbox (
-
TERMINALregister()Finalizes the block and submits it to
ContentRegistryfor Forge registration. Must be the last call.
import net.minecraft.util.math.AxisAlignedBB;
// Single hitbox — half-block height (1.0 = 1 block = 16 pixels)
EriBlock.create("mymod", "half_slab")
.material(Material.ROCK)
.hardness(2.0f)
.hitbox(new AxisAlignedBB(0, 0, 0, 1, 0.5, 1))
.register();
// Multiple hitboxes — base plate + central pillar
EriBlock.create("mymod", "machine")
.material(Material.IRON)
.hardness(4.0f)
.hitboxes(
new AxisAlignedBB(0, 0, 0, 1, 0.25, 1), // flat base
new AxisAlignedBB(0.25, 0.25, 0.25, 0.75, 1, 0.75) // central pillar
)
.register();
EriAPI automatically creates and registers the ItemBlock companion for every
EriBlock — the item form that appears in inventories. You do not have to create it
manually.
Cube block — Textures and model JSON
When you register a cubic EriBlock, EriAPI registers the model's resource location with
Forge through ModelLoader.setCustomModelResourceLocation(), but no JSON file is
generated automatically. You must provide the blockstate, block model and item model yourself
inside src/main/resources/assets/<modid>/. Those files are bundled into the mod JAR
at compile time.
EriBlock.create("mymod", "my_block")
.material(Material.ROCK)
.hardness(3.0f)
.register();
Case 1 — One texture for every face
Drop a PNG texture and provide the blockstate + block model + item model. This is the most common case.
src/main/resources/assets/mymod/textures/blocks/my_block.png— the texture (16x16, or 64x64 recommended)src/main/resources/assets/mymod/blockstates/my_block.json— create manuallysrc/main/resources/assets/mymod/models/block/my_block.json— create manuallysrc/main/resources/assets/mymod/models/item/my_block.json— create manually (for the inventory item)
{
"variants": {
"normal": { "model": "mymod:my_block" }
}
}
{
"parent": "block/cube_all",
"textures": {
"all": "mymod:blocks/my_block"
}
}
{
"parent": "mymod:block/my_block"
}
Case 2 — A different texture per face
To have a distinct top, bottom and sides (furnace, log, ore-like blocks...), provide a
models/block/my_block.json listing all six faces.
{
"parent": "block/cube",
"textures": {
"up": "mymod:blocks/my_block_top",
"down": "mymod:blocks/my_block_bottom",
"north": "mymod:blocks/my_block_side",
"south": "mymod:blocks/my_block_side",
"east": "mymod:blocks/my_block_side",
"west": "mymod:blocks/my_block_side",
"particle": "mymod:blocks/my_block_side"
}
}
Expected files in this case:
src/main/resources/assets/mymod/textures/blocks/my_block_top.pngsrc/main/resources/assets/mymod/textures/blocks/my_block_bottom.pngsrc/main/resources/assets/mymod/textures/blocks/my_block_side.png
EriAPI does not generate any JSON file. It only registers the model's resource
location with Forge. All files (blockstates/, models/block/,
models/item/, textures/) must be provided manually inside
src/main/resources/assets/<modid>/.
Animated 3D block (EriAnimBlock)
EriAnimBlock lets you create a block with a 3D Blockbench model
and animations. Under the hood it uses a TileEntity + a
TESR (Tile Entity Special Renderer), both registered automatically by EriAPI.
Creation workflow
- Model the block in Blockbench (project type Java Block/Item).
- Export the model to JSON at
assets/modid/models/block/name.json. - Create the animation file
assets/modid/animations/block/name.erianim.json. - Register the block in Java through
EriAnimBlock.create().
Required files
assets/modid/models/block/name.json— exported Blockbench modelassets/modid/animations/block/name.erianim.json— animation file- All textures referenced from the Blockbench JSON
Fluent builder for creating a 3D animated block with model and animations.
-
STATICEriAnimBlock create(String modid, String name)Creates a new animated block builder.
-
FLUENTEriAnimBlock material(Material mat)Block material (
Material.IRON,Material.WOOD, etc.). -
FLUENTEriAnimBlock hardness(float hardness)Mining time.
-
FLUENTEriAnimBlock resistance(float resistance)Explosion resistance.
-
FLUENTEriAnimBlock animation(String animFileId)Required. ID of the
.erianim.jsonfile in the format"modid:path/name"(without the extension). Resolves toassets/modid/animations/path/name.erianim.json. -
FLUENTEriAnimBlock tileEntity(Class<?> teClass)Custom TileEntity (optional). Must extend
AnimatedBlockTileEntityand provide a public no-arg constructor. If omitted, EriAPI usesAnimatedBlockTileEntityGeneric(enough for 90% of cases).
Use this option when you need custom logic: react to an animation event at a precise frame, conditionally chain animations, save a block-specific NBT state, sync data to the client, etc. -
EVENTEriAnimBlock onRightClick(Consumer<BlockActionContext> handler)Callback fired when the player right-clicks the block.
-
TERMINALBlock register()Finalizes and registers the block + the associated TileEntity + TESR. Returns the
Blockinstance.
import fr.eri.eriapi.anim.AnimatedBlockTileEntity;
import fr.eri.eriapi.anim.EriAnimBlock;
import net.minecraft.block.material.Material;
EriAnimBlock.create("mymod", "special_chest")
.material(Material.WOOD)
.hardness(2.5f)
.animation("mymod:block/special_chest")
.onRightClick(ctx -> {
AnimatedBlockTileEntity te = (AnimatedBlockTileEntity)
ctx.world.getTileEntity(ctx.pos);
if (te != null && !te.isAnimating()) {
te.playAnimation("open");
}
})
.register();
Custom TileEntity: AnimatedBlockTileEntity
When you pass .tileEntity(MyTile.class) to the builder, EriAPI uses your class instead
of AnimatedBlockTileEntityGeneric. Your class must extend
AnimatedBlockTileEntity and have a public no-arg constructor.
Base class to extend for custom behavior. The 3 methods below are designed to be overridden.
Overridable methods (protected)
-
OVERRIDEvoid onAnimationEvent(AnimationEvent event)Called for every event triggered by the animation (keyframes of type
effect,sound,particle,custom). Useful to spawn a sound / particle at a precise frame. -
OVERRIDEvoid onAnimationComplete(String animName)Called when an animation finishes (reaches the last keyframe in
ONCE/HOLD/HIDE, or after one full loop forLOOP_N). Ideal for chaining animations. -
OVERRIDEvoid onAnimationCallback(String callbackName)Called specifically for callback keyframes with the callback name (defined in the animation JSON). Lets you name precise points of the animation (e.g.
"chest_opened") and react to them in Java.
Public utilities (call these)
-
PUBLICvoid playAnimation(String animName)Plays the named animation with the
EndBehaviordefined in the JSON (default). -
PUBLICvoid playAnimation(String animName, EndBehavior endBehaviorOverride)Plays the animation forcing a custom
EndBehavior(ONCE,LOOP,HOLD,HIDE,LOOP_N). -
PUBLICvoid pauseAnimation()Pauses the animation at the current frame (synced client+server).
-
PUBLICvoid resumeAnimation()Resumes a paused animation.
-
PUBLICvoid resetAnimation()Returns to the initial pose (smooth interpolation).
-
PUBLICvoid resetAnimation(boolean instant)Returns to the initial pose;
instant=trueskips interpolation (immediate snap). -
PUBLICvoid stopAnimation()Stops the current animation (stays at current frame without returning to base pose).
-
PUBLICvoid setTexture(String target, String value)Replaces a model texture at runtime.
targetis the texture key from the Blockbench JSON,valueis the new texture path (e.g."mymod:blocks/core_active"). Synced to client. -
PUBLICvoid clearTexture(String target)Removes the texture override for
target(back to the original texture). -
PUBLICvoid clearAllTextures()Clears all texture overrides at once.
-
PUBLICboolean isAnimating()Returns
trueif an animation is currently playing. -
PUBLICString getCurrentAnimation()Name of the current animation, or
nullif none is active. -
PUBLICfloat getAnimationProgress()Progress of the current animation, from
0.0to1.0. -
PUBLICvoid setBlockYRotation(float rotation)Block rotation around the Y axis (degrees). Synced to client.
import fr.eri.eriapi.anim.AnimatedBlockTileEntity;
import fr.eri.eriapi.anim.AnimationEvent;
public class TileEntitySpecialChest extends AnimatedBlockTileEntity {
public TileEntitySpecialChest() {
super("mymod:block/special_chest", "mymod:block/special_chest");
}
@Override
protected void onAnimationEvent(AnimationEvent event) {
// Play a sound at a precise frame
if ("creak".equals(event.getValue())) {
world.playSound(null, pos, SoundEvents.BLOCK_CHEST_OPEN,
SoundCategory.BLOCKS, 1.0f, 1.0f);
}
}
@Override
protected void onAnimationComplete(String animName) {
// Chain another animation
if ("open".equals(animName)) {
playAnimation("idle_open", EndBehavior.LOOP);
}
}
@Override
protected void onAnimationCallback(String callbackName) {
// Spawn loot at a precise point
if ("spawn_loot".equals(callbackName) && !world.isRemote) {
spawnLootAt(pos);
}
}
}
// Registration:
EriAnimBlock.create("mymod", "special_chest")
.material(Material.WOOD)
.animation("mymod:block/special_chest")
.tileEntity(TileEntitySpecialChest.class)
.onRightClick(ctx -> {
TileEntitySpecialChest te = (TileEntitySpecialChest)
ctx.world.getTileEntity(ctx.pos);
if (te != null && !te.isAnimating()) {
te.playAnimation("open");
}
})
.register();
For more complex behaviors (callbacks on animation events, conditional chaining, tick logic),
extend AnimatedBlockTileEntity in a subclass and pass it via
.tileEntity(CustomTile.class). See the
Animation System page for full details.
EriRecipe
EriRecipe provides fluent builders for the three main recipe types in Minecraft 1.12.2:
shaped crafting, shapeless crafting, and smelting.
Each type has its own static factory method.
A shaped recipe requires ingredients to be placed in a specific grid pattern. Define the pattern with up to three strings of up to three characters each, then map each character to an ingredient.
EriRecipe.shaped("modid", "ruby_block")
.pattern("RRR", "RRR", "RRR")
.key('R', myRubyItem)
.result(myRubyBlock, 1)
.register();
Methods
-
STATICEriRecipe.shaped(String modid, String name)Static factory. Creates a shaped recipe builder.
nameis the recipe's registry name. -
FLUENTpattern(String... rows)Defines the crafting grid pattern. Pass 1–3 strings of 1–3 characters each. Use a space character for empty slots. Each non-space character must be mapped with
.key(). -
FLUENTkey(char symbol, Item item)Maps a character used in the pattern to an ingredient item. Call once per unique character. Also accepts
BlockorEriItem/EriBlockreferences. -
FLUENTresult(Item item, int count)The item produced by this recipe and how many copies. Also accepts
Block,EriItem, orEriBlockreferences. -
TERMINALregister()Submits the recipe to Forge's recipe registry. Must be the last call.
A shapeless recipe can be crafted by placing the ingredients anywhere in the grid — order and position do not matter.
EriRecipe.shapeless("modid", "ruby_from_block")
.ingredient(myRubyBlock)
.result(myRubyItem, 9)
.register();
Methods
-
STATICEriRecipe.shapeless(String modid, String name)Static factory. Creates a shapeless recipe builder.
-
FLUENTingredient(Item item)Adds a required ingredient. Call multiple times to add more. Also accepts
Block,EriItem, orEriBlock. -
FLUENTresult(Item item, int count)The item produced and how many copies.
-
TERMINALregister()Submits the recipe to Forge's recipe registry.
A smelting recipe defines what a furnace produces when a given input item is cooked.
EriRecipe.smelting("modid", "ruby_smelt")
.input(myRubyOre)
.result(myRubyItem)
.xp(1.0f)
.register();
Methods
-
STATICEriRecipe.smelting(String modid, String name)Static factory. Creates a smelting recipe builder.
-
FLUENTinput(Item item)The item that goes into the furnace input slot. Also accepts
Block,EriItem, orEriBlock. -
FLUENTresult(Item item)The item produced by smelting. Always produces exactly one copy.
-
FLUENTxp(float amount)How much XP the player earns each time they smelt this recipe. Ores typically give 0.7–1.0; food gives 0.1–0.35.
-
TERMINALregister()Submits the smelting recipe to Forge.
ContentRegistry
ContentRegistry is the internal engine that connects EriAPI builders to the Forge
event system. You do not need to interact with it directly — it is fully
automatic.
What it does
When you call .register() on any builder, EriAPI adds the entry to an internal
queue managed by ContentRegistry. The registry listens for Forge's
RegistryEvent.Register<Item> and RegistryEvent.Register<Block>
events and automatically flushes the queue at the correct moment during the mod loading lifecycle.
| Forge Event | What ContentRegistry does |
|---|---|
RegistryEvent.Register<Block> |
Registers all queued EriBlock instances |
RegistryEvent.Register<Item> |
Registers all queued EriItem instances and auto-generates ItemBlock companions |
ModelRegistryEvent |
Binds default item models using the registry name as the model path |
FMLInitializationEvent |
Injects all queued EriRecipe entries into Forge's recipe registry |
Call your builder chains during FMLPreInitializationEvent (the preInit
phase). Calling .register() after Forge has already fired the registry events will
silently do nothing — your content will not appear in-game.
Model resolution
By default, EriAPI looks for item models at
assets/<modid>/models/item/<name>.json and block states at
assets/<modid>/blockstates/<name>.json. Place your JSON files there and
EriAPI will pick them up automatically — no manual ModelLoader.setCustomModelResourceLocation()
calls required.
Action Contexts
When you register a callback with .onRightClick(), .onLeftClick(), or
.onBreak(), your lambda receives a context object that gives you access to
everything relevant to the interaction. You never need to construct these objects yourself — EriAPI
creates and passes them automatically.
ItemActionContext
Passed to .onRightClick() and .onLeftClick() callbacks on EriItem.
| Method | Return type | Description |
|---|---|---|
getPlayer() | EntityPlayer | The player who interacted with the item |
getWorld() | World | The world in which the interaction occurred |
getHand() | EnumHand | MAIN_HAND or OFF_HAND |
getStack() | ItemStack | The ItemStack the player was holding |
getHitPosition() | BlockPos | The block position the player was looking at, or null if targeting air |
isRemote() | boolean | true if running on the client side; use to avoid running server logic twice |
BlockActionContext
Passed to .onBreak(), .onPlace(), and .onWalk() callbacks on EriBlock.
| Method | Return type | Description |
|---|---|---|
getPlayer() | EntityPlayer | The player who broke the block (may be null if broken by explosion or other means) |
getWorld() | World | The world containing the block |
getPos() | BlockPos | The position of the block that was broken |
getState() | IBlockState | The block state at the moment of breaking |
isRemote() | boolean | true on the client side |
EriItem.create("mymod", "teleport_staff")
.onRightClick(ctx -> {
// Guard: only run on the server
if (ctx.isRemote()) return;
EntityPlayer player = ctx.getPlayer();
BlockPos target = ctx.getHitPosition();
if (target != null) {
// Teleport the player to the clicked block
player.setPositionAndUpdate(
target.getX() + 0.5,
target.getY() + 1.0,
target.getZ() + 0.5
);
}
})
.register();
EriBlock.create("mymod", "alarm_block")
.material(Material.ROCK)
.hardness(1.5f)
.onBreak(ctx -> {
if (ctx.isRemote()) return;
World world = ctx.getWorld();
BlockPos pos = ctx.getPos();
EntityPlayer player = ctx.getPlayer();
// Broadcast a chat message to all players
world.getMinecraftServer().getPlayerList()
.sendMessage(new TextComponentString(
(player != null ? player.getName() : "Someone")
+ " broke the alarm block at " + pos
));
})
.register();
Complete Example
The following shows a self-contained preInit method that registers a full set of ruby content: an ore block, a gem item, a sword, a chestplate, a food item, and the corresponding recipes.
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
// --- Blocks ---
EriBlock rubyOre = EriBlock.create("mymod", "ruby_ore")
.material(Material.ROCK)
.hardness(3.0f)
.resistance(15.0f)
.harvestTool("pickaxe", 2)
.soundType(SoundType.STONE)
.creativeTab(CreativeTabs.BUILDING_BLOCKS)
.register();
EriBlock rubyBlock = EriBlock.create("mymod", "ruby_block")
.material(Material.IRON)
.hardness(5.0f)
.resistance(30.0f)
.harvestTool("pickaxe", 2)
.soundType(SoundType.METAL)
.creativeTab(CreativeTabs.BUILDING_BLOCKS)
.register();
// --- Items ---
EriItem ruby = EriItem.create("mymod", "ruby")
.maxStackSize(64)
.rarity(EnumRarity.RARE)
.glowing(false)
.tooltip("&7A rare gemstone")
.creativeTab(CreativeTabs.MATERIALS)
.register();
EriItem.create("mymod", "ruby_sword")
.tool()
.type(ToolType.SWORD)
.damage(8f)
.durability(1500)
.speed(1.6f)
.done()
.creativeTab(CreativeTabs.COMBAT)
.register();
EriItem.create("mymod", "ruby_chestplate")
.armor()
.slot(ArmorSlot.CHEST)
.protection(8)
.toughness(2.0f)
.materialName("ruby")
.done()
.creativeTab(CreativeTabs.COMBAT)
.register();
EriItem.create("mymod", "ruby_candy")
.food()
.hunger(4)
.saturation(0.8f)
.alwaysEdible(false)
.effect(new PotionEffect(MobEffects.SPEED, 200, 1), 1.0f)
.done()
.tooltip("&aTastes like victory")
.creativeTab(CreativeTabs.FOOD)
.register();
// --- Recipes ---
// Smelt ore → gem
EriRecipe.smelting("mymod", "ruby_from_ore")
.input(rubyOre)
.result(ruby)
.xp(1.0f)
.register();
// 9 gems → block
EriRecipe.shaped("mymod", "ruby_block_craft")
.pattern("RRR", "RRR", "RRR")
.key('R', ruby)
.result(rubyBlock, 1)
.register();
// Uncraft block → 9 gems
EriRecipe.shapeless("mymod", "ruby_from_block")
.ingredient(rubyBlock)
.result(ruby, 9)
.register();
}
EriEntity v2.0
EriEntity is the fluent builder for registering custom entities with an animated
Blockbench model. It works together with AnimatedEntityRenderer and the
IAnimatedEntity interface for animated entity rendering.
Since v1.4.0, EriEntity.register() automatically registers the entity in
Forge through a pool of 32 pre-compiled slots (GeneratedEntitySlot0…GeneratedEntitySlot31).
The mod no longer needs to handle the EntityEntry or the renderer manually.
Up to 32 EriEntity types can be registered per mod.
Main classes
| Class | Package | Role |
|---|---|---|
EriEntity | fr.eri.eriapi.content | Fluent builder for defining an entity |
EntityDefinition | fr.eri.eriapi.content | POJO containing the entity configuration |
EntitySeat | fr.eri.eriapi.content | Passenger seat definition |
AnimatedEntityRenderer | fr.eri.eriapi.anim | Entity renderer using Blockbench models |
IAnimatedEntity | fr.eri.eriapi.anim | Interface for entities with animation |
EriEntity builder methods
-
FactoryEriEntity create(String modId, String registryName)Creates a new entity builder. Static method.
-
FluentEriEntity model(String modelId)Sets the Blockbench model (e.g.
"mymod:entity/monster"). -
FluentEriEntity texture(String key, String path)Maps a model texture key to a ResourceLocation path.
-
FluentEriEntity animation(String name, String animId)Maps an animation name (e.g. "idle", "walk") to an .erianim animation ID.
-
FluentEriEntity hitbox(float width, float height)Sets the hitbox dimensions (default: 0.6 x 1.8).
-
FluentEriEntity seat(float x, float y, float z)Adds a simple passenger seat at the given Blockbench pixel coordinates.
-
FluentEriEntity seat(float x, float y, float z, String attachedGroup)Adds a seat attached to a model group (follows the group's animations).
-
FluentEriEntity seat(float x, float y, float z, float yawOffset, String attachedGroup, boolean lockCamera)Full seat configuration with yaw rotation, attached group, and camera lock.
-
FluentEriEntity spawnEgg(int primary, int secondary)Enables a spawn egg with primary and secondary colors (0xRRGGBB format).
-
FluentEriEntity creativeTab(String tabName)Sets the creative tab for the spawn egg.
-
TerminalEriEntity register()Registers the definition in the ContentRegistry AND automatically registers the EntityEntry, renderer, spawn egg and spawn rules in Forge through the 32-slot pool (v1.4.0).
Advanced methods (v1.4.0)
-
FluentEriEntity name(String displayName)Entity display name.
-
FluentEriEntity health(double health)Max HP (default: 20.0).
-
FluentEriEntity armor(double armor)Armor value (default: 0).
-
FluentEriEntity armorToughness(double toughness)Armor toughness (default: 0).
-
FluentEriEntity attackDamage(double damage)Melee damage (default: 2.0).
-
FluentEriEntity movementSpeed(double speed)Movement speed (default: 0.25).
-
FluentEriEntity followRange(double range)Target detection range (default: 16).
-
FluentEriEntity knockbackResistance(double resist)Knockback resistance, 0.0–1.0 (default: 0).
-
FluentEriEntity immuneToFire()Entity is immune to fire.
-
FluentEriEntity immuneToKnockback()Entity is immune to knockback.
-
FluentEriEntity canSwim(boolean swim)Sets whether the entity can swim (default: true).
-
FluentEriEntity experienceDrop(int min, int max)XP dropped on death (random value between min and max).
-
FluentEriEntity ambientSound(SoundEvent sound)Ambient sound.
-
FluentEriEntity hurtSound(SoundEvent sound)Hurt sound.
-
FluentEriEntity deathSound(SoundEvent sound)Death sound.
-
FluentEriEntity stepSound(SoundEvent sound)Step sound.
-
FluentEriEntity ambientSoundInterval(int ticks)Interval between ambient sounds (default: 80 ticks).
-
FluentEriEntity aiPreset(AiPreset preset)Predefined AI behaviour. Values:
PASSIVE,HOSTILE_MELEE,HOSTILE_RANGED,NEUTRAL,BOSS. -
FluentEriEntity ai(Consumer<PathfinderBuilder> config)Custom AI configuration via
PathfinderBuilder(see dedicated section below). -
FluentEriEntity drop(ItemStack stack, float chance)Item drop on death (chance: 0.0–1.0).
-
FluentEriEntity drop(Item item, int min, int max, float chance)Random drop between min and max items.
-
FluentEriEntity rareDrop(ItemStack stack, float chance)Rare drop (chance divided by looting level).
-
FluentEriEntity spawn(Consumer<EriSpawner> config)Natural spawn rules (see
EriSpawnersection). -
FluentEriEntity tracker(int range, int freq, boolean velocity)Network tracking parameters (default: 64, 3, true).
-
FluentEriEntity onSpawn(Consumer<EntityLivingBase> cb)Callback called at initial spawn.
-
FluentEriEntity onDeath(BiConsumer<EntityLivingBase, DamageSource> cb)Death callback.
-
FluentEriEntity onUpdate(Consumer<EntityLivingBase> cb)Callback every tick (
onLivingUpdate). -
FluentEriEntity onAttack(BiConsumer<EntityLivingBase, Entity> cb)Callback when the entity attacks.
-
FluentEriEntity onHurt(BiConsumer<EntityLivingBase, DamageSource> cb)Callback when the entity takes damage.
-
FluentEriEntity onInteract(BiConsumer<EntityLivingBase, EntityPlayer> cb)Callback when a player right-clicks on the entity.
AnimatedEntityRenderer
The entity renderer that displays a Blockbench model with animations. The rendering pipeline
(groups, elements, faces, UV) is identical to AnimatedBlockTESR.
-
FactoryIRenderFactory<T> factory(String modelId)Creates a factory for
RenderingRegistry.registerEntityRenderingHandler(). -
FactoryIRenderFactory<T> factory(String modelId, float shadowSize)Factory with custom shadow size.
IAnimatedEntity
Interface that entities must implement to support animated rendering. The renderer checks for this interface to get the animation pose.
-
AnimationPose getCurrentPose(float partialTicks)Returns the current interpolated pose, or
nullif no animation is active. -
AnimState getAnimState()Returns the current animation state of the entity.
EntitySeat
Each entity can have one or more passenger seats. Coordinates are in Blockbench pixels (same coordinate space as the model).
| Field | Type | Description |
|---|---|---|
x, y, z | float | Seat position in Blockbench pixels (local to entity). |
yawOffset | float | Passenger yaw rotation in degrees (default: 0). |
attachedGroup | String | Model group name the seat is attached to (may be null). The seat follows the group's animations. |
lockCamera | boolean | If true, the seated player's camera is locked (default: false). |
// In preInit() — full auto-registration
EriEntity.create("mymod", "elite_guard")
.model("mymod:entity/guard")
.texture("body", "mymod:textures/entity/guard_body")
.animation("idle", "mymod:entity/guard_idle")
.animation("walk", "mymod:entity/guard_walk")
.animation("attack", "mymod:entity/guard_attack")
.hitbox(0.7f, 2.0f)
.name("Elite Guard")
.health(40).attackDamage(6).armor(4).movementSpeed(0.28)
.hurtSound(SoundEvents.ENTITY_ZOMBIE_HURT)
.deathSound(SoundEvents.ENTITY_ZOMBIE_DEATH)
.aiPreset(AiPreset.HOSTILE_MELEE)
.ai(ai -> ai.swim().meleeAttack(1.1, false).wander(0.8).watchPlayers(16f).lookIdle().targetPlayers().hurtByTarget(true))
.drop(new ItemStack(Items.IRON_SWORD), 0.05f)
.drop(Items.IRON_INGOT, 0, 2, 0.4f)
.experienceDrop(5, 15)
.spawn(s -> s.type(EnumCreatureType.MONSTER).weight(8).group(1, 3).lightLevel(0, 7).dimension(0))
.spawnEgg(0x2C2C2C, 0xFFD700)
.onDeath((entity, src) -> MyMod.LOGGER.info("Guard killed by {}", src.getDamageType()))
.register();
// That's it! No manual EntityEntry, no manual RenderingRegistry.
PathfinderBuilder v2.0
Fluent builder to configure AI tasks for an EriEntity. Used inside
EriEntity.ai(Consumer<PathfinderBuilder>). Tasks are applied
in the order they are called.
Goal tasks
-
FluentPathfinderBuilder swim()Lets the entity swim when in water.
-
FluentPathfinderBuilder meleeAttack(double speed, boolean longMemory)Melee attack.
longMemorykeeps chasing the target even when out of sight. -
FluentPathfinderBuilder wander(double speed)Random movement when idle.
-
FluentPathfinderBuilder watchClosest(Class<EntityLivingBase> target, float distance)Entity watches the closest matching entity within range.
-
FluentPathfinderBuilder watchPlayers(float distance)Shortcut: watches the closest player.
-
FluentPathfinderBuilder lookIdle()Random look direction while idle.
-
FluentPathfinderBuilder panicOnHurt(double speed)Entity panics and flees when hurt.
-
FluentPathfinderBuilder leapAtTarget(float motionY)Entity leaps at its target with the given vertical motion.
-
FluentPathfinderBuilder avoidEntity(Class cls, float distance, double walkSpeed, double sprintSpeed)Avoids entities of the given class (flees at
walkSpeed, sprints atsprintSpeed). -
FluentPathfinderBuilder openDoors(boolean closeBehind)Entity can open doors.
closeBehindcloses them after passing through.
Target tasks
-
FluentPathfinderBuilder targetPlayers()Targets players within range.
-
FluentPathfinderBuilder targetClass(Class cls)Targets a specific entity class.
-
FluentPathfinderBuilder hurtByTarget(boolean callForHelp)Retaliates against the attacker.
callForHelpcalls allies of the same type.
Custom tasks
-
FluentPathfinderBuilder task(EntityAIBase ai)Adds a pre-instantiated custom goal task.
-
FluentPathfinderBuilder targetTask(EntityAIBase ai)Adds a pre-instantiated custom target task.
-
FluentPathfinderBuilder task(Function<EntityLiving, EntityAIBase> factory)Factory that builds the goal task from the entity instance.
-
FluentPathfinderBuilder targetTask(Function<EntityLiving, EntityAIBase> factory)Factory that builds the target task from the entity instance.
EriEntity.create("mymod", "guard")
.health(30).attackDamage(4)
.aiPreset(AiPreset.HOSTILE_MELEE)
.ai(ai -> ai
.swim()
.meleeAttack(1.0, false)
.wander(0.8)
.watchPlayers(12f)
.lookIdle()
.targetPlayers()
.hurtByTarget(true))
.register();
EriSpawner v2.0
Fluent builder for natural spawn rules. Used inside
EriEntity.spawn(Consumer<EriSpawner>). Rules are registered automatically
in Forge through EntityRegistry.addSpawn().
Builder methods
-
FluentEriSpawner type(EnumCreatureType type)Creature category:
MONSTER,CREATURE,AMBIENT,WATER_CREATURE. -
FluentEriSpawner weight(int weight)Spawn weight (higher = more frequent).
-
FluentEriSpawner group(int min, int max)Group size at spawn (random between min and max).
-
FluentEriSpawner dimension(int... ids)Allowed dimensions (0 = Overworld, -1 = Nether, 1 = End).
-
FluentEriSpawner biome(Biome... biomes)Allowed biomes for spawning.
-
FluentEriSpawner excludeBiome(Biome... biomes)Forbidden biomes for spawning.
-
FluentEriSpawner lightLevel(int min, int max)Light level range for spawning (0–15).
-
FluentEriSpawner heightRange(int min, int max)Y-altitude range for spawning.
-
FluentEriSpawner maxPerChunk(int max)Maximum number of this entity type per chunk.
-
FluentEriSpawner despawnDistance(int blocks)Distance (in blocks) beyond which the entity despawns.
EriEntity.create("mymod", "zombie_guard")
.health(20).attackDamage(3)
.aiPreset(AiPreset.HOSTILE_MELEE)
.spawn(s -> s
.type(EnumCreatureType.MONSTER)
.weight(10)
.group(2, 4)
.lightLevel(0, 7)
.dimension(0)
.maxPerChunk(8))
.register();
EriProjectile v2.0
Fluent builder to register custom projectiles with physics, combat, visuals and impact
callbacks. Automatic Forge registration through a 32-slot pool
(GeneratedProjectileSlot0…GeneratedProjectileSlot31): no need
to subclass EntityThrowable or manually register an EntityEntry.
Factory
-
FactoryEriProjectile create(String modId, String registryName)Creates a new projectile builder. Static method.
Physics
-
FluentEriProjectile velocity(float velocity)Initial projectile velocity.
-
FluentEriProjectile gravity(float gravity)Gravity applied each tick (0 = no gravity).
-
FluentEriProjectile inaccuracy(float inaccuracy)Random shot inaccuracy.
-
FluentEriProjectile lifetime(int ticks)Maximum projectile lifetime in ticks.
-
FluentEriProjectile piercing(int maxEntities)Projectile pierces up to N entities before being destroyed.
-
FluentEriProjectile bouncing(int maxBounces)Projectile bounces N times on blocks.
Combat
-
FluentEriProjectile damage(float damage)Damage dealt on impact.
-
FluentEriProjectile damageType(DamageSource type)Custom damage type (default: thrown).
-
FluentEriProjectile knockback(float knockback)Knockback strength on impact.
-
FluentEriProjectile setFire(int seconds)Sets the hit entity on fire for N seconds.
Visual
-
FluentEriProjectile texture(ResourceLocation texture)Projectile texture.
-
FluentEriProjectile renderAsItem(ItemStack stack)Renders the projectile as a 3D item instead of a flat texture.
-
FluentEriProjectile scale(float scale)Render scale.
-
FluentEriProjectile glowing(boolean glow)Projectile glows (fullbright).
-
FluentEriProjectile trailParticle(EnumParticleTypes particle)Particles left in the projectile's trail.
-
FluentEriProjectile trailColor(int argb)Trail color (ARGB format).
Callbacks & impact effects
-
FluentEriProjectile onHitEntity(BiConsumer<Entity, Entity> cb)Callback (projectile, target) when the projectile hits an entity.
-
FluentEriProjectile onHitBlock(BiConsumer<Entity, BlockPos> cb)Callback (projectile, pos) when the projectile hits a block.
-
FluentEriProjectile onExpire(Consumer<Entity> cb)Callback when the projectile expires (lifetime elapsed).
-
FluentEriProjectile explosionOnImpact(float power, boolean fire)Triggers an explosion on impact.
-
FluentEriProjectile potionOnImpact(PotionEffect effect, float radius)Potion splash within a given radius.
-
FluentEriProjectile aoeOnImpact(float radius, float damage)Area damage within a radius (no explosion, no block breaking).
Spawn egg & tracker
-
FluentEriProjectile spawnEgg(int primary, int secondary)Enables a spawn egg (0xRRGGBB format).
-
FluentEriProjectile tracker(int range, int freq, boolean velocity)Network tracking parameters (default: 64, 10, true).
Registration & firing
-
TerminalEriProjectile register()Registers the projectile in Forge through the slot pool and returns the builder (usable afterwards for
spawn()). -
ActionEntity spawn(World world, Entity shooter)Instantiates and fires the projectile from an entity (shooter's position and direction).
-
ActionEntity spawnAt(World world, double x, double y, double z, float yaw, float pitch)Fires the projectile from specific coordinates and direction.
EriProjectile fireball = EriProjectile.create("mymod", "fireball")
.velocity(1.8f)
.gravity(0.02f)
.damage(6.0f)
.setFire(3)
.knockback(0.5f)
.texture(new ResourceLocation("mymod", "textures/entity/fireball"))
.explosionOnImpact(2.0f, true)
.onHitEntity((proj, target) -> target.setFire(60))
.register();
// Fire from an entity
fireball.spawn(world, shooterEntity);
// Fire from coordinates
fireball.spawnAt(world, x, y, z, yaw, pitch);
EntityDataSync / @SyncParam v2.0
Automatic DataParameter management for entities via annotations. Declare a field
with @SyncParam, call EntityDataSync.register(this) in
entityInit(), then use type-safe get() / set() with
zero reflection in hot path. Automatic NBT persistence for fields marked
persistent = true.
@SyncParam
-
@interface SyncParamAnnotation placed on an entity field to include it in client/server synchronization.
-
boolean persistent() default trueIf
false, the field is not saved to NBT (lost on restart). -
String nbtKey() default ""Custom NBT key. If empty, the Java field name is used.
EntityDataSync
-
Staticvoid register(EntityLiving entity)Call in
entityInit()aftersuper.entityInit(). Scans@SyncParamfields via reflection (once per class) and creates the matchingDataParameters. -
StaticT get(EntityLiving entity, String fieldName)Reads the synchronized value of a field. Zero runtime reflection.
-
Staticvoid set(EntityLiving entity, String fieldName, Object value)Writes the synchronized value of a field (automatically propagates to the client).
-
Staticvoid writeNBT(EntityLiving entity, NBTTagCompound tag)Call in
writeEntityToNBT(). Serializes allpersistent = truefields. -
Staticvoid readNBT(EntityLiving entity, NBTTagCompound tag)Call in
readEntityFromNBT(). Deserializes allpersistent = truefields.
Supported types
boolean, int, float, String,
ItemStack, BlockPos, ITextComponent.
public class EntityGuard extends EntityMob {
@SyncParam private boolean isEnraged = false;
@SyncParam private int variant = 0;
@SyncParam(persistent = false) private float animBlend = 0f;
@SyncParam(nbtKey = "rage_level") private int rageLevel = 0;
@Override
protected void entityInit() {
super.entityInit();
EntityDataSync.register(this);
}
public void setEnraged(boolean v) { EntityDataSync.set(this, "isEnraged", v); }
public boolean isEnraged() { return EntityDataSync.get(this, "isEnraged"); }
@Override
public void writeEntityToNBT(NBTTagCompound tag) {
super.writeEntityToNBT(tag);
EntityDataSync.writeNBT(this, tag);
}
@Override
public void readEntityFromNBT(NBTTagCompound tag) {
super.readEntityFromNBT(tag);
EntityDataSync.readNBT(this, tag);
}
}
EriEntityBase v1.5.0
EriEntityBase is the base class to extend when you want an entity with
custom Java logic (method overrides, synced fields, custom NBT) while keeping the
EriEntity auto-configuration (stats, AI, sounds, drops, callbacks).
Without this class, EriAPI generates the entity in a pre-compiled slot from the
GeneratedEntitySlots pool (capped at 32). With .entityClass(MyEntity.class),
your class is registered directly in Forge — so you can override any method from
EntityMob / EntityLivingBase.
Base class to extend for a custom entity. Mirrors the logic of GeneratedEntity but allows subclassing.
Extension contract
- Extend
EriEntityBase - Provide a public constructor:
public MyEntity(World world) { super(world); } - If you use
@SyncParam, callEntityDataSync.register(this)inentityInit()aftersuper.entityInit() - You can override any method; call
superto keep EriEntity behaviours (sounds, drops, callbacks...)
Accessible protected fields
-
PROTECTEDEntityDefinition eriDefThe definition driving this entity. May be null if misconfigured.
-
PROTECTEDString eriDefIdCanonical id (
modId:registryName) used for NBT restoration.
Public accessors
-
PUBLICEntityDefinition getEriDef()Returns the
EntityDefinitiondriving this entity. -
PUBLICString getEriDefId()Returns the canonical id (
modId:registryName).
-
FLUENTEriEntity entityClass(Class<? extends EriEntityBase> clazz)Specifies a custom Java class. When this method is called, EriAPI registers your class directly in Forge instead of using a pre-compiled slot. The class must extend
EriEntityBaseand have apublic MyEntity(World world)constructor.
import fr.eri.eriapi.content.*;
import net.minecraft.world.World;
public class EntityBoss extends EriEntityBase {
@SyncParam public boolean isEnraged = false;
@SyncParam public int phase = 1;
public EntityBoss(World world) {
super(world);
}
@Override
protected void entityInit() {
super.entityInit();
EntityDataSync.register(this);
}
@Override
public void onLivingUpdate() {
super.onLivingUpdate();
// Switch to phase 2 below 50% HP
if (!world.isRemote && phase == 1 && getHealth() < getMaxHealth() * 0.5f) {
EntityDataSync.set(this, "phase", 2);
EntityDataSync.set(this, "isEnraged", true);
}
}
}
// Registration:
EriEntity.create("mymod", "boss")
.health(200).attackDamage(12).armor(10)
.aiPreset(AiPreset.BOSS)
.entityClass(EntityBoss.class) // ← custom class
.spawnEgg(0x8B0000, 0xFF4500)
.register();
Use EriEntityBase when you need synced fields
(@SyncParam), custom NBT, overrides of onLivingUpdate()
or other vanilla methods. For simple entities (stats + AI + drops), the builder callbacks
(onSpawn, onUpdate, onDeath, etc.) are enough — no need
to create a class.
EriProjectileBase v1.5.0
EriProjectileBase is the base class to extend when you want a projectile
with custom Java logic (special trajectory, custom impact effects, homing) while
keeping the EriProjectile auto-configuration (velocity, gravity, piercing, bouncing, explosion, AOE,
potion).
Base class to extend for a custom projectile.
Extension contract
- Extend
EriProjectileBase - Required constructor:
public MyProjectile(World world) { super(world); } - Optional constructor:
public MyProjectile(World world, EntityLivingBase thrower) - Call
superin overrides to keep piercing, bouncing, AOE, etc.
Accessible protected fields
-
PROTECTEDProjectileDefinition eriDefThe definition driving this projectile.
-
PROTECTEDString eriDefIdCanonical id (
modId:registryName) used for NBT logging. -
PROTECTEDint pierceCountNumber of entities this projectile has already pierced.
-
PROTECTEDint bounceCountNumber of blocks this projectile has already bounced off.
-
PROTECTEDint ticksAliveNumber of ticks since the projectile was spawned.
-
FLUENTEriProjectile projectileClass(Class<? extends EriProjectileBase> clazz)Specifies a custom Java class. When this method is called, EriAPI registers your class directly in Forge instead of using a pre-compiled slot from
GeneratedProjectileSlots.
import fr.eri.eriapi.content.*;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.world.World;
public class ProjectileMissile extends EriProjectileBase {
public ProjectileMissile(World world) { super(world); }
public ProjectileMissile(World world, EntityLivingBase thrower) {
super(world, thrower);
}
@Override
public void onUpdate() {
super.onUpdate();
// Homing: steer toward the nearest target every 5 ticks
if (!world.isRemote && ticksExisted % 5 == 0) {
EntityLivingBase target = findNearestTarget();
if (target != null) {
double dx = target.posX - posX;
double dy = target.posY + target.height * 0.5 - posY;
double dz = target.posZ - posZ;
double len = Math.sqrt(dx*dx + dy*dy + dz*dz);
double speed = 0.4;
motionX = dx / len * speed;
motionY = dy / len * speed;
motionZ = dz / len * speed;
}
}
}
private EntityLivingBase findNearestTarget() {
AxisAlignedBB aabb = new AxisAlignedBB(posX-16, posY-16, posZ-16,
posX+16, posY+16, posZ+16);
EntityLivingBase nearest = null;
double best = Double.MAX_VALUE;
for (EntityLivingBase e : world.getEntitiesWithinAABB(EntityLivingBase.class, aabb)) {
if (e == getThrower()) continue;
double d = e.getDistanceSq(this);
if (d < best) { best = d; nearest = e; }
}
return nearest;
}
}
// Registration:
EriProjectile.create("mymod", "missile")
.damage(8f).velocity(1.2f).gravity(0f).lifetime(200)
.explosionOnImpact(2.0f, false)
.projectileClass(ProjectileMissile.class) // ← custom class
.register();
EriItemBase v1.5.0
EriItemBase is the base class to extend when you want an item with
custom Java logic (persistent NBT, complex cooldowns, conditional rendering, special
interactions) while keeping the EriItem auto-configuration (tooltip, rarity, glow, stack size,
durability).
Use the .tool() and .armor() sub-builders rather than
.itemClass() — EriAPI generates specialized classes (GeneratedTool,
GeneratedArmor) that handle tool and armor mechanics correctly. Use
.itemClass() for simple items that need custom logic.
Base class to extend for a custom item.
Extension contract
- Extend
EriItemBase - Required constructor:
public MyItem() { super(); }(no arguments) - You can override
onItemRightClick,onItemUse,onUpdate, etc. - Call
superin overrides to keep the builder's.onRightClick()callback
Accessible protected fields
-
PROTECTEDItemDefinition eriDefThe definition driving this item. Contains tooltip, rarity, glow, etc.
-
FLUENTEriItem itemClass(Class<? extends EriItemBase> clazz)Specifies a custom Java class. When this method is called, EriAPI registers your class directly in Forge instead of
GeneratedItem.
import fr.eri.eriapi.content.*;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.*;
import net.minecraft.world.World;
public class ItemPortal extends EriItemBase {
public ItemPortal() { super(); }
@Override
public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand) {
ItemStack stack = player.getHeldItem(hand);
NBTTagCompound nbt = stack.getOrCreateSubCompound("Portal");
if (!nbt.hasKey("X")) {
// First click: memorize the position
nbt.setInteger("X", (int) player.posX);
nbt.setInteger("Y", (int) player.posY);
nbt.setInteger("Z", (int) player.posZ);
if (!world.isRemote) {
player.sendMessage(new TextComponentString("Position saved!"));
}
} else {
// Second click: teleport
player.setPositionAndUpdate(
nbt.getInteger("X"),
nbt.getInteger("Y"),
nbt.getInteger("Z"));
stack.removeSubCompound("Portal");
}
return new ActionResult<>(EnumActionResult.SUCCESS, stack);
}
}
// Registration:
EriItem.create("mymod", "portal")
.maxStackSize(1).glowing(true).rarity(EnumRarity.RARE)
.tooltip("&7Right-click: save the position")
.tooltip("&7Second right-click: teleport")
.itemClass(ItemPortal.class) // ← custom class
.register();