Créer la base de son mod
-
Ce tutoriel est également disponible en vidéo.
Sommaire
Introduction
Dans ce tutoriel nous allons créer la base d’un mod. Elle est constituée d’une classe principale (celle que Forge va détecter à l’aide de l’annotation @Mod), d’une classe client, d’une classe serveur et d’une classe commune aux deux. En effet depuis la version 1.3.2 de Minecraft Forge permet de créer très facilement un mod compatible à la fois client et serveur (dit universal) grâce à la gestion des sides (client ou serveur). Il faudra cependant faire attention à bien utiliser les différentes fonctions au bon endroit, par exemple utiliser une classe client (comme par exemple la classe Minecraft ou la classe WorldClient) sur une classe commune de votre mod ou une classe serveur de votre mod causera un crash du serveur.
Nous allons également créer quelques fichiers de ressource. À propos de ressource, vous avez sûrement remarqué qu’il y a deux dossiers dans le projet : src/main/java et src/main/resources. Le premier doit être utilisé pour tous les fichiers .java et le deuxième pour les ressources, c’est-à-dire tout le reste (.png pour les images, .lang pour les fichiers de langages, .json pour les modèles json ou listes de son, .ogg pour les sons, .obj pour les modèles avancés, etc …).
Pré-requis
- Installer et configurer l’espace de travail de Forge
- Avoir des connaissances en Java
Code
La classe principale :
Commencez par créer un nouveau package à l’intérieur du dossier src/main/java. Dans mon cas je vais l’appeler “fr.minecraftforgefrance.tutorial”. Nous avons déjà parlé de la nomination des packages dans le tutoriel sur la base de Java. Pour rappel, si vous avez un nom de domaine utilisez-le dans le sens inverse (extension du domaine.nom du domaine.si présent, nom du sous-domaine) puis ajoutez le nom du mod. Si vous n’avez pas de nom de domaine vous pouvez mettre simplement votre pseudo.nom du mod. Il est très important de mettre le nom du mod dans le nom du package, pour qu’en cas de crash l’utilisateur puisse directement savoir de quel mod vient le problème.
Ensuite créez une nouvelle classe dans ce package. Cette classe va être la classe principale du mod, je vous conseille de la nommer “ModNomDuMod”. Ici et dans tous les autres tutoriels fait par moi (robin4002), elle sera nommée «ModTutorial» (par convention une classe commence toujours par une majuscule. Au lieu de mettre un espace, on met simplement une majuscule à la première lettre du mot suivant. D’où le nom «ModTutorial») :
package fr.minecraftforgefrance.tutorial; public class ModTutorial { }
Voila ma classe à l’heure actuelle, elle est un peu vide. La vôtre devrait être identique, hormis pour le nom et le package.
Au dessus de la déclaration de la classe (c’est à dire au dessus de «public class ModTutorial») nous allons ajouter l’annotation @Mod. Cette annotation est ajoutée par Forge Mod Loader (FML) et c’est grâce à elle que Forge va savoir que cette classe est la classe principale de notre mod (et donc qu’il faut la charger).
En ajoutant @Mod() Eclipse va indiquer une erreur, car il ne connaît pas @Mod. En passant la souris dessus Eclipse devrait vous proposer d’importer «net.minecraftforge.fml.common.Mod», faites-le cela va corriger l’erreur.
Cependant une autre erreur va apparaître, en effet @Mod demande au moins une valeur non nulle pour la chaîne de caractères «modid». Il faut donc lui indiquer un l’id de votre mod. Cette id sera simplement le nom du mod, entièrement en minuscule et sans espace. Voila à quoi devrait ressembler votre classe principale maintenant :package fr.minecraftforgefrance.tutorial; import net.minecraftforge.fml.common.Mod; @Mod(modid = "tutorial") public class ModTutorial { }
Comme on va souvent avoir besoin du modid, je vous propose de créer une constante pour ce dernier :
package fr.minecraftforgefrance.tutorial; import net.minecraftforge.fml.common.Mod; @Mod(modid = ModTutorial.MODID) public class ModTutorial { public static final String MODID = "tutorial"; // par convention une constante s'écrit tout en majuscule, c'est pour cela que j'ai écris MODID et non modid. }
Voila, maintenant si vous avez besoin du modid quelque part il suffira d’utiliser «NomDeVotreClassePrincipale.MODID»
L’annotation @Mod a d’autres variables que modid mais nous ne sommes pas obligés de leur donner une valeur. Appuyez sur la touche ctrl de votre clavier et cliquez sur Mod. La classe Mod va s’ouvrir, vous pouvez y voir toutes les variables. Cette classe est plutôt bien documentée.
Je vous propose d’ajouter une valeur pour variable suivante :- name : le nom complet du mod (avec les espaces et les majuscules cette fois)
- version : la version de votre mod
- acceptedMinecraftVersions : la version de Minecraft que votre mod supporte. Utilise le «maven version range». La documentation est disponible ici.
Une fois complété cela donne ceci :@Mod(modid = ModTutorial.MODID, name = "Mod Tutorial", version = "0.1", acceptedMinecraftVersions = "[1.8.9]")
Dans cet exemple le mod ne pourra que être chargé en 1.8.9. Si vous êtes en train de faire un mod 1.9.4, 1.10.2 ou 1.11.2 pensez à adapter
Pour autoriser le chargement du mod en 1.8.8 et en 1.8.9 on aurait utilisé la syntaxe suivante : acceptedMinecraftVersions = “[1.8.8,1.8.9]”
Dans cette série de tutoriels nous allons traiter principalement la création de bloc, item, entité, etc … de choses qui sont à la fois présentes sur le client et sur le serveur.
Si vous souhaitez créer un mod qui n’est que présent sur le client (par exemple un mod de minimap) vous pouvez ajouter toujours dans la ligne @Mod clientSideOnly = true. Ainsi votre mod ne sera pas chargé si un utilisateur l’installe sur un serveur. De même si vous souhaitez faire un mod qui n’est que présent sur le serveur (par exemple un mod qui n’ajoute que des commandes) vous pouvez ajouter serverSideOnly = true pour que le mod ne soit pas chargé s’il est installé sur un client.
Toujours dans la classe @Mod il y a des variables telles que guiFactory et updateJSON qui auront le droit à leur propre tutoriel.
Revenons à notre classe principale. Pour l’instant elle ressemble à ceci :
package fr.minecraftforgefrance.tutorial; import net.minecraftforge.fml.common.Mod; @Mod(modid = ModTutorial.MODID, name = "Mod Tutorial", version = "0.1", acceptedMinecraftVersions = "[1.8.9]") public class ModTutorial { public static final String MODID = "tutorial"; }
Nous allons lui ajouter une instance (elle sera utilisée principalement pour tout ce qui est réseau), un logger (pratique pour déboguer son code ou afficher une erreur dans la console) et deux fonctions qui seront appelées lors des différentes étapes de chargement du jeu.
Pour l’instance, ajoutez en dessous de la constante MODID ceci :
@Instance(ModTutorial.MODID) public static ModTutorial instance;
Je rappelle que ModTutorial est le nom de ma classe principale. On peut penser que cette variable va rester null car on ne lui affecte pas de valeur. En fait c’est l’annotation Instance qui va lui injecter sa valeur. Il est donc important que le modid indiqué entre les parenthèses de l’instance soit correct (ce qui devrait être le cas comme on a utilisé notre constante) mais surtout que l’annotation soit bien au dessus de la ligne «public static VotreClassePrincipale instance;». S’il y a une autre variable entre, cela ne fonctionnera pas.
Nous allons maintenant ajouter le logger (importez org.apache.logging.log4j.Logger) et les deux fonctions suivantes :
public static Logger logger; @EventHandler public void preInit(FMLPreInitializationEvent event) { logger = event.getModLog(); // initialise le logger. event.getModLog() retourne un logger avec votre modid } @EventHandler public void init(FMLInitializationEvent event) { }
Il est très important d’avoir EventHandler au dessus de la fonction preInit et de la fonction init. C’est grâce à cette annotation que FML va savoir qu’il faut appeler ces fonctions. De même pour les arguments FMLPreInitializationEvent et FMLInitializationEvent. Ces arguments vont permettre à FML de savoir quand il faut appeler ces fonctions.
Pour l’instant nous allons rester avec ces deux fonctions car elles permettent de presque tout faire. Dans le package net.minecraftforge.fml.common.event vous pouvez voir d’autres événements qui peuvent être mis en argument d’une fonction. Par exemple FMLServerStartingEvent qui s’utilisera de la façon suivante :
@EventHandler public void onServerStart(FMLServerStartingEvent event) { }
et qui sera nécessaire pour l’enregistrement de commande. Pour l’instant inutile de mettre dans votre classe principale (puisque votre mod ne contiendra pas forcement de commande), on le fera dans le tutoriel sur les commandes.
Bien sûr, n’oubliez pas d’importer les différentes classes pour corriger les erreurs. Vous pouvez utiliser le raccourci ctrl + shift + o pour aller plus vite. Votre classe principale devrait maintenant ressembler à ceci :
package fr.minecraftforgefrance.tutorial; import org.apache.logging.log4j.Logger; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.SidedProxy; import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.Mod.Instance; import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; @Mod(modid = ModTutorial.MODID, name = "Mod Tutorial", version = "0.1", acceptedMinecraftVersions = "[1.8.9]") public class ModTutorial { public static final String MODID = "tutorial"; @Instance(ModTutorial.MODID) public static ModTutorial instance; public static Logger logger; @EventHandler public void preInit(FMLPreInitializationEvent event) { logger = event.getModLog(); } @EventHandler public void init(FMLInitializationEvent event) { } }
Pour terminer nous allons ajouter les deux lignes suivantes (à mettre en dessous de «public static NomDeVotreClassePrincipale instance») :
@SidedProxy(clientSide = "à modifier ensuite", serverSide = "à modifier ensuite") public static TutorialCommon proxy;
TutorialCommon sera la classe commune au client et au serveur. Vous pouvez l’appeler ModidCommon (ce que j’ai fait), CommonProxy ou encore ModidCommonProxy. Gardez au moins «Common» dans le nom pour que l’on sache qu’il s’agit de la classe commune.
N’oubliez pas d’importer «net.minecraftforge.fml.common.SidedProxy».
Dans le cas où votre mod est un mod client seulement ou serveur seulement, faire cette séparation avec sidedproxy n’a pas d’utilité.La classe commune :
Faites un clic droit sur votre package puis allez dans new -> class. Créez une classe nommée comme ce que vous avez mis dans votre classe principale juste avant.
Dans cette classe nous allons ajouter deux fonctions, toutes les deux de type void. La première sera nommée preInit et aura comme argument un fichier, qui va correspondre au fichier de configuration. La deuxième sera nommée init. Comme vous l’avez surement deviné, cela correspond aux deux fonctions que nous avons créées dans la classe principale. Nous allons ensuite les appeler dans la classe principale. Voila ce que ça donne :package fr.minecraftforgefrance.tutorial; import java.io.File; public class TutorialCommon { public void preInit(File configFile) { System.out.println("pre init côté commun"); } public void init() { } }
(il ne faut pas oublier d’importer java.io.File)
Bien sûr chez vous le nom de la classe et du package seront différents. Vous pouvez aussi remarqué que j’ai ajouté une fonction System.out.println qui va afficher un message dans la console. Ceci est juste pour vous montrer que ce message s’affichera à la fois lors du lancement du client mais aussi lors du lancement du serveur. Vous allez pouvoir le retirer à la fin du tutoriel.
On va tout de suite en profiter pour appeler les deux méthodes dans la classe principale. Retournez donc dans cette dernière et à l’intérieur de la fonction «public void preInit(FMLPreInitializationEvent event)» ajoutez «proxy.preInit(event.getSuggestedConfigurationFile());»
getSuggestedConfigurationFile est une fonction de la classe FMLPreInitializationEvent qui renvoie le fichier de configuration votreModid.cfg se trouvant dans le dossier minecraft/config/Et à l’intérieur de la fonction «public void init(FMLInitializationEvent event)» ajoutez «proxy.init();»
Vos deux fonctions dans la classe principale devraient être maintenant comme cela :@EventHandler public void preInit(FMLPreInitializationEvent event) { logger = event.getModLog(); proxy.preInit(event.getSuggestedConfigurationFile()); } @EventHandler public void init(FMLInitializationEvent event) { proxy.init(); }
La classe client :
Nous allons maintenant créer une classe qui sera que appelée côté client. Tout le code se trouvant dans cette classe ne sera donc exécuté uniquement sur le client.
Créez une nouvelle classe nommée exactement comme votre classe commune mais en remplaçant «Common» par «Client». Ma classe commune s’appelle TutorialCommon comme ma classe client se nommera TutorialClient. Votre classe devra avoir comme classe mère la classe commune :
Une fois la classe créé elle devrait ressembler à ça :package fr.minecraftforgefrance.tutorial; public class TutorialClient extends TutorialCommon { }
Maintenant cliquez sur le menu source d’eclipse (le deuxième en partant de la gauche). Cliquez sur «Override/Implements Methods». Un menu va s’ouvrir, vous devez y voir votre classe commune ainsi que la classe Object. Cochez votre classe commune puis cliquez sur ok.
Et voila le résultat :package fr.minecraftforgefrance.tutorial; import java.io.File; public class TutorialClient extends TutorialCommon { @Override public void preInit(File configFile) { // TODO Auto-generated method stub super.preInit(configFile); } @Override public void init() { // TODO Auto-generated method stub super.init(); } }
Les super.nomDeLaFonction() sont très importants, ils permettent d’exécuter le code qui se trouve dans la classe mère, c’est à dire votre classe commune.
Bon on va juste retirer les TODO et ajouter de quoi afficher un petit message dans la console :package fr.minecraftforgefrance.tutorial; import java.io.File; public class TutorialClient extends TutorialCommon { @Override public void preInit(File configFile) { super.preInit(configFile); System.out.println("pre init côté client"); } @Override public void init() { super.init(); } }
Et voila !
Comme vous pouvez le voir j’ai mis le System.out.println en dessous de super.preInit(configFile);. Dans les futurs tutoriels quand il sera indiqué de mettre quelque chose dans le votre classe client (ou dans le proxy client, il arrivera qu’on l’appelle aussi comme ceci), il faudra toujours ajouter le code en dessous du super.nomDeLaFonction(). Pourquoi ? Car dans la plupart des cas nous allons faire des initialisations dans la fonction se trouvant dans la classe commune puis dans la classe client nous allons utiliser ces objets. Si on fait l’inverse il va donc avoir une erreur du type NullPointerException (aussi appelée NPE) car ils ne seront pas encore initialisées au moment de l’utilisation.
Une dernière chose à faire, cette fois dans la classe principale. Souvenez-vous de ces deux lignes?
@SidedProxy(clientSide = "à modifier ensuite", serverSide = "à modifier ensuite") public static TutorialCommon proxy;
Nous y avons laissé des chaines de caractères à modifier ensuite. En fait ici FML va injecter la valeur de proxy en fonction du side. Si le jeu lancé est un client, alors il va donner à proxy la valeur de ce qui se trouve dans clientSide. Or pour l’instant cela ne risque pas de fonctionner. Il faut donc remplacer cela par le chemin complet de notre classe. C’est à dire son package.son nom.
Cela va donc donner dans mon cas :@SidedProxy(clientSide = "fr.minecraftforgefrance.tutorial.TutorialClient", serverSide = "à modifier ensuite") public static TutorialCommon proxy;
puisque ma classe se nomme TutorialClient et que son package est fr.minecraftforgefrance.tutorial.
La classe serveur :
La classe serveur a exactement le même principe que la classe client, sauf que celle-ci ne sera appelée que dans le cas où un serveur dédié est lancé. Je précise bien serveur dédié, c’est à dire quand l’application minecraft server sera lancée. Dans le cas d’un monde solo, cette dernière ne sera pas appelée.
Son fonctionnement étant le même que pour la classe client, vous avez simplement à répéter la même chose que pour la classe client mais au lieu de mettre Client à la place de Client vous allez mettre Server :).Voila le résultat :
package fr.minecraftforgefrance.tutorial; import java.io.File; public class TutorialServer extends TutorialCommon { @Override public void preInit(File configFile) { super.preInit(configFile); System.out.println("pre init côté serveur"); } @Override public void init() { super.init(); } }
Et bien sûr il ne faut pas oublier de modifier la ligne de proxy dans la classe principale :
@SidedProxy(clientSide = "fr.minecraftforgefrance.tutorial.TutorialClient", serverSide = "fr.minecraftforgefrance.tutorial.TutorialServer") public static TutorialCommon proxy;
Et voila !
Cliquez sur la flèche verte d’eclipse (à droite du scarabée vert), le client devrait se lancer. Dans la console vous devrez apercevoir ceci :[19:05:59] [Client thread/INFO] [STDOUT]: [fr.minecraftforgefrance.tutorial.TutorialCommon:preInit:9]: pre init côté commun [19:05:59] [Client thread/INFO] [STDOUT]: [fr.minecraftforgefrance.tutorial.TutorialClient:preInit:11]: pre init côté client
Et maintenant cliquez sur la flèche noire/verte dirigée vers le bas à côté de la flèche verte, et choisissez «2 server».
Le serveur va maintenant se lancer, il va crasher la première fois car l’EULA n’est pas accepté. Allez dans le dossier de votre setup de forge, puis dans le dossier run et ouvrez le fichier eula.txt avec un éditeur de texte. Remplacez false par true et relancez le serveur (cliquer sur la flèche verte directement lancera cette fois le serveur car Eclipse lance toujours par défault le dernier lancé. Pour lancer à nouveau le client il faut cliquer sur la petite flèche noire puis choisir «2 client»). Dans la console il devrait avoir ceci :[19:54:05] [Server thread/INFO] [STDOUT]: [fr.minecraftforgefrance.tutorial.TutorialCommon:preInit:9]: pre init côté commun [19:54:05] [Server thread/INFO] [STDOUT]: [fr.minecraftforgefrance.tutorial.TutorialServer:preInit:11]: pre init côté serveur
Voila nous avons donc confirmation que lorsque le client se lance, les fonctions preInit de la classe client et de la classe commune sont appelées. Dans le cas du serveur on a vérifié que les fonctions preInit de la classe serveur et de la classe commune sont appelées. Ce qu’on vient de faire, c’est débogguer notre code. Si un jour une fonction ne fait pas ce qu’elle devrait faire, pensez à ajouter des System.out.println dedans et regarder si les messages sont présents dans la console. Si c’est le cas la fonction est bien appelée et donc le problème vient d’ailleurs. Si ce n’est pas le cas vous avez trouvé votre problème.
Vous pouvez maintenant retirer les System.out.println se trouvant dans trois classes citées précedement.
Le fichier d’information du mod :
La partie programmation de ce tutoriel est terminée, nous allons maintenant nous concentrer sur les ressources.
Dans le dossier src/main/resources vous devrez avoir par défault un dossier nommé mcmod.info. Nous allons le modifier.- modid : le modid de votre mod, remplacez donc examplemod par votre modid
- name : le nom du mod, à nouveau ce que vous avez mis dans la classe principale de votre mod
- description : une courte description du mod, vous pouvez ne rien mettre en laissant juste “”.
- version : la version du mod, ne changez rien ici ${version} est automatiquement remplacé par la valeur se trouvant dans le build.gradle
- mcversion : la version de minecraft, comme pour la version, le remplacement est automatique
- url : le lien vers la présentation de votre mod
- updateUrl : l’url pour télécharger le mod
- authorList : la liste des auteurs. [“auteur1”, “auteur2”, “auteur3”] pour plusieurs auteurs, [“auteur”] pour un seul auteur
- credits : créditez ici les éventuels contributeurs.
- logoFile : le chemin à partir du début de l’archive vers le logo du mod. Si votre logo se trouve directement dans src/main/resouces et s’appelle logo.png il faudra mettre logo.png
- screenshots : une liste de screenshots, fonctionne comme pour le logo (chemin à partir du début de l’archive)
- dependencies : les dépendances, je conseille plutôt d’utiliser le String dépendance de l’annotation @Mod.
C’est tout pour ce fichier.
Les fichiers de langues :
Nous allons finaliser ce tutoriel en créant les fichiers de langages.
Dans le dossier src/main/resources créez un nouveau package nommé “assets.votremodid.lang”.
Ensuite dans ce dossier créez deux fichiers, un fichier nommé en_US.lang pour les versions < 1.11.2, en_us.lang pour la 1.12 et plus récent (clic droit sur le package, new, file) et un fichier nommé fr_FR.lang (fr_fr.lang pour la 1.12 et plus récent).
Le format pour le nom des fichiers de langages est langage_REGION.lang (langage_region.lang en 1.12+), ainsi si vous souhaitez ajouter le français canadien il faudra ajoutez un fichier nommé fr_CA.lang
Nous n’allons rien mettre dedans pour l’instant, sachez que dedans il aura tout simplement une ligne par traduction et cette ligne aura le forme suivante :
nom.non.localisé=Nom localisé
(le nom non-localisé étant le nom anglais dans le code, le nom localisé est le nom une fois traduit dans la langue correspondante).Résultat
Voir le commit sur github Le commit sur github montre clairement où ont été placé les fichiers, ainsi que ce qui a été ajouté et retiré dans le fichier.
En vidéo
https://www.youtube.com/watch?v=_lfyYtcKe9U
Crédits
Rédaction :
Correction :
Ce tutoriel de Minecraft Forge France est mis à disposition selon les termes de la licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International -
Après beaucoup de retard ce tutoriel est enfin terminé ! À corriger.
-
Je reviens de voyage, je m’occupe très prochainement de ça.
-
Je me demande pourquoi minecraft forge modifie autant de choses,car à chaque mise à jour des mods,c’est trèèèèèès long à faire,car perso quand j’ai voulu mettre à jour mes sources,ma classe principal était saturée d’erreur
-
Le problème ne vient pas que de forge mais aussi de fml et Mojang
-
Juste de Mojang.
FML est fusionné dans Forge depuis la 1.8.
Et Forge ne bouge pas tellement, il s’adapte aux changements de Minecraft.En soit le tutoriel sur la classe principale de la 1.6.4/1.7.10 fonctionne encore, j’ai décidé de refaire ce tutoriel car j’utilise maintenant une organisation plus logique des proxys.
-
Il faudrait penser à changer tous les “par conversion” en “par convention” (pourquoi conversion d’ailleurs?) et à indiquer explicitement (parce que sinon on ne le voit que dans le code montré ensuite) quelle classe Logger doit être importée.
-
Fail d’un correcteur ou moi même qui me suis loupé lors de la rédaction du tutoriel ?
En tout cas merci de l’avoir signalé, étrange que personne ne l’a remarqué avant toi. -
A noter, petite faute à la toute fin du tuto : “vouhaitez” au lieu de “souhaitez”.
Très bon tuto, je n’ai pas eu de soucis particulier, outre le fait que pour lancer un “Server”, il faut sélectionner le package dans le “Package Explorer”, et que ceci n’est pas nécessaire pour lancer un “Client”
Je continue ! Encore merci !
(Et au passage, aucun problème de réalisation en version 1.10.2.)
-
Décidément il y en a des fautes dans ce tutoriel x)
-
J’ai trouvé une faute de frappe
http://i.imgur.com/chHpZCB.png -
Corrigé, merci !
-
Je sais pas si c’est eclipse qui bug mais quand je lance la compilation ca me met un erreur: http://i.imgur.com/tYxJT92.png et pourtant la compilation marchait avant… J’ai testé plusieurs commande comme celles ci: “gradlew setupDecompWorkspace --refresh-dependencies” et “gradlew eclipse”. Ces commande on marché avant quand eclipse n’avais crée aucun fichiers run mais là… Donc si vous avez une solution je suis preneur
-
Quand tu lances la compilation ? C’est pas plutôt quand tu lances le jeu ça ?
Mets ton curseur dans une classe de ton mod, ça devrait fonctionner à nouveau. -
Ah oui c’est bon… merci
-
Je ne vois pas le paktage explorer dans eclipse
-
Fallait pas supprimer la barre où il est, va dans Window (en haut) ->Perspective -> Reset perspective…
-
Ok merci
-
http://www.noelshack.com/2017-03-1484676905-capture7.png
Pourquoi y a-t-il une croix a côté de forge 1.7.10 ? A cause de cela, je ne peux plus configurer les runs
-
Car tu as une erreur dans la classe ExempleMod.
Et tu ne devrais pas avoir de classe main java dans un mod.