Structure d'un mod et manifest.lua¶
Un mod est un dossier placé dans mods/ qui contient un manifest.lua. C'est
ce fichier qui fait du dossier un mod : il lui donne un nom dans le gestionnaire
de mods, il lui permet de démarrer tout seul, et il liste les fichiers à
charger. Un dossier sans manifeste est ignoré, même s'il déborde de Lua. Les
deux anciens formats restent lus, voir Formats hérités.
Le dossier mods/ se trouve à côté de GTAIV.exe, avec le loader lui-même.
Voir Installation.
Le manifeste¶
-- manifest.lua
name 'Mon Premier Mod'
author 'Vous'
description 'Soigne le joueur sur F4.'
version '1.0.0'
episode 'IV'
autostart = true
client_scripts {
'lib/menu.lua',
'main.lua',
}
| Clé | Défaut | Rôle |
|---|---|---|
name |
le nom du dossier | Nom affiché dans le gestionnaire de mods. |
author |
vide | Texte libre. |
description |
vide | Texte libre, affiché dans le gestionnaire. |
version |
vide (1.0.0 à la réécriture) |
Texte libre. |
episode |
vide | L'épisode visé, par exemple IV, TLAD ou TBoGT. Purement déclaratif : le loader ne cache jamais un mod là-dessus. |
autostart |
false |
true démarre le mod tout seul une fois le jeu en partie. |
client_script / client_scripts |
main.lua |
Les fichiers à charger, dans l'ordre, dans le même état Lua. Les deux orthographes remplissent la même liste. |
main |
le premier script client | Fichier d'entrée. Rarement utile. |
Chaque clé est optionnelle, un manifeste peut donc tenir en une ligne, mais le fichier doit exister.
Comment la syntaxe marche¶
Le manifeste est du vrai Lua, exécuté dans un état jetable qui n'a ni io ni
os. Chaque ligne est un appel de fonction, et Lua permet d'enlever les
parenthèses quand l'argument unique est une chaîne ou une table. C'est ce qui
donne à name 'Mon Premier Mod' et à client_scripts { ... } leur allure.
autostart true est une erreur de syntaxe
On ne peut enlever les parenthèses que pour une chaîne ou une table, jamais
pour un booléen. Écrivez autostart = true ou autostart(true). Pareil
pour toute autre clé à laquelle vous voulez donner true, false ou un
nombre.
Une clé que le loader ne connaît pas n'est pas une erreur. Elle est rangée dans les métadonnées du mod, vous pouvez donc déclarer ce dont vous avez besoin :
Laisser l'éditeur l'écrire¶
Rien de tout cela n'est à taper à la main. Dans le gestionnaire de mods, faites
un clic droit puis New mod : le loader crée le dossier, un manifest.lua
avec les clés ci-dessus et un commentaire sur chacune, et un main.lua qui
contient déjà la boucle de thread et un onUnload. Il ouvre ensuite main.lua
pour vous. autostart part à false, le nouveau mod attend donc un Start
tant que vous ne changez pas cette ligne.
Vos commentaires survivent. Quand un mod appelle Script.DeclareModFile, le
loader glisse le nouveau chemin dans le bloc client_scripts au lieu de
réécrire le fichier, donc rien de ce que vous y avez écrit n'est perdu.
Plusieurs fichiers¶
client_scripts est ce qui permet d'étaler un mod sur plusieurs fichiers Lua.
Ils sont tous chargés dans le même lua_State, dans l'ordre où ils
apparaissent, donc une globale définie dans le premier est visible dans le
suivant.
Deux conséquences :
- L'ordre compte. Mettez les bibliothèques avant le code qui s'en sert.
- Les chemins s'écrivent avec
/et sont relatifs au dossier du mod.
Un mod peut aussi ajouter un fichier à son propre manifeste en cours
d'exécution avec
Script.DeclareModFile,
ce qui permet à un mod qui génère des fichiers de données de les faire charger
au démarrage suivant.
Streaming : modèles et données du jeu¶
Un mod ne se limite pas au Lua. Deux dossiers de plus lui permettent de livrer
un véhicule, un modèle d'arme, une texture ou quelques lignes de
handling.dat, et le joueur l'installe toujours en copiant un seul dossier dans
mods/. Aucun fichier du jeu n'est édité, et rien n'est écrit dans le dossier
du jeu.
📁 mods/
└── 📁 MaVoiture/
├── 📄 manifest.lua
├── 📁 stream/
│ ├── 📄 f620.wft
│ └── 📄 f620.wtd
└── 📁 gamedata/
├── 📄 vehicles.ide
├── 📄 handling.dat
└── 📄 carcols.dat
Les deux dossiers sont servis par dinput8.dll, qui se tient entre le jeu et
ses fichiers. Ils sont lus une fois, au lancement : un fichier ajouté dans
stream/ ou gamedata/ pendant que le jeu tourne est pris au lancement
suivant, pas à un rechargement du mod.
stream/¶
Chaque fichier sous stream/, sous-dossiers compris, est empaqueté au
lancement dans une archive IMG que le moteur monte comme les siennes. Modèles,
textures, collisions et animations (.wdr, .wft, .wtd, .wbn, .wad…)
vont tous là, sous leur simple nom de fichier : le moteur trouve une ressource
par son nom, pas par son chemin.
L'archive est écrite dans le cache du loader, le dossier du jeu s'il est
inscriptible et %LOCALAPPDATA%\LuaModLoader\cache\ sinon, et elle n'est
reconstruite que si le nom, la taille ou la date d'un fichier a changé.
asi_loader.log dit combien d'entrées y sont entrées et si le cache a été
réutilisé.
Si le joueur a posé son propre update/update.img dans le dossier du jeu, ses
entrées sont absorbées dans la même archive plutôt que contournées, et un
fichier de mod passe devant une entrée absorbée du même nom.
stream/ ajoute des fichiers ; il ne remplace pas ceux du jeu. Pour remplacer
un fichier d'origine, utilisez la disposition de FusionFix : update/common/…
et update/pc/… dans le dossier du jeu sont servis par le loader à la place de
common/… et pc/….
gamedata/¶
Un fichier sous gamedata/ est un fragment du fichier du jeu qui porte le
même nom : vehicles.ide, handling.dat, carcols.dat, default.ide,
images.txt… Il ne contient que vos lignes, sous l'en-tête de section de ce
format :
# gamedata/vehicles.ide
cars
f620, f620, car, SUPERGT, F620, VEH@LOW, 100, 999, 0.2740, 0.2740, 0, 1.0, 1.0, 0
end
Les lignes hors de toute section vont à la fin du fichier fusionné, ce qu'il
faut à handling.dat, qui n'a pas de sections. Vos lignes s'ajoutent à ce que
le jeu, FusionFix ou un autre mod sert déjà pour ce fichier : deux mods qui
ajoutent chacun une voiture ajoutent deux lignes au même vehicles.ide au lieu
que l'un écrase l'autre. Le fichier fusionné vit dans le cache et est servi au
moteur par la redirection de fichiers ; l'original n'est jamais touché.
Le moteur a une table de 160 entrées de handling ; le loader la porte à 512, donc ajouter des véhicules ne la fait pas déborder.
Fichiers en lignes uniquement
Le fusionneur comprend les fichiers faits d'enregistrements sous des
en-têtes de section, ou d'enregistrements nus sans section.
WeaponInfo.xml est du XML et n'est pas fusionné depuis gamedata/ ;
un fragment posé là serait ajouté comme du texte. Livrez plutôt un
update/common/data/WeaponInfo.xml complet.
Globales propres au mod¶
Chaque mod tourne dans son propre état Lua sandboxé, et le loader y injecte deux globales :
| Globale | Valeur |
|---|---|
MOD_DIR |
Chemin absolu du dossier du mod, sans séparateur final. nil pour un buffer lancé depuis l'éditeur. |
SCRIPT_NAME |
Identifiant de l'instance en cours. |
MOD_DIR est ce à partir de quoi on construit les chemins. Le répertoire de
travail du process appartient au jeu, pas à votre mod :
Le bac à sable
Un mod peut lire et écrire ses propres fichiers (io.open), mais le shell
est hors de portée : os.execute, os.exit et io.popen sont retirés de
la bibliothèque standard. Rien de ce que fait un mod ne doit pouvoir ouvrir
une fenêtre de commande.
Formats hérités¶
Deux anciennes dispositions restent lues, dans cet ordre : manifest.lua
d'abord, puis manifest.xml, puis meta.xml. Un mod qui marche continue de
marcher, et rien n'a besoin d'être converti.
manifest.xml était le format du loader avant manifest.lua. Les mêmes
champs, en XML :
<mod>
<name>Mon Premier Mod</name>
<description>Soigne le joueur sur F4.</description>
<version>1.0.0</version>
<main>main.lua</main>
<autostart>true</autostart>
<files>
<file>main.lua</file>
</files>
</mod>
Son <autostart> est comparé littéralement à true, donc TRUE, 1 et yes
sont lus comme false. Script.DeclareModFile continue d'écrire du XML pour un
mod qui en a encore un, rien n'est converti dans votre dos.
meta.xml vient des arborescences de ressources multijoueur. Chaque balise
<script> de type="client" ou type="shared" apporte son src à la liste
des fichiers, et la première devient le fichier d'entrée. Dans ce cas,
autostart vaut implicitement true.
Les nouveaux mods doivent utiliser manifest.lua.
Démarrer, arrêter, recharger¶
- Autostart : les mods marqués
<autostart>true</autostart>sont mis en file au lancement et démarrent au premier tick réellement en jeu, jamais pendant l'écran de chargement, où le moteur n'a pas encore de contexte de script. - À la main : depuis l'éditeur in-game F6, dont la liste de mods permet
de
Start,StopetReloadn'importe quel mod sans quitter la partie. - Reload repart de zéro : l'état Lua est jeté et les fichiers sont rechargés. Aucun besoin de relancer le jeu pour itérer.
Il n'y a pas de onLoad()
L'initialisation, c'est simplement le code écrit au niveau du fichier : il
s'exécute une fois, au chargement. À la sortie, onUnload() est bien
appelée : le loader l'invoque juste avant de fermer l'état Lua, lors d'un
arrêt comme sur la première moitié d'un rechargement. Une erreur levée
dedans est journalisée et l'état est fermé malgré tout.