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.
87 lines
3.4 KiB
Java
87 lines
3.4 KiB
Java
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;
|
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
import net.minecraftforge.fml.common.Mod;
|
|
import org.slf4j.Logger;
|
|
|
|
/**
|
|
* 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,
|
|
bus = Mod.EventBusSubscriber.Bus.FORGE,
|
|
value = Dist.CLIENT
|
|
)
|
|
public class EntityCleanupHandler {
|
|
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
|
|
/**
|
|
* Automatically clean up animation resources when an entity leaves the world.
|
|
*
|
|
* <p>This event fires when:
|
|
* <ul>
|
|
* <li>An entity is removed from the world (killed, despawned, unloaded)</li>
|
|
* <li>A player logs out</li>
|
|
* <li>A chunk is unloaded and its entities are removed</li>
|
|
* </ul>
|
|
*
|
|
* <p>Cleanup includes:
|
|
* <ul>
|
|
* <li>Removing animation layers from {@link BondageAnimationManager}</li>
|
|
* <li>Removing pending animations from {@link PendingAnimationManager}</li>
|
|
* </ul>
|
|
*
|
|
* @param event The entity leave level event
|
|
*/
|
|
@SubscribeEvent
|
|
public static void onEntityLeaveLevel(EntityLeaveLevelEvent event) {
|
|
// Only process on client side
|
|
if (!event.getLevel().isClientSide()) {
|
|
return;
|
|
}
|
|
|
|
java.util.UUID uuid = 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: {})",
|
|
uuid,
|
|
event.getEntity().getClass().getSimpleName()
|
|
);
|
|
}
|
|
}
|