LifePlay-Revival/Docs/Migration/etat/CAM/CAM-B2p-G-PC-IMPL.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

234 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Fiche CAM-B2p-G-PC — Niveau N3 — Modèle prévu non précisé par la fiche | utilisé claude-opus-5-5 — Agent lp-mcp-bp — Skills sk-protocol, sk-change-safety, lp-mcp, lp-lot
# CAM-B2p-G-PC-IMPL : C3 dans le cycle C du sexe (G-d) et panneau des positions (G-g), PC (2026-10-14)
**Résultat : réussi en deux temps.**
- **Premier passage :** arrêt au premier refus, à l'étape 7 de G-g (13:48), sans contournement.
- **Reprise :** sur l'option A, retenue par l'orchestrateur (amendement `B2p-G-PC-a` de la fiche). G-d et G-g ont été appliqués et relus conformes, puis compilés en mode strict sans diagnostic. Le PC a été enregistré seul.
- **MD5 :** PC `F45BE57A39D13D3EB19D0B543153AB0B` → `B07F16A9EC04CA297A8504F126D406E1` (2026-10-14 13:52) ; Room, GI et Person sont inchangés.
- **Éditeur :** laissé ouvert (pid 18468).
Fiche : `CAM-B2p-G-PC-FICHE.md`, avec l'amendement `B2p-G-PC-a` (l. 69-79). Préfixe des nœuds : `/Game/BPs/PC.PC:EventGraph.`. Outils :
`editor_toolset.toolsets.asset.AssetTools` (AT) et `editor_toolset.toolsets.blueprint.BlueprintTools` (BT). Un seul
appel MCP par message : 46 appels au total, et 46 lignes `Dispatching toolset tool` au journal de l'éditeur.
## 1. Fichiers modifiés
- `Content/BPs/PC.uasset` : enregistré par `AT.save_assets ["/Game/BPs/PC"]` (13:52) ; 3 404 786 → 3 424 059 octets.
- Ce livrable.
### 1.1 Contrôles d'entrée
| Contrôle | Résultat |
|---|---|
| MD5 avant écriture (`md5sum`, 2026-10-14 ≈ 13:44) | PC `f45be57a39d13d3eb19d0b543153ab0b`, Room `931387479e2d15f80bc4f36398907712`, GI `32fc0a970ab194626e8dc07df3a9633d`, Person `8668a47c627f7a5254910cf0ac33b12a` : conformes ; PC revérifié avant la reprise : `f45be57a…` |
| Instantané | `D:\Game\LifePlay_Backup_5.5\CAM-B2p-G-PC\manifest.csv` : `"Content\BPs\PC.uasset","True","3404786","F45BE57A39D13D3EB19D0B543153AB0B","2026-10-14T13:42:48"` |
| Journal éditeur au départ | `Saved\Logs\portage\CAM-B2p-G-PC-editeur_20261014-134248.log` : 2 260 lignes, 43 lignes `error|warning` au démarrage, 0 `Saving Package`, 0 `Dispatching toolset tool` |
| Action de `InputAction_3` | type `Entrée|ActionEvents|InputActionPause` ; `Config\DefaultInput.ini:97` : `+ActionMappings=(ActionName="Pause",...,Key=SpaceBar)` → **Pause, barre espace : conforme** |
### 1.2 État attendu relu avant écriture (appels 2 et 3) : conforme
| Nœud | Lu |
|---|---|
| `IfThenElse_23` | Condition ← `VariableGet_51` (`|GetSexOn`, self ← `VariableGet_50`) ; then → `44` ; else → `131` ; execute ← `CallFunction_211` |
| `IfThenElse_44` | execute ← `23` then et `CustomEvent_5` then [1] ; Condition ← `CallFunction_139` ; then → `VariableSet_25` (`POVOn` vrai) ; else → `VariableSet_30` (`POVOn` faux) |
| `IfThenElse_123` | Condition ← `VariableGet_233` (`|GetSexOn`) ; then → `CallFunction_35` (`|LeftMouseSexOn`) ; else → `IfThenElse_0` |
| `IfThenElse_15` | Condition ← `VariableGet_22` (`|GetSexOn`, self ← `VariableGet_6044`) ; then → `IfThenElse_10` ; else → `CallFunction_160` |
| `CustomEvent_11` | `AddEvent|Custom|LeftMouseSexOn` ; then [1] → `IfThenElse_10` |
| `InputAction_3` | `InputActionPause` ; Pressed [0] → `IfThenElse_63` |
| `IfThenElse_40` | execute ← `63` else ; Condition ← `CallFunction_125` ; then → `CallFunction_277` ; else → `IfThenElse_41` |
| `CallFunction_277` | `|PlayClick` ; then → `VariableSet_0` (`|SetGamePaused`) |
## 2. Écarts à la SPEC
1. **Refus à l'étape 7 (premier passage)** : appel 33 :
```
BT.create_node {type_id:"Variables|Sex|ObtenirSexOn"}
→ The node could not be created / Variables|Sex|ObtenirSexOn does not exist
```
- Ce type n'existe que sous contexte : `find_node_types` ne le rend qu'avec `context_pins` = la sortie d'un Get GI.
- Or `create_node` transmet un contexte vide : `create_node_from_name(type_id, pos, [], declaring_class)`, dans `D:\Game\Launcher\Engine\UE_5.8\Engine\Plugins\Experimental\Toolsets\EditorToolset\Content\Python\editor_toolset\toolsets\blueprint.py:912`.
- J'ai arrêté. L'orchestrateur a retenu l'option A (amendement `B2p-G-PC-a`).
- Conséquence : la Condition de S est prise sur le getter existant `VariableGet_22`, et le Get GI `VariableGet_387`, créé pour rien, a été supprimé.
2. **Vérification des types** : un seul `find_node_types` par type, juste avant le premier `create_node` de ce type. Les créations suivantes du même type ne l'ont pas refait : Branche ×3 (appel 6), `DéfinirSexC3` ×2 (appel 5).
3. **Contrôles groupés** : les nœuds créés ont été contrôlés par des `get_node_infos` groupés (appels 14 et 34), toujours avant tout lien vers eux.
4. **Ligne `Warning` au journal de l'éditeur** : une seule, due au refus de l'appel 33. Rien de nouveau depuis.
5. **`delete_node` et relecture** : après `delete_node`, `get_node_infos` résout encore `VariableGet_387` par son chemin. C'est attendu : `UEdGraph::RemoveNode` retire le nœud du tableau `Nodes` sans détruire l'objet (`Engine\Source\Runtime\Engine\Private\EdGraph\EdGraph.cpp:261-281`). L'absence dans le graphe est donc prouvée autrement : par `find_nodes`, qui parcourt `list_all_nodes` (`blueprint.py:709`), voir § Reprise.
## 3. Instantané, appels et relectures
- **Instantané :** lot `CAM-B2p-G-PC`, `D:\Game\LifePlay_Backup_5.5\CAM-B2p-G-PC\manifest.csv` (PC `F45BE57A…`).
- **Retour arrière :** `.\Tools\Restore.ps1 -Lot CAM-B2p-G-PC -Only Content\BPs\PC.uasset`, éditeur fermé. Il est proposé, pas lancé.
### 3.1 Appels MCP (46, un à la fois)
| # | Outil | Objet | Retour |
|---|---|---|---|
| 1 | AT.load_asset | `/Game/BPs/PC` | `/Game/BPs/PC.PC` |
| 2-3 | BT.get_node_infos | état attendu (16 nœuds) | conforme |
| 4 | BT.add_variable | `{blueprint:/Game/BPs/PC.PC, name:"SexC3", type_name:"bool"}` | null |
| 5 | BT.find_node_types | `SexC3` | `Variables|Pardéfaut|ObtenirSexC3`, `Variables|Pardéfaut|DéfinirSexC3` |
| 6 | BT.find_node_types | `ContrôleDeFlux|Branche` | `Utilitaires|ContrôleDeFlux|Branche` |
| 7-10 | BT.create_node | Branche, ObtenirSexC3, DéfinirSexC3, Branche | `IfThenElse_132`, `VariableGet_385`, `VariableSet_39`, `IfThenElse_133` |
| 11 | BT.find_node_types | `POVOn` | `Variables|Pardéfaut|ObtenirPOVOn`, `…|DéfinirPOVOn` |
| 12-13 | BT.create_node | ObtenirPOVOn, DéfinirSexC3 | `VariableGet_386`, `VariableSet_58` |
| 14 | BT.get_node_infos | 132, 385, 39, 133, 386, 58 | conformes, aucun lien |
| 15 | BT.set_pin_value | `58` [1] = `true` | null |
| 16-23 | BT.connect_pins | 8 liens G-d | null ×8 |
| 24 | BT.get_node_infos | relecture G-d | conforme |
| 25-26 | BT.break_pins | `123.then ↛ 35`, `15.then ↛ 10` | null ×2 |
| 27 | BT.find_node_types | `LeftMouseSexOn` | `Appelerunefonction|LeftMouseSexOn` |
| 28-29 | BT.create_node | appel LeftMouseSexOn, Branche | `CallFunction_408`, `IfThenElse_134` |
| 30 | BT.find_node_types | `Variables|Pardéfaut|ObtenirGI` | idem |
| 31 | BT.create_node | ObtenirGI | `VariableGet_387` (supprimé à l'appel 37) |
| 32 | BT.find_node_types | `SexOn`, contexte = sortie de 387 | `Variables|Sex|ObtenirSexOn`, `…|DéfinirSexOn` |
| 33 | BT.create_node | `Variables|Sex|ObtenirSexOn` | **refus** → arrêt (§ 2.1) |
| 34 | BT.get_node_infos | état G-g à l'arrêt | 408, 134, 387 sans lien ; 40 → 277 intact |
| 35 | BT.get_node_infos | reprise : 22, 134, 387, 408, 40, 277 | conforme à l'attendu de la reprise |
| 36 | BT.connect_pins | `22` [O0] → `134` [I1] | null |
| 37 | BT.delete_node | `VariableGet_387` | null |
| 38-40 | BT.connect_pins | `40` [O0] → `134` [I0] ; `134` [O1] → `277` [I0] ; `134` [O0] → `408` [I0] | null ×3 |
| 41 | BT.get_node_infos | relecture finale, 19 nœuds | conforme (§ 3.3) |
| 42 | BT.get_node_infos | `VariableGet_387` | objet encore résolu, sans lien (§ 2.5) |
| 43 | BT.find_nodes | `{title:"GI", node_class:K2Node_VariableGet}` | 111 nœuds, **`VariableGet_387` absent** (6044, 2873, 232 et 50 présents) |
| 44 | AT.is_dirty | `/Game/BPs/PC` | `true` |
| 45 | BT.compile_blueprint | `{blueprint:/Game/BPs/PC.PC, warnings_as_errors:true}` | null ; aucune ligne `Error`/`Warning` |
| 46 | AT.save_assets | `["/Game/BPs/PC"]` | `true` ; une ligne `Saving Package: /Game/BPs/PC` |
### 3.2 Variable, nœuds créés, liens (PinID `{direction, index_id, node}`)
**Variable de membre `SexC3`**
- Type : booléen. Défaut : faux, valeur par défaut du type, non relue sur le CDO.
- Nœuds Get et Set : `|GetSexC3` et `|SetSexC3`, sans broche cible (self = le PC).
**Nœuds créés et gardés (8)**
| Nœud | Rôle |
|---|---|
| `K2Node_IfThenElse_132` | branche A |
| `K2Node_VariableGet_385` | Get SexC3 |
| `K2Node_VariableSet_39` | S0, SexC3 = faux |
| `K2Node_IfThenElse_133` | branche B |
| `K2Node_VariableGet_386` | Get POVOn, self |
| `K2Node_VariableSet_58` | S1, SexC3 = vrai |
| `K2Node_IfThenElse_134` | branche S |
| `K2Node_CallFunction_408` | appel `|LeftMouseSexOn`, self non relié = self |
**Nœud créé puis supprimé :** `K2Node_VariableGet_387` (Get GI).
**Liens G-d**
1. `VariableGet_385` {EGPD_Output,0} → `IfThenElse_132` {EGPD_Input,1}
2. `IfThenElse_23` {EGPD_Output,0} → `IfThenElse_132` {EGPD_Input,0} (remplace 23.then → 44)
3. `IfThenElse_132` {EGPD_Output,0} → `VariableSet_39` {EGPD_Input,0}
4. `VariableSet_39` {EGPD_Output,0} → `IfThenElse_44` {EGPD_Input,0}
5. `IfThenElse_132` {EGPD_Output,1} → `IfThenElse_133` {EGPD_Input,0}
6. `VariableGet_386` {EGPD_Output,0} → `IfThenElse_133` {EGPD_Input,1}
7. `IfThenElse_133` {EGPD_Output,0} → `IfThenElse_44` {EGPD_Input,0}
8. `IfThenElse_133` {EGPD_Output,1} → `VariableSet_58` {EGPD_Input,0}
Valeurs : `VariableSet_58` {EGPD_Input,1} = `true` ; `VariableSet_39` {EGPD_Input,1} = `false`.
**Coupures G-g**
- `IfThenElse_123` {EGPD_Output,0} ↛ `CallFunction_35` {EGPD_Input,0}
- `IfThenElse_15` {EGPD_Output,0} ↛ `IfThenElse_10` {EGPD_Input,0}
**Liens G-g**
1. `VariableGet_22` {EGPD_Output,0} → `IfThenElse_134` {EGPD_Input,1}. Ce lien s'ajoute à `22 → 15` [1].
2. `IfThenElse_40` {EGPD_Output,0} → `IfThenElse_134` {EGPD_Input,0}. Il remplace 40 → 277.
3. `IfThenElse_134` {EGPD_Output,1} → `CallFunction_277` {EGPD_Input,0}
4. `IfThenElse_134` {EGPD_Output,0} → `CallFunction_408` {EGPD_Input,0}
### 3.3 Relecture finale (appel 41) : conforme
| Nœud | Relu |
|---|---|
| `IfThenElse_23` | execute ← `CallFunction_211` ; Condition ← `VariableGet_51` ; then → `132` ; else → `131` |
| `IfThenElse_44` | execute ← `CustomEvent_5` [1], `VariableSet_39` [0], `IfThenElse_133` [0] ; Condition ← `CallFunction_139` ; then → `VariableSet_25` ; else → `VariableSet_30` |
| `CustomEvent_5` | `SwitchCamera` ; then [1] → `44` ; OutputDelegate → `CallFunction_198` [1] |
| `IfThenElse_132` (A) | execute ← `23` ; Condition ← `385` ; then → `39` ; else → `133` |
| `IfThenElse_133` (B) | execute ← `132` else ; Condition ← `386` ; then → `44` ; else → `58` |
| `VariableGet_385` / `386` | SexC3 → `132` [1] / POVOn → `133` [1] |
| `VariableSet_39` (S0) | execute ← `132` then ; SexC3 = false ; then → `44` |
| `VariableSet_58` (S1) | execute ← `133` else ; SexC3 = true ; then vide |
| `IfThenElse_123` | then **vide** ; else → `IfThenElse_0` ; execute ← `IfThenElse_110` ; Condition ← `VariableGet_233` |
| `IfThenElse_15` | then **vide** ; else → `CallFunction_160` ; execute ← `IfThenElse_19` ; Condition ← `VariableGet_22` |
| `IfThenElse_10` | execute ← `CustomEvent_11` [1] seul ; then → `CallFunction_1524` ; else → `CallFunction_154` ; Condition ← `CallFunction_79` |
| `CallFunction_35` | execute vide (orphelin, voulu par l'étape 5 ; nœud d'origine, pas créé ici) |
| `IfThenElse_40` | execute ← `63` else ; Condition ← `CallFunction_125` ; then → `134` ; else → `IfThenElse_41` |
| `CallFunction_277` | execute ← `134` else ; then → `VariableSet_0` |
| `IfThenElse_63` | execute ← `InputAction_3` ; Condition ← `VariableGet_175` ; then vide ; else → `40` (non touché ; son « then vide » n'a pas été relu avant écriture) |
| `VariableGet_22` | self ← `VariableGet_6044` ; SexOn → `15` [1] et `134` [1] |
| `IfThenElse_134` (S) | execute ← `40` then ; Condition ← `22` ; then → `408` ; else → `277` |
| `CallFunction_408` | execute ← `134` then ; then vide ; self non relié |
Aucun autre lien modifié parmi les nœuds relus. Aucun nœud que j'ai créé n'est orphelin : 387 est absent du graphe, selon l'appel 43.
## 4. Ce qui n'a pas été fait
- La preuve en jeu (build empaqueté + `LogStats`) et la VAL (V5 et V6), éditeur fermé : elles ne relèvent pas de cette fiche.
- Le défaut de `SexC3` sur le CDO n'a pas été relu.
## 5. Points à confirmer en VAL
- V5 : diff du dump du PC contre `CAM-B2kmn-VAL_LPDumpFunction_20261014-011816.txt`. Seules différences attendues :
- variable `SexC3` ;
- les 8 nœuds du § 3.2 (sans `VariableGet_387`) ;
- coupures `123.then ↛ 35` et `15.then ↛ 10` ;
- les 8 liens G-d et les 4 liens G-g du § 3.2.
- `IfThenElse_63.then` : vide après écriture, à comparer au dump de référence (non relu avant écriture, non touché).
- `CallFunction_35` : exécution non reliée après la coupure. Attendu : aucun diagnostic (V6).
- Défaut de `SexC3` = faux.
- V6 : `CompileAllBlueprints` = référence.
- La taille du PC croît de 19 273 octets. Que l'objet supprimé `VariableGet_387` ait été écrit ou non comme export orphelin reste **incertain** : il n'est plus dans `Nodes`, et la suppression suit le même chemin que dans l'éditeur. Sans effet attendu ; le dump ne le montrerait pas.
- Comportement en jeu, preuve par le build empaqueté :
- Scene → C → C3 → C → POV 1 → … → Scene ;
- pendant le sexe, Espace bascule le panneau des positions au lieu de mettre en pause ;
- les clics courts gauche et droit ne le basculent plus ;
- hors sexe, Espace met toujours en pause.
## 6. Fichiers à commiter (dépôt du jeu, branche ue5.8)
- `M Content/BPs/PC.uasset`
- `A Docs/Migration/etat/CAM/CAM-B2p-G-PC-IMPL.md`
## Mesuré
```
$ md5sum Content/BPs/PC.uasset Content/BPs/Room.uasset Content/BPs/GI.uasset Content/BPs/Person.uasset (avant, ≈ 13:44)
f45be57a39d13d3eb19d0b543153ab0b *Content/BPs/PC.uasset
931387479e2d15f80bc4f36398907712 *Content/BPs/Room.uasset
32fc0a970ab194626e8dc07df3a9633d *Content/BPs/GI.uasset
8668a47c627f7a5254910cf0ac33b12a *Content/BPs/Person.uasset
$ md5sum (mêmes fichiers, après save_assets, 2026-10-14 13:52) ; ls -l Content/BPs/PC.uasset
b07f16a9ec04ca297a8504f126d406e1 *Content/BPs/PC.uasset
931387479e2d15f80bc4f36398907712 *Content/BPs/Room.uasset
32fc0a970ab194626e8dc07df3a9633d *Content/BPs/GI.uasset
8668a47c627f7a5254910cf0ac33b12a *Content/BPs/Person.uasset
-rw-r--r-- 1 <compte> 197609 3424059 Oct 14 13:52 Content/BPs/PC.uasset
$ wc -l ; grep -c "Dispatching toolset tool" ; grep -n "Saving Package" (journal de l'éditeur, après l'appel 46)
2407 Saved/Logs/portage/CAM-B2p-G-PC-editeur_20261014-134248.log
46
2378:[2026.10.14-11.52.07:434][589]LogFileHelpers: Saving Package: /Game/BPs/PC
$ tail -n +2348 | grep -nE "Error|Warning|error|warning" (depuis la reprise : compilation et enregistrement)
(aucune ligne)
$ extrait de la compilation
[2026.10.14-11.51.55:967][138]LogBlueprint: Compiling Blueprint '/Game/BPs/PC.PC'
[2026.10.14-11.51.57:058][138]LogUObjectHash: Compacting FUObjectHashTables data took 4.59ms
$ tail -n +2261 | grep -nE "Error|Warning|error|warning" (lignes nouvelles de toute la fiche, extrait utile)
83:[2026.10.14-11.47.28:843][104]LogScript: Warning: The node could not be created / Variables|Sex|ObtenirSexOn does not exist
$ git status --short -- Content/BPs/
M Content/BPs/GI.uasset
M Content/BPs/PC.uasset
M Content/BPs/Person.uasset
M Content/BPs/Room.uasset
```
Lecture de ces sorties :
- GI, Person et Room étaient déjà marqués `M` au début de la session. Leurs MD5 n'ont pas bougé : ils n'ont pas été modifiés ici.
- Les 4 lignes `LogModelContextProtocol: Error` de 11:44:31-32 (`Unknown session id`, `server/discover`) viennent de la poignée de main du client MCP. Elles précèdent le premier appel.
## Reprise (amendement B2p-G-PC-a, 2026-10-14 13:49-13:52)
| Étape de la consigne | Fait | Preuve |
|---|---|---|
| 1. Relecture de 22, 134, 387, 408, 40, 277 | conforme : 22 → 15 [1] seul ; 387, 134, 408 sans lien ; 40.then → 277 | appel 35 |
| 2. `22.SexOn` → `134.Condition` | fait | appel 36, relu (41) |
| 3. `delete_node` 387 | fait | appel 37 ; absent de `find_nodes` (43) |
| 4. 40 → 134 (remplace 40 → 277) ; 134.else → 277 ; 134.then → 408 | fait | appels 38-40, relus (41) |
| 5. Relecture complète (19 nœuds) | conforme | appel 41, § 3.3 |
| 6. `is_dirty` ; compilation stricte ; enregistrement du PC seul ; une seule ligne `Saving Package` ; MD5 | `true` ; null, sans diagnostic ; `true` ; ligne 2378 seule ; PC `b07f16a9…`, les trois autres inchangés | appels 44-46, § Mesuré |
Tours : 63 / 60. La reprise n'a pas reçu de nouveau budget écrit : 44 tours au premier passage, 19 à la reprise, sous le plafond de 75.
## Constats
- **Get d'un membre d'une autre classe :** `find_node_types` avec `context_pins` rend des `type_id` de membres d'une autre classe (ici `Variables|Sex|ObtenirSexOn` de GI) que `create_node` ne sait pas créer (contexte vide, `blueprint.py:912`). Pour un tel Get, la fiche doit prévoir la reprise d'un getter existant, qui marche : une sortie de données alimente plusieurs entrées.
- **Preuve d'une suppression :** après `delete_node`, `get_node_infos` résout encore le nœud par son chemin, car `RemoveNode` ne le retire que de `Nodes`. La preuve d'absence passe par `find_nodes`, filtré par titre et classe (`node_class:{refPath:"/Script/BlueprintGraph.K2Node_VariableGet"}` accepté).
- **Variable ajoutée :** `add_variable` rend `null` ; `find_node_types "<Var>"` (Get et Set) prouve qu'elle existe.
- **Types vérifiés :**
- appel d'un événement personnalisé du PC : `Appelerunefonction|<Nom>` ;
- `AT.is_dirty {asset_path:"/Game/BPs/PC"}` rend `true` / `false`.