LifePlay-Revival/Docs/Migration/etat/ST/STG/STG-SF4-VAL.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

83 lines
13 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 STG-SF4v — Niveau N1 — Modèle prévu lp-validator | utilisé Sonnet 5.5 (claude-sonnet-5-5) — Agent lp-validator — Skills lp-validate, sk-protocol, sk-recount, ue-cpp-build
# STG-SF4v-VAL : validation statique du correctif S-F4 (plancher Room.Box en extérieur) (2026-10-13)
Verdict global : **PASS-STATIC** (lecture seule, aucune compilation, aucun lancement ; le build reste à faire dans COMMUN-2).
## 1. Fiches amont relues
- `Docs/Migration/etat/ST/STG/STG-M1-ANALYSE.md` § 2 et § 4 (lignes 13-14, 27-34, 53-54) : motif et attendu.
- `Docs/Migration/etat/post-v1/PawnDyn/PawnDyn_LPDumpComponents_20261004-223842.txt` l. 116-130 : `Box` (BoxComponent) `parent=StaticMeshComponent0`, `BoxExtent = (400, 400, 64)`, `AreaClass = NavArea_Obstacle`, `ObjectType=ECC_Vehicle` ; racine `StaticMeshComponent0` `RelativeScale3D = (8, 8, 0.2)`, `Mobility = Static`. La `Box` n'imprime ni `RelativeLocation` ni `RelativeScale3D` ni `Mobility` (valeurs par défaut du dump : estimé 0 / 1 / non Static ; la mobilité réelle n'est pas établie, d'où le traitement du cas Static par le code).
- `Docs/Migration/etat/ST/STG/STG-TACHES.md` l. 25 (ligne STG-M1a, correctif S-F4 proposé).
- Aucune fiche IMPL, ni fichier `STG-SF4-IMPL.md`, ni fiche `STG-SF4*.md` dans le dossier STG (Glob `STG-*`) : seul le patch `STG-SF4.patch` tient lieu de livraison amont. Aucun « point à confirmer » d'IMPL à reprendre (voir § 2).
- Instantané d'origine `D:\Game\LifePlay_Backup_5.5\STG\manifest.csv` : .cpp MD5 `1340EE4F64FC10E84F829AEBCEE44161`, .h `66F21A879542DA505C744AE7C424A762`.
## 2. Points à confirmer des IMPL
sans objet : aucune IMPL amont pour S-F4 (Glob `STG-*` du dossier STG : pas de livrable IMPL pour SF4). Les points propres à cette VAL sont aux § 3 et § 6.
## 3. Critères rejoués (commande → résultat)
### a. Diff, périmètre
- `git status --short -- Source` → 4 fichiers modifiés : `LPDebugSubsystem.cpp`, `LPDebugSubsystem.h`, `LPOutdoorRoomSubsystem.cpp`, `LPOutdoorRoomSubsystem.h`.
- `LPDebugSubsystem.*` ne relève pas de S-F4 : ils sont déjà dans le manifeste d'instantané STG (date 13:41:41, avant S-F4 à 17:11) ; leur diff est hors périmètre de cette fiche et non relu ligne à ligne (`git diff --stat` : cpp +249, h 2+/1-). **Verdict a : CONFORME pour S-F4** (les deux fichiers attendus portent le correctif ; deux fichiers voisins modifiés relèvent d'un autre travail du lot STG, à rattacher à son ticket par l'orchestrateur).
- `diff <(git diff -- LPOutdoorRoomSubsystem.cpp LPOutdoorRoomSubsystem.h) STG-SF4.patch` → `PATCH_IDENTIQUE` : le patch livré est exactement le diff de l'arbre de travail.
- `git diff --numstat` → `.cpp 77 ajouts / 2 suppressions`, `.h 15 / 0`.
- MD5 (`md5sum`) : .cpp `dd8646f808aaab150e5ea1a330f870ea`, .h `ca18ac44c1e682490b689a81dfec0bd2` = valeurs de la fiche.
### b. API UE 5.8 (racine `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime`)
| API | Source | Verdict |
|---|---|---|
| `UBoxComponent::SetBoxExtent(FVector, bool=true)` | `Engine/Classes/Components/BoxComponent.h:39` ; corps `Engine/Private/Components/BoxComponent.cpp:28-40` (met à jour bornes, rendu, corps, forme physique) | OK |
| `GetUnscaledBoxExtent() const` → `FVector` | `BoxComponent.h:47`, inline l. 80-83 (renvoie `BoxExtent`) | OK |
| `GetComponentScale()` (monde) | `Engine/Classes/Components/SceneComponent.h:1084-1087` | OK |
| `GetRelativeScale3D()`, `GetRelativeLocation()` | `SceneComponent.h:1521`, `:1435` (const, par valeur) | OK |
| `SetRelativeLocation(FVector, bool bSweep=false, …)` | `SceneComponent.h:434` | OK |
| `SetMobility(EComponentMobility::Type)` | `SceneComponent.h:1296` (`ENGINE_API virtual`, publique) ; corps `Private/Components/SceneComponent.cpp:3098-3116` (ré-enregistre le composant ; si Movable, passe les enfants en Movable) | OK |
| Membre `Mobility` public | `SceneComponent.h:303`, déclaré après `public:` l. 291 ; `UPROPERTY(... Replicated) TEnumAsByte<EComponentMobility::Type>` ; commentaire l. 302 « Only safe to set in constructor » | OK (lecture publique) ; voir risque R1 |
| `EComponentMobility` (enum `Type : int`) | `Engine/Classes/Engine/EngineTypes.h:4028-4030` ; le .h inclut `Engine/EngineTypes.h` (diff l. 139) | OK |
| `UE_KINDA_SMALL_NUMBER` | `Core/Public/Math/UnrealMathUtility.h:131` (`1.e-4f`) ; comparé à un `double` : promotion sans perte gênante | OK |
| `FMath::IsNearlyEqual(double, double, double)` | `UnrealMathUtility.h:393` ; appel `IsNearlyEqual(Floor->GetRelativeScale3D().Z, 1.0)` : double/double, pas d'ambiguïté (LWC) | OK |
| `TEnumAsByte<EComponentMobility::Type>` en membre d'UCLASS sans `UPROPERTY` | même type que le membre moteur l. 303 ; membre brut dans une classe UObject (comme `FVector FloorExtent` et `FTSTicker::FDelegateHandle` déjà présent) : légal, ni GC ni sérialisation concernés. Initialiseur `= EComponentMobility::Movable` : conversion implicite enum → `TEnumAsByte` valide | OK |
| `TWeakObjectPtr<UBoxComponent>` : `IsValid()`, `Get()`, `Reset()`, affectation depuis `UBoxComponent*` | usage identique à `HiddenCeiling` existant ; `class UBoxComponent;` déclarée (.h) et `Components/BoxComponent.h` inclus (.cpp l. 5) | OK |
**Verdict b : CONFORME** (compilation non faite : établi par lecture des en-têtes, pas par UBT).
### c. Logique
1. **Dessus inchangé** — `.cpp:124-125` : demi-hauteur `E` → `T`, Z relatif `Z` → `Z + E − T`. Dessus relatif avant `Z + E`, après `(Z + E − T) + T = Z + E`. Valable car l'échelle relative Z de la Box est 1 (garde l. 105) : l'unité de la position relative et de l'extent est alors la même (échelle du parent). Hypothèse : pas de rotation relative de la Box (non gardée ; dump : aucune rotation affichée). Cohérent avec M1-ANALYSE l. 53-54. **CONFORME**.
2. **Épaisseur cible** — `T = E × 0,2 / ScaleZ` (l. 110) ; avec `E = 64`, `ScaleZ = 0,01` : `T = 1280` ; épaisseur monde `2 × 1280 × 0,01 = 25,6 cm` = `2 × 64 × 0,2` (intérieur, M1-ANALYSE l. 29). Le journal (l. 127-128) affiche `2·T·ScaleZ` = 25,6. Dessous descend de `2 × (1280 − 64) × 0,01 = 24,3 cm`. **CONFORME**.
3. **Restauration exacte** — `RestoreFloor` (l. 131-144) : `SetBoxExtent(FloorExtent)`, `SetRelativeLocation(FloorLocation)` (valeurs copiées avant modification, l. 115-117), mobilité remise si différente. Appelée : (i) retour en intérieur (`UpdateFloor`, l. 91-97) ; (ii) changement de Room ou de composant (`Tick`, l. 56-59, comparaison `ThickenedFloor.Get() != Floor`) ; (iii) `Deinitialize` (l. 39, après `RemoveTicker`). Ordre dans `RestoreFloor` : extent puis position puis mobilité ; `Reset()` toujours (l. 143). **CONFORME**. Limite : les enfants de la Box passés en Movable par `SetMobility(Movable)` ne sont pas remis en Static (dump : la Box n'a pas d'enfant listé) ; non restauré aussi si le composant est détruit avant (weak → rien à faire, correct).
4. **Aucune action en intérieur** — `bOutdoor` faux : `UpdateFloor` ne restaure que si `ThickenedFloor == Floor`, sinon `return` (l. 91-97) ; aucune écriture sur la Box. Et à l'échelle 0,2 la garde `TargetZ <= Extent.Z` (l. 111) retournerait de toute façon. **CONFORME**.
5. **WallT inchangé** — original (`git show HEAD`, diff l. 35-45) : `if (!Root || !Ceiling) return true;` puis `bOutdoor` et le bloc plafond. Après : `if (!Root) return true;` ; `bOutdoor` ; plancher traité ; `if (!Ceiling) return true;` ; bloc plafond identique (l. 74-85, ni ligne ni condition modifiée dans le diff). Cas : Root nul → retour comme avant ; Root non nul et Ceiling nul → retour comme avant (après traitement du plancher) ; les deux non nuls → même suite. Le rendu du plafond précédemment masqué (l. 49-54) est antérieur et inchangé. **CONFORME**.
6. **Pointeurs nuls** — `Room` : `IsValid(Room)` avant `Floor` (l. 55) ; `Floor` non nul testé avant `UpdateFloor` (l. 65) ; `Root` testé (l. 60) avant `GetRelativeScale3D` ; `ThickenedFloor.Get()` testé dans `RestoreFloor` (l. 133) ; `Floor->` dans `UpdateFloor` : appelant garantit non nul. `GetObjectProperty` tolère `Container` nul. Cast : `Cast<UBoxComponent>` sur la valeur de la propriété `Box` (nul si autre type). Aucune lecture nulle trouvée. **CONFORME**.
### d. Includes, module, EOL, chemins
- Includes : `Components/BoxComponent.h` (.cpp l. 5), `Engine/EngineTypes.h` (.h l. 139), déclaration avancée `UBoxComponent` (.h l. 143) ; `LifePlay.h` pour `LogLifePlay` déjà présent. Aucun `UE_VERSION_OLDER_THAN` ajouté (pas besoin de garde de version).
- `Source/LifePlay/LifePlay.Build.cs:17` : `PublicDependencyModuleNames … "Core", "CoreUObject", "Engine", …` : `Engine` présent. **Aucune modification de Build.cs nécessaire**.
- EOL : `git ls-files --eol` → `i/lf w/lf` pour les deux ; `tr -cd '\r' < fichier | wc -c` → `CR=0` pour .cpp et .h (compte par `tr`, pas par `grep -c $'\r'`).
- Chemins : Grep `C:\\Users|<nom du compte>` sur les deux fichiers → 0 occurrence.
**Verdict d : CONFORME**.
### e. Risques nommés (non corrigés, pas de changement de verdict)
- **R1 Mobilité à l'exécution** : `SetMobility` ré-enregistre le composant (`FComponentReregisterContext`, `SceneComponent.cpp:3102`) pendant le jeu, détruit/recrée l'état physique du plancher (un instant sans plancher), et le moteur documente `Mobility` « seulement sûr en constructeur » (`SceneComponent.h:302`). Le chemin n'est emprunté que si la Box n'est pas déjà Movable (mobilité réelle de la Box du Blueprint non établie par le dump). `SetMobility(Movable)` passe aussi les enfants en Movable, sans retour. Deuxième ré-enregistrement à la restauration.
- **R2 Navmesh** : la Box porte `NavArea_Obstacle` (dump l. 118-120). `SetBoxExtent` + `SetRelativeLocation` changent ses bornes et marquent le navmesh sale à chaque épaississement et à chaque restauration (rebuild local de tuiles si navigation dynamique) ; en extérieur la Box passe de 1,28 cm à 25,6 cm d'épaisseur, 24 cm plus bas, ce qui peut aussi toucher le décor placé sous la pièce (non vérifié).
- **R3 Appel à chaque image** : `Tick` du ticker central (déjà le cas). Ajout : un `FindPropertyByName` pour `Box` par image (coût du même ordre que `Room`/`WallT`) ; `UpdateFloor` sort tôt si le plancher est déjà épaissi ; si la garde d'échelle relative échoue (≠ 1), l'appel recalcule et retourne à chaque image, sans journal (pas de répétition de log). Le journal n'est écrit qu'aux deux transitions.
- **R4 Rotation relative** de la Box non gardée (hypothèse du calcul « dessus inchangé ») ; sans effet tant que le Blueprint ne tourne pas la Box.
- **R5 Valeurs non lues à l'exécution** : `ScaleZ` = échelle monde Z de la Box ; si un Blueprint modifie `Box` après le premier épaississement (autre `SetBoxExtent`), la restauration écrase cette valeur par l'extent d'origine.
## 4. Diff lu en entier
- Fichiers : 2 (`Source/LifePlay/LPOutdoorRoomSubsystem.cpp`, `.h`) ; compte : cpp +77/−2, h +15/−0 (`git diff --numstat`). Patch `STG-SF4.patch` = diff réel (diff vide entre les deux). Rien d'autre dans le diff que : includes, constantes `FloorPropertyName` / `IndoorScaleZ`, `RestoreFloor` dans `Deinitialize`, restructuration du retour anticipé de `Tick`, `UpdateFloor`, `RestoreFloor`, commentaire d'en-tête .h, trois membres (`ThickenedFloor`, `FloorExtent`, `FloorLocation`, `FloorMobility` : quatre membres) et deux méthodes privées. Aucun ajout non annoncé.
- `Content/`, `Config/` : non examinés au-delà de `git status` initial (Person.uasset, Room.uasset modifiés dans l'arbre, hors périmètre S-F4 ; le diff S-F4 n'y touche pas).
## 5. Verdict
**PASS-STATIC.** Contrôles a, b, c (1 à 6), d conformes. Risques R1 à R5 listés, aucun n'est une erreur certaine. Rien n'a été compilé ni lancé : la compilation (C2xxx éventuels), le comportement en jeu (plancher retrouvé en street, `floorHit` > 0, navmesh, absence d'effet en intérieur) restent à prouver dans COMMUN-2.
## 6. Points à confirmer par l'utilisateur
- [ ] Mobilité réelle de `Room.Box` dans le Blueprint (Static ou Movable) : le journal « Piece exterieure … plancher Box epaissi » ne le dit pas ; relever `Mobility` via `LPDumpComponents` ou trace en COMMUN-2 pour savoir si R1 est exercé.
- [ ] COMMUN-2 : compilation `LifePlayEditor` (0 erreur) puis `LPTestStringCompat` (3120 cas, `compat != herite(UE4) : 0`), puis session street : lignes de journal « plancher Box epaissi a 25.6 cm (demi-hauteur 64.0 -> 1280.0) » et « Plancher Box rendu … 64.0 » au retour en intérieur.
- [ ] Rattacher les modifications de `LPDebugSubsystem.cpp/.h` (hors S-F4) à leur ticket ; elles n'ont pas été validées ici.
- [ ] Vérifier à l'œil que le dessous de la Box (24 cm plus bas) ne traverse pas le décor ou le sol visible en street (R2).
## Fichiers à commiter
- A `Docs/Migration/etat/ST/STG/STG-SF4-VAL.md` (dépôt du jeu, copie 5.8, branche ue5.8). Aucune autre écriture : ni `Source/`, ni `Content/`, ni `Config/`.
Tours : 9 / 30
## Constats
- Le piège d'écriture : redirection `> "$S/…"` refusée par le crochet pour cet agent ; contourné par substitution de processus `diff <(git diff …) fichier` (lecture seule).