LifePlay-Revival/Docs/Migration/etat/U58/U7/U7-T5-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

16 KiB

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.