LifePlay-Revival/Docs/Migration/etat/U58/U7b/U7b-T4-IMPL.md

351 lines
26 KiB
Markdown
Raw Normal View History

Fiche U7b-T4 — Niveau N1 (rédaction N3) — Modèle prévu lp-impl | utilisé claude-opus-5-5 — Agent lp-impl — Skills sk-protocol (préchargés aussi : ue-cpp-build, lp-build, lp-ue-compat)
# U7b-T4 : tournée Mesure hors écran (T1, option A) et garde-fou de RetargetPack sous UE ≥ 5.6 (T2 reporté)
Date : 2026-10-09. Addon = worktree `D:\Game\LifePlay-Sandbox-5.8`, branche `ue5.8`, HEAD `d1efdd1` (tag `U7b-avant`).
Rien n'a été commité. Je n'ai lancé ni le jeu, ni l'éditeur, ni le pipeline. Les essais statiques du § 6 tournent hors
moteur : un faux module `unreal` et un faux `UnrealEditor.exe`, tous deux dans le bloc-notes de session.
Le checkout 5.5 `D:\Game\LifePlay-Sandbox` est intact (`git status --short` : 0 ligne).
## 0. Résumé
| Point | État | Preuve |
|---|---|---|
| (a) `-RenderOffScreen` est passé à la seule tournée Mesure du pilote | fait | § 3 (chaîne d'appel) et § 6.2 (cas 4 : la ligne reçue se termine par ` -RenderOffScreen`) |
| (a) usage interactif inchangé | **établi, à l'octet près**, sur la ligne de commande reçue par l'exécutable | § 4 et § 6.2 (cas 1/2/5/6 identiques) |
| (b) refus sous UE ≥ 5.6, avant toute suppression ou création | fait | § 5 : appel `RP:436`, première suppression `RP:265` atteinte par `RP:451` ; essai § 6.1 : 0 appel d'asset |
| (b) BuildMod / AnimPipeline voient un échec | **établi par le code + essai statique de la regex** | § 5.2 et § 6.3 |
| Correctif de portage T2 (diff § 5 de la CART) | **non appliqué**, conformément à la fiche | `grep -c "run_batch_retarget\|add_retarget_op" RetargetPack.py` = 0 |
## 1. Fichiers, instantané, fins de ligne
| Fichier (addon 5.8) | Instantané `U7b-addon` (MD5 avant) | MD5 après | Fins de ligne / encodage avant → après | `git diff --stat` |
|---|---|---|---|---|
| `Tools\anim\AnimPipeline.ps1` | `6BC085152D53C3A15DDDFABF9505F0E0` (fait par l'orchestrateur) | `adc7a19ebfa1cb0b93b382e7f40ee526` | LF 568 → LF 573, sans BOM, 4 octets non ASCII inchangés | 7 +, 2 − |
| `Tools\anim\RetargetPack.py` | `3C6491C6FF0DCCF4D44462FADC02C0ED` (fait par l'orchestrateur) | `182c60f3e8e0693ff9f6dcefc98790f9` | LF 463 → LF 486, ASCII | 23 + |
| `Tools\debug\LifePlay_interaction_sandbox.bat` | `EF13A3929D5E9A7332BFA2BA9FCE8984`, **ajouté par moi** avant modification (commande de la fiche, sortie `sauvegarde : ... 1 entree(s) ajoutee(s)`) | `7ec8d6350e2cbd36bedf2ccbef50fbd1` | CRLF 10 → CRLF 15, ASCII, sans LF nu | 6 +, 1 − |
- Manifeste : `D:\Game\LifePlay_Backup_5.5\U7b-addon\manifest.csv`, 3 entrées. Le MD5 du .bat sauvegardé est égal à celui de
`git show HEAD:Tools/debug/LifePlay_interaction_sandbox.bat` (`md5sum` : `ef13a392…`).
- Comptes produits par Python sur les octets (`d.count(b"\r\n")`, `d.count(b"\n")`, BOM, octets > 127). `.gitattributes`
de l'addon : `* -text`, donc aucune conversion par git.
- Pourquoi le .bat est modifié : il ne transmettait aucun argument (`%*` absent, `.bat:9` d'avant ; U7b-T1-CART § 1.1).
Sans cette modification, il aurait fallu lancer l'exécutable depuis le pilote en recopiant la résolution GAME/UE/PROJ
du .bat, ce qui duplique la logique du lanceur.
## 2. Diffs (`git -C D:\Game\LifePlay-Sandbox-5.8 diff`)
```diff
--- a/Tools/anim/AnimPipeline.ps1
+++ b/Tools/anim/AnimPipeline.ps1
@@ -304,6 +304,10 @@ function Invoke-Sandbox {
$modeF = Join-Path $saved 'sandbox_mode.txt'
$tourF = Join-Path $saved 'sandbox_tour.txt'
$bat = Join-Path $LPSbRoot 'Tools\debug\LifePlay_interaction_sandbox.bat'
+ # U7b (option A, U7b-T1-CART section 5) : la tournee rend hors ecran (pas de fenetre systeme), donc une fenetre reduite
+ # ne ramene plus le back buffer a 8x8 (assertion D3D12 de UE 5.8 a la capture). Option propre au pilote, passee en
+ # argument du .bat ; l'ouverture interactive (BuildMod.ps1 -Open) lance le .bat sans argument et garde sa fenetre.
+ $sbArgs = '-RenderOffScreen'
$sbRel = ($c.sortie.clips -replace '^/Game/', '')
$clipRel = ($c.source.clips -replace '^/Game/', '')
$sbRoot = [string] $c.sandbox
@@ -314,7 +318,7 @@ function Invoke-Sandbox {
$outDir = Join-Path $saved ($sbRoot -replace '/', '\')
Add-Cmd "ecrire Saved\sandbox_pack.ini (Name=$Pack, ...)"
Add-Cmd 'ecrire Saved\sandbox_mode.txt = Mesure ; Saved\sandbox_tour.txt = vignettes planches quitter'
- Add-Cmd "Tools\debug\LifePlay_interaction_sandbox.bat (attente de la fermeture de UnrealEditor, 40 min maximum)"
+ Add-Cmd "Tools\debug\LifePlay_interaction_sandbox.bat $sbArgs (attente de la fermeture de UnrealEditor, 40 min maximum)"
Add-Cmd "deplacer Saved\sandbox_pack.ini vers $(Get-WorkRel)\sandbox_pack.ini ; restaurer sandbox_mode.txt d'origine"
@@ -331,7 +335,8 @@ function Invoke-Sandbox {
Write-Text $tourF 'vignettes planches quitter'
# Sans tuyau ni attente : avec `& $bat | Out-Null`, PowerShell attendait la fin de l'editeur lance par le .bat
# (heritier de la sortie), puis ne le trouvait plus et concluait a tort « pas lancee » (AP7).
- Start-Process -FilePath 'cmd.exe' -ArgumentList ('/c "{0}"' -f $bat) -WindowStyle Hidden | Out-Null
+ # Commande entre guillemets doubles : cmd /c retire le premier et le dernier, le chemin du .bat reste entre guillemets.
+ Start-Process -FilePath 'cmd.exe' -ArgumentList ('/c ""{0}" {1}"' -f $bat, $sbArgs) -WindowStyle Hidden | Out-Null
```
(Dans le premier `Add-Cmd` du contexte, j'ai abrégé la parenthèse en `...` ; cette ligne n'est pas modifiée.)
```diff
--- a/Tools/anim/RetargetPack.py
+++ b/Tools/anim/RetargetPack.py
@@ -13,6 +13,8 @@
# Methode factice ou copie, ou profil du maillage source different de profil_source : ligne
# "RETARGET echec : <cause>" puis arret.
+# Lot U7b : sous UE >= 5.6, refus de meme forme avant toute suppression ou creation d'asset (API IK Retargeter
+# non portee, U7b-T2-CART ; portage reporte apres V2.0).
@@ -22,6 +24,7 @@
import json
import math
import os
+import re
@@ -57,6 +60,24 @@ class Refusal(Exception):
pass
+# Lot U7b (2026-10-09) : l'API Python de l'IK Retargeter a change en UE 5.6-5.8 (U7b-T2-CART : 9e argument de
+# duplicate_and_retarget devenu target_path, reglages de chaine inertes, retargeter neuf sans op). Lance tel quel,
+# le script supprimerait les IK/RTG de reference (make_rig, make_retargeter) et les clips cuits B_/M_
+# (delete_copies) avant de planter. Portage reporte apres V2.0 : refus avant toute suppression ou creation.
+UNPORTED_FROM = (5, 6)
+UNPORTED_MESSAGE = "RetargetPack : API IK Retargeter 5.8 non portee (U7b-T2-CART, reporte apres V2.0) ; aucun asset modifie"
+
+
+def refuse_unported_engine():
+ # GetEngineVersion = FEngineVersion::Current().ToString(), ex. "5.8.3-58210709+++UE5+Release-5.8".
+ # Version illisible : refus aussi (rien ne prouve alors un moteur anterieur a 5.6).
+ text = str(unreal.SystemLibrary.get_engine_version())
+ unreal.log_warning("RETARGET moteur %s" % text)
+ match = re.match(r"\s*(\d+)\.(\d+)", text)
+ if match is None or (int(match.group(1)), int(match.group(2))) >= UNPORTED_FROM:
+ raise Refusal(UNPORTED_MESSAGE)
+
+
def load_config(saved_dir):
@@ -411,6 +432,8 @@ def bake_named(clips, source_mesh, bodies):
def run():
global CFG
CFG = load_config(unreal.Paths.project_saved_dir())
+ # U7b : avant toute suppression ou creation d'asset ; apres load_config, qui lit le jeton de BuildMod.
+ refuse_unported_engine()
method = load_method(CFG["methode"])
```
```diff
--- a/Tools/debug/LifePlay_interaction_sandbox.bat
+++ b/Tools/debug/LifePlay_interaction_sandbox.bat
@@ -7,4 +7,9 @@
set "PROJ=%GAME%\LifePlay.uproject"
-start "" "%UE%" "%PROJ%" "/Engine/Maps/Entry?game=/Script/LPSandbox.LPPairSandboxGameMode" -game -windowed -ResX=1600 -ResY=900 "-ini:Engine:[/Script/EngineSettings.GameMapsSettings]:GameInstanceClass=/Script/Engine.GameInstance"
+rem Lot U7b : les arguments de l'appelant sont ajoutes en fin de ligne (AnimPipeline.ps1 : -RenderOffScreen pour la
+rem tournee Mesure). Sans argument (double-clic, BuildMod.ps1 -Target sandbox -Open), la ligne de lancement est inchangee,
+rem a l'octet pres : EXTRA n'est defini (espace de tete comprise) que si des arguments sont passes.
+set "EXTRA=%*"
+if defined EXTRA set "EXTRA= %EXTRA%"
+start "" "%UE%" "%PROJ%" "/Engine/Maps/Entry?game=/Script/LPSandbox.LPPairSandboxGameMode" -game -windowed -ResX=1600 -ResY=900 "-ini:Engine:[/Script/EngineSettings.GameMapsSettings]:GameInstanceClass=/Script/Engine.GameInstance"%EXTRA%
```
## 3. Chaîne d'appel de `-RenderOffScreen` (lignes après modification)
1. `AnimPipeline.ps1:310` `$sbArgs = '-RenderOffScreen'`. Puis `:339` lance
`Start-Process cmd.exe -ArgumentList '/c ""<addon>\Tools\debug\LifePlay_interaction_sandbox.bat" -RenderOffScreen"'`.
Une chaîne unique passée en `-ArgumentList` est transmise telle quelle comme ligne d'arguments.
2. `cmd /c` : la ligne compte 4 guillemets, donc la règle 2 de `cmd /?` s'applique. cmd retire le premier guillemet et le
dernier, et exécute `"<chemin du .bat>" -RenderOffScreen`. Texte de l'aide (`cmd /?`, poste en français) : « Dans le cas
contraire, l'ancien comportement vérifiera si le premier caractère est un caractère guillemet et si tel est le cas,
enlèvera le premier caractère et le dernier caractère guillemet de la ligne de commande ». Ainsi, le chemin du .bat
reste entre guillemets, même s'il contient une espace (essai § 6.2, dossier `dossier avec espace`).
3. `.bat:13` `set "EXTRA=%*"` donne `EXTRA=-RenderOffScreen`. `.bat:14` ajoute l'espace de tête :
`EXTRA= -RenderOffScreen`. `.bat:15` lance
`start "" "%UE%" "%PROJ%" "/Engine/Maps/Entry?game=…" -game -windowed -ResX=1600 -ResY=900 "-ini:…GameInstance" -RenderOffScreen`.
4. Moteur 5.8 : `FWindowsPlatformApplicationMisc::CreateApplication` teste
`FParse::Param(FCommandLine::Get(), TEXT("RenderOffScreen"))` et renvoie alors l'application nulle
(`D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\ApplicationCore\Private\Windows\WindowsPlatformApplicationMisc.cpp:173-176`).
Il n'y a ni écran de démarrage (`Runtime\Launch\Private\LaunchEngineLoop.cpp:3111`) ni fenêtre système
(`Runtime\ApplicationCore\Private\GenericPlatform\GenericWindow.cpp:29, 113`). La suite de la mécanique (texture
hors écran, pas de back buffer de 8×8) est décrite dans U7b-T1-CART § 4.1.
5. Le pilote attend toujours le processus `UnrealEditor` (`AnimPipeline.ps1:340-345`, inchangé). Le nom du processus ne
change pas avec l'option.
## 4. Preuve que l'usage interactif est inchangé
- **Appelants du .bat (établi)** : `grep -rn interaction_sandbox` sur l'addon (hors `Content`) et sur
`D:\Game\LifePlay-master 5.8\{Tools,README.md,Docs}` :
- `BuildMod.ps1:165` `& (Join-Path $LPSbRoot 'Tools\debug\LifePlay_interaction_sandbox.bat')`, **sans argument** ;
- `AnimPipeline.ps1:306/321/339` (le pilote) ;
- les docs `09_Commandes_Utiles.md:97` et `21_Sandbox_Addon.md:49`, qui décrivent un lancement manuel sans argument.
Aucun autre script n'appelle le .bat. `LifePlay_measure_sandbox.bat` et `LifePlay_pair_sandbox.bat` ne l'appellent pas
et ne sont pas modifiés.
- **Sans argument, `EXTRA` n'est pas défini** : `set "EXTRA="` efface la variable. Dans un .bat, une variable non définie
se développe en chaîne vide, donc `.bat:15` est identique à l'ancienne ligne 9. Une valeur `EXTRA` héritée de
l'environnement est aussi effacée (cas 6 ci-dessous).
- **Essai (établi, § 6.2)** : ligne de commande brute (`Environment.CommandLine`, c'est-à-dire `GetCommandLineW`)
reçue par un faux `UnrealEditor.exe`, en comparant l'ancien .bat (HEAD) et le nouveau :
- cas 1 (ancien, `& bat` comme `BuildMod.ps1:165`) = cas 2 (nouveau, même appel) : **identiques à l'octet** (`-ceq` → True) ;
- cas 5 (nouveau, `cmd /c "<bat>"`, comme un double-clic) = cas 1 : True ;
- cas 6 (nouveau, `EXTRA=-pollution` dans l'environnement, sans argument) = cas 1 : True ;
- cas 3 (ancien appel du pilote sur l'ancien .bat) = cas 1 : True, donc avant U7b le pilote lançait bien la même ligne
que l'usage interactif ;
- cas 4 (nouveau pilote) = cas 1 + ` -RenderOffScreen` : True ;
- cas 7 (deux arguments) = cas 1 + ` -RenderOffScreen -log` : True.
- Une première version (`… GameInstance" %*`) laissait une espace finale dans la ligne interactive : le cas 2 se terminait
par `GameInstance" ]` (écart constaté par le même essai). Je l'ai remplacée par la forme `EXTRA` avant de livrer. Ce
point est noté au § 7.
## 5. Garde-fou de `RetargetPack.py` (b)
### 5.1 Position (lignes après modification, `grep -n`)
- `RP:434` `CFG = load_config(...)` : lecture seule de `Saved\anim_retarget.json` (et de `anim_to_genesis_step.txt`), pour
le jeton (`RP:89`). Aucun appel d'asset dans `load_config` (`RP:81-112`).
- **`RP:436` `refuse_unported_engine()`**, définie en `RP:71-78`.
- Premier appel qui touche un asset après le garde-fou : `RP:451` `make_rig(...)`, qui exécute
**`RP:265` `delete_asset`** (première suppression) puis `RP:270` `create_asset` (première création). Viennent ensuite :
- `make_retargeter`, appelé en `RP:455` (`RP:287` suppression, `RP:288` création) ;
- `delete_copies`, appelé en `RP:461` (`RP:333`) ;
- `bake_named`, appelé en `RP:464`.
- Avant `RP:436`, il n'y a que des définitions et `NONE = unreal.Name()` au niveau du module. Aucun accès à un asset.
- Le garde-fou précède la bifurcation `mode == "rigs"` : il couvre donc les deux modes (`-Retarget bake` et `-Retarget rigs`).
### 5.2 Comment l'échec remonte (établi par lecture du code)
1. `Refusal` est interceptée par `main()` (`RP:476-478`). Le script écrit alors
`RETARGET echec : RetargetPack : API IK Retargeter 5.8 non portee (U7b-T2-CART, reporte apres V2.0) ; aucun asset modifie jeton <j>`,
puis appelle `quit_editor()`. Une ligne `RETARGET moteur <version>` la précède.
2. `BuildMod.ps1:74-76` `Find-RetargetLine` reconnaît la ligne comme un `Failure`. Ensuite, `BuildMod.ps1:139` lève
`throw "Conversion en echec : …"`, et `pwsh -File` se termine en erreur.
3. `AnimPipeline.ps1` : le `.cmd` de l'étape 4 se termine par `@exit /b %ERRORLEVEL%` (`:281`).
- `:284` lit le code de sortie.
- `:288` voit le contrôle « conversion : code 0 » en échec.
- `:287-290` ne trouve pas la ligne `RETARGET bridget n michael n` et lève `throw 'conversion non confirmee'`.
4. API moteur utilisée :
- `UKismetSystemLibrary::GetEngineVersion` : `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\Engine\Classes\Kismet\KismetSystemLibrary.h:213-215`
(5.5 : `:170`). Son implémentation est `FEngineVersion::Current().ToString()` (`…\Engine\Private\KismetSystemLibrary.cpp:268-271`).
- `ToString` vaut par défaut `EVersionComponent::Branch` (`Runtime\Core\Public\Misc\EngineVersion.h:34`). Le format
produit est `Major.Minor.Patch-CL+Branch` (`Runtime\Core\Private\Misc\EngineVersion.cpp:140-160`).
- Le nom Python `unreal.SystemLibrary` est celui que le script emploie déjà pour `quit_editor` (`RP:344`, `KismetSystemLibrary.h:656`).
- Version de l'installation : `Build.version`, 5.8.3 (5.5 : 5.5.4).
### 5.3 Correctif de portage non appliqué
Aucune des lignes du diff de U7b-T2-CART § 5 n'est présente :
`grep -c "run_batch_retarget\|add_retarget_op\|set_fk_chains" Tools/anim/RetargetPack.py` = 0.
## 6. Essais statiques (hors moteur, bloc-notes de session)
Dossier : `%USERPROFILE%\AppData\Local\Temp\claude\D--Game-LifePlay-master-5-5\ab8918a1-261f-45b4-904a-678ecfce98f7\scratchpad\u7bt4\`.
1. **Garde-fou** : `python -I -B test_guard.py`, avec le faux module `stub\unreal.py`. Tout appel d'asset y lève une
erreur. Le script importé est le vrai `RetargetPack.py` de l'addon. Résultat : `echecs : 0`, code 0.
- Versions `5.8.3-58210709+++UE5+Release-5.8`, `5.6.0-…`, `6.0.0-…` et `illisible` :
- journal = `RETARGET moteur <v>` puis `RETARGET echec : RetargetPack : API IK Retargeter 5.8 non portee (U7b-T2-CART, reporte apres V2.0) ; aucun asset modifie jeton abc123def456` ;
- appels = `get_engine_version` puis `quit_editor` seulement, soit **0 appel d'asset**.
- Version `5.5.4-40574608+++UE5+Release-5.5` : le garde-fou laisse passer. Le premier appel d'asset est
`load_asset /Game/Quaternius/UAL/UAL1_Standard…`, que le faux module fait échouer volontairement.
- Aucun `__pycache__` n'a été créé dans l'addon (`git status --short --ignored Tools/anim` : seulement les 2 `M`).
2. **Lanceur** : `pwsh -NoProfile -File sim\run_sim.ps1`.
- Faux `UnrealEditor.exe` compilé par `csc.exe` (.NET Framework 4, `/target:winexe`) : il écrit `Environment.CommandLine`.
- Copies de l'ancien et du nouveau .bat, dans `sim\dossier avec espace\`. Leur moteur par défaut est remplacé par un
chemin absent, et `LP_ENGINE_ROOT`/`LP_GAMEROOT` pointent vers des dossiers factices : aucun moteur réel ne pouvait partir.
- Les 7 cas et les 6 comparaisons sont au § 4 : toutes à True.
3. **Regex de BuildMod** : `pwsh -NoProfile -File test_find.ps1`. La fonction `Find-RetargetLine` est extraite de
`BuildMod.ps1` par l'AST (BuildMod n'est pas exécuté). Pour les modes `bake` et `rigs` : `Hit=[]` et
`Failure=[RETARGET echec : RetargetPack : … aucun asset modifie jeton abc123def456]`.
4. **Syntaxe** :
- `ast.parse(RetargetPack.py)` : OK ;
- `[Parser]::ParseFile(AnimPipeline.ps1)` : `erreurs de syntaxe : 0`.
## 7. Écarts à la fiche et à la SPEC
1. Le **.bat est modifié** (cas « si nécessaire » de la fiche). Il ne transmettait pas `%*`. Je l'ai ajouté à l'instantané
avant de le modifier, avec la commande de la fiche.
2. **Forme `EXTRA` au lieu d'un `%*` brut** en fin de ligne. Le `%*` brut ajoutait une espace finale à la ligne
interactive (constaté au § 6.2). La forme retenue donne une ligne identique à l'octet. Elle coûte 2 lignes de plus
que la suggestion de la fiche.
3. Le garde-fou **refuse aussi une version illisible**, par prudence : rien ne prouve alors un moteur antérieur à 5.6.
Il écrit en plus la ligne `RETARGET moteur <version>` avant le refus. Le message de refus est exactement celui de la
fiche, préfixé par `RETARGET echec : ` et suivi de ` jeton <j>` (format existant, `RP:476-478`).
4. « Ne rien lancer » : je n'ai lancé ni jeu, ni éditeur, ni pipeline. J'ai en revanche exécuté des essais statiques
hors moteur (§ 6) : le vrai script Python avec un faux module `unreal`, et les copies du .bat avec un faux exécutable.
C'est une lecture de la consigne, que je signale ici.
5. Erreur de lecture corrigée en cours de travail : un premier `grep -c $'\r$'` sous Git Bash avait donné « CRLF » pour
les 3 fichiers. Le comptage sur les octets montre que `AnimPipeline.ps1` et `RetargetPack.py` sont en **LF** et le .bat
en **CRLF**. Mon premier script de remplacement s'est donc arrêté sur `AnimPipeline.ps1` : 0 occurrence, aucune écriture.
Le .bat, déjà traité à ce moment, était en CRLF et l'est resté. Le second script détecte la fin de ligne de chaque
fichier. État final au § 1.
6. Docs non mises à jour (hors fichiers autorisés) : `D:\Game\LifePlay-master 5.8\Docs\Migration\20_Chaine_Animations.md`
(étape 5) et `21_Sandbox_Addon.md` (lanceurs, report U5 de RetargetPack). Elles relèvent de la phase DOC.
## 8. Incertitudes
- **Comportement réel de `-RenderOffScreen` en 5.8** : taille effective du viewport (avec `r.setres:1280x720` en
config), rythme des images, géométrie. Ce sont les risques (a) et (b) de U7b-T1-CART § 4.1. Ils restent **incertains**
jusqu'à l'essai du § 9.
- **Garde-fou dans l'éditeur 5.8 réel** : il a été essayé avec un faux module seulement. La chaîne `get_engine_version` →
`ToString` est établie par les sources, le comportement Python de bout en bout ne l'est pas. Essai sans risque
proposé pour la VAL :
- commande : `BuildMod.ps1 -GameRoot "D:\Game\LifePlay-master 5.8" -Retarget rigs -RetargetOnly -RetargetConfig <json>`,
avec `{"mode":"rigs","methode":"ue4","dest":"/Game/_TempU7b/Retarget"}` (mode `rigs` sur une destination temporaire :
même si le garde-fou ne jouait pas, ni référence ni clip ne seraient touchés) ;
- attendu : `Conversion en echec : RETARGET echec : RetargetPack : API IK Retargeter 5.8 non portee … jeton <j>`, et
`/Game/_TempU7b` absent.
- **Anomalie hors de mon périmètre (établi, à examiner avant l'essai T1)** : la copie de jeu 5.8 a des modifications qui
ne viennent pas de moi (`git -C "D:\Game\LifePlay-master 5.8" status --short` : `M .gitignore`, `M LifePlay.uproject`).
- `.gitignore` est modifié à 13:31:19, avant le début de cette fiche : bloc `# BEGIN HYPERAISTUDIO MANAGED RUNTIME IGNORE`.
- `LifePlay.uproject` est modifié à 13:36:27, soit 105 lignes : des plugins Éditeur `AIModuleToolset`,
`AnimationAssistantToolset`, `AutomationTestToolset`, `ConfigSettingsToolset`, `DataflowAgent`, etc. ont été activés.
- Aucune de mes commandes n'écrit dans ce dossier. La seule qui s'y place est `Snapshot.ps1` (`Set-Location`), et elle
copie seulement vers `Backup_5.5`.
- L'essai T1 lance ce `.uproject` : l'orchestrateur doit décider s'il garde ou restaure cet état avant l'essai.
Je n'y ai pas touché.
## 9. Essai T1 à confier à lp-builder (une tournée d'un clip, `AS_RightLegKick2`, pack freeanims)
Conditions préalables :
- pwsh 7, éditeur fermé ;
- build 5.8 du jeu à jour (aucun C++ modifié par cette fiche) ;
- DLL de l'addon vérifiées (lp-build § 8) ;
- anomalie du § 8 tranchée.
L'essai ne lance pas AnimPipeline. Il reproduit `Invoke-Sandbox` pour un seul clip, avec le même appel
`cmd /c` que `AnimPipeline.ps1:339`. La sortie va dans un dossier neuf, `Saved\U7b\T1essai_<date>`, pour ne pas écraser
les planches de U4-T6.
```powershell
$G = 'D:\Game\LifePlay-master 5.8'; $S = "$G\Saved"
$bat = 'D:\Game\LifePlay-Sandbox-5.8\Tools\debug\LifePlay_interaction_sandbox.bat'
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'; $rel = "U7b/T1essai_$stamp"; $W = "$S\U7b\T1essai_$stamp"
if (Get-Process UnrealEditor, UnrealEditor-Cmd -ErrorAction SilentlyContinue) { throw 'editeur ouvert' }
New-Item -ItemType Directory -Force $W | Out-Null
$utf8 = New-Object Text.UTF8Encoding $false
$origMode = if (Test-Path "$S\sandbox_mode.txt") { [IO.File]::ReadAllText("$S\sandbox_mode.txt") } else { $null } # 'Pair' le 2026-10-09
foreach ($f in 'sandbox_pack.ini', 'sandbox_tour.txt') { if (Test-Path "$S\$f") { Move-Item "$S\$f" "$W\$f.avant" } }
$ini = [IO.File]::ReadAllText("$S\AnimPipeline\freeanims\sandbox_pack.ini") -replace 'MeasureOutputRoot=.*', "MeasureOutputRoot=$rel/sandbox"
[IO.File]::WriteAllText("$S\sandbox_pack.ini", $ini, $utf8)
[IO.File]::WriteAllText("$S\sandbox_mode.txt", 'Mesure', $utf8)
[IO.File]::WriteAllText("$S\sandbox_tour.txt", 'vignettes planches quitter AS_RightLegKick2', $utf8)
try {
Start-Process -FilePath 'cmd.exe' -ArgumentList ('/c ""{0}" {1}"' -f $bat, '-RenderOffScreen') -WindowStyle Hidden | Out-Null
$d = (Get-Date).AddMinutes(3); while (-not (Get-Process UnrealEditor -ErrorAction SilentlyContinue) -and (Get-Date) -lt $d) { Start-Sleep 3 }
if (-not (Get-Process UnrealEditor -ErrorAction SilentlyContinue)) { throw 'sandbox non lancee (3 min)' }
$d = (Get-Date).AddMinutes(15); while ((Get-Process UnrealEditor -ErrorAction SilentlyContinue) -and (Get-Date) -lt $d) { Start-Sleep 5 }
if (Get-Process UnrealEditor -ErrorAction SilentlyContinue) { throw 'sandbox non fermee en 15 min (laissee ouverte)' }
}
finally {
foreach ($f in 'sandbox_pack.ini', 'sandbox_tour.txt', 'sandbox_mode.txt') { if (Test-Path "$S\$f") { Move-Item "$S\$f" "$W\$f" -Force } }
if ($null -ne $origMode) { [IO.File]::WriteAllText("$S\sandbox_mode.txt", $origMode, $utf8) }
foreach ($f in 'sandbox_pack.ini', 'sandbox_tour.txt') { if (Test-Path "$W\$f.avant") { Move-Item "$W\$f.avant" "$S\$f" } }
}
Copy-Item "$S\Logs\LifePlay.log" "$W\LifePlay.log"
Select-String -Path "$W\LifePlay.log" -Pattern 'Command Line:', 'VIGNETTE .*AS_RightLegKick2', 'PLANCHE .*AS_RightLegKick2', 'Assertion failed', 'out of bounds' | ForEach-Object { $_.Line }
Get-ChildItem "$W\sandbox" -Recurse -File | Select-Object FullName, Length
```
Attendus (référence U7b-T1-CART § 6.1, relue dans `D:\Game\LifePlay_Backup_5.5\U4-REF55\E4_freeanims.log:1939, 5460`
et `U4-T6\E4_freeanims.log:2277, 5806`) :
- ligne `LogInit: Command Line:` terminée par ` -RenderOffScreen` ;
- `VIGNETTE …/vignettes/AS_RightLegKick2.png (carre 695 px a 621,173)` ;
- `PLANCHE …/planches/AS_RightLegKick2.jpg 13 images 1.10 s <n> octets (carre 724 px)` (le nombre d'octets varie :
88113 en 5.5, 88496 en 5.8) ;
- 0 `Assertion failed`, 0 `out of bounds` ;
- fichiers `vignettes\AS_RightLegKick2.png` (512×512), `planches\AS_RightLegKick2.jpg` (3328×256 pour 13 images) et
`planches\AS_RightLegKick2.json`.
Le dossier de sortie est créé par la tournée (`MakeDirectory(…, true)`, `LPSandboxMeasureTour.cpp:421, 426, 432`).
Après l'essai, `Saved\sandbox_mode.txt` doit valoir à nouveau `Pair`. Variante pour un essai contraire : réduire une
fenêtre est sans objet, puisqu'il n'y a plus de fenêtre système.
## 10. État final
`git -C D:\Game\LifePlay-Sandbox-5.8 status --short` :
```
M Tools/anim/AnimPipeline.ps1
M Tools/anim/RetargetPack.py
M Tools/debug/LifePlay_interaction_sandbox.bat
```
- `git -C D:\Game\LifePlay-Sandbox status --short` (checkout 5.5) : 0 ligne.
- Fichiers écrits :
- les 3 fichiers ci-dessus ;
- `D:\Game\LifePlay_Backup_5.5\U7b-addon\manifest.csv` (+1 entrée) et `D:\Game\LifePlay_Backup_5.5\U7b-addon\Tools\debug\LifePlay_interaction_sandbox.bat` ;
- ce livrable ;
- les fichiers d'essai du bloc-notes de session (hors projet).
- Retour arrière proposé, non exécuté :
`pwsh -NoProfile -Command "Set-Location 'D:\Game\LifePlay-master 5.8'; .\Tools\Restore.ps1 -Lot U7b-addon -ProjectRoot 'D:\Game\LifePlay-Sandbox-5.8'"`,
ou `git -C D:\Game\LifePlay-Sandbox-5.8 checkout -- <fichier>`.