LifePlay-Revival/Docs/Migration/etat/REL/FIX1/FIX1-F6-IMPL.md
2026-10-21 09:36:04 +02:00

19 KiB

Fiche FIX1-F6-IMPL — Niveau N3 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-impl — Skills sk-protocol, ue-cpp-build, lp-paths

FIX1-F6-IMPL : redirection de Saves/… vers le dossier de sauvegarde, voie C++ (2026-10-20)

Ordre de l'utilisateur, cité par la fiche : « Oui, les 7 recommandations de FIX1-F6-SPEC (Q1 à Q7) ; lance l'IMPL. » Base : FIX1-F6-SPEC.md § 2.1 (étapes a à g). Ordonnateur : Big Boss (local_104ff22d), qui a donné le jeton UE. Hors gabarit : la compilation de contrôle (demandée par la fiche) est rendue dans ## Constats, avec sa note TRACE. Écart de skills : la fiche cite sk-protocol, ue-cpp-build, lp-paths. Dans mon contexte sont chargés sk-protocol, ue-cpp-build, lp-build, lp-ue-compat ; lp-paths ne l'est pas (non utilisé : aucun chemin de dépôt à décider). Niveaux de preuve : établi (fichier:ligne ou sortie de commande), estimé (raisonnement donné), inconnu (essai nommé).

1. Fichiers modifiés

Un seul fichier de source : D:\Game\LifePlay-master 5.8\Plugins\ModdingExtra\Source\ModdingExtra\Private\ModdingExtraBPLibrary.cpp.

Mesure Valeur Commande
MD5 avant (exigé 7628837e…) 7628837e7b77c68c3921acfad3d92ec0, conforme md5sum avant la première édition
MD5 après 1f93601c71d126f173fe30636aec0660 md5sum après les 7 éditions et après les 2 builds (inchangé)
Fins de ligne i/lf w/lf attr/-text ; 0 CR git ls-files --eol ; tr -cd '\r' | wc -c -> 0
Bilan du diff 1 file changed, 120 insertions(+), 2 deletions(-) git diff --stat -- Plugins/ModdingExtra
Autres fichiers touchés dans Plugins/ModdingExtra aucun git status --porcelain -- Plugins/ModdingExtra -> une seule ligne M …ModdingExtraBPLibrary.cpp

Étapes appliquées avec l'outil Edit, dans l'ordre a à g (numéros de ligne du fichier après édition) :

Étape SPEC Lignes finales Contenu
a § 2.1 a 15-16 #include "Misc/CommandLine.h", #include "Misc/Parse.h" après Misc/Paths.h (:14)
b § 2.1 b 30-140 (espace de noms LPSavesRedirect : 36-140) IsDisabled :40, Match :48, Note :67, ResolvePath :89, WarnListOnSaves :124 ; placé après DEFINE_LOG_CATEGORY_STATIC (:29), avant le constructeur (:142)
c § 2.1 c 156 LPSavesRedirect::WarnListOnSaves(TEXT("GetAllFilesFromFolder"), RootFolderFullPath); après le garde Len() < 1 de GetAllFilesFromFolder
d § 2.1 d 231 LPSavesRedirect::WarnListOnSaves(TEXT("GetAllSubfolders"), RootFolderFullPath); après le garde de GetAllSubfolders
e § 2.1 e 269-271 ligne Saves : %d lecture(s) nouvelle racine | %d lecture(s) repli | %d ecriture(s)%s dans LPLoadTxtStats::Dump, après la ligne TOTAL
f § 2.1 f 308 LoadTxt
g § 2.1 g 354 SaveTxt

Les deux seules lignes retirées (étapes f et g), texte exact :

-     const bool bOk = bFromCache || FFileHelper::LoadFileToString(SaveTextA, *(FPaths::ProjectContentDir() + FileNameA));
+     const bool bOk = bFromCache || FFileHelper::LoadFileToString(SaveTextA, *LPSavesRedirect::ResolvePath(FileNameA, false));
-     const bool bOk = FFileHelper::SaveStringToFile(SaveTextB, *(FPaths::ProjectContentDir() + FileNameB));
+     const bool bOk = FFileHelper::SaveStringToFile(SaveTextB, *LPSavesRedirect::ResolvePath(FileNameB, true));

Lignes laissées intactes, comme l'exige la SPEC (§ 1.3, § 5) : relecture de vérification :313 (ex-:195, ProjectContentDir() + FileNameA), Cache.TryGet(FileNameA, …) :307, LPLoadTxtStats::Record(FileNameA, …) :309, OnWritten(FileNameB, SaveTextB) :357 (ex-:239).

Conformité du texte à la SPEC (établi). Les 120 lignes + des blocs a à g de la SPEC (lignes 104-255, filtrées par grep '^+', préfixe retiré) ont été comparées aux 120 lignes ajoutées par git diff -U0 (même filtre) :

sed -n '104,255p' FIX1-F6-SPEC.md | tr -d '\r' | grep '^+' | sed 's/^+//'   -> 120 lignes
git diff -U0 -- <fichier> | tr -d '\r' | grep '^+' | grep -v '^+++' | sed 's/^+//'   -> 120 lignes
diff : "3d2 < (vide)" et "113a113 > (vide)"

Le seul écart du diff est la place d'une ligne vide : git l'affiche en fin de bloc b (avant le constructeur), la SPEC la montre en tête. Le texte final est le même : l. 29 DEFINE_LOG_CATEGORY_STATIC, l. 30 vide, l. 31 début du commentaire, l. 140 }, l. 141 vide, l. 142 constructeur (lu avec Read). Aucune ligne n'a été changée.

Vérification des API moteur employées (moteur 5.8, D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\Core, lecture directe, toutes établies) :

API Source
FPaths::ProjectContentDir() (retourne FString) Public/Misc/Paths.h:276
FPaths::ProjectSavedDir() (const FString&) Paths.h:290
FPaths::ConvertRelativePathToFull(const FString&) Paths.h:637
ProjectSavedDir : BaseDir + "Saved" puis + "/" ; ProjectContentDir : ProjectDir() + "Content/" Private/Misc/Paths.cpp:87, 107, 477
IFileManager::DirectoryExists(const TCHAR*) Public/HAL/FileManager.h:119
FString::FindChar(ElementType, int32&) Public/Containers/UnrealString.h.inl:1219
FString::Equals(const FString&, SearchCase = CaseSensitive), [[nodiscard]] UnrealString.h.inl:1271
FString::StartsWith(const TCHAR*, SearchCase = IgnoreCase), [[nodiscard]] UnrealString.h.inl:1505
FString::Left(int32), Mid(int32), ToLower() const &, ReplaceInline(const TCHAR*, const TCHAR*, SearchCase) UnrealString.h.inl:950, 1029, 1344, 1838
FParse::Param(const TCHAR*, const TCHAR*) Public/Misc/Parse.h:127
FCommandLine::Get() Public/Misc/CommandLine.h:58
TSet::Add(const T&, bool*), Add(T&&, bool*) Public/Containers/SparseSet.h.inl:415, 419 (Set.h redirige TSet vers ce fichier, Set.h:12-31)

2. Écarts à la SPEC

  1. Aucune correction de code n'a été nécessaire : le diff a compilé tel quel (0 erreur, aucun avertissement venant de ModdingExtraBPLibrary.cpp, voir Constats).
  2. Place de la ligne vide du bloc b dans le git diff : présentation seulement, texte identique (§ 1 ci-dessus). Pas un écart de texte.
  3. Taille du diff : la SPEC annonce « ≈ 90 lignes » (résumé), « ≈ 100 lignes » (§ 2.1) et « ≈ 100 insertions » (V1). Le diff exact de la SPEC compte 120 lignes ajoutées, 2 retirées. L'estimation de la SPEC était basse ; le texte appliqué est celui de la SPEC, pas une version raccourcie. La VAL doit lire V1 comme « 1 fichier, 120 insertions, 2 suppressions ».
  4. Numéros de ligne cités par la SPEC (:190, :236, :195, :239) : décalés de +118, +118, +118, +118 après l'insertion. Les lignes finales sont dans le tableau du § 1.
  5. Alerte CopieEtat.ps1 -Lot FIX1 (code 1, alertes=GIT) : jeu [ M] Source/LifePlay/LPKeyboardSubsystem.cpp. Fichier modifié avant ce lot (présent dans le git status de départ, lot KEY2), donc hors périmètre. Il n'a pas été touché. Conséquence sur les binaires : voir Constats.
  6. Section « Instantané » : l'instantané a été pris par l'appelant avant mon intervention ; je ne l'ai pas refait (consigne de la fiche).

3. Instantané

  • Dossier : D:\Game\LifePlay_Backup_5.5\FIX1\ (lot FIX1), pris par l'ordonnateur.
  • Manifeste D:\Game\LifePlay_Backup_5.5\FIX1\manifest.csv : une ligne, Plugins/ModdingExtra/Source/ModdingExtra/Private/ModdingExtraBPLibrary.cpp, Existed=True, Size=11994, MD5=7628837E7B77C68C3921ACFAD3D92EC0, Date=2026-10-20T19:04:55.
  • Contrôle (établi) : md5sum de la copie de l'instantané -> 7628837e7b77c68c3921acfad3d92ec0, identique au MD5 de départ du fichier du jeu.
  • Retour arrière à froid : Tools\Restore.ps1 -Lot FIX1 (proposé, non lancé), ou git revert du commit d'IMPL ; recompilation ensuite (SPEC § 3).

4. Ce qui n'a pas été fait

  • Aucun autre fichier : ni .h, ni Build.cs, ni .uplugin, ni Blueprint, ni GI, ni LPDataCache.*, ni Source/LifePlay/* (SPEC § 5).
  • Pas de build Shipping, pas d'empaquetage (BuildDebug.ps1), aucun jeu lancé : les essais E1-E9 (SPEC § 7.2) ne sont pas faits.
  • Pas de VAL (relecture indépendante), pas de DOC (README :23-24, 74, 82-83, 117, ERR-042, guides de modding Q2, REF1, doc 37 : lot DOC, Q2 « plus tard »).
  • Message de GI « copy your old LifePlay\Content\Saves folder… » non modifié (Q7 : build 2).
  • Aucune commande git qui écrit ; aucun commit ; rien publié.
  • Pas de relevé MD5 de Content\Saves ni de <SavedDir>\Saves : il se fait avant l'essai en jeu (SPEC § 7.2, préparation).
  • Pas de recherche du journal de référence « même build sans F6 » demandé par E8 : non cherché ici (voir § 5).
  • Pas de Tools/FicheStats.py : le nombre de tours ci-dessous est estimé par moi.

5. Points à confirmer en VAL

Un point par ligne. Les identifiants V1-V6 sont ceux de la SPEC § 7.1, les E1-E9 ceux de § 7.2.

  • V1 : git diff --stat -- Plugins/ModdingExtra rend 1 file changed, 120 insertions(+), 2 deletions(-) ; à accepter comme conforme malgré le « ≈ 100 » de la SPEC (écart 3).
  • V2 : relire ResolvePath, branche !Match (ModdingExtraBPLibrary.cpp:92-95) : return FPaths::ProjectContentDir() + ContentRelativePath;, expression identique à l'ancienne.
  • V3 : :313 (relecture bFromCache), :357 (OnWritten(FileNameB, …)), :307 (TryGet(FileNameA, …)), :309 (Record(FileNameA, …)) inchangés par rapport à l'instantané.
  • V4 : _session sans repli (:107-111) ; écriture jamais vers ProjectContentDir() quand la redirection est active (:107-111 renvoie NewPath si bForWrite) ; l'ancien chemin n'est rendu que pour un chemin hors Saves/ (:92-95) ou avec -LPNoSavesRedirect (:96-105).
  • V5 : git ls-files --eol du fichier -> i/lf w/lf (relevé ici : conforme, 0 CR).
  • V6 : build éditeur et build jeu à code 0, 0 erreur, aucun avertissement nouveau dans ModdingExtraBPLibrary.cpp ; seul avertissement : C4701 Aim (LPCameraModeSubsystem.cpp:2378), connu (relevé : conforme, voir Constats).
  • B1 : les binaires construits contiennent aussi la modification non commitée de Source/LifePlay/LPKeyboardSubsystem.cpp (lot KEY2) : Module.LifePlay.cpp a été recompilé pour cette raison, pas pour F6. L'essai E1-E9 se fait donc sur « F6 + KEY2 en cours », pas sur F6 seule. À signaler à l'ordonnateur avant de fixer le build de référence de la porte J4.
  • B2 : le build jeu Development est monolithique : LifePlay.exe embarque ModdingExtra (résumé Build.ps1 : « DLL liées : 0 », journal [4/5] Link [x64] LifePlay.exe). Le « build Development du commun » demandé par SPEC § 7.2 est un paquet : il reste à le produire (Tools\BuildDebug.ps1, -ArchiveDir neuf, lp-build § 4) ; ce n'est pas fait ici.
  • B3 : Shipping non compilé. Les messages UE_LOG disparaissent en Shipping sans bUseLoggingInShipping (SPEC R5) ; une compilation Shipping pourrait lever des avertissements propres à cette configuration (variable inutilisée). Inconnu : à mesurer au premier build Shipping.
  • B4 : les chaînes nouvelles sont présentes dans les deux binaires (LPNoSavesRedirect, Saves redirige, FIX1-F6, recherche UTF-16 dans UnrealEditor-ModdingExtra.dll et LifePlay.exe, trois fois True). Cela prouve que le nouveau code est lié, pas qu'il s'exécute comme prévu.
  • E1-E4 : montage Development empaqueté, relevé MD5 de <jeu>\Content\Saves avant et après ; journal LogModdingExtra: Display: Saves redirige : racine '…\Saved\Saves' une fois, Saves slot '_session' : nouvelle racine.
  • E5 : copie d'un slot d'essai dans <jeu>\Content\Saves\<SlotX> (jamais l'original de l'alpha), son .sav dans <SavedDir>\SaveGames ; attendu lu dans l'ancien Content/Saves (repli) puis nouvelle racine après un enregistrement.
  • E6 : lancement avec -LPNoSavesRedirect : Saves non redirige (-LPNoSavesRedirect) et bilan (redirection desactivee).
  • E7 : Select-String -Path <journal> -Pattern 'FIX1-F6' doit donner 0 ligne (aucune liste n'a visé Saves). Le texte du message d'avertissement contient FIX1-F6.
  • E8 : ModdingExtra.LoadTxtStats doit afficher Saves : n lecture(s) nouvelle racine | m lecture(s) repli | k ecriture(s) avec n > 0 et k > 0 ; la ligne Cache de donnees pret : <x> fichier(s) doit garder le même x qu'avant F6. Inconnu : quel journal sert de référence « même build sans F6 » (non cherché ; la référence doit tenir compte de B1).
  • E9 (Shipping, sur accord) : %LOCALAPPDATA%\LifePlay-Revival\Saved\Saves\… créé ; relevé de <jeu>\Content\Saves inchangé ; pas de journal en Shipping.
  • C1 : comportement à vérifier à la lecture, sans effet fonctionnel : un fichier posé directement dans Saves/ (slot vide) journalise Saves slot '' : nouvelle racine (chaîne vide entre guillemets). Aucun usage connu (SPEC § 1.1, ligne 3).
  • C2 : IsDisabled() mémorise l'argument à son premier appel (variable static const) ; le premier appel survient au premier LoadTxt/SaveTxt sur un chemin Saves/, ou au Dump. L'interrupteur est lu après l'initialisation de la ligne de commande, donc sans risque d'ordre ; estimé, E6 le tranche.

6. Fichiers à commiter

Dépôt du jeu (D:\Game\LifePlay-master 5.8, branche ue5.8) :

  • M Plugins/ModdingExtra/Source/ModdingExtra/Private/ModdingExtraBPLibrary.cpp
  • A Docs/Migration/etat/REL/FIX1/FIX1-F6-IMPL.md

Les deux résumés FIX1_Build_20261020-190716.txt et FIX1_Build_20261020-190831.txt sont écrits par Build.ps1 dans Docs/Migration/etat/REL/FIX1/ mais ignorés par git (git check-ignore -v : .gitignore:75 /Docs/Migration/etat/**, seul *.md est rétabli en :77) : rien à commiter pour eux. Ils sont en CRLF (48 et 25 CR, sortie du script, non retouchée) ; la règle « fins de ligne LF dans etat/ » vise les fichiers suivis, donc sans objet ici. Contrôle du livrable : FIX1-F6-IMPL.md en LF (0 CR).

À ne pas inclure dans ce ticket (modifiés par d'autres lots, présents dans le git status) : Source/LifePlay/LPKeyboardSubsystem.cpp, LifePlay.uproject, les fichiers Docs/Migration/etat/BOSS, REPO, REL/KEY2, REL/STM1, AG/ORGA2, Docs/LP-PROJET.ini. Le gardien commite par chemins explicites (sk-repo).

Les journaux complets restent dans Saved\Logs\portage\ (hors dépôt).

Tours : 36 / 60 (estimé ; Tools/FicheStats.py --lot FIX1 fait foi)

Constats

Compilation de contrôle (demandée par la fiche)

Trace : D:\Game\LifePlay-master 5.8\Docs\Migration\etat\REL\FIX1\FIX1_Build_20261020-190716.txt et …\FIX1_Build_20261020-190831.txt — Fiche FIX1-F6-IMPL — 2026-10-20 19:07 à 19:10 Commande : pwsh -NoProfile -File Tools/Build.ps1 -Lot FIX1 -EtatDir "D:\Game\LifePlay-master 5.8\Docs\Migration\etat\REL\FIX1" (éditeur), puis … -Lot FIX1 -Target LifePlay -Config Development -ExtraArgs '-NoUBA' -EtatDir … (jeu), dans D:\Game\LifePlay-master 5.8 Environnement: copie 5.8, branche ue5.8, HEAD b5feae4, moteur UE 5.8 (EngineAssociation 5.8), machine calme (UeState : processus=0 editeur=0 compilation=0 shaders=0 jeu=0 avant, entre et après), CopieEtat.ps1 -Lot FIX1 lancé avant (proc=0) Attendu : code 0 ; 0 erreur ; aucun avertissement nouveau ; seul le C4701 Aim connu ; unités ModdingExtraBPLibrary.cpp + Module.ModdingExtra.cpp recompilées Obtenu : tableau ci-dessous Lecture : le champ Makefile des deux résumés est vide ; les journaux disent Invalidating makefile for LifePlayEditor (working set of source files changed) (l. 8 du journal 1) et … for LifePlay … (l. 8 du journal 2) : makefiles recréés. Le résumé du build jeu écrit « Unreal Build Accelerator local executor » alors que -NoUBA est passé : les unités sont marquées [NoUba], le texte du résumé n'est pas le signe fiable.

Mesure Build 1 : LifePlayEditor Win64 Development Build 2 : LifePlay Win64 Development -NoUBA
Résumé …\REL\FIX1\FIX1_Build_20261020-190716.txt …\REL\FIX1\FIX1_Build_20261020-190831.txt
Journal D:\Game\LifePlay-master 5.8\Saved\Logs\portage\FIX1_Build_20261020-190716.log D:\Game\LifePlay-master 5.8\Saved\Logs\portage\FIX1_Build_20261020-190831.log
Code de sortie, durée 0 ; 58 s (Total execution time: 57.83 seconds) 0 ; 61 s (Total execution time: 60.91 seconds)
Ligne Using Visual Studio 14.50.35739 toolchain (…\MSVC\14.50.35717) and Windows 10.0.22621.0 SDK = MSVC épinglé de la copie 5.8 identique
Actions 10 5
Erreurs du compilateur 0 0
Avertissements du compilateur 1 : C4701 Aim, LPCameraModeSubsystem.cpp:2378 (connu) 1 : le même (connu)
Autres lignes (UBT, UHT, liens) 20 : toutes Plugin 'HyperAIStudio' does not list plugin '…' (nombre attendu pour LifePlayEditor, lp-build § 7) 0 (attendu pour la cible jeu)
Unités compilées 3 : ModdingExtraBPLibrary.cpp, Module.LifePlay.cpp, Module.ModdingExtra.cpp 3 : les mêmes ([NoUba])
DLL liées / exécutable 3 DLL : UnrealEditor-LifePlay.dll, UnrealEditor-LifePlayEditorTools.dll, UnrealEditor-ModdingExtra.dll 0 DLL (cible monolithique) ; [4/5] Link [x64] LifePlay.exe
[Adaptive Build] Excluded from … unity file LifePlay: LPKeyboardSubsystem.cpp ; ModdingExtra: ModdingExtraBPLibrary.cpp les mêmes

Avertissement nouveau : aucun. Le module modifié figure dans les unités compilées et dans les DLL liées (lp-build § 9, points 4 et 6).

MD5 des produits (md5sum, après les deux builds) :

Fichier MD5 Taille, heure
Plugins\ModdingExtra\Binaries\Win64\UnrealEditor-ModdingExtra.dll 7e8eeec53c104ffd0ea3673addb64cb6 416 768 o, 19:08:14
Binaries\Win64\LifePlay.exe 811f72f20545613b93eb913f384ca1b2 335 846 400 o, 19:09:31
Binaries\Win64\UnrealEditor-LifePlay.dll e5d2c9c9c27ac70c0be27871eaf224c0 1 992 704 o, 19:08:04
Plugins\ModdingExtra\Source\ModdingExtra\Private\ModdingExtraBPLibrary.cpp 1f93601c71d126f173fe30636aec0660 (source)

Preuve de liaison du nouveau code : recherche UTF-16 des chaînes LPNoSavesRedirect, Saves redirige, FIX1-F6 dans UnrealEditor-ModdingExtra.dll et LifePlay.exe : 3 fois True pour chacun. Inconnu : le comportement à l'exécution (essai E1-E9).

Autres contrôles

  • pwsh -NoProfile -File Tools/PubCheck.ps1 -Fichiers Plugins/ModdingExtra/Source/ModdingExtra/Private/ModdingExtraBPLibrary.cpp -Depot "D:/Game/LifePlay-master 5.8" (stdin /dev/null) -> PubCheck : 0 constat(s), code 0.
  • PubCheck du livrable et des deux résumés de build : pwsh -NoProfile -Command "& './Tools/PubCheck.ps1' -Fichiers '<f1>','<f2>','<f3>' -Depot 'D:/Game/LifePlay-master 5.8'" (stdin /dev/null) -> PubCheck : 0 constat(s), code 0. Piège : la forme pwsh -File … -Fichiers <f1> -Fichiers <f2> de Tools\LISEZMOI.md échoue (paramètre -Fichiers « spécifié plusieurs fois », code 1) ; la liste se passe en tableau, par -Command (comme lp-build § 8 pour UeState).
  • Fin de mission : Tools\UeState.ps1 -> processus=0 editeur=0 compilation=0 shaders=0 jeu=0, code 0 ; jeton UE rendu.
  • Scripts : aucun script écrit. Deux fichiers de comparaison temporaires (lignes + de la SPEC et du diff) sont restés dans le dossier temporaire de la session, hors dépôt.