LifePlay-Revival/Docs/Migration/21_Sandbox_Addon.md
2026-10-07 15:49:56 +02:00

187 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 21. Migration sandbox → addon éditeur (ébauche, 2026-10-06)
**Objectif :** extraire le code de sandbox Pair / Interaction / Mesure (aujourd'hui dans `Source/LifePlay/` et
`Source/LifePlayEditorTools/`, en grande partie non suivi par git) vers un **plugin éditeur** dédié, sans casser le
jeu empaqueté ni la chaîne d'animations (`20_Chaine_Animations.md`). Le module `LifePlay` ne garde que le contrat
minimal vers le jeu (dont `LPShowCoupleSubsystem`).
**Amont :** lot S0 validé (`S0-VAL.md` : PASS). Inventaire gelé : `Docs/Migration/etat/sandbox/S0/S0-CART.md`.
---
## Périmètre S0 (fichiers gelés)
Liste figée au 2026-10-06 ; toute évolution ultérieure passe par une fiche de lot et une entrée `CHG`.
### Runtime (`Source/LifePlay/`)
| Chemin | LOC approx. | Notes |
|---|---:|---|
| `LPPairSandbox.h` | 322 | Stage / Character / GameMode Pair |
| `LPPairSandbox.cpp` | 4261 | Monolith Pair, interaction, mesure |
| ~~`LPInteractionSandbox.*`~~ | — | **Retiré S4-T3** (addon) ; Interaction via menu `LPPairSandbox` |
| `LPPoseMeasure.h` | 100 | API mesure |
| `LPPoseMeasure.cpp` | 1558 | Sample / Transfer / T-fit |
| `LPSkeletonProfile.h` | 38 | Profils JSON |
| `LPSkeletonProfile.cpp` | 157 | |
| `LPPoseOrchestrator.h` | 30 | |
| `LPPoseOrchestrator.cpp` | 487 | |
| `LPAnimRedirect.h` | 15 | Cible `sandbox` \| `game` |
| `LPAnimRedirect.cpp` | 24 | |
| `LPShowCoupleSubsystem.h` | 77 | **Reste dans LifePlay** (sync lot S5) |
| `LPShowCoupleSubsystem.cpp` | 1048 | |
### Éditeur (`Source/LifePlayEditorTools/`)
| Chemin | LOC approx. | Notes |
|---|---:|---|
| `LPPairCollisionTestCommandlet.*` | ~546 | PairCol headless |
| `LPAnimEvalCommandlet.*` | ~489 | Mesure headless |
| `LPAnimCatalogCommandlet.*` | ~388 | |
| `LPAnimChecks.*` | ~435 | |
| `LPAnimTwistCommandlet.*` | ~384 | |
### Lanceurs (`Tools/debug/`)
| Chemin | Notes |
|---|---|
| `LifePlay_pair_sandbox.bat` | Pair |
| `LifePlay_interaction_sandbox.bat` | Interaction |
| `LifePlay_measure_sandbox.bat` | Mesure |
**Synthèse S0-CART :** ~7,5 k LOC runtime sandbox + mesure ; trois lanceurs désormais versionnables (motif
`.gitignore` : `/Debug/`).
---
## Contrat (S1 : configuration des chemins)
Le lot S1 fixe le premier contrat jeu ↔ addon : **tous les chemins d'assets et de fichiers du sandbox Pair passent par
`Config/LPSandbox.ini`**, lu par un point d'accès unique. Détail : `etat/sandbox/S1/S1-SPEC.md` ; implémentation
`S1-IMPL.md` ; audit `S1-CART.md` ; validation `S1-VAL.md` (PASS statique ; compilation et runtime à confirmer).
- **Fichier** : `Config/LPSandbox.ini` (versionné, pas dans `Saved/`), lecture directe non hiérarchique (`FConfigFile::Read`).
- **API** : `struct LIFEPLAY_API FLPSandboxConfig` (`Source/LifePlay/LPSandboxConfig.h/.cpp`), `static const FLPSandboxConfig& Get()`, chargé une fois. `LPPairSandbox.cpp` et `LPAnimRedirect::ClipPath` n'utilisent plus de littéral `/Game/...`.
- **Repli** : fichier, clé ou valeur vide absents => default = littéral historique (comportement identique sans ini). Un asset introuvable garde le journal `LogLifePlay` d'origine, sans repli silencieux.
- **19 clés, 6 sections** : `Sandbox.Mode` (`Mode` = `Pair` | `Interaction` | `Mesure`, `Saved/sandbox_mode.txt` reste prioritaire) ; `Sandbox.Assets` (`PersonBP`, `BridgetMesh`, `MichaelMesh`, `QuaterniusMannequinMesh`, `DazIdleFemale`, `DazIdleMale`, `FurnitureWallMaterial`) ; `Sandbox.Roots` (`FurnitureRoot`, `DazAnimRoot`, `QuaterniusRoot`, `QuaterniusUALRoot`) ; `Sandbox.Content`, chemins relatifs à `Content/` (`ModuleRoot`, `WorldFile`, `ChatAction`, `QuaterniusUALDisk`) ; `Sandbox.Sky` (`SkyCubemap`, `SkyIntensity`) ; `Sandbox.Output`, relatif à `Saved/` (`MeasureOutputRoot`).
- **Conventions** : asset = chemin UE complet `/Game/...Nom.Nom` (`LoadObject`) ; racine = package sans nom d'asset ; disque = relatif à `ProjectContentDir()` (ou `ProjectSavedDir()` pour `Output`).
- **Hors contrat (volontaire)** : `LPKeyboard.ini`, `Saved/sandbox_mode.txt` / `sandbox_tour.txt`, littéraux `/Engine/...`, noms de clips et poses (`Idle_Loop`, `Walk_Loop`, `A_TPose`, `B_`/`M_`), liste `Names[]`.
- **Reste ouvert (lots suivants)** : points d'accroche ShowCouple (S5) ; staging de `LPSandbox.ini` si le sandbox doit tourner empaqueté. (Dépendances `Build.cs` et répartition des exports : traitées en S2, section suivante.)
---
## Module `LPAnimMeasure` (S2 : extraction de la chaîne de mesure)
Le lot S2 sort la chaîne de mesure de `LifePlay` vers un module Runtime dédié, `Source/LPAnimMeasure/` (structure plate, comme `LifePlay`). Détail : `etat/sandbox/S2/S2-CART.md` (cartographie), `S2-IMPL-H.md` (implémentation), `S2-VAL.md` (validation : PASS statique et runtime Mesure).
- **Contenu** : `LPPoseMeasure`, `LPSkeletonProfile`, `LPPoseOrchestrator`, `LPAnimRedirect` (8 fichiers déplacés) + `LPAnimMeasure.Build.cs`, `LPAnimMeasure.h/.cpp` (catégorie de journal `LogLPAnimMeasure`, ex-`LogLifePlay` pour les lignes `POSET` / `PROFIL` / `ORCH` / `MESURE`).
- **Exports** : `LPANIMMEASURE_API` remplace `LIFEPLAY_API` ; 6 symboles ajoutés pour `LPPairSandbox.cpp` (`KeyBones`, `LogReport`, `TFitReport`, `Run`, `Restore`, `SampleAt`) ; les structs de données restent header-only.
- **Rupture du couplage** : `LPAnimRedirect` ne lit plus `LPSandboxConfig`. `FLifePlayModule::StartupModule()` (`LifePlay.cpp`) appelle `LPAnimRedirect::SetUALRoot(FLPSandboxConfig::Get().QuaterniusUALRoot)` ; défaut interne `/Game/Quaternius/UAL` (identique à celui de la config).
- **Déclaration** : entrée `Runtime` / `Default` dans `LifePlay.uproject`, placée avant `LifePlay` ; `ExtraModuleNames` des deux `*.Target.cs`.
### Graphe de dépendances des modules
```mermaid
flowchart LR
LPET["LifePlayEditorTools<br/>(Editor)"] -->|Public| LP["LifePlay<br/>(Runtime, module primaire)"]
LPET -->|Public<br/>FLPPose dans LPAnimChecks.h| AM["LPAnimMeasure<br/>(Runtime, nouveau S2)"]
LP -->|Public<br/>LPPairSandbox, LPDirectControl| AM
LP -.->|"StartupModule : SetUALRoot(QuaterniusUALRoot)"| AM
AM -->|Public| ENG["Core, CoreUObject, Engine"]
AM -->|Private| JSON["Json"]
```
Règles : `LPAnimMeasure` ne dépend ni de `LifePlay` ni de `LifePlayEditorTools` (graphe acyclique) ; `LPSandboxConfig` reste dans `LifePlay` et pousse la configuration vers `LPAnimMeasure` par un setter, jamais l'inverse. Ordre de chargement `LPAnimMeasure` avant `LifePlay` : dépendance `Build.cs` et ordre du `.uproject`.
| De | Vers | Type | Motif |
|---|---|---|---|
| `LifePlay` | `LPAnimMeasure` | Public | `LPPairSandbox.h/.cpp`, `LPDirectControlSubsystem.cpp`, `LifePlay.cpp` |
| `LifePlayEditorTools` | `LifePlay` | Public | existant |
| `LifePlayEditorTools` | `LPAnimMeasure` | Public | `LPAnimChecks.h` expose `FLPPose` ; commandlets `LPAnimEval`, `LPAnimCatalog`, `LPAnimTwist` |
| `LPAnimMeasure` | `Core`, `CoreUObject`, `Engine` / `Json` | Public / Privé | animations, squelettes ; lecture de `Tools/anim/profiles/*.json` |
| `LPAnimMeasure` | `LifePlay`, `LifePlayEditorTools` | **aucune** | évite un cycle de modules |
---
## Stratégie git et liaison jeu ↔ addon (S3)
Le lot S3 déplace le code sandbox dans un dépôt séparé, `LifePlay-Sandbox` (https://github.com/edujiro5972/LifePlay-Sandbox.git, branche `main`). Détail : `etat/sandbox/S3/S3-INT.md` ; validation : `etat/sandbox/S3/S3-VAL.md` (PASS).
**Décision : deux dépôts siblings + `AdditionalPluginDirectories`. Pas de submodule imbriqué.**
```
D:\Game\
LifePlay-master 5.5\ <- jeu (repo LifePlay)
LifePlay-Sandbox\ <- addon (repo LifePlay-Sandbox)
```
- **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 `a3b1b897715b05b22d75b79d53637e58b6698c2c`, `main`, AP7 méthodes de transformation et sandbox multi-pack ; 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 :
```powershell
cd D:\Game
git clone https://github.com/edujiro5972/LifePlay-Sandbox.git
git -C LifePlay-Sandbox checkout a3b1b897715b05b22d75b79d53637e58b6698c2c
# le jeu doit être cloné à côté : D:\Game\LifePlay-master 5.5
```
- **Content** : le jeu n'a rien à monter. L'addon utilisé seul (`LPSandbox.uproject`) a besoin du Content du jeu : junction NTFS `LifePlay-Sandbox\Content` → `LifePlay-master 5.5\Content` (`/Game` ne peut pointer que vers `<projet>/Content` ; la clé `AdditionalAssetDirectories` n'existe pas en UE 5.5). Voir le README de l'addon.
- **Contenu du jeu après S3-T3** : `Source/LPAnimMeasure/` et les fichiers `LPPairSandbox*`, `LPInteractionSandbox*`, `LPSandboxConfig*` ne sont plus dans le jeu. Le bat Pair lance toujours `LifePlay.uproject` avec `game=/Script/LPSandbox.LPPairSandboxGameMode`. `Source/LifePlay/LifePlay.Build.cs` dépend toujours de `LPAnimMeasure` (module désormais fourni par le plugin).
- **Validation S3-VAL** : bat Pair depuis le jeu, plugin monté et GameMode résolu sans erreur de module ; addon mince lancé avec le même GameMode (réserve : son `.uproject` n'active pas `StreetMap`, d'où des erreurs de chargement de Blueprints du jeu sans effet sur le sandbox).
---
## CoupleBox : `LPCoupleSim` partagé (S5)
Le lot S5 ferme la **dérive de fork** entre la sandbox (`LPSandbox/LPCoupleSim.cpp`, méthodes de `ALPPairSandboxStage`) et le jeu (`LPShowCoupleSubsystem`) : les deux gardaient une copie de la géométrie du couple (mesure de pose, boîte, balayage, soudure) qui divergeait (plancher −90 / −88, itérations 2 / 3, os exclus). Détail : `etat/sandbox/S5/S5-CART.md` (écarts), `S5-IMPL-H.md` (implémentation), `S5-VAL.md` (validation : PASS runtime Pair, PASS-STATIC ShowCouple). Addon pinné sur `c1fdd29`.
- **Où** : header `Plugins/LPSandbox/Source/LPAnimMeasure/LPCoupleSim.h` (215 l.) et `LPCoupleSimCore.cpp` (474 l.), module `LPAnimMeasure`, `namespace LPCoupleSim`, exports `LPANIMMEASURE_API`. Une seule copie : le jeu la voit par `LifePlay.Build.cs` (dépendance publique existante), sans recopie ni changement de `.Build.cs`. Le nom `LPCoupleSimCore.cpp` évite la collision avec `LPSandbox/LPCoupleSim.cpp`.
- **Trois niveaux d'API** :
- *pur* (sans `UObject` ni `UWorld`, testable sans monde) : `MeshToActor`, `ComposeComponentSpace`, `AccumulatePoints`, `IsExcludedLiveBone`, `FitFromAnimBounds`, `FitFromLiveBounds`, `SlideWish` ;
- *lecture* : `CapsuleHalfOf`, `AccumulateAnimBounds` (8 pas, os animés, `GetBoneTransform(raw=false)`), `AccumulateLiveBody` ;
- *effets de bord* : `EnsureBox`, `ApplyBoxFit`, `PlaceBodyMesh`, `SaveAndFreeze` / `RestoreMove` (`FSavedMove`), `AttachPartner`, `Unweld`, `ResolveCoupleMove`.
- **Paramètres** : `FBoxFitParams` (demi-capsule 88, pad anime 12/12/6 plafond 800, pad vivant 4/4/4 min 8 plafond 400, ancre pelvis 140, `FloorLocalZ`, `MinExtentZ` 20) ; `FSweepOptions::Show()` (3 itérations, `SweepMulti`, dépénétration, PNJ + coque `Wall*` ignorés) et `FSweepOptions::Sandbox()` (2 itérations, `SweepSingle`, arrêt net, marches, Z à plat). `ResolveCoupleMove` **ne déplace rien** : elle rend la position retenue (`FSweepResult`), chaque appelant l'applique.
- **Appelants** : sandbox `FitCoupleBox` / `Weld` / `MoveCouple` ; jeu `FitBox` / `FitToBodies` / `Weld` / `Engage` / `Release` / `KeepTogether`. Les copies `AddPose` (jeu), `Accumulate` (sandbox), les deux créations de `UBoxComponent` et les deux boucles de balayage n'existent plus (−336 lignes dupliquées, +689 dans le module partagé).
- **Écarts conservés volontairement** (options, pas de copies) : plancher sandbox −90 contre jeu −88 (`FloorLocalZ`), balayage simple 2 itérations contre multi 3 itérations, nom de composant `CoupleBox` contre `LPShowCoupleBox`. L'alignement du plancher est une ligne dans `FitCoupleBox`, à décider après capture en jeu.
- **Reste dans `LifePlay`** : sélection du couple (`SexPersons`, `AP1/AP2`, `ShowReach`), réflexion `GI` / `Room`, `IsBedMesh` (injecté via `IsWalkableSupport`), `IsUseFurniture`, `OpenUseFurniture`, `PairSibling` / `IsIdleClip`, `FlipResponse` / `RestoreResponses` (non partagés, hors S5-T2), CVar `lp.ShowCouple`.
- **Contrat de modification** : tout changement de géométrie du couple se fait dans `LPCoupleSim` (addon), puis bump du pin ; le jeu ne doit pas réintroduire de copie locale.
---
## Acceptation
État au 2026-10-06 (lot S8). Case cochée = prouvée par la fiche citée ; case vide = non faite.
- [x] **S0** : hygiène dépôt, inventaire gelé (`S0-VAL.md` PASS).
- [x] **S1** : tous les chemins du sandbox via `Config/LPSandbox.ini` (`S1-VAL.md` PASS statique).
- [x] **S2** : module `LPAnimMeasure` extrait, sans dépendance vers `LifePlay` (`S2-VAL.md` PASS).
- [x] **S3** : addon dans le dépôt `LifePlay-Sandbox`, liaison sibling + `AdditionalPluginDirectories`, pin manuel (`S3-VAL.md` PASS).
- [x] **S4** : sandbox Pair découpé, GameMode Interaction legacy retiré (`S4-VAL.md` PASS).
- [x] **S5** : géométrie du couple partagée via `LPCoupleSim`, une seule copie (`S5-VAL.md` PASS runtime Pair, PASS-STATIC ShowCouple).
- [x] **S6** : stack Mesure sur la liaison addon, `LPAnimEval` court (`S6-VAL.md` PASS).
- [x] **S7 / isolation** : plugin sans `include` / `LIFEPLAY_API` / module `LifePlay` ; `Person` = CDO seul, aucun `GI` / `Room` ; chemins par config (`S7-SPEC.md`, `S7-CART.md` : 0 écart).
- [x] **S7 / configurations** : `LPSandbox` hors Shipping (`TargetConfigurationDenyList`), `LPAnimMeasure` conservé, plugin `Optional` dans `LifePlay.uproject` (`S7-IMPL-L.md`).
- [x] **Smoke Pair** (bat court, plugin monté, boîte du couple identique à S5-VAL, 0 crash) (`S7-VAL.md`).
- [ ] Commandlets anim restants (`LPAnimCatalog`, `LPAnimChecks`, `LPAnimTwist`) déplacés dans un module éditeur de l'addon : **non fait** (restent dans `LifePlayEditorTools`).
- [x] **S8** : bilan du mode multi-agents (`22_Eval_Agents_Sandbox.md` : GO conditionnel) ; code sandbox absent du module jeu depuis S3 ; smoke Pair par le `.bat` réel, boîte du couple identique à S5 / S7, 0 crash (`S8-VAL.md` PASS).
- [ ] `BuildMod.ps1 -Target sandbox -Open` rejoué sur la liaison addon : **non fait**.
- [ ] Compilation / packaging **Shipping** complets avec le plugin (seul le plan UBT est vérifié).
- [ ] 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).
## Lots (S0 … S8)
| Lot | Objet (une ligne) |
|---|---|
| **S0** | Hygiène dépôt : `/Debug/` dans `.gitignore`, lanceurs `Tools/debug` suivis, inventaire et preuves `etat/sandbox/S0/*`. |
| **S1** | Contrat jeu ↔ addon : API minimale, dépendances modules, règles de linking (placeholder ci-dessus). |
| **S2** | Extraction du module `LPAnimMeasure` (mesure, profils, orchestrateur, redirection) hors de `LifePlay` ; dépendances `Build.cs` et exports. **Fait** : `S2-VAL.md` PASS (voir section « Module `LPAnimMeasure` »). Le plugin éditeur (uplugin) reste à faire. |
| **S3** | Création du dépôt addon `LifePlay-Sandbox` (scaffold, transfert du code, retrait côté jeu) et liaison par clone sibling + `AdditionalPluginDirectories`, pin manuel. **Fait** : `S3-VAL.md` PASS (voir section « Stratégie git »). Rejeu stack Mesure : lot **S6** (`S6-VAL.md`). |
| **S4** | Sandbox Pair : découpage `LPPairSandbox`, retrait GM Interaction legacy (S4-T3 **fait**), commandlet `LPPairCollisionTest`, bat Pair. Validation : `S4-VAL.md` (PASS). |
| **S5** | ShowCouple : `LPShowCoupleSubsystem` reste dans `LifePlay` ; géométrie du couple partagée via `LPCoupleSim` (module `LPAnimMeasure` de l'addon, pin `c1fdd29`). **Fait** : `S5-VAL.md` PASS (runtime Pair, PASS-STATIC ShowCouple ; voir section « CoupleBox »). Show complet en jeu et alignement du plancher restent à valider. |
| **S6** | Stack Mesure sur la liaison addon : `LifePlayEditorTools` → `LPAnimMeasure` (plugin), lanceurs `Tools/debug`, sorties `Saved/Measurement`, commandlet `-run=LPAnimEval`. **Fait** : `S6-VAL.md` PASS (doc `20` / `09`, essai court LPAnimEval). |
| **S7** | Isolation du plugin (sans lien vers `LifePlay`, `Person` en CDO seul, config seulement), `LPSandbox` hors Shipping, `Optional` dans `LifePlay.uproject`. **Fait** : `S7-VAL.md` PASS (smoke Pair ; Shipping vérifié par plan UBT). Le déplacement des commandlets Catalog / Checks / Twist reste à planifier (voir § Acceptation). |
| **S8** | Bilan et clôture : évaluation du mode multi-agents + multi-modèles (`22_Eval_Agents_Sandbox.md`, GO conditionnel, 3 recommandations), smoke Pair final. **Fait** : `S8-VAL.md` PASS. `BuildMod -Target` et commit côté jeu restent ouverts (§ Acceptation). |
Chaque lot suit `10_Protocole_Operations.md` : instantané, entrée `CHG`, retour arrière documenté.