LifePlay-Revival/Docs/Migration/etat/ST/STA/STA-T2b-VILLE-DIAG.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

31 KiB

Fiche STA-T2b-VILLE — 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

STA-T2b-VILLE-DIAG : StreetMap_Static, Room.pickPresetWorld, familles (Script Msg) et hitchs MapMarkers (2026-10-09)

Hors gabarit : partiel, annoncé par la fiche. Une sous-section DIAG 1-5 par clé (K1 à K9), puis § Constats (tableau récapitulatif, clés non traitées, compte rendu sk-protocol § 3).

Lecture seule : ni jeu, ni éditeur, ni commandlet, ni build. Seul fichier écrit : ce livrable. Abréviations : CIBLE = D:\Game\LifePlay_Build_5.8_Development_U8_essai\Windows\LifePlay\Saved\Logs\LifePlay.log ; REF = D:\Game\LifePlay-master 5.5\Docs\Migration\etat\U58\U6\LifePlay_58_session2.log ; dumps\ = D:\Game\LifePlay-master 5.5\Docs\Migration\etat\ST\STA\dumps\ ; Src\ = D:\Game\LifePlay-master 5.8\Source\LifePlay\ ; E58\ = D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\.

Deux mécanismes moteur servent à toutes les clés (lus dans le moteur 5.8 installé) :

  • Forme Script Msg: Attempted to access index … (sans ligne Function, sans pile) : émise par UKismetArrayLibrary::GenericArray_Get (nœud Get par copie) via FFrame::KismetExecutionMessage ; la valeur rendue est remise à la valeur par défaut (None, 0) et la fonction continue (E58\Engine\Private\KismetArrayLibrary.cpp:459-480). Cela confirme le point amont de STA-T1 § Écarts (pas de ligne Function pour ces événements).
  • Forme Accessed None trying to read (real) property P in <classe> : P est la propriété objet qui vaut None et sert de contexte (E58\CoreUObject\Private\UObject\ScriptCore.cpp:3203-3218). Forme nue Accessed None : Stack.MostRecentProperty est nul (ScriptCore.cpp:3220-3228). Forme « pending kill or garbage » : objet détruit mais pas encore collecté (ScriptCore.cpp:3194-3201).

K1. StreetMap_Static_C:SearchByAmenitySafe : CurrentCityExpansion in GI_C (9 / 14) et forme nue (1 / 1)

1. Symptôme et reproduction

  • CIBLE:7898-7910 : Accessed None trying to read (real) property CurrentCityExpansion in /Game/BPs/GI.GI_C, Function …StreetMap_Static_C:SearchByAmenitySafe:64X ; pile : CentralLondon_C:ReceiveTick → GI_C:UpdateTime → TopRight_C:GenerateRecs → Person_C:recommendActions → recommendKeyActions → recommendHomeIfNeeded → SearchByAmenitySafe.
  • CIBLE:51374-51386 : même message ; pile : Scene_C bouton Next → GI_C:ContinueScene → ReadSceneLine → CallFunction → MakePermanent → Person_C:assignWork → SearchByAmenitySafe.
  • Forme nue : CIBLE:51388-51400, même fonction, même milliseconde (16.45.55:195) et même pile que 51374.
  • Comptes (Grep count, motif Warning: Accessed None trying to read \(real\) property CurrentCityExpansion) : CIBLE 9, REF 16 (= 14 SearchByAmenitySafe + 2 findNearbyBuilding, STA-T1-INV.md:135,151).

2. Hypothèse testée

GI.CurrentCityExpansion vaut None parce qu'aucun emplacement de sauvegarde d'extension de ville n'existe, et SearchByAmenitySafe lit CurrentCityExpansion.Buildings sans test IsValid.

3. Essai et résultat brut

  • Nœud fautif : dumps\StreetMap_Static__SearchByAmenitySafe.txt:11-19 (VariableGet 'GI' → VariableGet 'CurrentCityExpansion' → VariableGet 'Buildings') ; Buildings alimente VariableSet_3 (Shuf, l.175-179, branche « Amenity non vide »), CallArrayFunction_3 (Array_Length, l.155-158) et FunctionResult_3 (l.169-171) (branche « Amenity vide », IfThenElse_6 l.164-168). Les 47 nœuds ne contiennent aucun IsValid : les deux seuls MacroInstance sont les boucles (l.23-30, l.87-94).
  • Écriture de la variable, dumps\GI__EventGraph.txt : événement à paramètre CityName (CustomEvent_1141, l.11365-11368) → DoesSaveGameExist(CityName) (l.11369-11375) → IfThenElse_2072 (l.11376-11380) : else (et CastFailed, l.11397) → VariableSet_4287 'CurrentCityExpansion' avec entrée vide, donc None (l.11388-11393) ; then → LoadGameFromSlot → cast → VariableSet_4288 (l.11381-11387, 11401-11406). Appelé par LoadCityExpansion(CityName) après SortStreetMapData (l.11411-11415, 13174-13177). L'objet n'est créé qu'à l'ajout d'un bâtiment (CustomEvent_447, IsValid → Is Not Valid → CreateSaveGameObject → VariableSet_5455, l.11323-11328, 11416-11426, 11437-11442) puis écrit par SaveGameToSlot (l.11443-11450).
  • Glob *.sav dans D:\Game\LifePlay_Build_5.8_Development_U8_essai\Windows\LifePlay\Saved\SaveGames : 2 fichiers, GameSettings.sav, Default.sav ; aucun emplacement de ville.
  • Effet en jeu, lu dans le dump : branche « Amenity vide » : Array_Length(None) = 0 → FunctionResult_0 rend StreetMap.Buildings (l.159-174) ; branche « Amenity non vide » : Shuf vide → boucle vide → Temp (variable locale, l.60-63) vide → IfThenElse_2.then → boucle sur StreetMap.Buildings (l.82-86, 187-198). Le repli voulu a lieu.

4. Verdict : confirmée

Établi : la variable vaut None tant que la ville n'a pas d'extension sauvegardée, état normal d'une ville jamais modifiée. Le résultat de la fonction est juste (repli sur StreetMap.Buildings) ; l'avertissement est du bruit. Forme nue : même appel ; le nœud exact n'est pas départagé entre les lectures de VariableSet_3 et CallArrayFunction_3 (incertain) ; le garde ci-dessous couvre les deux.

Correctif proposé (Blueprint StreetMap_Static, fonction SearchByAmenitySafe), garde justifié :

  • (a) entre IfThenElse_5.then et VariableSet_3 : macro IsValid sur GI.CurrentCityExpansion ; Is Valid → VariableSet_3 ; Is Not Valid → VariableSet_4 (boucle StreetMap.Buildings, Temp est encore vide).
  • (b) entre IfThenElse_5.else et IfThenElse_6 : même IsValid ; Is Valid → IfThenElse_6 ; Is Not Valid → FunctionResult_0.
  • Même garde dans findNearbyBuilding (REF 2, non dumpée).
  • Non recommandé : créer un objet vide dans LoadCityExpansion au lieu de None ; SaveGameToSlot (l.11443-11450) écrirait alors un emplacement de ville sans changement voulu.

5. Hypothèse suivante

Aucune. Suite : correction Blueprint (N2 lp-impl), puis VAL N1 : rejouer le parcours CIBLE ; attendu : 0 ligne property CurrentCityExpansion, 0 forme nue sous SearchByAmenitySafe.


K2. StreetMap_Static_C:ExecuteUbergraph_StreetMap_Static : BGA in StreetMap_Static_C (5 / 6, + 4 « pending kill » en REF)

1. Symptôme et reproduction

  • CIBLE:7446-7462 : Accessed None … property BGA ; pile : Action_C bouton → GI_C:InterruptAnyCurrentAction → StopDurationUpdate → MatchCurrentPlayerBuilding → Room_C:ShowCurrentPlayerBuilding → StreetMap_Static_C:PlayBGA.
  • CIBLE:7741-7756 : pile GI_C:TakeActionWithBuilding → StreetMap_Static_C:PlayerGoHere → UpdateWall → PlayBGA → ExecuteUbergraph_StreetMap_Static.
  • CIBLE:7974-7978 : pile réduite à ExecuteUbergraph_StreetMap_Static.
  • REF:8942-8953 : Attempted to access AudioComponent_2147460819 via property BGA, but … is not valid (pending kill or garbage).
  • Comptes (Grep count) : CIBLE 5 ; REF 10 (motif property BGA|via property BGA = 6 Accessed None + 4 pending kill, STA-T1-INV.md:137,191).

2. Hypothèse testée

BGA (composant audio) vaut None avant le premier SpawnSound2D (et redevient None après la collecte du composant auto-détruit) ; l'événement PlayBGA lit BGA.Sound et appelle BGA.Stop() sans IsValid.

3. Essai et résultat brut (dumps\StreetMap_Static__EventGraph.txt)

  • Événement à paramètre Amenity (CustomEvent_13, l.5417-5420 ; nom PlayBGA estimé : appel ?::PlayBGA() à paramètre Amenity, l.5453-5457, et pile CIBLE:7755-7756) → PlayStreetBGA = false (l.5486-5491) → IfThenElse_22 sur Map_Find(AmenityBGAs, Amenity) (l.5492-5505).
  • else → CallFunction_134 BGA.Stop() (l.5443-5446) : lecture 1.
  • then → IfThenElse_23 sur EqualEqual_ObjectObject(Value, BGA.Sound) (VariableGet_130, self = BGA, l.5458-5460, 5506-5515) : lecture 2 ; else → SwitchString_3 : street → PlayStreetBGA = true → RetriggerableDelay 0.5 → IfThenElse_21 → CallFunction_124 (l.5465-5485) ; Default → CallFunction_124 BGA.Stop() (l.5461-5464) : lecture 3 → SpawnSound2D(bAutoDestroy = true) → VariableSet_32 'BGA' (l.5421-5439).
  • Grep 'BGA' sur le dump : 2 nœuds seulement (VariableSet_32 l.5434, VariableGet_129 l.5440) : un seul écrivain, aucun IsValid.
  • CIBLE:7977-7978 (une seule trame ExecuteUbergraph) : reprise latente du RetriggerableDelay de la branche street (estimé).
  • REF « pending kill » : bAutoDestroy = 'true' (l.5432) ; composant détruit à la fin du son, encore référencé avant le GC (estimé) ; après le GC la référence vaut None (estimé).

4. Verdict : confirmée

Établi : 3 lectures de BGA sans garde. Bénin : Stop sur None ne fait rien et la comparaison rend faux, donc le son est lancé quand même.

Correctif proposé (Blueprint StreetMap_Static, graphe d'événements), garde justifié (None est l'état initial) :

  • (a) IfThenElse_22.then → IsValid(BGA) ; Is Valid → IfThenElse_23 ; Is Not Valid → SwitchString_3.execute.
  • (b) IfThenElse_22.else → IsValid(BGA) ; Is Valid → CallFunction_134 ; Is Not Valid → fin.
  • (c) devant CallFunction_124 (deux entrées : SwitchString_3.Default, IfThenElse_21.then) → IsValid(BGA) ; Is Valid → CallFunction_124 ; Is Not Valid → CallFunction_110 (SpawnSound2D). IsValid écarte aussi les objets « pending kill or garbage », donc les 4 lignes REF.

5. Hypothèse suivante

Aucune. VAL N1 : parcours ville (clic bâtiment, PlayerGoHere, rue) ; attendu : 0 ligne property BGA ou via property BGA.


K3. Room_C:pickPresetWorld : index hors bornes BuildingPoints (4 / 10)

1. Symptôme et reproduction

  • CIBLE:7465-7483 : Attempted to access index 1 from array BuildingPoints of length 0! ; pile Action_C → GI_C:InterruptAnyCurrentAction → … → Room_C:ShowCurrentPlayerBuilding → ShowCurrentWorldPreset → pickPresetWorld ; CIBLE:7485-7503 : index 0, même milliseconde.
  • CIBLE:7759-7791 : même paire ; pile TakeActionWithBuilding → PlayerGoHere → UpdateWall → Room_C:ShowCurrentWorldPreset → pickPresetWorld.
  • CIBLE:7505 et 7795, juste après chaque paire : [Room_C_0] Modules/vin_Base/World/street___vin01.lpworld test0203.
  • Comptes (Grep count, Warning: Attempted to access index \d+ from array BuildingPoints) : CIBLE 4, REF 10.

2. Hypothèse testée

Le bâtiment passé n'a pas de polygone (joueur dans la rue), et pickPresetWorld lit BuildingPoints[0] et [1] sans test de longueur quand BackgroundsSaved ne connaît pas le bâtiment.

3. Essai et résultat brut (dumps\Room__pickPresetWorld.txt)

  • Entrée (Building, Amenity) → GI.BuildingPointsToString(BuildingPoints) → Map_Find(GI.BackgroundsSaved) → IfThenElse_0 : trouvé → FunctionResult_1 ; sinon VariableSet_1 len = Array_Length(WorldPresets[Amenity].Strings) (l.2-13, 106-112, 136-158).
  • GetArrayItem_0 (index 0) et GetArrayItem_1 (index 1) sur BuildingPoints (l.20-23, 36-39) → graine Round(P0.X + P0.Y + P1.X + P1.Y) (l.29-35, 51-58) → FMod(graine, len) → Abs → Round → Strings[i] (l.45-50, 63-66, 83-86, 113-116) → Select(Strings[i], Strings[0], Len > 0) (l.117-135) → PrintString(… + ' test0203') (l.67-82). Aucun test de longueur sur BuildingPoints.
  • BuildingPoints est un membre natif de FStreetMapBuilding (D:\Game\LifePlay-master 5.8\Plugins\StreetMap\Source\StreetMapRuntime\Public\StreetMap.h:300-302).
  • Grep \[Room_C_0\] .* test0203 sur CIBLE : 7 lignes (7505, 7795, 7994, 8033, 37861, 49553, 51570) ; seules les deux premières (rue) suivent des avertissements ; la 3e ligne street (8033) n'en a pas, donc le bâtiment passé y avait des points.

4. Verdict : confirmée

Établi : BuildingPoints de longueur 0 avec un préréglage street ; tableau vide normal (rue, aucun bâtiment). Effet : la graine vaut 0 et le préréglage retenu est Strings[0] (street___vin01), choix fixe sans défaut visible (estimé).

Correctif proposé (Blueprint Room, fonction pickPresetWorld), garde justifié : Branch sur Array_Length(BuildingPoints) >= 2 entre IfThenElse_0.else et VariableSet_1 ; vrai → chemin actuel ; faux → VariableSet_2 string = GetArrayItem_3 (Strings[0]) → PrintString → FunctionResult_0. Risque voisin, non observé : len = 0 (aucun préréglage pour l'amenity) donne FMod par 0 (l.45-48) ; le même Branch peut tester len > 0.

5. Hypothèse suivante

Aucune. VAL N1 : rue au chargement ; attendu : 0 ligne array BuildingPoints, préréglage street___vin01 inchangé.


K4. GI_C:(Script Msg) : Person_24 in Variable (39 / 59)

1. Symptôme et reproduction

  • CIBLE:38010-38022 : Script Msg: Attempted to access index 0 from array 'Person_24_4B71…' of length 0 in '/Game/Structs/Variable.Variable'!, called by: GI_C, suivi à la même milliseconde de Accessed None trying to read (real) property CallFunc_Array_Get_Item in not an UClass, Function …GI_C:clearVariable:64X ; pile GI_C:UpdateCheckForScenes → CheckConditionsWho → ReadSceneLine → CallFunction → getPerson.
  • CIBLE:38063-38075 : idem ; pile se terminant par GI_C:getRelative.
  • Comptes (Grep count, Script Msg: Attempted to access index \d+ from array 'Person_24_) : CIBLE 39 ; REF 59 (STA-T1-INV.md:121, cohérent avec le compte REF regroupé du § Constats).

2. Hypothèse testée

clearVariable lit GetVariable(Var).Person[0] sans test de longueur, pour une variable de scène qui ne contient aucune personne.

3. Essai et résultat brut (dumps\GI__clearVariable.txt)

  • GetVariable(Var) → BreakStruct Variable → Person_24_… → GetArrayItem_0, index 0 (l.5-24). La sortie sert à EqualEqual(Companion), EqualEqual(Interacting), Prefix, SaveSlot, Name_First, OccupyingMoreVar, DestroyPerson (l.24, 39-151). Seul test : Len(Var) > 0 (l.25-38) ; aucun test sur la longueur du tableau.
  • Grep avec contexte (CallFunc_Array_Get_Item in not an UClass, -B 1 -A 2) : chaque Script Msg Person_24 est suivi d'un ou deux Accessed None de clearVariable à la même milliseconde (CIBLE:48954-48968 : deux ; CIBLE:38010-38012 : au moins un).

4. Verdict : confirmée

Établi : même événement que la famille GI_C:clearVariable (72 / 94, STA-T1-INV.md:117, fiche STA-T2b-GIVAR). Variable vide = état possible d'une condition de scène (estimé). Remarque : si Companion vaut None, None == None rend vrai et la fonction ne fait rien (l.46-63, IfThenElse_1.then vide).

Correctif proposé (Blueprint GI, fonction clearVariable), garde justifié : Branch sur Array_Length(Person_24) > 0 entre CallFunction_0.then (GetVariable) et IfThenElse_1 ; faux → retour. Supprime le Script Msg et les Accessed None qui le suivent (la part exacte des 72 reste à compter par GIVAR). À fusionner avec la proposition de STA-T2b-GIVAR (même nœud).

5. Hypothèse suivante

Aucune pour la clé. Consolidation STA-T2c : une seule correction GIVAR + VILLE.


K5. GI_C:(Script Msg) : Texture2dArray_3 in Te2dArray (6 / 0)

1. Symptôme et reproduction

  • CIBLE:51230-51231 : Script Msg: Attempted to access index 0 from array 'Texture2dArray_3_E303…' of length 0 in '/Game/Structs/Te2dArray.Te2dArray'!, called by: GI_C (trame 954, début de la scène de couple, juste avant Show couple : branche CIBLE:51232).
  • CIBLE:51255-51256 : trame 752, juste après [SexAction_C_…] MF_sp01 (Animation String) (CIBLE:51254). Autres : CIBLE:51290, 51307, 51322, 51346 (Grep : 6 lignes).
  • Pas de pile (forme Script Msg).

2. Hypothèse testée

Une fonction de GI lit Te2dArray.Texture2dArray[0] sur un tableau vide au changement d'animation de la scène de couple.

3. Essai et résultat brut

  • Grep Texture2dArray : dumps\GI__EventGraph.txt 0 ; dumps\GI__graphes.txt 0 (noms de graphes seulement) ; dumps\Person__EventGraph.txt 0.
  • « called by: GI_C » (instance) : fonction propre à GI hors graphe d'événements, non dumpée (estimé).
  • Grep Te2dArray sur Content\**\*.uasset : 0, non concluant (ripgrep saute les fichiers binaires).

4. Verdict : indécise (nœud non localisé)

Nouvelle en CIBLE parce que REF ne joue pas la scène de couple (STA-T1-INV.md:258). Correctif générique, à confirmer sur le nœud : Array_IsValidIndex(Texture2dArray, 0) avant le Get ; sinon texture par défaut ou rien.

5. Hypothèse suivante (N0 puis N3)

N0 lp-builder : LPDumpFunction -BP=GI -Fn=<f> -Pins -Exec sur les candidats proches dans le temps (PlayAppropriateSexAnimation, PlayAnimationMorph, PersonPlayAnimationMorph, getFaceTexture, getMID, CumSound : dumps\GI__graphes.txt:80,8,81,267,26,269), puis Grep Texture2dArray_3. N3 ensuite pour le correctif.


K6. SexInterface_C:(Script Msg) : SexPersons in GI_C (3 / 0)

1. Symptôme et reproduction

CIBLE:51235-51240 : Script Msg: Attempted to access index 2, puis 3, puis 4, from array 'SexPersons' of length 2 in '/Game/BPs/GI.GI_C'!, même milliseconde (16.44.17:984), called by: SexInterface_C …GI_C_2147482540.SexInterface_C_2147464127. Pas de pile.

2. Hypothèse testée

SexInterface lit GI.SexPersons aux indices fixes 0 à 4 (cinq emplacements) sans test de longueur ; avec 2 participants, 3 lectures hors bornes.

3. Essai et résultat brut

  • SexInterface n'a pas été dumpé ; l'asset existe (Glob : D:\Game\LifePlay-master 5.8\Content\UI\SexInterface.uasset).
  • Indices 2, 3, 4 consécutifs dans la même milliseconde : lectures à index fixe (estimé). Le Get rend None (KismetArrayLibrary.cpp:479).

4. Verdict : indécise (nœud non localisé), mécanisme estimé

Bénin pour une scène à deux. Correctif générique : Array_IsValidIndex(GI.SexPersons, i) devant chaque emplacement, ou boucle ForEach sur SexPersons.

5. Hypothèse suivante (N0 puis N3)

N0 : LPDumpFunction -BP=SexInterface (liste des graphes), puis dump de la fonction qui lit SexPersons. N3 ensuite.


K7. Person_C:(Script Msg) : Standspots_Occupied in Room_C (15 / 9)

1. Symptôme et reproduction

  • CIBLE:7635-7636 : Script Msg: Attempted to access index 0 from array 'Standspots_Occupied' of length 0 in '/Game/BPs/Room.Room_C'!, called by: Person_C …PlayerCharacter, juste après une pile terminée par Person_C:standIdle (CIBLE:7633).
  • CIBLE:19503 (index 0, longueur 0), 19573 (index 1, longueur 1), 19625 (index 2, longueur 2) : index = longueur à chaque fois.
  • Comptes (Grep count) : CIBLE 15 ; REF 9 (STA-T1-INV.md:134).

2. Hypothèse testée

standIdle parcourt Room.Standspots et lit le tableau parallèle Standspots_Occupied[i] ; celui-ci est rempli au fil de l'eau (Array_Set avec bSizeToFit), donc plus court que Standspots.

3. Essai et résultat brut (dumps\Person__standIdle.txt)

  • ForEach sur GI.Room.Standspots (MacroInstance_1, l.171-181) → GetArrayItem_0 Standspots_Occupied[Array Index] (l.182-188) → IsValid (MacroInstance_3, l.189-193) → Valid (l.197-204) ; Is Not Valid ou non Valid → Knot_1 → Dest = emplacement libre (l.194-196, 205-209).
  • Écriture dans la même fonction : Array_Set(Standspots_Occupied, Find(Standspots, Dest), self, bSizeToFit = 'true') (l.144-162).

4. Verdict : confirmée

Établi : tableau parallèle volontairement creux ; le Get rend None et l'emplacement est vu comme libre, ce qui est le comportement voulu. Bénin.

Correctif proposé (Blueprint Person, fonction standIdle), garde justifié : Branch sur Array_IsValidIndex(Standspots_Occupied, Array Index) avant MacroInstance_3 ; faux → Knot_1 (libre). Autre voie : dimensionner Standspots_Occupied à Array_Length(Standspots) là où Room remplit Standspots (Room non dumpé au-delà de pickPresetWorld).

5. Hypothèse suivante

Aucune. Les Accessed None qui suivent (OccupyingFnt, Near) relèvent de STA-T2b-PERSON.


K8. GI_C:(Script Msg) : WidgetComponents in MapMarkers_C (1 / 2)

1. Symptôme et reproduction

  • CIBLE:7380-7399 : Script Msg: Attempted to access index 0 from array 'WidgetComponents' of length 0 in '/Game/BPs/MapMarkers.MapMarkers_C'! (called by: GI_C), puis Accessed None … CallFunc_Array_Get_Item_53 in not an UClass (ExecuteUbergraph_GI) ; pile MainMenu_C Continue → GI_C:DelayedStart → PC_C:BeginAutoLoad → GI_C:AutoLoad → MoveCity → SpawnMapMarkers → FocusOnMapMarkers.
  • CIBLE:7413, 75 ms plus tard : LogLifePlay: Display: MapMarkers : slot de focus WidgetComponents[0] ajoute (ERR-008, …).
  • REF:7451 et REF:8172 (Grep : 2).

2. Hypothèse testée

FocusOnMapMarkers lit MapMarkers.WidgetComponents[0] juste après l'apparition des marqueurs, avant que la garde C++ (tick de 0,25 s) n'insère son emplacement de focus.

3. Essai et résultat brut

  • dumps\GI__EventGraph.txt:13185-13212 : événement sans paramètre (CustomEvent_183) → SetInputMode_GameAndUIEx(PC, MapMarkers.WidgetComponents[0].GetUserWidgetObject()) ; seule lecture de WidgetComponents dans le graphe d'événements de GI (Grep : 1 VariableGet, l.13191). Nom FocusOnMapMarkers estimé (appel l.13213-13216 et pile CIBLE:7398-7399).
  • Garde : Src\LPMapMarkersGuardSubsystem.cpp:61 (tick 0,25 s), remplit le tableau seulement s'il est vide (l.89-125) ; en-tête Src\LPMapMarkersGuardSubsystem.h:1-4 (« GI.FocusOnMapMarkers lit MapMarkers.WidgetComponents[0] sans test de longueur », « Aucun Blueprint n'est modifié »).
  • Un événement par instance de MapMarkers_C : CIBLE 1 instance (2147481658), REF 2 instances (2147481693, 2147479394, d'après les lignes de hitch).

4. Verdict : confirmée

Reste d'ERR-008 : une lecture par apparition de MapMarkers, sans plantage (SetInputMode avec widget None). Correctif proposé (Blueprint GI, CustomEvent_183), garde justifié : Branch sur Array_IsValidIndex(MapMarkers.WidgetComponents, 0) après l'événement ; faux → SetInputMode_GameAndUIEx sans widget (comportement actuel), ou rien. Alternative C++ (garde lancée à l'apparition de l'acteur) non recommandée : un emplacement en tête avant les marqueurs risque de décaler les index par rapport à Vector2Ds (estimé, LPMapMarkersGuardSubsystem.h:3-4).

5. Hypothèse suivante

Aucune. Lien avec K9 : si ce correctif Blueprint est fait, la création du composant de focus par la garde (cause de K9) peut devenir inutile ; à décider (§ Constats, Q1).


K9. Hitchs MapMarkers_C.LPFocusWidgetComponent (2 / 4)

1. Symptôme et reproduction

  • CIBLE:7972 (1,77 ms, trame 330, action « aller à ») et CIBLE:51241 (11,66 ms, trame 955, début de la scène de couple) : LogUObjectGlobals: Warning: Gamethread hitch waiting for resource cleanup on a UObject (WidgetComponent …MapMarkers_C_2147481658.LPFocusWidgetComponent) overwrite took …ms.
  • REF:7736 (3,45 ms), 8321 (6,07), 9687 (3,14), 14480 (3,43).
  • Comptes (Grep count, LPFocusWidgetComponent\) overwrite took) : CIBLE 2.

2. Hypothèse testée

La garde recrée son composant de focus avec un nom fixe alors que le précédent (détruit par le Blueprint, pas encore collecté) existe encore dans le même acteur : NewObject reconstruit par-dessus et attend la fin du nettoyage côté rendu.

3. Essai et résultat brut

  • Src\LPMapMarkersGuardSubsystem.cpp:127 : NewObject<UWidgetComponent>(Markers, TEXT("LPFocusWidgetComponent")), nom fixe. Ligne atteinte seulement si, après retrait des entrées invalides, le tableau est vide (l.89-101) et si aucun UWidgetComponent valide ne reste sur l'acteur (l.106-125). Grep LPFocusWidgetComponent sur Source : seul ce fichier (l.127 et 147).
  • Moteur : StaticAllocateObject cherche un objet de même nom dans le même outer (E58\CoreUObject\Private\UObject\UObjectGlobals.cpp:3515-3516), le détruit (ConditionalBeginDestroy, l.3687-3691), boucle tant que IsReadyForFinishDestroy est faux (l.3693-3705), puis écrit l'avertissement (l.3706-3710). UPrimitiveComponent::IsReadyForFinishDestroy attend DetachFence (E58\Engine\Private\Components\PrimitiveComponent.cpp:2050-2054).
  • Ce que mesure « overwrite took » : le temps passé par le thread de jeu à attendre que le thread de rendu libère le proxy de l'ancien composant avant de reconstruire l'objet au même nom. Plus lourd en scène chargée (11,66 ms en scène de couple).

4. Verdict : confirmée

Mécanisme établi par le code. Qui détruit l'ancien composant : HideAllMarkers du Blueprint (estimé, LPMapMarkersGuardSubsystem.h:2 et absence de composant valide à la l.107).

Correctif proposé (C++, Src\LPMapMarkersGuardSubsystem.cpp:127) :

const FName FocusName = MakeUniqueObjectName(Markers, UWidgetComponent::StaticClass(), TEXT("LPFocusWidgetComponent"));
UWidgetComponent* Focus = NewObject<UWidgetComponent>(Markers, FocusName);

MakeUniqueObjectName a la même signature en 5.5 (D:\Game\Launcher\Engine\UE_5.5\Engine\Source\Runtime\CoreUObject\Public\UObject\UObjectGlobals.h:972) et en 5.8 (E58\CoreUObject\Public\UObject\UObjectGlobals.h:1062), donc pas de garde de version. La fonction boucle tant qu'un objet de ce nom existe et ne rend jamais le nom de base (UObjectGlobals.cpp:2715-2728) : plus de reconstruction par-dessus, l'ancien objet part au GC normal. Autre voie (Q1) : correctif Blueprint K8, puis retrait de la création du composant de focus (garder le vidage de Vector2Ds, l.104).

5. Hypothèse suivante

Aucune. Suite : IMPL N2 (1 ligne C++), BUILD, puis VAL en jeu ; attendu : 0 ligne overwrite took sur LPFocusWidgetComponent, ligne de garde slot de focus toujours présente, plus aucune lecture hors bornes de WidgetComponents après la première.


Constats

Tableau récapitulatif

Clé Famille (T1) CIBLE / REF Preuve (nœud ou source) Pile (journal) Cause Verdict Correctif proposé Effort
K1 StreetMap_Static_C:SearchByAmenitySafe 9+1 / 14+1 SearchByAmenitySafe.txt:11-19,175-179 ; GI__EventGraph.txt:11376-11393 CIBLE:7898-7910, 51374-51400 CurrentCityExpansion None sans extension de ville, lu sans IsValid confirmée (forme nue : nœud incertain) 2 IsValid (BP) faible
K2 StreetMap_Static_C:ExecuteUbergraph_StreetMap_Static 5 / 6 (+4) StreetMap_Static__EventGraph.txt:5417-5515 CIBLE:7446-7462, 7741-7756 ; REF:8942 BGA None ou détruit, lu 3 fois sans IsValid confirmée 3 IsValid (BP) faible
K3 Room_C:pickPresetWorld 4 / 10 Room__pickPresetWorld.txt:20-39 CIBLE:7465-7503, 7759-7791 ; preset CIBLE:7505 rue sans polygone, BuildingPoints[0..1] sans test confirmée (tableau vide normal) 1 Branch (BP) faible
K4 GI_C:(Script Msg) Person_24 39 / 59 GI__clearVariable.txt:5-24 CIBLE:38010-38022, 38063-38075 variable sans personne, Person[0] sans test confirmée (même événement que GIVAR) 1 Branch (BP, avec GIVAR) faible
K5 GI_C:(Script Msg) Texture2dArray_3 6 / 0 non localisé (Grep 0 dans les dumps GI) CIBLE:51230, 51255 (sans pile) lecture [0] d'un Te2dArray vide (estimé) indécise IsValidIndex (à poser sur le nœud) dump N0 d'abord
K6 SexInterface_C:(Script Msg) 3 / 0 non dumpé CIBLE:51235-51240 (sans pile) index fixes 2-4 pour 2 participants (estimé) indécise IsValidIndex ou boucle dump N0 d'abord
K7 Person_C:(Script Msg) Standspots 15 / 9 Person__standIdle.txt:144-162,171-209 CIBLE:7633-7636, 19503-19625 tableau parallèle creux (bSizeToFit) confirmée (bénin) 1 Branch (BP) faible
K8 GI_C:(Script Msg) WidgetComponents 1 / 2 GI__EventGraph.txt:13185-13212 ; LPMapMarkersGuardSubsystem.cpp:61,89-125 CIBLE:7380-7399, 7413 lecture avant le tick de garde confirmée 1 Branch (BP) faible
K9 hitch LPFocusWidgetComponent 2 / 4 LPMapMarkersGuardSubsystem.cpp:127 ; UObjectGlobals.cpp:3515-3710 CIBLE:7972, 51241 NewObject à nom fixe sur un objet détruit non collecté confirmée MakeUniqueObjectName (C++, 1 ligne) faible + BUILD

Recompte (Grep count, une commande par clé, motifs cités dans chaque § 1) : CIBLE K1 9, K2 5, K3 4, K4 39, K7 15, K8 1, K9 2 ; K5 6 et K6 3 (lignes listées par Grep). REF K1 16, K2 10, K3 10 ; motif regroupé Script Msg: … array '(Person_24_|Standspots_Occupied|WidgetComponents|Texture2dArray_3_|SexPersons) = 70 = 59 + 9 + 2 + 0 + 0 (STA-T1-INV.md:121-123,134,143). Tous égaux aux comptes de STA-T1.

Clés non traitées ou partielles

  • K5 : nœud non localisé (fonction GI non dumpée) ; K6 : SexInterface non dumpé. Correctif générique seulement.
  • K1, forme nue (1 / 1) : même appel établi ; nœud exact non départagé.
  • Hors périmètre, cités pour mémoire : StreetMap_Static_C:findNearbyBuilding (REF 2, même clé que K1, même garde) ; Accessed None de clearVariable (GIVAR), OccupyingFnt et Near (PERSON), CallFunc_Array_Get_Item_53 (GIUBER) ; Divide by zero (SexAction_C, Room_C).

Établi / estimé / incertain

  • Établi : nœuds et liens cités (dumps:ligne), piles (journal:ligne), comptes (Grep), code de la garde et mécanismes moteur (sources 5.8 installées), absence d'emplacement de ville (Glob).
  • Estimé : noms des événements personnalisés (PlayBGA, FocusOnMapMarkers, LoadCityExpansion) déduits des appels et des piles, car le dump ne nomme pas les CustomEvent ; destruction du composant de focus par HideAllMarkers ; références nulles après le GC (K2) ; mécanismes K5 et K6.
  • Incertain : nœud de la forme nue (K1) ; fonction GI de K5 ; fonction SexInterface de K6. Essais : dumps N0 indiqués en K5 et K6.

Questions pour l'utilisateur ou la SPEC

  • Q1 (K8/K9) : garder la garde C++ et corriger seulement le nom (MakeUniqueObjectName), ou corriger FocusOnMapMarkers en Blueprint et retirer la création du composant de focus ? La première voie ne touche aucun Blueprint (principe de LPMapMarkersGuardSubsystem.h:4).
  • Q2 (K4) : une seule fiche IMPL pour clearVariable (GIVAR + VILLE).

Compte rendu (sk-protocol § 3)

[sk-protocol] Skills dans le contexte : sk-protocol, ue-cpp-build, lp-commandlets, lp-paths.

  1. Résultat : partiel. 7 clés sur 9 ont une cause confirmée, une preuve et un correctif ; K5 et K6 restent indécises (nœud non localisé, faute de dump).
  2. Preuves : dumps StreetMap_Static__SearchByAmenitySafe, StreetMap_Static__EventGraph, Room__pickPresetWorld, GI__clearVariable, GI__EventGraph, Person__standIdle ; Src\LPMapMarkersGuardSubsystem.cpp/.h ; moteur 5.8 (ScriptCore.cpp, KismetArrayLibrary.cpp, UObjectGlobals.cpp/.h, PrimitiveComponent.cpp) et 5.5 (UObjectGlobals.h:972).
  3. Chiffres : Grep count par clé sur CIBLE et REF (§ Constats, recompte), tous égaux à STA-T1.
  4. Incertitudes : K1 forme nue ; K5 et K6 ; noms des CustomEvent (estimés).
  5. Écarts : budget dépassé (≈ 58 appels d'outils contre ≈ 40 prévus, pic non mesuré) ; MapMarkers_C non dumpé, donc lu au journal et dans Source\. Fichier écrit : D:\Game\LifePlay-master 5.5\Docs\Migration\etat\ST\STA\STA-T2b-VILLE-DIAG.md (seul).
  6. Suite : l'orchestrateur doit lancer un N0 (dumps GI candidats pour K5, SexInterface pour K6), trancher Q1 et Q2, puis passer K1-K4, K7-K9 à STA-T2c. État : diagnostic livré, aucune correction faite.