Refactor V2 animation, furniture, and GLTF rendering

Broad consolidation of the V2 bondage-item, furniture-entity, and
client-side GLTF pipeline.

Parsing and rendering
  - Shared GLB parsing helpers consolidated into GlbParserUtils
    (accessor reads, weight normalization, joint-index clamping,
    coordinate-space conversion, animation parse, primitive loop).
  - Grow-on-demand Matrix4f[] scratch pool in GltfSkinningEngine and
    GltfLiveBoneReader — removes per-frame joint-matrix allocation
    from the render hot path.
  - emitVertex helper dedups three parallel loops in GltfMeshRenderer.
  - TintColorResolver.resolve has a zero-alloc path when the item
    declares no tint channels.
  - itemAnimCache bounded to 256 entries (access-order LRU) with
    atomic get-or-compute under the map's monitor.

Animation correctness
  - First-in-joint-order wins when body and torso both map to the
    same PlayerAnimator slot; duplicate writes log a single WARN.
  - Multi-item composites honor the FullX / FullHeadX opt-in that
    the single-item path already recognized.
  - Seat transforms converted to Minecraft model-def space so
    asymmetric furniture renders passengers at the correct offset.
  - GlbValidator: IBM count / type / presence, JOINTS_0 presence,
    animation channel target validation, multi-skin support.

Furniture correctness and anti-exploit
  - Seat assignment synced via SynchedEntityData (server is
    authoritative; eliminates client-server divergence on multi-seat).
  - Force-mount authorization requires same dimension and a free
    seat; cross-dimension distance checks rejected.
  - Reconnection on login checks for seat takeover before re-mount
    and force-loads the target chunk for cross-dimension cases.
  - tiedup_furniture_lockpick_ctx carries a session UUID nonce so
    stale context can't misroute a body-item lockpick.
  - tiedup_locked_furniture survives death without keepInventory
    (Forge 1.20.1 does not auto-copy persistent data on respawn).

Lifecycle and memory
  - EntityCleanupHandler fans EntityLeaveLevelEvent out to every
    per-entity state map on the client.
  - DogPoseRenderHandler re-keyed by UUID (stable across dimension
    change; entity int ids are recycled).
  - PetBedRenderHandler, PlayerArmHideEventHandler, and
    HeldItemHideHandler use receiveCanceled + sentinel sets so
    Pre-time mutations are restored even when a downstream handler
    cancels the render.

Tests
  - JUnit harness with 76+ tests across GlbParserUtils, GltfPoseConverter,
    FurnitureSeatGeometry, and FurnitureAuthPredicate.
This commit is contained in:
NotEvil
2026-04-18 17:34:03 +02:00
parent 17815873ac
commit 355e2936c9
63 changed files with 4965 additions and 2226 deletions

View File

@@ -1,8 +1,19 @@
package com.tiedup.remake.client.events;
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.render.DogPoseRenderHandler;
import com.tiedup.remake.client.animation.render.HeldItemHideHandler;
import com.tiedup.remake.client.animation.render.PetBedRenderHandler;
import com.tiedup.remake.client.animation.render.PlayerArmHideEventHandler;
import com.tiedup.remake.client.animation.tick.AnimationTickHandler;
import com.tiedup.remake.client.animation.tick.MCAAnimationTickCache;
import com.tiedup.remake.client.animation.tick.NpcAnimationTickHandler;
import com.tiedup.remake.client.gltf.GltfAnimationApplier;
import com.tiedup.remake.client.state.MovementStyleClientState;
import com.tiedup.remake.client.state.PetBedClientState;
import com.tiedup.remake.core.TiedUpMod;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.entity.EntityLeaveLevelEvent;
@@ -11,16 +22,10 @@ import net.minecraftforge.fml.common.Mod;
import org.slf4j.Logger;
/**
* Automatic cleanup handler for entity-related resources.
*
* <p>This handler automatically cleans up animation layers and pending animations
* when entities leave the world, preventing memory leaks from stale cache entries.
*
* <p>Phase: Performance & Memory Management
*
* <p>Previously, cleanup had to be called manually via {@link BondageAnimationManager#cleanup(java.util.UUID)},
* which was error-prone and could lead to memory leaks if forgotten.
* This handler ensures cleanup happens automatically on entity removal.
* Fans out {@link EntityLeaveLevelEvent} to every per-entity state map on
* the client — the single source of truth for "entity is gone, drop its
* tracked state". Each target owns its own static map; this handler
* ensures none of them leak entries for dead/unloaded entities.
*/
@Mod.EventBusSubscriber(
modid = TiedUpMod.MOD_ID,
@@ -56,15 +61,25 @@ public class EntityCleanupHandler {
return;
}
// Clean up animation layers
BondageAnimationManager.cleanup(event.getEntity().getUUID());
java.util.UUID uuid = event.getEntity().getUUID();
// Clean up pending animation queue
PendingAnimationManager.remove(event.getEntity().getUUID());
BondageAnimationManager.cleanup(uuid);
PendingAnimationManager.remove(uuid);
GltfAnimationApplier.removeTracking(uuid);
NpcAnimationTickHandler.remove(uuid);
MovementStyleClientState.clear(uuid);
PetBedClientState.clear(uuid);
PetBedRenderHandler.onEntityLeave(uuid);
AnimationTickHandler.removeFurnitureRetry(uuid);
AnimationStateRegistry.getLastTiedState().remove(uuid);
DogPoseRenderHandler.onEntityLeave(uuid);
PlayerArmHideEventHandler.onEntityLeave(event.getEntity().getId());
HeldItemHideHandler.onEntityLeave(event.getEntity().getId());
MCAAnimationTickCache.remove(uuid);
LOGGER.debug(
"Auto-cleaned animation resources for entity: {} (type: {})",
event.getEntity().getUUID(),
uuid,
event.getEntity().getClass().getSimpleName()
);
}