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 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`.