From f7f1775baeef5d13ccb2f686a9134f77cdd4cc63 Mon Sep 17 00:00:00 2001 From: edujiro5972 <336072401+edujiro5972@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:49:59 +0200 Subject: [PATCH] =?UTF-8?q?DOC-MOD-3=20:=20cycle=20de=20vie=20d'un=20mod,?= =?UTF-8?q?=20=C2=A7=202.6=20et=207=20sch=C3=A9mas=20Mermaid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase DOC-MOD-3 (CHG-227). Co-Authored-By: Claude Opus 5.5 --- Docs/Migration/23_Cycle_De_Vie_Mod.md | 182 ++++++++++++++++++++++++ Docs/Migration/Journal_Modifications.md | 1 + 2 files changed, 183 insertions(+) diff --git a/Docs/Migration/23_Cycle_De_Vie_Mod.md b/Docs/Migration/23_Cycle_De_Vie_Mod.md index 76629ce..756cd12 100644 --- a/Docs/Migration/23_Cycle_De_Vie_Mod.md +++ b/Docs/Migration/23_Cycle_De_Vie_Mod.md @@ -132,6 +132,188 @@ Aucune clé `.lpmod` ne permet de verrouiller un autre module. Convention des clés d'animation : `_` en minuscules (`qua_dance_loop`, `fap_as_talk`), assets `_F` et `_M`. Une clé inconnue ne joue rien (journal `PlayDaily() -> cle inconnue` hors Shipping). +### 2.6 Schémas du cycle de vie + +Schémas tirés des relevés `LPDumpFunction` de `GI` (graphe principal, 6 199 nœuds) et de `UI/ModuleTick`, du code C++ +du jeu et du moteur (`GameInstance.cpp`, `PluginManager.cpp`). Les noms entre parenthèses sont les paramètres des +fonctions ou des événements. `GI.X` désigne un événement ou une fonction du Blueprint `GI` ; `ULP…` une classe C++ du jeu. + +#### 2.6.1 États d'un module + +```mermaid +stateDiagram-v2 + [*] --> Installe : extraction de l'archive + Installe : Installé (fichiers sur disque) + Installe --> Monte : démarrage, FPluginManager (Mods/id) + Monte : Plugin monté (assets /id/ visibles) + Monte --> Liste : GI.LoadSettings, ULPModInfoSubsystem + Liste : Listé (.lpmod lu) + Liste --> Inscrit : ReadModLauncher ou GameSettings.sav, MODULE_DEFAULT_ENABLED + Inscrit : Inscrit dans GI.ModEnableds + Inscrit --> Actif : IsEnabled = true + Inscrit --> Inactif : IsEnabled = false + Actif --> Inactif : GI.ToggleMod(Mod, false) + Inactif --> Actif : GI.ToggleMod(Mod, true) + Actif : Actif (textes lus par GI.PopulateRaws) + Inactif : Inactif (textes ignorés, plugin toujours monté) + Liste --> Retire : dossier Modules/id supprimé + Retire : Retiré de ModEnableds (GI.removeDeletedMods) + Retire --> [*] + Actif --> Decharge : fermeture du jeu + Inactif --> Decharge : fermeture du jeu + Decharge : Déchargé (sous-systèmes Deinitialize, conteneurs démontés) + Decharge --> [*] +``` + +`vin_Base` ne peut pas passer à l'état Inactif : la case de `UI/ModuleTick` n'appelle `GI.ToggleMod` que si +`UniqueID != 'vin_Base'`. + +#### 2.6.2 Démarrage : séquence + +```mermaid +sequenceDiagram + autonumber + participant M as Moteur (FPluginManager) + participant R as Registre d'assets + participant GI as GI (Blueprint) + participant S as Sous-systèmes C++ + participant D as Disque Modules/ + M->>M: ReadAllPlugins : Mods/id/id.uplugin devient un plugin de type Mod + M->>M: ConfigureEnabledPlugins (dépendances Plugins, obligatoire absente = arrêt) + M->>R: CanContainContent : montage de /id/, MountPak(pak, 0) + R->>R: AssetRegistry.bin du mod ajouté + M->>GI: UGameInstance::Init puis ReceiveInit (minuterie 2 s) + M->>S: SubsystemCollection.Initialize + S->>D: ULPModInfoSubsystem lit Modules/*/id.lpmod + S->>R: ULPModDailiesSubsystem relève les UAnimSequence sous /id/Anims + GI->>GI: DelayedStart puis LoadSettings(LoadLastSave) + GI->>D: ReadModLauncher, ParseConfig(Content), ModEnableds si UseConfig + GI->>D: GameSettings.sav donne ModEnableds, puis removeDeletedMods + S->>GI: ULPHairstyleSubsystem ajoute les modules MODULE_DEFAULT_ENABLED + GI->>D: PopulateRaws lit les textes des modules cochés (LoadTxt, LPDataCache) + S->>GI: ULPModDailiesSubsystem::Tick ajoute les clips à Room.Dailies + S->>GI: ULPModInfoSubsystem::Tick (2 s) : avertissement MODULE_GAME_VERSION +``` + +#### 2.6.3 Arbre logique de `GI.LoadSettings(LoadLastSave)` + +```mermaid +flowchart TD + A["GI.LoadSettings(LoadLastSave)"] --> B["ThemeSong.SetActive(false), r.Streaming.PoolSize 1500, EnableDynamicResolution"] + B --> C{"DoesSaveGameExist('GameSettings', 0)"} + C -- oui --> D["LoadGameFromSlot('GameSettings', 0), cast GameSettings"] + D --> E["ModEnableds = GameSettings.ModEnableds"] + E --> F["GI.ReadModLauncher()"] + F --> G["GI.removeDeletedMods()"] + G --> H["environ 60 réglages copiés (DifficultyPlayerInput, SceneFrequency, MarkerSize...)"] + H --> I["loadAverageAttractiveness()"] + I --> J["DefaultDistributionIfNeeded()"] + J --> K["GI.PopulateRaws()"] + C -- non --> F2["GI.ReadModLauncher()"] + F2 --> K + K --> L{"LoadLastSave et réglages existants"} + L -- oui --> M["LoadThis = LastSaveGame"] + F -.-> F1["ParseConfig(Content = ModLauncher.config) : UseConfig, ModEnableds"] + F1 -.-> F1b{"UseConfig"} + F1b -. oui .-> F1c["ModEnableds = liste du fichier"] +``` + +`Modules/ModLauncher.config` est absent en pratique : `UseConfig` est faux et `ModEnableds` vient de +`GameSettings.sav` (ou de la valeur par défaut du Blueprint au premier lancement). + +#### 2.6.4 Arbre logique de `GI.PopulateRaws()` + +```mermaid +flowchart TD + P["GI.PopulateRaws()"] --> P1["FillNamePool, readUniforms, PopulateAllMorphTags, PopulateBeautyStandardsDefault, FillPregnancyMorphs"] + P1 --> P2["FilenamesUsed.Clear()"] + P2 --> L{"Pour chaque ModEnabled (UniqueID, IsEnabled) de ModEnableds, dans l'ordre"} + L -- "IsEnabled faux" --> L + L -- "IsEnabled vrai" --> T["Pour chaque type : lpstat, lpaction, lpscene, lptalk, lpdesc, lpquest, lpai"] + T --> F["GetAllFilesFromFolder('Modules/' + UniqueID, Ext)"] + F --> N{"nom de fichier (après le dernier '/') déjà dans FilenamesUsed ?"} + N -- oui --> X["ignoré : le module lu avant l'emporte"] + N -- non --> Y["LoadTxt vers Raws_Stat, Raws_Action, Raws_Scene, Raws_Talks, Raws_Descs, Raws_Quests, AI_High, AI_Med ; FilenamesUsed.Add(nom)"] + L -- fin --> Q["PopulateStats, PopulateActions, PopulateScenes, PopulateQuests, PopulateTalksAndDescs, PopulateWorldPresets, RemoveNPCOnly, CleanRaws"] + Q --> R["IsModEnabled(...) : options liées à un module (Datings...)"] +``` + +Le premier fichier lu l'emporte. Comme l'affichage du gestionnaire est inversé (§ 2.4), c'est le module le plus bas +de la liste affichée. Les `.lpscene` passent en plus deux conditions sur leur contenu avant d'être ajoutées. + +#### 2.6.5 Gestionnaire de mods : ouverture, actions, fermeture + +```mermaid +flowchart TD + O["Menu en partie : HideUI() puis GI.CreateModManagerWidget(GameStart)"] --> O2["CreateWidget ModManager, GameStart mémorisé, AddToViewport(0)"] + O1["Écran titre (UI15) : GI.CreateModManagerWidget(GameStart = false), ModManager.TitleMenu = menu"] --> O2 + O2 --> O3["TempModuleTicks.Clear(), TempModules.Clear()"] + O3 --> O4["LPMods = GetAllFilesFromFolder('Modules', 'lpmod')"] + O4 --> O5{"Pour chaque .lpmod : UniqueID déjà dans TempModules ?"} + O5 -- non --> O6["TempModules.Add(UniqueID) ; CreateWidget ModuleTick (FullString = fichier entier, UniqueID)"] + O6 --> O7["CheckIfModAlreadyFound(UniqueID, ModuleTick) : AlreadyExisted"] + O7 --> O8["NameText, AuthorText depuis MODULE_NAME, MODULE_AUTHOR ; TempModuleTicks.Add"] + O5 -- fin --> O9["removeDeletedMods(), AddTempModuleTicks(), SetInputMode_UIOnly(ModManager)"] + O9 --> U{"Action du joueur dans UI/ModuleTick"} + U -- "case cochée ou décochée" --> T1{"UniqueID différent de 'vin_Base'"} + T1 -- oui --> T2["GI.ToggleMod(Mod = UniqueID, IsEnabled) : remplace l'élément de ModEnableds"] + T1 -- non --> T3["sans effet"] + U -- "monter ou descendre" --> S1["GI.SortLoadOrder(ForUp = 1 ou -1, ModuleIndex = position affichée)"] + S1 --> S2{"ModuleIndex = 1 et ForUp = 1, ou hors bornes ?"} + S2 -- non --> S3["Array_Swap(ModEnableds, L-1-ModuleIndex, L-1-ModuleIndex+ForUp), ClearChildren, AddTempModuleTicks"] + S2 -- oui --> S4["refusé"] + U -- "fermer, en partie" --> C1["GI.CloseModManager(GameStart) : ShowUI, ClearModManagerWidget, PopulateRaws, ModManagerOn = false"] + C1 --> C2{"GameStart"} + C2 -- oui --> C3["CharGenInput(GameStart = true)"] + C2 -- non --> C4["DelayThenFollowUp()"] + U -- "fermer, écran titre (UI15)" --> C5["menu titre réaffiché, ClearModManagerWidget, PopulateRaws, ULPModSettingsLibrary::SaveModSelection"] +``` + +#### 2.6.6 Appel d'une animation de mod + +```mermaid +sequenceDiagram + participant Sc as Scène ou action + participant GI as GI.CallFunction + participant P as Person (Blueprint patché) + participant L as ULPModDailiesLibrary + participant Ro as Room.Dailies + Sc->>GI: Actor.animate(clé) ou ANIMATION: clé + GI->>P: PlayDaily(clé), cas 'animate' + P->>P: SwitchString des clés du jeu (dance...) + P->>L: cas Default : IsModDaily(clé) + alt clé de mod + L-->>P: vrai (journal « cle de mod » hors Shipping) + P->>Ro: PlayDailyAnim parcourt Room.Dailies, suffixe _F ou _M selon IsMale + Ro-->>P: UAnimSequence /id/Anims/.../clé_F ou clé_M + P->>P: currentDaily joué par l'AnimBP Daily ou Daily_F + else clé inconnue + L-->>P: faux (journal « cle inconnue ») : rien n'est joué + end +``` + +La case du gestionnaire n'intervient pas dans cet arbre (§ 6, E1). + +#### 2.6.7 Désactivation, désinstallation et déchargement + +```mermaid +flowchart TD + A["Module décoché : GI.ToggleMod(Mod, false)"] --> B["fermeture du gestionnaire : GI.PopulateRaws()"] + B --> B1["textes du module non relus : actions, scènes, stats absentes"] + A --> C["ULPHairstyleSubsystem::IsModuleEnabled : coiffures filtrées à la demande"] + A --> D["ULPModInfoSubsystem : plus d'avertissement de version"] + A --> E["ULPModDailiesSubsystem : clips toujours dans Room.Dailies (E1)"] + A --> F["plugin /id/ toujours monté jusqu'à la fermeture"] + G["Dossier Modules/id supprimé"] --> H["démarrage suivant : GI.removeDeletedMods() retire l'entrée de ModEnableds"] + I["Dossier Mods/id supprimé"] --> J["démarrage suivant : plugin absent, clips et coiffures du mod introuvables"] + K["Fermeture du jeu"] --> K1["UGameInstance::Shutdown, ReceiveShutdown"] + K1 --> K2["SubsystemCollection.Deinitialize : LPModDailies retire son ticker et vide les clés ; LPModInfo retire son ticker et ferme l'avertissement"] + K2 --> K3["moteur : modules et conteneurs démontés à la sortie"] +``` + +Il n'existe pas de déchargement à chaud : un plugin de mod reste monté du démarrage à la fermeture, quel que soit +l'état de sa case. + ## 3. Propriétés réellement lues ### 3.1 `.uplugin` (moteur) diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index 23ac5a9..ee40488 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -1131,6 +1131,7 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol | CHG-155 | 2026-10-05 | ShowCouple | Un lit est reconnu par son nom. Il ne bloque un déplacement que vers le bas, pour se poser ; le retour horizontal n'est plus arrêté et le contact ne renvoie plus la boîte | après avoir quitté le lit, la boîte ne pouvait plus y revenir | revenir au test `DoesPackageExist` et au filtre du flanc seul | | CHG-156 | 2026-10-05 | Clavier | `LPKeyboardSubsystem` : bouton « Clavier » ajouté à l'exécution dans `MainMenu.SettingsScrollBox` ; panneau pour changer, ajouter ou retirer une touche (actions et axes clavier). Écrit dans `Saved/Config/Windows`, pas dans `GameSettings.sav` | pouvoir réaffecter les touches, dont Q, sans modifier les Blueprints | retirer `LPKeyboardSubsystem.h/.cpp` et la dépendance `UMG` | | CHG-158 | 2026-10-05 | Réglages | `LPSettingsSubsystem` : onglets Audio, Video, Lewd, General, Keyboard, Misc au-dessus de `SettingsScrollBox`. Les lignes du Blueprint restent en place et sont montrées selon leur texte ; le reste va dans Misc. Keyboard affiche la liste des touches | la liste unique ne montrait pas le clavier | retirer `LPSettingsSubsystem.h/.cpp` | +| CHG-227 | 2026-10-07 | DOC-MOD-3 | `Docs/Migration/23_Cycle_De_Vie_Mod.md` (§ 2.6 nouveau, 7 schémas Mermaid) | schémas du cycle de vie d'un module demandés par l'utilisateur : états (installé, monté, listé, inscrit, actif / inactif, retiré, déchargé), séquence de démarrage (moteur, registre, `GI`, sous-systèmes C++), arbres logiques de `GI.LoadSettings(LoadLastSave)`, `GI.PopulateRaws()`, du gestionnaire (`CreateModManagerWidget(GameStart)`, `ToggleMod(Mod, IsEnabled)`, `SortLoadOrder(ForUp, ModuleIndex)`, `CloseModManager(GameStart)`), appel d'une animation de mod, désactivation et déchargement ; graphes reconstitués depuis le relevé `LPDumpFunction -BP=GI -Fn=EventGraph -Pins -Exec` et l'ordre d'initialisation de `UGameInstance` (moteur) ; 7 schémas rendus sans erreur par Mermaid 11 | aucun changement de code ni de configuration | `.\Tools\Restore.ps1 -Lot DOC-MOD-3` | | CHG-226 | 2026-10-07 | DOC-MOD-2 | `Docs/Migration/23_Cycle_De_Vie_Mod.md` (§ 2.4, 3.1, 3.2, 3.3 et 3.4 nouveaux, 5, 6) ; `Docs/Migration/Journal_Modifications.md` | complément de la documentation du cycle de vie d'un mod, constats vérifiés dans les Blueprints (`GI`, `UI/ModuleTick`, relevés `LPDumpFunction`) et le moteur UE 5.5 : l'ordre de chargement existe (`GI.SortLoadOrder`, `PopulateRaws` : le premier fichier lu par nom gagne, le module le plus bas l'emporte, fichiers texte seulement) ; `vin_Base` verrouillé en dur dans `UI/ModuleTick` ; dépendance `.uplugin` obligatoire absente = le jeu ne démarre pas, donc aucune dépendance entre mods dans le `.uplugin` ; doublons entre `.uplugin`, `.lpmod` et `pack.json`, divergences dues à `New-IfMissing` de `DeliverAnims.ps1` ; évolutions E12 corrigée, E13 à E15 ajoutées | aucun changement de code ni de configuration | `.\Tools\Restore.ps1 -Lot DOC-MOD-2` | | CHG-225 | 2026-10-07 | DOC-MOD | `Docs/Migration/23_Cycle_De_Vie_Mod.md` (nouveau) ; `Docs/Migration/README.md` (ligne d'index) | documentation demandée par l'utilisateur : cycle de vie d'un mod, propriétés réellement lues par le moteur (`.uplugin`) et par le jeu (`.lpmod`), classes, architecture, capacités, évolutions possibles. Constat consigné : `LPModDailies` ne consulte pas la case du gestionnaire de mods (un pack d'animations décoché fournit encore ses clés, évolution E1) ; `MODULE_REQUIREMENTS` n'est pas vérifié | aucun changement de code ni de configuration | supprimer `23_Cycle_De_Vie_Mod.md` et cette entrée ; `.\Tools\Restore.ps1 -Lot DOC-MOD` | | CHG-224 | 2026-10-07 | AP7 | Cuisson DLC et essai en jeu empaqueté. `Config/DefaultGame.ini` : `ProjectVersion=2026.10.1.6` (version de référence d'essai, non publiée) ; `+DirectoriesToNeverCook` pour `/Game/Quaternius`, `/Game/FreeAnimationsPack`, `/Game/AnimPacks`, `/Game/Retarget_ue5` (sources de travail, jamais dans le jeu). `Tools/anim/DeliverAnims.py` : à la copie dans le mod, les réglages de compression qui pointent sur `/Engine/` sont remis à vide (les clips d'un pack UE5 référençaient `/Engine/Animation/DefaultAnim*CompressionSettings` : cuisson DLC refusée) ; la compression par défaut du projet s'applique, comme pour Quaternius | lot AP7, étape 7. Empaquetage Shipping `2026.10.1.6` (`D:\Game\LifePlay_Build_5.5_Shipping_AP7\Windows`, `Releases/2026.10.1.6`) ; mods cuits contre cette version : `lpr_anim_quaternius` (84 paquets, zip 15 798 332 o), `lpr_anim_freeanims` (20 paquets, 0 référence moteur, zip 4 416 299 o) dans `D:\Game\LifePlay_Publication\Mods\` ; module d'essai `lpr_anim_essai` copié dans le seul jeu d'essai (hors du projet : `LifePlay_Backup_5.5/AP7/hors_build`). **Essai utilisateur en jeu empaqueté : animations jouées** (`qua_*`, `fap_*`, témoin `dance`) | `.\Tools\Restore.ps1 -Lot AP7` ; supprimer `D:\Game\LifePlay_Build_5.5_Shipping_AP7` et `Releases/2026.10.1.6` ; rien n'est commité dans le dépôt du jeu |