Couche de donnees, etape 1 : strategie documentee, mesure de LoadTxt par type de fichier

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
edujiro5972 2026-10-02 11:43:00 +02:00
parent 66d74a9f28
commit 4dfb70ed24
3 changed files with 91 additions and 1 deletions

View file

@ -469,3 +469,37 @@ de modding.
- **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.
## 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`).

View file

@ -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` |

View file

@ -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<FString> Files; };
static TMap<FString, FEntry> 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<FString> 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)
{