M2 Animation Playback Controller
Metadata
| Field |
Value |
| Status |
Implemented extraction |
| Target/work package |
M03 / M03-RND-M2-ANIMATION-PLAYBACK-001 |
| Owners |
Per-instance AnimationPlayer/native animator playback mutation |
| Last verified |
Worktree work/sindo-main-codex/m03-m2-animation-playback, 2026-07-18 |
| Profiles/capabilities |
Imported GLB and native experimental animated M2 instances |
Purpose
Apply deterministic playback to one already duplicated M2 instance: copy native
animator runtime fields, derive a stable phase, prepare/phase native animators,
select an imported animation and configure linear looping/playback/seek.
Non-goals
- Duplicate, name, transform or attach instances/batch roots.
- Apply visibility, shadows or Editor ownership.
- Load/finalize/cache animated scenes or decide eligibility.
- Change animation priorities, phase formula or native animator implementation.
Context and boundaries
Allowed dependencies are supplied Nodes, Script identity, AnimationPlayer and
Animation resources. ResourceLoader, files, workers, caches, scheduler,
MultiMesh, SceneTree attachment and application layers are forbidden.
Public API
| Symbol |
Kind |
Purpose |
Thread/lifetime |
Errors |
copy_native_animator_data(source_root, target_root, script) |
Command |
Copy five runtime fields in depth-first pair order |
Main thread; borrowed nodes |
Null/script mismatch yields no pairs |
start_instance_playback(root, path, index, script, players, collect_diagnostics) |
Command/query |
Start native/imported playback and optionally return native state |
Main thread; one instance |
Null/empty inventories no-op |
native_animators_in_subtree(root, script) |
Query |
Exact-script depth-first inventory |
Main thread; borrowed nodes |
Null root/script returns empty |
phase_for_instance(path, index) |
Pure query |
Stable phase in [0, 1) |
Any thread; scalar |
Historical hash behavior retained |
choose_default_animation(player, path) |
Pure engine query |
Apply exact ordinary/fish/bird/fallback priority |
Main thread; borrowed player |
Null/no animations returns empty |
Inputs and outputs
| Direction |
Contract/data |
Producer |
Consumer |
Ownership |
Thread/lifetime |
| Input |
Prototype and duplicate subtrees |
M2 animated instance materializer |
Native field copy |
Borrowed Nodes |
One duplicate |
| Input |
Exact native animator Script |
Loader composition |
Native inventory |
Borrowed Script |
Call-local |
| Input |
Relative path and instance index |
Build batch adapter |
Phase/selection |
Copied values |
One start |
| Input |
AnimationPlayer inventory |
Animated scene finalizer traversal |
Imported playback |
Borrowed players |
One start |
| Output |
Mutated native/imported playback |
Controller |
Rendered instance |
Nodes retain state/resources |
Instance lifetime |
| Output |
Detached native diagnostic records |
Controller |
Loader log adapter |
Caller-owned Dictionaries |
Debug call |
Side effects are native field assignment, prepare/phase calls, animation loop
mutation, play and seek. The service retains no inputs.
Data flow
Lifecycle/state
The controller itself is stateless; lifecycle labels describe borrowed instance state.
Main sequence
Dependency diagram
Ownership, threading and resources
- Materializer owns prototype duplication, instance/batch roots and SceneTree attachment.
- Materializer uses finalizer traversal for borrowed AnimationPlayer references.
- Controller borrows Nodes/Script/players only for the synchronous call.
- Native arrays are assigned by reference exactly as before extraction.
- Diagnostic Dictionaries are deep-duplicated before return.
- Main thread performs all engine-object mutation; pure phase math is thread-safe.
Errors, cancellation and recovery
| Failure |
Detection |
Behavior |
Diagnostic |
Recovery |
| Missing roots/script |
Inventory guard |
No native copy/start |
Contract fixture |
Correct composition |
| Fewer target animators |
Pair-count minimum |
Copy available pairs only |
Native copy fixture |
Rebuild import/prototype |
| No matching name |
Ordered fallback |
Substring, first name, then empty |
Selection fixtures |
Add compatible animation |
| Empty animation selection |
Empty result |
Skip loop/play/seek for that player |
Selection fixture |
Static/native path continues |
| Zero-length selected animation |
Length guard |
Play without seek |
Playback contract |
Imported timing remains engine-owned |
| Cancellation/shutdown |
Outside service |
No retained state |
N/A |
Loader owns subtree release |
Configuration and capabilities
| Setting/capability |
Default |
Profile |
Runtime mutable |
Effect |
| Phase buckets |
1000 |
All |
No |
Stable per path/index desynchronization |
| Loop mode |
LOOP_LINEAR |
All |
No |
Every available imported animation loops |
| Native diagnostics |
debug_streaming |
Debug |
Yes |
Samples state only when requested |
Persistence, cache and migration
No state or format is serialized. GLB/native cache formats, prototype lifetime
and material versions are unchanged; no rebake is required.
Diagnostics and observability
- Optional native states remain detached and contain existing prepared,
processing, mesh, bone, surface and length fields.
- Loader retains
M2_NATIVE_ANIMATOR log formatting and path normalization.
- Controller emits no logs or metrics.
Verification
verify_m2_animation_playback_controller.gd covers exact phase, ordinary/
fish/bird priorities, substring/first fallback, loop/play/seek, native exact-
script order, five-field copy, phase, detached diagnostics and boundaries.
- Finalizer/build/prototype/material/shutdown regressions protect adjacent behavior.
- Fidelity evidence is exact policy/mutation extraction; no private asset or
original-client animation comparison is claimed.
- Performance budget: 20,000 phase-and-selection pairs under one second.
Extension points
Server-driven animation state may later replace default selection through a
separate presentation contract; this fallback controller must remain available
for world doodads and compatibility fixtures.
Capability status
| Capability |
Status |
Evidence |
Gap/next step |
| Imported default selection/playback |
Implemented extraction |
Synthetic priority/play/seek verifier |
Asset-backed animation-name corpus pending |
| Native data/phase startup |
Implemented extraction |
Exact-reference/native state verifier |
Native visual fidelity remains experimental |
| Instance/batch materialization |
Implemented extraction |
Materializer contract verifier |
Asset-backed traversal pending |
Known gaps and risks
- Hash phase intentionally depends on existing Godot String hashing behavior.
- Default-name heuristics are not a complete WoW animation-state mapping.
- No proprietary traversal, animation timing comparison, p95/p99 or paired-client run exists.
Source map
| Path |
Responsibility |
src/render/m2/m2_animation_playback_controller.gd |
Phase, selection, native copy/start and imported playback |
src/render/m2/m2_animated_scene_finalizer.gd |
Accepted prototype/player inventory |
src/scenes/streaming/m2_native_animator.gd |
Experimental native deformation runtime |
src/render/m2/m2_animated_instance_materializer.gd |
Instance duplication/attachment and playback composition |
src/scenes/streaming/streaming_world_loader.gd |
Build adapter, diagnostic formatting and Editor ownership |
src/tools/verify_m2_animation_playback_controller.gd |
Policy/mutation/boundary/timing regression |
Related decisions and references