LifePlay-Revival/Docs/Migration/21_Sandbox_Addon.md
edujiro5972 04b053554f Sandbox addon : brancher le jeu sur LifePlay-Sandbox (plugin LPSandbox).
Charge le plugin via AdditionalPluginDirectories, conserve ShowCouple et les commandlets anim côté jeu, et documente les lots S0–S8 (CHG-212 à CHG-220).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-06 21:10:53 +02:00

18 KiB
Raw Blame History

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

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 f09c42e5a2bc613cf269cce76c878461ca41c8b7, main, S7-T2 uplugin ; précédents : 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 :
cd D:\Game
git clone https://github.com/edujiro5972/LifePlay-Sandbox.git
git -C LifePlay-Sandbox checkout f09c42e5a2bc613cf269cce76c878461ca41c8b7
# 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.

  • S0 : hygiène dépôt, inventaire gelé (S0-VAL.md PASS).
  • S1 : tous les chemins du sandbox via Config/LPSandbox.ini (S1-VAL.md PASS statique).
  • S2 : module LPAnimMeasure extrait, sans dépendance vers LifePlay (S2-VAL.md PASS).
  • S3 : addon dans le dépôt LifePlay-Sandbox, liaison sibling + AdditionalPluginDirectories, pin manuel (S3-VAL.md PASS).
  • S4 : sandbox Pair découpé, GameMode Interaction legacy retiré (S4-VAL.md PASS).
  • S5 : géométrie du couple partagée via LPCoupleSim, une seule copie (S5-VAL.md PASS runtime Pair, PASS-STATIC ShowCouple).
  • S6 : stack Mesure sur la liaison addon, LPAnimEval court (S6-VAL.md PASS).
  • 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).
  • S7 / configurations : LPSandbox hors Shipping (TargetConfigurationDenyList), LPAnimMeasure conservé, plugin Optional dans LifePlay.uproject (S7-IMPL-L.md).
  • 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).
  • 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).
  • 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é.