From 79fc470aa034922abdd8e7d9c9f2e10f40067f06 Mon Sep 17 00:00:00 2001
From: notevil
Date: Wed, 22 Apr 2026 22:12:44 +0200
Subject: [PATCH] =?UTF-8?q?Phase=202.4=20:=20=C3=A9toffer=20PlayerPatch=20?=
=?UTF-8?q?stubs=20+=20biped=20armature=20proc=C3=A9durale?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
PlayerPatch : getArmature→TiedUpArmatures.BIPED, overrideRender→true,
getModelMatrix avec PLAYER_SCALE=15/16, initAnimator bind IDLE→EMPTY.
ServerPlayerPatch : ré-override overrideRender→false (le serveur ne rend pas).
ClientPlayerPatch : stub isFirstPersonHidden() pour Phase 3 (V3-REW-01).
LocalPlayerPatch : override render→false si on est soi-même en first-person
(laisse rendre les bras vanilla), true sinon.
TiedUpArmatures.BIPED : HumanoidArmature procédurale 20 joints identity
(Root → Thigh/Leg/Knee_R/L, Torso → Chest → Head + Shoulder/Arm/Elbow/
Hand/Tool_R/L). Phase 2.7 remplacera par JSON Blender-authored.
Fixe P2-RISK-01 (InitAnimatorEvent listeners NPE si getArmature()=null).
Tests : 11 bridge tests GREEN, full suite GREEN, compile clean.
---
.../tiedup/remake/rig/TiedUpArmatures.java | 193 ++++++++++++++++++
.../remake/rig/patch/ClientPlayerPatch.java | 69 ++++---
.../remake/rig/patch/LocalPlayerPatch.java | 56 ++++-
.../tiedup/remake/rig/patch/PlayerPatch.java | 147 ++++++++++++-
.../remake/rig/patch/ServerPlayerPatch.java | 64 +++---
5 files changed, 450 insertions(+), 79 deletions(-)
create mode 100644 src/main/java/com/tiedup/remake/rig/TiedUpArmatures.java
diff --git a/src/main/java/com/tiedup/remake/rig/TiedUpArmatures.java b/src/main/java/com/tiedup/remake/rig/TiedUpArmatures.java
new file mode 100644
index 0000000..c0f7d34
--- /dev/null
+++ b/src/main/java/com/tiedup/remake/rig/TiedUpArmatures.java
@@ -0,0 +1,193 @@
+/*
+ * Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
+ * by the Epic Fight Team, licensed under GPLv3.
+ * Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
+ */
+
+package com.tiedup.remake.rig;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import net.minecraft.resources.ResourceLocation;
+
+import com.tiedup.remake.rig.armature.HumanoidArmature;
+import com.tiedup.remake.rig.armature.Joint;
+import com.tiedup.remake.rig.asset.AssetAccessor;
+import com.tiedup.remake.rig.math.OpenMatrix4f;
+
+/**
+ * Registry des armatures TiedUp exposé via {@link AssetAccessor} constants.
+ *
+ * Phase 2.4 — version procédurale
+ *
+ * Cette classe construit le biped TiedUp from scratch en Java
+ * (hiérarchie + offsets identity). Suffisant pour débloquer le rendering RIG
+ * Phase 2.4 : les joints existent dans le map, {@code searchJointByName}
+ * fonctionne, le GLB → SkinnedMesh bridge a un mapping valide, etc.
+ *
+ * Phase 2.7 remplacera par un JSON Blender-authored hot-reloadable.
+ * Pour l'instant, les joints sont tous à l'identité (offset/rotation nuls).
+ * Visuellement ça donnera un biped "effondré" sur le point d'origine si on
+ * rend sans animation — c'est acceptable car :
+ *
+ * - Phase 2.4 n'a pas encore de renderer player patched complet (Phase 2.5)
+ * - Phase 2.7 rechargera des offsets depuis {@code assets/tiedup/armatures/biped.json}
+ * co-authored via addon Blender (cf. MIGRATION.md §2.2.1)
+ * - Les tests existants `GltfToSkinnedMeshTest` utilisent déjà le même pattern
+ * (Armature identity, {@code bakeOriginMatrices}) et sont verts
+ *
+ *
+ * Hiérarchie biped EF (20 joints)
+ *
+ *
+ * Root
+ * ├─ Thigh_R ── Leg_R ── Knee_R
+ * ├─ Thigh_L ── Leg_L ── Knee_L
+ * └─ Torso
+ * └─ Chest
+ * ├─ Head
+ * ├─ Shoulder_R ── Arm_R ── Elbow_R ── Hand_R ── Tool_R
+ * └─ Shoulder_L ── Arm_L ── Elbow_L ── Hand_L ── Tool_L
+ *
+ *
+ * Noms conservés verbatim EF (pas renommés en TiedUp style) car :
+ *
+ * - Le {@code VanillaModelTransformer} forké EF (Phase 2.2) référence ces
+ * noms dans ses AABB / {@code WEIGHT_ALONG_Y} / {@code yClipCoord}
+ * - Le bridge GLB ({@code LegacyJointNameMapper}) mappe déjà les joints
+ * PlayerAnimator legacy sur ces noms-là
+ * - Re-authored serait un risque régression sans gain fonctionnel
+ *
+ */
+public final class TiedUpArmatures {
+
+ private TiedUpArmatures() {}
+
+ /** ResourceLocation registry pour l'accessor (même path que EF pour cohérence doc). */
+ private static final ResourceLocation BIPED_REGISTRY_NAME =
+ ResourceLocation.fromNamespaceAndPath(TiedUpRigConstants.MODID, "armature/biped");
+
+ /**
+ * Singleton biped — chargé à la première ref (init order :
+ * {@link TiedUpRigConstants} → chaînes d'ids → ici via
+ * {@link com.tiedup.remake.rig.patch.PlayerPatch#getArmature()}).
+ *
+ * Si construit plus tôt (ex. static init combat-lourd avant FMLCommonSetup)
+ * risquerait de toucher des classes client — garder cet accès lazy si les
+ * call sites évoluent.
+ */
+ private static HumanoidArmature BIPED_INSTANCE;
+
+ /**
+ * AssetAccessor biped biped TiedUp. Construit à la première lecture de
+ * {@link AssetAccessor#get()}.
+ *
+ * Utilisé par {@link com.tiedup.remake.rig.patch.PlayerPatch#getArmature()}
+ * et par les futurs {@code StaticAnimation(… , BIPED)} Phase 2.7+.
+ */
+ public static final AssetAccessor BIPED = new AssetAccessor<>() {
+ @Override
+ public HumanoidArmature get() {
+ if (BIPED_INSTANCE == null) {
+ BIPED_INSTANCE = buildBiped();
+ }
+ return BIPED_INSTANCE;
+ }
+
+ @Override
+ public ResourceLocation registryName() {
+ return BIPED_REGISTRY_NAME;
+ }
+
+ @Override
+ public boolean inRegistry() {
+ // Pas dans un JsonAssetLoader registry tant que Phase 2.7 n'a pas
+ // posé le biped.json. Une fois fait, ce flag repassera à true via
+ // un nouveau registry layer.
+ return false;
+ }
+ };
+
+ /**
+ * Build procédural de la hiérarchie biped EF. 20 joints, IDs 0..19 dans
+ * l'ordre d'insertion (important pour {@link Joint#getId()} stabilité
+ * inter-runs).
+ *
+ * Toutes les transforms sont identity — Phase 2.7 remplacera par les
+ * offsets Blender mesurés (cf. doc header).
+ */
+ private static HumanoidArmature buildBiped() {
+ // Tous les joints (ID == ordre d'insertion dans la map).
+ // On utilise LinkedHashMap pour garantir l'ordre d'itération stable
+ // (OpenMatrix4f.allocateMatrixArray dimensionne sur jointCount).
+ Map joints = new LinkedHashMap<>(20);
+
+ // Pattern EF : tous les joints démarrent avec une localTransform identity.
+ // bakeOriginMatrices() calcule ensuite les toOrigin relatives à la
+ // hiérarchie parent→enfant.
+ Joint root = joint(joints, "Root", 0);
+
+ // Jambes
+ Joint thighR = joint(joints, "Thigh_R", 1);
+ Joint legR = joint(joints, "Leg_R", 2);
+ Joint kneeR = joint(joints, "Knee_R", 3);
+ Joint thighL = joint(joints, "Thigh_L", 4);
+ Joint legL = joint(joints, "Leg_L", 5);
+ Joint kneeL = joint(joints, "Knee_L", 6);
+
+ // Tronc
+ Joint torso = joint(joints, "Torso", 7);
+ Joint chest = joint(joints, "Chest", 8);
+ Joint head = joint(joints, "Head", 9);
+
+ // Bras droit
+ Joint shoulderR = joint(joints, "Shoulder_R", 10);
+ Joint armR = joint(joints, "Arm_R", 11);
+ Joint elbowR = joint(joints, "Elbow_R", 12);
+ Joint handR = joint(joints, "Hand_R", 13);
+ Joint toolR = joint(joints, "Tool_R", 14);
+
+ // Bras gauche
+ Joint shoulderL = joint(joints, "Shoulder_L", 15);
+ Joint armL = joint(joints, "Arm_L", 16);
+ Joint elbowL = joint(joints, "Elbow_L", 17);
+ Joint handL = joint(joints, "Hand_L", 18);
+ Joint toolL = joint(joints, "Tool_L", 19);
+
+ // Hiérarchie. addSubJoints est idempotent (skip si déjà présent) — safe
+ // de le réappeler, utile si on étend plus tard.
+ root.addSubJoints(thighR, thighL, torso);
+ thighR.addSubJoints(legR);
+ legR.addSubJoints(kneeR);
+ thighL.addSubJoints(legL);
+ legL.addSubJoints(kneeL);
+
+ torso.addSubJoints(chest);
+ chest.addSubJoints(head, shoulderR, shoulderL);
+
+ shoulderR.addSubJoints(armR);
+ armR.addSubJoints(elbowR);
+ elbowR.addSubJoints(handR);
+ handR.addSubJoints(toolR);
+
+ shoulderL.addSubJoints(armL);
+ armL.addSubJoints(elbowL);
+ elbowL.addSubJoints(handL);
+ handL.addSubJoints(toolL);
+
+ HumanoidArmature arm = new HumanoidArmature("biped", joints.size(), root, joints);
+
+ // Calcule les toOrigin relatifs — obligatoire après la construction
+ // sinon Pose.orElseEmpty retournerait des matrices non initialisées.
+ arm.bakeOriginMatrices();
+
+ return arm;
+ }
+
+ private static Joint joint(Map target, String name, int id) {
+ Joint j = new Joint(name, id, new OpenMatrix4f());
+ target.put(name, j);
+ return j;
+ }
+}
diff --git a/src/main/java/com/tiedup/remake/rig/patch/ClientPlayerPatch.java b/src/main/java/com/tiedup/remake/rig/patch/ClientPlayerPatch.java
index 532562e..1cff54b 100644
--- a/src/main/java/com/tiedup/remake/rig/patch/ClientPlayerPatch.java
+++ b/src/main/java/com/tiedup/remake/rig/patch/ClientPlayerPatch.java
@@ -10,45 +10,50 @@ import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
-import com.tiedup.remake.rig.armature.Armature;
-import com.tiedup.remake.rig.math.OpenMatrix4f;
-
/**
- * Stub Phase 2.3 — patch côté client pour les joueurs remote (autres joueurs
- * dans une session multi). Etoffé Phase 2.4 avec la logique cape / scale /
- * slim vs default model selection.
+ * Patch côté client pour les joueurs remote (autres joueurs en session multi).
*
- * Version minimale : {@code overrideRender=true} (on veut que le renderer
- * patched intercepte et dispatch au RIG), {@code getArmature=null} stub (TODO
- * Phase 2.4), {@code updateMotion} no-op (Phase 2.7).
+ * Hérite tout de {@link PlayerPatch} :
+ * {@link #overrideRender()} = true, armature biped, updateMotion stub.
+ * Pour un remote player on veut systématiquement le rendering RIG — pas de
+ * cas où on voudrait le modèle vanilla MC (qui perdrait les items bondage
+ * RIG-attached Phase 3).
+ *
+ * Différence avec {@link LocalPlayerPatch} : ce dernier override
+ * {@link #overrideRender()} pour désactiver le render RIG en first-person
+ * (sinon on verrait l'intérieur du mesh). Les remote players ne sont jamais
+ * en first-person du point de vue de l'observer → pas besoin.
*
* Forke conceptuellement {@code yesman.epicfight.client.world.capabilites.entitypatch.player.AbstractClientPlayerPatch}
* (EF 479 LOC) mais réécrit from scratch (D-04) car ~80% du contenu original
- * est combat/skill non réutilisable pour TiedUp.
+ * est combat/skill non réutilisable pour TiedUp (slim/default model switch,
+ * cape handling, skin tick — tout ça pourrait être utile Phase 5 si on
+ * restaure le rendering custom skin layer).
+ *
+ * @param concrete type (remote player, et fallback pour LocalPlayer via
+ * {@link LocalPlayerPatch})
*/
@OnlyIn(Dist.CLIENT)
public class ClientPlayerPatch extends PlayerPatch {
- @Override
- public void updateMotion(boolean considerInaction) {
- // no-op stub — Phase 2.7
- }
-
- @Override
- public Armature getArmature() {
- // TODO Phase 2.4 — retourner TiedUpRigRegistry.BIPED.get()
- return null;
- }
-
- @Override
- public boolean overrideRender() {
- // On veut que le render pipeline patched prenne la main pour les
- // remote players visibles.
- return true;
- }
-
- @Override
- public OpenMatrix4f getModelMatrix(float partialTick) {
- return getMatrix(partialTick);
- }
+ /**
+ * Placeholder pour la logique "cacher le player en first-person pour les
+ * items de type wrap / latex_sack / cape-over-head" (V3-REW-01, item
+ * rework Phase 3).
+ *
+ * Actuellement retourne toujours {@code false} — aucun item ne demande
+ * ce hide. Sera re-implémenté Phase 3 quand le
+ * {@code PlayerArmHideEventHandler} V2 sera porté en jointMask-based
+ * logic côté StaticAnimation (cf.
+ * {@code docs/plans/rig/V3_REWORK_BACKLOG.md} V3-REW-01).
+ *
+ * Appelé depuis le renderer patched Phase 2.5+ : si true, skip le
+ * draw du mesh player (mais draw les items bondage attachés).
+ *
+ * @return true si le player doit être invisible en first-person
+ */
+ public boolean isFirstPersonHidden() {
+ // TODO Phase 3 (V3-REW-01) : check equipped items pour wrap / latex_sack.
+ return false;
+ }
}
diff --git a/src/main/java/com/tiedup/remake/rig/patch/LocalPlayerPatch.java b/src/main/java/com/tiedup/remake/rig/patch/LocalPlayerPatch.java
index 829bf2b..f6cb02a 100644
--- a/src/main/java/com/tiedup/remake/rig/patch/LocalPlayerPatch.java
+++ b/src/main/java/com/tiedup/remake/rig/patch/LocalPlayerPatch.java
@@ -6,25 +6,61 @@
package com.tiedup.remake.rig.patch;
+import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
/**
- * Stub Phase 2.3 — patch spécifique au joueur local (self). Spécialisation de
- * {@link ClientPlayerPatch} pour les hooks first-person / caméra / input
- * capture bondage-specific (struggle keys, adjustment menu).
+ * Patch spécifique au joueur local (self). Override
+ * {@link #overrideRender()} pour gérer le cas first-person :
*
- * Version minimale : hérite de {@link ClientPlayerPatch} sans override.
- * Phase 2.4 ajoutera les hooks first-person hide (arms/hands invisibles sous
- * wrap/latex_sack), camera sync leash, etc.
+ *
+ * - First-person (F5 = first) : retourne {@code false} — on laisse
+ * le rendu vanilla Minecraft prendre la main (bras en FP, item held,
+ * overlay mains). Rendre le biped TiedUp en FP donnerait une vue
+ * depuis l'intérieur du mesh, illisible.
+ * - Third-person (F5 = third, either view) ou tout autre cas :
+ * retourne {@code true} — le renderer RIG patched prend la main et
+ * dessine le biped TiedUp comme pour les autres players.
+ *
*
- * Forke conceptuellement {@code yesman.epicfight.client.world.capabilites.entitypatch.player.LocalPlayerPatch}
- * (EF 572 LOC) mais réécrit from scratch (D-04) car skill UI state / input /
- * camera cinematics non réutilisables.
+ * Pourquoi la check {@code entity == Minecraft.getInstance().player} :
+ * {@link LocalPlayerPatch} est attaché uniquement au LocalPlayer (cf.
+ * {@link EntityPatchProvider#registerEntityPatchesClient()}), donc en pratique
+ * {@code this.original == Minecraft.getInstance().player} toujours. Le check
+ * est une ceinture-bretelles au cas où le dispatcher change un jour pour
+ * attacher ce patch à autre chose (très improbable).
+ *
+ * Pattern EF équivalent : {@code LocalPlayerPatch.java:143-152} —
+ * même logique mais EF ajoute un toggle {@code ClientConfig.enableAnimatedFirstPersonModel}
+ * pour ceux qui veulent le biped rendu même en FP. TiedUp peut l'ajouter
+ * plus tard si demande gameplay, mais par défaut FP = vanilla.
+ *
+ * Hooks first-person hide specific bondage items (wrap / latex_sack /
+ * blindfold = vision obstruée) → Phase 3 via
+ * {@link ClientPlayerPatch#isFirstPersonHidden()} (V3-REW-01).
*/
@OnlyIn(Dist.CLIENT)
public class LocalPlayerPatch extends ClientPlayerPatch {
- // Hooks first-person / caméra / input → Phase 2.4
+ /**
+ * @return {@code false} si on est en first-person sur soi-même (laisse
+ * rendre le vanilla), {@code true} dans tous les autres cas.
+ */
+ @Override
+ public boolean overrideRender() {
+ Minecraft mc = Minecraft.getInstance();
+
+ // mc.player peut être null durant un rejoin (~1 tick entre
+ // disconnect et respawn). Dans ce cas pas de FP à gérer, on tombe
+ // sur le comportement parent (true).
+ if (mc.player != null && this.original == mc.player) {
+ if (mc.options.getCameraType().isFirstPerson()) {
+ return false;
+ }
+ }
+
+ return super.overrideRender();
+ }
}
diff --git a/src/main/java/com/tiedup/remake/rig/patch/PlayerPatch.java b/src/main/java/com/tiedup/remake/rig/patch/PlayerPatch.java
index b47449c..1d17d20 100644
--- a/src/main/java/com/tiedup/remake/rig/patch/PlayerPatch.java
+++ b/src/main/java/com/tiedup/remake/rig/patch/PlayerPatch.java
@@ -8,10 +8,151 @@ package com.tiedup.remake.rig.patch;
import net.minecraft.world.entity.player.Player;
+import com.tiedup.remake.rig.TiedUpArmatures;
+import com.tiedup.remake.rig.TiedUpRigRegistry;
+import com.tiedup.remake.rig.anim.Animator;
+import com.tiedup.remake.rig.anim.LivingMotions;
+import com.tiedup.remake.rig.armature.HumanoidArmature;
+import com.tiedup.remake.rig.math.MathUtils;
+import com.tiedup.remake.rig.math.OpenMatrix4f;
+
/**
- * Stub RIG Phase 0 — patch de capability attaché à un {@link Player}.
- * Re-implém complète Phase 2 (séparation ClientPlayerPatch / ServerPlayerPatch,
- * input handling, first-person, cam sync).
+ * RIG Phase 2.4 — patch de capability attaché à un {@link Player}.
+ *
+ * Étoffé à partir du stub Phase 0 pour :
+ *
+ * - Fournir l'{@link #getArmature() armature biped} TiedUp
+ * ({@link TiedUpArmatures#BIPED})
+ * - Activer le rendering RIG patched ({@link #overrideRender()} = true)
+ * — {@link ServerPlayerPatch} ré-override à false et
+ * {@link LocalPlayerPatch} gère le cas first-person
+ * - Fournir un {@link #getModelMatrix(float)} avec scale player vanilla
+ * ({@value #PLAYER_SCALE}) — cf. EF {@code PlayerPatch:82,176}
+ * - Stub {@link #initAnimator(Animator)} qui bind
+ * {@code LivingMotions.IDLE → EMPTY_ANIMATION} (Phase 2.7 remplacera par
+ * {@code CONTEXT_STAND_IDLE} co-authored)
+ *
+ *
+ * Ce qui manque vs EF PlayerPatch (766 LOC), voulu :
+ *
+ * - Stamina system ({@code STAMINA} EntityDataAccessor + regen logic)
+ * - PlayerMode VANILLA/EPICFIGHT toggle
+ * - modelYRot sync / turningLocked handling
+ * - xo/yo/zo previous pos caching
+ * - PlayerEventListener — remplacé par SystemMessageManager/events TiedUp
+ * - Skill system entièrement
+ * - Toute la logique attaque / damage / offhand / armor negation
+ *
+ *
+ * TiedUp n'a pas de combat — ces features seraient dead code. Si on en
+ * veut une (ex. stamina pour struggle), on l'ajoutera au cas par cas (D-04).
*/
public abstract class PlayerPatch extends LivingEntityPatch {
+
+ /**
+ * Scale vanilla player EF-identique (15/16 ≈ 0.9375). Le modèle vanilla
+ * Minecraft rend le joueur à 15/16 de sa taille logique — si on ne
+ * compense pas, le skinned mesh apparaît "trop grand" comparé à la hitbox.
+ *
+ * Source : EF {@code PlayerPatch:82}.
+ */
+ protected static final float PLAYER_SCALE = 15.0F / 16.0F;
+
+ /**
+ * Override — stub sans transition / sans vélocité.
+ *
+ * Phase 2.7+ implémentera la détection de motion (walk/run/sneak/sit/
+ * swim/kneel pour TiedUp) via vélocité + state checks. Ici on ne fait
+ * rien pour que les {@link #currentLivingMotion} restent à IDLE par
+ * défaut (cf. {@link LivingEntityPatch}).
+ *
+ * @param considerInaction paramètre EF legacy — si true, l'impl complète
+ * doit bloquer la transition de motion si l'entité
+ * est dans une {@code inaction()}. Pas utilisé ici.
+ */
+ @Override
+ public void updateMotion(boolean considerInaction) {
+ // Stub Phase 2.4 — pas de motion detection.
+ // Phase 2.7 : cf. EF LivingEntityPatch.updateMotion (velocity + state
+ // machine) + mapping sur LivingMotions TiedUp (idle/walk/kneel/sit).
+ }
+
+ /**
+ * Retourne l'armature biped TiedUp — identique pour tous les players.
+ *
+ * Phase 2.4 : version procédurale ({@link TiedUpArmatures#BIPED}
+ * construit from scratch). Phase 2.7 remplacera par un chargement JSON
+ * avec offsets Blender-authored.
+ *
+ * Pattern EF :
+ * {@code AbstractClientPlayerPatch.java:~70} expose {@code this.armature}
+ * initialisé au constructor. On renvoie directement le singleton — léger
+ * et thread-safe (construction lazy dans {@link TiedUpArmatures#BIPED}).
+ */
+ @Override
+ public HumanoidArmature getArmature() {
+ return TiedUpArmatures.BIPED.get();
+ }
+
+ /**
+ * Override — tous les players passent par le renderer RIG patched.
+ *
+ * {@link ServerPlayerPatch} ré-override à false (le serveur ne rend
+ * rien). {@link LocalPlayerPatch} ré-override pour gérer le cas
+ * first-person (cf. doc là-bas).
+ */
+ @Override
+ public boolean overrideRender() {
+ return true;
+ }
+
+ /**
+ * Model matrix pour le render : position identity (le renderer applique
+ * déjà la translation) + rotation yaw (le corps suit la caméra) + scale
+ * {@value #PLAYER_SCALE}.
+ *
+ * Simplifié vs EF : pas de handling {@code modelYRot} (self-managed
+ * rotation lerp pour turning lock) et pas de {@code ridingEntity} override
+ * — Phase 2.7+ si on ajoute un système de mount TiedUp (maid pet sitting
+ * etc.).
+ *
+ * Baby support (half scale) — EF applique 0.5 si {@code isBaby}. On
+ * garde pour cohérence (les NPCs Damsel n'ont pas d'état baby mais
+ * VillagerMCA si un jour on les patch le veut).
+ */
+ @Override
+ public OpenMatrix4f getModelMatrix(float partialTick) {
+ float scale = (this.original.isBaby() ? 0.5F : 1.0F) * PLAYER_SCALE;
+
+ // Signature : xPosO, xPos, yPosO, yPos, zPosO, zPos, xRotO, xRot,
+ // yRotO, yRot, partialTick, scaleX, scaleY, scaleZ
+ // On ignore pos/xRot (delta pose) — l'entity renderer applique déjà
+ // la position monde. Seul le yRot (yaw corps, pas tête) est utile
+ // pour que le mesh s'oriente avec l'entity.
+ return MathUtils.getModelMatrixIntegral(
+ 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F,
+ 0.0F, 0.0F,
+ this.original.yBodyRotO, this.original.yBodyRot,
+ partialTick, scale, scale, scale
+ );
+ }
+
+ /**
+ * Hook {@link LivingEntityPatch#initAnimator(Animator)} : bind la motion
+ * IDLE sur l'animation "ne fait rien" par défaut. Phase 2.7 remplacera
+ * par {@code TiedUpAnimationRegistry.CONTEXT_STAND_IDLE} avec quelques
+ * keyframes de balancement.
+ *
+ * L'ordre EF ajoute ~25 motions différentes (WALK, RUN, SNEAK, SIT,
+ * SLEEP, etc.). On se limite à IDLE Phase 2.4 — ajouter les autres
+ * sans anim source = pollution registre pour rien. Au fur et à mesure
+ * que les JSON co-authored arrivent (Phase 2.7 / 4), on ajoute les
+ * binds correspondants.
+ */
+ @Override
+ protected void initAnimator(Animator animator) {
+ super.initAnimator(animator);
+ animator.addLivingAnimation(LivingMotions.IDLE, TiedUpRigRegistry.EMPTY_ANIMATION);
+ }
+
}
diff --git a/src/main/java/com/tiedup/remake/rig/patch/ServerPlayerPatch.java b/src/main/java/com/tiedup/remake/rig/patch/ServerPlayerPatch.java
index 2d56af0..b1ea197 100644
--- a/src/main/java/com/tiedup/remake/rig/patch/ServerPlayerPatch.java
+++ b/src/main/java/com/tiedup/remake/rig/patch/ServerPlayerPatch.java
@@ -8,45 +8,41 @@ package com.tiedup.remake.rig.patch;
import net.minecraft.server.level.ServerPlayer;
-import com.tiedup.remake.rig.armature.Armature;
-import com.tiedup.remake.rig.math.OpenMatrix4f;
-
/**
- * Stub Phase 2.3 — patch côté serveur pour les joueurs. Version minimale pour
- * débloquer le dispatch via {@link EntityPatchProvider}. La version complète
- * (kidnap state sync, struggle progression, ownership) est Phase 2.4+.
+ * Patch côté serveur pour les joueurs. Hérite tout de {@link PlayerPatch} —
+ * on ne garde qu'un seul override : {@link #overrideRender()} forcé à
+ * {@code false}.
*
- * Garanties actuelles :
+ * Pourquoi override render → false : le serveur ne rend rien
+ * visuellement, mais l'animator continue de tourner pour calculer la pose
+ * "aveugle" (pos joints à un tick donné). Cette pose sert au tracker serveur
+ * (combat hitboxes EF, hook AI TiedUp). {@link PlayerPatch#overrideRender()}
+ * retourne {@code true} par défaut → si on oubliait l'override, un dispatcher
+ * de rendering théorique côté serveur (il n'y en a pas actuellement, mais
+ * l'API publique le permet) se déclencherait à tort.
+ *
+ * Ce qui est dans PlayerPatch et hérité ici :
*
- * - {@code overrideRender() = false} — serveur ne rend rien, pas de besoin
- * d'intercepter le render pipeline
- * - {@code getArmature() = null} — le serveur n'a pas besoin de mesh data,
- * il joue des animations "blind" (calcule la pose pour syncer aux clients).
- * Sera fixé en Phase 2.4 avec un fallback vers {@code TiedUpRigRegistry.BIPED}.
- * - {@code updateMotion} no-op — Phase 2.7 hook tick réel
+ * - {@code getArmature()} → {@link com.tiedup.remake.rig.TiedUpArmatures#BIPED}
+ * - {@code getModelMatrix(partialTick)} — inutile côté serveur mais pas de
+ * coût à le garder (pas appelé)
+ * - {@code updateMotion(boolean)} no-op Phase 2.4
+ * - {@code initAnimator} bind IDLE → EMPTY_ANIMATION
*
+ *
+ * Phase 2.7+ : {@code serverTick} override pour poser motion + sync aux
+ * clients via packet (cf. EF {@code PlayerPatch.serverTick}). Phase 3+ :
+ * hooks struggle / kidnap state ici.
*/
public class ServerPlayerPatch extends PlayerPatch {
- @Override
- public void updateMotion(boolean considerInaction) {
- // no-op stub — Phase 2.7
- }
-
- @Override
- public Armature getArmature() {
- // TODO Phase 2.4 — retourner TiedUpRigRegistry.BIPED.get()
- return null;
- }
-
- @Override
- public boolean overrideRender() {
- // Serveur ne rend rien.
- return false;
- }
-
- @Override
- public OpenMatrix4f getModelMatrix(float partialTick) {
- return getMatrix(partialTick);
- }
+ /**
+ * Override à false — le serveur n'intercepte jamais le render (il ne rend
+ * pas). Voir doc classe pour pourquoi c'est important malgré l'absence
+ * de renderer serveur actuel.
+ */
+ @Override
+ public boolean overrideRender() {
+ return false;
+ }
}