FeatureRenderer on the player model, which in turn renders several client-side "dummy" GeoEntity instances—one for each shoulder/head slot. Each dummy entity has its own AnimationController with a unique name based on its instance ID.HamsterShoulderFeatureRenderer.java: The core class that manages the dummy entities and initiates their rendering. This is the most important file.ClientShoulderHamsterData.java: Shows how the animation state for each shoulder pet is managed and ticked independently on the client thread.ShoulderHamsterState.java: The state machine class that determines which animation should be playing for a single shoulder pet.HamsterEntity.java: Specifically the registerControllers method, so they can see how the GeckoLib animation controller is set up.ShoulderHamsterRenderer.java: The specialized GeoEntityRenderer used for the dummy entities.
AnimatableManager. My guess is that when Iris's batch rendering processes multiple GeoEntity instances in the same frame, the manager for the first entity would get its lastUpdateTime set. When the renderer immediately moved to the next entity in the batch, the time delta since the last update was near-zero, causing GeckoLib to (correctly, from its perspective) skip the animation tick for that entity. This resulted in it being rendered with the stale bone data from the previously rendered entity.AnimatableManager to re-evaluate its state for each dummy entity right before its render call. By setting its lastUpdateTime to 0, I trick the manager into thinking a very long time has passed, guaranteeing it runs a full animation update and ignores any polluted state from other entities in the batch.