Skip to content

Changelog

Every AkumaLib release for Minecraft 1.20.1, newest first, as published on CurseForge.

2.5.0

Released 2026-09-24 · Download

Two large groups of additions since 2.4.0: professions (workstations, catchable creatures, traps, gathering) — the systems behind Cruise Cruise no Mi — and worldgen and dimensions (a cell grid, the overworld read from another dimension, travel between dimensions, the worldgen and fluid registers) — the ones behind Mine Mine no Mi: Sky Island. Both are written for every addon.

No signature was removed or changed: an addon built against 2.4.0 keeps compiling and keeps working. That is why this is a minor bump.

Requires Minecraft 1.20.1, Forge 47+ and Mine Mine no Mi 0.11.5. JEI and EMI are optional.

⚠️ The network protocol moves from 2 to 3: a client and a server must both run 2.5.0, or the connection is refused. Update both sides together.


New

Professions

  • Framework: a profession registry (AkumaProfessionRegistry), per-player XP and levels (AkumaProfessions, the ProfessionData capability), level curves in data (data/<ns>/professions/*.json: base_xp × L^exponent), synced to the client. Events: ProfessionXpEvent, ProfessionLevelUpEvent, ProfessionChangeEvent.
  • Modes (akumalib-server.toml, professionMode): Solo (every profession), Crew (one chosen profession per player; changing it resets everything), Auto (Solo in singleplayer and LAN, Crew on a dedicated server). /profession command (choose, mode, reset, get, set, addxp).
  • Professions screen (K, and a plank on Mine Mine no Mi's character screen): levels and XP; in Crew mode only the chosen profession, or the list with Choose buttons before any choice.
  • Profession pages: the screen wears Mine Mine no Mi's parchment and planks; clicking a profession opens its page in the base mod's book: level, perks (Profession.withPerk) and unlocks by level (ProfessionUnlocks: workstation recipes, profession-only seeds and blocks by their minimum level, and addon providers; synced from the server).
  • Mine Mine no Mi's character creator: in Crew mode, a fourth tab "Profession" (a client mixin): the arrows cycle the professions and Random, Finish applies the choice.
  • Icons: an item (withIcon) or a dedicated texture of any size (withIconTexture), shown in the screen and in the creator tab.
  • XP boosts: ProfessionXpBoostEffect, and exclusive effect groups (AkumaEffectGroups: one effect of a group at a time).

Workstations

  • WorkstationBlock + ProfessionRecipe (JSON, shapeless, a level and an XP each) + one shared menu and screen: recipes shown as silhouettes until the level is reached and until first made. ProfessionCraftEvent can change the result.
  • Recipes are not a crafting grid: up to 36 ingredients (ProfessionRecipe.MAX_INGREDIENTS), read as a bill of materials (getPortions(): each ingredient with its count). The workstation screen shows each line as have / need.
  • JEI and EMI: every workstation of every addon gets a category (the ingredients as a list with their counts, the result, the level, the XP), with no addon code.
  • Notes in JEI and EMI (RecipeViewers.note(items, text...)): an information page for an item no recipe explains (a part made at one station and fitted at another).
  • Profession drops in JEI and EMI (ProfessionDrops): what a gathering profession gets and from what (the rocks a miner breaks, the creatures a hunter catches), with each output's chance, count, level and XP; built on the server and synced before the recipes. LootSummary reads a loot table into outputs with their chances. CreatureHabitats says where a catchable creature lives (its biomes in the wild and in traps), shown under a "Where?" label; BiomeNames is the biome list it uses, for any other "where" line.

Catchable creatures

  • CatchableCreature / CatchableFlyingCreature: small creatures caught rather than killed, with variants (a rare one as a texture swap), a minimum render range, a capture that rolls capture/<entity> and fires CreatureCaptureEvent, and release from an item (a released creature is calm and kept).
  • Reactions to a player who does not sneak: FLEE, TAKE_OFF (runs 2–3 s, flies off and vanishes), ATTACK (a few hits, then the take-off).
  • Perches (CreaturePerch): on top of a block (a hornet on a flower, a lizard on a rock) or on a side face (a beetle on a trunk, drawn nose up by CatchableCreatureRenderer); still until disturbed or hurt; tall plants are sat on, not in; one creature to a block; breaking a perch (or the block above or below it) startles the creature, which flees.
  • Flying creatures stay airborne when spawned in the air; one that canLand() rests on solid ground now and then (never on water); they flee up into the air.
  • Surface spawner (data/<ns>/creature_spawns/*.json: entity, biomes, chance, group, max around the player, sky, altitude): tries every 5 s around each player, on the surface, through the entity's own spawn rules. Vanilla's natural spawning put most attempts underground.
  • Creature spawns take an optional time (day or night: a firefly), underground (a spot in the caves under the surface: a floor, room above, no sky), min_y and max_y (with the biomes under the base mod's sky islands, the islands alone).
  • Creature spawns keep to their dimension: the Overworld, unless "dimensions" names others ([] for every one). An entry with no biomes no longer spawns in the Nether.
  • Lures: CreatureSpawns.lure(level, player, rounds) runs a player's spawn entries a few rounds early and says how many landed. It cannot bring anything the place would not give on its own.

Traps

  • TrapBlock (+ one shared block entity type): set on the ground, it closes on a creature after a random delay, picked from data/<ns>/trapping/*.json (entity, weight, biomes, sky, which traps); a right-click collects it through the capture. Single use or reusable; optionally kept to one profession.
  • Trap catches keep to their dimension too: the Overworld unless "dimensions" says otherwise.
  • A trap remembers what it was made of: the stack it was placed from (getGear(), NBT included) is saved with it, handed to the capture, and given back when it is broken. Two hooks read it: delayFor(gear, random) and isSpentBy(player, gear).

Gathering

  • HangingFruitBlock: a fruit hanging under leaves that ripens (random ticks, bone meal) and grows back once picked; kept softly to a profession (others bruise half the fruit, practitioners sometimes pick two and earn XP). Hangs in generated trees through vanilla's attached_to_leaves decorator.
  • ProfessionBlockItem: a block item only a profession's practitioners can place (a farmer's sapling) — or one of several (or(profession, minLevel): "Only a Farmer or a Lumberjack can plant this.").
  • ProfessionCropBlock and ProfessionSeedsItem (both item classes take a minimum level): a crop with the fruit's rules (others spoil half the ripe harvests, practitioners sometimes reap one more and earn XP), and seeds only the profession sows.

Worldgen and dimensions

  • Worldgen registers on AkumaRegistry: DENSITY_FUNCTION_TYPES, BIOME_SOURCES, CHUNK_GENERATORS, STRUCTURE_TYPES, STRUCTURE_PLACEMENT_TYPES, STRUCTURE_PIECE_TYPES, FEATURES and PLACEMENT_MODIFIER_TYPES. They register codecs and types; the instances come from datapack registries. ⚠️ A structure whose piece type is not registered generates once and is lost on reload.
  • Fluids: FLUIDS and FLUID_TYPES on AkumaRegistry. A Fluid that is not a FlowingFluid never flows — a body of liquid that stays where it was placed (a sea with no container); the javadoc says why and what LiquidBlock would do instead.
  • AkumaCells: a deterministic grid of jittered cells, each of which may hold a round feature — "an island here, and nothing for a few thousand blocks". mask, cellAt, featureAt, and a filter every reader shares. The outline can be warped off a circle (off by default); blend mixes a per-cell value across overlapping features (weighted, no seam where two meet); a RadiusRule (withRadiusRule) lets a cell's radius depend on the cell. The nine-cell condition is stated in the javadoc as an inequality to check with your own numbers.
  • OverworldLookup: the overworld's biome and seed from anywhere, including another dimension's worldgen thread: is(x, z, tag), coverage(x, z, tag) (0 to 1, smoothed over a window wide enough to be a fade), seed().
  • AkumaDimensionTravel: send a body to another dimension with what it rides and who rides it, arriving with a chosen velocity (send); push an entity so the push sticks on the client, rider case included (launch); generate the arrival early (preload). The arrival chunk is held loaded across the transfer, for a boat or a mob as for a player.
  • AkumaJigsaw: add a placed feature or a structure template to another mod's jigsaw pool (a village's decorations) at server start, without replacing its files.

Currency

  • AkumaCurrency: read, give and take Belly and Extol through Mine Mine no Mi.

Doriki

  • AkumaStats: read and grant Mine Mine no Mi's Doriki, synced to the client. DorikiGainEffect: an effect that multiplies the Doriki earned in fights (NPC and player kills) while it lasts.
  • AkumaDevilFruits: which fruit an entity ate, whether it is a Zoan, whether it is transformed; end its abilities' cooldowns.
  • AkumaStats.runsOnCola/getCola/addCola: refill a cyborg's Cola as the base mod's Cola does, synced. SatietyEffect: an effect that slows hunger drain while it lasts.

Advancements

  • Triggers akumalib:profession_level, akumalib:profession_craft (item predicate on the result, NBT included), akumalib:creature_capture (creature, variant, tool, released).

Registry

  • AkumaRegistry: menu, block entity, recipe type and recipe serializer registers (and the worldgen and fluid registers above).

Fixes (within this cycle, never released)

  • A data-load crash (TrapCatches built before its Gson) that made every world fail to load.
  • The profession sync no longer throws for a player with no open connection (a player being disconnected, a game test's player).
  • A blank profession no longer reads as a finished one ("level 1, Max", a gold XP bar).
  • From the code review: a reusable trap waited no delay after a collection; requires_sky failed under leaves (now: nothing solid above); the spawner goes through Forge's spawn hooks; workstation ingredients are matched rather than first-fit; an interrupted take-off no longer leaves a creature floating.

2.4.0

Released 2026-09-18 · Download

Nine groups of additions and two fixes since 2.3.0. No signature was removed or changed: an addon built against 2.3.0 keeps compiling and keeps working. That is why this is a minor bump.

Requires Minecraft 1.20.1, Forge 47+ and Mine Mine no Mi 0.11.5.

⚠️ One fix changes how existing abilities behave: a charge force-stopped from outside (water, Seastone, handcuffs) now releases what its start put in the world. See Fixes below.


New

Targeting: three more AkumaTargeting helpers

AkumaTargeting already had firstInLine, firstInSight and inCone. It gains the three checks aimed abilities kept re-deriving by hand:

Method Answers
aimPoint(caster, range) where the caster is aiming: the hit location, or range blocks along the look on a miss
isEnemy(user, target) the one enemy check, through the base mod's own faction predicate, caster excluded
sizedRange(caster, base) a reach grown by the caster's own bulk, so a fixed range stays usable in a large morph

Pulls: AkumaMotion and PullProfile

Moving a body toward a point - a target dragged to a centre, a user lunging at a target, loot drawn to hand - as one call. A PullProfile states the speed curve (base, perBlock, max, boost), the vertical rule, whether the pull replaces or adds to the motion, and a minimum distance. The velocity always goes through AbilityHelper.setDeltaMovement, so a player's client cannot drop it.

Deferred work: AkumaTickQueue

One queue for work that has to wait for a later tick - typically a blow that must land at the end of the tick so the triggering hit's invulnerability frames do not swallow it.

Effect owners: EffectOwners

Who applied an effect to whom, for effects whose point is that relationship. Keyed on the target's UUID, so it survives the entity object being replaced while the effect lives on - a dimension change, a respawn, a chunk reload.

Charges: ChargeGuard, IChargeInterruptible, AkumaCharges.interrupt

  • ChargeGuard tells a completed charge from one stopped early, and gives the partial cooldown an early stop should cost. It reads the charge's own progress rather than counting ticks, so it agrees with the component under a changed TIME_PROGRESSION.
  • IChargeInterruptible: a charge whose start puts something in the world (a lock, a pose, a sphere) implements it to take that back when the charge is cut from outside.
  • AkumaCharges.interrupt(entity, ability): the one right way to cut a charge yourself - neither firing the ability nor leaking its start state.
  • DomainAbility.interruptCharge: a zone's charge can be cancelled from outside, taking down its sphere or putting back the blocks it had placed.

Zone sounds: ZoneSoundSet.intervalClip

A zone's charge sound was replayed every 18 ticks - the pace of the base mod's short Room sound - so any longer clip layered two or three copies of itself over the whole charge. A set can now declare its clip's length, and setZoneSounds replays it only once a play has finished. The cadence is taken at the slot's base pitch, since a lower pitch plays a clip for longer. A set without it keeps the old cadence.

Protected areas: AkumaProtection

The protected-area checks the base mod only runs on the caster and on block placement, for everything else an ability touches (a pulled target, a landing point, a container). Matches the base mod exactly: the server's global whitelist, ABILITY_GRIEFING and the mobGriefing gamerule.

Lines: AkumaLines

A line between two anchors - a rope, a thread, a tether - drawn on the client as real geometry. One packet instead of a particle per step, and an end anchored to an entity follows it every frame.

Failed presses: AkumaAbilityHelper.FAILED_PRESS_COOLDOWN

The cooldown an aimed ability charges when it finds nothing or is refused: never zero (a held key would sweep a room for free), never the full cooldown. GrabAbility.DEFAULT_WHIFF_COOLDOWN now points to it, at the same value.


Fixes

  • A force-stopped charge now cleans up after itself. AbilityHelper.emergencyStopAbility - reached by water, Seastone, handcuffs, nets and anything calling disableAbilities - skipped the charge's end event, which is where a charge takes back what its start put in the world. A zone left its sphere behind, a spreading zone's charge blocks became permanent, and a charging user stayed rooted. A mixin now runs AkumaCharges.interrupt first. StompAbility, OceanAbility and every DomainAbility implement the cleanup.
  • OceanAbility scans one surface per column and covers grass, instead of leaving holes in a meadow.

Documentation

  • New pages and sections for every addition above
  • ChargeGuard documented as preventive for most abilities, with the exact list of stops that reach a charge's end event
  • The awakened-Zoan smoke head anchor does not work on every partial morph, and why

2.3.0

Released 2026-09-14 · Download

One addition, three fixes and one deprecation since 2.2.0. No signature was removed: a renamed constant keeps its old name as a deprecated alias, so an addon built against 2.2.0 keeps compiling and keeps working. That is why this is a minor bump.

Requires Minecraft 1.20.1, Forge 47+ and Mine Mine no Mi 0.11.5.

⚠️ Two fixes change how existing abilities play: every InstantDashAbility now hits over the width it declares, and a RunningSmashAbility hits a target twice a second instead of every tick. See Fixes below.


New

Spreading zones are restored when their holder leaves

A SpreadingBlockAbility puts back the ground it converted from its own ticks, and those ticks stop the moment its holder is gone: a logout, a dimension change, a server shutdown. The ability reloaded afterwards is a fresh instance that knows none of the positions, so the converted blocks stayed in the world for good.

The static zone registry that allows a restore from outside existed, but the only thing draining it lived in Awaken Awaken no Mi. Any other addon extending the class left its blocks behind.

The library now ships SpreadingBlockCleanupHandler itself:

Event Restores
PlayerLoggedOutEvent the zones of the player leaving
EntityLeaveLevelEvent the zones of an entity changing dimension or being unloaded
ServerStoppingEvent every zone still active
  • You wire nothing. Extending SpreadingBlockAbility is enough.
  • Running alongside an addon's own copy is harmless. A zone leaves the registry when it restores, and each restore walks a snapshot of the registry, so whichever handler runs second finds nothing left to do. An addon can drop its copy on its next library bump rather than in lockstep.

Fixes

InstantDashAbility hits over GetWidth()

GetWidth() (default 2.5) was declared overridable and never read: the dash path was swept with the user's entity reach instead. An override changed nothing, and the width grew in creative mode and with any reach bonus.

⚠️ Every dash changes width:

Dash Before After
Ferret Dart (InoFruits) about 3, more in creative 1.5, the width it already declared
Totsushin (InoFruits) about 3 2.5
Hyozan: Kyousou (Awaken Awaken no Mi) about 3 2.5

The width no longer depends on the game mode or on reach bonuses.

RunningSmashAbility no longer hits every tick

Its hit tracker was cleared every tick while its hits clear the target's invulnerability frames, so nothing limited the rate: a target the knockback did not throw clear took the damage 20 times a second. The tracker now resets every 10 ticks, the window MoveAbility already uses, so a target in contact is hit about twice a second.

⚠️ A subclass that turns knockback off was the one hitting every tick, and loses the most. Awaken Awaken no Mi's Reverse Slow Smash, at its default damage, drops from 16 to 1.6 real HP per second against a target in contact.

The 10-tick window is shared by all targets: a target first hit on the last tick of a window can be hit once more on the next tick.

CraftingAbility shows a real message when materials are missing

Its default message was a developer placeholder, "A message is missing here please contact the dev to fix it.", shown to every player lacking the materials unless the subclass set its own. The default is now "You do not have the materials to use this ability." It stays overridable through the message field.


Deprecated

  • AkumaI18n.MESSAGE_MISSING: use MESSAGE_MISSING_MATERIALS. The old name is an alias of the new one, and its lang key akumalib.ability.missing_message stays mapped to the new text, so an addon built against 2.2.0 shows the right line rather than a raw key. Both go at the next major version.

Notes for addon developers

  • Bump your akumalib_version to 2.3.0 if you rely on the zone restore: below it, your mods.toml range would accept a library that restores nothing.
  • If you carried your own copy of SpreadingBlockCleanupHandler, delete it once you require 2.3.0. Keeping it is harmless, just redundant.
  • Check your dashes and running smashes in game. A dash declaring a narrow GetWidth() now needs a more precise aim; a running smash without knockback deals a tenth of what it did.
  • The distributable jar still comes from gradlew releaseJar, in build/release — never from build/libs.
  • Run the control build (gradlew build -PakumalibFromMaven in the consumer, after gradlew publishToMavenLocal here) before releasing anything that depends on this version.

Consumers

  • Awaken Awaken no Mi, in its next release, requires AkumaLib 2.3.0 and drops its own copy of the zone restore handler.

2.2.0

Released 2026-09-13 · Download

Six additions and one behaviour change since 2.1.0. No existing signature moved, so an addon built against 2.1.0 keeps compiling. That is why this is a minor bump.

Requires Minecraft 1.20.1, Forge 47+ and Mine Mine no Mi 0.11.5.

⚠️ One default changed: a TornadoEntity is no longer put out by rain. See Changes below.


New

AkumaLootInjection — addon fruits inside the base mod's own box pool

An addon fruit could never come out of the base mod's 5% tier cascade — the roll where a wooden box upgrades into an iron one — because that nested roll resolves through a code path that skips loot modifiers by design. Every addon fruit was therefore rarer than a base mod fruit of the same tier, and no number in a loot file could fix it.

The fruit is now added as an entry inside the pool mineminenomi:fruits, while the table is still being loaded:

  • Parity is weight 100, the figure every base mod fruit already carries. No share to compute and nothing to re-tune when a fruit is added.
  • The cascade works, because the upgraded roll draws from a pool that now holds the addon fruit.
  • The base mod's own guard applies: the entry carries FruitAlreadyExistsFunction, as theirs do.

registerFruitItem injects by default, into the box matching the fruit's tier, with an inLootBoxes overload to opt out. That removes a whole class of bug: a fruit registered with no loot entry exists, drops from commands and grants its abilities, and cannot be found in survival — with nothing logged. Two consumers had shipped exactly that.

AkumaLootInjection.addFruit(...) is public for anything registered another way. Each injected table logs one line at server start.

BlockHighlightManager — outline block positions through terrain, for one player

Draws a box around scattered block positions visible through walls, sent to a single player. The three obvious routes each fail: particles are depth-tested against terrain, glowing marker entities cost hundreds of real entities per player, and BlockOverlayManager shades a contiguous zone rather than single blocks.

BlockHighlightManager.show(player, groupId, positions, colour, ticks);
BlockHighlightManager.clear(player, groupId);
  • Positions travel in groups, one colour each, keyed by a UUID the caller owns.
  • A group counts itself down on the client: an ability showing something for twelve seconds sends one packet and never has to take it away.
  • ⚠️ Capped at 512 positions per group, enforced when writing and when reading.
  • Cleared on level unload, so highlights never survive into the next world.

GrabAbility.beginGrab(user, target) — a grab that is not a melee swing

GrabAbility only knew one way to catch somebody: open a reach window, then turn the next hit into the grab. A ranged grab — a tongue, a rope, a harpoon — decides its target when a projectile arrives, with no swing to intercept. beginGrab is that entry; the pull, the hold and the release run exactly as they do for a melee catch.

  • It applies the one-tick ANTI_KNOCKBACK the melee path applies. Without it, the impact that caught the target knocks them straight back out of the grab.
  • Server side only, and it refuses to start a second grab while one is running.

AkumaTargeting.inCone(caster, target, halfAngleDegrees)

The cone test two consumers were carrying as character-for-character copies. Written as a plain dot product against a cosine rather than delegated to TargetPredicate.testAdvancedInView(float), whose float is documented nowhere. A target standing exactly on the caster counts as inside the cone.

IAwakenZoanSmokeLayerHeadAnchor — the awakened-zoan smoke follows the model's head

A form implementing this marker has its smoke moved to the pivot of the morph model's head, read at render time, before its Y/Z offsets apply. On most morph models that pivot is where the neck leaves the body, so the offsets shrink to small corrections — and the scarf follows the model when it moves its head.

  • Only Y and Z follow the pivot, never X: some models shift the head group sideways and compensate in the cubes.
  • Opt-in: a form without the marker renders exactly as before.

AkumaRollTableModifier — an insertion mode

"mode": "append" (the default, and what every existing file already means) or "mode": "compete". compete inserts the rolled loot at the front of the list, for a container that keeps only its first match. An unrecognised value is a hard codec error rather than a silent fall back to append.

For Devil Fruit boxes you no longer need this at all — AkumaLootInjection handles them. It stays for chests and any container whose pool cannot be reached.


Changes

A tornado is no longer put out by rain

TornadoEntity was a copy of the base mod's Suna Suna no Mi sandstorm, and it inherited the sandstorm's rule: water and rain discard it. A generic funnel switching itself off when the weather turns is nonsense, so the rule is now two hooks:

Hook Default
isQuenchedByWater() true kept — water is the universal Devil Fruit weakness
isQuenchedByRain() false changed — override and return true for a sand funnel

⚠️ This changes existing consumers. A tornado subclass that overrides neither hook now survives rain where it did not before — AwakenAwakenNoMi's diamond funnel among them.


Fixes

  • compete could replace a higher-tier fruit with a lower one. The cascade pool is rolled before the same-tier pool, so its fruit heads the list, and a compete insert went in front of it: the player lost the upgrade, and nothing said so.

Notes for addon developers

  • Bump your akumalib_version to 2.2.0 so your mods.toml range refuses an older library rather than loading against one and crashing during registration.
  • ⚠️ If you shipped loot modifier files for Devil Fruit boxes, delete them. With injection on by default, keeping them makes a fruit appear in the pool and get inserted by the modifier.
  • The distributable jar still comes from gradlew releaseJar, in build/release — never from build/libs.
  • Run the control build (gradlew build -PakumalibFromMaven in the consumer, after gradlew publishToMavenLocal here) before releasing anything that depends on this version.

Consumers released alongside

  • Awaken Awaken no Mi 1.1.0 requires AkumaLib 2.2.0: its twelve new awakened forms place their smoke with the head anchor.

2.1.0

Released 2026-09-10 · Download

Six additions since 2.0.0. Everything here is additive — no existing signature moved, so an addon built against 2.0.0 keeps compiling. That is why this is a minor bump.

Requires Minecraft 1.20.1, Forge 47+ and Mine Mine no Mi 0.11.5.


New

MorphGates — a factory for requiresMorph conditions

Every ability locked to a form was writing the same use-condition by hand, and a form gate that is subtly wrong fails in the worst possible way: the ability is simply usable out of shape, which nothing logs and nobody notices until somebody tries it. One addon had eleven such copies. They are now one constant each.

MorphStats — a builder for a morph ability's attribute block

A Zoan form is mostly a wall of attribute modifiers — speed, armour, knockback resistance, step height, jump height, reach — and the base mod's own morphs write them out longhand. This builds the block instead, which makes two forms of the same fruit readable side by side and stops the modifier UUIDs being copy-pasted between them.

onlyWhile(...) adds a conditional block: a set of modifiers that applies only while some predicate holds, so a form that is faster in water or heavier on land is one declaration rather than a tick handler.

AkumaTargeting — the target-picking helpers

Consolidates the entity selection every ability was reimplementing, and — more usefully — documents which of them exclude the caster and which do not. That distinction had already produced two shipped bugs in a consumer, where a technique with a radius damaged the person who used it.

AmplifiedPresentation and the amplification bus

The pattern where a form amplifies its own fruit's kit — the base mod does it with Venom Demon and Shinokuni — has three axes: the numbers, the shape of the technique, and its name and icon. The third is pushed once by the form rather than polled by each technique, and that is the part a sixth ability in a kit forgets.

AmplificationBus.push(entity, amplified, ...abilities) is the whole of the wiring; IAmplifiableAbility and AmplifiedPresentation are what a technique implements to answer.

MarkerEffect — a base for effects that are read, not felt

An effect that exists only so something else can ask about it has no per-tick work, and saying so matters: returning true from isDurationEffectTick asks the game to tick it every single tick for nothing. It also settles the client-visibility override that, left at its default, makes an effect exist only on the server — so the overlay is never painted and the failure looks like the colour being wrong rather than like the effect being invisible.

OverlayEffect — a marker that also tints the body it is on

Adds the body tint on top of MarkerEffect. ⚠️ The alpha has to be spelled out: new Color(rgb) fills it in at 255, which paints the body a flat silhouette with no skin left underneath. Two calibrated values are documented on the class — around 100 for something the user wears and has to stay readable through, around 150 for a mark whose whole job is to be impossible to miss.

⚠️ The amplifier on such an effect is not always a potency. An instance carries a duration and an amplifier and nothing else, so it is the only place a per-application value can travel — one consumer puts a DyeColor id there, which is what makes sixteen colours one registered effect instead of sixteen.


Documentation

Three corrections, each of which had already cost somebody a wrong answer:

  • ModAttributes.STEP_HEIGHT is an alias, not an inert attribute. It resolves to Forge's own, so writing to it works and reading the Forge one back reflects it.
  • What does and does not exclude the caster, spelled out per helper rather than left to be discovered by damaging yourself.
  • Only one of MarkerEffect's four overrides was ever load-bearing — the other three restate defaults, and the class says which is which so a subclass knows what it is actually inheriting.

Notes for addon developers

  • Bump your akumalib_version to 2.1.0 so your mods.toml range refuses an older library rather than loading against one and crashing during registration.
  • gradlew build leaves a mapped jar in build/libs; the distributable comes from gradlew releaseJar, in build/release. The two are indistinguishable by eye, and shipping the mapped one produces a mod that loads and then fails on its first Minecraft call.
  • Consumers resolving AkumaLib from Maven need gradlew publishToMavenLocal in this checkout first.
  • ⚠️ Run the control build before releasing anything that depends on this. The composite build (includeBuild('../AkumaLib')) puts this project's classes on the classpath directly, which hides a resource missing from the packaged jar completely — a lang file, a texture, mods.toml itself. gradlew build -PakumalibFromMaven in the consumer is the only build that would catch it.

2.0.0

Released 2026-09-07 · Download

Ten changes since 1.0.0. Everything here is additive — no existing signature moved, so an addon built against 1.0.0 keeps compiling.

Requires Minecraft 1.20.1, Forge 47+ and Mine Mine no Mi 0.11.5.


New

GrabAbility — a base class for grabs

Mine Mine no Mi has exactly one worked grab, SpinningBrawlAbility, and roughly two thirds of it is plumbing rather than technique. This class owns that part: the four-way state machine behind the key, the reach window that turns the first entity struck into the grabbed target, the hold, and a cooldown that runs once however the grab ends — at full price only if the grab actually connected.

A subclass provides three things: what happens on the catch, on each tick of the hold, and on release. The throw is deliberately left out, so a technique can end by hurling its target or by dropping it.

BarrelRollAnimation — the missing roll axis

ForwardRollAnimation somersaults; the base mod's YAW_SPIN pirouettes. Neither is a body spinning about the axis it faces along. Typed on EntityModel rather than HumanoidModel, so it is not restricted to player-shaped models.

ProduceItemAbility — for fruits that make their own substance

The counterpart of CraftingAbility. Crafting is materials in, result out — a recipe, a cost, a failure message. A fruit producing salt, ink, honey or bone has none of those, because the user is the material. On CraftingAbility such an ability spends three abstract methods saying nothing and carries a charge component it never starts.

A subclass provides the stack, and optionally a sound. A full inventory and a holder with no inventory at all are both handled here.

BoomerangProjectile — an out-and-back projectile

The base mod's ChakramEntity has reusable return maths but an unusable base class: it extends AbstractArrow and hurts with a vanilla trident source, which carries no AbilityCore and therefore skips the ability damage pipeline entirely. This extends NuProjectileEntity like every other ability projectile and borrows only the steering.

registerFruitItem — group a fruit's abilities

registerItem was the wrong entry point for a Devil Fruit and nothing said so. The difference is one call, AbilityGroups.addFruitGroup, and it is what ties a fruit's moves together in the ability menu. Registered as a plain item, a fruit loads, drops, is edible and grants its abilities — ungrouped. Nothing logs it; it shows up only when a player opens the menu.

akumalib:roll_table — a loot modifier consumers drive from data

Addons need to add items to loot tables they do not own. Minecraft loot tables do not merge, so the only supported route is a Forge global loot modifier, which needs a codec registered in Java — and the codec is identical for every addon. Consumers now ship JSON only.


Fixed

The loot modifier never rolled its table

It had never worked. Every invocation logged Detected infinite loop in loot tables and contributed nothing. Found after about fifty wooden Devil Fruit boxes in a dev client produced no drop against a 0.208 chance — roughly 1 in 116,000 of being luck.

Opening a box could crash the server thread

StackOverflowError, from the modifier re-entering itself: every public getRandomItems* overload funnels into one private method that calls ForgeHooks.modifyLoot, so rolling a table from inside a modifier re-enters the modifier pipeline with the same context. The nested table is now rolled raw.


Notes for addon developers

  • gradlew build leaves a mapped jar in build/libs; the distributable comes from gradlew releaseJar, in build/release. The two are indistinguishable by eye, and shipping the mapped one produces a mod that loads and then fails on its first Minecraft call.
  • Consumers resolving AkumaLib from Maven need gradlew publishToMavenLocal in this checkout first.
  • Bump your akumalib_version to 2.0.0 so your mods.toml range refuses an older library rather than loading against one and crashing during registration.

1.0.0

Released 2026-09-03 · Download

AkumaLib is a shared ability framework for Mine Mine no Mi addons.

⚠️ This is a library, not a content mod. On its own it adds no fruits, no abilities and no items — nothing will change in your game. Install it only because another mod requires it.


For players

Install it alongside Mine Mine no Mi and the addon that asked for it. That's all there is to do.

Requires

  • Minecraft 1.20.1
  • Forge 47.4.18 or newer
  • Mine Mine no Mi

Known consumers


For addon developers

Mine Mine no Mi gives you Ability and a set of components. Everything above that — a zone that charges and expands, a wave that converts terrain, a passive that grants attribute bonuses under a condition — you would otherwise write yourself, slightly differently each time. AkumaLib is that layer, extracted once and shared: 65 classes, 17 ability base classes, 12 utilities.

Ability base classes

Class For
ZoneAbility a charged spherical zone applying effects continuously
SpreadingBlockAbility a zone that replaces ground blocks and restores them at the end
DomainAbility the shared parent of both, carrying the four-slot ZoneSoundSet
BlockWaveAbility a radial wave converting blocks outward — sphere, surface or terrain-following
ConditionalStatPassiveAbility a passive granting attribute bonuses while a condition holds
AuraAbility a passive aura ticking every nearby enemy
StompAbility · MoveAbility · InstantDashAbility charged stomp, continuous dash, teleport dash
BlockUseAbility · CraftingAbility · TransmutationAbility · GroundAbility smaller bases

Utilities

PropagationHelper (BFS propagation with easing), BlockPlacingHelper, AttributeBonusHelper (idempotent attribute modifiers), tooltip helpers, plus morph, animation and networking scaffolding.

Why the base classes carry so many warnings

Most exist because something shipped broken first. Extending the class gets you the fix for free:

  • SpreadingBlockAbility restores blocks through Block.updateFromNeighbourShapes — putting a remembered state back raw leaves a grass block flagged snowy under open sky.
  • Its zones are tracked statically and restored on PlayerLoggedOutEvent, because the tick that drains the restore queue stops the moment the holder leaves — and the blocks stayed permanently.
  • ConditionalStatPassiveAbility applies and removes idempotently rather than on transitions: addPermanentModifier persists in NBT, and a reconnect turned a conditional bonus into a permanent one.
  • It also hooks DisableComponent.addStartEvent, because a Devil Fruit passive is disabled the moment its holder gets wet, and PassiveAbility.tick() stops dispatching before it can clean up.

Adding it to your project

# gradle.properties
akumalib_version=1.0.0
// build.gradle
implementation fg.deobf("com.inovactio:akumalib:${akumalib_version}")
# mods.toml
[[dependencies.yourmod]]
    modId = "akumalib"
    mandatory = true
    versionRange = "[${akumalib_version},)"
    ordering = "AFTER"
    side = "BOTH"

⚠️ API stability

Publishing freezes the API. From 1.0.0 onward, a breaking change to the shared surface is a major version bump and forces a synchronised release of the library and every consumer. Your versionRange should exclude the next major.


All Rights Reserved.