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
- 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). - 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. - 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 ».
- 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. - Alerte
CopieEtat.ps1 -Lot FIX1(code 1,alertes=GIT) :jeu [ M] Source/LifePlay/LPKeyboardSubsystem.cpp. Fichier modifié avant ce lot (présent dans legit statusde départ, lot KEY2), donc hors périmètre. Il n'a pas été touché. Conséquence sur les binaires : voir Constats. - 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) :
md5sumde 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é), ougit revertdu commit d'IMPL ; recompilation ensuite (SPEC § 3).
4. Ce qui n'a pas été fait
- Aucun autre fichier : ni
.h, niBuild.cs, ni.uplugin, ni Blueprint, ni GI, niLPDataCache.*, niSource/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\Savesni 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/ModdingExtrarend1 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(relecturebFromCache),:357(OnWritten(FileNameB, …)),:307(TryGet(FileNameA, …)),:309(Record(FileNameA, …)) inchangés par rapport à l'instantané. - V4 :
_sessionsans repli (:107-111) ; écriture jamais versProjectContentDir()quand la redirection est active (:107-111renvoieNewPathsibForWrite) ; l'ancien chemin n'est rendu que pour un chemin horsSaves/(:92-95) ou avec-LPNoSavesRedirect(:96-105). - V5 :
git ls-files --eoldu 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 : C4701Aim(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.cppa é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.exeembarqueModdingExtra(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,-ArchiveDirneuf,lp-build§ 4) ; ce n'est pas fait ici. - B3 : Shipping non compilé. Les messages
UE_LOGdisparaissent en Shipping sansbUseLoggingInShipping(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 dansUnrealEditor-ModdingExtra.dlletLifePlay.exe, trois foisTrue). 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\Savesavant et après ; journalLogModdingExtra: 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.savdans<SavedDir>\SaveGames; attendulu dans l'ancien Content/Saves (repli)puisnouvelle racineaprè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 contientFIX1-F6. - E8 :
ModdingExtra.LoadTxtStatsdoit afficherSaves : n lecture(s) nouvelle racine | m lecture(s) repli | k ecriture(s)avec n > 0 et k > 0 ; la ligneCache 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\Savesinchangé ; pas de journal en Shipping. - C1 : comportement à vérifier à la lecture, sans effet fonctionnel : un fichier posé directement dans
Saves/(slot vide) journaliseSaves 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 (variablestatic const) ; le premier appel survient au premierLoadTxt/SaveTxtsur un cheminSaves/, ou auDump. 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.cppA 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 formepwsh -File … -Fichiers <f1> -Fichiers <f2>deTools\LISEZMOI.mdéchoue (paramètre-Fichiers« spécifié plusieurs fois », code 1) ; la liste se passe en tableau, par-Command(commelp-build§ 8 pourUeState). - 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.