From 32202dab97402206ec0ed01c9aed64f1403a5c50 Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 15 Apr 2026 10:48:21 +0200 Subject: [PATCH] =?UTF-8?q?feat(D-01/E):=20quickwins=20=E2=80=94=20debug?= =?UTF-8?q?=20toggle,=20HumanChairHelper=20move,=20collar=20equip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Q1: Remove F9 debug toggle from GltfAnimationApplier + delete dead GltfRenderLayer + remove keybind registration Q2: Move HumanChairHelper from state/ to util/ — pure utility with no state dependency. 7 import updates. Q3: Wire NECK collar equip flow in DataDrivenBondageItem: - Target must be tied up (V1 rule preserved) - Distance + line-of-sight validation - Owner added to NBT before equip via CollarHelper.addOwner() - V2EquipmentHelper handles conflict resolution - ModSounds.COLLAR_PUT played on success - OwnershipComponent.onEquipped registers in CollarRegistry --- .../render/DogPoseRenderHandler.java | 2 +- .../animation/render/PetBedRenderHandler.java | 2 +- .../animation/tick/AnimationTickHandler.java | 2 +- .../client/gltf/GltfAnimationApplier.java | 48 -------- .../remake/client/gltf/GltfClientSetup.java | 48 +------- .../remake/client/gltf/GltfRenderLayer.java | 106 ------------------ .../tiedup/remake/entities/EntityMaster.java | 2 +- .../ai/master/MasterHumanChairGoal.java | 2 +- .../mixin/MixinLivingEntityBodyRot.java | 2 +- .../remake/mixin/client/MixinCamera.java | 2 +- .../{state => util}/HumanChairHelper.java | 2 +- .../bondage/client/V2BondageRenderLayer.java | 4 +- .../datadriven/DataDrivenBondageItem.java | 43 ++++++- 13 files changed, 50 insertions(+), 215 deletions(-) delete mode 100644 src/main/java/com/tiedup/remake/client/gltf/GltfRenderLayer.java rename src/main/java/com/tiedup/remake/{state => util}/HumanChairHelper.java (98%) diff --git a/src/main/java/com/tiedup/remake/client/animation/render/DogPoseRenderHandler.java b/src/main/java/com/tiedup/remake/client/animation/render/DogPoseRenderHandler.java index f61f877..0c77dd7 100644 --- a/src/main/java/com/tiedup/remake/client/animation/render/DogPoseRenderHandler.java +++ b/src/main/java/com/tiedup/remake/client/animation/render/DogPoseRenderHandler.java @@ -3,7 +3,7 @@ package com.tiedup.remake.client.animation.render; import com.tiedup.remake.core.TiedUpMod; import com.tiedup.remake.items.base.PoseType; import com.tiedup.remake.v2.bondage.PoseTypeHelper; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; diff --git a/src/main/java/com/tiedup/remake/client/animation/render/PetBedRenderHandler.java b/src/main/java/com/tiedup/remake/client/animation/render/PetBedRenderHandler.java index 53e0bbd..289fb19 100644 --- a/src/main/java/com/tiedup/remake/client/animation/render/PetBedRenderHandler.java +++ b/src/main/java/com/tiedup/remake/client/animation/render/PetBedRenderHandler.java @@ -4,7 +4,7 @@ import com.tiedup.remake.client.state.PetBedClientState; import com.tiedup.remake.core.TiedUpMod; import com.tiedup.remake.items.base.PoseType; import com.tiedup.remake.v2.bondage.PoseTypeHelper; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import net.minecraft.client.player.AbstractClientPlayer; 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 4b31a49..f9b4ebd 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 @@ -17,7 +17,7 @@ import com.tiedup.remake.client.state.PetBedClientState; import com.tiedup.remake.items.base.PoseType; import com.tiedup.remake.v2.bondage.BindModeHelper; import com.tiedup.remake.v2.bondage.PoseTypeHelper; -import com.tiedup.remake.state.HumanChairHelper; +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; diff --git a/src/main/java/com/tiedup/remake/client/gltf/GltfAnimationApplier.java b/src/main/java/com/tiedup/remake/client/gltf/GltfAnimationApplier.java index 5d2f4f8..e83d879 100644 --- a/src/main/java/com/tiedup/remake/client/gltf/GltfAnimationApplier.java +++ b/src/main/java/com/tiedup/remake/client/gltf/GltfAnimationApplier.java @@ -13,8 +13,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import net.minecraft.client.Minecraft; -import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.LivingEntity; import net.minecraftforge.api.distmarker.Dist; @@ -433,52 +431,6 @@ public final class GltfAnimationApplier { ContextAnimationFactory.clearCache(); } - // LEGACY F9 DEBUG TOGGLE - - private static boolean debugEnabled = false; - - /** - * Toggle debug mode via F9 key. - * When enabled, applies handcuffs V2 animation (rightArm + leftArm) to the local player - * using STAND_IDLE context. When disabled, clears all V2 animation. - */ - public static void toggle() { - debugEnabled = !debugEnabled; - LOGGER.info( - "[GltfPipeline] Debug toggle: {}", - debugEnabled ? "ON" : "OFF" - ); - - AbstractClientPlayer player = Minecraft.getInstance().player; - if (player == null) return; - - if (debugEnabled) { - ResourceLocation modelLoc = ResourceLocation.fromNamespaceAndPath( - "tiedup", - "models/gltf/v2/handcuffs/cuffs_prototype.glb" - ); - Set armParts = Set.of("rightArm", "leftArm"); - RegionBoneMapper.BoneOwnership debugOwnership = - new RegionBoneMapper.BoneOwnership(armParts, Set.of()); - applyV2Animation( - player, - modelLoc, - null, - AnimationContext.STAND_IDLE, - debugOwnership - ); - } else { - clearV2Animation(player); - } - } - - /** - * Whether F9 debug mode is currently enabled. - */ - public static boolean isEnabled() { - return debugEnabled; - } - // INTERNAL /** diff --git a/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java b/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java index 4527318..7c2b204 100644 --- a/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java +++ b/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java @@ -1,11 +1,9 @@ package com.tiedup.remake.client.gltf; -import com.mojang.blaze3d.platform.InputConstants; import com.tiedup.remake.client.animation.context.ContextAnimationFactory; import com.tiedup.remake.client.animation.context.ContextGlbRegistry; import com.tiedup.remake.v2.bondage.client.V2BondageRenderLayer; import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemReloadListener; -import net.minecraft.client.KeyMapping; import net.minecraft.client.renderer.entity.player.PlayerRenderer; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.SimplePreparableReloadListener; @@ -13,8 +11,6 @@ import net.minecraft.util.profiling.ProfilerFiller; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.EntityRenderersEvent; import net.minecraftforge.client.event.RegisterClientReloadListenersEvent; -import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; @@ -23,24 +19,16 @@ import org.apache.logging.log4j.Logger; /** * Forge event registration for the glTF pipeline. - * Registers keybind (F9), render layers, and animation factory. + * Registers render layers and animation factory. */ public final class GltfClientSetup { private static final Logger LOGGER = LogManager.getLogger("GltfPipeline"); - private static final String KEY_CATEGORY = "key.categories.tiedup"; - static final KeyMapping TOGGLE_KEY = new KeyMapping( - "key.tiedup.gltf_toggle", - InputConstants.Type.KEYSYM, - InputConstants.KEY_F9, - KEY_CATEGORY - ); - private GltfClientSetup() {} /** - * MOD bus event subscribers (FMLClientSetupEvent, RegisterKeyMappings, AddLayers). + * MOD bus event subscribers (FMLClientSetupEvent, AddLayers). */ @Mod.EventBusSubscriber( modid = "tiedup", @@ -58,21 +46,11 @@ public final class GltfClientSetup { }); } - @SubscribeEvent - public static void onRegisterKeybindings( - RegisterKeyMappingsEvent event - ) { - event.register(TOGGLE_KEY); - LOGGER.info("[GltfPipeline] Keybind registered: F9"); - } - @SuppressWarnings("unchecked") @SubscribeEvent public static void onAddLayers(EntityRenderersEvent.AddLayers event) { - // Add GltfRenderLayer (prototype/debug with F9 toggle) to player renderers var defaultRenderer = event.getSkin("default"); if (defaultRenderer instanceof PlayerRenderer playerRenderer) { - playerRenderer.addLayer(new GltfRenderLayer(playerRenderer)); playerRenderer.addLayer( new V2BondageRenderLayer<>(playerRenderer) ); @@ -81,10 +59,9 @@ public final class GltfClientSetup { ); } - // Add both layers to slim player renderer (Alex) + // Add V2 layer to slim player renderer (Alex) var slimRenderer = event.getSkin("slim"); if (slimRenderer instanceof PlayerRenderer playerRenderer) { - playerRenderer.addLayer(new GltfRenderLayer(playerRenderer)); playerRenderer.addLayer( new V2BondageRenderLayer<>(playerRenderer) ); @@ -143,23 +120,4 @@ public final class GltfClientSetup { } } - /** - * FORGE bus event subscribers (ClientTickEvent for keybind toggle). - */ - @Mod.EventBusSubscriber( - modid = "tiedup", - bus = Mod.EventBusSubscriber.Bus.FORGE, - value = Dist.CLIENT - ) - public static class ForgeBusEvents { - - @SubscribeEvent - public static void onClientTick(TickEvent.ClientTickEvent event) { - if (event.phase != TickEvent.Phase.END) return; - - while (TOGGLE_KEY.consumeClick()) { - GltfAnimationApplier.toggle(); - } - } - } } diff --git a/src/main/java/com/tiedup/remake/client/gltf/GltfRenderLayer.java b/src/main/java/com/tiedup/remake/client/gltf/GltfRenderLayer.java deleted file mode 100644 index fe54166..0000000 --- a/src/main/java/com/tiedup/remake/client/gltf/GltfRenderLayer.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.tiedup.remake.client.gltf; - -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.Minecraft; -import net.minecraft.client.model.PlayerModel; -import net.minecraft.client.player.AbstractClientPlayer; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.entity.RenderLayerParent; -import net.minecraft.client.renderer.entity.layers.RenderLayer; -import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.joml.Matrix4f; - -/** - * RenderLayer that renders the glTF mesh (handcuffs) on the player. - * Only active when enabled and only renders on the local player. - *

- * Uses the live skinning path: reads live skeleton from HumanoidModel - * via {@link GltfLiveBoneReader}, following PlayerAnimator + bendy-lib rotations. - * Falls back to GLB-internal skinning via {@link GltfSkinningEngine} if live reading fails. - */ -@OnlyIn(Dist.CLIENT) -public class GltfRenderLayer - extends RenderLayer> -{ - - private static final Logger LOGGER = LogManager.getLogger("GltfPipeline"); - - private static final ResourceLocation CUFFS_MODEL = - ResourceLocation.fromNamespaceAndPath( - "tiedup", - "models/gltf/v2/handcuffs/cuffs_prototype.glb" - ); - - public GltfRenderLayer( - RenderLayerParent< - AbstractClientPlayer, - PlayerModel - > renderer - ) { - super(renderer); - } - - /** - * The Y translate offset to place the glTF mesh in the MC PoseStack. - *

- * After LivingEntityRenderer's scale(-1,-1,1) + translate(0,-1.501,0), - * the PoseStack origin is at the model top (1.501 blocks above feet), Y-down. - * The glTF mesh (MC-converted) has feet at Y=0 and head at Y≈-1.5. - * Translating by 1.501 maps glTF feet to PoseStack feet and head to top. - */ - private static final float ALIGNMENT_Y = 1.501f; - - @Override - public void render( - PoseStack poseStack, - MultiBufferSource buffer, - int packedLight, - AbstractClientPlayer entity, - float limbSwing, - float limbSwingAmount, - float partialTick, - float ageInTicks, - float netHeadYaw, - float headPitch - ) { - if (!GltfAnimationApplier.isEnabled()) return; - if (entity != Minecraft.getInstance().player) return; - - GltfData data = GltfCache.get(CUFFS_MODEL); - if (data == null) return; - - // Live path: read skeleton from HumanoidModel (after PlayerAnimator) - PlayerModel parentModel = this.getParentModel(); - Matrix4f[] joints = GltfLiveBoneReader.computeJointMatricesFromModel( - parentModel, - data, - entity - ); - if (joints == null) { - // Fallback to GLB-internal path if live reading fails - joints = GltfSkinningEngine.computeJointMatrices(data); - } - - poseStack.pushPose(); - - // Align glTF mesh with MC model (feet-to-feet alignment) - poseStack.translate(0, ALIGNMENT_Y, 0); - - GltfMeshRenderer.renderSkinned( - data, - joints, - poseStack, - buffer, - packedLight, - net.minecraft.client.renderer.entity.LivingEntityRenderer.getOverlayCoords( - entity, - 0.0f - ) - ); - poseStack.popPose(); - } -} diff --git a/src/main/java/com/tiedup/remake/entities/EntityMaster.java b/src/main/java/com/tiedup/remake/entities/EntityMaster.java index 0c52da9..aaa9be8 100644 --- a/src/main/java/com/tiedup/remake/entities/EntityMaster.java +++ b/src/main/java/com/tiedup/remake/entities/EntityMaster.java @@ -1072,7 +1072,7 @@ public class EntityMaster extends EntityKidnapperElite { if ( tag != null && tag.getBoolean( - com.tiedup.remake.state.HumanChairHelper.NBT_KEY + com.tiedup.remake.util.HumanChairHelper.NBT_KEY ) ) { bindState.unequip(BodyRegionV2.ARMS); diff --git a/src/main/java/com/tiedup/remake/entities/ai/master/MasterHumanChairGoal.java b/src/main/java/com/tiedup/remake/entities/ai/master/MasterHumanChairGoal.java index 9fb0909..9d85789 100644 --- a/src/main/java/com/tiedup/remake/entities/ai/master/MasterHumanChairGoal.java +++ b/src/main/java/com/tiedup/remake/entities/ai/master/MasterHumanChairGoal.java @@ -5,7 +5,7 @@ import com.tiedup.remake.dialogue.DialogueBridge; import com.tiedup.remake.entities.EntityMaster; import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem; import net.minecraft.resources.ResourceLocation; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import java.util.EnumSet; diff --git a/src/main/java/com/tiedup/remake/mixin/MixinLivingEntityBodyRot.java b/src/main/java/com/tiedup/remake/mixin/MixinLivingEntityBodyRot.java index 5eca9ff..6f08da5 100644 --- a/src/main/java/com/tiedup/remake/mixin/MixinLivingEntityBodyRot.java +++ b/src/main/java/com/tiedup/remake/mixin/MixinLivingEntityBodyRot.java @@ -1,6 +1,6 @@ package com.tiedup.remake.mixin; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import net.minecraft.world.entity.LivingEntity; diff --git a/src/main/java/com/tiedup/remake/mixin/client/MixinCamera.java b/src/main/java/com/tiedup/remake/mixin/client/MixinCamera.java index 814cf5c..6b1c715 100644 --- a/src/main/java/com/tiedup/remake/mixin/client/MixinCamera.java +++ b/src/main/java/com/tiedup/remake/mixin/client/MixinCamera.java @@ -2,7 +2,7 @@ package com.tiedup.remake.mixin.client; import com.tiedup.remake.items.base.PoseType; import com.tiedup.remake.v2.bondage.PoseTypeHelper; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import net.minecraft.client.Camera; diff --git a/src/main/java/com/tiedup/remake/state/HumanChairHelper.java b/src/main/java/com/tiedup/remake/util/HumanChairHelper.java similarity index 98% rename from src/main/java/com/tiedup/remake/state/HumanChairHelper.java rename to src/main/java/com/tiedup/remake/util/HumanChairHelper.java index 73aa51b..f432dd6 100644 --- a/src/main/java/com/tiedup/remake/state/HumanChairHelper.java +++ b/src/main/java/com/tiedup/remake/util/HumanChairHelper.java @@ -1,4 +1,4 @@ -package com.tiedup.remake.state; +package com.tiedup.remake.util; import com.tiedup.remake.items.base.PoseType; import net.minecraft.nbt.CompoundTag; diff --git a/src/main/java/com/tiedup/remake/v2/bondage/client/V2BondageRenderLayer.java b/src/main/java/com/tiedup/remake/v2/bondage/client/V2BondageRenderLayer.java index 62f3b33..7434012 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/client/V2BondageRenderLayer.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/client/V2BondageRenderLayer.java @@ -46,9 +46,7 @@ import org.joml.Matrix4f; * pushPose/popPose pair. Joint matrices are computed per-GLB-model * because different GLB models have different skeletons. * - *

Unlike {@link com.tiedup.remake.client.gltf.GltfRenderLayer}, - * this layer is always active (no F9 toggle guard) and renders on - * ALL entities (no local-player-only guard). + *

This layer is always active and renders on ALL entities. */ @OnlyIn(Dist.CLIENT) public class V2BondageRenderLayer< diff --git a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java index 6d3e61c..e6a90dc 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java @@ -1,7 +1,12 @@ package com.tiedup.remake.v2.bondage.datadriven; +import com.tiedup.remake.core.ModSounds; import com.tiedup.remake.core.TiedUpMod; +import com.tiedup.remake.state.IRestrainable; +import com.tiedup.remake.util.KidnappedHelper; import com.tiedup.remake.v2.BodyRegionV2; +import com.tiedup.remake.v2.bondage.CollarHelper; +import com.tiedup.remake.v2.bondage.V2EquipResult; import com.tiedup.remake.v2.bondage.BindModeHelper; import com.tiedup.remake.v2.bondage.IV2BondageEquipment; import com.tiedup.remake.v2.bondage.TyingInteractionHelper; @@ -168,11 +173,39 @@ public class DataDrivenBondageItem extends AbstractV2BondageItem { return TyingInteractionHelper.handleTying(serverPlayer, target, stack, hand); } - // NECK: blocked — collar equip requires owner setup (add owner to NBT, - // register in CollarRegistry, play sound, sync) which is not yet wired - // through interactLivingEntity. TODO: implement collar equip flow. - if (regions.contains(BodyRegionV2.NECK)) { - TiedUpMod.LOGGER.debug("[DataDrivenBondageItem] NECK equip via right-click not yet implemented"); + // NECK: collar equip flow + if (regions.contains(BodyRegionV2.NECK) && player instanceof ServerPlayer serverPlayer) { + // Target must be tied up to collar (V1 rule preserved) + IRestrainable targetState = KidnappedHelper.getKidnappedState(target); + if (targetState == null || !targetState.isTiedUp()) { + return InteractionResult.PASS; + } + + // Distance + line-of-sight + if (player.distanceTo(target) > 4.0 || !player.hasLineOfSight(target)) { + return InteractionResult.PASS; + } + + // Add player as owner in NBT before equip + ItemStack collarCopy = stack.copy(); + CollarHelper.addOwner(collarCopy, player); + + // Equip via V2 system (handles conflict resolution, displaced items) + V2EquipResult result = V2EquipmentHelper.equipItem(target, collarCopy); + if (result.isSuccess()) { + for (ItemStack displaced : result.displaced()) { + target.spawnAtLocation(displaced); + } + stack.shrink(1); + // Play collar equip sound + target.level().playSound(null, target.getX(), target.getY(), target.getZ(), + ModSounds.COLLAR_PUT.get(), + net.minecraft.sounds.SoundSource.PLAYERS, 0.5f, 1.0f); + V2EquipmentHelper.sync(target); + TiedUpMod.LOGGER.info("[DataDrivenBondageItem] {} collared {} (owner added)", + player.getName().getString(), target.getName().getString()); + return InteractionResult.SUCCESS; + } return InteractionResult.PASS; }