Fiche FIX1-MZ — Niveau N2 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-impl — Skills sk-protocol, ue-cpp-build # FIX1-MZ-IMPL : instrumentation de la ligne de journal M-Z (HeelHeight, currentAnimation, −90 + 12 × HeelHeight) (2026-10-20) Décision citée par la fiche : l'ordre de l'utilisateur, recopié mot pour mot dans la fiche : « Oui : F7a, F7c et F8b en build 2 ; en build 1, ajoute la hauteur de talons et l'animation à la trace M-Z, et prépare la séance de mesure -LPPersonsLog=2. » La fiche ne donne ni la session ni l'heure (`sk-protocol` § 2) : je la traite comme une consigne de fiche, sans en tirer d'autre décision. Ma part se limite à l'instrumentation C++ ; la séance de mesure n'est pas dans cette fiche (voir § 4). Hors gabarit : les deux compilations, `CopieEtat`, `PubCheck` et la preuve de présence de la chaîne dans les binaires sont rendus dans `## Constats` (notes TRACE). Écart de skills : la fiche cite `sk-protocol, ue-cpp-build` ; `lp-build` et `lp-ue-compat` sont aussi chargés dans mon contexte (utilisés pour les commandes de build et la liste des lignes connues). Niveaux de preuve : **établi** (fichier:ligne ou sortie de commande), **estimé** (raisonnement donné), **incertain** (essai nommé). ## 1. Fichiers modifiés Un seul fichier de source : `D:\Game\LifePlay-master 5.8\Source\LifePlay\LPDirectControlSubsystem.cpp`. Comportement inchangé (établi par lecture du diff : la correction `Body->SetRelativeLocation(…)` `:1255` et `MeshZScans.Remove(Person)` `:1256` sont intactes ; seules des lectures et le texte du `UE_LOG` ont changé). | Mesure | Valeur | Commande | |---|---|---| | MD5 avant (exigé `5f9d7e00`) | `5f9d7e00402688822687e7eb32979bd1`, conforme ; identique à l'instantané | `certutil -hashfile Source/LifePlay/LPDirectControlSubsystem.cpp MD5` avant la première édition ; colonne MD5 de `D:\Game\LifePlay_Backup_5.5\FIX1-MZ\manifest.csv` | | MD5 après | `687c63d85395888b8a334cb537c16a53` (inchangé après les deux builds) | `certutil -hashfile …` après les 2 éditions, puis après les 2 builds | | Taille avant, après | 62 510 o, 64 858 o | manifeste de l'instantané ; `ls -la` | | Fins de ligne | `i/crlf w/crlf attr/-text` ; 1818 CR = 1818 LF (aucune fin de ligne mixte) | `git ls-files --eol Source/LifePlay/LPDirectControlSubsystem.cpp` ; `tr -cd '\r' < … \| wc -c` et `tr -cd '\n' < … \| wc -c` | | Bilan du diff | `1 file changed, 49 insertions(+), 2 deletions(-)` | `git diff --stat -- Source/LifePlay/LPDirectControlSubsystem.cpp` | | Autres fichiers de `Source` modifiés | `Source/LifePlay/LPCameraModeSubsystem.cpp` (déjà modifié avant ma fiche : F8a, `FIX1-F8a-IMPL.md`) ; aucun autre | `git status --short -- Source Tools` | Les deux éditions (outil Edit, sans script) ; le premier essai du second `Edit` a échoué (indentation à 5 tabulations au lieu de 4, aucune écriture), repris à 4 tabulations. ```diff @@ -151,6 +151,47 @@ namespace return Prop ? Cast(Prop->GetObjectPropertyValue_InContainer(Object)) : nullptr; } + // FIX1-MZ (instrumentation de la ligne M-Z, lecture seule) : formule de Person::updateHeelHeight, Z relatif du maillage = + // -90 + 12 x HeelHeight (L6f_dump_Person.txt:19818-19827, A='-90.000000', B='12.000000'). + constexpr double MZHeelBaseZ = -90.0; + constexpr double MZHeelFactor = 12.0; + + // Variable Blueprint numerique lue par reflexion (HeelHeight est un double : Conv_StringToDouble, L6f_dump_Person.txt:19774-19781). + // Faux si la variable est introuvable ou non numerique : aucun journal, l'appelant ecrit "?". + bool MZReadNumber(const UObject* Object, const TCHAR* Name, double& OutValue) + { + const FNumericProperty* Prop = Object ? CastField(Object->GetClass()->FindPropertyByName(Name)) : nullptr; + if (!Prop) + { + return false; + } + const void* Data = Prop->ContainerPtrToValuePtr(Object); + if (Prop->IsFloatingPoint()) + { + OutValue = Prop->GetFloatingPointPropertyValue(Data); + return true; + } + if (Prop->IsInteger()) + { + OutValue = static_cast(Prop->GetSignedIntPropertyValue(Data)); + return true; + } + return false; + } + + // Nom de l'objet d'une variable Blueprint objet (currentAnimation) : "?" si la variable est introuvable, "-" si elle est vide + // (meme convention de vide que ReadObjectName de LPDebugSubsystem.cpp). + FString MZReadObjectName(const UObject* Object, const TCHAR* Name) + { + const FObjectPropertyBase* Prop = Object ? CastField(Object->GetClass()->FindPropertyByName(Name)) : nullptr; + if (!Prop) + { + return TEXT("?"); + } + const UObject* Value = Prop->GetObjectPropertyValue_InContainer(Object); + return Value ? Value->GetName() : FString(TEXT("-")); + } + bool KeyDown(const APlayerController* PC, const FKey& Key) @@ -1213,8 +1254,14 @@ void ULPDirectControlSubsystem::FitPersonBounds(float DeltaTime) Body->SetRelativeLocation(FVector(Relative.X, Relative.Y, TemplateZ)); MeshZScans.Remove(Person); - UE_LOG(LogLifePlay, Display, TEXT("M-Z : %s maillage relatif Z %.1f -> %.1f (gabarit %s), debout sans meuble"), - *Person->GetName(), Relative.Z, TemplateZ, *Person->GetClass()->GetName()); + // FIX1-MZ : instrumentation seule (F8b), lue par reflexion apres la correction, sans effet sur elle. "?" = variable introuvable. + double HeelHeight = 0.0; + const bool bHeelRead = MZReadNumber(Person, TEXT("HeelHeight"), HeelHeight); + const FString HeelText = bHeelRead ? FString::Printf(TEXT("%.3f"), HeelHeight) : FString(TEXT("?")); + const FString HeelZText = bHeelRead ? FString::Printf(TEXT("%.2f"), MZHeelBaseZ + MZHeelFactor * HeelHeight) : FString(TEXT("?")); + UE_LOG(LogLifePlay, Display, TEXT("M-Z : %s maillage relatif Z %.1f -> %.1f (gabarit %s), debout sans meuble ; HeelHeight=%s ; currentAnimation=%s ; -90+12*HeelHeight=%s"), + *Person->GetName(), Relative.Z, TemplateZ, *Person->GetClass()->GetName(), + *HeelText, *MZReadObjectName(Person, TEXT("currentAnimation")), *HeelZText); } } else ``` **Texte exact de la nouvelle ligne de journal** (catégorie `LogLifePlay`, niveau `Display`, une ligne par déclenchement de M-Z, émise après la correction) : ``` M-Z : maillage relatif Z -> (gabarit ), debout sans meuble ; HeelHeight= ; currentAnimation= ; -90+12*HeelHeight= ``` - `` : `%.3f`, ou `?` si `HeelHeight` est introuvable (ou ni flottant ni entier). `` : `%.2f`, de la même formule que `Person::updateHeelHeight` (`-90 + 12 × h`), ou `?` si `` vaut `?`. - `` : nom de l'objet de `currentAnimation`, `-` si la variable existe mais est vide, `?` si elle est introuvable (ou n'est pas une propriété objet). - Forme attendue, **non observée en jeu** : `M-Z : maillage relatif Z -80.4 -> -90.0 (gabarit Person_C), debout sans meuble ; HeelHeight=0.800 ; currentAnimation= ; -90+12*HeelHeight=-80.40`. Lecture : `` égal à `` (à 0,05 près) = décalage de talons ; différent = autre cause. - **Motif à chercher** : chaîne fixe `HeelHeight=` sur les lignes `M-Z :`, ou l'expression régulière `M-Z : .* maillage relatif Z (-?[0-9.]+) -> (-?[0-9.]+) \(gabarit (\S+)\), debout sans meuble ; HeelHeight=(\S+) ; currentAnimation=(\S+) ; -90\+12\*HeelHeight=(\S+)`. Le début de ligne (`M-Z : … debout sans meuble`) est inchangé : une recherche de `M-Z :` retrouve les anciennes et les nouvelles lignes. Aucun consommateur de cette ligne dans `Tools` (Grep `M-Z` sur `D:\Game\LifePlay-master 5.8\Tools` : aucun résultat). APIs moteur utilisées, lues dans UE 5.8.3 (`Build.version` : 5.8.3) : `UStruct::FindPropertyByName(FName)` `Class.h:663` ; `FNumericProperty::IsFloatingPoint` `UnrealType.h:1819`, `IsInteger` `:1822`, `GetSignedIntPropertyValue(void const*)` `:1886`, `GetFloatingPointPropertyValue(void const*)` `:1902` (pas de variante `_InContainer` pour les flottants, d'où `ContainerPtrToValuePtr`) ; `FProperty::ContainerPtrToValuePtr(UObject const*, int32)` `:811` ; `FObjectPropertyBase::GetObjectPropertyValue_InContainer(const void*, int32)` `:2896`. Les mêmes signatures existent en 5.5.4 (`UnrealType.h:1720, 1723, 1787, 1803`, vérifiées par Grep). ## 2. Écarts à la SPEC La fiche tient lieu de SPEC. Écarts et choix : 1. **Référence inexacte de la fiche** : `LPDebugSubsystem.cpp:631-643` ne lit ni `currentAnimation` ni `meshZ` (ce sont l'enregistrement de `PersonsDumpCommand` et le ticker `-LPPersonsLog`). La lecture par réflexion est à `LPDebugSubsystem.cpp:316-321` (`ReadObjectName`), `:333` (`SeatPlace`), `:414-418` (ligne `STATE … meshZ= … currentAnimation=`). Modèle suivi : `ReadObjectName`. 2. **`?` et `-` distincts** : la fiche demande `?` si une variable est introuvable. Pour `currentAnimation`, un objet **vide** s'écrit `-` (convention de `ReadObjectName`, `LPDebugSubsystem.cpp:320`), pour ne pas confondre « variable absente » (`?`) et « aucune animation » (`-`). 3. **Constantes −90 et 12 littérales** (nommées `MZHeelBaseZ`, `MZHeelFactor`), comme dans la formule de l'ordre de l'utilisateur et du Blueprint (`L6f_dump_Person.txt:19820, 19826`), et non `TemplateZ + 12 × HeelHeight` ; `TemplateZ` figure déjà dans la ligne (`-> %.1f`). Si un jour le gabarit n'est plus à −90, les deux valeurs divergent, ce qui se verra. 4. **Types admis pour `HeelHeight`** : flottant (cas attendu : `double`, `Conv_StringToDouble`, `L6f_dump_Person.txt:19774-19781`) ou entier ; tout autre type donne `?`. Le dump est celui de la 5.5 (L6f) : le type en 5.8 n'est pas relu dans le `.uasset` 5.8. 5. **Noms préfixés `MZ`** dans le `namespace` anonyme (unity build : un nom identique dans un autre `.cpp` du module donnerait C2374, `lp-build` § 8) ; Grep `\bMZ[A-Z][A-Za-z]*` sur `Source` avant édition : aucun résultat. 6. **Instantané non refait** : `D:\Game\LifePlay_Backup_5.5\FIX1-MZ\` existait déjà (voir § 3). 7. **`CopieEtat.ps1 -Lot FIX1` rend le code 1** (alerte GIT) : voir `## Constats`. La commande est celle de la fiche, sans `-Assets`. ## 3. Instantané - Lot `FIX1-MZ`, dossier `D:\Game\LifePlay_Backup_5.5\FIX1-MZ\` : créé avant ma fiche (par l'ordonnateur ; date du manifeste `2026-10-20T20:04:15`), contient `Source\LifePlay\LPDirectControlSubsystem.cpp` et `manifest.csv`. - Manifeste (lu) : `"Source/LifePlay/LPDirectControlSubsystem.cpp","True","62510","5F9D7E00402688822687E7EB32979BD1","2026-10-20T20:04:15"`. - Vérifié par `certutil -hashfile D:\Game\LifePlay_Backup_5.5\FIX1-MZ\Source\LifePlay\LPDirectControlSubsystem.cpp MD5` = `5f9d7e00402688822687e7eb32979bd1` : la copie d'instantané est l'état de départ exact. - Retour arrière (proposé, non lancé) : `Tools\Restore.ps1 -Lot FIX1-MZ`, ou recopie de ce fichier d'instantané. ## 4. Ce qui n'a pas été fait - **Aucune mesure en jeu, aucune séance** : la ligne n'a jamais été émise par un exécutable lancé. La préparation de la séance `-LPPersonsLog=2` n'est pas dans cette fiche (périmètre : instrumentation C++ seule) ; elle revient à l'ordonnateur. - **Aucun paquet** (`BuildDebug.ps1`, cuisson) : seuls `LifePlayEditor` et `LifePlay` Development ont été compilés dans `Binaries\Win64` de la copie. Une séance sur un build empaqueté demande une fiche de paquet ; le build COM21 existant n'a pas ce code. - Aucune modification de comportement (M-Z force toujours le Z du gabarit), aucun correctif Blueprint (F8b reste en build 2), aucun fichier hors `LPDirectControlSubsystem.cpp`. - Pas de test de la branche `?` : elle n'a pas été déclenchée (aucune exécution). - Aucune commande git qui écrit. ## 5. Points à confirmer en VAL 1. La chaîne `HeelHeight=%s ; currentAnimation=%s ; -90+12*HeelHeight=%s` est dans `Binaries\Win64\LifePlay.exe` et `UnrealEditor-LifePlay.dll` (rejouer les deux recherches de `## Constats`, ou `LogStats` sur un journal d'une séance). 2. Dans un journal de séance, chaque ligne `M-Z :` porte les trois champs ; relever le nombre de lignes avec `HeelHeight=?` (attendu 0 si la variable existe sous ce nom dans `Person_C` 5.8) et avec `currentAnimation=?` (attendu 0). 3. Pour chaque ligne : comparer `` (valeur avant `->`) et `-90+12*HeelHeight=` ; égalité à 0,05 près = décalage de talons ; écart (cas à −88,0 de `FIX1-F7-PRE.md`, 7 sur 18) = autre cause à instruire. 4. Contrôle de non-régression : le Z gabarit après correction (`-> -90.0`) et le moment du déclenchement (2 balayages) sont ceux des journaux précédents (même nombre de lignes `M-Z :` par séance comparable). 5. Type réel de `HeelHeight` en 5.8 : si une ligne porte `HeelHeight=?` alors que le personnage porte des talons, la variable n'est pas flottante ou entière sous ce nom (relire `Person.uasset` 5.8 par `LPDumpFunction`/MCP). 6. Le build `LifePlayEditor` compte exactement **20** lignes UBT `HyperAIStudio` et le build `LifePlay` **0** (`lp-build` § 7) : retrouvés ici (20 et 0). ## 6. Fichiers à commiter Dépôt du jeu (`D:\Game\LifePlay-master 5.8`, branche `ue5.8`) : - `M Source/LifePlay/LPDirectControlSubsystem.cpp` - `A Docs/Migration/etat/REL/FIX1/FIX1-MZ-IMPL.md` Ne pas inclure `Source/LifePlay/LPCameraModeSubsystem.cpp` (autre lot, F8a). Les résumés de build `FIX1_Build_20261020-200728.txt` et `FIX1_Build_20261020-200822.txt` (même dossier) n'apparaissent pas dans `git status --short -- Docs/Migration/etat/REL/FIX1` (les `.txt` de ce dossier sont ignorés par git) : à ne pas commiter. Les journaux bruts `Saved\Logs\portage\FIX1_Build_*.log` restent hors dépôt. Aucun fichier de l'addon ni des Modules. Tours : 25 / 40 (budget N2 par défaut : la fiche ne donne pas de rubrique 9 ; `Tools/FicheStats.py` fait foi) ## Constats ### Compilation (hors gabarit, deux relevés) Trace : `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\REL\FIX1\FIX1_Build_20261020-200728.txt` — Fiche FIX1-MZ — 2026-10-20 20:07 ``` Commande : cd 'D:\Game\LifePlay-master 5.8'; pwsh -NoProfile -File Tools/Build.ps1 -Lot FIX1 -EtatDir "D:\Game\LifePlay-master 5.8\Docs\Migration\etat\REL\FIX1" (LifePlayEditor Win64 Development, après Tools\CopieEtat.ps1 -Lot FIX1) Environnement: copie 5.8, branche ue5.8, HEAD ad3c9c6 ; UE 5.8.3 ; éditeur fermé (UeState : processus=0) ; une seule compilation à la fois ; machine calme (cpu=1 % au CopieEtat) Attendu : code 0, 0 erreur, 0 avertissement du compilateur ; 20 lignes UBT HyperAIStudio (connues) Obtenu : code=0 duree=43s erreurs=0 avertissements=0 autres=20 unites=2 ; unités LPDirectControlSubsystem.cpp et Module.LifePlay.cpp ; DLL liée UnrealEditor-LifePlay.dll ; journal Saved\Logs\portage\FIX1_Build_20261020-200728.log Lecture : « Makefile : » vide dans le résumé (statut non relevé par Build.ps1) ; module modifié bien recompilé (unité + DLL) ; chaîne « Using Visual Studio 14.50.35739 toolchain (…\MSVC\14.50.35717) and Windows 10.0.22621.0 SDK » = MSVC épinglé de la 5.8 ``` Trace : `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\REL\FIX1\FIX1_Build_20261020-200822.txt` — Fiche FIX1-MZ — 2026-10-20 20:08 ``` Commande : cd 'D:\Game\LifePlay-master 5.8'; pwsh -NoProfile -File Tools/Build.ps1 -Lot FIX1 -Target LifePlay -Config Development -ExtraArgs '-NoUBA' -EtatDir "D:\Game\LifePlay-master 5.8\Docs\Migration\etat\REL\FIX1" Environnement: identique ; lancé après la fin du premier build (UeState -Kind Editeur,Compilation -WaitExit : « plus rien ne tourne » avant lancement) Attendu : code 0, 0 erreur, 0 avertissement ; 0 ligne UBT (cible jeu) Obtenu : code=0 duree=56s erreurs=0 avertissements=0 autres=0 unites=2 ; unités LPDirectControlSubsystem.cpp [NoUba] et Module.LifePlay.cpp [NoUba] ; « DLL liées : 0 » (cible monolithique) ; exécutable Binaries\Win64\LifePlay.exe daté 2026-10-20 20:09:16 ; journal Saved\Logs\portage\FIX1_Build_20261020-200822.log Lecture : « DLL liées : 0 » est normal pour la cible jeu ; la preuve de l'exécutable passe par la date, le MD5 et la recherche de chaîne ci-dessous ; 0 avertissement : le C4701 de F8a est bien absent ``` MD5 des binaires (`certutil -hashfile … MD5`) : `Binaries\Win64\LifePlay.exe` = **`e2ba7dec63d479974394e2cffd1efc19`** (335 850 496 o, 2026-10-20 20:09:16) ; `Binaries\Win64\UnrealEditor-LifePlay.dll` = `68aee3039e0a320a4a085db1aa6e6018` (1 996 800 o, 20:08:10). L'exécutable livré dans `Binaries\Win64\LifePlay-Win64-Shipping.exe` (2026-10-14) n'est pas touché. ### Présence de la nouvelle chaîne dans les binaires Trace : sortie de commande, Fiche FIX1-MZ — 2026-10-20 ~20:12 ``` Commande : grep -a -c -P 'H\x00e\x00e\x00l\x00H\x00e\x00i\x00g\x00h\x00t\x00=\x00%\x00s\x00' puis grep -a -c -P 'd\x00e\x00b\x00o\x00u\x00t\x00 \x00s\x00a\x00n\x00s\x00 \x00m\x00e\x00u\x00b\x00l\x00e\x00 \x00;\x00' (chaînes UTF-16LE de TEXT()) Environnement: depuis D:\Game\LifePlay-master 5.8 ; binaires ci-dessus Attendu : ≥ 1 ligne correspondante dans chaque binaire (chaîne nouvelle compilée) Obtenu : LifePlay.exe : 1 et 1 ; UnrealEditor-LifePlay.dll : 1 et 1 Lecture : `grep -c` compte des « lignes » binaires, pas des occurrences ; 1 = au moins une. Exception à la règle « Grep, pas grep » : l'outil Grep ne cherche pas de l'UTF-16 dans un binaire (lecture seule, ponctuelle). Preuve de présence seulement : elle ne dit pas que le code s'exécute (voir § 4) ``` ### CopieEtat (avant les builds) ``` COPIE ALERTE lot=FIX1 copie=LifePlay-master 5.8 proc=0 assets=0 absents=0 modifies=0 git=2/0/0 hors_declares=2 parasites=0 jeu_hors_docs=3 jeu_docs=8 head=ad3c9c6/30beef0/c8661cd verrou=248:libre cpu=1% alertes=GIT ALERTE GIT : jeu [ M] Source/LifePlay/LPCameraModeSubsystem.cpp ALERTE GIT : jeu [ M] Source/LifePlay/LPDirectControlSubsystem.cpp INFO : controle d'instantane non fait (parametre -Depuis absent) code=1 ``` Lecture : les deux entrées sont des fichiers modifiés non déclarés par `-Assets` (la fiche n'en passe pas) : `LPDirectControlSubsystem.cpp` est le mien, `LPCameraModeSubsystem.cpp` vient de F8a (déjà modifié avant ma fiche, `git status` initial de la session). Aucun processus (`proc=0`), aucun parasite. Alerte attendue, non bloquante ; rien corrigé. ### PubCheck ``` Commande : pwsh -NoProfile -File Tools/PubCheck.ps1 -Fichiers Source/LifePlay/LPDirectControlSubsystem.cpp -Depot "D:/Game/LifePlay-master 5.8" < /dev/null Sortie : PubCheck : 0 constat(s) code=0 ``` Le livrable `FIX1-MZ-IMPL.md` et les résumés de build ne sont pas passés à `PubCheck` (la fiche ne demande que le fichier source ; chemins `D:\Game\…` présents dans les textes, comme dans `FIX1-F8a-IMPL.md`) : à contrôler par le gardien avant commit si la règle l'exige. ### État final `Tools\UeState.ps1` en fin de fiche : `processus=0 editeur=0 compilation=0 shaders=0 jeu=0` (code 0). Aucun processus Unreal lancé par moi n'est resté ouvert.