diff --git a/Docs/Migration/11_Audit_Optimisation.md b/Docs/Migration/11_Audit_Optimisation.md index 775a3f7..daddb46 100644 --- a/Docs/Migration/11_Audit_Optimisation.md +++ b/Docs/Migration/11_Audit_Optimisation.md @@ -468,4 +468,38 @@ de modding. - 3 captures de diagnostic de la racine rangées dans `D:\Game\LifePlay_Backup_5.5\O7\racine`. - **O7b** : carte des dossiers et liste des dossiers à ne pas déplacer dans `08_Guide_Developpeur.md` §2.1. -Aucun asset déplacé, aucun Blueprint modifié : ni build ni test en jeu nécessaires. \ No newline at end of file +Aucun asset déplacé, aucun Blueprint modifié : ni build ni test en jeu nécessaires. + +## 17. Couche de données : stratégie (2026-10-02) + +Proposition utilisateur : convertir les fichiers texte de `Modules` en fichiers JSON groupés par type, avec un calque de +valeurs par défaut, chargés une seule fois, avec un sous-module pour ingérer l'ancien format (mods). + +Retenu : **le texte reste la source** (jeu de base et mods), et **le JSON devient la représentation compilée** chargée en +une fois. Cela évite deux sources de vérité, deux analyseurs à maintenir à l'identique, et une rupture avec le format +connu des moddeurs. + +| Étape | Contenu | Effort | +|---|---|---| +| 1 | mesure : temps de chargement des données et lectures par type | 1 lot | +| 2 | sous-système C++ : `Modules` lu une seule fois et gardé en mémoire ; `LoadTxt` servi depuis la mémoire | 1 lot | +| 3 | cache compilé par type (JSON ou binaire), régénéré quand les sources changent, avec calque de valeurs par défaut | 1 à 2 lots | +| 4 (facultatif) | analyseurs de `GI` réécrits en C++, format par format, vérifiés à l'identique | 1 lot par format | + +Rappels : +- `Modules` pèse 14 Mo : le gain porte sur le nombre de fichiers et le temps, pas sur la taille. +- Les corps de `.lpscene`/`.lpai` sont des scripts : un format JSON ne change rien à leur interprétation. + +### 17.1 Étape 1 : premières mesures (trace O2, build Development) + +| Fonction (`GI`) | Durée | Remarque | +|---|---:|---| +| `PopulateRaws` | **12,6 s par appel**, 3 appels dans la session | lit et analyse tout `Modules` ; **relancé à chaque chargement de partie** | +| `LoadTxt` | 7,4 s pour 2 705 lectures | 2,75 ms par fichier de quelques Ko : coût d'ouverture de fichier | +| `PopulateScenes` | 3,6 s par appel | analyse des en-têtes de scènes | +| `PopulateActions` | 1,9 s par appel | — | +| `PlayScene`, `LoadPreset` en jeu | 0,2 s et 0,3 s au total | négligeable | + +Au menu principal, seuls 11 fichiers sont lus (0,085 s) : la lecture de `Modules` a lieu au lancement d'une partie. +Instrumentation ajoutée à `ModdingExtra::LoadTxt` : bilan par type de fichier à la fermeture du jeu et par la commande +console `ModdingExtra.LoadTxtStats` (build `LifePlay_Build_5.5_DATA1`). \ No newline at end of file diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index ebe8ef4..f3597a0 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -919,3 +919,4 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol | CHG-072 | 2026-10-02 | ERR-038 | `LPRoomTransitionSubsystem` : détection par la seule échelle de la racine de `Room`, délai de réaffichage 10 s → 3 s | premier test en jeu concluant (anciens personnages cachés, compagnon présent, nouveaux personnages apparus), mais 2 faux positifs au relevé : nom de lieu vidé un instant sans changement de pièce ; arrivée au restaurant peuplée 3 s avant la construction de la pièce (personnages apparus 10 s en retard) ; build `LifePlay_Build_5.5_ERR038` reconstruit | `git checkout ERR038-apres -- Source/LifePlay/LPRoomTransitionSubsystem.*` | | CHG-073 | 2026-10-02 | ERR-038 | `Source/LifePlay/LPRoomTransitionSubsystem.h/.cpp` supprimés (copie dans `D:\Game\LifePlay_Backup_5.5\ERR038_retrait`) ; build `LifePlay_Build_5.5_ERR038` reconstruit sans le sous-système (vérifié dans l'exécutable) | second test « plus problématique » : relevé `ERR038_apres2_20261002` = 2 déclenchements, tous deux à tort (clients du lieu d'arrivée cachés 3 s) ; le critère temporel ne distingue pas les personnages créés pendant le trajet puis retirés de ceux qui sont conservés ; retrait proposé et accepté par l'utilisateur ; ERR-038 reste ouvert | `git checkout ERR038-apres -- Source/LifePlay` | | CHG-074 | 2026-10-02 | O7 | O7a : `Content/Saves` (14 125 fichiers, parties de test) → `D:\Game\LifePlay_Backup_5.5\O7\Content_Saves` (vérifié), `Content/Developers` et `Content/Collections` (dossiers vides) retirés, 3 captures de la racine rangées ; O7b : `08_Guide_Developpeur.md` §2.1 (carte de `Content/Heavy`, dossiers à ne pas déplacer) | ménage sans risque et documentation, à la place d'un renommage des dossiers jugé trop risqué (préfixes de chemins en dur, contrat de modding) | déplacer en retour le contenu de `O7\Content_Saves` | +| CHG-075 | 2026-10-02 | DATA1 | `Plugins/ModdingExtra/.../ModdingExtraBPLibrary.cpp` : statistiques de `LoadTxt` par type de fichier (bilan à la fermeture, commande `ModdingExtra.LoadTxtStats`) ; `11_Audit_Optimisation.md` §17 (stratégie de couche de données) | étape 1 de la couche de données : mesurer avant de construire ; `PopulateRaws` = 12,6 s par chargement de partie (trace O2) ; build `LifePlay_Build_5.5_DATA1` | `git checkout DATA1-avant -- Plugins/ModdingExtra` | diff --git a/Plugins/ModdingExtra/Source/ModdingExtra/Private/ModdingExtraBPLibrary.cpp b/Plugins/ModdingExtra/Source/ModdingExtra/Private/ModdingExtraBPLibrary.cpp index 02bdd33..f9732b6 100644 --- a/Plugins/ModdingExtra/Source/ModdingExtra/Private/ModdingExtraBPLibrary.cpp +++ b/Plugins/ModdingExtra/Source/ModdingExtra/Private/ModdingExtraBPLibrary.cpp @@ -5,6 +5,9 @@ #include "AssetRegistry/AssetRegistryModule.h" #include "HAL/FileManager.h" #include "Engine/World.h" +#include "HAL/IConsoleManager.h" +#include "HAL/PlatformTime.h" +#include "Misc/CoreDelegates.h" #include "Modules/ModuleManager.h" #include "Misc/FileHelper.h" #include "Misc/Paths.h" @@ -120,9 +123,61 @@ float UModdingExtraBPLibrary::ModdingExtraSampleFunction(float Param) } +// Portage 5.5, couche de donnees (etape 1, mesure) : statistiques de LoadTxt par type de fichier (appels, fichiers +// distincts, relectures, octets, temps de lecture). Bilan ecrit a la fermeture du jeu et par la commande console +// ModdingExtra.LoadTxtStats. Appels uniquement sur le thread de jeu (Blueprints) : pas de verrou necessaire. +namespace LPLoadTxtStats +{ + struct FEntry { int32 Calls = 0; int32 Rereads = 0; int64 Bytes = 0; double Seconds = 0.0; TSet Files; }; + static TMap ByExt; + static double FirstCall = 0.0, LastCall = 0.0; + + static void Dump(const TCHAR* When) + { + int32 Calls = 0, Files = 0, Rereads = 0; int64 Bytes = 0; double Seconds = 0.0; + TArray Keys; ByExt.GetKeys(Keys); + Keys.Sort([](const FString& A, const FString& B) { return ByExt[A].Seconds > ByExt[B].Seconds; }); + UE_LOG(LogModdingExtra, Display, TEXT("===== LoadTxt : bilan (%s) ====="), When); + for (const FString& Key : Keys) + { + const FEntry& E = ByExt[Key]; + UE_LOG(LogModdingExtra, Display, TEXT(" %-14s %6d appel(s) | %5d fichier(s) | %6d relecture(s) | %8.1f Ko | %7.3f s"), + *Key, E.Calls, E.Files.Num(), E.Rereads, E.Bytes / 1024.0, E.Seconds); + Calls += E.Calls; Files += E.Files.Num(); Rereads += E.Rereads; Bytes += E.Bytes; Seconds += E.Seconds; + } + UE_LOG(LogModdingExtra, Display, TEXT(" %-14s %6d appel(s) | %5d fichier(s) | %6d relecture(s) | %8.1f Ko | %7.3f s (premier appel a %.1f s, dernier a %.1f s)"), + TEXT("TOTAL"), Calls, Files, Rereads, Bytes / 1024.0, Seconds, FirstCall, LastCall); + } + + static FAutoConsoleCommand Command(TEXT("ModdingExtra.LoadTxtStats"), TEXT("Bilan des lectures LoadTxt par type de fichier."), + FConsoleCommandDelegate::CreateLambda([]() { Dump(TEXT("commande console")); })); + + static void Record(const FString& FileName, int32 Len, double Seconds) + { + static bool bExitHooked = false; + if (!bExitHooked) + { + bExitHooked = true; + FCoreDelegates::OnEnginePreExit.AddLambda([]() { Dump(TEXT("fermeture")); }); + } + const double Now = FPlatformTime::Seconds() - GStartTime; + if (FirstCall == 0.0) { FirstCall = Now; } + LastCall = Now; + FString Ext = FPaths::GetExtension(FileName).ToLower(); + if (Ext.IsEmpty()) { Ext = TEXT("(sans)"); } + FEntry& E = ByExt.FindOrAdd(Ext); + ++E.Calls; E.Bytes += Len; E.Seconds += Seconds; + bool bAlready = false; + E.Files.Add(FileName, &bAlready); + if (bAlready) { ++E.Rereads; } + } +} + bool UModdingExtraBPLibrary::LoadTxt(FString FileNameA, FString& SaveTextA) { + const double StartTime = FPlatformTime::Seconds(); const bool bOk = FFileHelper::LoadFileToString(SaveTextA, *(FPaths::ProjectContentDir() + FileNameA)); + LPLoadTxtStats::Record(FileNameA, SaveTextA.Len(), FPlatformTime::Seconds() - StartTime); if (!bOk) {