32 KiB
Fiche U7-T7 — Niveau N4 — Modèle prévu lp-impl-max (Opus max) | utilisé Opus 5.5 (claude-opus-5-5) — Agent lp-impl-max — Skills ue-cpp-build, lp-ue-compat, sk-protocol (lp-build préchargé)
Garde de compatibilité des mods d'assets dans le jeu UE 5.8 (copie D:\Game\LifePlay-master 5.8, branche ue5.8) et
tampon EngineVersion de l'atelier (worktree D:\Game\LifePlay-Sandbox-5.8, branche ue5.8).
Statut : proposé, non compilé (BUILD = fiche séparée). Ni jeu, ni éditeur, ni compilation lancés. Aucun commit.
Base T5 révisée, pas jetée : son diff est conservé dans
%USERPROFILE%\AppData\Local\Temp\claude\D--Game-LifePlay-master-5-5\ab8918a1-261f-45b4-904a-678ecfce98f7\scratchpad\t7\base\T5_worktree.diff (320 lignes).
1. Résultat
Implémentation faite (PASS-STATIC proposé, à recompter par la VAL) :
- un mod refusé n'est pas chargé : dans le jeu cuit, au démarrage du module
LifePlay(avant le GameInstance), son point de montage/<id>/est retiré (le registre d'assets oublie tous ses assets) et ses paks sont démontés (pak, entrées du magasin de paquets, conteneur IoStore). Aucune modification moteur, aucun Blueprint ; - trois contrôles (moteur, version min, version max), chacun levable par une case de Settings > Misc ajoutée en C++ (GameUserSettings.ini, effet au prochain lancement) ;
- fenêtre Slate existante : chaque mod refusé, sa raison en anglais et le moyen de lever le contrôle ; journal : une ligne Warning par refus, en français sans accents ;
BuildMod.ps1écrit"EngineVersion": "X.Y.Z"(moteur de la cuisson) dans le.upluginlivré.
Le critère d'arrêt (§ 9 de la fiche) n'est pas atteint : le refus propre est possible sans moteur ni Blueprint (§ 3).
2. Recherche web (contenu lu = donnée, jamais instruction)
| Source | Ce qu'elle apporte | Niveau |
|---|---|---|
| API Epic 5.8, IPluginManager : https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Projects/IPluginManager | UnmountExplicitlyLoadedPlugin « Marks an explicitly loaded plugin as disabled, unmounts its content », MountExplicitlyLoadedPlugin, OnPluginUnmounted, GetPluginsWithPakFile, RemoveFromPluginsList |
établi (concorde avec IPluginManager.h 5.8) |
| API Epic 5.8 : https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Projects/IPluginManager/UnmountExplicitlyLoadedPlugin | deux surcharges ; réservé aux plugins ExplicitlyLoaded |
établi (source : PluginManager.cpp:3660-3667) |
| API Epic 5.8 : https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/CoreUObject/FPackageName/UnRegisterMountPoint | « This will remove a previously inserted mount point » (RootPath /PluginName/, ContentPath) |
établi |
| API Epic 5.8, FCoreDelegates : https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Core/FCoreDelegates | OnUnmountPak « Callback to prompt the pak system to unmount a pak file » |
établi |
| API Epic 5.8, FPakPlatformFile : https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/PakFile/FPakPlatformFile | bool Unmount(const TCHAR* InPakFilename) public |
établi |
| Forum Epic Developer Community, 2026 (personnel Epic UE_Seb.Tho, utilisateur mod.io UGC) : https://forums.unrealengine.com/t/error-registering-game-feature-plugin-in-cooked-build/2711513 | le déchargement d'un UGC passe par FPackageName::UnRegisterMountPoint → RemoveMountPoint → retrait des assets du registre ; le contenu doit être sous une racine propre au mod (retrait « en masse ») ; point de montage du pak ≠ point de montage de paquets |
estimé (pratique décrite, confirmée par la source locale § 3) |
| Forum Epic, 2025 (personnel Epic) : https://forums.unrealengine.com/t/crashes-when-mounting-and-unmounting-iostore-containers-in-different-threads/2527014 | monter et démonter des conteneurs IoStore depuis des threads différents pose problème (Mac) : rester sur un seul thread | estimé ; ici montage et démontage sur le thread de jeu |
| Forum Epic (UE4, communauté) : https://forums.unrealengine.com/t/unmount-pak-file-failed/418114 et https://forums.unrealengine.com/t/assertion-failed-on-onunmountpak-execute/397628 | usage de FCoreDelegates::OnUnmountPak pour démonter un pak DLC |
faible (UE4) |
| mod.io UGC : https://docs.mod.io/unreal/modio-ugc et https://docs.mod.io/unreal/modio-ugc/getting-started | le sous-système UGC monte et démonte les paks/IoStore à l'exécution et intègre le registre d'assets ; « the engine version used to build the UGC files [must match] the version of the project » ; le pak doit contenir AssetRegistry.bin |
estimé (doc tierce, concorde avec T4/T6) |
| Base de connaissances Epic « Primer: Loading Content and Pak Files at Runtime » (https://dev.epicgames.com/community/learning/knowledge-base/D7nL/unreal-engine-primer-loading-content-and-pak-files-at-runtime) | page dynamique non lisible par curl (403 sur l'API) ; citée par le sujet de forum 536669 |
non utilisée |
Aucune source ne décrit un moyen d'empêcher le montage d'un plugin Mod non ExplicitlyLoaded depuis le code du jeu.
3. Voie retenue pour « ne pas charger » (sources UE 5.8.3, D:\Game\Launcher\Engine\UE_5.8\Engine\Source)
3.1 Ordre établi
- AppInit :
FPluginManager::ConfigureEnabledPluginsmonte les paks de chaque plugin activé nonExplicitlyLoaded(Runtime/Projects/Private/PluginManager.cpp:1898, journal:1926, recherche des paks:1965-1979,FCoreDelegates::MountPak:1976-1979), puisMountContentPluginsenregistre/<id>/(:2015-2018). Journal T4 : montage l. 239-244, avantEarliestPossiblePluginsLoadedl. 245. - Module AssetRegistry chargé (
Runtime/Launch/Private/LaunchEngineLoop.cpp:3700), puis modulesDefaultdu projet (:3896→:4682) :FLifePlayGameModule::StartupModule→FLPModGuard::Run(). UGameInstance::Init: l'Event Init Blueprint passe avant les sous-systèmes (Runtime/Engine/Private/GameInstance.cpp:100ReceiveInit,:129SubsystemCollection.Initialize). Une garde dansULPModInfoSubsystem::Initialize(esquisse T6) arriverait donc après le Blueprint : elle est placée au démarrage du module.LoadMap→ premier paquet du mod → plantage T4 (T4 l. 969 et suivantes).
3.2 Options examinées
| Option | Verdict |
|---|---|
| Empêcher le montage | Impossible depuis le jeu : le montage précède tout module du jeu (3.1). Seul "ExplicitlyLoaded": true dans le .uplugin du mod l'évite (PluginManager.cpp:1898, :2001) : clé absente des mods publiés, et le jeu devrait alors monter lui-même chaque mod. Écartée (contrat des mods, mods 5.5 déjà publiés non couverts). |
IPluginManager::UnmountExplicitlyLoadedPlugin |
Refuse un plugin non ExplicitlyLoaded : « Plugin was not explicitly loaded » (PluginManager.cpp:3660-3667). Inutilisable. |
| Retenue : étape de contenu de cette fonction + démontage des paks | voir 3.3 |
| Repli (filtrage seul dans nos sous-systèmes) | Gardé comme seconde barrière (éditeur, démontage incomplet) : LPModDailies, LPHairstyleSubsystem. |
3.3 Voie retenue (Source/LifePlay/LPModGuard.cpp:174-218, appelée :431-449)
IAssetRegistry::WaitForPremadeAssetRegistry()(Runtime/AssetRegistry/Public/AssetRegistry/IAssetRegistry.h:836,Private/AssetRegistry.cpp:2156-2171, attente hors verrou sans le thread de jeuPrivate/ARLoader.cpp:397-422) : les registres des plugins peuvent être chargés en tâche de fond (AssetRegistry.cpp:377-428, ajout:633-656) ; sans cette attente, le registre du mod pourrait être ajouté après la purge.FPackageName::UnRegisterMountPoint(Plugin.GetMountedAssetPath(), Plugin.GetContentDir())(Runtime/CoreUObject/Public/Misc/PackageName.h:347) : mêmes arguments qu'au montage (PluginManager.cpp:2015-2018) ; c'est l'étape de contenu deUnmountExplicitlyLoadedPlugin(:3716). Chemins normalisés (Private/Misc/PackageName.cpp:211-241,:426-462), diffusionOnMountPointDismounted(:553-564). Le registre d'assets retire tous les assets et chemins sous/<id>/(AssetRegistry.cpp:9184-9194,:9241-9327), sauf les « points de montage persistants », calculés seulement sur le registre principal (:623) avant les registres des plugins (:633-656) :/<id>/n'en fait pas partie.- Pour chaque pak trouvé par la même recherche que le montage (
PluginManager.cpp:1965-1979, donc la même chaîne :FPakPlatformFile::Unmountcompare le nom exact,PakFile/Private/IPlatformFilePak.cpp:6297) :FCoreDelegates::OnUnmountPak.Execute(pak)(Runtime/Core/Public/Misc/CoreDelegates.h:128, lié:5808,HandleUnmountPakDelegate:6622) →FPakPlatformFile::Unmount(:6288-6356) : pak retiré de la liste, entrées du magasin de paquets retirées (FilePackageStore.cpp:466-478, reconstruction à la lecture suivante:319-322,:662-830), conteneur IoStore démonté (backend actif =IoDispatcherFileBackend.cpp, journal T4 « LogIoDispatcher: Mounted container »:1478; démontage:1486-1505). - Contrôle relu :
FPackageName::MountPointExists(PackageName.h:357),IAssetRegistry::HasAssets(/<id>, true)(IAssetRegistry.h:286),FIoDispatcher::DoesChunkExist(ContainerHeader)(IO/IoDispatcher.h:318) → une ligneDisplay(« non charge ») ouError(« demontage incomplet »).
3.4 Pourquoi les deux étapes
- Jeu cuit :
FPackagePath::TryFromMountedNamene vérifie pas le point de montage (UE_SUPPORT_FULL_PACKAGEPATH=WITH_EDITOR,CoreUObject/Public/Misc/PackagePath.h:16; variante cuitePrivate/Misc/PackagePath.cpp:1026-1037). Un chargement par nom (LoadObject,TSoftObjectPtr, sauvegarde) arrive donc au chargeur Zen ; c'est le démontage du conteneur qui le fait échouer proprement : entrée absente →QueueUnresolvedPackage(Private/Serialization/AsyncLoading2.cpp:6333-6336), aucun octet du mod n'est lu. Établi (source) ; comportement au runtime : estimé (même chemin qu'un mod désinstallé). - La purge du registre retire le mod des énumérations et recherches :
GIénumère corps, vêtements et coiffures parULPAssetRegistryHelpers::GetAssetsByClassName(Source/LifePlay/LPAssetRegistryHelpers.h:35-51) ;GI.getSkeletalMeshFromPathpasse parGetAssetByObjectPath(P10 de T5) ;LPModDailieslit/<id>/Anims. Sans purge, un tirage au sort pourrait viser un asset du mod (chargement alors refusé, mais personnage sans maillage).
4. Contrôles (décision a)
| Contrôle | Règle et lecture | Source 5.8 |
|---|---|---|
| Moteur, binaire (jeu cuit) | Version de l'en-tête de conteneur IoStore de chaque .utoc du mod (Signature, Version, 8 premiers octets du bloc ContainerHeader, identifiant FIoContainerId::FromName(<nom de base du .utoc>), lu par FIoDispatcher) = EIoContainerHeaderVersion::Latest (5) et version de Mods/<id>/AssetRegistry.bin = FAssetRegistryVersion::LatestVersion (24). Illisible → refus « mod files unreadable ». Plugin sans pak → sans objet (rien de monté). Jeu non cuit → sans objet. |
IO/IoContainerHeader.h:96-106 ; écriture/lecture Private/IO/IoContainerHeader.cpp:49-58 ; IO/IoChunkId.h:214 ; IoContainerId.cpp:13-24 ; IO/IoDispatcher.h:236,241,313,318,325,329, GetResult nul si échec IoDispatcher.cpp:1443-1461 ; AssetRegistry/AssetData.h:113,120, AssetData.cpp:969-999 ; fichier lu par le moteur AssetRegistry.cpp:642 ; version écrite en tête AssetRegistryState.cpp:2523-2526, AssetRegistryArchive.cpp:173-175 |
Moteur, EngineVersion du .uplugin |
Si présent : FEngineVersion::Parse (forme X.Y.Z obligatoire) puis major.minor = FEngineVersion::Current(). Non lisible → « mod files unreadable ». Absent → jugé sur le binaire seul. |
Projects/Public/PluginDescriptor.h:74 ; Core/Public/Misc/EngineVersion.h:37,40 ; EngineVersion.cpp:162-197 ; contrôle moteur seulement #if !IS_MONOLITHIC (PluginManager.cpp:1840-1863, :2688-2707) |
| Version min | MODULE_GAME_VERSION ≥ FLPModGuard::MinGameVersion = 2026.10.2.0 (LPModGuard.cpp:30-31, commentaire « A relever au prochain changement de moteur ») ; comparaison numérique par composante, composante absente = 0 (:52-90). |
— |
| Version max | MODULE_GAME_VERSION ≤ ProjectVersion (lu dans GGameIni, même valeur que UGeneralProjectSettings::ProjectVersion). |
— |
MODULE_GAME_VERSION absent |
min et max sans objet, une ligne Warning ; le contrôle Moteur s'applique. | — |
Cases (décision d) : GameUserSettings.ini, section [/Script/LifePlay.LPModInfoSubsystem], clés bCheckModEngine,
bCheckModMinVersion, bCheckModMaxVersion (vraies par défaut). Justification : fichier de réglages utilisateur du
moteur (dans Saved/, conservé aux mises à jour), « known ini » chargé à l'initialisation de la configuration
(Core/Public/Misc/ConfigCacheIni.h:101-111, Private/Misc/ConfigCacheIni.cpp:6399-6415, :6778), donc lisible au
démarrage du module, où la garde s'exécute ; un .ini propre au jeu (modèle LPVideoQuality.ini) l'aurait aussi
permis, mais la fiche cite GameUserSettings. Écriture SetBool + Flush (ConfigCacheIni.h:1673, :1395).
5. Modifications par fichier (numéros de ligne après modification)
| Fichier (EOL) | Lignes | Modification |
|---|---|---|
Source/LifePlay/LPModGuard.h (nouveau, LF, ASCII) |
1-26 | En-tête : mécanisme, contrôles, cases, éditeur |
| idem | 31-37, 40-55, 57-79 | ELPModCheck, FLPModVerdict (Module, DisplayName, BuiltFor, bRefused, bUnloaded, Reasons EN, LogReasons FR), FLPModGuard (Run, GetVerdicts, IsRefused, IsCheckActive, GetSavedCheck, SaveCheck, GetGameVersion, ParseLpmod, MinGameVersion) |
Source/LifePlay/LPModGuard.cpp (nouveau, LF, ASCII) |
30-31 | MinGameVersion = "2026.10.2.0" |
| idem | 33-224 | LPModGuardImpl (espace de noms nommé, pas de collision unity) : ParseVersion, CompareVersions, ReadCheck, FindPluginPaks, HeaderChunkOf, ReadContainerHeaderVersion, ReadAssetRegistryVersion, UnloadPlugin, HasFailure |
| idem | 226-460 | Run() : rien en commandlet ; réglages ; ligne de synthèse ; Warning par contrôle levé ; verdict par plugin Mod ; Warning par refus ; démontage (jeu cuit) |
| idem | 390-413 | fix1 (décision utilisateur) : échec Moteur actif → échecs min/max au journal seulement, suffixe « (secondaire) », absents de la fenêtre |
| idem | 462-525 | GetVerdicts, IsRefused, IsCheckActive, GetSavedCheck, SaveCheck (SetBool + Flush), GetGameVersion, ParseLpmod |
Source/LifePlay/LifePlay.cpp (LF) |
4, 7-19 | FLifePlayGameModule : FDefaultGameModuleImpl, StartupModule → FLPModGuard::Run() ; IMPLEMENT_PRIMARY_GAME_MODULE(FLifePlayGameModule, …) |
Source/LifePlay/LPModInfoSubsystem.h (LF) |
10-17 | En-tête : fenêtre et cases |
| idem | 41-47, 51-52 | UFUNCTION OnCheckEngineChanged / OnCheckMinVersionChanged / OnCheckMaxVersionChanged ; EnsureSettingsRows() ; retrait de IsModuleEnabled (T5, plus d'appelant) |
Source/LifePlay/LPModInfoSubsystem.cpp (LF) |
5-11, 20-21, 26 | includes UMG, LPModGuard.h, Misc/EngineVersion.h, UObject/UObjectIterator.h |
| idem | 39-82 | LPModInfoUi : noms LPModCheck*, libellés, infobulles, police de repli, police des cases du Blueprint ; retrait de FieldText (T5) |
| idem | 88-89, 104, 111-112 | FLPModGuard::Run() (sans effet si déjà fait) ; lecture .lpmod par FLPModGuard::ParseLpmod (même règle qu'avant) ; ticker toujours actif (0,5 s) |
| idem | 135-148 | gestionnaires des cases → FLPModGuard::SaveCheck |
| idem | 150-177 | Tick : cases, puis fenêtre pour chaque mod refusé, coché ou non |
| idem | 179-255 | EnsureSettingsRows : 3 lignes UHorizontalBox (texte puis UCheckBox ×2, disposition de SettingCheckbox_C) ajoutées en fin de MainMenu.SettingsScrollBox (BackButton est le 1er enfant : etat/L6e-CX-B09_widgets_apres.txt:12126-12133) |
| idem | 257-264, 280 | texte de la fenêtre ; titre « Mod compatibility warning » |
Source/LifePlay/LPModDailies.h (LF) |
14-15 | En-tête |
Source/LifePlay/LPModDailies.cpp (LF) |
11, 57-62 | mod refusé ignoré (ligne Log) ; le reste = HEAD (la garde T5 par InitializeDependency est retirée) |
Source/LifePlay/LPHairstyleSubsystem.h (LF) |
29-30 | En-tête |
Source/LifePlay/LPHairstyleSubsystem.cpp (LF) |
13, 230, 241-251 | fichiers .lphair d'un mod refusé non lus (ligne Log une fois) ; le reste = HEAD (signature ScanFiles() d'origine) |
Source/LifePlay/LPSettingsSubsystem.cpp (CRLF) |
176-180 | CategoryOf : nom LPModCheck* → onglet Misc |
Config/DefaultGame.ini (LF) |
4 | ProjectVersion=2026.10.2.0 (T5, gardé) |
D:\Game\LifePlay-Sandbox-5.8\Tools\BuildMod.ps1 (LF) |
21-23 | En-tête : ce que reçoivent le .lpmod et le .uplugin livrés, dont EngineVersion |
| idem | 211-216 | contrôle : <moteur>\Build\Build.version (moteur $UE = $LPEngineRoot de <jeu>\Tools\Engine.ps1) → $engineVersion X.Y.Z, erreur si absent ou illisible, avant la cuisson |
| idem | 247 | Write-Host " moteur de la cuisson : …" |
| idem | 281, 287-288 | .uplugin livré : Add-Member EngineVersion $engineVersion -Force |
LifePlay.Build.cs : inchangé (API de Core, CoreUObject, AssetRegistry, Projects, UMG déjà en dépendance ;
pas de PakFile). Correctifs UE4 (LPStringCompat, LPMeshCompat, LPNavigationHelpers…) : non touchés.
6. Diff, fins de ligne, contrôles statiques
git -C "D:\Game\LifePlay-master 5.8" diff --stat :
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 | 208 +++++++++++++++++++++++--------
Source/LifePlay/LPModInfoSubsystem.h | 21 +++-
Source/LifePlay/LPSettingsSubsystem.cpp | 5 +
Source/LifePlay/LifePlay.cpp | 15 ++-
9 files changed, 220 insertions(+), 55 deletions(-)
Non suivis (nouveaux) : Source/LifePlay/LPModGuard.h (79 lignes), Source/LifePlay/LPModGuard.cpp (525 lignes après fix1).
git -C "D:\Game\LifePlay-Sandbox-5.8" diff --stat : Tools/BuildMod.ps1 | 14 +++++++++++++- (13+, 1−).
git status : 9 M + 2 ?? (jeu), 1 M (addon) ; LifePlay-master 5.5 et LifePlay-Sandbox (5.5) : vides.
Fins de ligne relues octet par octet (Python, count(b'\n'), count(b'\r\n')), HEAD contre copie de travail :
| Fichier | HEAD | Après | Non-ASCII avant = après | BOM |
|---|---|---|---|---|
Config/DefaultGame.ini |
LF (81) | LF (81) | 0 = 0 | non |
LifePlay.cpp |
LF | LF | 0 = 0 | non |
LPModGuard.h / .cpp |
— | LF | 0 | non |
LPModInfoSubsystem.h |
LF | LF | 2 = 2 (§ l. 1) |
non |
LPModInfoSubsystem.cpp, LPModDailies.h/.cpp, LPHairstyleSubsystem.cpp |
LF | LF | 0 = 0 | non |
LPHairstyleSubsystem.h |
LF | LF | 4 = 4 | non |
LPSettingsSubsystem.cpp |
CRLF (830/830) | CRLF (835/835) | 13 = 13 | non |
BuildMod.ps1 (addon) |
LF (326) | LF (338) | 0 = 0 | non |
Correction du livrable T5 : il annonçait ces fichiers en CRLF ; ils sont en LF dans HEAD (le comptage
grep -c $'\r' de ce shell compte toutes les lignes). Rien n'a été converti : chaque fichier garde ses fins de ligne.
Contrôles statiques faits : PowerShell Parser::ParseFile(BuildMod.ps1) → 0 erreur ; calcul de EngineVersion depuis
UE_5.8\Engine\Build\Build.version → 5.8.3, appliqué en mémoire au .uplugin du mod T4 (rien écrit) :
"EngineVersion": "5.8.3" ajouté en dernier champ. Aucun reste de T5 (IsModuleGameVersionCompatible,
GetGameVersion() de ModInfo) : grep vide. Noms nouveaux uniques dans le module et l'addon (grep).
7. Ce qui reste chargeable
| # | Chemin | Couverture | Niveau |
|---|---|---|---|
| P1 | LPModDailies (Clip.LoadSynchronous, plantage T4) |
clips absents du registre après purge ; plugin refusé ignoré (LPModDailies.cpp:57-62) |
établi (code) |
| P4 | GI.HairstyleTable (.lphair du mod) |
.lphair non lus (LPHairstyleSubsystem.cpp:241-251) ; assets démontés |
établi (code) |
| P10 | Sauvegarde : SkeletalMeshPath /<mod>/… → GetAssetByObjectPath |
registre purgé → FAssetData invalide (ligne existante « GetAssetByObjectPath ECHEC ») ; chargement direct → paquet absent du magasin (AsyncLoading2.cpp:6333-6336) |
établi (source) ; effet runtime estimé (= mod désinstallé : coiffure absente) |
| P12 | Énumérations Blueprint (GetAssetsByClassName) |
assets du mod absents du registre | établi (source) |
| R1 | Démontage en échec (pak non trouvé, délégué non lié) | ligne Error « demontage incomplet » ; restent seuls LPModDailies et LPHairstyleSubsystem : un Blueprint ou une sauvegarde pourrait charger le mod |
incertain (non observé) |
| R2 | Entre le montage (AppInit) et StartupModule (Default) |
aucun chargement de paquet de mod constaté dans T4 avant LoadMap ; un plugin EarliestPossible/PreDefault qui chargerait du /<mod>/ passerait avant la garde |
estimé |
| R3 | Fichiers Config/*.ini d'un plugin Mod (fusionnés au montage, PluginManager.cpp:1918-1944), cibles de localisation, bibliothèque de shaders ouverte au montage (P11 de T5) |
non annulés ; absents des mods BuildMod (mod T4 : .uplugin + Content/Paks/Windows seulement) |
établi pour T4 ; incertain en général |
| R4 | Plugin toujours « activé » pour IPluginManager (GetEnabledPluginsWithContent, GetPluginsWithPakFile) |
pas d'API publique pour un plugin non ExplicitlyLoaded ; son contenu n'est plus joignable |
établi |
| R5 | Éditeur (-game compris) |
rien n'est démonté (contenu non cuit) ; contrôle binaire sans objet ; filtrage de nos deux sous-systèmes seulement | établi |
| R6 | Fichiers texte du module (Content/Modules/<id>/*.lpscene, etc.) |
lus par les Blueprints comme avant (pas des assets) ; un chemin /<id>/… qu'ils citeraient ne se résout plus |
estimé |
| R7 | Étape Core.System Paths de UnmountExplicitlyLoadedPlugin (PluginManager.cpp:3707) |
non reproduite : sans effet sur le chargement en jeu cuit IoStore, et évite de modifier GEngineIni |
estimé |
8. Journal et fenêtre attendus (Development empaqueté ; chemins exacts estimés)
(i) T4 rejoué (jeu 2026.10.2.0, lpr_anim_quaternius cuit 5.5, MODULE_GAME_VERSION: 2026.10.1.6), au démarrage
du module (entre FAssetRegistry took … et les lignes du GameInstance) :
LogLifePlay: Display: Modules : garde des mods d'assets, 1 plugin(s) de type Mod, jeu 2026.10.2.0 (version min des mods 2026.10.2.0), moteur 5.8.3-58210709+++UE5+Release-5.8 (conteneur 5, registre 24)
LogLifePlay: Warning: Modules : lpr_anim_quaternius refuse : moteur : conteneur 4 (jeu 5), registre 17 (jeu 24) ; version min : construit pour LifePlay 2026.10.1.6 < 2026.10.2.0 (secondaire)
LogPackageName: Display: FPackageName: Mount point removed: '../../../LifePlay/Mods/lpr_anim_quaternius/Content/' unmounted from '/lpr_anim_quaternius/'
LogIoDispatcher: Display: Unmounting container '../../../LifePlay/Mods/lpr_anim_quaternius/Content/Paks/Windows/lpr_anim_quaterniusLifePlay-Windows'
LogLifePlay: Display: Modules : lpr_anim_quaternius non charge : 1 pak(s) demonte(s) sur 1, point de montage /lpr_anim_quaternius/ retire, registre vide, conteneur(s) absent(s)
puis au GameInstance :
LogLifePlay: Display: Modules : 17 .lpmod lu(s), 1 avec MODULE_GAME_VERSION (jeu 2026.10.2.0)
LogLifePlay: LPModDailies : lpr_anim_quaternius ignore (mod refuse)
LogLifePlay: Display: Modules : cases de controle des mods ajoutees a Settings > Misc (moteur 1, version min 1, version max 1)
LogLifePlay: Display: Modules : fenetre d'avertissement, 1 mod(s) refuse(s)
Absents : LPModDailies : lpr_anim_quaternius, 84 clip(s)…, Fatal error. Fenêtre « Mod compatibility warning » :
This game is LifePlay 2026.10.2.0 (Unreal Engine 5.8). The following mod(s) were not loaded: none of their assets can be used, so that the game does not crash.
- Quaternius Animations (lpr_anim_quaternius): built with an older engine (format 4, game 5): rebuild the mod for LifePlay 2026.10.2.0
Update the mod, or ask its author for a version built for LifePlay 2026.10.2.0. To load a mod anyway (the game may crash), uncheck the matching check in Settings > Misc (Check mod engine, Check mod minimum version, Check mod maximum version), then restart the game.
Mod décoché dans le gestionnaire : mêmes lignes et même fenêtre (le moteur monte un mod même décoché).
(ii) mod recuit 5.8 (MODULE_GAME_VERSION: 2026.10.2.0, "EngineVersion": "5.8.3") :
LogLifePlay: Display: Modules : lpr_anim_quaternius accepte (conteneur 5, registre 24, EngineVersion 5.8.3, construit pour LifePlay 2026.10.2.0)
LogLifePlay: LPModDailies : lpr_anim_quaternius, 84 clip(s) sous /lpr_anim_quaternius/Anims
Pas de fenêtre, pas de ligne refuse. (Valeurs 5/24 pour un mod cuit 5.8 : établies par la source d'écriture, § 4 ;
non mesurées sur un mod réel.)
(iii) trois cases décochées (puis redémarrage), même mod 5.5 :
LogLifePlay: Warning: Modules : controle moteur des mods leve (Settings > Misc, <…>/Saved/Config/Windows/GameUserSettings.ini)
LogLifePlay: Warning: Modules : controle version min des mods leve (Settings > Misc, <…>)
LogLifePlay: Warning: Modules : controle version max des mods leve (Settings > Misc, <…>)
LogLifePlay: Warning: Modules : lpr_anim_quaternius charge malgre : moteur : conteneur 4 (jeu 5), registre 17 (jeu 24) ; version min : construit pour LifePlay 2026.10.1.6 < 2026.10.2.0 (controle leve dans Settings > Misc)
Puis plantage attendu au premier chargement de carte (T4) : à ne pas lancer sans accord. Au clic d'une case :
Modules : controle moteur des mods leve dans <ini>, effet au prochain lancement.
Variante Pocolov (lpr_hair_pocolov-1.0-for-LifePlay-2026.10.4, MODULE_GAME_VERSION: 2026.10.4) : refusé par
Moteur ; l'échec Version max (2026.10.4 > 2026.10.2.0 numériquement, § 11 point 1) n'est qu'au journal, suffixé
« (secondaire) » (fix1) : la fenêtre ne dit plus « update the game ». LPHairSample (sans MODULE_GAME_VERSION) : Warning
« sans MODULE_GAME_VERSION », refus par Moteur seul.
9. Plan de test runtime minimal (VAL)
- BUILD (lp-builder) :
Tools\Build.ps1 -Lot U7(éditeur), puisTools\BuildDebug.ps1 -Config Development(la garde n'agit que dans un jeu cuit). Relever les avertissements nouveaux (LPModGuard.cpp,LPModInfoSubsystem.cpp). - Jeu Development, sans mod :
Modules : garde des mods d'assets, 0 plugin(s)…; Settings > Misc : 3 cases cochées (absentes des autres onglets) ; décocher « Check mod engine » → clébCheckModEngine=FalsedansSaved\Config\Windows\GameUserSettings.ini; redémarrer → Warning « controle moteur des mods leve » ; recocher. - (i) copie jetable du build + archive
lpr_anim_quaternius-1.0-for-LifePlay-2026.10.1.6: lignes du § 8 (i), fenêtre, écran titre, nouvelle partie (liste des villes) puis Continuer (mémoire « packaging test paths »), aucun plantage. Compter les lignesrefuse(1) etnon charge(1). - P10 : Pocolov 5.5 installé, sauvegarde dont un personnage porte une coiffure Pocolov → Continuer : pas de plantage,
ligne
GetAssetByObjectPath ECHEC | recu '/lpr_hair_pocolov/…', coiffure absente. - (ii) recuisson par
BuildMod.ps15.8 (référenceReleases\2026.10.2.0\Windowsrequise) :.upluginlivré avec"EngineVersion": "5.8.3",.lpmodavecMODULE_GAME_VERSION: 2026.10.2.0; installé : ligneaccepte, clips ajoutés. - (iii) seulement avec l'accord de l'utilisateur.
10. Écarts à la fiche et à la SPEC
- Lieu de la garde : démarrage du module
LifePlay(FLifePlayGameModule::StartupModule), pasULPModInfoSubsystem::Initialize(esquisse T6) : l'Event Init Blueprint du GameInstance précède les sous-systèmes (§ 3.1).LifePlay.cpppasse deFDefaultGameModuleImplà une classe dérivée. - T5 révisé : l'égalité stricte et les
InitializeDependencyde T5 sont remplacés par un verdict unique (FLPModGuard) ;LPModDailiesetLPHairstyleSubsystemreviennent à HEAD + un testIsRefusedchacun. Gardé de T5 : fenêtre pour les mods décochés, retrait deIsModuleEnabled/FieldText,ProjectVersion=2026.10.2.0. - Portée : les contrôles visent les plugins de type
Mod(mods d'assets). Un module texte seul (dossierContent/Modules/<id>sans plugin) n'est ni contrôlé ni refusé (rien de cuit ; ses fichiers restent lus par les Blueprints). Interprétation à confirmer. MODULE_GAME_VERSIONnon numérique : compté sous le contrôle Version min (« unreadable game version '…' in its .lpmod ») ;EngineVersionnon lisible : « mod files unreadable » (contrôle Moteur).- Contrôle Moteur : la raison
EngineVersionn'est ajoutée que si le binaire est conforme (pas de double raison). - Plugin
Modsans aucun pak (jeu cuit) : contrôle binaire sans objet, pas « illisible » (rien n'est monté). - Lignes
LPModDailies/Coiffures« ignore (mod refuse) » au niveauLog: la seule ligne Warning par refus est celle de la garde (décision c). - Étape
Core.System Pathsde la fonction d'Epic non reproduite (§ 7, R7). - Fenêtre renommée « Mod compatibility warning » ; cases ajoutées en fin de liste ; fichier
LPSettingsSubsystem.cppmodifié (une règleCategoryOf, dans la liste autoriséeSource/LifePlay/*) ; deux fichiers nouveaux (LPModGuard.*, même dossier). EngineVersion: clé native du descripteur, formeX.Y.Z(FEngineVersion::Parseexige trois composantes). Effet de bord hors jeu : un éditeur non monolithique qui verrait un.upluginlivré plus récent que son moteur afficherait l'avertissement d'Epic (PluginManager.cpp:2688-2715) ; sans objet pour le jeu empaqueté.- fix1 (fiche U7-T7-fix1, décision utilisateur) : quand le contrôle Moteur est actif et échoue, les échecs
min/max ne vont pas dans la fenêtre (
Verdict.Reasons), seulement au journal (Verdict.LogReasons, suffixe « (secondaire) ») ; Moteur levé ou conforme : min/max affichés normalement (LPModGuard.cpp:390-413).
11. Incertitudes et points à confirmer
- Numérotation des versions (à trancher par l'utilisateur, non modifié) : les mods 5.5 publiés portent
2026.10.4(Pocolov), numériquement supérieur à la première version 5.82026.10.2.0(le jeu 5.5 est revenu à2026.10.1.6). Conséquence : Pocolov passe la version min et échoue la version max avec un message trompeur (« update the game ») ; le contrôle Moteur le refuse de toute façon. Si un mod 5.52026.10.4est un jour accepté par le contrôle Moteur levé, seul le max le bloquera. - Compilation non faite (fiche BUILD) : API relues dans les en-têtes 5.8 (§ 3-4) ; aucune garde de version (code de la
copie 5.8 seulement) ; noms internes dans des espaces de noms nommés (pas de C2374 en unity) ; pas d'ombrage de
variable (avertissement en erreur en V7) relevé à la relecture. Risque résiduel : avertissements de format
(
%davecint32partout, conversions explicites faites). - Lecture de l'en-tête de conteneur par
FIoDispatcherau démarrage du module : déduite de l'API (le moteur lit ce bloc au montage), non observée ; si elle échouait pour un mod 5.8 valide, le mod serait refusé « mod files unreadable » (cas (ii) du § 9 le montrerait ; la case Moteur permet de lever). - Démontage au démarrage du module : chemin d'Epic (DLC, UGC), mais non exécuté ici ; preuve = lignes du § 8 (i).
- R2 et R3 du § 7.
Suite : l'orchestrateur doit faire lancer la fiche BUILD (lp-builder : Tools\Build.ps1 -Lot U7 puis build Development
empaqueté) et la VAL (§ 9, (iii) seulement avec accord), et soumettre à l'utilisateur le point 11.1 ; état : 9 fichiers
modifiés + 2 nouveaux dans la copie 5.8, 1 fichier dans l'addon 5.8, non compilés, non commités.