From 316592551b5b3239a4ceedf15e961e0cd1db8945 Mon Sep 17 00:00:00 2001 From: edujiro5972 <336072401+edujiro5972@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:50:03 +0200 Subject: [PATCH] SB1 : registre des mods de la sandbox, barre laterale, modes enveloppes en mods (specification, implementation, build, validation ; pin 82dae58) Phase SB1 (CHG-229). Co-Authored-By: Claude Opus 5.5 --- Docs/Migration/21_Sandbox_Addon.md | 7 +- Docs/Migration/Journal_Modifications.md | 1 + Docs/Migration/etat/sandbox/S3/S3-INT.md | 4 +- Docs/Migration/etat/sandbox/SB1/SB1-BUILD.md | 70 ++++++ Docs/Migration/etat/sandbox/SB1/SB1-IMPL.md | 26 ++ Docs/Migration/etat/sandbox/SB1/SB1-SPEC.md | 244 +++++++++++++++++++ Docs/Migration/etat/sandbox/SB1/SB1-VAL.md | 113 +++++++++ 7 files changed, 461 insertions(+), 4 deletions(-) create mode 100644 Docs/Migration/etat/sandbox/SB1/SB1-BUILD.md create mode 100644 Docs/Migration/etat/sandbox/SB1/SB1-IMPL.md create mode 100644 Docs/Migration/etat/sandbox/SB1/SB1-SPEC.md create mode 100644 Docs/Migration/etat/sandbox/SB1/SB1-VAL.md diff --git a/Docs/Migration/21_Sandbox_Addon.md b/Docs/Migration/21_Sandbox_Addon.md index 6517e34..e7935f3 100644 --- a/Docs/Migration/21_Sandbox_Addon.md +++ b/Docs/Migration/21_Sandbox_Addon.md @@ -117,12 +117,12 @@ D:\Game\ - **Liaison** : `LifePlay.uproject` déclare `"AdditionalPluginDirectories": ["../LifePlay-Sandbox/Plugins"]` (chemin relatif au `.uproject`) et active `LPSandbox` dans `Plugins`. UBT et l'éditeur y trouvent `LPSandbox.uplugin` (modules `LPSandbox` et `LPAnimMeasure`) ; les DLL du plugin sont compilées avec le jeu (`Build.bat LifePlayEditor Win64 Development`). - **Pourquoi pas un submodule** : l'addon est un `uproject` mince qui contient le plugin sous `Plugins/LPSandbox/`. Un submodule monté sur `Plugins/LPSandbox` du jeu donnerait `Plugins/LPSandbox/Plugins/LPSandbox/` (plugin imbriqué, non scanné au bon niveau) ; un submodule `External/LifePlay-Sandbox` dupliquerait l'addon à côté du sibling et rendrait ambigu l'exemplaire chargé. Un clone sibling évite les deux problèmes. -- **Pin** : le jeu consomme un commit précis de l'addon, consigné à la main dans `S3-INT.md` (aujourd'hui `3b21fd180c04f0fa49a1b5c9ef1b517ade1cb362`, `lot/SB0-conception`, SB0 conception de la sandbox modulaire ; précédents : `f09c42e5a2bc613cf269cce76c878461ca41c8b7` S7-T2 uplugin, `c1fdd29e2c1036c7d6365ba6389eff5d2f8c8c96` S5-T2 `LPCoupleSim`, `f8102647ccdebad37e4977ccbf2c51e583823a80`, S4-T3). Le pin est mis à jour (hash dans `S3-INT.md` et README de l'addon) à chaque évolution de l'addon que le jeu doit prendre. Reproduire : +- **Pin** : le jeu consomme un commit précis de l'addon, consigné à la main dans `S3-INT.md` (aujourd'hui `82dae58380b76c45f60541fd7cb5a0a409700e10`, `lot/SB1-registre`, SB1 registre de mods et barre latérale ; précédents : `3b21fd180c04f0fa49a1b5c9ef1b517ade1cb362` SB0 conception de la sandbox modulaire, `f09c42e5a2bc613cf269cce76c878461ca41c8b7` S7-T2 uplugin, `c1fdd29e2c1036c7d6365ba6389eff5d2f8c8c96` S5-T2 `LPCoupleSim`, `f8102647ccdebad37e4977ccbf2c51e583823a80`, S4-T3). Le pin est mis à jour (hash dans `S3-INT.md` et README de l'addon) à chaque évolution de l'addon que le jeu doit prendre. Reproduire : ```powershell cd D:\Game git clone https://github.com/edujiro5972/LifePlay-Sandbox.git -git -C LifePlay-Sandbox checkout 3b21fd180c04f0fa49a1b5c9ef1b517ade1cb362 +git -C LifePlay-Sandbox checkout 82dae58380b76c45f60541fd7cb5a0a409700e10 # le jeu doit être cloné à côté : D:\Game\LifePlay-master 5.5 ``` @@ -170,6 +170,9 @@ Le lot S5 ferme la **dérive de fork** entre la sandbox (`LPSandbox/LPCoupleSim. - [ ] Parcours Show complet en jeu et décision d'alignement du plancher (-90 / -88). - [x] Commit addon `f09c42e` poussé vers `origin/main` (constaté en S8 : `origin/main` = `dae6206`, qui le contient). - [ ] Migration commitée **côté jeu** (`LifePlay.uproject`, `Config/LPSandbox.ini`, `Build.cs`, `LPShowCoupleSubsystem.*`, docs `21` / `22`, `etat/sandbox/`) : **non fait** (`22` § 6). +- [x] **SB0** : conception de la sandbox modulaire (addon, pin `3b21fd1`, tag `lot-SB0-valide`). +- [x] **SB1** : registre de mods, barre latérale, 7 descripteurs `.lpmod`, modes Pair / Interaction / Mesure enveloppés en `lps_couple` / `lps_interaction` / `lps_asset_integration` (`etat/sandbox/SB1/SB1-VAL.md` PASS, contrôle visuel utilisateur 2026-10-07 ; tag `lot-SB1-valide`). +- [ ] Reports SB1 (registre unique, lot propriétaire **SB2**) : chaque ouverture de mod réécrit `Open` dans `Saved/sandbox_mods.ini` (une tournée automatique laisse donc `Open=lps_asset_integration`) ; images de la tournée Mesure : empreintes différentes d'une exécution à l'autre (JSON et CSV identiques), la non-régression stricte de SB2-T5 doit comparer les JSON/CSV, et les images visuellement ou par écart de pixels. ## Lots (S0 … S8) | Lot | Objet (une ligne) | diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index 8c00ee5..87fa2b3 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -1131,6 +1131,7 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol | CHG-155 | 2026-10-05 | ShowCouple | Un lit est reconnu par son nom. Il ne bloque un déplacement que vers le bas, pour se poser ; le retour horizontal n'est plus arrêté et le contact ne renvoie plus la boîte | après avoir quitté le lit, la boîte ne pouvait plus y revenir | revenir au test `DoesPackageExist` et au filtre du flanc seul | | CHG-156 | 2026-10-05 | Clavier | `LPKeyboardSubsystem` : bouton « Clavier » ajouté à l'exécution dans `MainMenu.SettingsScrollBox` ; panneau pour changer, ajouter ou retirer une touche (actions et axes clavier). Écrit dans `Saved/Config/Windows`, pas dans `GameSettings.sav` | pouvoir réaffecter les touches, dont Q, sans modifier les Blueprints | retirer `LPKeyboardSubsystem.h/.cpp` et la dépendance `UMG` | | CHG-158 | 2026-10-05 | Réglages | `LPSettingsSubsystem` : onglets Audio, Video, Lewd, General, Keyboard, Misc au-dessus de `SettingsScrollBox`. Les lignes du Blueprint restent en place et sont montrées selon leur texte ; le reste va dans Misc. Keyboard affiche la liste des touches | la liste unique ne montrait pas le clavier | retirer `LPSettingsSubsystem.h/.cpp` | +| CHG-229 | 2026-10-07 | SB1 | addon `D:\Game\LifePlay-Sandbox` : `Plugins/LPSandbox/Source/LPSandbox/*` (registre `FLPSandboxModRegistry`, journal `LogLPSandboxMods`, `Saved/sandbox_mods.ini`, barre latérale, enveloppes), `Plugins/LPSandbox/Modules/` (7 descripteurs `.lpmod`), `README.md` (commits `82dae58380b76c45f60541fd7cb5a0a409700e10` code + commit pin, tag `lot-SB1-valide`, branche `lot/SB1-registre`) ; jeu : `Docs/Migration/etat/sandbox/SB1/SB1-VAL.md`, `Docs/Migration/etat/sandbox/S3/S3-INT.md`, `Docs/Migration/21_Sandbox_Addon.md` (pin, SB1, reports) | lot SB1 : registre de mods, barre latérale, 7 descripteurs ; modes Pair / Interaction / Mesure enveloppés en `lps_couple` / `lps_interaction` / `lps_asset_integration` ; validation E1 à E5 PASS + contrôle visuel utilisateur (a, b, c, barre latérale, images E5 : pas de défauts constatés) | pin `3b21fd1` → `82dae58` (hash complet `82dae58380b76c45f60541fd7cb5a0a409700e10`) | `.\Tools\Restore.ps1 -Lot SB1` (docs du jeu), `.\Tools\Restore.ps1 -Lot SB1-addon -ProjectRoot D:\Game\LifePlay-Sandbox` et `git -C D:\Game\LifePlay-Sandbox checkout lot-SB1-avant` ; rien n'est commité dans le dépôt du jeu ni poussé | | CHG-228 | 2026-10-07 | SB0 | addon `D:\Game\LifePlay-Sandbox` : `Docs/SB0-conception.md`, `Docs/SB-plan.md`, `README.md` (commits C1 `3b21fd1`, C2 `12d6198`, tags `lot-SB0-avant` / `lot-SB0-valide`, branche `lot/SB0-conception`) ; jeu : `Docs/Migration/etat/sandbox/S3/S3-INT.md`, `Docs/Migration/21_Sandbox_Addon.md` (pin) | conception de la sandbox modulaire demandée par l'utilisateur (modes Pair/Interaction/Mesure → mods `lps_couple` / `lps_interaction` / `lps_asset_integration`, modules système et bibliothèques `lps_*`, `.lpmod` étendu rétrocompatible, interface `ILPSandboxMod`, registre, barre latérale ; plan SB0–SB4 au format du cahier S0–S8) | pin `a3b1b89` → `3b21fd1` (hash complet `3b21fd180c04f0fa49a1b5c9ef1b517ade1cb362`) | `.\Tools\Restore.ps1 -Lot SB0` (jeu) et `.\Tools\Restore.ps1 -Lot SB0-addon -ProjectRoot D:\Game\LifePlay-Sandbox` ; commits addon conservés dans l'historique (branche) ; rien n'est commité dans le dépôt du jeu ni poussé | | CHG-227 | 2026-10-07 | DOC-MOD-3 | `Docs/Migration/23_Cycle_De_Vie_Mod.md` (§ 2.6 nouveau, 7 schémas Mermaid) | schémas du cycle de vie d'un module demandés par l'utilisateur : états (installé, monté, listé, inscrit, actif / inactif, retiré, déchargé), séquence de démarrage (moteur, registre, `GI`, sous-systèmes C++), arbres logiques de `GI.LoadSettings(LoadLastSave)`, `GI.PopulateRaws()`, du gestionnaire (`CreateModManagerWidget(GameStart)`, `ToggleMod(Mod, IsEnabled)`, `SortLoadOrder(ForUp, ModuleIndex)`, `CloseModManager(GameStart)`), appel d'une animation de mod, désactivation et déchargement ; graphes reconstitués depuis le relevé `LPDumpFunction -BP=GI -Fn=EventGraph -Pins -Exec` et l'ordre d'initialisation de `UGameInstance` (moteur) ; 7 schémas rendus sans erreur par Mermaid 11 | aucun changement de code ni de configuration | `.\Tools\Restore.ps1 -Lot DOC-MOD-3` | | CHG-226 | 2026-10-07 | DOC-MOD-2 | `Docs/Migration/23_Cycle_De_Vie_Mod.md` (§ 2.4, 3.1, 3.2, 3.3 et 3.4 nouveaux, 5, 6) ; `Docs/Migration/Journal_Modifications.md` | complément de la documentation du cycle de vie d'un mod, constats vérifiés dans les Blueprints (`GI`, `UI/ModuleTick`, relevés `LPDumpFunction`) et le moteur UE 5.5 : l'ordre de chargement existe (`GI.SortLoadOrder`, `PopulateRaws` : le premier fichier lu par nom gagne, le module le plus bas l'emporte, fichiers texte seulement) ; `vin_Base` verrouillé en dur dans `UI/ModuleTick` ; dépendance `.uplugin` obligatoire absente = le jeu ne démarre pas, donc aucune dépendance entre mods dans le `.uplugin` ; doublons entre `.uplugin`, `.lpmod` et `pack.json`, divergences dues à `New-IfMissing` de `DeliverAnims.ps1` ; évolutions E12 corrigée, E13 à E15 ajoutées | aucun changement de code ni de configuration | `.\Tools\Restore.ps1 -Lot DOC-MOD-2` | diff --git a/Docs/Migration/etat/sandbox/S3/S3-INT.md b/Docs/Migration/etat/sandbox/S3/S3-INT.md index 4d2588e..e30c72d 100644 --- a/Docs/Migration/etat/sandbox/S3/S3-INT.md +++ b/Docs/Migration/etat/sandbox/S3/S3-INT.md @@ -8,7 +8,7 @@ Date : 2026-10-06 — Role A-INT |---|---| | Repo addon | https://github.com/edujiro5972/LifePlay-Sandbox.git | | Branche | `main` | -| **Commit pin** | `3b21fd180c04f0fa49a1b5c9ef1b517ade1cb362` (`3b21fd1`, SB0, **local, non pousse** au 2026-10-07) | +| **Commit pin** | `82dae58380b76c45f60541fd7cb5a0a409700e10` (`82dae58`, SB1, **local, non pousse** au 2026-10-07) | | Contenu du commit | S5-T2 : `LPCoupleSim` (géométrie du couple partagée jeu/addon) ; inclut S4-T3 (retrait GM Interaction legacy), S4-T2 (découpe `LPPairSandbox.cpp`) et S3-T4 (`StartupModule` → `SetUALRoot`) | | Pins précédents | `f09c42e5a2bc613cf269cce76c878461ca41c8b7` (S7-T2), `c1fdd29e2c1036c7d6365ba6389eff5d2f8c8c96` (S5-T2), `f8102647ccdebad37e4977ccbf2c51e583823a80` (S4-T3), `4de1f0b6764ed3bc575bb687630d3f070e1be221` (S3-T4) | @@ -46,7 +46,7 @@ sur la copie effectivement chargee. Pin manuel a la place (ci-dessous). ```powershell cd D:\Game git clone https://github.com/edujiro5972/LifePlay-Sandbox.git -git -C LifePlay-Sandbox checkout 3b21fd180c04f0fa49a1b5c9ef1b517ade1cb362 +git -C LifePlay-Sandbox checkout 82dae58380b76c45f60541fd7cb5a0a409700e10 # Le jeu doit etre clone a cote : D:\Game\LifePlay-master 5.5 ``` diff --git a/Docs/Migration/etat/sandbox/SB1/SB1-BUILD.md b/Docs/Migration/etat/sandbox/SB1/SB1-BUILD.md new file mode 100644 index 0000000..f22c4e0 --- /dev/null +++ b/Docs/Migration/etat/sandbox/SB1/SB1-BUILD.md @@ -0,0 +1,70 @@ +# Fiche SB1-T4 — A-BUILD + +**Modèle prévu:** haiku (M0) +**Modèle utilisé:** haiku +**Agent:** A-BUILD + +--- + +## Commande de compilation + +```powershell +Set-Location 'D:\Game\LifePlay-master 5.5' +& 'D:\Game\Launcher\Engine\UE_5.5\Engine\Build\BatchFiles\Build.bat' LifePlayEditor Win64 Development -Project='D:\Game\LifePlay-master 5.5\LifePlay.uproject' -WaitMutex *> 'D:\Game\LifePlay-master 5.5\Saved\Logs\portage\SB1_build.log' +``` + +--- + +## Résultats + +- **Code de sortie:** 0 (succès) +- **Durée totale:** 42.95 secondes +- **Nombre d'actions compilées:** 4 + - [1/4] Compile [x64] Module.LPSandbox.cpp + - [2/4] Link [x64] UnrealEditor-LPSandbox.lib + - [3/4] Link [x64] UnrealEditor-LPSandbox.dll + - [4/4] WriteMetadata LifePlayEditor.target + +--- + +## Erreurs et avertissements + +**Erreurs:** Aucune +**Avertissements C++:** Aucun + +--- + +## Vérification des DLL + +### Binaires du jeu LifePlay + +``` +UnrealEditor-LPSandbox.dll + Nom: UnrealEditor-LPSandbox.dll + Date: 07/10/2026 11:33:43 + Chemin: D:\Game\LifePlay-master 5.5\Binaries\Win64\UnrealEditor-LPSandbox.dll +``` + +### Plugin LPSandbox externe + +Dossier `D:\Game\LifePlay-Sandbox\Plugins\LPSandbox\Binaries` : **n'existe pas** + +--- + +## Journal de compilation + +**Chemin:** `D:\Game\LifePlay-master 5.5\Saved\Logs\portage\SB1_build.log` + +### Extrait clé + +``` +Total time in Unreal Build Accelerator local executor: 21.57 seconds +Total execution time: 42.95 seconds +``` + +--- + +## Conclusion + +Compilation **réussie**. Le plugin LPSandbox a été compilé et linkée avec succès. La DLL `UnrealEditor-LPSandbox.dll` est présente dans `D:\Game\LifePlay-master 5.5\Binaries\Win64\` avec un horodatage récent (11:33:43). + diff --git a/Docs/Migration/etat/sandbox/SB1/SB1-IMPL.md b/Docs/Migration/etat/sandbox/SB1/SB1-IMPL.md new file mode 100644 index 0000000..9f47817 --- /dev/null +++ b/Docs/Migration/etat/sandbox/SB1/SB1-IMPL.md @@ -0,0 +1,26 @@ +# SB1-IMPL — Registre, interface, enveloppes (SB1-T2) + +- **Fiche** : SB1-T2 — **Agent** : A-IMPL-H — **Modèle** : prévu opus (M3) | utilisé opus (session principale) +- **Dépôt** : addon, branche `lot/SB1-registre` (tag d'entrée `lot-SB1-avant` = `12d6198`) +- **Instantané** : `LifePlay_Backup_5.5\SB1-addon` (`LPSandbox.cpp`, `LPSandbox.Build.cs`, `LPPairSandbox.h/.cpp`, `LPPairSandboxMenu.h`) + +## Fichiers + +| Fichier | Nature | +|---|---| +| `LPSandboxMod.h` | nouveau : types (§ 2 de la SPEC) | +| `LPSandboxModRegistry.h/.cpp` | nouveau : registre (§ 3-4), journal `LogLPSandboxMods` | +| `LPSandboxModWrappers.h/.cpp` | nouveau : enveloppes `lps_couple` (Pair), `lps_interaction` (Interaction), `lps_asset_integration` (Mesure) | +| `LPSandboxSidebar.h` | nouveau : déclaration du widget (implémentation : SB1-T3) | +| `LPSandbox.cpp` | `StartupModule` : `RegisterAll()` puis `Scan()` | +| `LPSandbox.Build.cs` | `Projects` en dépendance privée | +| `LPPairSandbox.h/.cpp` | `OpenModeLabel`, `OpenNeutral`, `OpenInitialMod`, `MakeModContext` ; barre latérale à la place de la liste des modes ; reste du panneau dans `BodyRow` (repliable) ; `EndPlay` → `Registry.EndSession()` | +| `LPPairSandboxMenu.h` | `BodyRow` | + +## Écarts à la SPEC (décisions de l'implémenteur) + +1. `EndSession()` ajouté au registre : à la fin de partie, le mod ouvert est oublié sans appel (la scène est détruite) ; évite un pointeur de personnage pendant entre deux sessions. +2. Scène neutre : `CloseOpen` du registre appelle `Player->OpenNeutral()` (Pair sans liste de clips) ; `OpenModeLabel` remet la liste de clips visible au retour sur Pair (`SelectMode` ne fait rien si le mode est déjà Pair). +3. `Saved/sandbox_mode.txt` : le fichier est remis à `Pair` après lecture (comportement existant) ; `Pair` est désormais traité comme « pas de déclencheur », sinon il masquerait à chaque lancement le mod ouvert mémorisé. Avant SB1, un fichier `Pair` masquait aussi `[Sandbox.Mode]` ; après, `[Sandbox.Mode]` n'est consulté que s'il n'y a ni déclencheur ni mod mémorisé. +4. Doublons : contrôlés par source (sandbox / jeu) ; un module du jeu peut porter le même identifiant qu'un module de la sandbox sans refus. +5. Le registre cherche un prérequis dans la sandbox puis dans le jeu (un module du jeu présent satisfait un prérequis). diff --git a/Docs/Migration/etat/sandbox/SB1/SB1-SPEC.md b/Docs/Migration/etat/sandbox/SB1/SB1-SPEC.md new file mode 100644 index 0000000..96f32c4 --- /dev/null +++ b/Docs/Migration/etat/sandbox/SB1/SB1-SPEC.md @@ -0,0 +1,244 @@ +# SB1-SPEC — Registre de mods et barre latérale (modes enveloppés) + +- **Fiche** : SB1-T1 — **Agent** : A-ARCH — **Modèle** : prévu opus (M4) | utilisé opus +- **Dépend de** : SB0 (`D:\Game\LifePlay-Sandbox\Docs\SB0-conception.md`, commit `3b21fd1`, tag `lot-SB0-valide`) +- **Dépôt** : addon `D:\Game\LifePlay-Sandbox`, branche `lot/SB1-registre` créée depuis `lot-SB0-valide`, tag `lot-SB1-avant` +- **Principe SB1** : aucun code de mode n'est déplacé. Les trois modes actuels deviennent trois mods **logiques** + (descripteur + enveloppe qui appelle `SelectMode` existant). La liste déroulante des modes est remplacée par la + barre latérale. Comportement de chaque mode strictement inchangé. + +## 1. Fichiers + +| Fichier (sous `Plugins/LPSandbox/Source/LPSandbox/`) | Fiche | Contenu | +|---|---|---| +| `LPSandboxMod.h` (nouveau) | T2 | descripteur, états, contexte, interface `ILPSandboxMod` | +| `LPSandboxModRegistry.h/.cpp` (nouveaux) | T2 | registre (§ 3), persistance (§ 4), journal | +| `LPSandboxModWrappers.cpp` (nouveau) | T2 | 3 enveloppes + enregistrement (§ 5) | +| `LPSandbox.cpp` | T2 | `StartupModule` : enregistrement des fabriques, `Scan()` | +| `LPPairSandbox.h/.cpp` | T2 | `OpenModeLabel(const FString&)` public ; choix du mod initial (§ 6) ; emplacement de la barre (§ 7) | +| `LPSandbox.Build.cs` | T2 | `"Projects"` en dépendance privée (`IPluginManager`) | +| `LPSandboxSidebar.h/.cpp` (nouveaux) | T3 | widget Slate `SLPSandboxSidebar` (§ 7) | +| `Plugins/LPSandbox/Modules/**/.lpmod` (nouveaux) | T3 | 7 descripteurs (§ 8) | + +## 2. Types (`LPSandboxMod.h`) + +```cpp +#pragma once +#include "CoreMinimal.h" + +class SWidget; +class ALPPairSandboxCharacter; +class ALPPairSandboxStage; + +enum class ELPSandboxModKind : uint8 { System, Library, Mod, Data }; // Data = .lpmod du jeu, lecture seule +enum class ELPSandboxModSource : uint8 { Sandbox, Game }; +enum class ELPSandboxModState : uint8 +{ + Ready, // utilisable (mod activé, prérequis satisfaits) + Disabled, // case décochée + Missing, // prérequis absent ou désactivé (Reason renseignée) + Refused, // règle violée : doublon, mod requis par un mod, cycle (Reason renseignée) + NoFactory, // descripteur de la sandbox sans code enregistré + DataOnly // descripteur du jeu +}; + +struct FLPSandboxModDescriptor +{ + FString Id; // MODULE_UNIQUEID + FString Name; // MODULE_NAME + FString Author; // MODULE_AUTHOR + FString Description, Version, License, Link, AuthorLink; + TArray Requirements; // MODULE_REQUIREMENTS, séparés par ',' ; espaces retirés ; vides ignorés + ELPSandboxModKind Kind = ELPSandboxModKind::Mod; + ELPSandboxModSource Source = ELPSandboxModSource::Sandbox; + bool bSystem = false, bHidden = false, bLocked = false, bDefaultEnabled = false; + int32 Index = -1; // index hexadécimal du dossier (0001_…), -1 sans index + FString Folder; // chemin complet du dossier + FString File; // chemin complet du .lpmod + TArray Warnings; // lecture : clé manquante, UNIQUEID ≠ nom du fichier, 3 premières lignes dans le désordre… + TMap Keys; // toutes les clés lues (majuscules) +}; + +struct FLPSandboxContext +{ + ALPPairSandboxCharacter* Player = nullptr; // SB1 : les enveloppes passent par le personnage joueur + ALPPairSandboxStage* Stage = nullptr; +}; + +class ILPSandboxMod +{ +public: + virtual ~ILPSandboxMod() = default; + virtual FString GetId() const = 0; + virtual void Activate(const FLPSandboxContext& Context) = 0; // le mod prend la scène + virtual void Deactivate(const FLPSandboxContext& Context) = 0; // il la rend (SB1 : rien, SelectMode gère la transition) + virtual TSharedPtr BuildPanel() { return nullptr; } // SB1 : nul (les rangées actuelles du menu restent) + virtual void Tick(float DeltaSeconds) {} +}; +``` + +## 3. Registre (`FLPSandboxModRegistry`, singleton `Get()`) + +### 3.1 API (figée : la barre latérale de T3 n'utilise que celle-ci) + +```cpp +struct FLPSandboxModEntry +{ + FLPSandboxModDescriptor Desc; + ELPSandboxModState State = ELPSandboxModState::Disabled; + FString Reason; // texte affichable (infobulle ⚠) + bool bEnabled = false; + bool bOpen = false; +}; + +class LPSANDBOX_API FLPSandboxModRegistry +{ +public: + static FLPSandboxModRegistry& Get(); + void RegisterFactory(const FString& Id, TFunction()> Factory); + void Scan(); // découverte + lecture + contrôles + état persisté (§ 3.2-3.4) + const TArray& GetEntries() const; // ordre d'affichage : système/bibliothèques, mods, jeu + const FLPSandboxModEntry* Find(const FString& Id) const; + bool SetEnabled(const FString& Id, bool bEnabled); // refus si verrouillé ou Data ; recalcul des états ; Save() + bool Move(const FString& Id, int32 Delta); // mods seulement (-1 monte, +1 descend) ; Save() + bool Open(const FString& Id, const FLPSandboxContext& Context); // Ready + activé exigé ; Deactivate du mod ouvert, Activate du nouveau ; Save() + FString GetOpenId() const; + FString IdForModeLabel(const FString& Label) const; // "Pair"→lps_couple, "Interaction"→lps_interaction, "Mesure"→lps_asset_integration + FSimpleMulticastDelegate OnChanged; // diffusé après tout changement +}; +``` + +### 3.2 Découverte + +- Sandbox : `IPluginManager::Get().FindPlugin(TEXT("LPSandbox"))->GetBaseDir()` + `/Modules/System/*/` et + `/Modules/Mods/*/` ; dans chaque dossier, le premier `*.lpmod`. +- Jeu (lecture seule) : `FPaths::ProjectContentDir() / TEXT("Modules")` ; `/.lpmod` (même règle + que `ULPModInfoSubsystem` du jeu). Source = Game, Kind = Data, état DataOnly. Absent (projet `LPSandbox.uproject` + seul) : section Jeu vide, sans erreur. +- Index : si le nom du dossier commence par 4 chiffres hexadécimaux suivis de `_`, `Index` = valeur ; sinon -1. + +### 3.3 Lecture d'un `.lpmod` + +- Ligne `CLÉ: valeur` : clé = texte avant le premier `:`, sans espaces, mise en majuscules ; valeur coupée aux + extrémités ; lignes vides et commençant par `//` ignorées ; première occurrence retenue. +- Clés lues : `MODULE_UNIQUEID`, `MODULE_NAME`, `MODULE_AUTHOR`, `MODULE_DESCRIPTION`, `MODULE_VERSION`, + `MODULE_LICENSE`, `MODULE_LINK`, `MODULE_AUTHOR_LINK`, `MODULE_REQUIREMENTS`, `MODULE_SYSTEM`, `MODULE_HIDDEN`, + `MODULE_LOCKED`, `MODULE_KIND`, `MODULE_DEFAULT_ENABLED` (booléens : `true` insensible à la casse, sinon faux). +- Défauts : `Kind` = `system` si `MODULE_SYSTEM: true`, `library` si l'identifiant commence par `lps_lib_`, sinon `mod` ; + `MODULE_KIND` explicite prime. `bHidden` et `bLocked` valent `bSystem || Kind==Library` si la clé est absente. + `bDefaultEnabled` vaut vrai pour système/bibliothèque, faux pour un mod si la clé est absente. +- Avertissements (sans refus) : `MODULE_UNIQUEID` absent (identifiant = nom du fichier) ; identifiant ≠ nom du fichier ; + les 3 premières lignes utiles ne sont pas, dans l'ordre, `MODULE_UNIQUEID`, `MODULE_NAME`, `MODULE_AUTHOR` + (rétrocompatibilité avec la lecture par position de `GI`). + +### 3.4 Contrôles et états (dans cet ordre, recalculés après chaque changement) + +1. Identifiant en double (sandbox) : le second est `Refused` (« identifiant déjà utilisé par »). +2. Kind Mod qui requiert un module de Kind Mod : `Refused` (« un mod ne peut pas requérir un autre mod () : + partager le composant dans une bibliothèque »). +3. Cycle dans les prérequis : tous les modules du cycle `Refused` (« cycle de prérequis : a → b → a »). +4. Prérequis introuvable : `Missing` (« prérequis absent : ») ; prérequis présent mais non `Ready` ou désactivé : + `Missing` (« prérequis désactivé : »). Un prérequis peut être un module du jeu (Data) : présent = satisfait. +5. Pas de fabrique (descripteur sandbox sans code) : `NoFactory` pour un mod ; une bibliothèque ou un système sans + fabrique est `Ready` (SB1 : services non encore extraits). +6. Sinon : `Ready` si activé, `Disabled` sinon. Système et bibliothèques sont toujours activés. +7. Si le mod ouvert n'est plus `Ready` : `Deactivate`, plus aucun mod ouvert (journal). + +### 3.5 Ordre de `GetEntries()` + +Système et bibliothèques par `Index` croissant (sans index : après, par identifiant) ; puis mods dans l'ordre persisté +(nouveaux en fin, par identifiant) ; puis modules du jeu par identifiant. + +### 3.6 Journal `LogLPSandboxMods` (catégorie déclarée dans `LPSandboxModRegistry.h`, `LPSANDBOX_API`) + +- `Display` au `Scan` : `Modules : sandbox, jeu ; ordre : , , …` +- `Display` par module : ` [] <État> ` ; `Warning` pour chaque avertissement de lecture et chaque refus. +- `Display` à chaque `SetEnabled`, `Move`, `Open` : `Ouvert : ` (preuve de chargement du nouveau code). + +## 4. Persistance : `Saved/sandbox_mods.ini` (projet hôte) + +```ini +[Mods] +Order=lps_couple,lps_interaction,lps_asset_integration +Open=lps_couple +[Enabled] +lps_couple=true +lps_interaction=true +lps_asset_integration=true +``` + +Lecture au `Scan` (`FConfigFile::Read`, comme `FLPSandboxConfig::LoadFile`), écriture après chaque changement +(`FConfigFile::Write`). Fichier absent : ordre par identifiant, cases = `bDefaultEnabled`, aucun mod ouvert mémorisé. + +## 5. Enveloppes (`LPSandboxModWrappers.cpp`) + +Trois classes `FLPSandboxModeWrapper : ILPSandboxMod` paramétrées par (Id, ModeLabel) : + +| Id | ModeLabel | +|---|---| +| `lps_couple` | `Pair` | +| `lps_interaction` | `Interaction` | +| `lps_asset_integration` | `Mesure` | + +- `Activate(Context)` : `Context.Player->OpenModeLabel(ModeLabel)`. +- `Deactivate` : rien (la transition, y compris `EndMeasure`, `SetAutoRun(false)`, `SetFreeView(false)` et la + restauration du bras caméra, est déjà faite par `SelectMode` quand le mode suivant s'ouvre). +- Fermeture sans mod suivant (mod ouvert désactivé) : `OpenModeLabel("Pair")` puis rangée d'animations masquée + (`Menu->AnimRow` réduit) : scène neutre. +- Enregistrement dans `FLPSandboxModule::StartupModule` : `RegisterFactory` ×3 puis `Scan()`. + +## 6. Personnage (`ALPPairSandboxCharacter`) + +- Ajouter `void OpenModeLabel(const FString& Label);` public : cherche `Label` dans `Menu->Modes` et appelle + `SelectMode` (privé, inchangé) ; journalise si introuvable. +- Le menu garde la liste interne `Menu->Modes` (Pair, Interaction, Mesure) mais **n'affiche plus** `ModeCombo`. +- Mod initial, à la fin de `ShowAnimMenu` (remplace le bloc actuel `sandbox_mode.txt` / `[Sandbox.Mode]`, même priorité + et même effacement de `sandbox_mode.txt`) : + 1. `Saved/sandbox_mode.txt` présent : `Registry.Open(IdForModeLabel(contenu))` (pilote `AnimPipeline` : `Mesure`) ; + 2. sinon `Open` persisté s'il est `Ready` ; + 3. sinon `[Sandbox.Mode]` de `LPSandbox.ini` (défaut `Pair`) via `IdForModeLabel` ; + 4. si le mod visé n'est pas `Ready` : premier mod `Ready` dans l'ordre ; aucun : scène neutre (§ 5). +- `ModeLabel` reste la vérité interne du mode ; le registre ne le modifie que par `OpenModeLabel`. + +## 7. Barre latérale (`SLPSandboxSidebar`, T3) + +- Remplace, dans `ShowAnimMenu`, le slot de `ModeCombo` (premier slot du `SVerticalBox`) ; le reste du panneau + (Mesure, MC, PNJ, animation, vue, filtres, aide) reste en dessous, inchangé. +- `SLATE_BEGIN_ARGS` : `_OnOpen` (`TFunction`, le personnage appelle `Registry.Open(Id, Context)`). +- S'abonne à `Registry.OnChanged` et reconstruit ses lignes (désabonnement au destructeur). +- Section **Mods** (Kind Mod, non masqués) — une ligne : `SCheckBox` (désactivée si verrouillé) → `SetEnabled` ; + nom (`Name`) ; boutons ▲ ▼ → `Move(Id, -1/+1)` ; bouton ▶ (activé si `Ready`, libellé « ouvert » si `bOpen`) → + `OnOpen(Id)` ; si `Missing`/`Refused`/`NoFactory` : « ⚠ » avec infobulle `Reason`. Ligne du mod ouvert surlignée. +- Case **Afficher les modules masqués** (non persistée). +- Section **Système** (visible si la case est cochée) : `Index` en hexadécimal, `Id`, `Kind`, `Version`, état ; lecture seule. +- Section **Jeu (lecture)** (repliée par défaut, en-tête « Jeu : modules ») : `Id`, `Name`, `Author`, + `Requirements`, avertissements de lecture en orange. +- Bouton **◀ / ▶** en tête du panneau : replie / déplie tout le panneau du menu (`Menu->Widget` réduit à la barre de titre). +- Style : polices et couleurs du menu actuel (`FCoreStyle::GetDefaultFontStyle("Regular", 14)` / 11 pour le détail), + largeur 300 comme les listes existantes. + +## 8. Descripteurs (T3) + +Sous `Plugins/LPSandbox/Modules/` ; trois premières lignes dans l'ordre `MODULE_UNIQUEID`, `MODULE_NAME`, `MODULE_AUTHOR`. + +| Dossier | Id | Nom | Kind / clés | Prérequis | +|---|---|---|---|---| +| `System/0001_lps_system_core` | `lps_system_core` | Socle sandbox | `MODULE_SYSTEM: true` | — | +| `System/0002_lps_system_animmeasure` | `lps_system_animmeasure` | Chaîne de mesure (LPAnimMeasure) | `MODULE_SYSTEM: true` | `lps_system_core` | +| `System/0010_lps_lib_scene` | `lps_lib_scene` | Bibliothèque scène | (library par préfixe) | `lps_system_core` | +| `System/0011_lps_lib_actors` | `lps_lib_actors` | Bibliothèque personnages | (library) | `lps_system_core, lps_system_animmeasure` | +| `Mods/lps_couple` | `lps_couple` | Couple | `MODULE_DEFAULT_ENABLED: true` | `lps_system_core, lps_lib_scene, lps_lib_actors, lps_system_animmeasure` | +| `Mods/lps_interaction` | `lps_interaction` | Interaction | `MODULE_DEFAULT_ENABLED: true` | idem | +| `Mods/lps_asset_integration` | `lps_asset_integration` | Asset integration | `MODULE_DEFAULT_ENABLED: true` | `lps_system_core, lps_system_animmeasure` | + +`MODULE_AUTHOR: LifePlay Revival`, `MODULE_VERSION: 0.1`, `MODULE_DESCRIPTION` d'une ligne. + +## 9. Critères (repris par SB1-T5) + +(a) chaque mod s'ouvre et se comporte comme l'ancien mode ; (b) couple → interaction → asset integration → couple ; +(c) mod décoché non ouvrable, et décocher le mod ouvert ramène la scène neutre ; (d) `Mods/lps_test_bad` (créé +pour l'essai puis retiré) requérant `lps_couple` → `Refused` avec raison ; (e) prérequis absent (renommer +temporairement `lps_lib_scene`) → `Missing` sur couple et interaction ; (f) ordre, cases et mod ouvert persistés après +relance ; (g) section Jeu : `.lpmod` du jeu lus, avertissements éventuels listés ; (h) pilote : `Saved/sandbox_mode.txt` += `Mesure` + `Saved/sandbox_tour.txt` = `vignettes planches quitter` → tournée identique à AP7 (mesures, vignettes, +planches) ; (i) journal `LogLPSandboxMods` : 7 modules sandbox, ordre attendu, 0 refus hors essais. diff --git a/Docs/Migration/etat/sandbox/SB1/SB1-VAL.md b/Docs/Migration/etat/sandbox/SB1/SB1-VAL.md new file mode 100644 index 0000000..35e599a --- /dev/null +++ b/Docs/Migration/etat/sandbox/SB1/SB1-VAL.md @@ -0,0 +1,113 @@ +# SB1-VAL — Validation automatique du registre de mods (SB1-T5, partie automatique) + +**Fiche SB1-T5 — A-VAL — Modèle prévu sonnet | utilisé sonnet** + +- Date : 2026-10-07. Build testé : celui de SB1-T4 (compilation code 0). +- Journaux copiés : `D:\Game\LifePlay-master 5.5\Saved\Logs\portage\SB1_VAL_E1.log`, `_E2`, `_E3`, `_E4`, `_E4b`, `_E5`. +- Procédure : lancement par `LifePlay_pair_sandbox.bat`, attente des lignes `LogLPSandboxMods` + `Ouvert :`, copie du journal, arrêt du seul `UnrealEditor` lancé (E5 : le pilote lance et ferme lui-même). + +## Tableau des essais + +| Essai | Attendu | Observé | Verdict | +|---|---|---|---| +| E1 démarrage (g, i) | 7 modules sandbox ; ordre système puis mods ; aucun refus ni prérequis manquant ; `.lpmod` du jeu lus ; mod ouvert ; `sandbox_mods.ini` créé | `Modules : 7 sandbox, 16 jeu ; ordre : lps_system_core, lps_system_animmeasure, lps_lib_scene, lps_lib_actors, lps_asset_integration, lps_couple, lps_interaction` ; les 7 en `pret` ; aucun `Warning` de lecture (`LogLifePlay: Modules : 16 .lpmod lu(s), 0 avec MODULE_GAME_VERSION`) ; `Ouvert : lps_couple`. `sandbox_mods.ini` : `Order=lps_asset_integration,lps_couple,lps_interaction`, `Open=lps_couple`, 3 cases `true` | PASS | +| E2 mod requérant un mod (d) | `lps_test_bad` refusé, 3 mods prêts | `Modules : 8 sandbox …, lps_test_bad` ; `lps_test_bad [mod] refuse : un mod ne peut pas requerir un autre mod (lps_couple) : partager le composant dans une bibliotheque` (Display + Warning) ; les 3 mods `pret` ; `Ouvert : lps_couple` | PASS | +| E3 prérequis absent (e) | couple et interaction « prérequis manquant », asset_integration prêt | `Modules : 6 sandbox` ; `lps_couple [mod] prerequis manquant : prerequis absent : lps_lib_scene` ; idem `lps_interaction` ; `lps_asset_integration [mod] pret`. Le `Open=lps_couple` persisté est tenté puis refusé : `Ouverture refusee : lps_couple (prerequis absent : lps_lib_scene)` puis `Ouvert : lps_asset_integration` (repli sur le premier mod prêt, § 6.4 de la SPEC) | PASS | +| E4 persistance (f) | ordre du fichier, `lps_couple` désactivé, `Ouvert : lps_interaction` | ordre `…, lps_asset_integration, lps_couple, lps_interaction` ; `lps_couple [mod] desactive` ; `Ouvert : lps_interaction` | PASS (*) | +| E4b (essai ajouté) ordre non alphabétique | l'ordre du fichier prime | avec `Order=lps_interaction,lps_asset_integration,lps_couple` : `ordre : …, lps_lib_actors, lps_interaction, lps_asset_integration, lps_couple` | PASS | +| E5 non-régression Mesure (h) | tournée identique, mêmes fichiers, `Ouvert : lps_asset_integration` | `AnimPipeline.ps1 -Pack freeanims -From 5 -To 5` : `verdict : ok (435 s)`, `Chaine terminee`, code de sortie 0 ; journal `SB1_VAL_E5.log` : `Ouvert : lps_asset_integration` (ordre `lps_couple, lps_interaction, lps_asset_integration`) ; 32 fichiers de même noms (10 `vignettes\*.png`, 10 `planches\*.jpg`, 10 `planches\*.json`, 2 `retarget_pose_*.txt`) ; 10 `planches\*.json` identiques ; `eval_Bridget.csv` / `eval_Michael.csv` identiques (SHA-256) ; 20 images (10 png, 10 jpg) d'empreinte différente | PASS pour les fichiers et JSON ; images : voir ci-dessous | + +(*) L'ordre demandé en E4 (`lps_asset_integration, lps_couple, lps_interaction`) est aussi l'ordre alphabétique par défaut : il ne prouve pas à lui seul la lecture du fichier ; E4b le prouve. + +### E5 — images dont l'empreinte diffère (tailles en octets, référence → nouveau) + +Tous les noms et les `.json` sont identiques ; écarts de taille faibles (rendu non déterministe attendu), à juger par l'utilisateur. + +| Fichier | Référence → nouveau | +|---|---| +| planches\AS_AirKiss.jpg | 148582 → 148574 | +| planches\AS_AttackInLevitation2.jpg | 139248 → 139244 | +| planches\AS_ChillDriving.jpg | 187677 → 187692 | +| planches\AS_ComboHands.jpg | 159898 → 159913 | +| planches\AS_EatingOnParty.jpg | 141183 → 141291 | +| planches\AS_FlyingUp.jpg | 119016 → 119041 | +| planches\AS_GoForward.jpg | 150693 → 150742 | +| planches\AS_Rest.jpg | 138519 → 138633 | +| planches\AS_RightLegKick2.jpg | 88070 → 88116 | +| planches\AS_Talk.jpg | 144361 → 144274 | +| vignettes\AS_AirKiss.png | 261580 → 261353 | +| vignettes\AS_AttackInLevitation2.png | 253439 → 253838 | +| vignettes\AS_ChillDriving.png | 289382 → 289345 | +| vignettes\AS_ComboHands.png | 268915 → 270145 | +| vignettes\AS_EatingOnParty.png | 263792 → 263826 | +| vignettes\AS_FlyingUp.png | 263466 → 263846 | +| vignettes\AS_GoForward.png | 256944 → 256957 | +| vignettes\AS_Rest.png | 242586 → 242608 | +| vignettes\AS_RightLegKick2.png | 265358 → 265512 | +| vignettes\AS_Talk.png | 260640 → 259551 | + +Référence : `D:\Game\LifePlay_Backup_5.5\SB1-VAL\reference_freeanims\` (+ `eval_*.csv` avant). Nouveau : `D:\Game\LifePlay-master 5.5\Saved\AnimPipeline\freeanims\sandbox\`. + +## Observations + +- Le fichier `sandbox_mods.ini` n'existait pas avant E1 (créé au premier lancement avec l'ordre alphabétique, conforme à la SPEC § 4). +- E2 a inscrit `lps_test_bad` dans l'ini (Order et Enabled) ; E3 et E5 ont modifié `Open` (le mod ouvert est mémorisé à chaque ouverture, dont celle de la tournée Mesure : `Open=lps_asset_integration` après E5). Le fichier a été remis à l'état demandé. +- Le pilote (étape 5) lance `LifePlay_interaction_sandbox.bat` et non `LifePlay_pair_sandbox.bat`. +- Aucun avertissement de lecture sur les 16 `.lpmod` du jeu (le registre ne journalise pas les modules du jeu un par un ; seul le total est donné). + +## État final des fichiers remis en place + +- `Test-Path ...\Modules\Mods\lps_test_bad` : False (dossier supprimé). +- `Test-Path ...\Modules\System\0010_lps_lib_scene\lps_lib_scene.lpmod` : True ; `D:\Game\LifePlay_Backup_5.5\SB1-VAL\0010_lps_lib_scene` : absent (déplacé retour). +- Aucun processus `UnrealEditor` actif à la fin. +- `D:\Game\LifePlay-master 5.5\Saved\sandbox_mods.ini` : + +``` +[Mods] +Order=lps_couple,lps_interaction,lps_asset_integration +Open=lps_couple +[Enabled] +lps_couple=true +lps_interaction=true +lps_asset_integration=true +``` + +- `Saved\sandbox_mode.txt` : `Pair` (remis par le pilote). `git status` de l'addon : aucun fichier parasite sous `Modules\`. +- Aucun source, `.lpmod` existant ni script modifié ; aucun commit. + +## À contrôler visuellement par l'utilisateur + +- (a) Chaque mod (Couple, Interaction, Asset integration) s'ouvre depuis la barre latérale et se comporte comme l'ancien mode. +- (b) Enchaînement couple → interaction → asset integration → couple sans résidu (caméra, vue libre, animation automatique). +- (c) Un mod décoché n'est pas ouvrable (▶ inactif) ; décocher le mod ouvert ramène la scène neutre. +- Aspect de la barre latérale : sections Mods, Système (case « modules masqués »), Jeu (16 modules, repliée), bouton ◀ / ▶, ⚠ avec infobulle. +- Images E5 qui diffèrent : les 20 fichiers du tableau ci-dessus (comparer vignettes et planches avec la référence). + +## Verdict partiel + +**PASS** de la partie automatique (E1 à E5 : critères d, e, f, g, h, i satisfaits ; images E5 : écarts de pixels à juger visuellement). Critères a, b, c restent à contrôler par l'utilisateur. + +## Contrôle visuel utilisateur (2026-10-07) + +| Critère | Verdict | +|---|---| +| (a) chaque mod s'ouvre depuis la barre latérale et se comporte comme l'ancien mode | PASS (« pas de défauts constatés ») | +| (b) enchaînement couple, interaction, asset integration, couple sans résidu | PASS (« pas de défauts constatés ») | +| (c) mod décoché non ouvrable ; décocher le mod ouvert ramène la scène neutre | PASS (« pas de défauts constatés ») | +| Aspect de la barre latérale | PASS (« pas de défauts constatés ») | +| Images E5 (20 fichiers qui diffèrent) | PASS (« pas de défauts constatés ») | + +## Verdict global du lot SB1 + +**PASS** (partie automatique E1 à E5 + contrôle visuel utilisateur). Commits addon : `82dae58380b76c45f60541fd7cb5a0a409700e10` (code) puis commit de pin README ; tag `lot-SB1-valide`. + +## Modèles par fiche + +| Fiche | Modèle prévu | Modèle utilisé | +|---|---|---| +| SB1-T1 | opus | opus | +| SB1-T2 | opus | opus | +| SB1-T3 | sonnet | sonnet | +| SB1-T4 | haiku | haiku | +| SB1-T5 | sonnet | sonnet | +| SB1-T6 | sonnet | sonnet | \ No newline at end of file