Patchnote
Complete changelog of new features, changes, and bug fixes for each EriAPI release.
v1.8.5 May 26, 2026
Critical fix — cosmetic: ModelBakeEvent registered too late (3rd iteration)
True root cause of the pink/black cosmetic texture bug, identified from
client logs. The 1.8.3 and 1.8.4 fixes addressed plausible hypotheses (wrong GL texture
slot, stale MISSING_TEXTURE cache, GUI alpha cull) but the bug persisted
because no ArmorCosmeticBakedModel was ever installed in the model registry.
Exact cause: ArmorCosmeticManager.bootstrap() — called
from ClientProxy.init() (FMLInit) — registered the
BakeHandler after the initial ModelBakeEvent had
already been dispatched. That event fires during the resource-pack reload that happens
between FMLPreInit and FMLInit. As a result, our baked model never replaced the
default one, and vanilla rendered the model from the items atlas sprite — which
rejected any non-POT texture as MISSING_TEXTURE at stitch time.
-
New public entry point:
EriCosmetics.preInit()(alias forArmorCosmeticManager.registerBakeHandler()) — to be called fromFMLPreInitializationEventin consumer mods. -
Automatic preInit registration:
EriAPI.ClientProxy.preInit()now callsArmorCosmeticManager.registerBakeHandler(), registering the listener BEFORE the firstModelBakeEvent. Existing consumer mods need no change. -
Refactor of
bootstrap(): the method now only attaches theArmorCosmeticLayertoRenderPlayervariants. TheModelBakeEventregistration logic moved into the new publicregisterBakeHandler(). Backward-compatible —bootstrap()defensively callsregisterBakeHandler()so consumers that never migrate still get the handler registered (only too late for the initial bake). -
Late-registration detector: if
bootstrap()runs with at least one registered cosmetic but zeroswaps applied, a loudWARNis logged with the actionable fix.
No API breakage. Recommended upgrade for any mod using the cosmetic module — the
fix applies implicitly; calling EriCosmetics.preInit() remains optional and
documented.
v1.8.4 May 26, 2026
Fix — cosmetic: follow-up on the missing-texture fix (2nd iteration)
Version 1.8.3 fixed the texture unit (forcing GL_TEXTURE0 before every
bind) but the item-context rendering remained broken in practice — the pink/black
missing-texture pattern persisted in the inventory, hotbar, hand, ground, and GUI
contexts. 1.8.4 is a follow-up that adds three complementary measures: texture
preload at bake-time, alpha-test neutralisation plus explicit GL_TEXTURE_2D
enable in the TEISR, and a one-shot diagnostic log to identify the exact cause if
the bug were still present.
-
Texture preload on
ModelBakeEvent—ArmorCosmeticManager.BakeHandlernow forcesTextureManager.loadTexture(rl, new SimpleTexture(rl))for every texture of every cosmetic at resource-pack-load time. This guarantees that theSimpleTextureis uploaded to a valid GL ID before any rendering, and that any I/O error (corrupted PNG, missing resource) is logged immediately with the expected on-disk path. An additional check detects whether the loaded texture is theTextureUtil.MISSING_TEXTUREsentinel and emits an explicitWARN. -
ArmorCosmeticTEISR: addedGlStateManager.enableTexture2D()plusdisableAlpha()/enableAlpha()around the render to neutralise any pipeline-side disables (item glint, the alpha cull at 0.1 applied byrenderItemModelIntoGUI). -
BlockbenchRenderer.bindTexture: one-shot diagnostic log per texture per session showing theResourceLocation,glTextureId, and amissing=true/falseflag. No per-frame or per-face spam.
No public API change. Modifications are purely internal to the rendering and registration classes.
v1.8.3 May 26, 2026
Fix — cosmetic: missing-texture (pink/black) in the item context
Fixed the texture rendering of armor cosmetics
(ArmorCosmeticItem) in the item context — inventory, hotbar,
first person, third person, ground, item frame, GUI. The 3D Blockbench model
showed up correctly (geometry was fine) but the texture was replaced by the
characteristic pink/black missing-texture checkerboard. Player rendering through
ArmorCosmeticLayer was not affected.
Root cause — combination of two issues:
-
Wrong texture unit: the
RenderItem.renderItem(stack, IBakedModel)pipeline may leaveGL_TEXTURE1(used for the lightmap) as the active texture unit when delegating toTileEntityItemStackRenderer.renderByItem.TextureManager.bindTexture()binds the texture on the currently active unit — if that is slot 1, the fragment shader keeps sampling slot 0 (which still holds the item atlas or any stale residual texture), which is why the rendering was broken. -
Potentially stale static cache: the
BlockbenchRenderer.lastBoundTexturefield skipped the rebind when the same texture had already been bound, without accounting for the fact that another rendering system (player layer, another TESR, GUI) may have bound a different texture in between.
Solution — three cumulative measures:
-
ArmorCosmeticTEISR.renderByItemforcesGlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit)before any draw, and restores that slot on exit (defensive). -
BlockbenchRenderer.bindTextureforcessetActiveTexture(defaultTexUnit)right before everyTextureManager.bindTexture()call, as a safety net regardless of the call context. -
The
lastBoundTexture = nullreset at the start of every publicBlockbenchRendererentry point was already in place — confirmed correct to avoid stale caches across successive calls.
No public API change — no migration required for consumers. The fix is purely internal to the rendering classes.
v1.8.2 May 25, 2026
Feat — cosmetic: 3D Blockbench rendering in every item context
ArmorCosmeticItem instances now render their Blockbench model in 3D
in every display context: inventory, hotbar, first person (right
/ left hand), third person (right / left hand), on the ground, fixed (item frame),
head (worn on the head). Before this patch, only the player rendering via
ArmorCosmeticLayer drew the 3D model — the inventory and all
other views went through a 2D extruded sprite (item/generated) routed
through the item atlas. That forced square power-of-two textures and rejected
non-POT PNGs (black/pink missing-texture output).
The new pipeline relies on three pieces: ArmorCosmeticTEISR (a
TileEntityItemStackRenderer that draws the Blockbench geometry via
BlockbenchRenderer), ArmorCosmeticBakedModel (an empty
IBakedModel that flags isBuiltInRenderer() and
isGui3d() to true, and exposes per-context transforms
built from the JSON display block), and a ModelBakeEvent
handler in ArmorCosmeticManager that swaps every cosmetic's vanilla
baked model with an ArmorCosmeticBakedModel on every resource pack
reload. Textures are bound via TextureManager.bindTexture() (goes
through SimpleTexture, off-atlas), so any PNG dimension
works — POT not required, multi-texture supported, custom UVs
honored.
Feat — cosmetic: respects the Blockbench display field
The JSON model's display block (keys
thirdperson_righthand, thirdperson_lefthand,
firstperson_righthand, firstperson_lefthand,
head, gui, ground, fixed)
is converted into a vanilla ItemCameraTransforms and applied
automatically by RenderItem before the TEISR call. Translations in
Blockbench pixels (1/16 block) are divided by 16 to match the vanilla OpenGL
transform convention. Rotation in degrees and unit-less scale are passed through
as-is.
Feat — anim: AnimatedBlockModel.getDisplayTransforms()
AnimatedBlockModel now publicly exposes the per-context transform
map via getDisplayTransforms() (plus hasDisplayTransforms()
and getDisplayTransform(String) for targeted lookups). The map is
extracted by BlockbenchModelParser from the JSON display
field, with an identity fallback when the block is missing — existing models
remain backward compatible.
v1.8.1 May 25, 2026
Fix — cosmetic: Y-flip in ArmorCosmeticLayer
Fixed the Y-axis flip in ArmorCosmeticLayer.renderGroupAtBone —
Blockbench models now render right-side up on biped bones. The Blockbench
convention (Y+ up, X+ right) is now correctly converted to the MC entity
bone-local space (Y- up, ModelBiped.addBox convention) via a
GlStateManager.scale(-1, -1, 1) applied right after
bone.postRender(0.0625f). The two sign flips preserve the normal
orientation (winding order) without needing glFrontFace. Before
this fix, cosmetic models rendered upside-down, hanging below the bone.
The public API of the cosmetic module is unchanged — no
consumer-side migration required. The default offsets
(DEFAULT_BONE_OFFSETS) and the group-name-to-bone mapping table
(GROUP_NAME_TO_BONE) were already correct for the flipped space.
v1.8.0 May 25, 2026
New module — cosmetic (3D armor cosmetics)
New package fr.eri.eriapi.cosmetic for attaching a Blockbench 3D model
to any of the 4 vanilla armor slots (HEAD, CHEST,
LEGS, FEET). Cosmetic items inherit zero stats —
no damage reduction, no durability, no enchantability — they only serve as a
visual layer rendered on top of the player via a LayerRenderer<EntityPlayer>
attached to every RenderPlayer in the skinMap (default + slim).
Item mask = EriCosmetics.armor()
.modId("yourmod")
.registryName("barry_mask")
.displayName("Barry Mask")
.creativeTab(CreativeTabs.COMBAT)
.slot(EntityEquipmentSlot.HEAD)
.model(new ResourceLocation("yourmod", "barry_mask"))
.build();
The model automatically follows the vanilla biped bone matching the slot via
postRender(0.0625f):
HEAD → bipedHead, CHEST → bipedBody,
LEGS / FEET → bipedRightLeg — head tilt, body sneak,
arm swing and leg walking work natively without any custom rig.
Multi-bone convention — Blockbench group naming
A model may contain multiple root groups whose name determines
which vanilla bone they attach to (case-insensitive):
head, body, right_arm, left_arm,
right_leg, left_leg (variants without underscore are also
accepted). Any group not matching a bone is rendered on the slot's primary bone.
Lets you build a full cosmetic armor set that follows the entire vanilla biped.
Refactor — BlockbenchRenderer extracted from the TESR
The geometry code from AnimatedBlockTESR (AnimatedBlockModel
rendering, ModelGroup / ModelElement handling, texture atlas,
Blockbench transform application) is now exposed in the public class
fr.eri.eriapi.cosmetic.BlockbenchRenderer. It is reused by the
cosmetic module and by AnimatedBlockTESR (which now delegates
to renderModelWithOverrides()). The TESR keeps its IDLE optimization and
perf tracking — 100% backward compatible, no public API change
for existing users.
renderModel(AnimatedBlockModel)— full render of a model at rest.renderModel(model, AnimationPose)— render with applied pose.renderGroup(ModelGroup, ...)— render of a single group (used by multi-bone dispatch).renderModelWithOverrides(model, pose, textureOverrides)— signature preserving TESR semantics.
Documentation
New page cosmetic.html (FR + EN) covering the
full builder API, bone mapping, multi-bone convention, default offsets, the public
BlockbenchRenderer API and a complete troubleshooting section.
v1.7.2 May 13, 2026
Added — LineChart.yLabelDecimals(int)
The number of decimals displayed on Y-axis labels (and in the point hover tooltip)
is now configurable. Previously, the internal formatValue method was
static and hardcoded to 1 decimal, with no public setter —
making it impossible to display cent-precise prices or integer-only values without
a stray decimal.
LineChart chart = new LineChart()
.yLabelDecimals(2) // 12.34 instead of 12.3
.addDataset("Price", points);
Default: 1 (backward compatible). Setting 0 forces integer
display. Negative values are clamped to 0.
v1.6.8 May 07, 2026
Critical fix — EventBuilder.filter() overwrote previous filters
Each call to .filter(...) on an EventBuilder replaced
the previous filter outright instead of combining them. As a result, listeners
configured with multiple chained .filter(...) calls only honored
the last one, leaking events that should have been rejected by
earlier filters.
Real-world example: a PlayerTickEvent handler scoped to a specific
dimension, configured like this:
EriEvents.on(TickEvent.PlayerTickEvent.class)
.filter(e -> e.phase == TickEvent.Phase.END)
.filter(e -> e.player.world.provider.getDimension() == TARGET_DIM)
.filter(e -> !e.player.world.isRemote) // this filter OVERWROTE the previous ones
.handle(e -> applyEffect(e.player));
The dimension check was lost, and the effect was applied across every dimension on the server side.
Fixed: successive calls to filter(...) are now combined with
logical AND via Predicate.and(). All predicates must return
true for the handler to be invoked. The expected and natural behavior
is restored, with no impact on listeners that used a single filter.
v1.6.7 May 05, 2026
Fix — 3D animated models offset from their hitbox
Blockbench models rendered by AnimatedEntityRenderer appeared visually
shifted by half a block on X and Z compared to the entity hitbox.
Cause: Blockbench centers models on (8, 0, 8) in pixels (= 0.5, 0, 0.5 in GL units),
but the entity origin (posX/posY/posZ) is the bottom-center
of the bounding box. Without an offset, the model's (0,0,0) corner is rendered at the
hitbox center.
Fixed in doRender(): a GlStateManager.translate(-0.5f, 0.0f, -0.5f)
is now applied right after the yaw rotation to recenter the model. Yaw rotation now
happens around the model's actual vertical axis (its center) instead of an off-center
pivot. Cascade-fixes movement animations that looked buggy — they were
simply applied around the wrong pivot.
Fix — Hostile mobs targeting Creative / Spectator players
The HOSTILE_MELEE, HOSTILE_RANGED and BOSS presets
of EriEntity created EntityAINearestAttackableTarget tasks
without any predicate. Result: staff/admins in creative mode were targeted by mobs
like any survival player.
EriEntityBase,GeneratedEntityandPathfinderBuilder.targetPlayers()— all target tasks aimed atEntityPlayer.classnow use the 6-arg constructor with aPredicate<EntityPlayer>that rejects players inisCreative()orisSpectator().- The predicate is stored as a static singleton per class to avoid one lambda allocation per spawned entity — critical at 500-1000 concurrent players.
Perf — AnimatedEntityRenderer: per-texture draw call batching
The previous implementation called buffer.begin() + tessellator.draw()
per face. A model with 200 elements and 6 faces per element therefore
issued 1200 GPU draw calls per entity per frame — catastrophic
FPS drop with even a handful of visible entities.
Refactor of renderElement(): faces of the same element are now grouped by
texture into a single begin() / draw() pair. Most elements
only use one or two textures — the reduction is typically 6x to 12x
depending on the model. For a complex 200-element single-texture mob:
1200 draw calls -> 200.
- New internal methods:
resolveTexture(String) -> ResourceLocation(resolution without binding) andappendFaceVertices(buffer, ...)(appends a face's 4 vertices into an already-open buffer). - The old
drawFace()method was replaced — no public API change, only the internal pipeline. - Compatible with all existing animations (translation, rotation, scale, cumulativeRotation, texture overrides). Matrix transforms are unchanged; only the draw-flushing is batched.
100% backward compatible — no public API change.
v1.6.6 May 05, 2026
Fix — EriEntityBase: eriDef resolved before AI / attribute setup
The EntityLiving constructor calls applyEntityAttributes() and
initEntityAI() via super(world) before the
EriEntityBase subclass can assign this.eriDef. Result: mobs
spawned with no AI and vanilla default stats, regardless of what was declared via
EriEntity.create(...).
Restructured: the applyEntityAttributes() and initEntityAI()
overrides are now no-ops, and the constructor calls applyEriAttributes() +
setupEriAI() after resolving eriDef via DEF_BY_CLASS.
Subclasses of EriEntityBase (used for custom Java logic) now correctly
receive their stats, AI and hitbox.
v1.6.5 May 02, 2026
Java animations on the builder — .javaAnimation()
The EriAnimBlock and EriEntity builders now expose
.javaAnimation(String name, EriAnimJava anim) to register a Java animation
directly on the fluent builder — no more need to create a custom
TileEntity subclass nor to call addJavaAnimation() manually
in the constructor. Anims are stored in the BlockDefinition /
EntityDefinition and retrievable via the matching helpers.
EriAnimBlock.javaAnimation(name, anim)— appends to theAnimBlockDefinition.javaAnimationsmap.GeneratedBlock.createTileEntity()now callsconfigure()and loops to wire the anims onto the auto-generatedAnimatedBlockTileEntityGeneric.EriEntity.javaAnimation(name, anim)— appends to theEntityDefinition.javaAnimationsmap. Retrievable viaContentRegistry.getEntityDef(modId, name).getJavaAnimation("idle")in the entity constructor.- Coexists with
.animation()—.erianimfiles andEriAnimJavaclasses can be mixed on the same builder. - New helper
ContentRegistry.getEntityDef(modId, registryName)— direct lookup by name (previously you had to iterategetEntityDefs()).
Fix — AnimatedBlockTileEntityGeneric.configure() never called
Latent bug since v1.6.4: the no-arg constructor + configure() pattern of
AnimatedBlockTileEntityGeneric was never invoked — animated
blocks generated by EriAnimBlock without a custom .tileEntity(MyTE.class)
had no modelId, no animFileId, and no defaultAnimation.
Resolved in GeneratedBlock.createTileEntity(): configuration is now applied at
TE creation, alongside the Java anims declared on the builder.
100% backward compatible — existing .erianim files and
custom TEs continue to work unchanged.
v1.6.4 May 02, 2026
Java Animation API — write animations in pure Java
New API to write animations directly in Java, bypassing .erianim files.
Ideal for procedural animations (sin/cos oscillations, reactions to entity state, parametric movements)
impossible or tedious to express in JSON. Coexists with .erianim via the same
te.playAnimation("name") API.
EriAnimJava— abstract class to extend. Two methods:defineKeyframes(ctx)(declarative, cached) andcompute(ctx, pose)(procedural, per-frame).duration()required,endBehavior()optional.AnimContext— context withanimTick,partial,worldTick,target, helpersasEntity()/asTileEntity(), and DSLat(tick).group(name)....GroupPoseBuilder— fluent API accessible viapose.group("name").add*methods (additive on keyframes) andset*(replace).- Overlay —
te.playOverlay("name", anim)plays a second Java anim on top of the main one (overlay wins on touched groups). Client-local, never goes through packets. - Registration —
te.addJavaAnimation("walk", new WalkAnim())in the TileEntity constructor. Registry shared server+client.
See the full documentation in Animation System — Java Animation API.
No bug fixes — additions only, 100% backward compatible. Existing .erianim
files continue to work unchanged.
v1.6.1 April 28, 2026
ListRenderContext — Proportional vertical offset (drawTextProp)
New method ListRenderContext.drawTextProp(String, int offsetX, int offsetYPermil, int maxWidth, int color).
Unlike drawText() where offsetY is in raw screen pixels,
drawTextProp expresses the vertical offset in permil of the row height
(1/1000) — the layout stays correct at every window size.
Symptom before: a fixed -14 px offset that correctly placed the name above the center
at fullscreen (itemHeight ~96 px) pushed the text out of the row bounds in a smaller
window when the row was resized. drawTextProp(text, x, -150, w, color) places the
text ~15% of the row height above the center, regardless of the actual row height.
// BEFORE — offsetY in screen pixels (breaks in small windows)
ctx.drawText(name, textX, -14, textMaxW, 0xFFFFFFFF);
ctx.drawText(cost, textX, +14, textMaxW, 0xFF4ADE80);
ctx.drawText(status, statusOffsetX, -14, statusW + gap, 0xFFBB55FF);
// AFTER — offset in permil of the row height (correct at every resolution)
ctx.drawTextProp(name, textX, -150, textMaxW, 0xFFFFFFFF);
ctx.drawTextProp(cost, textX, +150, textMaxW, 0xFF4ADE80);
ctx.drawTextProp(status, statusOffsetX, -150, statusW + gap, 0xFFBB55FF);
The original drawText method is preserved — no breaking change.
v1.6.0 April 24, 2026
EriBlock — Block variants and entity callbacks
The EriBlock builder gains six new methods that cover most vanilla patterns
(logs, leaves, plants) and expose entity walk/collision callbacks.
No more custom BlockRotatedPillar or BlockLeaves subclasses:
a simple .logLike() or .leavesLike(sapling) is enough.
.renderLayer(BlockRenderLayer)— sets the client render layer (SOLID, CUTOUT, CUTOUT_MIPPED, TRANSLUCENT). Required for leaves, plants, glass..onEntityWalk(TriConsumer<World, BlockPos, Entity>)— low-level callback when an entity walks on top of the block..onEntityCollision(TriConsumer<World, BlockPos, Entity>)— callback when an entity passes through the block (useful for traps / toxic plants)..logLike()— generates aBlockRotatedPillar(AXIS property) for orientable logs..leavesLike(Block sapling)— generates a fullBlockLeaves(CHECK_DECAY + DECAYABLE, natural decay, 5% sapling drop). Forces CUTOUT_MIPPED automatically..plantLike()— decorative plant: reduced hitbox, no collision, CUTOUT. The blockstate must point toblock/cross.
TriConsumer — New functional interface
Java 8 does not provide a native TriConsumer<A, B, C>. EriAPI adds this interface
in fr.eri.eriapi.content.TriConsumer for 3-parameter callbacks (used by
onEntityWalk / onEntityCollision and available for your own builders).
Internal refactor — GeneratedBlockCommons
To avoid duplication between the 4 generated block variants (GeneratedBlock,
GeneratedLogBlock, GeneratedLeavesBlock, GeneratedPlantBlock),
shared logic (drops, hitbox, callbacks, TileEntity) has been extracted into
GeneratedBlockCommons. No visible API change for users.
Full example
// Custom tree log (automatic X/Y/Z axis)
Block ERINA_LOG = EriBlock.create("mymod", "erina_log")
.material(Material.WOOD)
.hardness(2.0f)
.harvestTool("axe", 0)
.soundType(SoundType.WOOD)
.logLike()
.register();
// Leaves that decay + 5% sapling drop
Block ERINA_LEAVES = EriBlock.create("mymod", "erina_leaves")
.material(Material.LEAVES)
.hardness(0.2f)
.soundType(SoundType.PLANT)
.leavesLike(ERINA_SAPLING)
.register();
// Toxic plant that damages on contact
Block TOXIC_PLANT = EriBlock.create("mymod", "toxic_plant")
.material(Material.PLANTS)
.hardness(0.0f)
.soundType(SoundType.PLANT)
.plantLike()
.onEntityCollision((world, pos, entity) -> {
if (entity instanceof EntityLivingBase && !world.isRemote) {
((EntityLivingBase) entity).addPotionEffect(
new PotionEffect(MobEffects.POISON, 60, 0)
);
}
})
.register();
v1.5.2 April 22, 2026
EriBlock — Custom hitboxes (.hitbox / .hitboxes)
EriBlock now supports custom collision and selection boxes directly in the builder,
without manually subclassing Block.
-
.hitbox(AxisAlignedBB)— Replaces the default full-block hitbox with a single custom box. Automatically setsisFullCubeandisOpaquetofalse. Unit:1.0 = 1 block = 16 pixels. -
.hitboxes(AxisAlignedBB...)— Defines multiple independent collision boxes. Each box is tested separately during entity collision viaaddCollisionBoxToList. The selection outline (hover highlight) shows the union of all boxes. Automatically setsisFullCubeandisOpaquetofalse.
// 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 — flat base + central pillar
EriBlock.create("mymod", "machine")
.material(Material.IRON)
.hardness(4.0f)
.hitboxes(
new AxisAlignedBB(0, 0, 0, 1, 0.25, 1),
new AxisAlignedBB(0.25, 0.25, 0.25, 0.75, 1, 0.75)
)
.register();
v1.5.1 April 18, 2026
Element-level animation (elementTracks)
.erianim.json animations can now target individual elements inside a
Blockbench group, not only the group as a whole. Each element has its own
rotationOrigin (pivot) defined in the model, and animations can apply
rotation, translation, scale, visibility and spin around that pivot.
Fully backward compatible: existing animations without elementTracks
render exactly as before.
- JSON format — New optional
"elementTracks"field alongside"tracks"in eachAnimationDef. Keys use the"groupName:elementIndex"format (e.g."body:2"). The structure of an element track is identical to a group track (rotation,rotate,translation,scale,visible,spin). - AnimationDef — New
getElementTracks()getter,hasElementTracks()helper, shortcutgetElementTrack(String groupName, int elementIndex), and static utilityelementTrackKey(String, int)to format the canonical key. - AnimationPose — New
elementPosesmap parallel to the existinggroupPoses, withgetElement(groupName, elementIndex)andgetOrCreateElement(...). The methodsreset(),copyFrom()andlerpToward()now include element poses. - AnimationController — The sampler iterates
elementTracksafter the group tracks and populates element poses via a factored helperpopulatePoseFromTrack(...). Animation-to-animation blending (START / END) also applies to element poses. - AnimatedBlockTESR / AnimatedEntityRenderer — Both renderers read the element pose and apply a local OpenGL matrix around each element's
rotationOriginbefore rendering its faces. Static JSON rotation is preserved and applied first; the animated pose is applied on top.
{
"formatVersion": 1,
"modelId": "mymod:block/turret",
"animations": {
"idle": {
"length": 40,
"loop": true,
"tracks": {
"base": { }
},
"elementTracks": {
"body:2": {
"rotation": [
{ "tick": 0, "value": [0, 0, 0] },
{ "tick": 20, "value": [0, 180, 0] },
{ "tick": 40, "value": [0, 360, 0] }
]
}
}
}
}
}
AnimationPose pose = controller.getCurrentPose(partialTicks);
AnimationPose.GroupPose bodyPose = pose.getGroup("body");
AnimationPose.GroupPose elemPose = pose.getElement("body", 2);
if (elemPose != null && !elemPose.visible) return;
Bug fixes
- No bug fixes in this release — additions only, 100% backward compatible.
v1.4.2 April 17, 2026
EriProjectile — custom projectile builder
New fluent builder for declaring custom projectiles (thrown entities) without Forge
boilerplate. Supports velocity, direct damage and area-of-effect (AOE) damage, physics
(gravity, drag), hit effects, and lifecycle callbacks. Projectiles are generated at
runtime via GeneratedProjectile and allocated a slot in
GeneratedProjectileSlots (pool of 32 static subclasses) — CleanRoom
compatible, no ASM.
- EriProjectile — Fluent builder:
model(),texture(),velocity(),gravity(),drag(),damage(),aoe(),effectOnHit(),onHitBlock(),onHitEntity(),onTick(),register(). - ProjectileDefinition — Configuration POJO containing all projectile parameters.
- GeneratedProjectile / GeneratedProjectileSlots — Entities generated dynamically from the definition, each in its own slot to respect Forge's "one Class per EntityEntry" constraint.
- ContentRegistry — Automatic registration of projectiles during the
RegistryEvent.Register<EntityEntry>event.
Bug fixes
- No bug fixes in this release — additions only.
v1.4.1 April 17, 2026
PathfinderBuilder — custom AI tasks for EriEntity
Fluent builder to configure an EriEntity's AI tasks beyond the presets
(PASSIVE, HOSTILE_MELEE, ...). Lets you add fine-grained
behaviors (swim, melee attack, wander, panic, open doors, leap at target, etc.) in
a single chain. Tasks are stored as factories
(Function<EntityLiving, EntityAIBase>) to defer instantiation
until the entity exists.
- API —
EriEntity.ai(ai -> ai.swim().meleeAttack(1.0, false).wander(1.0).watchPlayers(12f).lookIdle().targetPlayers().hurtByTarget(true)) - Goals available —
swim,meleeAttack,wander,watchClosest,watchPlayers,lookIdle,panicOnHurt,leapAtTarget,avoidEntity,openDoors,task(...)(custom). - Target goals available —
targetPlayers,targetClass,hurtByTarget,targetTask(...)(custom).
Bug fixes
- No bug fixes in this release — additions only.
v1.4.0 April 17, 2026
Full Entity Framework — stats, AI presets, drops, spawn rules, callbacks
Major extension of EriEntity (39 fluent methods) to declare a full entity
in a single chain: stats (health/armor/damage/speed/range), sounds, AI preset
(PASSIVE / HOSTILE_MELEE / HOSTILE_RANGED /
NEUTRAL / BOSS), drops, spawn rules (biomes/light/height/dimension),
and lifecycle callbacks (onSpawn, onDeath, onUpdate,
onAttack, onHurt, onInteract).
- EntityDefinition — Extended with all the new fields + inner class
EntityDrop. - AiPreset / EriSpawner — New configuration types for behavior and spawning.
- GeneratedEntity — Generic entity (extends
EntityMob) that applies the definition:applyEntityAttributes,initEntityAI(switches on preset),dropLoot,getExperiencePoints, sounds.onSpawnuses an NBT-persisted flag to avoid re-fires on chunk reload. - GeneratedEntitySlots — Pool of 32 static subclasses (
Slot0..Slot31) to respect Forge 1.12.2's "one Class per EntityEntry" constraint without ASM (CleanRoom compatible). - ContentRegistry.onRegisterEntities — Automatic registration (egg, tracker, spawn rules) and client-side wiring of
AnimatedEntityRenderer.
Bug fixes
- No bug fixes in this release — additions only.
v1.3.2 April 16, 2026
AnimatedItemController: animation playback for animated items
Items created with EriItem.animatedModel() can now play
.erianim.json animations in real time. The new
AnimatedItemController exposes a simple static API
(play / stop / isPlaying) and shares playback
state per modelId for every item using the same model.
- AnimatedItemController — New client-only controller in
fr.eri.eriapi.anim. Handles looping (EndBehavior LOOP), one-shot playback, and per-modelId state. - AnimatedBlockItemRenderer — The renderer now queries the controller every frame and feeds the pose to the TESR pipeline through a proxy TileEntity. No more static pose.
EriItem.create("mymod", "sword")
.animatedModel("mymod:item/sword")
.onRightClick(ctx -> {
if (ctx.world.isRemote) {
AnimatedItemController.play("mymod:item/sword", "swing");
}
})
.register();
Bug fixes
- Documentation
animation.html— removed the “no animation playback” warning and replaced it with documentation for the new controller.
v1.3.1 April 16, 2026
EriItem: .animatedModel() support for items with Blockbench 3D rendering
Items created via EriItem can now use an animated 3D Blockbench model
when held or displayed in inventory, using the new .animatedModel(String modelId) method.
Rendering is delegated to the same AnimatedBlockItemRenderer used for animated blocks.
- EriItem.animatedModel(String) — New fluent method that configures an item to use a Blockbench model in
"modid:item/name"format. - ContentRegistry — Automatically wires the TEISR (TileEntityItemStackRenderer) on animated items client-side, and registers the
builtin/entitymodel so Forge delegates rendering. - ItemDefinition — New
animatedModelIdfield to store the model identifier.
EriItem.create("eriniumfaction", "faction_sword")
.maxStackSize(1)
.rarity(EnumRarity.EPIC)
.animatedModel("eriniumfaction:item/faction_sword")
.register();
Bug fixes
- No bug fixes in this release — additions only.
v1.3.0 April 16, 2026
New: AnimatedEntityRenderer — animated rendering for entities
Extension of the Blockbench rendering pipeline to entities. The renderer uses exactly the same
pipeline as AnimatedBlockTESR (groups, elements, faces, UV, animated textures).
- AnimatedEntityRenderer<T> —
RenderLivingthat renders a Blockbench model with animations. Static factory methods for registration viaRenderingRegistry. - IAnimatedEntity — Interface that entities implement to provide their animation pose to the renderer. Methods
getCurrentPose(partialTicks)andgetAnimState().
New: EriEntity builder
Fluent builder to define custom entities with Blockbench model, textures, animations, hitbox,
passenger seats, and spawn eggs. Definitions are stored in ContentRegistry for
the mod to register in Forge manually.
- EriEntity — Fluent builder:
model(),texture(),animation(),hitbox(),seat()(3 overloads),spawnEgg(),creativeTab(),register(). - EntityDefinition — Configuration POJO containing all entity parameters.
- EntitySeat — Passenger seat definition with Blockbench position, yaw offset, attached group, and camera lock.
- ContentRegistry.registerEntity() /
getEntityDefs()— Storage and access for entity definitions.
Category field in AnimationFile
.erianim.json files now support an optional "category" field
("block", "item", or "entity"). This field is informational
and indicates which renderer type expects this animation file. Backwards compatible — defaults to "block".
Bug fixes
- No bug fixes in this release — additions only.
v1.2.1 March 26, 2026
Label: new scaleToFit() and wrapped() modes
- scaleToFit(boolean) — Instead of truncating text with "...", automatically reduces the text scale so it fits exactly within the component width. Mutually exclusive with
wrapped()(wrapped takes priority). - wrapped(boolean) — Text wraps to multiple lines at word boundaries. If the wrapped content exceeds the component height, vertical mouse scroll is enabled (scissor clipped). Mutually exclusive with
scaleToFit()(wrapped wins).
Tooltip: literal \n support from .lang files
Tooltip.render() now normalizes literal \\n (from .lang files) to real newlines before splitting. Both \n in Java strings and \\n from .lang files now work.
Dropdown: deferred rendering (always on top)
- The drop-down list is now rendered AFTER all other components (deferred rendering), ensuring it always appears on top.
- Click handling works even when the Dropdown is inside a
ScrollPanel. - New static methods:
Dropdown.renderDeferredDropdown(),Dropdown.handleDeferredClick(),Dropdown.resetState(). EriGuiScreencalls these methods automatically — no action needed for standard screens.
ItemStackRenderer: separate showDurability() from showCount()
New method showDurability(boolean) independent from showCount(boolean). Allows rendering the durability bar without the count text, or vice versa. Both default to true.
Bug fixes
- No bug fixes in this release — additions and improvements only.
v1.2.0 March 22, 2026
Overlay editor: auto-snap alignment system
The in-game overlay editor now features an auto-snap system. When dragging an overlay in edit mode, it automatically snaps to the edges and centers of other overlays as well as to screen reference points, enabling precise positioning without manually entering coordinates.
- Overlay edge snapping — The left, right, top and bottom edges of an overlay snap to the corresponding edges of neighboring overlays when within 5 design pixels. 10 snap types are supported: edge-to-edge and center-to-center alignment on both axes.
- Screen edge snapping — The overlay snaps to all four screen borders (x = 0, x = 1920, y = 0, y = 1080) when approaching within 5 design pixels.
- Screen center snapping — The overlay can align to the absolute center of the screen (960, 540), both horizontally and vertically.
- Center-to-center alignment — The horizontal and vertical centers of two overlays can align with each other for symmetric layouts.
- Cyan guide lines — When a snap is active, a semi-transparent cyan guide line appears on the snap axis to visualize the alignment.
- Red overlap indicator — If an overlay overlaps another during dragging, it turns red to signal the overlap.
- Hold Shift to bypass snap — Hold Shift while dragging to disable snapping and move the overlay freely pixel by pixel.
Bug fixes
- No bug fixes in this release — additions only.
v1.1.0 March 22, 2026
New module: Security Framework
Added the fr.eri.eriapi.security package — a complete server-side anti-duplication
and exploit prevention toolkit. Designed for high-player-count servers (500-1000 players).
- GuiRateLimiter — Per-player action throttling with configurable sliding window. Prevents container click spam.
- ItemIntegrityValidator — Snapshot/validate/rollback system to verify that the total item count doesn't change after a container operation. Automatically detects duplication attempts.
- ContainerLock — Synchronized per-key locks to prevent concurrent access to shared containers (e.g. faction chest).
- DupeAlertManager — Real-time alerts sent to all online operators via EriChat when a duplication attempt is detected.
New documentation: Network GUI
Added the Network GUI page documenting the client-server communication
system for GUI interactions: GuiNetworkHandler, IGuiDataReceiver, packet formats,
and complete examples.
GUI documentation expanded
- Added visual effect components:
GradientRectangle,Aurora,Starfield,ParticleSystem,SmokeFog - Network section moved to the dedicated Network GUI page
- Sidebar links updated with Security and Network
Bug fixes
- No bug fixes in this release — additions only.
v1.0.0 Initial release
Included modules
- GUI Framework — Complete UI toolkit in 1920x1080 design pixels with 30+ components, animations, sounds, and automatic scaling.
- Overlay HUD — Draggable overlay system with design-pixel positioning, anchoring, and in-game editor.
- Config System — Annotation-based configuration with auto-generated GUI, client-server sync, and validation.
- Command Framework — Command builder with auto-completion, typed arguments, permissions, cooldowns, and sub-commands.
- Content Builder — Create items, blocks, recipes and entities via fluent API without Forge boilerplate.
- Data & Storage — Persistent storage via annotated POJOs with automatic client sync via
@Sync. - Scheduler — Schedule delayed, repeating, chained, and async tasks.
- Chat Builder — Rich chat messages with colors, clicks, hover, and pagination.
- Capability Helpers — Simplified Forge capabilities via annotations with auto-attach and auto-sync.
- Event Simplifier — One-line Forge event subscriptions with filters, priority, and expiration.
- Keybinding Manager — Fluent keyboard shortcuts with combos, double-tap, hold, and contexts.
- Network GUI — Client-server communication for GUI interactions.