Skip to content

KUMO 2 - A new animation format - #344

Closed
iwoplaza wants to merge 28 commits into
1.X/forge-1.12from
kumo-v2
Closed

iwoplaza wants to merge 28 commits into
1.X/forge-1.12from
kumo-v2

Conversation

@iwoplaza

Copy link
Copy Markdown
Collaborator

No description provided.

Fixes:
- Item, ramp and mirror `when` conditions start over when their node is
  entered (core:ticks_passed counted from the layer's start, core:decreased
  was never primed); the layer's own `when` starts with the layer.
- A crossfade between a relative (PRE/POST, additive) and an absolute pose
  resolves the relative side against the layers below first, instead of
  dropping it for the whole transition.
- core:animation_finished waits for every timed item of the node, not only
  the first one.
- SmoothOrientation smooths towards the nearer of q / -q. The squid/swim
  golden is re-recorded: tentacle_7_0 (base yaw -225 degrees) used to swing
  the long way round, up to 88 degrees off.
- Quaternion.negate() computed the conjugate; renamed to conjugate(), which
  is what the sword trail wants.
- Any error while animating (e.g. an unknown variable or state) fails the
  animator with a MalformedKumoTemplateException instead of escaping.
- Malformed JSON always ends in a JsonParseException with a message; missing
  pieces of and/or/not conditions and layer masks are refused at instancing.
- Layer buffers are sized once the whole skeleton is known.

Format:
- `formatVersion` is now required and checked on load, in one place
  (AnimatorTemplateSerializer) where an older format would be upgraded.
- Layers lose their `type` (always KEYFRAME) and keyframes their `scale`
  (never applied); the generator, bundled assets, examples and docs follow.

Cleanup:
- KeyframeLayerState/ILayerState -> LayerState, KeyframeNode* -> Node*,
  templates merged into one package; LayerType and the never-called
  validate() pipeline are gone (its rules moved into instancing).
- The node, driver and condition registries share one TypeRegistry.
- IKumoContext is the real contract (scopes, mirrored negation); no more
  downcasts to KumoContext.
- Removed flux (and FluxHandler), the KumoVariableRegistry that held the last
  entity data in a static, the old orient/rotate/slide APIs, clip mirroring
  helpers, and comments describing the deleted procedural code.
- IModelPart, BoneSinks and the GL matrix helper moved out of core.
- VariableScope keeps values unboxed; per-frame allocations removed.
- Core gets unit tests for the above.
…er API

Fixes:
- Animation content is reloaded whenever the resource pack policy in force
  differs from the one it was loaded under (AnimationPolicy). Before, the
  first join of a LIMITED or DENY server kept what was loaded under ALLOW on
  the title screen. The server's configuration is applied on the client
  thread, and NetworkConfiguration no longer touches client classes.
- Model definitions name their entity class without it being initialised,
  and it has to be a living entity.
- Entities of a new world that reuse an old entity's id get their own types
  and data (the caches check identity, and are cleared on world join).
- The server's resource pack is scanned for types and extensions, and a
  higher-priority pack's type or extension wins over one with the same id.
- Reordering types or extensions changes as few ranks as it can, so the
  order chosen for one mob no longer overwrites another's through a shared
  type. An `or` / `not` selector can't inflate a type's specificity.
- Type and extension errors log their stack trace; a selector nested too
  deep no longer aborts the reload.
- Assets download on a background thread (startup and F10) instead of on
  every resource reload, with timeouts, and paths from the remote manifest
  can't leave the assets folder. Streams and readers are closed.

Cleanup:
- IAnimationController is gone: each data class names its default animator
  and EntityData owns a KumoAnimatorController.
- EntityData makes its parts and bindings in initialize(), called by the
  creator, instead of calling overridable methods from its constructor (no
  more ThreadLocal in DefinedEntityData).
- Mutators no longer keep a vanilla model copy (RendererState restores
  vanilla); the unused scale factor, flags, afterRender and onTicksRestart
  hooks are gone, and so is ZombieMutatorBase.
- One resource reader (GsonResources) with the caches owned by
  AnimatorResources and ModelDefinitions; AnimationLoader is gone.
- The "Customize" entry, the animation editor registry and WebAPI (the old
  pack editor) are removed; alterable parts, the ticksAfterPunch alias and
  OverridableProperty too.
- core no longer depends on standard: ModStatics moved into core, the
  vanilla-entity config check is installed by the client proxy, supporter
  bind points find parts by bone name, and there is one refresh path
  (CoreClient.refresh) and one logger.
- "Perform Spin Attack" works: the player's sword combo ends with the whirl
  only while the option is on and the player isn't riding (the new
  CAN_SPIN_ATTACK state; other bipeds always whirl). The unused
  currentAttack state machine behind the option is gone. A lab test covers
  both settings.
- The zombie villager is registered, so its ported data, mutator and
  animator are used.
- The wolf animator uses core:state SITTING; WolfStateCondition is gone.
- The cape's wave speed is worked out by PlayerData before it advances the
  phase, instead of by the cape driver during posing (a frame late); the
  cape's smoothness is a template field.
- Spider leg geometry is defined once (SpiderData constants, used by the
  mutator, the data and the IK); the leg drivers no longer allocate per
  frame, validate feelLimbs, and the moving gait's foot lift is a template
  field. Unused spider and player state and variables are removed.
- Data classes register their animator inputs in registerKumoBindings().
- The right arm's inflate typo (0.1 for 0.01), the sword trail leaving
  face culling disabled, and ModConfig's per-entity vanilla cache (entities
  reuse ids) are fixed; its pattern matching is no longer duplicated.
- DefaultAddon lost its commented-out registrations.
- it_IT, no_NO, pl_PL and pt_BR are renamed to lowercase: with
  pack_format 3 the game never loaded them.
- Every language has the same keys as en_us: the type and extension order
  screens, the configuration options and the rest were missing in all of
  them (the new strings want a native speaker's look before a release).
- Keys of the removed pack editor and "Customize" entry are gone, as is
  mobends.setting.* (now the config keys); en_us gains mobends.gui.addons,
  which the addons window uses.
- Unused textures (stand_block, the node editor, bends.png) are removed.
- The cape field no longer has its final modifier stripped: nothing
  assigns it.
- GenVanillaFields is ported to Java (its output is byte-identical), so
  the build applies no Kotlin plugin and fetches no Kotlin stdlib. The lab
  had the plugin with no Kotlin sources; it's gone there too.
- The animation lab gets a Gradle wrapper (8.14.3); its README, the testing
  docs and its workflow use ./gradlew instead of a system Gradle.
- The root build loses jcenter, the MDK's example comments and template
  publishing block, and uses testImplementation.
- The mod is built on pushes to 1.X/forge-1.12 as well as pull requests.
- The changelog is 1.3.0 (1.12.2 releases are the 1.X line, see
  CONTRIBUTING.md) and covers this branch's fixes and removals, and what
  addons have to change.
- The README drops "development of 1.0.0" and the note that runClient
  fails at startup, and points at the lab, CONTRIBUTING and docs/.
- CONTRIBUTING says 1.12.2 builds core from its own module until the
  published core is in place.
- docs/api-communication.md names the real connection classes.
- misc/kumo-migration-plan.md (the finished study that planned this
  migration) and the old bends pack data and thumbnails are removed.
core:compare compared a variable with a constant; it now compares two
expressions ("left", "op", "right"), and core:decreased watches an
expression ("value") instead of a variable. Condition factories get the
expression scope they're written in, so a condition sees the named
expressions of its layer or node, like the items do.

The built-in animators are regenerated in the new shape; apart from it
they are unchanged (checked by comparing the parsed JSON), and the lab's
golden traces still match.
Only animators carried a formatVersion. Types, extensions, model
definitions and a mod's bends/models/index.json now need one too
("formatVersion": 2, numbered per format), checked by the same
FormatVersion helper: a missing, newer or older version is refused with
a message saying which, and that helper is where an older format would
be upgraded once one exists.

The shipped model definitions, the examples and the docs carry the field.
A broken animator only went to the log, so the entity silently stood
still. KumoAnimatorController now also shows the error in the chat, once
per animator (with its extensions) however many entities use it, and
again after a reload. While there is no player to show it to, it isn't
marked as shown, so the next entity tries again.
CoreClientConfig and CoreServerConfig used the suggested configuration
file, config/mobends.cfg, which the mod's @config (ModConfig) also owns:
two Configurations on one file, each saving its own view over the other's
changes. CoreConfig now takes the Configuration Forge keeps for the file
(ConfigManager.getConfiguration, package-private, so by reflection) rather
than opening a second one, so both sides save one object with each other's
categories in it, and the players' settings stay where they were. If
Forge's Configuration can't be reached, it falls back to its own with a
warning, as before.
Mo' Bends for 1.12.2 moves to the forge-1.12 branch and is labeled 2.0
from this release on (1.X was the mod before Kumo).
…o TODO.md

CONTRIBUTING describes the repository as it is: 1.12.2 on forge-1.12,
with core/, the mod and the animation lab. The version line is about what
players and pack authors get, not the code layout: 2.X is the reworked
animation system, so 1.12.2 from 2.0.0 on and the planned modern builds
are all 2.X. 1.12.2's tags carry the Minecraft version (v2.x.y-1.12.2) so
they don't clash with the modern releases' tags.

The plan for other Minecraft versions and loaders (Stonecutter, the common/
and loader modules, main, the migration plan) moves to TODO.md, which also
records the plan for the Customize entry (the web animation editor).

The changelog is now 2.0.0 and mentions this round's changes.
Keys were modId + "-" + name: "mobends-player" next to
"mobends-minecraft:zombie", which looks like a resource location with
the namespace "mobends-minecraft" and is ambiguous for mod ids with a
hyphen. They are now resource locations: the registering mod and the
name it gave the bender, or else the entity's id ("mobends:player",
"mobends:zombie", "mobends:othermod/beast" for another mod's entity).
They are also the ids of the built-in types, which extensions and type
files name.

The per-mob "animated" setting saved under the old key by 1.2.2 is
carried over to the new one the first time it's read.
A clip's weight scaled its rotation angles and offsets. For a bone the
clip writes relatively (PRE / POST) that is the blend, but for one it
replaces (OVERRIDE) a weight of 0.5 gave half the clip's angle measured
from the rest pose, dropping what the layers below had posed. Such bones
now blend by the weight from what they have so far this frame (the
layers below, the items before) to the clip. Where nothing has written
the bone yet, that is the rest pose rather than the live target, so the
weight doesn't turn into a smoothing rate; this is also what keeps the
shipped animators, whose weighted clips all come first on their base
layer, unchanged (the golden traces still match).
The entry of the Mo' Bends menu stays for the web animation editor in the
works. It opens the officialAnimationEditorUrl of static-api.json in
mobends-resources, as the old editor link did, so it can point at the web
editor without a release; until then that is mobends.com/roadmap. The file
is read on each click on a thread of its own (with timeouts) instead of on
the render thread, only https links are opened, and when it can't be read
the link is the roadmap. The old editor registry and WebAPI stay gone.

Its translations are back, with new ones for Norwegian and Russian.
The .gitignore was an allowlist (ignore everything, then un-ignore the
tracked paths), so every new top-level file needed an entry. It now
ignores the usual: OS files, Gradle and IDE output, the run directory.
The animation lab's own .gitignore is folded into it (without Kotlin,
which is gone), as are the empty leftovers of src/tools/kotlin.

Newly tracked:
- .java-version, which pins JDK 8 for jenv-style version managers.
- run-arm64/: the init script and launcher that run the client natively
  on Apple Silicon. The patched Forge Side class they need is extracted by
  runClient.sh from the Forge universal jar in the Gradle cache on its
  first run, into the ignored run-arm64/sidefix/, so no compiled class is
  committed. The JDK, jars and natives stay out; run-arm64/README.md says
  where to get each, with checksums.
…json

The nine mobs made from model definitions (cow, mooshroom, polar bear,
pig, creeper, chicken, villager, witch, iron golem) were registered at
startup from bends/models/index.json. Each now has a type file in
bends/types/ that names its model definition, with the id its bender key
had (mobends:cow, ...; the iron golem's entity id is villager_golem), so
extensions that name them still work. The index, its reader and
DefinedBenders.registerAll are gone.

These mobs no longer have a default model, so a resource pack's type
that is refused its own model definition (the server limits resource
packs) no longer falls back to it. Such a type is now ignored as a
whole: it can't replace a trusted type with the same id and leave the
mob vanilla, and its animator never runs on a model it wasn't made for.

The lab finds the defined mobs through the type files, and checks that
every model definition is named by one.
Registering the resource reload listener in CoreClient.init runs it right
away, so the types loaded before DefaultAddon registered its benders (in
the proxy's init, just after): the player, zombie, skeleton and the other
mobs written in code had no built-in type, and stayed vanilla until the
next resource reload. The log showed "Loaded 10 entity types" (the nine
defined mobs and a pack's) before "Registering mobends:player", and the
player's extensions were dropped for extending a type that didn't exist.

Registering a bender now marks the types stale, so they load again, with
its built-in type, when they're next used.
@github-actions

Copy link
Copy Markdown

🦾 Fresh bends, straight out the oven, ready to be tested out:

@github-actions

Copy link
Copy Markdown

🦾 Fresh bends, straight out the oven, ready to be tested out:

@iwoplaza iwoplaza closed this Sep 30, 2026
@iwoplaza
iwoplaza deleted the kumo-v2 branch September 30, 2026 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant