15 KiB
Fiche CAM-B2r-VAL — Niveau N1 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-validator — Skills sk-protocol, sk-recount, debug-verification, lp-validate
CAM-B2r-VAL : remise de SpaceOn / MainMenuOn après CharGen et témoins C1, VAL statique sans compilation (2026-10-14)
Date : 2026-10-14. Copie D:\Game\LifePlay-master 5.8, branche ue5.8, UE 5.8 (D:\Game\Launcher\Engine\UE_5.8).
Portée : VAL statique (lecture et commandes de lecture seule). Aucune compilation, aucun éditeur, aucun commandlet,
aucun MCP, aucun git en écriture. Seul fichier écrit : ce livrable.
1. Fiches amont relues
| Fichier | Lu | Remarque |
|---|---|---|
Docs/Migration/etat/CAM/CAM-B2r-FICHE.md (81 lignes) |
oui, en entier | R1, R2, R3, essais CAM-50 à CAM-53, retour arrière |
Docs/Migration/etat/CAM/CAM-B2r-IMPL.md (47 lignes) |
oui, en entier | voir écart de gabarit en Constats C-7 |
D:\Game\LifePlay_Backup_5.5\CAM-B2r\manifest.csv + copies Source\LifePlay\LPCameraModeSubsystem.{cpp,h} |
oui | instantané d'avant, date 2026-10-14 07:46 |
Source/LifePlay/LPCameraModeSubsystem.cpp (662 lignes) et .h (77 lignes) actuels |
oui, en entier | |
lp-doc/gabarits.md (gabarit VAL) |
oui | sections 1 à 6 ci-dessous |
2. Points à confirmer des IMPL
L'IMPL ne contient pas de section « points à confirmer » (écart de gabarit, C-7). Je reprends à la place ses deux écarts déclarés et son seul « non fait ».
| Identifiant | Point | Résultat |
|---|---|---|
| IMPL-E1 | Critère « vue = caméra de pièce » par le libellé de ComputeLabel (C1, C2, C3 plafond, C3 ancienne), Birdview exclu, pas d'écriture pendant un menu |
PASS-STATIC : libellés de RoomCameraLabels (cpp 79-81) égaux, caractère par caractère, aux retours de ComputeLabel (cpp 564, 576, 582, 594) ; Birdview rend « Camera: Overview » (cpp 564), absent de la table. Plus strict que la fiche pour le cas Builder (voir 6.R1) : acceptable |
| IMPL-E2 | R3 : si l'axe de visée de FirstPerson_C n'est pas son X local, une ligne par seconde en C1 (bruit sans effet) |
PASS-STATIC pour le risque (journal seulement, aucune écriture dans WitnessC1) ; l'axe lui-même : reporté à l'essai CAM-53 (BLOCKED sans jeu) |
| IMPL-N1 | Compilation (COMMUN-7, jeton UE) | reporté : critère « Build.ps1 : 0 erreur, aucun avertissement nouveau » de la fiche non vérifié ici (BLOCKED-jeton-UE, voulu) |
| IMPL-N2 | Essais CAM-50 à CAM-53 | reporté (V7, build suivant) |
3. Critères rejoués (commande → résultat)
| N° | Contrôle | Commande | Sortie brute (courte) | Verdict |
|---|---|---|---|---|
| 1 | MD5 avant (instantané) | md5sum <Backup>/CAM-B2r/Source/LifePlay/LPCameraModeSubsystem.* |
fe9236bd138a46bbc2b63027977b3cd9 (.cpp), 7da111cb65b267a9e2f7482cba368be9 (.h) ; manifest.csv : FE9236BD…, 7DA111CB… |
PASS : égaux aux valeurs annoncées |
| 1 | MD5 après (fichiers actuels) | md5sum "<copie>/Source/LifePlay/LPCameraModeSubsystem.*" |
124336d3293ef06befcf076d10956234 (.cpp), 61a3e06280bf7226adb52ecfdd9173de (.h) |
PASS : égaux à 124336D3… et 61A3E062… annoncés |
| 2 | Diff complet | diff -u <instantané> <actuel> pour les deux fichiers, lu en entier |
.h : 1 hunk, +11 −0 ; .cpp : 7 hunks, +154 non vides (+7 lignes vides), −2 ; 503→662 lignes, 66→77 | PASS-STATIC : tout dans le périmètre (section 4) |
| 3 | Fins de ligne | tr -cd '\r' < f | wc -c |
avant : .cpp 0, .h 0 ; après : .cpp 0, .h 0 ; espaces en fin de ligne : 0 et 0 (grep -c "[[:space:]]$") |
PASS : LF avant et après |
| 4 | Compilation « à l'œil » | relecture ligne à ligne + lecture des en-têtes UE 5.8 | voir détail 3.a | PASS-STATIC, aucun bloquant |
| 5 | Sûreté | lecture + Grep WriteBool\( |
WriteBool : définition cpp 116, appels cpp 336 et 341 seulement, tous deux dans RepairAfterCharGen (cpp 297-344) |
PASS-STATIC (détail 3.b) |
| 6 | Cohérence fiche / IMPL | comparaison R1, R2, R3 | voir section 6 | PASS-STATIC avec 3 écarts non déclarés non bloquants (C-1, C-2, C-3) |
3.a Détail du contrôle 4 (types, signatures, const, format)
FBoolProperty::SetPropertyValue_InContainer(void*, bool, int32) const:UnrealType.h:2710(UE 5.8).WriteBoolprendUObject*(non const) et uneconst FBoolProperty*: correct (cpp 116-123).FTimerHandle::StaticStruct():USTRUCT(Engine/TimerHandle.h:10-13) ; la fonction estinlinedansTimerHandle.generated.h:26et appelleENGINE_API Z_Construct_UScriptStruct_FTimerHandle(ligne 22) : pas de risque d'édition de liens depuis le module LifePlay.TimerManager.h:13inclutEngine/TimerHandle.h.FTimerManager::IsTimerActive(FTimerHandle) const:TimerManager.h:331;UWorld::GetTimerManager() const:World.h:4289.TimerActiveprendconst UWorld*: correct (cpp 126-134).ContainerPtrToValuePtr<FTimerHandle>surconst UObject*: même motif queReadVectorexistant (cpp 143).FRotator::NormalizeAxis:static,Rotator.h:432(modèleT, double en LWC).YawGapetDistancesont desdouble;CameraRotation.Pitch/Yawsont desdouble(FRotator UE5).FVector::Distrend un double.UWidget::IsInViewport() const(Widget.h:549),GetVisibility() const(Widget.h:586),GetGameInstance() const(Widget.h:871) :WidgetOnScreen(cpp 211-227) travaille surconst UUserWidget*sans appel non const.TObjectIterator::operator boolestexplicit(UObjectIterator.h:286), accepté dansIt && GI(conversion contextuelle).- Const-correctness : aucune fonction
constnouvelle.LogSwitchKey(const) n'appelle queReadBool,GetNameSafe,GetGameViewportClient(déjà présents) ; la lambdaFlagprendconst UObject*et reçoitGI/PC/Roomconst.RepairAfterCharGenetWitnessC1sont non const dans le .h (61, 67) et le cpp (297, 346) : déclarations égales aux définitions (typesconst FString&, bool, float/float). - Format
UE_LOG, spécificateurs contre arguments :- cpp 328 :
%s,%s←*GetNameSafe(View),*Label(2/2) ; - cpp 337 :
%s,%s← idem (2/2) ; - cpp 342 : aucun spécificateur, aucun argument ;
- cpp 375-378 :
%.0f %+.0f %+.0f %d %d←Distance(double),Pitch(double),YawGap(double), deux? 1 : 0int (5/5) ; - cpp 417-423 (R2) : 19 spécificateurs (14 d'origine + 5 nouveaux
%d), 19 arguments : 3+3+3int, 5%senTCHAR*(*GetNameSafe,View ? *… : TEXT("-"),*ShownLabel), puis 5Flag(...)int. Comptés à la main, un par un.
- cpp 328 :
- Unité de compilation unitaire :
Grepde tous les noms nouveaux (WriteBool,TimerActive,WidgetOnScreen,MainMenuClass,LabelC1,RoomCameraLabels,GICharGenOn,GIMainMenuOn,GIJournalOn,GIContactsOn,PCMoveCamera,PlayerFirstPersonSM,PlayerOccupyingFnt,RoomCamTimer,LeftEyeSocket,RightEyeSocket,C1Interval,C1LogInterval,C1MaxEyeDistance,C1MaxPitch,C1MaxYawGap,CharGenRepairDelay) dans toutSource/:LPCameraModeSubsystem.cpp:39et aucun autre fichier.LPCameraModeLocaln'apparaît que dans ce fichier (61 occurrences). Tous les appels sont qualifiésLPCameraModeLocal::(cpp 250-378, 417-423 ; les seuls appels non qualifiés sont à l'intérieur de l'espace nommé, il n'y en a aucun de nouveau). - Ombrage (C4458) : les locaux nouveaux (
GI,World,PC,View,Camera,Mesh,Player,Eyes,Distance,Label) ne portent le nom d'aucun membre de classe (membres :Text,Root,Viewport,ShownLabel,bShown, …). - Pas de variable inutilisée :
View(cpp 316) sert aux deux journaux ;RoomObject(cpp 373) sert àTimerActive.
3.b Détail du contrôle 5 (sûreté)
- Pointeurs :
GItesté nul (cpp 300) ;World,PC,Viewchaînés par? :(cpp 314-316) ;GetNameSafeaccepte nul ;ReadBool/ReadObject/WriteBool/TimerActivetestentContainer(cpp 105, 111, 118, 128).WitnessC1:PlayerparIsValid,MeshetCamerapar test,PCpar test,RoomparIsValidavantTimerActive,Worldtesté dansTimerActive(cpp 354-363, 373, 378). Aucune déréférence nue. WriteBool: seulement dansRepairAfterCharGen(cpp 336, 341).- Une seule réparation :
bCharGenSeen = falseest posé en cpp 333, avant les écritures ; le seul autre chemin qui le remet à faux est le délai (cpp 327).bCharGenSeenne repasse à vrai que parCharGenOnvrai (cpp 306). - Jamais hors caméra de pièce ou avec un menu : la sortie
if (!bRoomCamera || bMenu)(cpp 322-331) précède toute écriture et rend la main.Birdviewdonne « Camera: Overview », absent deRoomCameraLabels: jamais réparé.bMenuvient deMenuOnScreen(inchangé), qui inclutMainMenu_C(cpp 98) : le contrôleWidgetOnScreendu cpp 339 est une double garde (voir C-5). - Seules écritures du correctif : deux booléens de GI (
SpaceOnà vrai,MainMenuOnà faux), chacun seulement s'il vaut l'inverse (cpp 334, 339).WitnessC1etLogSwitchKeyne font que lire.
4. Diff lu en entier
Commande : diff -u entre la copie d'instantané et le fichier actuel, deux fichiers, lu intégralement.
Comptes : .h +11 −0 (1 hunk) ; .cpp +161 (dont 7 lignes vides) −2 (7 hunks). Les −2 sont la ligne UE_LOG de
LogSwitchKey (format) et sa dernière ligne d'arguments, réécrites pour R2 ; aucune autre ligne d'origine n'est modifiée.
| Hunk | Contenu | Dans le périmètre de la fiche ? |
|---|---|---|
| .h | RepairAfterCharGen, bCharGenSeen, CharGenRepairTimeout, WitnessC1, C1Cooldown |
oui (membres du .h de R1 et R3) |
| .cpp 21 | #include "TimerManager.h" |
oui |
| .cpp 67-95 | constantes R1 et R3 (GICharGenOn, GIMainMenuOn, GIJournalOn, GIContactsOn, PCMoveCamera, MainMenuClass, CharGenRepairDelay, RoomCameraLabels, LabelC1, PlayerFirstPersonSM, PlayerOccupyingFnt, RoomCamTimer, LeftEyeSocket, RightEyeSocket, C1*) |
oui. MainMenuClass, RoomCameraLabels, LabelC1 sont des aides de R1/R3, non nommées dans la fiche mais demandées par le contrôle 2 (constantes) |
| .cpp 115-134 | WriteBool, TimerActive |
oui |
| .cpp 210-227 | WidgetOnScreen |
oui |
| .cpp 250, 290-293 | appel de RepairAfterCharGen dans Tick ; branche else if (Label == LabelC1) |
oui |
| .cpp 297-379 | corps de RepairAfterCharGen (R1) et de WitnessC1 (R3) |
oui |
| .cpp 416-423 | extension de LogSwitchKey (R2) |
oui, texte égal à la fiche : ; MainMenuOn=%d JournalOn=%d ContactsOn=%d CharGenOn=%d MoveCamera=%d |
Ajouts non demandés : aucun fonctionnel. Ajouts mineurs hors lettre de la fiche : mot « indicateur » dans les journaux
(C-1), IsInViewport-et-visibilité dans WidgetOnScreen (calqué sur MenuOnScreen, cohérent). Le .cpp est non suivi par
git (??, git ls-files --error-unmatch : « did not match any file(s) known to git »), donc git diff <lot>-avant est
inutilisable ; la référence est l'instantané. git status --short -- Source ne montre pas d'autre fichier source
nouveau lié au lot (les M de LPDebugSubsystem, LPDirectControlSubsystem, LPOutdoorRoomSubsystem, LifePlay.cpp
préexistent et ne relèvent pas de ce lot : non lus ici).
5. Verdict
PASS-STATIC avec réserves (réserves non bloquantes, section Constats). Aucun défaut bloquant. Non rejoué : compilation
(Build.ps1), LPTestStringCompat, essais CAM-50 à CAM-53 (BLOCKED-jeton-UE, voulu par la fiche et l'IMPL).
Le fichier n'est pas encore prouvé compilable : seule la lecture des en-têtes UE 5.8 (section 3.a) le soutient.
6. Points à confirmer par l'utilisateur (cases)
- R1 (cohérence fiche) : la fiche définit « caméra de pièce » par
CamC1On/CamRoom/SceneCamera+CamC3On; l'IMPL utilise les libellés. Différences : le libellé exclutCamera: Scene/POV(SexOn) etCamera: Builder(scène Builder avecSceneCameraetCamC3On). Plus sûr ; accepter ? Avis du validateur : acceptable. - R1/R2/R3 sont écrits conformément à la fiche pour le texte de R2 et les seuils de R3 (8 cm, ±25°, 30°, 0,5 s, 1 s, 10 s) : confirmé par relecture (cpp 78, 90-94, 417).
- Libellés des journaux de R1 : la fiche dit
(vue %s), le code écrit(vue %s, indicateur '%s')(C-1). Accepter ? La sous-chaîne attendue par CAM-50 (« CharGen : SpaceOn remis a vrai ») reste présente. K2_IsTimerActiveHandle(fiche) remplacé parFTimerManager::IsTimerActive(C-2) : accepter ?- Compilation sous jeton UE (COMMUN-7) puis essais CAM-50 à CAM-53 : à faire ; le verdict ci-dessus ne les couvre pas.
Tours : 12 / 40
Constats
Bloquants : aucun.
Non bloquants (ligne exacte dans Source/LifePlay/LPCameraModeSubsystem.cpp) :
- C-1 (cpp 328 et 337) : les deux journaux ajoutent
indicateur '%s'au texte de la fiche (vue %s). Écart non déclaré dans l'IMPL ; sans effet fonctionnel ; le préfixe testé par CAM-50 est conservé. - C-2 (cpp 133) : la fiche R3 prescrit
UKismetSystemLibrary::K2_IsTimerActiveHandle; l'IMPL appelleWorld->GetTimerManager().IsTimerActive(Handle)après vérification deProp->Struct == FTimerHandle::StaticStruct(). Écart non listé dans « Écart à la fiche » de l'IMPL (il figure seulement dans la liste des écritures). Équivalent attendu (estimé : la fonction Kismet délègue au gestionnaire de minuteries du monde ; non relu dans le moteur) et plus simple (pas d'objet de contexte). - C-3 (cpp 322-329) : quand la vue est une caméra de pièce mais qu'un menu est à l'écran, le délai de 10 s s'écoule
quand même ; à l'échéance le journal dit « sortie sans camera de piece » alors que la cause est le menu, et la
réparation est abandonnée. Si un menu reste ouvert plus de 10 s juste après CharGen,
SpaceOnn'est pas remis. Cas rare, conforme à « jamais pendant un menu » ; message trompeur seulement. - C-4 (cpp 364) : si les douilles
lEye/rEyen'existent pas,GetSocketLocationrend la position du composant et le témoin C1 peut écrire une ligne par seconde (bruit, journal seulement). Cousin du risque déclaré IMPL-E2. - C-5 (cpp 339) :
WidgetOnScreen(MainMenu_C)est redondant avecbMenu(qui contientMainMenu_C) : quand on arrive à cette ligne,bMenuest déjà faux. Inoffensif (double garde) ; à garder pour la lisibilité ou à retirer plus tard, sur décision de l'orchestrateur. - C-6 (cpp 1) : l'en-tête du .cpp porte toujours « Lot CAM-B2c » (inchangé, pré-existant) ; rien de B2r n'y est mentionné. Cosmétique.
- C-7 (gabarit) :
CAM-B2r-IMPL.mdne suit pas le gabarit IMPL delp-doc/gabarits.md: pas de tête « Fiche … Niveau … Modèle … Agent … Skills », sections 1 à 6 absentes ou non numérotées (pas de « Points à confirmer en VAL », pas de « Fichiers à commiter », pas de ligne « Tours »). Les informations utiles (MD5, écarts, non fait) y sont. Le présent livrable déduit donc ses « points à confirmer » de la section « Écart à la fiche » et de « Non fait ». - C-8 (dépôt) :
LPCameraModeSubsystem.cppet.hsont non suivis par git (??) : pour le gardien, ils serontA(nouveaux pour git), pasM. Le retour arrière du lot passe par l'instantané (Restore.ps1 -Lot CAM-B2r).
Fichiers à commiter (par le gardien, sur ticket ; non suivis par git donc A) :
A Source/LifePlay/LPCameraModeSubsystem.cppA Source/LifePlay/LPCameraModeSubsystem.hA Docs/Migration/etat/CAM/CAM-B2r-VAL.md(ce livrable)
Fichier écrit par le validateur : D:\Game\LifePlay-master 5.8\Docs\Migration\etat\CAM\CAM-B2r-VAL.md (seul).