
Overgrown's Apoli
A data-driven power system that's build for custom abilities, mechanics, and entire origin systems from JSON alone.
Список изменений
Added
- Per-face UV, including models that mix box UV and per-face UV cube by cube. Previously every face of a per-face cube was given the north face's UV, so anything not authored with Box UV checked was mis-textured.
- Warnings instead of silence for duplicate bone names, unknown
parentreferences, bone parent cycles, anduv_rotation(Bedrock format 1.21.0+), which is still unsupported.
If you built a model against the old behaviour: to attach geometry to a body part, build it around that part's pivot in Bedrock coordinates —
head/hat/bodyat[0, 24, 0], arms at[±5, 22, 0], legs at[±1.9, 12, 0]. Everything else is now placed exactly where Blockbench shows it, relative to the player's feet.
apoli:suppress_powerandapoli:unsuppress_powertake arrays.powerandsourceeach accept a single identifier or a list, so one action replaces a stack of them inside anapoli:and. The plural spellingspowers/sourceswork as aliases. Existing single-string packs parse unchanged.
Suppression is source-counted — a power stays off until every source that suppressed it is lifted — which is what makes multi-source arrays safe to combine.
Changed
-
Fabric builds moved onto the refreshed templates; only the Fabric API version had drifted.
-
Version alignment with Origins 1.9.2. No Apoli behaviour change.
-
NeoForge toolchain updated to ModDevGradle 2.0.143 and NeoForge 21.1.248.
Fixed
-
Per-cube rotation was discarded. Bedrock lets each cube carry its own
pivotandrotationindependently of its bone. Neither was read, so every rotated cube drew axis-aligned — a part modelled at 45° came out flat. Models built with rotated detail cubes looked like they had exploded. -
Bones named after body parts were teleported onto the vanilla skeleton. A bone called
headwas moved to the vanilla head pivot,bodyto the model origin, the arms to(±5, 2, 0)— so a model authored standing on the Blockbench floor had its parts flung apart, and its height never matched the player. The vanilla animation is now applied on top of the pose you authored: a bone rotates about its own pivot and stays where you put it. A model built on the vanilla skeleton is unaffected. -
Bone names are matched case-insensitively. Blockbench's default capitalisation (
Head,RightArm) matched nothing, so on a typical model both pose-following andbody_partssilently did nothing at all. -
Bone-level
mirrorandinflateare inherited by cubes that don't set their own. -
Suppression did nothing on
apoli:multiplepowers — the reason suppress/unsuppress looked completely broken. Amultiplehas no behaviour of its own; it all lives in the sub-powers the loader generates. Suppressing the id you actually know the name of therefore switched off nothing. Suppression is now effective: suppressing a power also suppresses everything it granted, at any depth. The actions, the commands, the codecs and the sync were all fine. -
Suppressed powers left lasting effects behind. A suppressed power stops ticking, so anything it installed outside the container stayed applied forever:
apoli:attributemodifiers, creative flight, and game-event listeners. Those three now undo and redo their state when suppression flips. -
apoli:edible_itemdid nothing. The power type was registered and parsed, but nothing ever read it. It now works end to end: hunger and saturation, status effects,entity_actionanditem_action,result_stack,consume_sound, the eating animation and its duration — and it takes priority over vanilla food, so it can replace an existing food's behaviour rather than only adding food to non-food items. An omitteditem_conditionstill means every stack, matching upstream. -
apoli:raycast'scommand_at_hitonly fired when the ray ended on a block. Any entity in front of the block silently swallowed it, and a ray with"entity": true, "block": falsecould never fire it at all. It now runs on the nearest hit of either kind.command_hit_offset(default0) pushes back along the block's face for a block hit and back along the ray for an entity hit. -
Speech-to-action works on NeoForge again, without reintroducing the 1.17.4 launch crash. JNA and Vosk now ship as plain resources, are unpacked on first use, and load in an isolated class loader, so they never become JPMS modules and can't collide with another mod's copy. Verified to still work with a foreign JNA already on the classpath (the ImmediatelyFast case).
-
apoli:player_model_typetreated every unreported player as wide, so"model_type": "slim"could never pass and a wide/slim power pair only ever fired the wide half. The model type is now resolved in order from the client's own report, then the player's signed skin metadata, then vanilla's default-skin rule for their UUID. Vanilla clients, older Apoli clients, and players whose skin hadn't loaded yet are all handled. -
A malformed optional field was dropped in complete silence. A typo in a nested field name, or broken escaping inside an SNBT
tag, made the field vanish with no error — so a power ran unconditionally, or with no NBT filter, and the log said nothing. Apoli now logs a warning naming both the power and the field. It is a warning, not a hard failure, so one bad field can't drop a whole power on a live server. -
SNBT parse errors now report the actual problem (
Expected '}' at position 81: …) instead of the uselessExpected NBT compound, got STRING. -
Legacy
origins:-namespaced action ids skipped their field defaults. The type resolved fine, which is why nothing showed in the log, butorigins:replace_inventorydecoded asoperation: modify— a silent no-op.origins:drop_inventoryand the three*_teamaliases had the same problem;origins:set_no_gravityandorigins:set_gravityfailed to parse outright. -
inventory_actionwithoperation: replacecould not fill an empty slot, which is the common use — putting an item into an empty offhand.modifyanddropstill skip empty slots.
