Phase 2.4 : étoffer PlayerPatch stubs + biped armature procédurale
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.
This commit is contained in:
193
src/main/java/com/tiedup/remake/rig/TiedUpArmatures.java
Normal file
193
src/main/java/com/tiedup/remake/rig/TiedUpArmatures.java
Normal file
@@ -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.
|
||||||
|
*
|
||||||
|
* <h2>Phase 2.4 — version procédurale</h2>
|
||||||
|
*
|
||||||
|
* <p>Cette classe construit le biped TiedUp <b>from scratch en Java</b>
|
||||||
|
* (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.</p>
|
||||||
|
*
|
||||||
|
* <p><b>Phase 2.7 remplacera par un JSON Blender-authored hot-reloadable</b>.
|
||||||
|
* 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 :</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>Phase 2.4 n'a pas encore de renderer player patched complet (Phase 2.5)</li>
|
||||||
|
* <li>Phase 2.7 rechargera des offsets depuis {@code assets/tiedup/armatures/biped.json}
|
||||||
|
* co-authored via addon Blender (cf. MIGRATION.md §2.2.1)</li>
|
||||||
|
* <li>Les tests existants `GltfToSkinnedMeshTest` utilisent déjà le même pattern
|
||||||
|
* (Armature identity, {@code bakeOriginMatrices}) et sont verts</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <h2>Hiérarchie biped EF (20 joints)</h2>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 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
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p><b>Noms conservés verbatim EF</b> (pas renommés en TiedUp style) car :</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>Le {@code VanillaModelTransformer} forké EF (Phase 2.2) référence ces
|
||||||
|
* noms dans ses AABB / {@code WEIGHT_ALONG_Y} / {@code yClipCoord}</li>
|
||||||
|
* <li>Le bridge GLB ({@code LegacyJointNameMapper}) mappe déjà les joints
|
||||||
|
* PlayerAnimator legacy sur ces noms-là</li>
|
||||||
|
* <li>Re-authored serait un risque régression sans gain fonctionnel</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
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()}).
|
||||||
|
*
|
||||||
|
* <p>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.</p>
|
||||||
|
*/
|
||||||
|
private static HumanoidArmature BIPED_INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AssetAccessor biped biped TiedUp. Construit à la première lecture de
|
||||||
|
* {@link AssetAccessor#get()}.
|
||||||
|
*
|
||||||
|
* <p>Utilisé par {@link com.tiedup.remake.rig.patch.PlayerPatch#getArmature()}
|
||||||
|
* et par les futurs {@code StaticAnimation(… , BIPED)} Phase 2.7+.</p>
|
||||||
|
*/
|
||||||
|
public static final AssetAccessor<HumanoidArmature> 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).
|
||||||
|
*
|
||||||
|
* <p>Toutes les transforms sont identity — Phase 2.7 remplacera par les
|
||||||
|
* offsets Blender mesurés (cf. doc header).</p>
|
||||||
|
*/
|
||||||
|
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<String, Joint> 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<String, Joint> target, String name, int id) {
|
||||||
|
Joint j = new Joint(name, id, new OpenMatrix4f());
|
||||||
|
target.put(name, j);
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,45 +10,50 @@ import net.minecraft.client.player.AbstractClientPlayer;
|
|||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
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
|
* Patch côté client pour les joueurs remote (autres joueurs en session multi).
|
||||||
* dans une session multi). Etoffé Phase 2.4 avec la logique cape / scale /
|
|
||||||
* slim vs default model selection.
|
|
||||||
*
|
*
|
||||||
* <p>Version minimale : {@code overrideRender=true} (on veut que le renderer
|
* <p>Hérite tout de {@link PlayerPatch} :
|
||||||
* patched intercepte et dispatch au RIG), {@code getArmature=null} stub (TODO
|
* {@link #overrideRender()} = true, armature biped, updateMotion stub.
|
||||||
* Phase 2.4), {@code updateMotion} no-op (Phase 2.7).</p>
|
* 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).</p>
|
||||||
|
*
|
||||||
|
* <p><b>Différence avec {@link LocalPlayerPatch}</b> : 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.</p>
|
||||||
*
|
*
|
||||||
* <p>Forke conceptuellement {@code yesman.epicfight.client.world.capabilites.entitypatch.player.AbstractClientPlayerPatch}
|
* <p>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
|
* (EF 479 LOC) mais réécrit from scratch (D-04) car ~80% du contenu original
|
||||||
* est combat/skill non réutilisable pour TiedUp.</p>
|
* 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).</p>
|
||||||
|
*
|
||||||
|
* @param <T> concrete type (remote player, et fallback pour LocalPlayer via
|
||||||
|
* {@link LocalPlayerPatch})
|
||||||
*/
|
*/
|
||||||
@OnlyIn(Dist.CLIENT)
|
@OnlyIn(Dist.CLIENT)
|
||||||
public class ClientPlayerPatch<T extends AbstractClientPlayer> extends PlayerPatch<T> {
|
public class ClientPlayerPatch<T extends AbstractClientPlayer> extends PlayerPatch<T> {
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void updateMotion(boolean considerInaction) {
|
* Placeholder pour la logique "cacher le player en first-person pour les
|
||||||
// no-op stub — Phase 2.7
|
* items de type wrap / latex_sack / cape-over-head" (V3-REW-01, item
|
||||||
}
|
* rework Phase 3).
|
||||||
|
*
|
||||||
@Override
|
* <p>Actuellement retourne toujours {@code false} — aucun item ne demande
|
||||||
public Armature getArmature() {
|
* ce hide. Sera re-implémenté Phase 3 quand le
|
||||||
// TODO Phase 2.4 — retourner TiedUpRigRegistry.BIPED.get()
|
* {@code PlayerArmHideEventHandler} V2 sera porté en jointMask-based
|
||||||
return null;
|
* logic côté StaticAnimation (cf.
|
||||||
}
|
* {@code docs/plans/rig/V3_REWORK_BACKLOG.md} V3-REW-01).</p>
|
||||||
|
*
|
||||||
@Override
|
* <p>Appelé depuis le renderer patched Phase 2.5+ : si true, skip le
|
||||||
public boolean overrideRender() {
|
* draw du mesh player (mais draw les items bondage attachés).</p>
|
||||||
// On veut que le render pipeline patched prenne la main pour les
|
*
|
||||||
// remote players visibles.
|
* @return true si le player doit être invisible en first-person
|
||||||
return true;
|
*/
|
||||||
}
|
public boolean isFirstPersonHidden() {
|
||||||
|
// TODO Phase 3 (V3-REW-01) : check equipped items pour wrap / latex_sack.
|
||||||
@Override
|
return false;
|
||||||
public OpenMatrix4f getModelMatrix(float partialTick) {
|
|
||||||
return getMatrix(partialTick);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,25 +6,61 @@
|
|||||||
|
|
||||||
package com.tiedup.remake.rig.patch;
|
package com.tiedup.remake.rig.patch;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stub Phase 2.3 — patch spécifique au joueur local (self). Spécialisation de
|
* Patch spécifique au joueur local (self). Override
|
||||||
* {@link ClientPlayerPatch} pour les hooks first-person / caméra / input
|
* {@link #overrideRender()} pour gérer le cas first-person :
|
||||||
* capture bondage-specific (struggle keys, adjustment menu).
|
|
||||||
*
|
*
|
||||||
* <p>Version minimale : hérite de {@link ClientPlayerPatch} sans override.
|
* <ul>
|
||||||
* Phase 2.4 ajoutera les hooks first-person hide (arms/hands invisibles sous
|
* <li><b>First-person (F5 = first)</b> : retourne {@code false} — on laisse
|
||||||
* wrap/latex_sack), camera sync leash, etc.</p>
|
* 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.</li>
|
||||||
|
* <li><b>Third-person (F5 = third, either view)</b> ou tout autre cas :
|
||||||
|
* retourne {@code true} — le renderer RIG patched prend la main et
|
||||||
|
* dessine le biped TiedUp comme pour les autres players.</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Forke conceptuellement {@code yesman.epicfight.client.world.capabilites.entitypatch.player.LocalPlayerPatch}
|
* <p><b>Pourquoi la check {@code entity == Minecraft.getInstance().player}</b> :
|
||||||
* (EF 572 LOC) mais réécrit from scratch (D-04) car skill UI state / input /
|
* {@link LocalPlayerPatch} est attaché uniquement au LocalPlayer (cf.
|
||||||
* camera cinematics non réutilisables.</p>
|
* {@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).</p>
|
||||||
|
*
|
||||||
|
* <p>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.</p>
|
||||||
|
*
|
||||||
|
* <p>Hooks first-person hide specific bondage items (wrap / latex_sack /
|
||||||
|
* blindfold = vision obstruée) → Phase 3 via
|
||||||
|
* {@link ClientPlayerPatch#isFirstPersonHidden()} (V3-REW-01).</p>
|
||||||
*/
|
*/
|
||||||
@OnlyIn(Dist.CLIENT)
|
@OnlyIn(Dist.CLIENT)
|
||||||
public class LocalPlayerPatch extends ClientPlayerPatch<LocalPlayer> {
|
public class LocalPlayerPatch extends ClientPlayerPatch<LocalPlayer> {
|
||||||
|
|
||||||
// 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,151 @@ package com.tiedup.remake.rig.patch;
|
|||||||
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
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}.
|
* RIG Phase 2.4 — patch de capability attaché à un {@link Player}.
|
||||||
* Re-implém complète Phase 2 (séparation ClientPlayerPatch / ServerPlayerPatch,
|
*
|
||||||
* input handling, first-person, cam sync).
|
* <p>Étoffé à partir du stub Phase 0 pour :</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>Fournir l'{@link #getArmature() armature biped} TiedUp
|
||||||
|
* ({@link TiedUpArmatures#BIPED})</li>
|
||||||
|
* <li>Activer le rendering RIG patched ({@link #overrideRender()} = true)
|
||||||
|
* — {@link ServerPlayerPatch} ré-override à false et
|
||||||
|
* {@link LocalPlayerPatch} gère le cas first-person</li>
|
||||||
|
* <li>Fournir un {@link #getModelMatrix(float)} avec scale player vanilla
|
||||||
|
* ({@value #PLAYER_SCALE}) — cf. EF {@code PlayerPatch:82,176}</li>
|
||||||
|
* <li>Stub {@link #initAnimator(Animator)} qui bind
|
||||||
|
* {@code LivingMotions.IDLE → EMPTY_ANIMATION} (Phase 2.7 remplacera par
|
||||||
|
* {@code CONTEXT_STAND_IDLE} co-authored)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p><b>Ce qui manque vs EF PlayerPatch (766 LOC)</b>, voulu :</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>Stamina system ({@code STAMINA} EntityDataAccessor + regen logic)</li>
|
||||||
|
* <li>PlayerMode VANILLA/EPICFIGHT toggle</li>
|
||||||
|
* <li>modelYRot sync / turningLocked handling</li>
|
||||||
|
* <li>xo/yo/zo previous pos caching</li>
|
||||||
|
* <li>PlayerEventListener — remplacé par SystemMessageManager/events TiedUp</li>
|
||||||
|
* <li>Skill system entièrement</li>
|
||||||
|
* <li>Toute la logique attaque / damage / offhand / armor negation</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>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).</p>
|
||||||
*/
|
*/
|
||||||
public abstract class PlayerPatch<T extends Player> extends LivingEntityPatch<T> {
|
public abstract class PlayerPatch<T extends Player> extends LivingEntityPatch<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* <p>Source : EF {@code PlayerPatch:82}.</p>
|
||||||
|
*/
|
||||||
|
protected static final float PLAYER_SCALE = 15.0F / 16.0F;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override — stub sans transition / sans vélocité.
|
||||||
|
*
|
||||||
|
* <p>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}).</p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p>Phase 2.4 : version procédurale ({@link TiedUpArmatures#BIPED}
|
||||||
|
* construit from scratch). Phase 2.7 remplacera par un chargement JSON
|
||||||
|
* avec offsets Blender-authored.</p>
|
||||||
|
*
|
||||||
|
* <p>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}).</p>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public HumanoidArmature getArmature() {
|
||||||
|
return TiedUpArmatures.BIPED.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override — tous les players passent par le renderer RIG patched.
|
||||||
|
*
|
||||||
|
* <p>{@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).</p>
|
||||||
|
*/
|
||||||
|
@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}.
|
||||||
|
*
|
||||||
|
* <p>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.).</p>
|
||||||
|
*
|
||||||
|
* <p>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).</p>
|
||||||
|
*/
|
||||||
|
@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.
|
||||||
|
*
|
||||||
|
* <p>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.</p>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void initAnimator(Animator animator) {
|
||||||
|
super.initAnimator(animator);
|
||||||
|
animator.addLivingAnimation(LivingMotions.IDLE, TiedUpRigRegistry.EMPTY_ANIMATION);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,45 +8,41 @@ package com.tiedup.remake.rig.patch;
|
|||||||
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
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
|
* Patch côté serveur pour les joueurs. Hérite tout de {@link PlayerPatch} —
|
||||||
* débloquer le dispatch via {@link EntityPatchProvider}. La version complète
|
* on ne garde qu'un seul override : {@link #overrideRender()} forcé à
|
||||||
* (kidnap state sync, struggle progression, ownership) est Phase 2.4+.
|
* {@code false}.
|
||||||
*
|
*
|
||||||
* <p>Garanties actuelles :</p>
|
* <p><b>Pourquoi override render → false</b> : 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.</p>
|
||||||
|
*
|
||||||
|
* <p><b>Ce qui est dans PlayerPatch et hérité ici</b> :</p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code overrideRender() = false} — serveur ne rend rien, pas de besoin
|
* <li>{@code getArmature()} → {@link com.tiedup.remake.rig.TiedUpArmatures#BIPED}</li>
|
||||||
* d'intercepter le render pipeline</li>
|
* <li>{@code getModelMatrix(partialTick)} — inutile côté serveur mais pas de
|
||||||
* <li>{@code getArmature() = null} — le serveur n'a pas besoin de mesh data,
|
* coût à le garder (pas appelé)</li>
|
||||||
* il joue des animations "blind" (calcule la pose pour syncer aux clients).
|
* <li>{@code updateMotion(boolean)} no-op Phase 2.4</li>
|
||||||
* Sera fixé en Phase 2.4 avec un fallback vers {@code TiedUpRigRegistry.BIPED}.</li>
|
* <li>{@code initAnimator} bind IDLE → EMPTY_ANIMATION</li>
|
||||||
* <li>{@code updateMotion} no-op — Phase 2.7 hook tick réel</li>
|
|
||||||
* </ul>
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>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.</p>
|
||||||
*/
|
*/
|
||||||
public class ServerPlayerPatch extends PlayerPatch<ServerPlayer> {
|
public class ServerPlayerPatch extends PlayerPatch<ServerPlayer> {
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void updateMotion(boolean considerInaction) {
|
* Override à false — le serveur n'intercepte jamais le render (il ne rend
|
||||||
// no-op stub — Phase 2.7
|
* pas). Voir doc classe pour pourquoi c'est important malgré l'absence
|
||||||
}
|
* de renderer serveur actuel.
|
||||||
|
*/
|
||||||
@Override
|
|
||||||
public Armature getArmature() {
|
|
||||||
// TODO Phase 2.4 — retourner TiedUpRigRegistry.BIPED.get()
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean overrideRender() {
|
public boolean overrideRender() {
|
||||||
// Serveur ne rend rien.
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public OpenMatrix4f getModelMatrix(float partialTick) {
|
|
||||||
return getMatrix(partialTick);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user