175 lines
16 KiB
Markdown
175 lines
16 KiB
Markdown
# 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é.
|