Content Builder

Create Minecraft items, blocks, recipes, and entities in a few lines of code — no boilerplate subclasses required.

Package: fr.eri.eriapi.content Fluent API Auto-registry

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.

The entire workflow — one chain
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().

Call .register() last

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.

Imports
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.

Your main mod class
@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
fr.eri.eriapi.content
Item builder

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.

Full example — a glowing rare gem
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

  • EriItem.create(String modid, String name)
    Static factory. Creates a new item builder. modid is your mod's ID; name is the registry name (lowercase, underscores). Must be called first.
    STATIC
  • maxStackSize(int size)
    Sets the maximum number of items that can stack in one inventory slot. Default: 64.
    FLUENT
  • maxDamage(int damage)
    Sets the maximum durability of the item. Items with durability automatically show the durability bar. Setting this implicitly sets maxStackSize to 1.
    FLUENT
  • rarity(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).
    FLUENT
  • creativeTab(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.
    FLUENT
  • glowing(boolean glow)
    When true, applies the enchantment glint visual effect to the item regardless of whether it is actually enchanted.
    FLUENT
  • tooltip(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.
    FLUENT
  • onRightClick(Consumer<ItemActionContext> action)
    Registers a callback fired when the player right-clicks while holding this item. Receives an ItemActionContext with the player, world, hand, and position.
    EVENT
  • onLeftClick(Consumer<ItemActionContext> action)
    Registers a callback fired when the player left-clicks (attacks) while holding this item. Receives an ItemActionContext.
    EVENT
  • food()
    Opens a FoodBuilder sub-builder for configuring food properties. Call .done() on the sub-builder to return here.
    SUB-BUILDER
  • tool()
    Opens a ToolBuilder sub-builder for configuring tool properties. Call .done() to return.
    SUB-BUILDER
  • armor()
    Opens an ArmorBuilder sub-builder for configuring armor properties. Call .done() to return.
    SUB-BUILDER
  • animatedModel(String modelId)
    Configures the item to use an animated 3D Blockbench model when held or in inventory. Rendering is delegated to an internal TileEntityItemStackRenderer. modelId uses the format "modid:item/name" (e.g. "eriniumfaction:item/faction_sword"). The Blockbench JSON file must be at assets/{modid}/models/item/{name}.json.
    FLUENT
  • register()
    Finalizes the item and submits it to EriAPI's ContentRegistry for Forge registration. Must be the last call in every chain.
    TERMINAL
Storing the item reference

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.

Java — Minimal declaration
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:

JSON — models/item/ruby.json (create manually)
{
    "parent": "item/generated",
    "textures": {
        "layer0": "mymod:items/ruby"
    }
}
Held items (tools, swords...)

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
Java — Declaring an animated 3D item
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();
Java — Controlling playback from any client-side code
// 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 resolution

The 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
fr.eri.eriapi.content
Sub-builder of EriItem

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.

Custom food item with regeneration effect
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

  • hunger(int amount)
    How many hunger points are restored when eaten. Each full shank icon = 2 points; a full hunger bar = 20 points.
    FLUENT
  • saturation(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.
    FLUENT
  • alwaysEdible(boolean value)
    When true, the item can be eaten even when the player's hunger bar is full (like golden apples). Default: false.
    FLUENT
  • effect(PotionEffect effect, float probability)
    Applies a potion effect when the food is eaten. probability is a float from 0.0 (never) to 1.0 (always). Call multiple times for multiple effects.
    FLUENT
  • done()
    Closes the FoodBuilder and returns the parent EriItem builder so you can continue chaining.
    RETURN

ToolBuilder

ToolBuilder
fr.eri.eriapi.content
Sub-builder of EriItem

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.

Custom ruby sword
EriItem.create("modid", "ruby_sword")
    .tool()
        .type(ToolType.SWORD)
        .damage(8f)
        .durability(1500)
        .speed(1.6f)
        .done()
    .register();

ToolType enum values

Value Description
ToolType.SWORDMelee weapon — increases attack damage, sweeping
ToolType.PICKAXEMines stone-type blocks faster, required harvest tool
ToolType.AXEChops wood faster, deals extra damage on first hit
ToolType.SHOVELDigs dirt/gravel faster
ToolType.HOETills farmland

Methods

  • type(ToolType type)
    Determines which kind of tool behavior is applied. See table above.
    FLUENT
  • damage(float attackDamage)
    The amount of attack damage dealt. A stone sword deals 5.0; a diamond sword deals 7.0.
    FLUENT
  • durability(int uses)
    How many uses before the item breaks. A diamond sword has 1561 uses; an iron sword has 251.
    FLUENT
  • speed(float attackSpeed)
    The attack speed modifier. Higher values mean faster attack cooldown recovery. A sword is ~1.6; an axe is ~0.9.
    FLUENT
  • done()
    Closes the ToolBuilder and returns the parent EriItem builder.
    RETURN

ArmorBuilder

ArmorBuilder
fr.eri.eriapi.content
Sub-builder of EriItem

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.

Custom ruby chestplate
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.HEADHelmet (head)3
ArmorSlot.CHESTChestplate (torso)8
ArmorSlot.LEGSLeggings (legs)6
ArmorSlot.FEETBoots (feet)3

Methods

  • slot(ArmorSlot slot)
    Which armor slot this item equips into. See table above.
    FLUENT
  • protection(int points)
    Number of armor points granted. Each full shield icon in the HUD = 2 points; maximum total from all four pieces is 20.
    FLUENT
  • toughness(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.
    FLUENT
  • materialName(String name)
    The base name used to look up the armor layer textures. For "ruby", Minecraft will look for textures/models/armor/ruby_layer_1.png and ruby_layer_2.png.
    FLUENT
  • done()
    Closes the ArmorBuilder and returns the parent EriItem builder.
    RETURN

EriBlock

EriBlock
fr.eri.eriapi.content
Block builder

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.

Full example — a mineable ore block
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

  • EriBlock.create(String modid, String name)
    Static factory. Creates a new block builder. Must be called first.
    STATIC
  • material(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.
    FLUENT
  • hardness(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.0f for unbreakable.
    FLUENT
  • resistance(float value)
    Explosion resistance. Stone is 10.0, obsidian is 6000.0. Generally set this higher than hardness.
    FLUENT
  • harvestTool(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.
    FLUENT
  • soundType(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.
    FLUENT
  • drops(Item item, int min, int max)
    Sets what the block drops when broken. min and max define the random drop count range. For a block that drops itself, omit this method.
    FLUENT
  • creativeTab(CreativeTabs tab)
    Places the block's item form in a creative mode tab.
    FLUENT
  • onBreak(Consumer<BlockActionContext> action)
    Registers a callback fired when a player breaks this block. Receives a BlockActionContext with the world, position, and player.
    EVENT
  • onPlace(Consumer<BlockActionContext> action)
    Registers a callback fired when a player places this block. The callback is triggered in onBlockPlacedBy, which guarantees that ctx.getPlayer() always returns the player who placed the block (never null).
    EVENT
  • onWalk(Consumer<BlockActionContext> action)
    Registers a callback fired every tick an entity walks on top of this block.
    EVENT
  • hitbox(AxisAlignedBB box)
    Sets a single custom hitbox (selection + collision). Replaces the default full-block hitbox. Automatically sets isFullCube and isOpaque to false. Coordinates are in blocks: 1.0 = 1 block = 16 pixels. Example: new AxisAlignedBB(0, 0, 0, 1, 0.5, 1) = half-block height.
    FLUENT
  • hitboxes(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 isFullCube and isOpaque to false. Recommended: keep under 6-8 boxes to avoid TPS impact on busy servers.
    FLUENT
  • renderLayer(BlockRenderLayer layer)
    Sets the client render layer for the block. Used for translucent, alpha-tested, or transparent blocks. Common values:
    • SOLID (default) — opaque full block
    • CUTOUT — binary alpha (plants, grates)
    • CUTOUT_MIPPED — binary alpha with mipmaps (leaves)
    • TRANSLUCENT — continuous alpha transparency (stained glass, ice)
    FLUENT
  • onEntityWalk(TriConsumer<World, BlockPos, Entity> action)
    Callback invoked every tick an entity walks on top of the block (equivalent to Forge's onEntityWalk). Unlike onWalk(Consumer<BlockActionContext>), this variant gives direct access to the world, position, and entity - useful for movement effects or zone damage.
    FLUENT
  • onEntityCollision(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.
    FLUENT
  • logLike()
    Turns the block into a tree log (BlockRotatedPillar) with the AXIS property (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 variants axis=x, axis=y, axis=z.
    FLUENT
  • leavesLike(Block sapling)
    Turns the block into tree leaves (BlockLeaves) with the vanilla CHECK_DECAY and DECAYABLE properties. Leaves automatically decay when disconnected from a log. Forces the CUTOUT_MIPPED render layer. The sapling argument is dropped with 5% probability during natural decay (may be null to disable).
    Overload: leavesLike() is equivalent to leavesLike(null).
    FLUENT
  • plantLike()
    Turns the block into a decorative plant:
    • Reduced hitbox (0.15, 0, 0.15 to 0.85, 0.9, 0.85)
    • No collision (entities pass through)
    • Automatic CUTOUT render layer
    • isOpaque=false, isFullCube=false
    The blockstate JSON should use the block/cross model for the X-shaped display (like vanilla flowers).
    FLUENT
  • register()
    Finalizes the block and submits it to ContentRegistry for Forge registration. Must be the last call.
    TERMINAL
Java — Custom hitboxes
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();
Block items are registered automatically

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.

Java — Minimal declaration (shared by both cases)
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.jsoncreate manually
  • src/main/resources/assets/mymod/models/block/my_block.jsoncreate manually
  • src/main/resources/assets/mymod/models/item/my_block.jsoncreate manually (for the inventory item)
JSON — blockstates/my_block.json (create manually)
{
    "variants": {
        "normal": { "model": "mymod:my_block" }
    }
}
JSON — models/block/my_block.json (create manually)
{
    "parent": "block/cube_all",
    "textures": {
        "all": "mymod:blocks/my_block"
    }
}
JSON — models/item/my_block.json (create manually)
{
    "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.

JSON — models/block/my_block.json (create manually)
{
    "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.png
  • src/main/resources/assets/mymod/textures/blocks/my_block_bottom.png
  • src/main/resources/assets/mymod/textures/blocks/my_block_side.png
Rule of thumb

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

  1. Model the block in Blockbench (project type Java Block/Item).
  2. Export the model to JSON at assets/modid/models/block/name.json.
  3. Create the animation file assets/modid/animations/block/name.erianim.json.
  4. Register the block in Java through EriAnimBlock.create().

Required files

  • assets/modid/models/block/name.json — exported Blockbench model
  • assets/modid/animations/block/name.erianim.json — animation file
  • All textures referenced from the Blockbench JSON
EriAnimBlock
fr.eri.eriapi.anim.EriAnimBlock
Builder

Fluent builder for creating a 3D animated block with model and animations.

  • EriAnimBlock create(String modid, String name)
    Creates a new animated block builder.
    STATIC
  • EriAnimBlock material(Material mat)
    Block material (Material.IRON, Material.WOOD, etc.).
    FLUENT
  • EriAnimBlock hardness(float hardness)
    Mining time.
    FLUENT
  • EriAnimBlock resistance(float resistance)
    Explosion resistance.
    FLUENT
  • EriAnimBlock animation(String animFileId)
    Required. ID of the .erianim.json file in the format "modid:path/name" (without the extension). Resolves to assets/modid/animations/path/name.erianim.json.
    FLUENT
  • EriAnimBlock tileEntity(Class<?> teClass)
    Custom TileEntity (optional). Must extend AnimatedBlockTileEntity and provide a public no-arg constructor. If omitted, EriAPI uses AnimatedBlockTileEntityGeneric (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.
    FLUENT
  • EriAnimBlock onRightClick(Consumer<BlockActionContext> handler)
    Callback fired when the player right-clicks the block.
    EVENT
  • Block register()
    Finalizes and registers the block + the associated TileEntity + TESR. Returns the Block instance.
    TERMINAL
Java — Play an animation on right-click
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.

AnimatedBlockTileEntity
fr.eri.eriapi.anim.AnimatedBlockTileEntity
Extend

Base class to extend for custom behavior. The 3 methods below are designed to be overridden.

Overridable methods (protected)

  • void 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.
    OVERRIDE
  • void onAnimationComplete(String animName)
    Called when an animation finishes (reaches the last keyframe in ONCE / HOLD / HIDE, or after one full loop for LOOP_N). Ideal for chaining animations.
    OVERRIDE
  • void 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.
    OVERRIDE

Public utilities (call these)

  • void playAnimation(String animName)
    Plays the named animation with the EndBehavior defined in the JSON (default).
    PUBLIC
  • void playAnimation(String animName, EndBehavior endBehaviorOverride)
    Plays the animation forcing a custom EndBehavior (ONCE, LOOP, HOLD, HIDE, LOOP_N).
    PUBLIC
  • void pauseAnimation()
    Pauses the animation at the current frame (synced client+server).
    PUBLIC
  • void resumeAnimation()
    Resumes a paused animation.
    PUBLIC
  • void resetAnimation()
    Returns to the initial pose (smooth interpolation).
    PUBLIC
  • void resetAnimation(boolean instant)
    Returns to the initial pose; instant=true skips interpolation (immediate snap).
    PUBLIC
  • void stopAnimation()
    Stops the current animation (stays at current frame without returning to base pose).
    PUBLIC
  • void setTexture(String target, String value)
    Replaces a model texture at runtime. target is the texture key from the Blockbench JSON, value is the new texture path (e.g. "mymod:blocks/core_active"). Synced to client.
    PUBLIC
  • void clearTexture(String target)
    Removes the texture override for target (back to the original texture).
    PUBLIC
  • void clearAllTextures()
    Clears all texture overrides at once.
    PUBLIC
  • boolean isAnimating()
    Returns true if an animation is currently playing.
    PUBLIC
  • String getCurrentAnimation()
    Name of the current animation, or null if none is active.
    PUBLIC
  • float getAnimationProgress()
    Progress of the current animation, from 0.0 to 1.0.
    PUBLIC
  • void setBlockYRotation(float rotation)
    Block rotation around the Y axis (degrees). Synced to client.
    PUBLIC
Java — Custom TileEntity with animation callbacks
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();
Advanced animations

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
fr.eri.eriapi.content
Recipe builder

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.

EriRecipe.shaped()
Shaped crafting

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.

3x3 ruby block from 9 rubies
EriRecipe.shaped("modid", "ruby_block")
    .pattern("RRR", "RRR", "RRR")
    .key('R', myRubyItem)
    .result(myRubyBlock, 1)
    .register();

Methods

  • EriRecipe.shaped(String modid, String name)
    Static factory. Creates a shaped recipe builder. name is the recipe's registry name.
    STATIC
  • pattern(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().
    FLUENT
  • key(char symbol, Item item)
    Maps a character used in the pattern to an ingredient item. Call once per unique character. Also accepts Block or EriItem / EriBlock references.
    FLUENT
  • result(Item item, int count)
    The item produced by this recipe and how many copies. Also accepts Block, EriItem, or EriBlock references.
    FLUENT
  • register()
    Submits the recipe to Forge's recipe registry. Must be the last call.
    TERMINAL
EriRecipe.shapeless()
Shapeless crafting

A shapeless recipe can be crafted by placing the ingredients anywhere in the grid — order and position do not matter.

Uncraft a ruby block back into 9 rubies
EriRecipe.shapeless("modid", "ruby_from_block")
    .ingredient(myRubyBlock)
    .result(myRubyItem, 9)
    .register();

Methods

  • EriRecipe.shapeless(String modid, String name)
    Static factory. Creates a shapeless recipe builder.
    STATIC
  • ingredient(Item item)
    Adds a required ingredient. Call multiple times to add more. Also accepts Block, EriItem, or EriBlock.
    FLUENT
  • result(Item item, int count)
    The item produced and how many copies.
    FLUENT
  • register()
    Submits the recipe to Forge's recipe registry.
    TERMINAL
EriRecipe.smelting()
Furnace smelting

A smelting recipe defines what a furnace produces when a given input item is cooked.

Smelt ruby ore into a ruby
EriRecipe.smelting("modid", "ruby_smelt")
    .input(myRubyOre)
    .result(myRubyItem)
    .xp(1.0f)
    .register();

Methods

  • EriRecipe.smelting(String modid, String name)
    Static factory. Creates a smelting recipe builder.
    STATIC
  • input(Item item)
    The item that goes into the furnace input slot. Also accepts Block, EriItem, or EriBlock.
    FLUENT
  • result(Item item)
    The item produced by smelting. Always produces exactly one copy.
    FLUENT
  • xp(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.
    FLUENT
  • register()
    Submits the smelting recipe to Forge.
    TERMINAL

ContentRegistry

ContentRegistry
fr.eri.eriapi.content
Auto-managed

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
Register during preInit — not later

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

ItemActionContext & BlockActionContext
fr.eri.eriapi.content
Callback parameter

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()EntityPlayerThe player who interacted with the item
getWorld()WorldThe world in which the interaction occurred
getHand()EnumHandMAIN_HAND or OFF_HAND
getStack()ItemStackThe ItemStack the player was holding
getHitPosition()BlockPosThe block position the player was looking at, or null if targeting air
isRemote()booleantrue 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()EntityPlayerThe player who broke the block (may be null if broken by explosion or other means)
getWorld()WorldThe world containing the block
getPos()BlockPosThe position of the block that was broken
getState()IBlockStateThe block state at the moment of breaking
isRemote()booleantrue on the client side
Using context in a callback
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();
Using BlockActionContext
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.

Full ruby content module
@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.

Auto-registration

Since v1.4.0, EriEntity.register() automatically registers the entity in Forge through a pool of 32 pre-compiled slots (GeneratedEntitySlot0GeneratedEntitySlot31). 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

ClassPackageRole
EriEntityfr.eri.eriapi.contentFluent builder for defining an entity
EntityDefinitionfr.eri.eriapi.contentPOJO containing the entity configuration
EntitySeatfr.eri.eriapi.contentPassenger seat definition
AnimatedEntityRendererfr.eri.eriapi.animEntity renderer using Blockbench models
IAnimatedEntityfr.eri.eriapi.animInterface for entities with animation

EriEntity builder methods

  • EriEntity create(String modId, String registryName)
    Creates a new entity builder. Static method.
    Factory
  • EriEntity model(String modelId)
    Sets the Blockbench model (e.g. "mymod:entity/monster").
    Fluent
  • EriEntity texture(String key, String path)
    Maps a model texture key to a ResourceLocation path.
    Fluent
  • EriEntity animation(String name, String animId)
    Maps an animation name (e.g. "idle", "walk") to an .erianim animation ID.
    Fluent
  • EriEntity hitbox(float width, float height)
    Sets the hitbox dimensions (default: 0.6 x 1.8).
    Fluent
  • EriEntity seat(float x, float y, float z)
    Adds a simple passenger seat at the given Blockbench pixel coordinates.
    Fluent
  • EriEntity seat(float x, float y, float z, String attachedGroup)
    Adds a seat attached to a model group (follows the group's animations).
    Fluent
  • EriEntity seat(float x, float y, float z, float yawOffset, String attachedGroup, boolean lockCamera)
    Full seat configuration with yaw rotation, attached group, and camera lock.
    Fluent
  • EriEntity spawnEgg(int primary, int secondary)
    Enables a spawn egg with primary and secondary colors (0xRRGGBB format).
    Fluent
  • EriEntity creativeTab(String tabName)
    Sets the creative tab for the spawn egg.
    Fluent
  • EriEntity 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).
    Terminal

Advanced methods (v1.4.0)

  • EriEntity name(String displayName)
    Entity display name.
    Fluent
  • EriEntity health(double health)
    Max HP (default: 20.0).
    Fluent
  • EriEntity armor(double armor)
    Armor value (default: 0).
    Fluent
  • EriEntity armorToughness(double toughness)
    Armor toughness (default: 0).
    Fluent
  • EriEntity attackDamage(double damage)
    Melee damage (default: 2.0).
    Fluent
  • EriEntity movementSpeed(double speed)
    Movement speed (default: 0.25).
    Fluent
  • EriEntity followRange(double range)
    Target detection range (default: 16).
    Fluent
  • EriEntity knockbackResistance(double resist)
    Knockback resistance, 0.0–1.0 (default: 0).
    Fluent
  • EriEntity immuneToFire()
    Entity is immune to fire.
    Fluent
  • EriEntity immuneToKnockback()
    Entity is immune to knockback.
    Fluent
  • EriEntity canSwim(boolean swim)
    Sets whether the entity can swim (default: true).
    Fluent
  • EriEntity experienceDrop(int min, int max)
    XP dropped on death (random value between min and max).
    Fluent
  • EriEntity ambientSound(SoundEvent sound)
    Ambient sound.
    Fluent
  • EriEntity hurtSound(SoundEvent sound)
    Hurt sound.
    Fluent
  • EriEntity deathSound(SoundEvent sound)
    Death sound.
    Fluent
  • EriEntity stepSound(SoundEvent sound)
    Step sound.
    Fluent
  • EriEntity ambientSoundInterval(int ticks)
    Interval between ambient sounds (default: 80 ticks).
    Fluent
  • EriEntity aiPreset(AiPreset preset)
    Predefined AI behaviour. Values: PASSIVE, HOSTILE_MELEE, HOSTILE_RANGED, NEUTRAL, BOSS.
    Fluent
  • EriEntity ai(Consumer<PathfinderBuilder> config)
    Custom AI configuration via PathfinderBuilder (see dedicated section below).
    Fluent
  • EriEntity drop(ItemStack stack, float chance)
    Item drop on death (chance: 0.0–1.0).
    Fluent
  • EriEntity drop(Item item, int min, int max, float chance)
    Random drop between min and max items.
    Fluent
  • EriEntity rareDrop(ItemStack stack, float chance)
    Rare drop (chance divided by looting level).
    Fluent
  • EriEntity spawn(Consumer<EriSpawner> config)
    Natural spawn rules (see EriSpawner section).
    Fluent
  • EriEntity tracker(int range, int freq, boolean velocity)
    Network tracking parameters (default: 64, 3, true).
    Fluent
  • EriEntity onSpawn(Consumer<EntityLivingBase> cb)
    Callback called at initial spawn.
    Fluent
  • EriEntity onDeath(BiConsumer<EntityLivingBase, DamageSource> cb)
    Death callback.
    Fluent
  • EriEntity onUpdate(Consumer<EntityLivingBase> cb)
    Callback every tick (onLivingUpdate).
    Fluent
  • EriEntity onAttack(BiConsumer<EntityLivingBase, Entity> cb)
    Callback when the entity attacks.
    Fluent
  • EriEntity onHurt(BiConsumer<EntityLivingBase, DamageSource> cb)
    Callback when the entity takes damage.
    Fluent
  • EriEntity onInteract(BiConsumer<EntityLivingBase, EntityPlayer> cb)
    Callback when a player right-clicks on the entity.
    Fluent

AnimatedEntityRenderer

The entity renderer that displays a Blockbench model with animations. The rendering pipeline (groups, elements, faces, UV) is identical to AnimatedBlockTESR.

  • IRenderFactory<T> factory(String modelId)
    Creates a factory for RenderingRegistry.registerEntityRenderingHandler().
    Factory
  • IRenderFactory<T> factory(String modelId, float shadowSize)
    Factory with custom shadow size.
    Factory

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 null if 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).

FieldTypeDescription
x, y, zfloatSeat position in Blockbench pixels (local to entity).
yawOffsetfloatPassenger yaw rotation in degrees (default: 0).
attachedGroupStringModel group name the seat is attached to (may be null). The seat follows the group's animations.
lockCamerabooleanIf true, the seated player's camera is locked (default: false).
Java — Full EriEntity example (v1.4.0)
// 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

  • PathfinderBuilder swim()
    Lets the entity swim when in water.
    Fluent
  • PathfinderBuilder meleeAttack(double speed, boolean longMemory)
    Melee attack. longMemory keeps chasing the target even when out of sight.
    Fluent
  • PathfinderBuilder wander(double speed)
    Random movement when idle.
    Fluent
  • PathfinderBuilder watchClosest(Class<EntityLivingBase> target, float distance)
    Entity watches the closest matching entity within range.
    Fluent
  • PathfinderBuilder watchPlayers(float distance)
    Shortcut: watches the closest player.
    Fluent
  • PathfinderBuilder lookIdle()
    Random look direction while idle.
    Fluent
  • PathfinderBuilder panicOnHurt(double speed)
    Entity panics and flees when hurt.
    Fluent
  • PathfinderBuilder leapAtTarget(float motionY)
    Entity leaps at its target with the given vertical motion.
    Fluent
  • PathfinderBuilder avoidEntity(Class cls, float distance, double walkSpeed, double sprintSpeed)
    Avoids entities of the given class (flees at walkSpeed, sprints at sprintSpeed).
    Fluent
  • PathfinderBuilder openDoors(boolean closeBehind)
    Entity can open doors. closeBehind closes them after passing through.
    Fluent

Target tasks

  • PathfinderBuilder targetPlayers()
    Targets players within range.
    Fluent
  • PathfinderBuilder targetClass(Class cls)
    Targets a specific entity class.
    Fluent
  • PathfinderBuilder hurtByTarget(boolean callForHelp)
    Retaliates against the attacker. callForHelp calls allies of the same type.
    Fluent

Custom tasks

  • PathfinderBuilder task(EntityAIBase ai)
    Adds a pre-instantiated custom goal task.
    Fluent
  • PathfinderBuilder targetTask(EntityAIBase ai)
    Adds a pre-instantiated custom target task.
    Fluent
  • PathfinderBuilder task(Function<EntityLiving, EntityAIBase> factory)
    Factory that builds the goal task from the entity instance.
    Fluent
  • PathfinderBuilder targetTask(Function<EntityLiving, EntityAIBase> factory)
    Factory that builds the target task from the entity instance.
    Fluent
Java — PathfinderBuilder example
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

  • EriSpawner type(EnumCreatureType type)
    Creature category: MONSTER, CREATURE, AMBIENT, WATER_CREATURE.
    Fluent
  • EriSpawner weight(int weight)
    Spawn weight (higher = more frequent).
    Fluent
  • EriSpawner group(int min, int max)
    Group size at spawn (random between min and max).
    Fluent
  • EriSpawner dimension(int... ids)
    Allowed dimensions (0 = Overworld, -1 = Nether, 1 = End).
    Fluent
  • EriSpawner biome(Biome... biomes)
    Allowed biomes for spawning.
    Fluent
  • EriSpawner excludeBiome(Biome... biomes)
    Forbidden biomes for spawning.
    Fluent
  • EriSpawner lightLevel(int min, int max)
    Light level range for spawning (0–15).
    Fluent
  • EriSpawner heightRange(int min, int max)
    Y-altitude range for spawning.
    Fluent
  • EriSpawner maxPerChunk(int max)
    Maximum number of this entity type per chunk.
    Fluent
  • EriSpawner despawnDistance(int blocks)
    Distance (in blocks) beyond which the entity despawns.
    Fluent
Java — EriSpawner example
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 (GeneratedProjectileSlot0GeneratedProjectileSlot31): no need to subclass EntityThrowable or manually register an EntityEntry.

Factory

  • EriProjectile create(String modId, String registryName)
    Creates a new projectile builder. Static method.
    Factory

Physics

  • EriProjectile velocity(float velocity)
    Initial projectile velocity.
    Fluent
  • EriProjectile gravity(float gravity)
    Gravity applied each tick (0 = no gravity).
    Fluent
  • EriProjectile inaccuracy(float inaccuracy)
    Random shot inaccuracy.
    Fluent
  • EriProjectile lifetime(int ticks)
    Maximum projectile lifetime in ticks.
    Fluent
  • EriProjectile piercing(int maxEntities)
    Projectile pierces up to N entities before being destroyed.
    Fluent
  • EriProjectile bouncing(int maxBounces)
    Projectile bounces N times on blocks.
    Fluent

Combat

  • EriProjectile damage(float damage)
    Damage dealt on impact.
    Fluent
  • EriProjectile damageType(DamageSource type)
    Custom damage type (default: thrown).
    Fluent
  • EriProjectile knockback(float knockback)
    Knockback strength on impact.
    Fluent
  • EriProjectile setFire(int seconds)
    Sets the hit entity on fire for N seconds.
    Fluent

Visual

  • EriProjectile texture(ResourceLocation texture)
    Projectile texture.
    Fluent
  • EriProjectile renderAsItem(ItemStack stack)
    Renders the projectile as a 3D item instead of a flat texture.
    Fluent
  • EriProjectile scale(float scale)
    Render scale.
    Fluent
  • EriProjectile glowing(boolean glow)
    Projectile glows (fullbright).
    Fluent
  • EriProjectile trailParticle(EnumParticleTypes particle)
    Particles left in the projectile's trail.
    Fluent
  • EriProjectile trailColor(int argb)
    Trail color (ARGB format).
    Fluent

Callbacks & impact effects

  • EriProjectile onHitEntity(BiConsumer<Entity, Entity> cb)
    Callback (projectile, target) when the projectile hits an entity.
    Fluent
  • EriProjectile onHitBlock(BiConsumer<Entity, BlockPos> cb)
    Callback (projectile, pos) when the projectile hits a block.
    Fluent
  • EriProjectile onExpire(Consumer<Entity> cb)
    Callback when the projectile expires (lifetime elapsed).
    Fluent
  • EriProjectile explosionOnImpact(float power, boolean fire)
    Triggers an explosion on impact.
    Fluent
  • EriProjectile potionOnImpact(PotionEffect effect, float radius)
    Potion splash within a given radius.
    Fluent
  • EriProjectile aoeOnImpact(float radius, float damage)
    Area damage within a radius (no explosion, no block breaking).
    Fluent

Spawn egg & tracker

  • EriProjectile spawnEgg(int primary, int secondary)
    Enables a spawn egg (0xRRGGBB format).
    Fluent
  • EriProjectile tracker(int range, int freq, boolean velocity)
    Network tracking parameters (default: 64, 10, true).
    Fluent

Registration & firing

  • EriProjectile register()
    Registers the projectile in Forge through the slot pool and returns the builder (usable afterwards for spawn()).
    Terminal
  • Entity spawn(World world, Entity shooter)
    Instantiates and fires the projectile from an entity (shooter's position and direction).
    Action
  • Entity spawnAt(World world, double x, double y, double z, float yaw, float pitch)
    Fires the projectile from specific coordinates and direction.
    Action
Java — EriProjectile example
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 SyncParam
    Annotation placed on an entity field to include it in client/server synchronization.
  • boolean persistent() default true
    If 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

  • void register(EntityLiving entity)
    Call in entityInit() after super.entityInit(). Scans @SyncParam fields via reflection (once per class) and creates the matching DataParameters.
    Static
  • T get(EntityLiving entity, String fieldName)
    Reads the synchronized value of a field. Zero runtime reflection.
    Static
  • void set(EntityLiving entity, String fieldName, Object value)
    Writes the synchronized value of a field (automatically propagates to the client).
    Static
  • void writeNBT(EntityLiving entity, NBTTagCompound tag)
    Call in writeEntityToNBT(). Serializes all persistent = true fields.
    Static
  • void readNBT(EntityLiving entity, NBTTagCompound tag)
    Call in readEntityFromNBT(). Deserializes all persistent = true fields.
    Static

Supported types

boolean, int, float, String, ItemStack, BlockPos, ITextComponent.

Java — Full @SyncParam example
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.

EriEntityBase
fr.eri.eriapi.content.EriEntityBase
Extends EntityMob

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, call EntityDataSync.register(this) in entityInit() after super.entityInit()
  • You can override any method; call super to keep EriEntity behaviours (sounds, drops, callbacks...)

Accessible protected fields

  • EntityDefinition eriDef
    The definition driving this entity. May be null if misconfigured.
    PROTECTED
  • String eriDefId
    Canonical id (modId:registryName) used for NBT restoration.
    PROTECTED

Public accessors

  • EntityDefinition getEriDef()
    Returns the EntityDefinition driving this entity.
    PUBLIC
  • String getEriDefId()
    Returns the canonical id (modId:registryName).
    PUBLIC
EriEntity.entityClass()
fr.eri.eriapi.content.EriEntity
Builder
  • EriEntity 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 EriEntityBase and have a public MyEntity(World world) constructor.
    FLUENT
Java — Boss with synced @SyncParam field
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();
When to use EriEntityBase?

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).

EriProjectileBase
fr.eri.eriapi.content.EriProjectileBase
Extends EntityThrowable

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 super in overrides to keep piercing, bouncing, AOE, etc.

Accessible protected fields

  • ProjectileDefinition eriDef
    The definition driving this projectile.
    PROTECTED
  • String eriDefId
    Canonical id (modId:registryName) used for NBT logging.
    PROTECTED
  • int pierceCount
    Number of entities this projectile has already pierced.
    PROTECTED
  • int bounceCount
    Number of blocks this projectile has already bounced off.
    PROTECTED
  • int ticksAlive
    Number of ticks since the projectile was spawned.
    PROTECTED
EriProjectile.projectileClass()
fr.eri.eriapi.content.EriProjectile
Builder
  • EriProjectile 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.
    FLUENT
Java — Homing missile
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).

For tools / armor

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.

EriItemBase
fr.eri.eriapi.content.EriItemBase
Extends Item

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 super in overrides to keep the builder's .onRightClick() callback

Accessible protected fields

  • ItemDefinition eriDef
    The definition driving this item. Contains tooltip, rarity, glow, etc.
    PROTECTED
EriItem.itemClass()
fr.eri.eriapi.content.EriItem
Builder
  • EriItem 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.
    FLUENT
Java — Portal stone with custom NBT
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();