# 16. H3 : assets apportés par les mods (étude, 2026-10-03) Objectif : qu'un mod apporte ses propres maillages, textures et matériaux (coiffures d'abord, puis vêtements, meubles, animations) **sans recompiler ni recuire le jeu**. Aujourd'hui, un mod `Content/Modules/` ne contient que du texte : tout asset doit être cuit avec le jeu (lots H1, H2). ## 1. Constats (lecture du moteur 5.5 et de la configuration du projet) | Point | Constat | Source | |---|---|---| | Dossier des mods | Dans un jeu empaqueté, le gestionnaire de plugins lit **toujours** `/Mods/` (« Always add the mods from the loose directory ») ; chaque `Mods//.uplugin` devient un plugin de type `Mod` | `PluginManager.cpp`, `ReadAllPlugins` | | Activation | un plugin `Mod` est considéré comme chargé depuis le projet : **activé par défaut** | `FPlugin::IsEnabledByDefault` | | Paquets | pour un plugin de contenu, les paquets `Mods//Content/Paks/Windows/*.pak` (et leurs conteneurs IoStore `.utoc`/`.ucas`) sont **montés au démarrage** ; ses assets ont le préfixe `//` | `PluginManager.cpp`, montage des paquets de plugins | | Registre d'assets | au démarrage, le registre ajoute le `AssetRegistry.bin` de chaque plugin de contenu activé : les assets du mod sont trouvables par classe et par nom (`GetAssetsByClass`, utilisé par le jeu pour vêtements, animations, meubles…) | `AssetRegistry.cpp`, `LoadPremadeAssetRegistry_Plugins` | | Signature | aucun `DefaultCrypto.ini` : paquets non signés, non chiffrés | `Config/` | | Shaders | `bShareMaterialShaderCode=False` : les shaders sont dans les matériaux eux-mêmes, pas de bibliothèque partagée à fournir ; une instance de matériau (`DazHair`) n'en ajoute aucun | `DefaultGame.ini` | | Format | le jeu est cuit en conteneurs IoStore (O2) : un mod doit l'être aussi, avec le même moteur et contre la **même version cuite** du jeu | `DefaultGame.ini` | Le mécanisme existe donc dans le moteur ; il n'a jamais été utilisé par LifePlay. ## 2. Architecture proposée ``` /LifePlay/Mods//.uplugin descripteur (plugin de contenu, type Mod) /LifePlay/Mods//AssetRegistry.bin registre des assets du mod /LifePlay/Mods//Content/Paks/Windows/*.pak/.utoc/.ucas assets cuits /LifePlay/Content/Modules//.lpmod module LifePlay (texte, comme aujourd'hui) /LifePlay/Content/Modules//Hairstyles/*.lphair HAIRSTYLE: //Hairs/X.X ``` Un mod se distribue en une archive à extraire dans le dossier du jeu. Le texte reste dans `Modules` (gestionnaire de mods, activation, données) ; les assets sont dans `Mods` (moteur). Désactiver le module retire ses coiffures (H1) ; le paquet reste monté mais n'est plus utilisé. **Fabrication d'un mod** (par un moddeur, avec le projet source publié) : 1. créer un plugin de contenu dans `Mods/` du projet ; 2. y importer ses assets (coiffures : `Tools/HairPipeline.ps1` avec `folder` = `//Hairs`) ; 3. cuire **ce seul plugin** en DLC contre la version publiée du jeu : `RunUAT BuildCookRun ... -cook -stage -pak -iostore -dlcname= -basedonreleaseversion=2026.10.4` ; 4. assembler l'archive du mod (plugin cuit + module texte). **Côté jeu** : rien à recompiler pour le montage. À ajuster : - `LPHairstyleSubsystem` : contrôle d'une coiffure par le registre (déjà) avec repli sur `FPackageName::DoesPackageExist` si le registre du mod manquait ; - **version de référence** : chaque publication doit produire `Releases//Windows/` (option `-createreleaseversion=` du build de distribution) et la livrer avec les sources ; un mod est cuit contre elle. Pour la V1.4 publiée, elle se recrée par une cuisson incrémentale (contenu inchangé depuis le tag `v1.4`). ## 3. Risques et inconnues à lever par un prototype | Risque | Conséquence | Vérification | |---|---|---| | Cuisson DLC dans ce projet (plugin dans `Mods/`, `-basedonreleaseversion`) | outil des moddeurs inutilisable | cuisson d'un mod d'essai | | Compatibilité du conteneur IoStore du mod avec celui du jeu publié | mod non chargé, ou plantage au chargement | montage dans une copie du build Shipping V1.4 | | Références du mod vers le jeu (squelette Genesis, matériau `DazHair`) | coiffure sans matériau ou sans squelette | contrôle visuel en jeu | | Mod cuit contre une version, joué avec une autre | incompatibilités si les assets référencés changent | règle : un mod indique la version du jeu ; les assets de base référencés (squelettes, matériaux maîtres) ne sont plus renommés | | Taille de `Releases/` à livrer avec les sources | dépôt alourdi | mesure ; hors git si volumineux (archive des sources seulement) | | Mods malveillants | un paquet ne contient que des assets cuits (pas de code) ; les plugins de code ne sont pas chargés dans un jeu Shipping sans binaire compatible | rien à faire au-delà de l'avertissement | ## 4. Lots proposés | Lot | Contenu | Sortie | |---|---|---| | **H3a, prototype** | version de référence `Releases/2026.10.4` ; mod d'essai `LPHairSample` (une coiffure Pocolov non encore intégrée, ex. Pocolov 05/07) ; cuisson DLC ; installation dans une **copie** du build Shipping V1.4 | coiffure du mod visible et fonctionnelle en jeu, sans recuire le jeu | | **H3b, outillage** | `Tools/BuildMod.ps1` (squelette de plugin, cuisson DLC, archive du mod) ; option `HairPipeline` vers `//` ; repli `DoesPackageExist` dans le sous-système | un mod de coiffures en une commande | | **H3c, documentation moddeurs** | guide (anglais) : créer, cuire, distribuer, compatibilité de version ; intégration de la création de `Releases/` au protocole de publication | guide publié avec la version suivante | ## 5. Prototype H3a (2026-10-03) : résultats Validé en jeu le 2026-10-03 : la coiffure Pocolov 05, apportée par un mod cuit à part, est chargée par le jeu publié sans recuisson du jeu ; décocher le mod la retire. | Étape | Résultat | |---|---| | Version de référence | `-createreleaseversion=2026.10.4` (build Shipping, cuisson incrémentale) : `Releases/2026.10.4/Windows` ; paquets **identiques octet pour octet** à ceux du V1.4 publié. Taille : **1,3 Go** (copie complète des paquets du jeu, à livrer aux moddeurs : H3c) | | Mod d'essai | plugin `LPHairSample` (`CanContainContent`), coiffure importée par `Tools/HairPipeline.ps1 -Config Tools/mods/LPHairSample.json` dans `/LPHairSample/Hairs` ; dépendances : ses assets et `/Game/Heavy/Daz/Bridget/Hairs/DazHair` | | Cuisson DLC | `-dlcname= -basedonreleaseversion=2026.10.4` : 6 paquets, 0 erreur, 0 avertissement ; `LPHairSampleLifePlay-Windows.pak/.utoc/.ucas` (≈ 337 Ko) ; le `.pak` contient le `AssetRegistry.bin` du mod | | Installation | `/LifePlay/Mods/LPHairSample/` (`.uplugin` + `Content/Paks/Windows/*`) et `/LifePlay/Content/Modules/LPHairSample/` (`.lpmod`, `.lphair`, `CREDITS.txt`) | | Compatibilité | mod cuit contre 2026.10.4, chargé aussi par le build UI15b (menu principal modifié depuis) | Commandes : ```powershell cmd /c '"\Build\BatchFiles\RunUAT.bat" BuildCookRun -project="" -platform=Win64 -clientconfig=Shipping -build -cook -iterate -stage -pak -iostore -compressed -createreleaseversion=2026.10.4 -archive -archivedirectory= -nodebuginfo -unattended -utf8output -nop4' cmd /c '"\Build\BatchFiles\RunUAT.bat" BuildCookRun -project="" -platform=Win64 -clientconfig=Shipping -cook -stage -pak -iostore -compressed -dlcname="\Mods\LPHairSample\LPHairSample.uplugin" -basedonreleaseversion=2026.10.4 -nodebuginfo -unattended -utf8output -nop4' ``` Enseignements : - **Version en argument** : appelé directement depuis PowerShell, `-createreleaseversion=2026.10.4` est coupé en `2026` et `.10.4` (version `2026` créée, déplacée vers `LifePlay_Backup_5.5\H3a`) : passer par `cmd /c '…'`. - **Sources des mods hors du jeu de base** : un plugin dans `/Mods/` est un plugin Mod chargé par l'éditeur et cuit avec le jeu ; un module dans `Content/Modules/` est livré avec le jeu. Les sources du mod d'essai sont donc dans `ModSources/LPHairSample/` (`.uplugin`, `Content/` non versionné, `Module/` texte) ; pour la cuisson DLC, le plugin est placé temporairement dans `Mods/` (H3b : `Tools/BuildMod.ps1`). Les builds de test H3a/UI15 ont été faits avec les sources sorties du projet ; vérifié : aucun asset `/LPHairSample/` dans les paquets du jeu. - **Activation des modules** (défaut de H1, présent en V1.4) : pour le jeu (`GI.IsModEnabled`, gestionnaire de mods), un module absent de `GI.ModEnableds` est inactif ; `LPHairstyleSubsystem` le croyait actif (coiffure du mod présente, mod décoché). Corrigé : même règle que le jeu ; nouvelle clé `.lpmod` `MODULE_DEFAULT_ENABLED: true`, qui ajoute le module coché à la liste des mods s'il n'y figure pas (`lp_Revival`, livré avec le jeu). - **Gestionnaire de mods** : accessible seulement en partie ; entrée ajoutée sur l'écran titre (lot UI15, [17_Mods_Ecran_Titre.md](17_Mods_Ecran_Titre.md)). - `Tools/HairPipeline.ps1` accepte un dossier de plugin (`//...` → `Mods//Content/...`) et un fichier de crédits propre au mod (`credits`) ; `Tools/HairMaterials.py` contrôle les dépendances par chemin de paquet. - Relancer `HairMaterials.py` sur une coiffure dont les textures existent déjà dans un plugin fait planter la commandlet (boîte de dialogue de remplacement à l'import) : à traiter en H3b. ## 6. Outillage H3b (2026-10-03) Validé en jeu le 2026-10-03 (build `LifePlay_Build_5.5_H3b`, mod installé par l'outil). Organisation d'un mod d'assets : ``` ModSources//.uplugin plugin de contenu (versionné) ModSources//Content/ assets du mod (non versionnés, comme Content/Heavy) ModSources//Module/ module LifePlay en texte : .lpmod (MODULE_UNIQUEID = ), Hairstyles/*.lphair, CREDITS.txt (versionnés) ``` `/Mods/` reste vide : `Tools/ModSource.ps1` y déplace le mod le temps d'un travail (import, cuisson) puis le remet dans `ModSources/` (déplacements seulement). Le build du jeu de base ne contient donc rien des mods sans manipulation (vérifié : aucun asset `/LPHairSample/`, ni `Mods/`, ni module dans le build H3b). Fabrication : ```powershell .\Tools\HairPipeline.ps1 -Config Tools\mods\.json -Step Inspect # "mod": "", "folder": "//Hairs" .\Tools\HairPipeline.ps1 -Config Tools\mods\.json -Step Apply .\Tools\BuildMod.ps1 -Name [-Version 2026.10.4] [-Out D:\Game\LifePlay_Publication\Mods] [-Install \Windows] ``` `BuildMod.ps1` : contrôles (plugin, module, version de référence `Releases/`, aucun autre plugin dans `Mods/`), cuisson DLC par un `.cmd` écrit à côté du journal (`Saved/Logs/portage/mods/`), contrôle du conteneur (toutes les entrées sous `Mods//`), assemblage `-for-LifePlay-` (`LifePlay/Mods/`, `LifePlay/Content/Modules/`, `README.txt` bilingue), archive `.zip` testée ; un assemblage précédent est déplacé (`precedent_`). Mod d'essai : `LPHairSample-for-LifePlay-2026.10.4.zip`, 334 Ko, 8 fichiers. Corrections du lot : - `Tools/HairMaterials.py` : dans un plugin, le registre d'assets de la commandlet ne connaît pas encore les assets existants (`does_asset_exist` faux) ; la création d'un matériau existant ouvrait une boîte de dialogue et plantait (`Assertion failed: IsValid()`). Analyse synchrone du dossier au départ ; texture déjà importée depuis la même source réutilisée (autre source dans un plugin : refus, `-Rebuild`). - `HairPipeline.ps1 -Rebuild` d'un mod déplace aussi les textures vers la sauvegarde ; une reconstruction demande une nouvelle inspection (les maillages de travail sont rangés dans la sauvegarde après chaque application). - `LPHairstyleSubsystem` : coiffure de mod absente du registre d'assets acceptée si son paquet existe (`FPackageName::DoesPackageExist`, avertissement). - `BuildMod.ps1` : `cmd /c` avec guillemets imbriqués échoue depuis PowerShell (« syntaxe du nom de fichier incorrecte ») ; d'où le fichier `.cmd`. ## 7. Documentation et publication (H3c, 2026-10-03) - Guide des moddeurs (anglais, avec les guides d'origine de Vinfamy) : `Docs/Modding/FullGuides/Creating_Asset_Mods.md` (fonctionnement, prérequis, création, coiffures, éditeur, cuisson et essai, diffusion, dépannage) ; `Docs/Modding/ShortGuides/explanation.lphair` (format `.lphair`) ; `explanation.lpmod` complété (`MODULE_DEFAULT_ENABLED`). - Outils des moddeurs : `Tools/NewMod.ps1` (squelette `ModSources/` : `.uplugin`, `Module/.lpmod`, `CREDITS.txt`, et avec `-Hairs` `Tools/mods/.json`), `Tools/EditMod.ps1` (éditeur ouvert avec le mod monté, remis en place à la fermeture). Essai de `NewMod.ps1` sur un mod jetable `H3cTest` (déplacé vers `LifePlay_Backup_5.5\H3c`) : fichiers conformes, nom déjà pris refusé. - Publication : la version de référence fait partie de chaque publication (`10_Protocole_Operations.md` §6, liste de contrôle `09_Commandes_Utiles.md` §7). L'archive des sources V1.4 publiée ne la contient pas : les mods d'assets sont donc possibles pour les moddeurs à partir de la publication suivante (la V1.4 reste jouable avec des mods cuits ici contre `Releases/2026.10.4`). - Ne pas faire de build complet destiné à la publication sans recréer la version de référence correspondante. ## 8. Métadonnées, avertissement de version et paquet Pocolov (H3d, 2026-10-03) Validé en jeu le 2026-10-03 (build `LifePlay_Build_5.5_H3d`). - **Lecture des `.lpmod` par le jeu** (vérifiée dans `GI`) : seules les lignes 1 à 3 sont lues, **par position** (`MODULE_UNIQUEID`, `MODULE_NAME`, `MODULE_AUTHOR`) ; le gestionnaire de mods affiche le fichier entier (`ModuleTick.FullString`). Toute ligne ajoutée après la troisième est donc sans risque et visible des joueurs. - **Clés ajoutées** : `MODULE_VERSION`, `MODULE_CREATED`, `MODULE_LICENSE`, `MODULE_AUTHOR_LINK`, `MODULE_PRICE` (écrites par `Tools/NewMod.ps1`), `MODULE_PACKAGED`, `MODULE_GAME_VERSION` (écrites par `Tools/BuildMod.ps1` dans la copie livrée). `BuildMod.ps1` contrôle l'ordre des trois premières lignes, reporte la version et le lien de l'auteur dans le `.uplugin` livré (`VersionName`, `CreatedByURL`), nomme l'archive `--for-LifePlay-.zip` et refuse un mod payant dont `CREDITS.txt` cite une licence non commerciale ou de magasin. - **Avertissement** : `ULPModInfoSubsystem` (C++, `Source/LifePlay`) lit les `.lpmod` et, toutes les 2 s, compare le `MODULE_GAME_VERSION` des modules **actifs** à `ProjectVersion` ; un écart ouvre une fenêtre Slate « Mod version warning » (indépendante des Blueprints, aussi sur l'écran titre), une fois par module et par session. Testé avec un module texte déclaré pour 2026.9.1 (déplacé ensuite vers `LifePlay_Backup_5.5\H3d`). Métadonnées accessibles aux Blueprints : `GetModuleMetadata(Module, Key)`. - **Paquet `lpr_hair_pocolov` 1.0** : sur 192 produits Pocolov (Fab), les 12 coiffures CC BY (03, 05, 06, 07, 09, 10, 11, 13, 14, 15, 16, 17) ; exclus : 5 coiffures payantes (licence standard Fab : redistribution interdite), vêtements « Avatall » (autre squelette), objets, armes, véhicules. Configuration `Tools/mods/lpr_hair_pocolov.json` (réglages HAIRS1 repris ; détection identique aux choix validés). Pocolov 07, 09, 10, 13, 14 téléchargées (CC BY 4.0, accord du 2026-10-03). La Pocolov 09 a sa calotte en maillage séparé, non convertie : rendu jugé correct en jeu. `MODULE_DEFAULT_ENABLED: true` (les sauvegardes qui utilisaient les anciennes coiffures du jeu les retrouvent dès que le paquet est installé). Archive `lpr_hair_pocolov-1.0-for-LifePlay-2026.10.4.zip` (3,8 Mo, 70 paquets cuits). - **Jeu de base allégé** : les 6 Pocolov de `/Game/Heavy/New5/Hairs` et le module `lp_Revival` sont retirés du jeu (déplacés vers `LifePlay_Backup_5.5\H3d\retires`), leurs crédits passent dans le `CREDITS.txt` du paquet ; le mod d'essai `LPHairSample` est retiré (même dossier). Modules essentiels de la prochaine publication : `Tools/modules/next.json`.