### Add PlayerAnimator Dependency (Forge) Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Configure your build.gradle for ForgeGradle or NeoForge to include the PlayerAnimator library. This example demonstrates adding the Maven repository and using `fg.deobf` for the Forge implementation, with JarJar bundling for Forge 1.19+. ```groovy minecraft { runs { client { property 'mixin.env.remapRefMap', 'true' property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" } server { property 'mixin.env.remapRefMap', 'true' property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" } } } repositories { maven { url 'https://maven.kosmx.dev/' } } dependencies { // Forge 1.19+: use JarJar for bundling implementation fg.deobf("dev.kosmx.player-anim:player-animation-lib-forge:2.0.4") } ``` -------------------------------- ### Get Animation from Registry Source: https://github.com/kosmx/minecraftplayeranimator/blob/1.21/README.md Use this method to retrieve a loaded animation from the PlayerAnimationRegistry. Ensure the animation file is placed in the correct directory. ```java dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry#getAnimation() ``` -------------------------------- ### KeyframeAnimationPlayer Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt The primary implementation for playing `KeyframeAnimation` objects, supporting looping, custom start times, and first-person mode configurations. Animations can be stopped manually, and their active state can be checked. ```APIDOC ## `KeyframeAnimationPlayer` — Play a `KeyframeAnimation` The primary `IAnimation` implementation for file-loaded and programmatically built animations. Supports looping (`isInfinite`), starting from an arbitrary tick, first-person mode configuration, and chainable setters. Call `stop()` to end early; `isActive()` returns `false` when playback finishes. ```java // Load from registry and play with first-person config IPlayable playable = PlayerAnimationRegistry.getAnimation( ResourceLocation.fromNamespaceAndPath("mymod", "slash") ); KeyframeAnimationPlayer animPlayer = (KeyframeAnimationPlayer) playable.playAnimation(); animPlayer .setFirstPersonMode(FirstPersonMode.THIRD_PERSON_MODEL) // show 3rd-person arms in 1st-person view .setFirstPersonConfiguration( new FirstPersonConfiguration() .setShowRightArm(true) .setShowLeftArm(false) .setShowRightItem(true) .setShowLeftItem(false) ); myModifierLayer.setAnimation(animPlayer); // Manual construction (from a KeyframeAnimation object): KeyframeAnimation anim = ...; // built via AnimationJson.GSON or AnimationBuilder KeyframeAnimationPlayer p2 = new KeyframeAnimationPlayer(anim, 5); // start at tick 5 p2.stop(); // immediately stop System.out.println(p2.isActive()); // false System.out.println(p2.isInfinite()); // true/false depending on anim data ``` ``` -------------------------------- ### Play Keyframe Animations Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Use `KeyframeAnimationPlayer` to play file-loaded or programmatically built animations. Supports looping, custom start ticks, and first-person view configurations. Call `stop()` to end playback early. ```java // Load from registry and play with first-person config IPlayable playable = PlayerAnimationRegistry.getAnimation( ResourceLocation.fromNamespaceAndPath("mymod", "slash") ); KeyframeAnimationPlayer animPlayer = (KeyframeAnimationPlayer) playable.playAnimation(); animPlayer .setFirstPersonMode(FirstPersonMode.THIRD_PERSON_MODEL) // show 3rd-person arms in 1st-person view .setFirstPersonConfiguration( new FirstPersonConfiguration() .setShowRightArm(true) .setShowLeftArm(false) .setShowRightItem(true) .setShowLeftItem(false) ); myModifierLayer.setAnimation(animPlayer); // Manual construction (from a KeyframeAnimation object): KeyframeAnimation anim = ...; // built via AnimationJson.GSON or AnimationBuilder KeyframeAnimationPlayer p2 = new KeyframeAnimationPlayer(anim, 5); // start at tick 5 p2.stop(); // immediately stop System.out.println(p2.isActive()); // false System.out.println(p2.isInfinite()); // true/false depending on anim data ``` -------------------------------- ### Get Player Animation Stack Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Retrieve the AnimationStack for a player to add or remove animation layers at runtime. Throws IllegalArgumentException if the player is invalid or mixins failed. ```java AbstractClientPlayer player = Minecraft.getInstance().player; AnimationStack stack = PlayerAnimationAccess.getPlayerAnimLayer(player); ModifierLayer customLayer = new ModifierLayer<>(); stack.addAnimLayer(100, customLayer); // add at priority 100 // later: stack.removeLayer(customLayer); // remove by instance stack.removeLayer(100); // remove ALL layers at priority 100 ``` -------------------------------- ### Get 3D Transform for Animation Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Implement this method to provide custom 3D transform values for specific animation types. It allows modifying position and rotation based on the current state. ```java @Override public @NotNull Vec3f get3DTransform( @NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0) { if (modelName.equals("head")) { return switch (type) { case ROTATION -> new Vec3f(value0.getX(), value0.getY() + 0.1f, value0.getZ()); case POSITION -> new Vec3f(value0.getX(), value0.getY() + 0.05f, value0.getZ()); default -> value0; }; } return value0; } ``` -------------------------------- ### Configure PlayerAnimator for Development (Forge) Source: https://github.com/kosmx/minecraftplayeranimator/blob/1.21/README.md Set up the Forge development environment to run PlayerAnimator, including necessary mixin properties for remapping. ```groovy minecraft { (...) runs { client { (...) //You have to set mixin propert if you want to run playerAnimator in development environment. property 'mixin.env.remapRefMap', 'true' property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" } server { (...) //Add this to the server too. property 'mixin.env.remapRefMap', 'true' property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" } } } repositories { (...) maven { name "KosmX's maven" url 'https://maven.kosmx.dev/' } } dependencies { (...) //If you don't want to include the library in your jar, remove the include word implementation fg.deobf("dev.kosmx.player-anim:player-animation-lib-forge:${project.player_anim}") //Bendy-lib also has a Forge version: //runtimeOnly fg.deobf("io.github.kosmx.bendy-lib:bendy-lib-forge:${project.bendylib_version}") //Forge JarJar only works on MC 1.19. Do not use JarJar on older version! } ``` -------------------------------- ### PlayerAnimationRegistry.getAnimation / getAnimationOptional Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Allows looking up animations loaded from the `player_animations/` directory. Supports both null-safe and Optional-based retrieval, as well as fetching all animations for a given namespace. ```APIDOC ## `PlayerAnimationRegistry.getAnimation` / `getAnimationOptional` — Look up a loaded animation Animations placed in `assets//player_animations/` (`.json` Emotecraft or GeckoLib format) are loaded automatically on resource reload. `getAnimation` returns `null` if not found; `getAnimationOptional` wraps the result in `Optional`. ```java // assets/mymod/player_animations/spin.json → key: mymod:spin ResourceLocation id = ResourceLocation.fromNamespaceAndPath("mymod", "spin"); // Null-safe lookup IPlayable playable = PlayerAnimationRegistry.getAnimation(id); if (playable != null) { IActualAnimation player = playable.playAnimation(); myLayer.setAnimation(player); } // Optional style PlayerAnimationRegistry.getAnimationOptional(id) .map(IPlayable::playAnimation) .ifPresent(myLayer::setAnimation); // All animations for a namespace Map allMyAnims = PlayerAnimationRegistry.getModAnimations("mymod"); ``` ``` -------------------------------- ### Compare PartKeys using Identity Equals Source: https://github.com/kosmx/minecraftplayeranimator/wiki/1.21.3-notable-changes PartKeys can be efficiently compared using identity equals (==) because the keyForId function guarantees that identical keys will always return the same object instance. This is faster than string comparison. ```java if (partKey == PartKey.BODY) {...} ``` -------------------------------- ### Add PlayerAnimator Dependency (Fabric) Source: https://github.com/kosmx/minecraftplayeranimator/blob/1.21/README.md Include the PlayerAnimator library for Fabric projects using Gradle. Ensure the Maven repository is added and specify the library version. ```groovy repositories { (...) maven { name "KosmX's maven" url 'https://maven.kosmx.dev/' } } dependencies { (...) //If you don't want to include the library in your jar, remove the include word //You can find the latest version in [](https://maven.kosmx.dev/dev/kosmx/player-anim/player-animation-lib-fabric/) include modImplementation("dev.kosmx.player-anim:player-animation-lib-fabric:${project.player_anim}") //You might want bendy-lib. playerAnimator will wrap it. //include modRuntimeOnly("io.github.kosmx.bendy-lib:bendy-lib-fabric:${project.bendylib_version}") } ``` -------------------------------- ### Look Up Loaded Animations Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Find animations loaded from `assets//player_animations/`. `getAnimation` returns null if not found, while `getAnimationOptional` wraps the result in an Optional. ```java // assets/mymod/player_animations/spin.json → key: mymod:spin ResourceLocation id = ResourceLocation.fromNamespaceAndPath("mymod", "spin"); // Null-safe lookup IPlayable playable = PlayerAnimationRegistry.getAnimation(id); if (playable != null) { IActualAnimation player = playable.playAnimation(); myLayer.setAnimation(player); } // Optional style PlayerAnimationRegistry.getAnimationOptional(id) .map(IPlayable::playAnimation) .ifPresent(myLayer::setAnimation); // All animations for a namespace Map allMyAnims = PlayerAnimationRegistry.getModAnimations("mymod"); ``` -------------------------------- ### Cache Custom PartKey with keyForId Source: https://github.com/kosmx/minecraftplayeranimator/wiki/1.21.3-notable-changes For custom model parts, use the PartKey.keyForId(String id) function to create and cache a PartKey. This function ensures that the same PartKey instance is returned for identical string IDs, even across multiple calls or mods. ```java private static final PartKey tailKey = PartKey.keyForId("tail"); private void fun(...) { ... animation.get3DTransform(tailKey, ...); ``` -------------------------------- ### Control First-Person Rendering Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Set the first-person render mode and configure visibility of arms and items. This is useful for accurate first-person animation or custom HUD elements. ```java KeyframeAnimationPlayer player = (KeyframeAnimationPlayer) playable.playAnimation(); player.setFirstPersonMode(FirstPersonMode.THIRD_PERSON_MODEL); player.setFirstPersonConfiguration( new FirstPersonConfiguration() .setShowRightArm(true) // show right arm model .setShowLeftArm(false) // hide left arm model .setShowRightItem(true) // show item in right hand .setShowLeftItem(false) // hide item in left hand ); myLayer.setAnimation(player); ``` ```java // Inside a custom IAnimation: @Override public @NotNull FirstPersonMode getFirstPersonMode(float tickDelta) { return isActive() ? FirstPersonMode.THIRD_PERSON_MODEL : FirstPersonMode.NONE; } @Override public @NotNull FirstPersonConfiguration getFirstPersonConfiguration(float tickDelta) { return new FirstPersonConfiguration().setShowRightArm(true); } ``` -------------------------------- ### AnimationJson.GSON Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Provides a Gson instance for parsing and serializing Emotecraft-format .json animation files. This is useful for external tooling or loading animations outside the standard resource-pack pipeline. ```APIDOC ## `AnimationJson.GSON` — Parse / serialize Emotecraft JSON `AnimationJson.GSON` is a `Gson` instance that can deserialize Emotecraft-format `.json` files into `List` and serialize back. Suitable for tooling, tests, or loading animations outside the resource-pack pipeline. ### Usage ```java import dev.kosmx.playerAnim.core.data.gson.AnimationJson; import dev.kosmx.playerAnim.core.data.KeyframeAnimation; import com.google.gson.reflect.TypeToken; // Deserialize from JSON string / reader String json = Files.readString(Path.of("spin.json")); List animations = AnimationJson.GSON.fromJson( json, AnimationJson.getListedTypeToken() ); KeyframeAnimation anim = animations.get(0); System.out.println(anim.endTick); // e.g. 40 System.out.println(anim.isInfinite); // false // Serialize back to JSON String out = AnimationJson.GSON.toJson(anim, KeyframeAnimation.class); // or use the static helpers: JsonObject emoteJson = AnimationJson.emoteSerializer(anim); // only the "emote" block JsonArray movesJson = AnimationJson.moveSerializer(anim); // only the "moves" array ``` ``` -------------------------------- ### PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Registers an animation layer for every client player. The factory is invoked once per player construction. Returning null skips registration for that player. ```APIDOC ## PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory ### Description The recommended initialization point for adding an animation layer to every client player. The factory is invoked once per player construction; returning a non-null `IAnimation` automatically registers it in the player's `AnimationStack` and in `PlayerAssociatedAnimationData` under the given `ResourceLocation`. Returning `null` skips registration for that player (e.g., to restrict to `LocalPlayer` only). ### Method Signature ```java PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory(ResourceLocation key, int priority, Function factory) ``` ### Parameters #### Path Parameters - **key** (ResourceLocation) - Required - The storage key for the animation layer. - **priority** (int) - Required - The priority of the animation layer (higher = overrides lower). - **factory** (Function) - Required - A function that returns an `IAnimation` layer or null. ### Request Example ```java // Called once during client initialization (ClientModInitializer / FMLClientSetupEvent) PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory( ResourceLocation.fromNamespaceAndPath("mymod", "attack"), // storage key 42, // priority (higher = overrides lower) (player) -> { if (!(player instanceof LocalPlayer)) return null; // only local player ModifierLayer layer = new ModifierLayer<>(); layer.addModifierBefore(new SpeedModifier(1.5f)); // play 1.5× speed return layer; // stored and added to stack } ); ``` ``` -------------------------------- ### Register Animation Layer via Factory Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Use `PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory` to add an animation layer to every client player upon their construction. This method allows for conditional registration (e.g., only for `LocalPlayer`) and stores the layer in `PlayerAssociatedAnimationData`. ```java // Called once during client mod initialization (ClientModInitializer / FMLClientSetupEvent) PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory( ResourceLocation.fromNamespaceAndPath("mymod", "attack"), // storage key 42, // priority (higher = overrides lower) (player) -> { if (!(player instanceof LocalPlayer)) return null; // only local player ModifierLayer layer = new ModifierLayer<>(); layer.addModifierBefore(new SpeedModifier(1.5f)); // play 1.5× speed return layer; // stored and added to stack } ); ``` -------------------------------- ### Per-Player Animation Data Storage Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Utilize a typed key-value store embedded in the player for animation data, avoiding external map performance costs. Data is automatically cleaned up when the player unloads. ```java ResourceLocation key = ResourceLocation.fromNamespaceAndPath("mymod", "spin"); // Store a layer PlayerAnimationAccess.getPlayerAssociatedData(player).set(key, spinLayer); // Retrieve it anywhere (returns null if not set) IAnimation stored = PlayerAnimationAccess.getPlayerAssociatedData(player).get(key); if (stored instanceof ModifierLayer ml) { ml.setAnimation(new KeyframeAnimationPlayer(someAnim)); } ``` -------------------------------- ### Player Animation Registry Source: https://github.com/kosmx/minecraftplayeranimator/blob/1.21/README.md Provides a method to retrieve animations registered within the game. Animations should be placed in the `assets/modid/player_animation/` directory. ```APIDOC ## getAnimation ### Description Retrieves a registered animation from the game. ### Method `PlayerAnimationRegistry.getAnimation(String animationName)` ### Parameters #### Path Parameters - **animationName** (String) - Required - The name of the animation to retrieve. ### Response #### Success Response (Animation) - Returns the animation object if found. #### Response Example ```java // Assuming 'my_animation' is the name of the animation file Animation animation = PlayerAnimationRegistry.getAnimation("my_animation"); ``` ``` -------------------------------- ### Deserialize and Serialize Emotecraft Animations with Gson Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Use the pre-configured Gson instance to parse Emotecraft animation JSON into KeyframeAnimation objects or serialize them back. Suitable for external tooling or testing. ```java import dev.kosmx.playerAnim.core.data.gson.AnimationJson; import dev.kosmx.playerAnim.core.data.KeyframeAnimation; import com.google.gson.reflect.TypeToken; // Deserialize from JSON string / reader String json = Files.readString(Path.of("spin.json")); List animations = AnimationJson.GSON.fromJson( json, AnimationJson.getListedTypeToken() ); KeyframeAnimation anim = animations.get(0); System.out.println(anim.endTick); // e.g. 40 System.out.println(anim.isInfinite); // false // Serialize back to JSON String out = AnimationJson.GSON.toJson(anim, KeyframeAnimation.class); // or use the static helpers: JsonObject emoteJson = AnimationJson.emoteSerializer(anim); // only the "emote" block JsonArray movesJson = AnimationJson.moveSerializer(anim); // only the "moves" array ``` -------------------------------- ### Register Animation Layer via Event Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Utilize `PlayerAnimationAccess.REGISTER_ANIMATION_EVENT` for low-level animation layer registration that fires for all players (local and remote) after factories have run. This event provides direct access to the `AnimationStack` and is suitable for registering layers without storing them in `PlayerAssociatedAnimationData`. ```java PlayerAnimationAccess.REGISTER_ANIMATION_EVENT.register((player, animationStack) -> { ModifierLayer layer = new ModifierLayer<>(); animationStack.addAnimLayer(69, layer); // Optionally store for later retrieval without a separate map PlayerAnimationAccess .getPlayerAssociatedData(player) .set(ResourceLocation.fromNamespaceAndPath("mymod", "wave"), layer); }); ``` -------------------------------- ### Replace String with PartKey in get3DTransform Source: https://github.com/kosmx/minecraftplayeranimator/wiki/1.21.3-notable-changes Migrate from using string identifiers to PartKey constants for the get3DTransform function to improve performance. This change reduces string hashing and comparisons on the render thread. ```diff - Vec3f someVector = animation.get3DTransform("head", TransformType.SCALE, 0, Vec3f.ZERO); + Vec3f someVector = animation.get3DTransform(PartKey.HEAD, TransformType.SCALE, 0, Vec3f.ZERO); ``` -------------------------------- ### Add Animation to Player (Java) Source: https://github.com/kosmx/minecraftplayeranimator/blob/1.21/README.md Access the player's animation layer and add a new animation layer to it. It's recommended to use ModifierLayer for animations with modifiers and fade effects. ```java AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(clientPlayer); animationStack.addAnimLayer(...); ``` -------------------------------- ### Manage Animation Layers with AnimationStack Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Use AnimationStack to manage a stack of animation layers for a player. Layers are prioritized, allowing higher-priority layers to override lower ones. ```java AnimationStack stack = PlayerAnimationAccess.getPlayerAnimLayer(player); ModifierLayer base = new ModifierLayer<>(); // base idle ModifierLayer top = new ModifierLayer<>(); // combat override stack.addAnimLayer(10, base); // low priority stack.addAnimLayer(50, top); // higher priority, overrides base // Remove specific layer instance stack.removeLayer(base); // Remove all layers at a given priority stack.removeLayer(50); // Check if any layer is active boolean anyActive = stack.isActive(); ``` -------------------------------- ### Implement Custom Procedural Animations with IAnimation Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Implement the IAnimation interface to create code-driven animations. The get3DTransform method is called each frame to modify bone transformations. ```java public class WaveAnimation implements IAnimation { private boolean running = true; private int tick = 0; @Override public boolean isActive() { return running; } @Override public void tick() { tick++; if (tick > 40) running = false; } @Override public void setupAnim(float tickDelta) {} @Override public @NotNull Vec3f get3DTransform( @NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0) { if (type == TransformType.ROTATION && modelName.equals("rightArm")) { float angle = (float) Math.sin((tick + tickDelta) * 0.3) * 0.5f; return new Vec3f(value0.getX() + angle, value0.getY(), value0.getZ()); } return value0; // pass through all other parts unchanged } } // Usage ModifierLayer layer = ...; layer.setAnimation(new WaveAnimation()); ``` -------------------------------- ### Decode Animations from InputStream with AnimationCodecs Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Decode Emotecraft or legacy GeckoLib animations from an InputStream using AnimationCodecs.deserialize. Custom codecs can be registered for new formats. ```java // Load an animation from a file at runtime (e.g., from a config folder) try (InputStream is = Files.newInputStream(Path.of("custom_anim.json"))) { Collection loaded = AnimationCodecs.deserialize("json", is); loaded.forEach(playable -> { System.out.println("Loaded: " + playable.getName()); IActualAnimation player = playable.playAnimation(); myLayer.setAnimation(player); }); } // Register a custom codec AnimationCodecs.INSTANCE.registerCodec(new AnimationCodec() { @Override public String getExtension() { return "myext"; } @Override public String getFormatName() { return "MyFormat"; } @Override public List decode(InputStream stream) throws IOException { ... } }); ``` -------------------------------- ### Creating Cross-Fades Between Animations with AbstractFadeModifier Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt AbstractFadeModifier enables smooth transitions between animations. Use standardFadeIn for ease-based fades or functionalFadeIn for per-part control. The modifier automatically removes itself after fading. ```java // Simple ease-in fade over 20 ticks AbstractFadeModifier simpleFade = AbstractFadeModifier.standardFadeIn(20, Ease.INOUTSINE); // Per-part functional fade (e.g., only fade arms, keep legs instant) AbstractFadeModifier perPartFade = AbstractFadeModifier.functionalFadeIn( 15, (modelName, type, progress) -> { if (modelName.equals("rightArm") || modelName.equals("leftArm")) { return Ease.OUTQUAD.invoke(progress); } return progress >= 1f ? 1f : 0f; // snap for other parts } ); layer.replaceAnimationWithFade(simpleFade, new KeyframeAnimationPlayer(newAnim)); // or with fade-from-nothing forced: layer.replaceAnimationWithFade(perPartFade, new KeyframeAnimationPlayer(newAnim), true); ``` -------------------------------- ### AnimationStack.addAnimLayer / removeLayer Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Methods for managing the animation layer stack on a player. Layers are prioritized, allowing higher-priority layers to override lower ones. ```APIDOC ## `AnimationStack.addAnimLayer` / `removeLayer` — Manage the layer stack `AnimationStack` is the root `IAnimation` on every player. Layers are sorted by priority (ascending); the highest-priority layer is evaluated last, letting it override everything below. Multiple layers at the same priority are evaluated in insertion order (first-in = higher priority within that level). ### Usage ```java AnimationStack stack = PlayerAnimationAccess.getPlayerAnimLayer(player); ModifierLayer base = new ModifierLayer<>(); // base idle ModifierLayer top = new ModifierLayer<>(); // combat override stack.addAnimLayer(10, base); // low priority stack.addAnimLayer(50, top); // higher priority, overrides base // Remove specific layer instance stack.removeLayer(base); // Remove all layers at a given priority stack.removeLayer(50); // Check if any layer is active boolean anyActive = stack.isActive(); ``` ``` -------------------------------- ### Using ModifierLayer to Chain Animations and Modifiers Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt ModifierLayer allows chaining multiple animations and modifiers. Modifiers are applied in the order they are added. Use setAnimation for immediate swaps and replaceAnimationWithFade for smooth transitions. ```java ModifierLayer layer = new ModifierLayer<>(); // Add modifiers (order matters: first added = outermost = applied first) layer.addModifierBefore(new MirrorModifier(true)); // mirror left/right layer.addModifierBefore(new SpeedModifier(2.0f)); // then double speed // Set animation directly (no fade) layer.setAnimation(new KeyframeAnimationPlayer(runAnim)); // Later, swap to a new animation with a 10-tick linear fade layer.replaceAnimationWithFade( AbstractFadeModifier.standardFadeIn(10, Ease.LINEAR), new KeyframeAnimationPlayer(idleAnim) ); // Fade to nothing (returns player to default pose) layer.replaceAnimationWithFade( AbstractFadeModifier.standardFadeIn(20, Ease.OUTCUBIC), null ); // Check state boolean active = layer.isActive(); // true if inner anim or any modifier is active int modifierCount = layer.size(); ``` -------------------------------- ### AnimationCodecs.deserialize Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Decodes animations from an InputStream using internal codec pipelines. It supports both Emotecraft and legacy GeckoLib formats and allows registration of custom codecs. ```APIDOC ## `AnimationCodecs.deserialize` — Decode animation from InputStream The codec pipeline used internally by `PlayerAnimationRegistry`. Supports `.json` in both Emotecraft and legacy GeckoLib formats. Custom codecs can be registered via `AnimationCodecs.INSTANCE.registerCodec(...)`. Returns a `Collection` usable directly with `IPlayable.playAnimation()`. ### Usage ```java // Load an animation from a file at runtime (e.g., from a config folder) try (InputStream is = Files.newInputStream(Path.of("custom_anim.json"))) { Collection loaded = AnimationCodecs.deserialize("json", is); loaded.forEach(playable -> { System.out.println("Loaded: " + playable.getName()); IActualAnimation player = playable.playAnimation(); myLayer.setAnimation(player); }); } // Register a custom codec AnimationCodecs.INSTANCE.registerCodec(new AnimationCodec() { @Override public String getExtension() { return "myext"; } @Override public String getFormatName() { return "MyFormat"; } @Override public List decode(InputStream stream) throws IOException { ... } }); ``` ``` -------------------------------- ### IAnimation Interface Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Implement this interface to create custom procedural or code-driven animations. The `get3DTransform` method is called each render frame to modify body part transformations. ```APIDOC ## `IAnimation` — Custom animation interface Implement `IAnimation` to create procedural or code-driven animations. `get3DTransform` is called every render frame for each active body part; return the modified `Vec3f` or pass `value0` through unchanged. ### Example Implementation ```java public class WaveAnimation implements IAnimation { private boolean running = true; private int tick = 0; @Override public boolean isActive() { return running; } @Override public void tick() { tick++; if (tick > 40) running = false; } @Override public void setupAnim(float tickDelta) {} @Override public @NotNull Vec3f get3DTransform( @NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0) { if (type == TransformType.ROTATION && modelName.equals("rightArm")) { float angle = (float) Math.sin((tick + tickDelta) * 0.3) * 0.5f; return new Vec3f(value0.getX() + angle, value0.getY(), value0.getZ()); } return value0; // pass through all other parts unchanged } } // Usage ModifierLayer layer = ...; layer.setAnimation(new WaveAnimation()); ``` ``` -------------------------------- ### AbstractFadeModifier Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Provides functionality to smoothly interpolate between animations over a specified number of ticks. It supports both simple ease-in fades and more complex functional fades. ```APIDOC ## AbstractFadeModifier Cross-fade between animations Smoothly interpolates between the previous animation and the new one over a number of ticks. `standardFadeIn` uses a single `Ease` curve; `functionalFadeIn` accepts a per-part lambda for fine-grained control. Once the fade completes the modifier removes itself automatically from the `ModifierLayer`. ### Usage ```java // Simple ease-in fade over 20 ticks AbstractFadeModifier simpleFade = AbstractFadeModifier.standardFadeIn(20, Ease.INOUTSINE); // Per-part functional fade (e.g., only fade arms, keep legs instant) AbstractFadeModifier perPartFade = AbstractFadeModifier.functionalFadeIn( 15, (modelName, type, progress) -> { if (modelName.equals("rightArm") || modelName.equals("leftArm")) { return Ease.OUTQUAD.invoke(progress); } return progress >= 1f ? 1f : 0f; // snap for other parts } ); layer.replaceAnimationWithFade(simpleFade, new KeyframeAnimationPlayer(newAnim)); // or with fade-from-nothing forced: layer.replaceAnimationWithFade(perPartFade, new KeyframeAnimationPlayer(newAnim), true); ``` ``` -------------------------------- ### Mirroring Animations Left-to-Right with MirrorModifier Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt MirrorModifier swaps left and right body parts and negates relevant axes for mirroring. It also mirrors FirstPersonConfiguration arm visibility. Mirroring can be toggled on/off using setEnabled. ```java MirrorModifier mirror = new MirrorModifier(true); // enabled = true layer.addModifierBefore(mirror); layer.setAnimation(new KeyframeAnimationPlayer(rightHandAttack)); // At runtime, disable mirroring mirror.setEnabled(false); ``` -------------------------------- ### PlayerAnimationAccess.REGISTER_ANIMATION_EVENT Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt A low-level layer registration event that fires for every player after all factories have run. It provides direct access to the `AnimationStack`. ```APIDOC ## PlayerAnimationAccess.REGISTER_ANIMATION_EVENT ### Description Fires for every player (local and remote) after all factories have run. Provides direct access to the `AnimationStack`. Use this when you need to register layers for all players, or when you do not want the layer stored in `PlayerAssociatedAnimationData`. ### Method Signature ```java PlayerAnimationAccess.REGISTER_ANIMATION_EVENT.register(BiConsumer consumer) ``` ### Parameters #### Path Parameters - **consumer** (BiConsumer) - Required - A consumer that accepts the player and their animation stack. ### Request Example ```java PlayerAnimationAccess.REGISTER_ANIMATION_EVENT.register((player, animationStack) -> { ModifierLayer layer = new ModifierLayer<>(); animationStack.addAnimLayer(69, layer); // Optionally store for later retrieval without a separate map PlayerAnimationAccess .getPlayerAssociatedData(player) .set(ResourceLocation.fromNamespaceAndPath("mymod", "wave"), layer); }); ``` ``` -------------------------------- ### PlayerAnimationAccess.getPlayerAssociatedData Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Provides access to a typed key-value store embedded within the player object for per-player animation data storage. Data is automatically managed. ```APIDOC ## `PlayerAnimationAccess.getPlayerAssociatedData` — Per-player animation storage Provides a typed key-value store embedded directly inside the player object (via mixin), avoiding the performance cost of external `WeakHashMap`/`ObjectMap` solutions. Data is automatically cleaned up when the player is unloaded. ```java ResourceLocation key = ResourceLocation.fromNamespaceAndPath("mymod", "spin"); // Store a layer PlayerAnimationAccess.getPlayerAssociatedData(player).set(key, spinLayer); // Retrieve it anywhere (returns null if not set) IAnimation stored = PlayerAnimationAccess.getPlayerAssociatedData(player).get(key); if (stored instanceof ModifierLayer ml) { ml.setAnimation(new KeyframeAnimationPlayer(someAnim)); } ``` ``` -------------------------------- ### PlayerAnimationAccess.getPlayerAnimLayer Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Retrieves the AnimationStack for a given AbstractClientPlayer. This is useful for dynamically adding or removing animation layers at runtime. ```APIDOC ## `PlayerAnimationAccess.getPlayerAnimLayer` — Get a player's AnimationStack directly Returns the `AnimationStack` for any `AbstractClientPlayer`. Useful when you need to add or remove layers at runtime outside the factory/event lifecycle (e.g., from a key-press handler). Throws `IllegalArgumentException` if the argument is not a valid player or if library mixins failed. ```java // Inside a key binding handler (client-side tick) AbstractClientPlayer player = Minecraft.getInstance().player; AnimationStack stack = PlayerAnimationAccess.getPlayerAnimLayer(player); ModifierLayer customLayer = new ModifierLayer<>(); stack.addAnimLayer(100, customLayer); // add at priority 100 // later: stack.removeLayer(customLayer); // remove by instance stack.removeLayer(100); // remove ALL layers at priority 100 ``` ``` -------------------------------- ### Applying Additive Transforms with AdjustmentModifier Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt AdjustmentModifier applies additional rotations, offsets, or scales to specific body parts. It supports automatic fading based on animation timing and manual fade-out calls. Return Optional.empty() for parts that should not be adjusted. ```java // Tilt head and arms with the player's pitch (vertical look angle) AdjustmentModifier pitchAdjust = new AdjustmentModifier((partName) -> { float pitch = (float) Math.toRadians(localPlayer.getPitch() / 2f); return switch (partName) { case "body" -> Optional.of(new AdjustmentModifier.PartModifier( new Vec3f(-pitch, 0, 0), // rotation Vec3f.ZERO // offset )); case "rightArm", "leftArm" -> Optional.of(new AdjustmentModifier.PartModifier( new Vec3f(pitch, 0, 0), Vec3f.ZERO )); default -> Optional.empty(); // no adjustment }; }); layer.addModifierBefore(pitchAdjust); // Trigger a manual fade-out over 10 ticks before stopping the animation pitchAdjust.fadeOut(10); ``` -------------------------------- ### Adjusting Animation Playback Speed with SpeedModifier Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt SpeedModifier alters the playback rate of an animation. A speed of 2.0 plays twice as fast, while 0.5 plays at half speed. The speed value must be a finite float and can be changed at runtime. ```java ModifierLayer layer = new ModifierLayer<>(); SpeedModifier speed = new SpeedModifier(0.5f); // half speed layer.addModifierBefore(speed); layer.setAnimation(new KeyframeAnimationPlayer(walkAnim)); // Change speed at runtime speed.speed = 1.75f; ``` -------------------------------- ### ModifierLayer Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt A composable animation layer that wraps another animation and chains a list of AbstractModifier objects. Modifiers are applied in order before the inner animation. ```APIDOC ## ModifierLayer Composable animation layer with modifiers `ModifierLayer` is an `IAnimation` that wraps another animation and chains a list of `AbstractModifier` objects before it. Modifiers are applied in order; each one delegates to the next. `setAnimation` swaps the inner animation immediately; `replaceAnimationWithFade` performs a smooth transition. ### Usage ```java ModifierLayer layer = new ModifierLayer<>(); // Add modifiers (order matters: first added = outermost = applied first) layer.addModifierBefore(new MirrorModifier(true)); // mirror left/right layer.addModifierBefore(new SpeedModifier(2.0f)); // then double speed // Set animation directly (no fade) layer.setAnimation(new KeyframeAnimationPlayer(runAnim)); // Later, swap to a new animation with a 10-tick linear fade layer.replaceAnimationWithFade( AbstractFadeModifier.standardFadeIn(10, Ease.LINEAR), new KeyframeAnimationPlayer(idleAnim) ); // Fade to nothing (returns player to default pose) layer.replaceAnimationWithFade( AbstractFadeModifier.standardFadeIn(20, Ease.OUTCUBIC), null ); // Check state boolean active = layer.isActive(); // true if inner anim or any modifier is active int modifierCount = layer.size(); ``` ``` -------------------------------- ### MirrorModifier Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Mirrors an animation along the left/right axis. This modifier swaps corresponding body parts and negates rotation/position axes as needed. ```APIDOC ## MirrorModifier Mirror animation left/right Swaps left/right body parts (`leftArm` ↔ `rightArm`, `leftLeg` ↔ `rightLeg`, `leftItem` ↔ `rightItem`) and negates the appropriate rotation/position axes. Also mirrors `FirstPersonConfiguration` arm visibility. Can be toggled with `setEnabled`. ### Usage ```java MirrorModifier mirror = new MirrorModifier(true); // enabled = true layer.addModifierBefore(mirror); layer.setAnimation(new KeyframeAnimationPlayer(rightHandAttack)); // At runtime, disable mirroring mirror.setEnabled(false); ``` ``` -------------------------------- ### AdjustmentModifier Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Applies an additive transform overlay to specific parts of a model. This modifier can adjust rotation, offset, and scale for selected parts based on a provided function. ```APIDOC ## AdjustmentModifier Additive per-part transform overlay Applies an additional `Vec3f` rotation, offset, and/or scale on top of the running animation for any subset of parts. The function receives the part name and returns an `Optional` — return `Optional.empty()` for parts that should not be adjusted. Automatically fades in/out based on the `KeyframeAnimationPlayer`'s `beginTick`/`stopTick`, and supports an explicit `fadeOut(ticks)` call. ### Usage ```java // Tilt head and arms with the player's pitch (vertical look angle) AdjustmentModifier pitchAdjust = new AdjustmentModifier((partName) -> { float pitch = (float) Math.toRadians(localPlayer.getPitch() / 2f); return switch (partName) { case "body" -> Optional.of(new AdjustmentModifier.PartModifier( new Vec3f(-pitch, 0, 0), // rotation Vec3f.ZERO // offset )); case "rightArm", "leftArm" -> Optional.of(new AdjustmentModifier.PartModifier( new Vec3f(pitch, 0, 0), Vec3f.ZERO )); default -> Optional.empty(); // no adjustment }; }); layer.addModifierBefore(pitchAdjust); // Trigger a manual fade-out over 10 ticks before stopping the animation pitchAdjust.fadeOut(10); ``` ``` -------------------------------- ### SpeedModifier Source: https://context7.com/kosmx/minecraftplayeranimator/llms.txt Alters the playback rate of an animation. This modifier scales the internal tick rate, allowing animations to play faster or slower. ```APIDOC ## SpeedModifier Alter animation playback rate Wraps any `IAnimation` and scales its internal tick rate. `speed = 2.0` plays the animation twice as fast (half the duration); `speed = 0.5` plays at half speed. Must be a finite float. ### Usage ```java ModifierLayer layer = new ModifierLayer<>(); SpeedModifier speed = new SpeedModifier(0.5f); // half speed layer.addModifierBefore(speed); layer.setAnimation(new KeyframeAnimationPlayer(walkAnim)); // Change speed at runtime speed.speed = 1.75f; ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.