Load Emotecraft emotes into a registry and add a whole-player bone

- core: EmotecraftLoader reads Emotecraft / player-animation-lib JSON
  into a KeyframeAnimation (per-channel keyframes with Easing), following
  player-animation-lib's reader: absolute pivots become offsets, body
  (and torso before version 3) is the whole player in blocks, angles are
  degrees unless "degrees": false, turn adds whole turns, and loops jump
  back to returnTick after endTick. Emote and EmoteRegistry hold emotes
  by ID with their name, description and author. Animation.loopStart
  lets loops restart after an intro. Uses Jackson 2.17.0, the version
  Saturn Client ships.
- Bone.ROOT moves the whole player around its middle (0.7 blocks above
  the feet), through the model's root part, so armor and held items
  follow.
- fabric: Emotes.registry() and Emotes.play(uuid, id). Core and Jackson
  are bundled in the jar.
- testmod: loads Saturn's six emotes into the library's registry
  (/emotes play), and keeps its test animations in a separate registry
  (/emotes test). The gametest shoots the loaded emotes too.

Blending with the vanilla pose is issue #4.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-09-26 20:51:52 +02:00
co-authored by claude
parent cf05623c54
commit e40aa1353b
33 changed files with 17596 additions and 36 deletions
+34 -3
View File
@@ -5,11 +5,15 @@ A player emote library for Saturn Client, with limb bending.
## Layout
- `core/`: Minecraft-independent animation model (`Bone`, `BoneTransform`, `Pose`, `Animation`,
`AnimationPlayer`). Plain Java 21 with JUnit tests. Published as `org.saturnclient:emotes-core`.
`AnimationPlayer`), keyframes and easings, the Emotecraft JSON loader and the `EmoteRegistry`.
Plain Java 21 plus Jackson, with JUnit tests. Published as `org.saturnclient:emotes-core`.
- `src/main/`: the Fabric mod that puts poses on player models. It's built once per Minecraft
version with [Stonecutter](https://stonecutter.kikugie.dev/) and published as
`org.saturnclient:emotes-fabric:<version>+<mc>`, with `core` bundled inside the jar.
- `src/testmod/`: a dev-only tester mod that is never published. It adds `/emotes play <name>`,
`org.saturnclient:emotes-fabric:<version>+<mc>`, with `core` and Jackson 2.17.0 (the version Saturn
Client ships) bundled inside the jar.
- `src/testmod/`: a dev-only tester mod that is never published. It loads Saturn's emotes into the
library's registry and keeps its own hand-written test animations in a separate registry.
`/emotes play <id>` plays a registered emote, `/emotes test <name>` a test animation, and there's
`/emotes stop` and `/emotes info`. Press F5 to see yourself. It also has a Fabric client gametest
that creates a world and screenshots each animation from the front and side, into
`versions/<mc>/build/run/clientGameTest/screenshots/`.
@@ -17,6 +21,33 @@ A player emote library for Saturn Client, with limb bending.
Supported versions: 1.21.4 to 1.21.11, the same range as Saturn Client. Each version's Fabric API
version is set in `stonecutter.properties.toml`. The code uses Mojang mappings.
## Using it
```java
// At startup: load emotes (Emotecraft / player-animation-lib JSON) into the registry.
try (InputStream in = MyMod.class.getResourceAsStream("/assets/mymod/player_animations/facepalm.json")) {
Emotes.registry().loadEmotecraft("facepalm", in);
}
Emotes.play(player.getUUID(), "facepalm"); // false if there's no such emote
Emotes.play(player.getUUID(), animation); // or any Animation, e.g. written in code
Emotes.stop(player.getUUID());
Emotes.playing(player.getUUID()); // Optional<AnimationPlayer>
```
Emotes work for any player you can see, run on game ticks, and a non-looping one stops by itself.
`Emotes.registry().all()` lists the emotes with their name, description and author, for an emote wheel.
An `Animation` returns a `Pose` for a time in ticks: a `BoneTransform` (offset in model pixels,
pitch/yaw/roll in radians, bend and bend axis) for any of `ROOT` (the whole player), `HEAD`, `BODY`,
`RIGHT_ARM`, `LEFT_ARM`, `RIGHT_LEG` and `LEFT_LEG`. Bones it doesn't set keep their vanilla pose.
`KeyframeAnimation` builds one from keyframes and `Easing`s.
`EmotecraftLoader` follows player-animation-lib's reader: absolute part pivots become offsets,
`body` (and `torso` before format version 3) is the whole player in blocks, angles are degrees unless
`"degrees": false`, `turn` adds whole turns, and loops jump back to `returnTick` after `endTick`. Scale
channels, `easingArg` and blending with the vanilla pose aren't supported yet (issue #4).
## How it hooks in
1. `LivingEntityRendererMixin` samples the entity's current pose into its render state (players only).