187 lines
16 KiB
Markdown
187 lines
16 KiB
Markdown
# 23. Cycle de vie d'un mod (2026-10-07)
|
||
|
||
Référence pour les développeurs du jeu et des outils de mods : ce qu'est un mod LifePlay, comment il est fabriqué,
|
||
livré, découvert, activé et consommé, quelles propriétés sont réellement lues, et ce qui pourrait évoluer.
|
||
Constats relevés dans le code du jeu, des Blueprints (`GI`) et du moteur UE 5.5 installé
|
||
(`Engine/Source/Runtime/Projects`) le 2026-10-07. Guide des moddeurs (anglais) :
|
||
`Docs/Modding/FullGuides/Creating_Asset_Mods.md`.
|
||
|
||
Documents liés : [01](01_Architecture_Initiale.md) (données texte, `GI`), [15](15_Modules_Optionnels.md) (modules
|
||
livrés à part), [16](16_H3_Mods_Paquets.md) (mods d'assets H3), [17](17_Mods_Ecran_Titre.md) (gestionnaire de mods
|
||
depuis l'écran titre), [20](20_Chaine_Animations.md) (packs d'animations AP1–AP7).
|
||
|
||
## 1. Deux moitiés, deux mécanismes
|
||
|
||
Un mod LifePlay est un **module texte** du jeu, éventuellement accompagné d'un **plugin de contenu** Unreal.
|
||
|
||
| | Module LifePlay (texte) | Plugin de contenu (assets cuits) |
|
||
|---|---|---|
|
||
| Emplacement installé | `LifePlay/Content/Modules/<id>/` | `LifePlay/Mods/<id>/` |
|
||
| Descripteur | `<id>.lpmod` (`CLÉ: valeur`) | `<id>.uplugin` (JSON) |
|
||
| Contenu | `.lpaction`, `.lpscene`, `.lpstat`, `.lpquest`, `.lptalk`, `.lpdesc`, `.lpai`, `.lpcharacter`, `.lpworld`, `.lphair`, tables, crédits, catalogue | `Content/Paks/Windows/<id>LifePlay-Windows.pak/.utoc/.ucas` (IoStore, `AssetRegistry.bin` du mod dans le `.pak`) |
|
||
| Lu par | `ModdingExtra` (`LoadTxt`, `LPDataCache`), Blueprint `GI`, sous-systèmes C++ | Gestionnaire de plugins du moteur (montage), registre d'assets |
|
||
| Activation | case du gestionnaire de mods (`GI.ModEnableds`) | toujours monté s'il est présent (plugin `Mod` activé par défaut) |
|
||
| Existe depuis | jeu d'origine (Vinfamy) | lot H3 (2026-10-03) |
|
||
|
||
Mods texte seuls : `vin_*`. Mods d'assets : `lpr_hair_pocolov` (coiffures), `lpr_anim_quaternius` et
|
||
`lpr_anim_freeanims` (animations). Les deux moitiés portent le **même identifiant** `<id>` : `MODULE_UNIQUEID` du
|
||
`.lpmod` = nom du plugin = point de montage `/<id>/` des assets.
|
||
|
||
## 2. Cycle de vie
|
||
|
||
```
|
||
FABRICATION (projet source) LIVRAISON JEU (à chaque démarrage)
|
||
───────────────────────── ───────── ────────────────────────
|
||
Tools/NewMod.ps1 → ModSources/<id>/ <id>-<v>-for-LifePlay-<jeu>.zip
|
||
.uplugin, Module/<id>.lpmod, CREDITS LifePlay/Mods/<id>/… 1. moteur : découverte Mods/, montage
|
||
import des assets (HairPipeline, LifePlay/Content/Modules/<id>/ 2. registre d'assets : AssetRegistry.bin
|
||
AnimPipeline → DeliverAnims) README.txt 3. GI : LoadSettings → ModEnableds
|
||
Tools/BuildMod.ps1 -Name <id> → removeDeletedMods → PopulateRaws
|
||
cuisson DLC -basedonreleaseversion installation : extraire 4. sous-systèmes C++ : métadonnées,
|
||
contrôle du conteneur, archive dans le dossier du jeu coiffures, animations, avertissement
|
||
désinstallation : supprimer 5. gestionnaire de mods : cocher / décocher
|
||
les deux dossiers → PopulateRaws, SaveModSelection
|
||
```
|
||
|
||
### 2.1 Fabrication
|
||
|
||
| Étape | Outil | Résultat |
|
||
|---|---|---|
|
||
| Squelette | `Tools/NewMod.ps1 -Name <id> [-Hairs]` | `ModSources/<id>/<id>.uplugin`, `Module/<id>.lpmod` (trois premières lignes imposées, voir § 3.2), `CREDITS.txt` |
|
||
| Édition | `Tools/EditMod.ps1`, `Tools/ModSource.ps1` | le mod est déplacé dans `<projet>/Mods/` le temps du travail, puis remis dans `ModSources/` : le jeu de base n'en contient jamais rien |
|
||
| Assets : coiffures | `Tools/HairPipeline.ps1 -Config Tools/mods/<id>.json` | `/<id>/Hairs/…` + fichiers `.lphair` |
|
||
| Assets : animations | `Tools/anim/AnimPipeline.ps1` (`BuildMod.ps1 -Anim <pack>`), étape 6 `DeliverAnims` | `/<id>/Anims/Female/<clé>_F`, `/<id>/Anims/Male/<clé>_M`, `.lpmod` et `CREDITS.txt` tirés de `AnimSources/<pack>/pack.json`, catalogue, vignettes, planches |
|
||
| Cuisson | `Tools/BuildMod.ps1 -Name <id> -Version <jeu>` | cuisson DLC du seul plugin contre `Releases/<jeu>` ; refus si un autre plugin est dans `Mods/`, si une entrée du conteneur sort de `Mods/<id>/`, si un mod payant cite une licence non commerciale |
|
||
| Archive | `BuildMod.ps1` | `<id>-<version>-for-LifePlay-<jeu>.zip` testée ; le `.lpmod` livré reçoit `MODULE_PACKAGED` et `MODULE_GAME_VERSION`, le `.uplugin` livré `VersionName` et `CreatedByURL` |
|
||
|
||
Contrainte de compatibilité : un conteneur de mod est cuit **contre une version de référence précise** du jeu
|
||
(`Releases/<version>`, produite par `-createreleaseversion` à chaque publication). Les assets de base qu'il référence
|
||
(squelettes Genesis, matériau `DazHair`, paramètres de compression du projet) ne doivent plus être renommés ni déplacés.
|
||
Un mod ne doit référencer aucun contenu `/Engine/` non cuit par le jeu (cas des clips UE5 en AP7 : compression remise
|
||
au défaut du projet par `DeliverAnims.py`).
|
||
|
||
### 2.2 Installation et désinstallation
|
||
|
||
Le joueur extrait l'archive dans le dossier du jeu (celui de `LifePlay.exe`), en fusionnant le dossier `LifePlay`.
|
||
Pour désinstaller : supprimer `Content/Modules/<id>` **et** `Mods/<id>`. Aucun outil ne vérifie aujourd'hui qu'une
|
||
moitié n'a pas été laissée seule (voir § 6).
|
||
|
||
### 2.3 Démarrage du jeu
|
||
|
||
| Ordre | Qui | Ce qui se passe |
|
||
|---|---|---|
|
||
| 1 | Moteur, `FPluginManager::ReadAllPlugins` | lit **toujours** `<jeu>/Mods/` ; chaque `Mods/<id>/<id>.uplugin` devient un plugin de type `Mod`, activé par défaut (`FPlugin::IsEnabledByDefault` : chargé « depuis le projet ») |
|
||
| 2 | Moteur | `CanContainContent: true` : le point de montage `/<id>/` est ajouté, les conteneurs `Content/Paks/Windows/*` sont montés |
|
||
| 3 | Registre d'assets | ajoute le `AssetRegistry.bin` de chaque plugin de contenu : les assets du mod sont trouvables par chemin et par classe sans être chargés |
|
||
| 4 | `ULPModInfoSubsystem::Initialize` (C++) | lit chaque `Modules/<id>/<id>.lpmod` (toutes les clés), retient `MODULE_GAME_VERSION` ; journal `Modules : N .lpmod lu(s)` |
|
||
| 5 | `ULPModDailiesSubsystem::Initialize` (C++, AP5) | relève les `UAnimSequence` sous `/<plugin>/Anims` de chaque plugin `Mod` monté ; clé = nom sans suffixe `_F`/`_M` |
|
||
| 6 | `GI.LoadSettings` (Blueprint) | `GameSettings.sav` → `ModEnableds` ; `ReadModLauncher` (`Modules/ModLauncher.config`, absent en pratique) ; `removeDeletedMods` retire de la liste les modules dont le `.lpmod` n'existe plus |
|
||
| 7 | `ULPHairstyleSubsystem` (C++) | après `LoadSettings`, ajoute cochés les modules `MODULE_DEFAULT_ENABLED: true` absents de la liste ; relève les `.lphair` |
|
||
| 8 | `GI.PopulateRaws` (Blueprint) | pour **chaque module coché** : lecture des fichiers texte (`LoadTxt`, cache `LPDataCache`) → actions, scènes, stats, quêtes, dialogues, mondes |
|
||
| 9 | `ULPModDailiesSubsystem::Tick` | dès que `GI.Room` existe : ajoute les clips de mod à `Room.Dailies` (une fois par pièce) |
|
||
| 10 | `ULPModInfoSubsystem::Tick` (toutes les 2 s) | module coché dont `MODULE_GAME_VERSION` ≠ `ProjectVersion` : fenêtre « Mod version warning », une fois par module et par session |
|
||
|
||
### 2.4 Activation et désactivation
|
||
|
||
- État : tableau `GI.ModEnableds` de structures `ModEnabled` (`UniqueID`, `IsEnabled`). Un module **absent** de la
|
||
liste est inactif (`GI.IsModEnabled`, `ULPModInfoSubsystem::IsModuleEnabled`, `ULPHairstyleSubsystem::IsModuleEnabled`).
|
||
- Gestionnaire de mods (`UI/ModManager`) : en partie (entrée d'origine) ou depuis l'écran titre (lot UI15). À la
|
||
fermeture : `PopulateRaws` (données relues avec les modules choisis) ; depuis l'écran titre, le choix est écrit dans
|
||
`GameSettings.sav` par `ULPModSettingsLibrary::SaveModSelection` (seul `ModEnableds` est remplacé).
|
||
- Effet de la case selon la nature du contenu :
|
||
|
||
| Contenu | Décoché → |
|
||
|---|---|
|
||
| Données texte (actions, scènes, stats…) | non lues par `PopulateRaws` : absentes du jeu |
|
||
| Coiffures (`.lphair`) | retirées des listes (`LPHairstyleSubsystem`) ; paquet toujours monté, inutilisé |
|
||
| Animations (`/<id>/Anims`) | **toujours ajoutées** à `Room.Dailies` : `LPModDailies` ne consulte pas la case (§ 6, E1) |
|
||
| Avertissement de version | non affiché |
|
||
| Plugin Unreal | toujours monté : le moteur ignore `ModEnableds` |
|
||
|
||
### 2.5 Consommation des assets
|
||
|
||
| Type | Déclaration côté texte | Résolution | Code |
|
||
|---|---|---|---|
|
||
| Coiffure | `.lphair` : `HAIRSTYLE: /<id>/Hairs/X.X` | registre d'assets, repli `FPackageName::DoesPackageExist` | `LPHairstyleSubsystem` |
|
||
| Animation | `ANIMATION: <clé>` (`.lpaction`), `Actor.animate(<clé>)` (`.lpscene`) | `Person::PlayDaily` : clés en dur, sinon (correctif `LPPatchDailies`) `IsModDaily(<clé>)` → `PlayDailyAnim` parcourt `Room.Dailies`, suffixe `_F`/`_M` selon le sexe | `LPModDailies`, `Person.uasset` patché |
|
||
|
||
Convention des clés d'animation : `<préfixe du pack>_<clip>` en minuscules (`qua_dance_loop`, `fap_as_talk`), assets
|
||
`<clé>_F` et `<clé>_M`. Une clé inconnue ne joue rien (journal `PlayDaily(<clé>) -> cle inconnue` hors Shipping).
|
||
|
||
## 3. Propriétés réellement lues
|
||
|
||
### 3.1 `<id>.uplugin` (moteur)
|
||
|
||
| Propriété | Lue | Effet |
|
||
|---|---|---|
|
||
| nom du fichier / dossier | oui | nom du plugin, point de montage `/<id>/` |
|
||
| `CanContainContent` | oui | **indispensable** : sans elle, le conteneur n'est pas monté |
|
||
| `EnabledByDefault` | oui (absente) | absente : plugin `Mod` activé |
|
||
| `ExplicitlyLoaded` | oui (absente) | absente : monté au démarrage |
|
||
| `Modules` | oui (absente) | aucun code : un mod Shipping ne peut pas en apporter (pas de binaire compatible) |
|
||
| `Plugins`, `SupportedTargetPlatforms`, `EngineVersion` | oui si présentes | dépendances, plateformes ; `EngineVersion` ne fait qu'avertir |
|
||
| `FriendlyName`, `Description`, `Category`, `CreatedBy`, `CreatedByURL`, `Version`, `VersionName`, `IsBetaVersion`, `Installed` | non en jeu | descriptifs (navigateur de plugins de l'éditeur) |
|
||
|
||
### 3.2 `<id>.lpmod` (jeu)
|
||
|
||
| Clé | Lue par | Effet |
|
||
|---|---|---|
|
||
| ligne 1 `MODULE_UNIQUEID` | `GI` (**par position**), sous-systèmes C++ (par clé) | identifiant du module |
|
||
| ligne 2 `MODULE_NAME` | `GI` (par position), `LPModInfoSubsystem` | nom dans le gestionnaire, dans l'avertissement |
|
||
| ligne 3 `MODULE_AUTHOR` | `GI` (par position) | auteur dans le gestionnaire |
|
||
| `MODULE_DEFAULT_ENABLED` | `LPHairstyleSubsystem` | coché au premier démarrage où il est présent |
|
||
| `MODULE_GAME_VERSION` | `LPModInfoSubsystem` | avertissement de version |
|
||
| `MODULE_LINK`, `MODULE_DESCRIPTION`, `MODULE_REQUIREMENTS`, `MODULE_VERSION`, `MODULE_CREATED`, `MODULE_LICENSE`, `MODULE_AUTHOR_LINK`, `MODULE_PRICE`, `MODULE_PACKAGED` | aucun code | affichées telles quelles : le gestionnaire montre le fichier entier ; `GetModuleMetadata` les rend accessibles aux Blueprints |
|
||
|
||
`MODULE_REQUIREMENTS` n'est pas vérifié : un mod dont un prérequis manque se charge quand même. Les trois premières
|
||
lignes sont lues par position : `BuildMod.ps1` en contrôle l'ordre. Réserve : la recherche dans les Blueprints porte
|
||
sur les noms de clés écrits en clair ; une clé assemblée à l'exécution n'y apparaîtrait pas.
|
||
|
||
## 4. Classes et fichiers
|
||
|
||
| Élément | Rôle |
|
||
|---|---|
|
||
| `FPluginManager` (moteur) | découverte de `Mods/`, activation, montage du contenu |
|
||
| Registre d'assets (moteur) | `AssetRegistry.bin` des plugins, recherche par chemin / classe |
|
||
| `Plugins/ModdingExtra` : `LoadTxt`, `SaveTxt`, `GetAllFilesFromFolder`, `LPDataCache` | accès disque aux `Modules` (fichiers nus, `DirectoriesToAlwaysStageAsNonUFS`) ; cache analysé des formats `CLÉ: valeur` dont `.lpmod` |
|
||
| `Content/BPs/GI.uasset` | `LoadSettings`, `ReadModLauncher`, `removeDeletedMods`, `PopulateRaws`, `IsModEnabled`, `CreateModManagerWidget`, `CloseModManager`, `ModEnableds` |
|
||
| `UI/ModManager`, `UI/MainMenu` | gestionnaire de mods ; entrée de l'écran titre (`LPTitleModManager`) |
|
||
| `ULPModInfoSubsystem` | lecture des `.lpmod`, `GetModuleMetadata`, `IsModuleEnabled`, avertissement de version |
|
||
| `ULPModSettingsLibrary::SaveModSelection` | enregistre `ModEnableds` seul dans `GameSettings.sav` |
|
||
| `ULPHairstyleSubsystem` | `MODULE_DEFAULT_ENABLED`, coiffures `.lphair` des modules cochés |
|
||
| `ULPModDailiesSubsystem`, `ULPModDailiesLibrary::IsModDaily` | clips de mod → `Room.Dailies` ; reconnaissance des clés de mod dans `Person::PlayDaily` |
|
||
| `ULPSessionLogSubsystem` | inventaire des modules au démarrage (journal) |
|
||
| Outils | `NewMod`, `EditMod`, `ModSource`, `BuildMod`, `SplitModules`, `HairPipeline`, `anim/AnimPipeline`, `anim/DeliverAnims` |
|
||
|
||
## 5. Capacités actuelles
|
||
|
||
Un mod peut, sans recompiler ni recuire le jeu :
|
||
|
||
- ajouter ou modifier des données de jeu en texte (actions, scènes, stats, quêtes, dialogues, IA, personnages, mondes) ;
|
||
- apporter des assets cuits : coiffures (H3), animations (AP5–AP7), tout asset que le jeu trouve par le registre ;
|
||
- être coché ou décoché dans le gestionnaire (sauf les animations, § 6 E1), coché d'office au premier démarrage ;
|
||
- prévenir le joueur s'il a été construit pour une autre version du jeu ;
|
||
- documenter licence, auteur, liens et crédits (affichés, non interprétés).
|
||
|
||
Il ne peut pas : apporter du code (C++ ou Blueprint exécuté en Shipping), remplacer un asset du jeu de base,
|
||
exiger un autre mod, ni être démonté à chaud.
|
||
|
||
## 6. Évolutions possibles
|
||
|
||
| Réf. | Évolution | Intérêt | Ampleur |
|
||
|---|---|---|---|
|
||
| E1 | `LPModDailies` : ne garder que les clips des modules cochés (même règle que `IsModuleEnabled`), au remplissage de `Room.Dailies` | un seul interrupteur pour tous les types de contenu ; clé « inconnue » quand le mod est décoché | petit lot C++, essai empaqueté coché / décoché |
|
||
| E2 | Vérifier `MODULE_REQUIREMENTS` au démarrage et à la fermeture du gestionnaire (avertissement comme pour la version) | prérequis manquant signalé au lieu d'un mod à moitié fonctionnel | petit lot C++ |
|
||
| E3 | Contrôle de cohérence des deux moitiés : `Mods/<id>` sans `Modules/<id>` (assets orphelins) ou l'inverse (`.lphair` / clés vers des assets absents) | désinstallation partielle détectée | petit lot C++ |
|
||
| E4 | Avertissement de version élargi aux mods d'assets décochés (un conteneur incompatible est monté même décoché) | prévenir avant un échec de chargement | très petit |
|
||
| E5 | Détection des collisions : deux mods qui déclarent la même clé d'animation, la même coiffure, le même identifiant d'action | comportement déterministe, message clair | moyen |
|
||
| E6 | Lecture des trois premières lignes du `.lpmod` par clé plutôt que par position (Blueprint `GI` ou relais C++) | un `.lpmod` écrit à la main dans le désordre ne casse plus le gestionnaire | moyen (graphe de `GI`) |
|
||
| E7 | Ne pas monter les plugins des mods décochés (liste lue avant le montage, `ExplicitlyLoaded` + montage par `IPluginManager::MountExplicitlyLoadedPlugin`) | mémoire et temps de démarrage ; mod réellement inerte | moyen, à prototyper (ordre de démarrage) |
|
||
| E8 | Déclaration explicite des animations (`.lpanim` : clé, clips F/M, catégorie, usage, posture) plutôt que par convention de nom | catalogue en jeu, filtres par posture, alias, plusieurs variantes par clé | moyen |
|
||
| E9 | Autres types d'assets par le même schéma registre + fichier texte : vêtements, meubles, décors de pièce (`.lpworld` vers `/<id>/…`) | extension naturelle de H3 | un lot par type |
|
||
| E10 | Empreintes dans l'archive (SHA-256 par fichier, vérifiées par un outil d'installation) | intégrité, mods modifiés détectés | petit (outillage) |
|
||
| E11 | Gestionnaire enrichi : description, licence, lien, version, état (cuit pour, prérequis, conflits), catalogue d'animations | information du joueur, aujourd'hui texte brut | moyen (UI) |
|
||
| E12 | Ordre de chargement des modules (priorité dans `.lpmod`) | surcharge contrôlée d'un module par un autre | moyen ; dépend de `PopulateRaws` |
|
||
|
||
Recommandation : E1 en premier (seule incohérence visible du joueur), puis E2 et E3 dans un même lot (mêmes
|
||
lectures que `LPModInfoSubsystem`).
|