175 lines
16 KiB
Markdown
175 lines
16 KiB
Markdown
|
|
# Fiche U7-T5 — Niveau N3 — Modèle prévu lp-impl (Opus xhigh) | utilisé Opus 5.5 (claude-opus-5-5) — Agent lp-impl — Skills sk-protocol, ue-cpp-build, lp-build, lp-ue-compat
|
||
|
|
|
||
|
|
Garde D2 : un mod d'assets cuit pour une autre version du jeu ne fait plus planter le jeu. Copie
|
||
|
|
`D:\Game\LifePlay-master 5.8`, branche `ue5.8`. **Non compilé** (fiche BUILD séparée). Aucun commit.
|
||
|
|
|
||
|
|
## 1. Résultat
|
||
|
|
Implémentation faite dans les 7 fichiers autorisés, et aucun autre. Le contrat des mods ne change pas (format
|
||
|
|
`.lpmod`, clés, structure, gestionnaire). Statut : **proposé, non compilé**.
|
||
|
|
|
||
|
|
## 2. Modifications par fichier (numéros de ligne après modification)
|
||
|
|
|
||
|
|
| Fichier | Lignes | Modification |
|
||
|
|
|---|---|---|
|
||
|
|
| `Source/LifePlay/LPModInfoSubsystem.h` | 10-14 | En-tête : garde de version (égalité stricte, assets non chargés, fenêtre affichée que le module soit coché ou non, cas sans `MODULE_GAME_VERSION`) |
|
||
|
|
| idem | 38-44 | Ajout public C++ : `bool IsModuleGameVersionCompatible(const FString& Module, FString* OutBuiltFor = nullptr) const;` et `const FString& GetGameVersion() const` |
|
||
|
|
| idem | ex-37 | Retrait de la déclaration privée `IsModuleEnabled` (plus aucun appelant, voir § 7) |
|
||
|
|
| `Source/LifePlay/LPModInfoSubsystem.cpp` | 24-27 | Retrait de `FieldText` (espace anonyme), plus utilisé |
|
||
|
|
| idem | 89-98 | `IsModuleGameVersionCompatible` : `GetModuleMetadata(Module, "MODULE_GAME_VERSION")` ; vrai si vide, sinon `Built.Equals(GameVersion, ESearchCase::CaseSensitive)` |
|
||
|
|
| idem | 109-121 | `Tick` : condition `!IsModuleEnabled` retirée ; même prédicat que la garde (`IsModuleGameVersionCompatible`) |
|
||
|
|
| idem | 131-133 | Texte de la fenêtre : « The following mod(s) were built for another version … Their 3D assets (hair, animations...) are not loaded, so that the game does not crash. Update the mod, or ask its author for a version built for LifePlay %s. » |
|
||
|
|
| `Source/LifePlay/LPModDailies.h` | 14-15 | En-tête : plugin ignoré si module incompatible |
|
||
|
|
| `Source/LifePlay/LPModDailies.cpp` | 11 | `#include "LPModInfoSubsystem.h"` |
|
||
|
|
| idem | 47-52 | `Collection.InitializeDependency<ULPModInfoSubsystem>()` ; `Error` si nul |
|
||
|
|
| idem | 63-75 | Par plugin de type Mod, **avant** le relevé du registre et `ModClips.Add` (:93) : incompatible → `Warning` + `continue` ; sans version → `Warning` une fois |
|
||
|
|
| `Source/LifePlay/LPHairstyleSubsystem.h` | 29-31 | En-tête : fichiers d'un module incompatible ignorés dès le relevé |
|
||
|
|
| idem | 42 | `class ULPModInfoSubsystem;` |
|
||
|
|
| idem | 83-84 | `void ScanFiles(const ULPModInfoSubsystem* ModInfo);` |
|
||
|
|
| `Source/LifePlay/LPHairstyleSubsystem.cpp` | 13 | `#include "LPModInfoSubsystem.h"` |
|
||
|
|
| idem | 150-156 | `InitializeDependency<ULPModInfoSubsystem>()` puis `ScanFiles(ModInfo)` ; `Error` si nul |
|
||
|
|
| idem | 193 | signature `ScanFiles(const ULPModInfoSubsystem* ModInfo)` |
|
||
|
|
| idem | 236-237 | `TMap<FString, bool> Accepted`, `TSet<FString> Unversioned` (un verdict par module) |
|
||
|
|
| idem | 248-268 | Garde **avant lecture et `ParseFile`** : module incompatible → `Warning` une fois, aucun de ses `.lphair` lu |
|
||
|
|
| idem | 275-282 | Module sans version : `Warning` une fois, seulement s'il ajoute des coiffures hors base (`Defs`) |
|
||
|
|
| `Config/DefaultGame.ini` | 4 | `ProjectVersion=2026.10.1.6` → `ProjectVersion=2026.10.2.0` (seule ligne) |
|
||
|
|
|
||
|
|
Lignes de journal ajoutées (sans accents, style existant) :
|
||
|
|
- `LPModDailies : %s ignore, construit pour LifePlay %s, jeu %s` (Warning)
|
||
|
|
- `LPModDailies : %s sans MODULE_GAME_VERSION, assets charges sans controle de version` (Warning)
|
||
|
|
- `Coiffures : %s ignore, construit pour LifePlay %s, jeu %s` (Warning)
|
||
|
|
- `Coiffures : %s sans MODULE_GAME_VERSION, assets charges sans controle de version` (Warning)
|
||
|
|
- `LPModDailies : LPModInfoSubsystem absent, version des mods non controlee` / `Coiffures : …` (Error, défensif)
|
||
|
|
|
||
|
|
## 3. Diff et fins de ligne (vérifiés)
|
||
|
|
`git -C "D:\Game\LifePlay-master 5.8" diff --stat` :
|
||
|
|
```
|
||
|
|
Config/DefaultGame.ini | 2 +-
|
||
|
|
Source/LifePlay/LPHairstyleSubsystem.cpp | 41 +++++++++++++++++++++++--
|
||
|
|
Source/LifePlay/LPHairstyleSubsystem.h | 10 +++++--
|
||
|
|
Source/LifePlay/LPModDailies.cpp | 20 +++++++++++++
|
||
|
|
Source/LifePlay/LPModDailies.h | 2 ++
|
||
|
|
Source/LifePlay/LPModInfoSubsystem.cpp | 51 +++++++++-----------------------
|
||
|
|
Source/LifePlay/LPModInfoSubsystem.h | 16 ++++++++--
|
||
|
|
7 files changed, 97 insertions(+), 45 deletions(-)
|
||
|
|
```
|
||
|
|
`git status --short` : exactement ces 7 fichiers en `M`.
|
||
|
|
|
||
|
|
Fins de ligne (`grep -c ''` vs `grep -c $'\r'`, en-tête `od`) : tous en **CRLF** avant et après, sans BOM,
|
||
|
|
`.gitattributes` `* -text` :
|
||
|
|
|
||
|
|
| Fichier | lignes | CR | octets non ASCII (avant = après) |
|
||
|
|
|---|---|---|---|
|
||
|
|
| LPModInfoSubsystem.h | 58 | 58 | 1 = 1 (`§` ligne 1, préexistant) |
|
||
|
|
| LPModInfoSubsystem.cpp | 183 | 183 | 0 |
|
||
|
|
| LPModDailies.h | 61 | 61 | 0 |
|
||
|
|
| LPModDailies.cpp | 194 | 194 | 0 |
|
||
|
|
| LPHairstyleSubsystem.h | 116 | 116 | 1 = 1 (préexistant) |
|
||
|
|
| LPHairstyleSubsystem.cpp | 643 | 643 | 0 |
|
||
|
|
| Config/DefaultGame.ini | 81 | 81 | 0 |
|
||
|
|
|
||
|
|
## 4. API moteur lues (UE 5.8.3, `D:/Game/Launcher/Engine/UE_5.8/Engine/Source`)
|
||
|
|
- `FSubsystemCollectionBase::InitializeDependency<T>()` : `Runtime/Engine/Public/Subsystems/SubsystemCollection.h:35-45`.
|
||
|
|
Implémentation `Private/Subsystems/SubsystemCollection.cpp:366-394` → `AddAndInitializeSubsystem` (:478-509) →
|
||
|
|
`AddAndInitializeValidatedSubsystem` qui appelle `Subsystem->Initialize(*this)` (:526) **avant de rendre la main** ;
|
||
|
|
si le sous-système existe déjà, il est rendu tel quel (:508). Ordre garanti : **établi**. Existe aussi en 5.5
|
||
|
|
(`SubsystemCollection.h:33-42`).
|
||
|
|
- `UGeneralProjectSettings::ProjectVersion` : `Runtime/EngineSettings/Classes/GeneralProjectSettings.h:56`.
|
||
|
|
- `IPlugin::GetName` / `GetType`, `EPluginType::Mod`, `GetEnabledPluginsWithContent` :
|
||
|
|
`Runtime/Projects/Public/Interfaces/IPluginManager.h:121, 168, 45, 435` (mêmes en 5.5 : :121, :168, :45, :416).
|
||
|
|
- `FString::Equals(…, ESearchCase::CaseSensitive par défaut)` : `Runtime/Core/Public/Containers/UnrealString.h.inl:1271`.
|
||
|
|
`FString ==` **ignore la casse** (`UEOpEquals` → `Equals(Rhs, ESearchCase::IgnoreCase)`, `:912-915`) : d'où
|
||
|
|
`Equals(…, CaseSensitive)` pour l'égalité stricte (décision b).
|
||
|
|
|
||
|
|
## 5. Points de chargement d'assets de mods et couverture
|
||
|
|
|
||
|
|
| # | Point | Couverture | Niveau |
|
||
|
|
|---|---|---|---|
|
||
|
|
| P1 | `ULPModDailiesSubsystem::Tick`, `Clip.LoadSynchronous()` (`LPModDailies.cpp:139`, ex-:119) : le plantage de T4 | Clips enregistrés seulement à :93-94 ; plugin incompatible écarté à :63-70, **avant** le registre : ni `TSoftObjectPtr` ni clé de mod. Sans clip, le ticker n'est même pas créé (:101-104). | établi (code) ; plus de plantage : estimé (T4 rejoué) |
|
||
|
|
| P2 | `ScanPathsSynchronous` (éditeur, `WITH_EDITOR`) | Ignoré pour un plugin incompatible (même `continue`). Ce n'est pas un chargement de paquet. | établi |
|
||
|
|
| P3 | `Person::PlayDaily` → `IsModDaily` → `Room.Dailies` | Clés absentes de `GModKeys` : « cle inconnue », rien n'est joué. | établi (code) |
|
||
|
|
| P4 | Table `GI.HairstyleTable` (`SkeletalMeshPath`) → Blueprint `GI.getSkeletalMeshFromPath` → `AssetRegistryHelpers::GetAsset` (chargement synchrone ; relevé `etat/L5-diag_dump55_GI_getSkeletalMeshFromPath.txt:4,9`) | Les `.lphair` d'un module incompatible ne sont jamais lus (`LPHairstyleSubsystem.cpp:248-268`) : pas de `Defs`, donc pas d'entrée de table (:525-568). | établi (code C++) ; usage par les Blueprints : estimé |
|
||
|
|
| P5 | `FIT_MORPH` → `AddMorph` (:566) | Uniquement pour une coiffure ajoutée : couvert par P4. | établi |
|
||
|
|
| P6 | `MODE: BASE` / `MORPHABLE` (base, :364) | Le fichier d'un module incompatible n'est pas lu : il ne peut pas devenir la base. Repli sur la base suivante (vin_Base) ou sur le Blueprint. | établi |
|
||
|
|
| P7 | Pilosités (`GI.FHairstyles`, `BHairstyles`, `BHairstyles_M`) → `Person.setBHairstyle` | Noms non lus (même garde). Les textures sont toujours sous les dossiers `/Game/...` imposés (`TextureLists`, :33-37). | établi |
|
||
|
|
| P8 | Requêtes `GetAssetByObjectPath` (:536, :632), `FPackageName::DoesPackageExist` (:538) | Ces requêtes interrogent le registre ou le magasin de paquets, sans charger d'asset. Elles ne sont de toute façon plus atteintes pour un module incompatible. | établi (non atteint) |
|
||
|
|
| P9 | Montage du conteneur IoStore et lecture de `AssetRegistry.bin` du mod par le moteur | Non couvert, et pas nécessaire : T4 montre le montage et le registre lus sans plantage (`T4_LifePlay.log:239-244`, :929). | établi (T4) |
|
||
|
|
| P10 | **Sauvegardes** : personnage dont `SMBool.SkeletalMeshPath` vise une coiffure de mod (Person → `GI.getSkeletalMeshFromPath`, `etat/L6f_dump_Person.txt:3660-3670`) | **Non couvert** : le chemin vient de la structure sauvegardée, pas de la table. Il faudrait un Blueprint ou un fichier hors liste. | estimé (non testé) |
|
||
|
|
| P11 | Bibliothèque de shaders d'un mod de matériaux, ouverte par le moteur au montage | Non couvert (moteur). T4 (mod d'animations) n'a rien montré. | incertain |
|
||
|
|
| P12 | Autres chargements C++ : `LPDirectControlSubsystem.cpp:961` (racine `/Game/Quaternius/UAL`), `LPSettingsSubsystem.cpp:417` (chemins fixes), `LPDataCompat.cpp:277` (ville), `LPShowCoupleSubsystem.cpp:178` (paire d'un clip déjà joué) | Aucun ne vise un chemin `/<mod>/` : soit un chemin fixe du jeu, soit le voisin d'un clip déjà chargé (P1 bloque en amont). | établi (lecture) ; ShowCouple : estimé |
|
||
|
|
|
||
|
|
Essais qui trancheraient P10 et P11 : rejouer T4 avec `lpr_hair_pocolov-1.0-for-LifePlay-2026.10.4` installé,
|
||
|
|
(a) nouvelle partie (P11), puis (b) Continuer une sauvegarde où un personnage porte une coiffure Pocolov (P10).
|
||
|
|
Pistes pour P10, **non appliquées** (décision de l'orchestrateur, fichiers hors liste) :
|
||
|
|
- garde dans le helper C++ `ULPAssetRegistryHelpers::GetAssetByObjectPath` (`Source/LifePlay/LPAssetRegistryHelpers.cpp:10-40`,
|
||
|
|
appelé par `getSkeletalMeshFromPath`) : rendre un `FAssetData` invalide pour un chemin `/<module incompatible>/` ;
|
||
|
|
- ou démonter le conteneur d'un module incompatible au démarrage.
|
||
|
|
|
||
|
|
## 6. Journal attendu
|
||
|
|
Ordre : avec `InitializeDependency`, la ligne `Modules : N .lpmod lu(s)…` passe **avant** la première ligne
|
||
|
|
`Coiffures :` et avant `LPModDailies` (dans T4 elle venait après : `T4_LifePlay.log:921-931`). `Preload ville` suit
|
||
|
|
cette ligne, donc il est demandé plus tôt (sans effet attendu).
|
||
|
|
|
||
|
|
**T4 rejoué** (jeu 2026.10.2.0, `lpr_anim_quaternius` 2026.10.1.6, coché, puis décoché) :
|
||
|
|
```
|
||
|
|
Display: Modules : 17 .lpmod lu(s), 1 avec MODULE_GAME_VERSION (jeu 2026.10.2.0)
|
||
|
|
Display: Preload ville : demande envoyee
|
||
|
|
Display: Coiffures : 1 fichier(s) .lphair, base vin_Base/Hairstyles/vin_Base.lphair (126 coiffure(s), 89 morph(s), 44 pilosite(s)), modules : 0 coiffure(s), 0 pilosite(s)
|
||
|
|
Display: Modules : lpr_anim_quaternius actif par defaut (MODULE_DEFAULT_ENABLED), ajoute a la liste des mods
|
||
|
|
Warning: LPModDailies : lpr_anim_quaternius ignore, construit pour LifePlay 2026.10.1.6, jeu 2026.10.2.0
|
||
|
|
Display: Jeu LifePlay 2026.10.2.0 | moteur 5.8.3-… | configuration Development
|
||
|
|
… (écran titre, viewport prêt, ≤ 2 s)
|
||
|
|
Warning: Modules : lpr_anim_quaternius construit pour LifePlay 2026.10.1.6, jeu 2026.10.2.0
|
||
|
|
```
|
||
|
|
Fenêtre « Mod version warning » : `- Quaternius Animations (lpr_anim_quaternius) : built for LifePlay 2026.10.1.6`.
|
||
|
|
Attendus **absents** : `LPModDailies : lpr_anim_quaternius, 84 clip(s)…`, `… cle(s) de mod`, `… clip(s) ajoute(s) a …Dailies`,
|
||
|
|
`FlushAsyncLoading` suivi de `Fatal error`. Cas décoché (b) : mêmes lignes, et la fenêtre s'affiche aussi (décision c).
|
||
|
|
Variante avec Pocolov 2026.10.4 : `Warning: Coiffures : lpr_hair_pocolov ignore, construit pour LifePlay 2026.10.4, jeu 2026.10.2.0`,
|
||
|
|
`Warning: LPModDailies : lpr_hair_pocolov ignore, …` (Pocolov est un plugin de type Mod), et une deuxième ligne dans la fenêtre.
|
||
|
|
|
||
|
|
**T2** (3 mods recuits, `MODULE_GAME_VERSION: 2026.10.2.0`) :
|
||
|
|
```
|
||
|
|
Display: Modules : 19 .lpmod lu(s), 3 avec MODULE_GAME_VERSION (jeu 2026.10.2.0)
|
||
|
|
Display: Coiffures : 13 fichier(s) .lphair, base vin_Base/Hairstyles/vin_Base.lphair (126 coiffure(s), 89 morph(s), 44 pilosite(s)), modules : 12 coiffure(s), 0 pilosite(s)
|
||
|
|
Display: Modules : lpr_… actif par defaut (MODULE_DEFAULT_ENABLED), ajoute a la liste des mods (x3 au premier démarrage)
|
||
|
|
Log: LPModDailies : lpr_anim_quaternius, 84 clip(s) sous /lpr_anim_quaternius/Anims
|
||
|
|
Log: LPModDailies : lpr_anim_freeanims, N clip(s) sous /lpr_anim_freeanims/Anims
|
||
|
|
Log: LPModDailies : K cle(s) de mod : …
|
||
|
|
Log: LPModDailies : M clip(s) ajoute(s) a <Room>.Dailies (…)
|
||
|
|
```
|
||
|
|
Aucune ligne `ignore, construit pour`, aucune `sans MODULE_GAME_VERSION`, pas de fenêtre. Les comptes 19 / 13 / 12 /
|
||
|
|
84 sont **estimés** : 16 `vin_*` + 3 mods, 12 `.lphair` dans l'archive Pocolov, 84 clips vus dans T4, si la recuisson
|
||
|
|
garde le même jeu de clips. N, K et M sont à relever.
|
||
|
|
|
||
|
|
## 7. Écarts à la fiche / à la SPEC
|
||
|
|
1. `ULPModInfoSubsystem::IsModuleEnabled` (privé) et `FieldText` (espace anonyme) sont **retirés**. Après le retrait de
|
||
|
|
la condition demandé par la décision (c), ils n'avaient plus d'appelant. Garder `FieldText` sans usage risquait
|
||
|
|
C4505. Aucun autre usage dans le jeu ni dans l'addon (grep : seule occurrence, l'ancien code). `#include "UObject/UnrealType.h"` est laissé en place (inutilisé, sans effet).
|
||
|
|
2. Getter public `GetGameVersion()` ajouté (« ou équivalent clair ») pour les lignes de journal des deux sous-systèmes.
|
||
|
|
3. Texte de la fenêtre : la phrase « You can disable it in the Mod Manager. » est **supprimée**. Décocher le module ne
|
||
|
|
change plus rien à ses assets, et la fenêtre s'affiche maintenant aussi pour un module décoché. La phrase aurait été
|
||
|
|
trompeuse.
|
||
|
|
4. Avertissement « sans MODULE_GAME_VERSION » dans LPHairstyle : émis seulement pour un module qui **ajoute des
|
||
|
|
coiffures hors base** (`Defs`). Ni la base ni les pilosités ne le déclenchent (assets `/Game` par construction).
|
||
|
|
Conséquence : `vin_Base` (base seule) n'écrit rien, conformément à « pas les 16 mods texte ». C'est mon
|
||
|
|
interprétation de « définitions de coiffures ».
|
||
|
|
5. LPModDailies : l'avertissement « sans version » vise **tout plugin de type Mod** avec contenu, même sans clip sous
|
||
|
|
`/Anims` (lecture littérale de la décision). Un mod de coiffures sans version écrit donc deux lignes
|
||
|
|
(`LPModDailies :` et `Coiffures :`).
|
||
|
|
6. Le prédicat de la fenêtre passe de `==` (insensible à la casse) à `Equals(…, CaseSensitive)`, le même que la garde,
|
||
|
|
pour que fenêtre et filtrage ne divergent jamais. Pour des versions numériques, aucune différence.
|
||
|
|
7. `MODULE_DEFAULT_ENABLED` d'un module incompatible : toujours coché d'office dans `GI.ModEnableds`
|
||
|
|
(`EnsureDefaultEnabledModules`, inchangé : contrat). Ses assets ne sont pas chargés pour autant.
|
||
|
|
8. `InitializeDependency` qui rendrait nul (impossible tant que `ULPModInfoSubsystem` ne surcharge pas
|
||
|
|
`ShouldCreateSubsystem`) : ligne `Error` et comportement d'avant, sans contrôle.
|
||
|
|
|
||
|
|
## 8. Incertitudes et points à confirmer
|
||
|
|
- Compilation 5.8 non faite (fiche BUILD) : aucune API nouvelle hors § 4. Pas de nouveau symbole dans un espace de noms
|
||
|
|
anonyme, donc pas de risque C2374 en unity build. Pas de masquage de variable (membres relus).
|
||
|
|
- Effet réel sur le plantage : estimé. Preuve = T4 rejoué en build Development 5.8 empaqueté (§ 6).
|
||
|
|
- P10 (sauvegardes) et P11 (shaders d'un mod de matériaux) : non couverts ; essais au § 5.
|
||
|
|
- Le code ne vit que dans la copie 5.8. Il compilerait aussi en 5.5 (mêmes API, § 4), mais ce n'est pas demandé.
|
||
|
|
|
||
|
|
Suite : l'orchestrateur doit lancer la fiche BUILD (lp-builder, `Tools/Build.ps1 -Lot U7`), puis T4 rejoué et T2 en
|
||
|
|
build Development empaqueté. Il doit aussi décider de P10. État : 7 fichiers modifiés dans la copie 5.8, non compilés,
|
||
|
|
non commités.
|