LifePlay-Revival/Docs/Migration/etat/ST/STE/STE-H2b-DIAG.md

283 lines
24 KiB
Markdown
Raw Normal View History

Fiche STE-H2b — Niveau N3 — Modèle prévu (modèle de l'agent lp-cart) | utilisé claude-opus-5-5 — Agent lp-cart — Skills sk-protocol, ue-cpp-build, lp-commandlets, lp-paths (préchargés), lp-doc (gabarits)
# STE-H2b-DIAG : F4, cause (corps vidé du mannequin recyclé) ; voies A et B instruites (2026-10-10)
Lecture seule stricte : aucun éditeur, commandlet ni build ; seul ce fichier est écrit. Copie `D:\Game\LifePlay-master 5.8`,
branche `ue5.8`, HEAD `16b126c`. Gabarit DIAG : `D:\Game\LifePlay-Sandbox\Tools\claude\skills\lp-doc\gabarits.md:32-34`.
Consigne reçue en cours de fiche (orchestrateur, précision de l'utilisateur « instruit les 2 ») : A et B instruites au même
niveau de détail (§ « Correctif de cause proposé »).
**Statut : fait.** Hypothèse confirmée. Recommandation : **abstention** maintenant ; B à reprendre avec H4 ; A écartée.
État des Blueprints (`md5sum Content/BPs/GI.uasset Content/BPs/Person.uasset` ; `git status --short -- Content/BPs/` vide) :
- GI `b8b97c46…` = `STE-GARDES-IMPL.md:18` (état G-H2) ; dernier commit de GI `5c55db4` (STE-R5).
- Person `be21838a…` ; dernier commit de Person `ee2821a` (2026-10-09 21:07:14, STB-PEB), `git log -- Content/BPs/Person.uasset`.
Abréviations (chemins absolus) :
- **AG** = `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE\dumps\GI__EventGraph_apres-GARDES.txt` (GI après G-H2, MD5 du
fichier `8d65267f…`, écrit le 2026-10-10 17:44, après la pose de G-H2 à 17:33) ;
- **GS** / **GP** / **LP** = `...\etat\ST\STE\dumps\GI__GetSpecific.txt` / `GI__getPerson.txt` / `GI__LoadPreset.txt` ;
- **PEG** = `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STB\dumps\Person__EventGraph.txt` ;
- **DD** = `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\PROP\PROP_LPDumpFunction_20261008-010647.txt` (GI `destroyDummy`, copie 5.5,
`-Pins` sans `-Exec` : DD:1) ;
- **L2** = `D:\Game\LifePlay_Build_5.8_STB-JEU2_Development\Windows\LifePlay\Saved\Logs\LifePlay.log` (avant STE) ;
- **LJ3** = `D:\Game\LifePlay_Build_5.8_STF-STE-LISIB_Development\Windows\LifePlay\Saved\Logs\LifePlay.log` (preuve J3, GI B8B97C46) ;
- **UE** = `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\Engine\Private\` (moteur installé, lu).
## 1. Symptôme et reproduction
- F4 avant G-H2 : 96 avertissements `CallFunc_GetSkeletalMesh_ReturnValue` sous `UpdatePlayerMeshWithMorphs` pour 96 SPAWN de
mannequin CheckScenes (L2:5173-5193 ; `STE-H2-DIAG.md` § 1). Après G-H2 : **0** pour 151 SPAWN (`STE-VAL.md:359, 363` ;
recompté : Grep `index SPAWN` sur LJ3, mode count → **151**). F4 est donc masqué ; la cause reste : le mannequin recyclé
n'a pas de corps au SPAWN.
- F5 aujourd'hui (même cause probable) : Grep `No SkeletalMesh` sur LJ3, count → **18 518** (LJ3 = 32 927 lignes,
`STE-VAL.md:351`) ; dont **18 513** sur les 6 mannequins (Grep
`No SkeletalMesh.*Person_C_214748(1954|1930|1906|1882|1858|1834)\)`, count) ; 6 acteurs distincts relevés par Grep `-o`
`\] (Person_C_\d+) index SPAWN` (…1954, …1930, …1906, …1882, …1858, …1834). Reste 5 lignes sur d'autres acteurs (non identifiés).
- DESTROY sur LJ3 (Grep count) : `test0304 DESTROY` **147** ; `DestroyDummy was from` **143**, dont `CheckScenes` **106** ; les
37 autres (liste Grep `-o`, comptée à la main) : `DestroyPerson` 20, `GenerateUnknown` 11, `AddVariable` 3,
`GenerateHomeWork` 2, `getKnown` 1 (106 + 37 = 143).
- Restes `GetSkeletalMesh` de J3, hors périmètre, **non instruits** ici : 4 `Person_C:ExecuteUbergraph_Person`, 1 `GI_C:SavePreset`,
1 `CharGen_C:PopulateGenitalSlidersDaz`, 1 `MainMenu` NewGame (`STE-VAL.md:365-366`).
## 2. Hypothèse testée (une seule)
**H2b** : la cause de F4 est l'état « sans corps » du mannequin en réserve, produit au DESTROY par `ResetVars` (Person), qui vide
le maillage (`SetSkinnedAssetAndUpdate(None)`) et remet `Race` à vide ; au SPAWN suivant, aucun corps n'est donné entre
`SpawnDummy` et `LoadPreset`, et `Race` vide force `PlayerChangeRace` → `UpdatePlayerMeshWithMorphs` à chaque reprise.
Réfutée si : l'état 5.8 de `ResetVars` ne vide pas `Mesh`, ou un corps est affecté entre `SpawnDummy` et `LoadPreset`.
## 3. Essai et résultat brut
### 3.1 Le dump Person décrit l'état 5.8 actuel (estimé fort ; corrige `STE-H2-DIAG.md:15`)
- PEG:1-3 : `LPDumpFunction -BP=Person -Fn=EventGraph -Pins -Exec`, lot `STB-VAL`, date **2026-10-09T21:54:04**, code 0.
- Dernier commit de Person : `ee2821a` à **21:07:14** le même jour (git log ci-dessus) ; aucun commit de Person ensuite ;
arbre propre (`git status --short -- Content/BPs/` vide). `STB-VAL.md:54` lit dans ce dump un nœud de CHG-284
(`Person__EventGraph.txt:19419-19427`).
- Donc PEG est **postérieur** aux deux derniers commits de Person, contrairement à `STE-H2-DIAG.md:15` (« antérieur à STC ;
Person modifié ensuite par CHG-284, CHG-285 »). Reste non établi : le MD5 de Person au moment du dump (non noté par STB-VAL).
- Le dump Person 5.8 n'est donc **pas absent** ; manquent en revanche, en 5.8 : GI `destroyDummy` (seul DD, copie 5.5, sans liens
d'exécution), GI `SpawnDummy`, `CheckCbeGenderModel`, `LoadMeshIfNeeded`, Person `ClearSMCs` (commandes au § 5).
- `destroyDummy` en 5.8 : aucun CHG ne le modifie (Grep `destroyDummy|ResetVars|SpawnDummy|DestroyDummy` sur
`D:\Game\LifePlay-master 5.8\Docs\Journal_Modifications.md` → 3 lignes : 701, 1046 CHG-070 et 1209 CHG-244, sous-systèmes C++
et documentation, aucun Blueprint modifié) ; DD reste donc représentatif (estimé).
### 3.2 `ResetVars` vide le corps et remet `Race` à vide (établi sur PEG)
Chaîne d'exécution suivie maillon par maillon (liens `then` / `execute` lus) depuis l'événement `K2Node_CustomEvent_40`
`ResetVars` (PEG:11873-11875) :
`_221` Ethnicity = white (18701) → `_220` VoicePitch = 1 (18692) → `_217` (18379) → `_901` `Map_Clear(SimplifiedMorphs)` (18388) →
`_213` HeelHeight = 0 (18320) → `_17` `K2_SetRelativeLocation(Mesh, 0,0,-90)` (18329) → `_167` (15199) → `_210` (non lu) →
`_787` `clearParticle` (16855) → `_592` `clearProps` (15063) → `_166` (15183) → `_160` (15157) → `_164` (15163) →
`_689` `SetCollisionEnabled(Capsule, QueryAndPhysics)` (15175) → `_60` (12440) → `_109` HeadtrackTarget (12500) →
`_372` `ClearSMCs` (12293) → `_458` `ClearMorphTargets` (13366) → **`_433` `SetSkinnedAssetAndUpdate`** (13248-13253) →
`_444` `SetVisibility(Mesh, HairSM, true)` (13296-13301) → `_440` `SetVisibility(PenisSM, false)` (13260-13265) →
`_386` `TalkDirty('<>')` (12563) → `_379` `SetHiddenInGame(ButtonSM, true)` (12917) → `_45`, `_48` (noms) → `_126`, `_129`
(13535, 13661) → `_49` … `_54` (Interested_*, Age, Fat, Muscle, 11888-11923) → **`_55` IsMale = true** (11924-11927) →
**`_56` Race = (vide)** (11930-11933) → `_57` Genital = (vide) (11936) → `Array_Clear` `_27`, `_28`, `_29` … (11948-11958, suite non suivie).
Nœuds porteurs :
- `_433` (PEG:13248-13253) : `self` ← `VariableGet_241.Mesh`, `VariableGet_242.HairSM`, `VariableGet_247.PenisSM` ; `NewMesh`
(vide) ; `bReinitPose = false`. Un seul nœud vide les trois composants.
- `_458` appelle l'événement `ClearMorphTargets` (`K2Node_CustomEvent_62`, PEG:13350-13352) →
`SkeletalMeshComponent::ClearMorphTargets` sur `Mesh`, `PenisSM`, `HairSM` (`_457`, PEG:13356-13359) : les poids de morphs
sont remis à zéro avant le vidage.
- `DD:139-143` : `destroyDummy` appelle `SetActorHiddenInGame(true)` puis `Person_C::ResetVars()` ; DD:144-155 :
`K2_SetActorTransform` à l'origine ; DD:226-229 : `Valid = false`. Ordre d'exécution non établi (DD sans `-Exec`).
Appelants (table de noms des paquets, `python -I Tools/uasset58.py locate <paquet> ResetVars SpawnDummy destroyDummy` en boucle
sur `Content/BPs/**/*.uasset`, `Content/UI/**/*.uasset`, `Content/Scenes/*.umap`, **223** paquets lus) :
`ResetVars` : GI, Person seulement ; `destroyDummy` : GI, Person, Room, ContactMenu ; `SpawnDummy` : GI, ContactMenu.
Un nom présent ne prouve qu'une référence, pas un appel (estimé) ; dans GI, `ResetVars` n'apparaît que dans `destroyDummy`
(Grep `ResetVars` sur AG → 0 ligne ; DD:142).
### 3.3 `Race` vide force la mise à jour du corps à chaque SPAWN (établi pour le graphe)
AG:1096-1099 `PlayerChangeRace` → `IfThenElse_1471` (AG:1275-1279), condition `EqualEqual_StriStri(NewRace, Race)`. Après
`ResetVars`, `Race` est vide (§ 3.2) : pour toute race non vide, `else` → `VariableSet_712` → `_10834`
`UpdatePlayerMeshWithMorphs` (AG:1280-1283). La branche `then` (même race → `CheckCbeGenderModel` `_782` → `IfThenElse_139`,
AG:25439-25448) n'est pas prise. De même `IsMale = true` : pour un préréglage féminin, `PlayerChangeGender` → `IfThenElse_733`
`else` (AG:1092-1109) → `RemoveFacialHair` → `_8720` `UpdatePlayerMeshWithMorphs` (AG:1163-1166).
### 3.4 Aucun corps n'est donné entre `SpawnDummy` et `LoadPreset` (établi pour les graphes lus ; `SpawnDummy` non dumpé)
| Site | `SpawnDummy` | → `CharacterBeingEdited` | → `LoadPreset` |
|---|---|---|---|
| GS (CheckScenes) | `_32` (GS:1084-1088) → `PersonUsed` | `VariableSet_6783` (GS:220-223) | `_34239`, `StatOnly = false` (GS:236-242 ; `execute` aussi depuis `_837`) |
| GP (`getPerson`) | `_6` (GP:554-558) → `PersonUsed` | `VariableSet_6783` | `_34239`, `StatOnly = false` (GP:316-322) |
| AG (EventGraph) | `_641` (AG:23873-23880) → `Knot_19` | `VariableSet_6783` (AG:23880) | `_34239` (AG:9639-9644) |
| AG (`LoopUpdateAI`) | `_768` (AG:25006-25010) → `AIDummy` | — | `_19`, **`StatOnly = true`** (AG:25050-25056) |
Aucun `SetSkinnedAssetAndUpdate` sur ces chemins avant `LoadPreset` (LP : `STE-H2-DIAG.md` § 3.1). Journal : L2:5160-5172
`No SkeletalMesh` sur l'acteur juste avant son SPAWN (L2:5174), suivi de F4 : `SpawnDummy` ne donne pas de corps sur ce chemin.
`LoadPreset` est synchrone (Grep `Delay|Latent|AsyncLoad|LoadAsset` sur LP, count → 0 ; estimé : les sous-fonctions non toutes lues).
### 3.5 Mannequin neuf et mannequin recyclé (estimé, journal L2)
- `Person_C_2147481850` : `BeginPlay` L2:1095, 1er SPAWN L2:2223, 1er `No SkeletalMesh` sur cet acteur L2:2228 (Grep
`No SkeletalMesh.*Person_C_2147481850`, premier résultat). Aucun F5 entre la création et le 1er DESTROY : le mannequin neuf a
vraisemblablement un corps (ou personne ne lit ses sockets). L'état « sans corps » naît du DESTROY.
- L2:2227-2233, même image [909] : `test0304 DESTROY…` (2227) → 5 `No SkeletalMesh` `Head` ×2, `pelvis`, `chestUpper`, `hip`
(2228-2232) → `DestroyDummy was from DestroyPerson` (2233). Une lecture de sockets a lieu **pendant** le DESTROY, après le vidage
(piste pour H4 : `unOccupyFnt`, DD:168-169, ou un maillon de `ResetVars` après `_433` ; non vérifié).
### 3.6 G-H2 réalise déjà « donner le corps sans lire les morphs » (établi)
AG:1178-1180 `_231.then` → `IfThenElse_215` ; AG:1192-1193 `_207.Exec` ← `IfThenElse_215.then` ; AG:1241-1243 `_10426`
`UpdatePlayerMesh.execute` ← `_207.Completed`, **`IfThenElse_215.else`** ; nœud `IsValid` `CallFunction_567`
(`STE-GARDES-IMPL.md:35`). Sans corps, la garde saute la lecture et appelle `UpdatePlayerMesh` (corps affecté par `_5556`,
AG:1116-1121, choisi dans `MeshSexRace` selon `IsMale` et `Race`, AG:1125-1159).
### 3.7 Faits moteur et C++ utilisés pour les effets (lus, UE 5.8 installé)
- F5 : `GetSocketInfoByName` n'avertit que si `GetSkinnedAsset()` est nul (UE `Components\SkinnedMeshComponent.cpp:3599-3613`).
- `ACharacter` règle `Mesh->VisibilityBasedAnimTickOption = AlwaysTickPose` (UE `Character.cpp:125`) ; `ShouldTickPose` vrai
même non rendu avec cette option (UE `SkinnedMeshComponent.cpp:1795-1803`) ; os rafraîchis seulement si rendu ou
`AlwaysTickPoseAndRefreshBones` (UE `SkinnedMeshComponent.cpp:1805-1808`). Valeur du CDO de Person : non lue (incertain).
- `InitAnim` ne fait rien sans maillage (UE `SkeletalMeshComponent.cpp:1164`) ; avec maillage, dimensionne `MorphTargetWeights`
au nombre de morphs (`:1170`) ; l'instance d'animation n'est créée qu'avec un maillage (`:1267-1280`).
- `LPPersonPropGuardSubsystem.cpp:198-201` : purge des props si `bHidden || bBodyGone` (acteur caché **ou** maillage nul).
- `LPSecondScreenSubsystem.cpp:31` : lit `head` seulement si `GetSkinnedAsset()` non nul.
## 4. Verdict : **confirmée**
- **Établi** : `ResetVars` (état 5.8 estimé fort, § 3.1) vide `Mesh`, `HairSM`, `PenisSM` (`_433`) après avoir remis les morphs à
zéro (`_457`) et remet `Race` à vide, `IsMale` à vrai (§ 3.2) ; ce `Race` vide force `UpdatePlayerMeshWithMorphs` à chaque
reprise (§ 3.3) ; aucun corps n'est donné avant `LoadPreset` sur les 4 chemins lus (§ 3.4) ; G-H2 couvre déjà le cas (§ 3.6).
- **Estimé** : PEG = Person actuel (§ 3.1) ; mannequin neuf avec corps (§ 3.5) ; `LoadPreset` synchrone ; DD (5.5) représentatif.
- **Incertain** : contenu de `SpawnDummy`, `CheckCbeGenderModel`, `LoadMeshIfNeeded`, `ClearSMCs` (non dumpés) ; contenu de
`MeshSexRace` (entrée à race vide ?) ; option d'animation du CDO de Person ; lecteur des sockets de F5 (H4).
## 5. Hypothèse suivante proposée, et à quel niveau
- **H4 (F5, N3, lp-cart)** : identifier le lecteur des sockets `Head`, `pelvis`, `chestUpper`, `hip`, `CenterBrow` ; piste : la
lecture synchrone pendant le DESTROY (§ 3.5) ; candidats `unOccupyFnt` (DD:168-169), `ReceiveTick` (PEG:9628) et les
`GetSocketLocation` de PEG:9782-10331, 12826, 13054-13060, 13498. La voie B ci-dessous s'y rattache.
- Dumps 5.8 à produire avant toute SPEC (orchestrateur, BUILD-GO, éditeur fermé ; `lp-commandlets`) :
```
.\Tools\RunCommandlet.ps1 -Lot STE-D -Commandlet LPDumpFunction -Args '-BP=GI -Fn=destroyDummy -Pins -Exec'
.\Tools\RunCommandlet.ps1 -Lot STE-D -Commandlet LPDumpFunction -Args '-BP=GI -Fn=SpawnDummy -Pins -Exec'
.\Tools\RunCommandlet.ps1 -Lot STE-D -Commandlet LPDumpFunction -Args '-BP=GI -Fn=CheckCbeGenderModel -Pins -Exec'
.\Tools\RunCommandlet.ps1 -Lot STE-D -Commandlet LPDumpFunction -Args '-BP=GI -Fn=LoadMeshIfNeeded -Pins -Exec'
.\Tools\RunCommandlet.ps1 -Lot STE-D -Commandlet LPDumpFunction -Args '-BP=Person -Fn=ClearSMCs -Pins -Exec'
.\Tools\RunCommandlet.ps1 -Lot STE-D -Commandlet LPDumpFunction -Args '-BP=Person -Fn=unOccupyFnt -Pins -Exec'
.\Tools\RunCommandlet.ps1 -Lot STE-D -Commandlet LPDumpFunction -Args '-BP=Person -Fn=EventGraph -Pins -Exec'
```
Le dernier redonne PEG avec un MD5 de Person relevé (`be21838a…` attendu). `-BP=Person -Fn=ResetVars` ne convient pas :
`ResetVars` et `ClearMorphTargets` sont des événements de l'EventGraph de Person (PEG:11873, 13350) ; `destroyDummy` est une
fonction de GI (DD:120), pas de Person. `unOccupyFnt` : fonction ou événement de Person (DD:168), à vérifier.
## Correctif de cause proposé
Les deux voies, au même niveau de détail. Aucune n'est à appliquer sans décision de l'utilisateur. G-H2 reste en place dans les
deux cas (ceinture). Conventions MCP : celles de `STE-H2-DIAG.md` (préalables P1-P3, gel, `Tools/Snapshot.ps1`, relecture des
liens et des indices de broches avant toute écriture).
### Voie A : donner le corps avant `LoadPreset`
**Nœuds touchés.** Le corps ne peut pas être donné par `UpdatePlayerMesh` : après `ResetVars`, `Race` est vide (PEG:11930-11933)
et la recherche dans `MeshSexRace` exige l'égalité de race (AG:1133-1137, 1150-1159) : aucune entrée trouvée, donc aucun
`SetSkinnedAssetAndUpdate` (estimé : `MeshSexRace` non lu). A exige donc un **corps provisoire** explicite :
- Où : soit dans `SpawnDummy` (1 site, couvre GS `_32`, GP `_6`, AG `_641`, `_768` et ContactMenu ; **non dumpé** : dump requis) ;
soit entre `VariableSet_6783.then` et `LoadPreset _34239.execute` sur 3 sites (GS:220-222, GP:316-317, AG:9639-9640) ; dans
GS, `_34239.execute` a un second prédécesseur `_837.then` (GS:237) à traiter aussi.
- Quoi (types de nœuds déjà présents dans GI, aucun nouveau type) : `LoadMeshIfNeeded(Index = k)` (comme `_249`, AG:19661-19665)
→ lecture `MeshSexRace[k]` + `Break` (comme `BreakStruct_26`, AG:1138-1140) → `SetSkinnedAssetAndUpdate(self = PersonUsed.Mesh,
NewMesh = Mesh)` (comme `_5556`, AG:1116-1121). L'indice `k` (quel corps provisoire) est une **décision** non tranchée ici :
le choisir serait improviser. Bilan : ≈ 4 nœuds par site, 1 coupure et 2 à 3 liens par site (1 ou 3 sites).
**Effets.**
- F4 : inchangé (déjà 0 avec G-H2) ; la garde prend `then` au lieu de `else`. La boucle lit les morphs du corps provisoire, tous à
zéro après `ClearMorphTargets` (PEG:13356-13359) : rien n'est ajouté ; puis `UpdatePlayerMesh` pose le vrai corps. Même état
final qu'aujourd'hui (estimé fort).
- F5 : **inchangé** (DESTROY non touché) : 18 513 lignes restent sur les mannequins (§ 1).
- Mémoire : réserve inchangée (corps toujours vidé au DESTROY) ; le corps `k` reste chargé dès le premier SPAWN (probablement déjà
chargé par les personnages vivants : estimé). Non chiffré.
- Processeur : un `SetSkinnedAssetAndUpdate` de plus par SPAWN (151 sur LJ3), avec réinitialisation de l'animation et de
`MorphTargetWeights` (UE `SkeletalMeshComponent.cpp:1151-1170`). Non chiffré.
- Comportement visible : aucun attendu (même image, `LoadPreset` synchrone, § 3.4 ; estimé).
**Risques.**
- R-A1 : un préréglage sans ligne de race ni de sexe laisse aujourd'hui le mannequin sans corps (invisible) ; avec A, il garderait le
corps provisoire, visible (incertain : contenu des préréglages non lu).
- R-A2 : redondance avec G-H2, dont la branche `else` fait déjà « corps sans lecture des morphs » (§ 3.6) : A ajoute des nœuds
(jusqu'à 3 sites) pour un résultat identique.
- R-A3 : `SpawnDummy` non dumpé : le site unique n'est pas décrit ; sur les 3 sites, risque d'en oublier un (ContactMenu appelle
aussi `SpawnDummy`, § 3.2).
### Voie B : ne pas vider le corps au DESTROY
**Nœuds touchés.** Person, EventGraph, événement `ResetVars` : nœud existant `K2Node_CallFunction_433`
(`SetSkinnedAssetAndUpdate`, `NewMesh` vide, PEG:13248-13253). Retirer le seul lien d'entrée `self ← VariableGet_241.Mesh`
(PEG:13245-13246, 13251) ; garder `HairSM` (`_242`) et `PenisSM` (`_247`). Garder `VariableGet_241.Mesh → _444.self`
(`SetVisibility`, PEG:13246, 13299) et `ClearMorphTargets` sur `Mesh` (`_457`). Bilan : **0 nœud créé**, 1 lien retiré ; si
`break_pins` coupe les 3 liens de `self`, reconnecter `_242.HairSM` et `_247.PenisSM` (2 `connect_pins`) : à relire en P3.
Portée : `ResetVars` n'est référencé que par GI et Person (§ 3.2), appelé par `destroyDummy` (DD:142) : les 6 sources de DESTROY
(LJ3 : CheckScenes 106, DestroyPerson 20, GenerateUnknown 11, AddVariable 3, GenerateHomeWork 2, getKnown 1).
**Effets.**
- F4 : reste 0. Au SPAWN, `Race` vide force toujours `UpdatePlayerMeshWithMorphs` (§ 3.3) ; G-H2 prend `then` ; la boucle lit les
morphs de l'ancien corps, à zéro (`_457`) : rien n'est ajouté ; `UpdatePlayerMesh` pose le nouveau corps. Coût : une boucle
sur les morphs de l'ancien corps par SPAWN (nombre non relevé).
- F5 : les avertissements des mannequins disparaissent, le corps restant affecté (condition UE `SkinnedMeshComponent.cpp:3599-3613`) :
**18 513** lignes sur LJ3, soit ≈ 56 % du journal (estimé fort ; suppose les sockets présents sur tous les corps de `MeshSexRace`).
Les lecteurs de sockets (H4, non identifiés) calculent alors des positions réelles sur un acteur caché à l'origine (DD:144-155).
- Mémoire : jusqu'à 6 mannequins (LJ3) gardent un corps, une instance d'animation (UE `SkeletalMeshComponent.cpp:1267-1280`), des
poids de morphs (`:1170`), des tampons d'os et peut-être des ressources de rendu. Les corps sont ceux de `MeshSexRace`, en
général déjà chargés pour les vivants (estimé). **Non chiffré** ; essai : `memreport -full` et `obj list class=AnimInstance` en
jeu, avant / après, même parcours que J3.
- Processeur : avec `AlwaysTickPose` (UE `Character.cpp:125`), l'animation du mannequin caché est mise à jour à chaque image
(UE `SkinnedMeshComponent.cpp:1802`), sans rafraîchissement des os s'il n'est pas rendu (`:1807`), sauf réglage contraire du
CDO de Person (incertain). Aujourd'hui, sans corps, aucune instance n'est recréée (UE `SkeletalMeshComponent.cpp:1164`) ; que
l'ancienne continue de tourner est incertain. Non chiffré.
- Comportement visible : en réserve, rien (acteur caché, DD:139-141). À la reprise, l'ancien corps neutre (morphs à zéro, sans
cheveux : `HairSM` reste vidé) existe jusqu'à `UpdatePlayerMesh`, dans la même image (estimé). Le mannequin redevient, quant au
corps, dans l'état d'un mannequin neuf (§ 3.5, estimé).
**Risques.**
- R-B1 : chemin `LoopUpdateAI` (`LoadPreset` `StatOnly = true`, AG:25050-25056) : le mannequin d'IA garderait l'ancien corps au
lieu d'aucun ; s'il est un jour affiché, il montrerait le corps de la personne précédente (incertain).
- R-B2 : `LPPersonPropGuardSubsystem` purge les props si l'acteur est caché **ou** sans maillage (`:198-201`) : tant que le
mannequin est caché, rien ne change ; un mannequin réaffiché avant `LoadPreset` ne serait plus purgé au titre « maillage nul ».
- R-B3 : lecteurs de sockets inconnus (H4) qui s'exécutent alors avec de vraies positions sur un acteur caché (effets inconnus).
- R-B4 : `SpawnDummy` (non dumpé) pourrait choisir un mannequin libre selon l'absence de corps (incertain ; `destroyDummy` pose
`Valid = false`, DD:226-229, critère plus probable).
- R-B5 : B revient sur un choix d'origine (corps vidé exprès, `STE-H2-DIAG.md` § 4) et va à l'inverse du correctif F5 proposé par
STD (« désactiver tick / animation au DESTROY », `STD-SCN-DIAG.md:165`) : moins de journal contre plus de calcul en réserve.
### Comparaison et recommandation
| Critère | A | B |
|---|---|---|
| Fichier | GI (1 à 3 sites) | Person (1 lien) |
| Nœuds | ≈ 4 créés par site, choix d'un corps `k` à décider | 0 créé, 1 lien retiré |
| F4 | 0 (déjà 0 par G-H2) | 0 (déjà 0 par G-H2) |
| F5 (LJ3) | inchangé (18 513) | ≈ 0 sur les mannequins (estimé fort) |
| Mémoire, processeur | +1 changement de corps par SPAWN | corps, animation et tampons gardés en réserve (≤ 6 mannequins), non chiffré |
| Visible | aucun attendu ; R-A1 | aucun attendu ; R-B1 |
| Dumps requis avant SPEC | `SpawnDummy`, `LoadMeshIfNeeded` | `SpawnDummy`, `destroyDummy`, H4 (lecteur), CDO de Person |
Recommandation : **abstention** pour cette fiche. A n'apporte rien que G-H2 ne fasse déjà (R-A2) et ajoute des nœuds et un choix
arbitraire de corps : à écarter. B a un gain réel (F5, ≈ 56 % du journal J3) mais échange du journal contre du calcul et de la
mémoire en réserve, sur un choix d'origine ; il doit être décidé avec H4 (lecteur identifié) et mesuré en jeu, comparé aux deux
autres correctifs F5 de STD (tick coupé au DESTROY, garde dans le lecteur).
## Question à l'utilisateur
**Q-H2b** : pour la cause de F4 (corps vidé du mannequin recyclé), que retenez-vous ?
- **A** : donner un corps provisoire avant `LoadPreset` (GI ; F5 inchangé ; résultat identique à G-H2) ;
- **B** : ne plus vider le corps au DESTROY (Person, 1 lien ; F5 ≈ 0 sur les mannequins ; mémoire et calcul en réserve non chiffrés) ;
- **abstention** : garder G-H2 seule ; reprendre B dans H4 (F5) après les dumps du § 5 et une mesure en jeu.
Recommandation : **abstention** (B à instruire avec H4 ; A écartée).
Fichiers à commiter (dépôt jeu, branche `ue5.8`, ticket au gardien) :
- A `Docs/Migration/etat/ST/STE/STE-H2b-DIAG.md`
Tours : 35 / 60
## Constats
- Écart à la fiche § 9 (« arrêt si le dump Person 5.8 est requis et absent ») : pas d'arrêt. PEG est postérieur au dernier commit
de Person (§ 3.1) et la consigne de l'orchestrateur demandait de poursuivre en marquant « estimé ».
- Correction d'un livrable amont : `STE-H2-DIAG.md:15` date PEG d'avant CHG-284 / CHG-285 ; PEG (21:54:04) est postérieur aux deux
commits (21:05:05 et 21:07:14).
- Outils hors Grep / Read / Glob, en lecture seule : `md5sum`, `ls --time-style=full-iso`, `git log`, `git status` ; le script du
projet `Tools/uasset58.py locate` (écrit pour les widgets, lecture générique de la table de noms ; contrôle « exports contigus =
True » affiché pour GI et Person). Le Grep de l'outil sur les `.uasset` avait rendu 0 fichier (arrêt de ripgrep au premier octet
nul) : ce 0 n'était pas une preuve d'absence, d'où le script.
- Le Grep lancé sur `etat\` saute les `.txt` (ignorés par git, `.gitignore`) : chaque dump a été lu par son chemin complet.
- `Person_C:ExecuteUbergraph_Person` apparaît 489 fois dans LJ3 (Grep count) : c'est un cadre de pile commun à tous les
avertissements de Person, pas le compte des 4 `GetSkeletalMesh` de Person cités par `STE-VAL.md:365`.