diff --git a/src/main/java/com/tiedup/remake/client/animation/BondageAnimationManager.java b/src/main/java/com/tiedup/remake/client/animation/BondageAnimationManager.java index bf54669..1646e61 100644 --- a/src/main/java/com/tiedup/remake/client/animation/BondageAnimationManager.java +++ b/src/main/java/com/tiedup/remake/client/animation/BondageAnimationManager.java @@ -90,41 +90,30 @@ public class BondageAnimationManager { /** * Initialize the animation system. - * Must be called during client setup to register the player animation factory. + * + *
Phase 2.8 RIG cleanup — les 3 {@link PlayerAnimationFactory} + * (context / item / furniture) ont été supprimées : le renderer RIG + * patched ne passe plus par le pipeline PlayerAnimator pour le joueur, + * donc les factories devenaient dead code (aucun call site n'atteint + * jamais la map associée côté joueur).
+ * + *Les NPCs continuent d'être animés via cette classe : le chemin + * {@link #getOrCreateLayer} pour les entités {@code IAnimatedPlayer} + * non-joueur utilise {@code animated.getAnimationStack().addAnimLayer(...)} + * en direct — ça ne dépend d'aucune factory. Voir {@code NpcAnimationTickHandler} + * pour le consumer. Le path player dans {@link #getOrCreateLayer} est + * laissé en place volontairement : il retombe proprement sur null + * (PlayerAnimationAccess throw → catch → null) et laisse le tick RIG + * s'occuper du joueur.
+ * + *Conservé comme méthode publique pour ne pas casser les call sites + * externes et pour documenter la bascule. Rework V3 (player anim + * natives RIG) : voir V3-REW-01 dans {@code docs/plans/rig/V3_REWORK_BACKLOG.md}.
*/ public static void init() { - LOGGER.info("BondageAnimationManager initializing..."); - - // Context layer: lower priority = evaluated first, overridable by item layer. - // In AnimationStack, layers are sorted ascending by priority and evaluated in order. - // Higher priority layers override lower ones. - PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory( - CONTEXT_FACTORY_ID, - CONTEXT_LAYER_PRIORITY, - player -> new ModifierLayer<>() - ); - - // Item layer: higher priority = evaluated last, overrides context layer - PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory( - FACTORY_ID, - ITEM_LAYER_PRIORITY, - player -> new ModifierLayer<>() - ); - - // Furniture layer: highest priority = overrides item layer on blocked bones. - // Non-blocked bones are disabled via FurnitureAnimationContext so items - // can still animate free regions (gag, blindfold, etc.). - PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory( - FURNITURE_FACTORY_ID, - FURNITURE_LAYER_PRIORITY, - player -> new ModifierLayer<>() - ); - LOGGER.info( - "BondageAnimationManager: Factories registered — context (pri {}), item (pri {}), furniture (pri {})", - CONTEXT_LAYER_PRIORITY, - ITEM_LAYER_PRIORITY, - FURNITURE_LAYER_PRIORITY + "BondageAnimationManager: player-side factories no-op (Phase 2.8 RIG cleanup). " + + "NPC-side animation stack access untouched." ); } diff --git a/src/main/java/com/tiedup/remake/client/animation/tick/AnimationTickHandler.java b/src/main/java/com/tiedup/remake/client/animation/tick/AnimationTickHandler.java index 4160468..332ea42 100644 --- a/src/main/java/com/tiedup/remake/client/animation/tick/AnimationTickHandler.java +++ b/src/main/java/com/tiedup/remake/client/animation/tick/AnimationTickHandler.java @@ -3,29 +3,17 @@ package com.tiedup.remake.client.animation.tick; import com.mojang.logging.LogUtils; import com.tiedup.remake.client.animation.AnimationStateRegistry; import com.tiedup.remake.client.animation.BondageAnimationManager; -import com.tiedup.remake.client.animation.PendingAnimationManager; -import com.tiedup.remake.client.animation.context.AnimationContext; -import com.tiedup.remake.client.animation.context.AnimationContextResolver; -import com.tiedup.remake.client.animation.context.RegionBoneMapper; import com.tiedup.remake.client.events.CellHighlightHandler; import com.tiedup.remake.client.events.LeashProxyClientHandler; import com.tiedup.remake.client.gltf.GltfAnimationApplier; import com.tiedup.remake.client.state.ClothesClientCache; import com.tiedup.remake.client.state.MovementStyleClientState; import com.tiedup.remake.client.state.PetBedClientState; -import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; -import com.tiedup.remake.v2.BodyRegionV2; -import com.tiedup.remake.v2.bondage.IV2BondageEquipment; -import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper; -import com.tiedup.remake.v2.bondage.movement.MovementStyle; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import net.minecraft.client.Minecraft; -import net.minecraft.client.player.AbstractClientPlayer; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.event.TickEvent; @@ -35,16 +23,29 @@ import net.minecraftforge.fml.common.Mod; import org.slf4j.Logger; /** - * Event handler for player animation tick updates. + * Event handler for animation tick updates. * - *Simplified handler that: + *
Phase 2.8 RIG cleanup : le ticking player V2 (boucle + * {@code mc.level.players()} + {@code updatePlayerAnimation}) est entièrement + * désactivé. Les joueurs sont désormais pilotés par + * {@link com.tiedup.remake.rig.tick.RigAnimationTickHandler} via le pipeline + * RIG (capability {@code LivingEntityPatch} + {@code Animator} natif EF). + * Les features V2 qui dépendaient du tick player sont trackées dans + * {@code docs/plans/rig/V3_REWORK_BACKLOG.md} (V3-REW-01/02/03/07).
+ * + *Restent actifs ici : *
Registered on the FORGE event bus (not MOD bus). + *
Le ticking NPC est assuré par {@link NpcAnimationTickHandler}. Ce + * handler ne tick plus les NPCs directement — il ne gère que les hooks + * lifecycle globaux (logout + world unload).
+ * + *Registered on the FORGE event bus (not MOD bus).
*/ @Mod.EventBusSubscriber( modid = "tiedup", @@ -83,8 +84,20 @@ public class AnimationTickHandler { } /** - * Client tick event - called every tick on the client. - * Updates animations for all players when their bondage state changes. + * Client tick event — called every tick on the client. + * + *Phase 2.8 : la boucle {@code mc.level.players()} qui appelait + * {@code updatePlayerAnimation}, {@code tickFurnitureSafety} et le + * cold-cache retry furniture a été entièrement supprimée. Les joueurs + * sont désormais ticked par {@link com.tiedup.remake.rig.tick.RigAnimationTickHandler} + * via le pipeline RIG (capability {@code LivingEntityPatch} + + * {@code Animator}). Les régressions visuelles (V2 bondage layer cassé, + * furniture seat pose sur joueur cassée, pet bed pose cassée) sont + * listées dans {@code docs/plans/rig/V3_REWORK_BACKLOG.md}.
+ * + *Seul le nettoyage périodique de {@link ClothesClientCache} reste + * — c'est de l'hygiène mémoire sur un cache indexé UUID joueur, + * indépendant du pipeline de rendu.
*/ @SubscribeEvent public static void onClientTick(TickEvent.ClientTickEvent event) { @@ -97,193 +110,17 @@ public class AnimationTickHandler { return; } - // Process pending animations first (retry failed animations for remote players) - PendingAnimationManager.processPending(mc.level); - - // Periodic cleanup of stale cache entries (every 60 seconds = 1200 ticks) + // Periodic cleanup of stale clothes cache entries (every 60 seconds = 1200 ticks). + // Indépendant du rendu V2/RIG — c'est juste un cache UUID→ClothesData qui + // doit libérer la mémoire des joueurs déconnectés depuis >5min. if (++cleanupTickCounter >= 1200) { cleanupTickCounter = 0; ClothesClientCache.cleanupStale(); } - // Then update all player animations - for (Player player : mc.level.players()) { - if (player instanceof AbstractClientPlayer clientPlayer) { - updatePlayerAnimation(clientPlayer); - } - // Safety: remove stale furniture animations for players no longer on seats - BondageAnimationManager.tickFurnitureSafety(player); - // Cold-cache retry: if the player is seated on furniture but has no - // active pose (GLB was not yet loaded at mount time, or the GLB cache - // entry was a transient failure), retry until the cache warms. - // FurnitureGltfCache memoizes failures via Optional.empty(), so - // retries after a genuine parse failure return instantly with no - // reparse. Bounded at MAX_FURNITURE_RETRIES so a legacy V1-only - // GLB (no Player_* armature → seatSkeleton==null → no animation - // ever possible) doesn't spam retries at 20 Hz forever. - // Single read of getVehicle() — avoids a re-read where the - // vehicle could change between instanceof and cast. - com.tiedup.remake.v2.furniture.EntityFurniture furniture = - player.getVehicle() instanceof - com.tiedup.remake.v2.furniture.EntityFurniture f ? f : null; - boolean hasAnim = BondageAnimationManager.hasFurnitureAnimation( - player - ); - UUID playerUuid = player.getUUID(); - if (furniture != null && !hasAnim) { - int retries = furnitureRetryCounters.getOrDefault( - playerUuid, - 0 - ); - if (retries < MAX_FURNITURE_RETRIES) { - furnitureRetryCounters.put(playerUuid, retries + 1); - com.tiedup.remake.v2.furniture.client.FurnitureClientAnimator - .start(furniture, player); - if (retries + 1 == MAX_FURNITURE_RETRIES) { - LOGGER.debug( - "[FurnitureAnim] Giving up on furniture animation retry for {} after {} attempts — GLB likely has no Player_* armature.", - player.getName().getString(), - MAX_FURNITURE_RETRIES - ); - } - } - } else { - // Dismounted or successfully applied — drop the counter so a - // later re-mount starts fresh. - furnitureRetryCounters.remove(playerUuid); - } - } - } - - /** - * Update animation for a single player. - */ - private static void updatePlayerAnimation(AbstractClientPlayer player) { - // Safety check: skip for removed/dead players - if (player.isRemoved() || !player.isAlive()) { - return; - } - - PlayerBindState state = PlayerBindState.getInstance(player); - UUID uuid = player.getUUID(); - - // Check if player has ANY V2 bondage item equipped (not just ARMS). - // isTiedUp() only checks ARMS, but items on LEGS, HEAD, etc. also need animation. - boolean isTied = - state != null && - (state.isTiedUp() || V2EquipmentHelper.hasAnyEquipment(player)); - boolean wasTied = - AnimationStateRegistry.getLastTiedState().getOrDefault(uuid, false); - - // Pet bed animations take priority over bondage animations - if (PetBedClientState.get(uuid) != 0) { - // Lock body rotation to bed facing (prevents camera from rotating the model) - float lockedRot = PetBedClientState.getFacing(uuid); - player.yBodyRot = lockedRot; - player.yBodyRotO = lockedRot; - - // Clamp head rotation to ±50° from body (like vehicle) - float headRot = player.getYHeadRot(); - float clamped = - lockedRot + - net.minecraft.util.Mth.clamp( - net.minecraft.util.Mth.wrapDegrees(headRot - lockedRot), - -50f, - 50f - ); - player.setYHeadRot(clamped); - player.yHeadRotO = clamped; - - AnimationStateRegistry.getLastTiedState().put(uuid, isTied); - return; - } - - // Human chair: clamp 1st-person camera only (body lock handled by MixinLivingEntityBodyRot) - // NO return — animation HUMAN_CHAIR must continue playing below - if (isTied && state != null) { - ItemStack chairBind = state.getEquipment(BodyRegionV2.ARMS); - if (HumanChairHelper.isActive(chairBind)) { - // 1st person only: clamp yRot so player can't look behind - // 3rd person: yRot untouched → camera orbits freely 360° - if ( - player == Minecraft.getInstance().player && - Minecraft.getInstance().options.getCameraType() == - net.minecraft.client.CameraType.FIRST_PERSON - ) { - float lockedRot = HumanChairHelper.getFacing(chairBind); - float camClamped = - lockedRot + - net.minecraft.util.Mth.clamp( - net.minecraft.util.Mth.wrapDegrees( - player.getYRot() - lockedRot - ), - -90f, - 90f - ); - player.setYRot(camClamped); - player.yRotO = - lockedRot + - net.minecraft.util.Mth.clamp( - net.minecraft.util.Mth.wrapDegrees( - player.yRotO - lockedRot - ), - -90f, - 90f - ); - } - } - } - - if (isTied) { - // Resolve V2 equipped items - IV2BondageEquipment equipment = V2EquipmentHelper.getEquipment( - player - ); - Map- * ┌─────────────────────────────────────────────────────────────────┐ - * │ PLAYERS │ - * ├─────────────────────────────────────────────────────────────────┤ - * │ 1. PlayerArmHideEventHandler.onRenderPlayerPre() │ - * │ - Offset vertical (-6 model units) │ - * │ - Rotation Y lissée (dogPoseState tracking) │ - * │ │ - * │ 2. Animation (PlayerAnimator) │ - * │ - body.pitch = -90° → appliqué au PoseStack automatiquement │ - * │ │ - * │ 3. MixinPlayerModel.setupAnim() @TAIL │ - * │ - Uses DogPoseHelper.applyHeadCompensationClamped() │ - * └─────────────────────────────────────────────────────────────────┘ + *Architecture — NPCs only (Phase 2.8 RIG cleanup)
+ *Le path PLAYER (ex-{@code MixinPlayerModel.setupAnim @TAIL}) a été retiré + * Phase 2.8 : le renderer RIG patched ne passe plus par {@code PlayerModel.setupAnim}, + * donc le mixin devenait dead code. La compensation head dog pose sera ré-exprimée + * nativement en StaticAnimation {@code pose_dog.json} (cf. V3-REW-07 dans + * {@code docs/plans/rig/V3_REWORK_BACKLOG.md}).
* + ** ┌─────────────────────────────────────────────────────────────────┐ * │ NPCs │ * ├─────────────────────────────────────────────────────────────────┤ @@ -48,25 +40,13 @@ import net.minecraftforge.api.distmarker.OnlyIn; * └─────────────────────────────────────────────────────────────────┘ ** - *Key Differences
- *
| Aspect | Players | NPCs |
|---|---|---|
| Rotation X application | Auto by PlayerAnimator | Manual in setupRotations() |
| Rotation Y smoothing | PlayerArmHideEventHandler | EntityDamsel.tick() via RotationSmoother |
| Head compensation | MixinPlayerModel | DamselModel.setupAnim() |
| Reset body.xRot | Not needed | Yes (prevents double rotation) |
| Vertical offset | -6 model units | -7 model units |
Used by: *