LifePlay-Revival/Docs/Migration/etat/U58/U7/U7-T9-IMPL.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

175 lines
16 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.

# Fiche U7-T9 — Niveau N3 — Modèle prévu lp-impl (Opus xhigh) | utilisé Opus 5.5 (claude-opus-5-5) — Agent lp-impl — Skills ue-cpp-build, sk-protocol (lp-build, lp-ue-compat préchargés)
Marquage, dans le gestionnaire de mods, des lignes de mods refusés par la garde U7 (copie `D:\Game\LifePlay-master 5.8`,
branche `ue5.8`). Décision utilisateur : C++ seulement, aucun Blueprint modifié.
Statut : **proposé, non compilé** (BUILD = fiche séparée). Ni jeu, ni éditeur, ni commandlet, ni compilation lancés. Aucun commit.
## 1. Résultat
PASS-STATIC proposé (à recompter par la VAL). Critère d'arrêt (§ 9) non atteint : la ligne est identifiable sans
Blueprint, et la reconstruction du widget ne rend pas le marquage instable (§ 3).
- Ligne d'un mod refusé : nom suffixé « (not loaded) », ligne teintée rouge atténué, infobulle « Not loaded: <raisons
anglaises de la fenêtre> », case `ModEnabledBox` désactivée. `GI.ModEnableds` n'est pas touché (choix du joueur conservé).
- Idempotent : marque reconnue à la teinte de la ligne, suffixe protégé par `EndsWith` ; tri et images suivantes sans effet.
- Une ligne Display par ouverture : `Modules : gestionnaire, N mod(s) refuse(s) marque(s)`.
- Aucun mod refusé : aucun ticker ajouté (coût nul).
## 2. Widget et propriétés (lecture seule des relevés, aucun outil lancé)
| Élément | Preuve | Niveau |
|---|---|---|
| Gestionnaire `/Game/UI/ModManager` (classe `ModManager_C`), liste `ModScrollBox` (ScrollBox, variable) | `etat/L6e-CX-B09_widgets_apres.txt:14811`, `:14838` ; nom de classe `ModManager_C` : `Source/LifePlay/LPDirectControlSubsystem.cpp:53` | établi |
| Ligne `/Game/UI/ModuleTick` : `ModEnabledBox` (CheckBox), `HoverButton` (Button) contenant `NameText` et `AuthorText` (TextBlock, variables), `UpButton`, `DownButton` | `etat/L6e-CX-B09_widgets_apres.txt:14934`, `:14945`, `:14955`, `:14972`, `:14985`, `:14998`, `:15014` | établi |
| Création à chaque ouverture : `GI.CreateModManagerWidget` → `CreateWidget` → `AddToViewport` ; fermeture `RemoveFromParent` | `etat/DOC-MOD_LPDumpFunction_20261007-083554.txt:1484`, `:1489`, `:1508` ; bouton titre → même événement : `Source/LifePlayEditorTools/LPTitleModManagerCommandlet.cpp:295-296` | établi |
| Par `.lpmod` : `CreateWidget` ModuleTick, `FullString` = fichier, `UniqueID` = ligne 1 après `:` (Trim, TrimTrailing) = `MODULE_UNIQUEID`, `CheckIfModAlreadyFound`, `NameText.SetText` (ligne 2), `AuthorText.SetText` (ligne 3), ajout à `TempModuleTicks` | `083554.txt:1561`, `:1573`, `:1579`, `:1639`, `:1644`, `:3413` | établi |
| Lignes ajoutées en bloc après la boucle (`AddTempModuleTicks`, dans l'ordre de `ModEnableds`) | `083554.txt:3437`, `:3482` | établi |
| Tri (▲/▼, `SortLoadOrder`) : `Array_Swap(ModEnableds)`, `ModScrollBox.ClearChildren`, `AddTempModuleTicks` = **mêmes objets** remis | `083554.txt:3497`, `:3501` | établi |
| `CheckIfModAlreadyFound` fixe la variable `ModEnabled` de la ligne depuis `ModEnableds` (aucun `SetIsChecked`) | `etat/L6f_dump_GI.txt:153`, `:179` | établi |
| ModuleTick, un seul graphe (EventGraph, 48 nœuds) : case → `ModEnabled` → `GI.ToggleMod(UniqueID)` ; un 2ᵉ événement inverse `ModEnabled` et appelle `ToggleMod` | `etat/DOC-MOD_LPDumpFunction_20261007-083154.txt:9-12` ; `083338.txt:141`, `:165`, `:182`, `:191`, `:211` | établi |
| Ce 2ᵉ événement = `HoverButton.OnClicked` ; la case affiche `ModEnabled` par une liaison de propriété (pas de graphe de liaison) | déduit : seul bouton restant hors ▲/▼ et survol ; aucun `SetIsChecked` dans le graphe | estimé |
| Pocolov : dossier `lpr_hair_pocolov`, `MODULE_UNIQUEID: lpr_hair_pocolov`, `MODULE_NAME: Pocolov Hairstyles`, `MODULE_AUTHOR: LifePlay Revival (hairstyles by Pocolov)` | `D:\Game\LifePlay_Build_5.8_U7_P10\Windows\LifePlay\Content\Modules\lpr_hair_pocolov\lpr_hair_pocolov.lpmod:1-3` | établi |
| Pocolov refusé et coché par défaut | `...\P10\Windows\LifePlay\Saved\Logs\LifePlay.log:827` (refus), `:887` (« actif par defaut (MODULE_DEFAULT_ENABLED), ajoute a la liste des mods ») | établi |
## 3. Mécanisme retenu, stabilité
- Ticker `FTSTicker` à chaque image (`0.f`, comme `LPSettingsSubsystem.cpp:437`), **ajouté seulement si au moins un mod est
refusé**. Recherche par `TObjectIterator<UScrollBox>` (requête par classe, `UObjectIterator.h:251-265`) de `ModScrollBox`
dont le propriétaire est un `ModManager*` **au viewport** (`UWidget::IsInViewport`) de cette instance de jeu : une
instance fermée (en attente de GC) est écartée.
- Pour chaque enfant `ModuleTick*` : variable Blueprint `UniqueID` lue par réflexion (`FStrProperty`), recherchée dans une
table `RefusedTips` construite à l'initialisation depuis `FLPModGuard::GetVerdicts()` (clés : nom du plugin **et**
`MODULE_UNIQUEID` du `.lpmod` ; `FString` : égalité et hachage sans casse, `UnrealString.h.inl:910-912`, `:2129-2132`,
comme le `EqualEqual_StriStri` du Blueprint).
- Stabilité : les marques sont des propriétés UMG des objets (`Text`, `ColorAndOpacity`, `bIsEnabled`, `ToolTipText`),
réappliquées à toute reconstruction Slate (`UserWidget.cpp:406-422`, `Widget.cpp:321-333`, `:526-536`,
`TextBlock.cpp:469-478`). Tri = mêmes objets → marques conservées ; réouverture = nouveaux objets → marqués à la
première image. Le Blueprint n'écrit `NameText` qu'à la création (avant l'ajout à la liste) : pas d'écrasement constaté.
- Le marquage a lieu entre deux images (ticker), jamais au milieu de l'exécution Blueprint qui remplit la liste.
## 4. API moteur lues (UE 5.8.3, `D:\Game\Launcher\Engine\UE_5.8\Engine\Source`)
| API | Source |
|---|---|
| `UWidget::IsInViewport` (vrai si ajouté par `AddToViewport`) | `Runtime/UMG/Public/Components/Widget.h:547-549`, `Private/Components/Widget.cpp:344-354` ; `AddToViewport` → sous-système `UserWidget.cpp:1366-1378`, drapeau `Blueprint/GameViewportSubsystem.cpp:158`, `:214` |
| `UWidget::SetIsEnabled`, `SetToolTipText`, `GetGameInstance` | `Widget.h:545`, `:556`, `:871` ; `Widget.cpp:321-333`, `:526-536` |
| `UUserWidget::SetColorAndOpacity` (« affects all child widgets »), `GetColorAndOpacity`, `GetWidgetFromName` | `Blueprint/UserWidget.h:1050-1061`, `:1426` ; `UserWidget.cpp:427-441`, `:1259-1262` ; réappliqué `:406-422` |
| `UTextBlock::GetText`, `SetText` | `Components/TextBlock.h:95`, `:103` ; `TextBlock.cpp:457-478` |
| `UPanelWidget::GetChildrenCount`, `GetChildAt` | `Components/PanelWidget.h:28`, `:36` |
| Infobulle : chemin sous le curseur **y compris widgets désactivés**, de la feuille vers la racine, 1ʳᵉ infobulle non vide | `Runtime/Slate/Private/Framework/Application/SlateUser.cpp:1350-1386` |
| `FStrProperty` : en-tête `UObject/StrProperty.h` (inclus par `UnrealType.h` seulement si `UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_7`, `UnrealType.h:7806-7816` ; la copie 5.8 est en `Unreal5_8`, `Source/LifePlay.Target.cs:12`) ; `GetPropertyValue_InContainer` | `CoreUObject/Public/UObject/StrProperty.h:1-19`, `StrProperty.h.inl:27-29`, `UnrealType.h:1663` ; existe aussi en 5.5 (`UE_5.5/.../UObject/StrProperty.h`) |
| `FName` comparé à `const TCHAR*` | `Core/Public/UObject/NameTypes.h:1362`, `:1413-1417` (précédent du fichier : `RowNames[0]`) |
| `constexpr FLinearColor`, `operator==` | `Core/Public/Math/Color.h:130`, `:332` ; précédent moteur `Developer/LogVisualizer/Private/SVisualLoggerFilterWidget.h:133` |
| `FTSTicker::RemoveTicker` sans effet sur une poignée vide | `Core/Private/Containers/Ticker.cpp:39-58` (`Handle.Pin()` nul) |
## 5. Modifications par fichier (numéros après modification)
| Fichier (EOL) | Lignes | Modification |
|---|---|---|
| `Source/LifePlay/LPModInfoSubsystem.h` (LF) | 17-21 | En-tête : 4ᵉ rôle du sous-système (gestionnaire de mods, aucun Blueprint modifié) |
| idem | 31 | `class UScrollBox;` |
| idem | 60-61 | `bool TickModManager(float DeltaTime);` |
| idem | 71-77 | `RefusedTips`, `ManagerTickHandle`, `ManagerList` (faible), `bManagerListLogged`, `bWarnedNoUniqueId` |
| `Source/LifePlay/LPModInfoSubsystem.cpp` (LF) | 25 | `#include "UObject/StrProperty.h"` |
| idem | 84-118 | `LPModInfoUi` : noms `ModScrollBox` / `ModManager` / `ModuleTick`, suffixe, teinte `RefusedTint (0.75, 0.28, 0.28, 0.9)`, `MarkRefusedRow` (teinte, infobulle, suffixe si absent, case désactivée ; faux si déjà marquée) |
| idem | 151-172 | `Initialize` : table `RefusedTips` (raisons `FLPModVerdict::Reasons` jointes par « ; »), ticker par image seulement si la table n'est pas vide |
| idem | 178 | `Deinitialize` : retrait du ticker |
| idem | 318-383 | `TickModManager` : liste à l'écran, lignes, `UniqueID`, marquage, Warning unique si `UniqueID` absent, ligne Display |
Rien d'autre : `LPSettingsSubsystem.cpp`, `LPModGuard.*`, Blueprints, Build.cs inchangés (UMG et CoreUObject déjà en
dépendance). Correctifs UE4 (`LPStringCompat`, `LPMeshCompat`, `LPNavigationHelpers`…) non touchés.
## 6. Diff, fins de ligne, contrôles statiques
T9 seul (état T7 copié avant modification dans
`%USERPROFILE%\AppData\Local\Temp\claude\D--Game-LifePlay-master-5-5\ab8918a1-261f-45b4-904a-678ecfce98f7\scratchpad\t9\avant\`,
diffs `..\T9_LPModInfoSubsystem.cpp.diff` 165 lignes, `..\T9_LPModInfoSubsystem.h.diff` 47 lignes) :
```
git diff --no-index --stat <avant>/LPModInfoSubsystem.cpp Source/LifePlay/LPModInfoSubsystem.cpp 1 file changed, 128 insertions(+)
git diff --no-index --stat <avant>/LPModInfoSubsystem.h Source/LifePlay/LPModInfoSubsystem.h 1 file changed, 16 insertions(+), 1 deletion(-)
```
`git -C "D:\Game\LifePlay-master 5.8" diff --stat` (cumul U7 contre HEAD) :
```
Config/DefaultGame.ini | 2 +-
Source/LifePlay/LPHairstyleSubsystem.cpp | 13 ++
Source/LifePlay/LPHairstyleSubsystem.h | 2 +
Source/LifePlay/LPModDailies.cpp | 7 +
Source/LifePlay/LPModDailies.h | 2 +
Source/LifePlay/LPModInfoSubsystem.cpp | 332 ++++++++++++++++++++++++++-----
Source/LifePlay/LPModInfoSubsystem.h | 36 +++-
Source/LifePlay/LPSettingsSubsystem.cpp | 5 +
Source/LifePlay/LifePlay.cpp | 15 +-
9 files changed, 361 insertions(+), 53 deletions(-)
```
(avant T9 : `LPModInfoSubsystem.cpp | 208`, `.h | 21`, total 220+/55− ; seules ces deux lignes changent.)
`git status --short` : 9 `M` + 2 `??` (`LPModGuard.*`, T7), inchangé en nombre ; copie 5.5 : vide ; addon 5.8 : `M Tools/BuildMod.ps1` (T7).
Fins de ligne relues octet par octet (Python, `count(b'\n')`, `count(b'\r\n')`, octets > 127) :
| Fichier | Avant T9 | Après T9 | Non-ASCII | BOM | Fin de fichier |
|---|---|---|---|---|---|
| `LPModInfoSubsystem.cpp` | LF 314 / CRLF 0 | LF 442 / CRLF 0 | 0 = 0 | non | LF |
| `LPModInfoSubsystem.h` | LF 63 / CRLF 0 | LF 78 / CRLF 0 | 2 = 2 (`§` l. 1) | non | LF |
Noms nouveaux uniques dans `Source/` (`grep -rln "MarkRefusedRow\|TickModManager\|RefusedTint\|NotLoadedSuffix\|ManagerListName"` →
les 2 fichiers seulement) ; tout dans l'espace de noms nommé `LPModInfoUi` (pas de collision unity).
## 7. Journal attendu (jeu cuit, Pocolov 5.5 dans `Mods/`)
Démarrage, inchangé (T7) : `Modules : lpr_hair_pocolov refuse : moteur : conteneur 4 (jeu 5), registre 17 (jeu 24) ; …`,
puis `… non charge : 1 pak(s) demonte(s) …`, puis la fenêtre `Modules : fenetre d'avertissement, 1 mod(s) refuse(s)`.
Nouveau, **une fois par ouverture** du gestionnaire :
```
LogLifePlay: Display: Modules : gestionnaire, 1 mod(s) refuse(s) marque(s)
```
Aucune ligne au tri (▲/▼) ni aux images suivantes. Ne doivent pas apparaître :
`Modules : gestionnaire, variable UniqueID absente de ModuleTick_C …` (Warning : lignes non marquées) ; `… 0 mod(s) …`
alors que Pocolov est dans la liste (clé `UniqueID` non reconnue). Sans mod refusé : aucune ligne « gestionnaire ».
## 8. Plan d'essai (après BUILD par lp-builder et empaquetage ; jeu de référence `LifePlay_Build_5.8_U7_P10`)
Le jeu P10 est empaqueté : le code T9 n'y est pas. Compilation `.\Tools\Build.ps1 -Lot U7`, puis empaquetage Development
(`Tools\BuildDebug.ps1`) vers un dossier neuf (ex. `LifePlay_Build_5.8_U7_P11`) avec le même `Mods\lpr_hair_pocolov`
(cuit 5.5) et le même `Content\Modules\lpr_hair_pocolov`. Sauvegardes du joueur mises de côté comme en P10.
1. Lancer ; fenêtre « Mod compatibility warning » → OK (non-régression T7).
2. Écran titre → Mod Manager. Attendu : « Pocolov Hairstyles (not loaded) », ligne teintée rouge atténué, case grisée
dans l'état choisi (cochée ici), auteur inchangé ; les autres lignes inchangées. Capture même cadrage que la capture
utilisateur.
3. Survol de la ligne (nom ou case) : infobulle `Not loaded: built with an older engine (format 4, game 5): rebuild the mod
for LifePlay 2026.10.2.0` ; le panneau de détail montre toujours le `.lpmod` (survol Blueprint intact).
4. Clic sur la case : sans effet. Clic sur le **nom** : noter si la coche change (point ouvert § 9.6).
5. ▲/▼ sur Pocolov puis sur une autre ligne : marque conservée, pas de double suffixe, aucune nouvelle ligne de journal.
6. Confirm, rouvrir : marque présente dès l'ouverture, une nouvelle ligne Display, pas de double suffixe.
7. Relancer le jeu : Pocolov toujours dans la liste avec le choix laissé (ModEnableds non modifié par le C++).
8. Journal : compter `Modules : gestionnaire` (= nombre d'ouvertures) ; aucun Warning « UniqueID absente ».
9. Non-régression sans mod refusé : `Mods\lpr_hair_pocolov` déplacé (vers `Backup_5.5\U7`) → gestionnaire normal, aucune
ligne « gestionnaire ». Ne pas lever le contrôle Moteur avec Pocolov 5.5 (plantage T4).
## 9. Écarts à la fiche et points ouverts
1. Identification : `FLPModGuard::GetVerdicts()` au lieu de `IsRefused(<id>)` (même source, donne aussi les raisons de
l'infobulle) ; clé double nom du plugin + `MODULE_UNIQUEID` (la ligne porte `MODULE_UNIQUEID`, la garde le nom du plugin ;
égaux pour Pocolov, établi § 2).
2. Infobulle : préfixe « Not loaded: » devant les raisons jointes par « ; » (repli « see the mod compatibility warning »
si la liste était vide, cas non attendu).
3. Teinte appliquée à toute la ligne (`UUserWidget::SetColorAndOpacity` : texte, case, flèches), pas seulement au texte.
4. Ligne de journal écrite aussi avec N = 0 à l'ouverture (diagnostic) ; Warning unique ajouté si `UniqueID` manque.
5. Ticker dédié par image, enregistré seulement s'il y a un mod refusé ; le ticker 0,5 s existant est inchangé.
6. **Point ouvert** : le clic sur le nom (`HoverButton`, estimé § 2) appelle toujours `GI.ToggleMod` : le joueur peut encore
changer son choix enregistré, et la case désactivée suivrait la liaison. Rien n'est chargé pour autant (garde au
démarrage). Options si l'orchestrateur veut l'empêcher : (a) laisser ainsi (choix du joueur, recommandé) ;
(b) `HoverButton->SetIsEnabled(false)` (perte probable du survol qui affiche le `.lpmod`, à vérifier) ;
(c) retirer le délégué Blueprint `OnClicked` de la ligne (intrusif). Non fait sans décision.
7. Observation hors périmètre : `LPHairstyleSubsystem` ajoute un mod refusé à la liste des mods actifs par défaut
(`MODULE_DEFAULT_ENABLED`, journal P10 l. 887) : c'est pourquoi Pocolov apparaît coché.
## 10. Compte rendu (sk-protocol)
[sk-protocol] Skills présents : sk-protocol, ue-cpp-build, lp-build, lp-ue-compat.
1. **Résultat** : réussi (PASS-STATIC proposé, non compilé) ; 2 fichiers modifiés, aucun Blueprint.
2. **Preuves** : § 2 (relevés `etat/`), § 4 (sources moteur 5.8 `fichier:ligne`).
3. **Chiffres** : T9 = +128 (`.cpp`), +16/−1 (`.h`) (`git diff --no-index --stat`) ; EOL LF conservées (442 et 78 lignes, 0 CRLF).
4. **Incertitudes** : identité du 2ᵉ événement (`HoverButton`) et liaison de la case : estimées ; rendu (teinte, infobulle) :
à voir en jeu (§ 8) ; compilation non faite.
5. **Écarts** : § 9 ; fichiers écrits : `D:\Game\LifePlay-master 5.8\Source\LifePlay\LPModInfoSubsystem.cpp`,
`D:\Game\LifePlay-master 5.8\Source\LifePlay\LPModInfoSubsystem.h`, ce livrable.
6. Suite : lp-builder doit compiler la copie 5.8 (`.\Tools\Build.ps1 -Lot U7`) puis empaqueter pour l'essai § 8, état : code T9 écrit, non compilé, non commité.