LifePlay-Revival/Docs/Migration/23_Cycle_De_Vie_Mod.md
edujiro5972 135468380f DOC-MOD : document 23 (cycle de vie d'un mod), ligne d'index README
Phase DOC-MOD (CHG-225).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 15:49:57 +02:00

187 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`).