12 KiB
Fiche STM1-SPEC-01 — Niveau N2 — Modèle prévu opus medium | utilisé claude-opus-5-5 — Agent session « Medium · STM1 » (pas de sous-agent) — Skills sk-protocol, lp-ue-compat (lus en CART-01)
STM1-SPEC-01 : trace en jeu du choix de la ville (F3, option O1) (2026-10-20)
Ordre : Big Boss (local_104ff22d), 2026-10-20. Décision de l'utilisateur citée par Big Boss (session Big Boss, 04:30) :
« pour street map on prépare la trace en jeu pour vérifier le pb puis on fournis une buil de développment pour les fixes en cours. La build
sera générée une fois que l'on auras complété toutes les opérations ». Contrainte de Big Boss : GI.uasset ne sera révisé qu'une fois, en B2
(décision CG2 de 04:00) ; trace hors GI de préférence. SPEC rédigée sans copie UE ni MCP ; aucun fichier du projet modifié.
Résultat : SPEC prête, trace entièrement hors GI. Elle tient en C++ du module LifePlay : un sous-système neuf qui lit le GI par
réflexion, sans l'écrire, et une ligne de journal ajoutée dans LPAssetRegistryHelpers. Elle s'appuie aussi sur les PrintString déjà présents dans
MoveCity, qui écrivent au journal en Development. Aucun asset touché, ni Blueprint ni plugin StreetMap. Aucune lecture MCP préalable n'est nécessaire.
1. Ce que la trace doit trancher
D'après STM1-CART-02 § 1 (M1-M5), la carte tombe sur 000Dummy par l'un de ces chemins. Chacun doit laisser une signature distincte :
| Chemin | Signature attendue dans la trace |
|---|---|
C1 : démarrage resté sur la ville factice (AutoLoad(Dummy=true), pas de MoveCity suivant) |
POSE … Static/000Dummy sans DEMANDE d'une autre ville ensuite |
C2 : nom de ville sauvegardé introuvable (GetCityByName vide) |
SAUVEGARDE ville='X' … AUCUNE correspondance, puis RESOLUTION '/Game/Heavy/StreetMap/' … ECHEC (chemin par defaut de City) |
C3 : chemin résolu en échec, puis repli sur Cities[0] |
RESOLUTION '<chemin ville>' ECHEC, puis LogBlueprintUserMessages … NOT VALID, puis POSE de Cities[0] |
| C4 : autre cause (ville bien demandée et trouvée, mais mauvais maillage posé) | DEMANDE et RESOLUTION OK de la bonne ville, mais POSE d'un autre maillage |
2. Forme
- Journal seulement, rien à l'écran. Catégorie dédiée
LogLPCity, verbositéLog, préfixe[LPCity]et une ligne par événement. Visible dansSaved/Logs/LifePlay.logen Development et en PIE. Absente du Shipping : corps sous#if !UE_BUILD_SHIPPING, et journalisation désactivée en Shipping par défaut. - Sur changement seulement : chaque valeur suivie n'est écrite que quand elle change. Le coût reste négligeable : une lecture par réflexion toutes les 0,25 s.
- Toujours active en Development, sans option de ligne de commande. Une commande console
LP.CityTraceécrit l'état courant à la demande. - Les
PrintString/PrintTextexistants deMoveCity(nom de ville,NOT VALID,Cast To Static Mesh Succeeds) sont déjà écrits dansLogBlueprintUserMessagesen Development. Ils s'affichent aussi à l'écran, comme aujourd'hui : on ne les modifie pas (ils sont dans le GI). Preuve :UE_5.8/Engine/Source/Runtime/Engine/Private/KismetSystemLibrary.cpp,PrintString, garde#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) || USE_LOGGING_IN_SHIPPING; nœuds :etat/ST/STB/dumps/GI__EventGraph.txt:17775('NOT VALID',bPrintToLog = 'true'),etat/ST/STE/dumps/GI__EventGraph_P2.txt:16510-16570.
3. Points de mesure et emplacement
T1. Résolution des chemins de ville : Source/LifePlay/LPAssetRegistryHelpers.cpp (modification)
MoveCity résout StreetMapName et StaticMeshName par ULPAssetRegistryHelpers::GetAssetByObjectPath (CART-02 M2,
GI__EventGraph_P2.txt:12245-12275). C'est le seul passage obligé de chaque changement de ville : on le trace ici.
- Si
ObjectPathcommence par/Game/Heavy/StreetMap: à chaque appel, sans le dédoublonnage actuel (qui ne garde que le premier échec),UE_LOG(LogLPCity, Log, TEXT("[LPCity] RESOLUTION '%s' -> %s%s"), Chemin, OK|ECHEC, Etiquette)avec :Etiquette=(chemin par defaut de City : ville non trouvee)si le chemin finit par/(/Game/Heavy/StreetMap/ou.../Static/, valeurs par défaut de la structureCity, CART-02 M1) ;(ville factice)si le chemin contient000Dummy;- vide sinon.
- Les autres chemins (corps, vêtements, coiffures) gardent le comportement actuel, sans changement.
- L'avertissement
GetAssetByObjectPath ECHECexistant (LPAssetRegistryHelpers.cpp:25-36) reste tel quel.
T2. Sous-système ULPCityTraceSubsystem : Source/LifePlay/LPCityTraceSubsystem.h / .cpp (nouveaux)
Un UGameInstanceSubsystem, sur le modèle de LPDebugSubsystem et LPDirectControlSubsystem. Ticker FTSTicker à 0,25 s créé dans
Initialize et retiré dans Deinitialize. Lecture par réflexion comme DirectActor (LPDirectControlSubsystem.cpp:148-152) et FScriptArrayHelper
(:217). Lecture seule : aucune écriture dans le GI, aucun appel de fonction Blueprint.
| Id | Valeur lue | Chemin de réflexion (noms établis par les dumps) | Ligne écrite (au changement) |
|---|---|---|---|
| T2a | Liste des villes | GI.Cities (tableau de City) |
une fois, dès que la liste n'est plus vide : [LPCity] CITIES n=<N> [0]='<DisplayName>' sm='<StreetMapName>' mesh='<StaticMeshName>' ; CentralLondon a l'indice <i|absent> |
| T2b | Ville sauvegardée | GI.SaveGameInstance → propriété City (lue par ExportTextItem, quel que soit son type) |
[LPCity] SAUVEGARDE ville='<X>' -> trouvee indice <i> ('<DisplayName>') ou -> AUCUNE correspondance (GetCityByName rendra la City par defaut) |
| T2c | Ville demandée | GI.CityToMoveTo (struct City) |
[LPCity] DEMANDE '<DisplayName>' sm='<StreetMapName>' mesh='<StaticMeshName>' |
| T2d | Données StreetMap posées | GI.StreetMapStatic → propriété StreetMap (objet UStreetMap) |
[LPCity] STREETMAP <chemin de l'objet|None> |
| T2e | Maillage affiché | GI.StreetMapStatic → propriété StaticMeshComponent → GetStaticMesh() |
[LPCity] POSE <chemin du maillage|None> (t=<s depuis le demarrage>) |
Règles d'écriture :
- Les membres de
Cityportent un suffixe de GUID (DisplayName_2_EBDEAF…,StreetMapName_26_C43E02…,StaticMeshName_28_61293D…,etat/portage55/L6f/L6f_dump_GI.txt:25637-25647). On les cherche par préfixe (DisplayName_,StreetMapName_,StaticMeshName_), pas par nom complet. - T2b reproduit
GetCityByName(L6f_dump_GI.txt:25623-25660) : comparaison insensible à la casse (ESearchCase::IgnoreCase) entre la ville sauvegardée etDisplayNameconverti en chaîne. On ne fait que la constater ; le GI garde sa propre logique. - Propriété introuvable (le GI a pu changer depuis les dumps du 9-10 octobre) : une ligne
[LPCity] PROPRIETE ABSENTE <classe>.<nom>une seule fois, puis on saute ce point de mesure. La trace se signale elle-même, sans plantage. - Commande console
LP.CityTrace: écrit d'un coup T2a à T2e, plusGI.Cities.Num().
T3. Catégorie de journal : Source/LifePlay/LifePlay.h / LifePlay.cpp (modification)
LIFEPLAY_API DECLARE_LOG_CATEGORY_EXTERN(LogLPCity, Log, All); à côté de LogLifePlay (LifePlay.h:9), et DEFINE_LOG_CATEGORY(LogLPCity); dans LifePlay.cpp.
Une seule définition : pas de DEFINE_LOG_CATEGORY_STATIC répété, qui entrerait en conflit dans un même fichier unity.
4. Pourquoi hors GI, et l'option GI chiffrée
| Option | Ce qu'elle voit | Coût | Risque |
|---|---|---|---|
| S1 (retenue par cette SPEC) : C++ (T1 + T2 + T3) | toutes les signatures C1-C4 : ville sauvegardée, correspondance, ville demandée, chemins résolus, maillage posé | 2 fichiers neufs (~200 lignes), 3 fichiers modifiés (~25 lignes), une compilation de LifePlay |
faible : lecture seule, sans asset ; se signale si un nom de propriété manque |
| S2 : plugin StreetMap | rien d'utile : le plugin ne connaît ni le GI ni le StaticMeshComponent de la carte (CART-01 § 1) |
— | — |
S3 : nœuds PrintString dans le GI (AutoLoad : valeur de Dummy ; GetCityByName : sortie trouvée ou vide ; MoveCity : branches) |
en plus de S1 : la valeur exacte de Dummy et la branche prise sans déduction |
~8-12 nœuds par MCP (lp-mcp-bp), sauvegarde du GI (52,7 Mo), CompileAllBlueprints, VAL ; seulement dans la révision unique B2, sinon une seconde révision du GI |
moyen : touche le gros Blueprint partagé ; inutile si S1 suffit |
S4 : PC.uasset (BeginAutoLoad(Dummy) est un événement du PC, etat/ST/STE/dumps/PC__EventGraph.txt:2762-2766) |
la valeur de Dummy au démarrage |
1-2 nœuds, sauvegarde du PC, VAL | faible à moyen ; seulement si S1 laisse C1 ambigu |
Les crochets du moteur sur l'exécution Blueprint (FBlueprintCoreDelegates::OnScriptProfilingEvent, UE_5.8/.../CoreUObject/Public/UObject/Script.h:455)
exigent des Blueprints instrumentés (profilage éditeur) : ils ne sont pas fiables dans une build cuite (estimé), donc écartés. Le GI dérive de
/Script/Engine.GameInstance et non d'une classe C++ du projet (grep -a sur Content/BPs/GI.uasset) : on ne peut pas surcharger ProcessEvent côté projet.
5. Lecture MCP préalable : non nécessaire
Les noms utilisés (Cities, CityToMoveTo, StreetMapStatic, SaveGameInstance, City, StreetMap, StaticMeshComponent, préfixes des
membres de City) sont établis par les dumps STB, STE et L6f. S'ils ont changé (CG2, B2), la trace écrit PROPRIETE ABSENTE au lieu de mentir.
La lecture MCP de CART-02 § 4 reste utile pour interpréter le résultat (ordre de Cities), pas pour poser la trace.
6. Déroulé proposé (lp-lot), sous le jeton UE quand il sera libre
- IMPL (
lp-impl, N3) : T3, T1, T2 sous instantanéTools/Snapshot.ps1, entrée CHG, aucun asset. - BUILD (
lp-builder) :Tools/Build.ps1, éditeur Development ; 0 erreur, 0 avertissement nouveau. - VAL (
lp-validator) : en PIE ou par un commandlet de démarrage, puisgrep "\[LPCity\]" Saved/Logs/LifePlay.log. Attendu au minimum :CITIES n=…(au moins une ligne) etPOSE …/000Dummyau démarrage ; 0 lignePROPRIETE ABSENTE; rien de nouveau à l'écran ; build Shipping sans symboleLPCityTrace(corps exclu). Diff relu : aucune écriture par réflexion. - Livraison dans la build Development commune, générée après toutes les opérations en cours (décision de l'utilisateur).
- L'utilisateur joue jusqu'au symptôme et transmet le journal. Lecture : la signature C1-C4 du § 1 désigne le correctif (SPEC suivante).
7. Critères d'acceptation
| # | Critère | Preuve attendue |
|---|---|---|
| A1 | Aucun fichier Content\ ni Plugins\ modifié |
git status --short -- Content Plugins vide |
| A2 | Compilation Development sans erreur ni avertissement nouveau | résumé Tools/Build.ps1 |
| A3 | Lignes [LPCity] présentes en Development : CITIES et POSE au minimum |
grep -c "\[LPCity\]" ≥ 2 sur un journal PIE |
| A4 | Rien de nouveau à l'écran | essai visuel ou absence d'appel à AddOnScreenDebugMessage dans le diff |
| A5 | Absent du Shipping | #if !UE_BUILD_SHIPPING autour du corps, relu dans le diff |
| A6 | Lecture seule du GI | diff : seulement GetObjectPropertyValue, ContainerPtrToValuePtr en lecture, ExportTextItem |
8. Incertitudes, écarts, fichiers
- Incertain : le type exact de
Save.City(chaîne ou texte).ExportTextItemcouvre les deux. Il reste aussi à confirmer queCityToMoveToest bien posé à chaqueMoveCity(VariableSet_4974,GI__EventGraph_P2.txt:12289-12294, sur deux branches seulement). T1 et T2e couvrent le cas où T2c manque un changement. - Non mesuré : rien (SPEC). Aucun fichier du projet modifié.
- Fichiers écrits :
D:\Game\LifePlay-master 5.8\Docs\Migration\etat\REL\STM1\STM1-SPEC-01.md(A) ;STM1-TACHES.md(M).
Tours : ≈ 9 / 40 (déclaré ; FicheStats.py fait foi).
À verser : lp-mcp ou lp-commandlets — en Development, les PrintString/PrintText des Blueprints écrivent dans LogBlueprintUserMessages (garde !(SHIPPING||TEST)) :
un graphe déjà bavard se trace par le journal, sans toucher au Blueprint.
Suite : Big Boss doit ouvrir l'IMPL STM1 (T1-T3, C++ seul) dans la file du jeton UE, avant la build Development commune. État : SPEC close, aucun fichier du projet modifié.