Créer un GUI de configuration pour votre mod


  • Rédacteurs

    Sommaire

    Introduction

    Forge permet de créer un gui pour configurer votre mod, nous allons voir comment faire.
    Nous allons permettre au joueur de modifier le message de bienvenue affiché par le mod lors de la connexion du joueur au monde.

    Pré-requis

    Code

    La classe de configuration

    Dans un premier temps nous allons créer la classe qui contiendra la configuration du mod, dans mon cas je vais l'appeler ModTutorielConfig,
    vous pouvez la placer où vous voulez, personnellement je vais la créer dans un package nommé modtuto.config

    Dans cette classe nous allons tout d'abord créer un objet privé et static de type Configuration

    public class ModTutorielConfig
    {
        private static Configuration config;
    
        public static Configuration getConfig()
        {
            return config;
        }
    }
    

    Il faut importer net.minecraftforge.common.config.Configuration, j'ai créé un getter au passage afin que l'on puisse accéder à l'objet
    en dehors de la classe.

    Nous allons à présent créer une fonction qui sera appelée lors de la pre-initialisation du mod afin de charger le fichier de configuration

        public static void preInit(FMLPreInitializationEvent event)
        {
            config = new Configuration(event.getSuggestedConfigurationFile());
            config.load();
    
            syncConfig();
        }
    

    Cette fonction récupère le fichier de configuration puis le charge, enfin nous synchronisons les données contenues dans le fichier de configuration
    avec celles contenues dans le mod, créez la fonction syncConfig()

    public static void syncConfig() {
    
    }
    

    Dans cette fonction rajoutez le code suivant qui a pour but de vérifier si la configuration a changé et dans ce cas sauvegarder les données dans le fichier

    if(config.hasChanged()) {
        config.save();
    }
    

    Ce morceau de code devra rester à la fin de la fonction pour sauvegarder les changements préalablement effectués.
    Nous allons avoir besoin de deux variables pour le système de message de bienvenue, un String pour stocker le message à afficher, et une boolean
    pour permettre au joueur de désactiver l'affichage du message.
    Créons ces deux variables dans notre classe, ce qui nous donne :

    public class ModTutorielConfig
    {
        private static Configuration config;
    
        public static String welcomeMessage = "";
        public static boolean showWelcomeMessage = true;
    
        public static void preInit(FMLPreInitializationEvent event)
        {
            config = new Configuration(event.getSuggestedConfigurationFile());
            config.load();
    
            syncConfig();
        }
    
        public static Configuration getConfig()
        {
            return config;
        }
    
        public static void syncConfig()
        {
            // Le reste du code se mettra ici, il faut garder la sauvegarde à la fin de la fonction
    
            if(config.hasChanged())
            {
                config.save();
            }
        }
    }
    

    Avant d'attaquer la récupération des données depuis le fichier de configuration nous allons ajouter une dernière fonction à notre classe, elle se chargera
    de synchroniser le fichier de configuration lorsque le joueur changera les valeurs dans le Gui.

        public static void onConfigChanged(OnConfigChangedEvent event)
        {
            if(event.getModID().equals(ModTutoriel.MODID))
            {
                syncConfig();
            }
        }
    

    On vérifie si le fichier de configuration changé est bien celui de notre mod, et si c'est le cas on synchronise les données.
    Maintenant nous allons compléter la fonction syncConfig, vous insérerez le code au début de la fonction.

    // Permettra de stocker la propriété que nous allons récupérer
    Property prop;
    
    // Contiendra la liste des propriétés dans l'ordre dans lequel elles devront être affichées
    List <string>propOrder = new ArrayList<string>();
    

    Il faut importer java.util.List

    A la suite, nous utilisons la variable config pour récupérer la propriété contenant le message de bienvenue.

    prop = config.get(Configuration.CATEGORY_GENERAL, "welcomeMessage", "Bienvenue à toi %player%");
    prop.setComment("The code %player% will be replaced by player's name");
    prop.setLanguageKey("modtuto.config.welcomeMessage.title");
    

    Ici nous allons voir plusieurs points importants. Premièrement la fonction get(String category, String key, String defaultValue),
    cette fonction renvoie la propriété de la catégorie passée en premier argument et ayant pour nom le second argument, si elle n'existe pas
    elle renvoie une propriété ayant pour valeur la valeur passée un troisième argument.
    La classe Configuration contient la variable CATEGORY_GENERAL qui est égale à "general", ici nous l'utilisons mais sachez
    que vous pouvez mettre n'importe quelle valeur et que cela revient au même que marquer directement "general" en premier argument.
    C'est bon nous avons une référence vers notre propriété, grâce à la fonction setComment(String comment) nous avons rajouté
    un commentaire dans le fichier de configuration, il se présente sous la forme suivante :

    # Configuration file
    
    general {
        # The code %player% will be replaced by player's name
        S:welcomeMessage=Bienvenue à toi %player%
    }
    

    Ce commentaire sera aussi affiché dans le Gui lorsque l'utilisateur mettra sa souris sur le texte devant la propriété pendant un moment.
    J'en profite donc pour parler de ce fameux texte, c'est celui que l'on définie avec setLanguageKey(String langKey), vous pouvez
    mettre directement le texte que vous voulez (il doit décrire à quoi correspond la propriété) ou alors mettre la clé de traduction, puis
    mettre la traduction dans les fichiers .lang

    Maintenant que la propriété est prête comme il le faut, on va récupérer sa valeur et ajouter son nom à la liste propOrder.

            welcomeMessage = prop.getString();
            // On évite d'avoir un message vide
            if(welcomeMessage.equals(""))
            {
                welcomeMessage = "Bienvenue à toi %player%";
                prop.setValue("Bienvenue à toi %player%");
            }
            propOrder.add(prop.getName());
    

    Et on fait de même pour la seconde propriété.

            prop = config.get(Configuration.CATEGORY_GENERAL, "showWelcomeMessage", true);
            prop.setComment("Set to false to disable welcome's message");
            prop.setLanguageKey("modtuto.config.showWelcomeMessage.title");
    
            showWelcomeMessage = prop.getBoolean();
            propOrder.add(prop.getName());
    

    Une fois toutes les propriétés d'une catégorie ajoutées à la liste, nous pouvons passer cette liste en paramètre de la
    fonction setCategoryPropertyOrder(String category, List <string>propOrder)

    Nous ne créons qu'une seule catégorie car nous n'en avons pas besoin de plus, libre à vous de créer d'autres catégories avec d'autres propriétés,
    il faudra alors vider la liste de la façon suivante entre chaque catégorie.

    propOrder = new ArrayList<string>();
    

    Nous avons donc comme classe finale ceci.

    package votre.package;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import net.minecraftforge.common.config.Configuration;
    import net.minecraftforge.common.config.Property;
    import net.minecraftforge.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent;
    import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
    
    public class ModTutorielConfig
    {
        private static Configuration config;
    
        public static String welcomeMessage = "";
        public static boolean showWelcomeMessage = true;
    
        public static void preInit(FMLPreInitializationEvent event)
        {
            config = new Configuration(event.getSuggestedConfigurationFile());
            config.load();
    
            syncConfig();
        }
    
        public static Configuration getConfig()
        {
            return config;
        }
    
        public static void onConfigChanged(OnConfigChangedEvent event)
        {
            if(event.getModID().equals(ModTutoriel.MODID))
            {
                syncConfig();
            }
        }
    
        public static void syncConfig()
        {
            Property prop;
    
            List <string>propOrder = new ArrayList<string>();
    
            prop = config.get(Configuration.CATEGORY_GENERAL, "welcomeMessage", "Bienvenue à toi %player%");
            prop.setComment("The code %player% will be replaced by player's name");
            prop.setLanguageKey("modtuto.config.welcomeMessage.title");
    
            welcomeMessage = prop.getString();
            if(welcomeMessage.equals(""))
            {
                welcomeMessage = "Bienvenue à toi %player%";
                prop.setValue("Bienvenue à toi %player%");
            }
            propOrder.add(prop.getName());
    
            prop = config.get(Configuration.CATEGORY_GENERAL, "showWelcomeMessage", true);
            prop.setComment("Set to false to disable welcome's message");
            prop.setLanguageKey("modtuto.config.showWelcomeMessage.title");
    
            showWelcomeMessage = prop.getBoolean();
            propOrder.add(prop.getName());
    
            config.setCategoryPropertyOrder(Configuration.CATEGORY_GENERAL, propOrder);
    
            if(config.hasChanged())
            {
                config.save();
            }
        }
    }
    

    La classe du GuiConfig :

    Cette classe contiendra les informations nécessaires pour que FML crée le Gui de configuration de votre mod. Je vais placer cette classe
    dans le package modtuto.client.gui, et je vais l'appeler ModTutorielGuiFactory.
    Cette classe doit implémenter l'interface IModGuiFactory.

    public class ModTutorielGuiFactory implements IModGuiFactory
    {
    
        @Override
        public void initialize(Minecraft minecraftInstance)
        {}
    
        @Override
        public Class mainConfigGuiClass()
        {
            return null;
        }
    
        @Override
        public Set <runtimeoptioncategoryelement>runtimeGuiCategories()
        {
            return null;
        }
    
        @Override
        public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element)
        {
            return null;
        }
    }
    

    J'ai implémenté les fonctions de l'interface, nous allons seulement modifier la fonction mainConfigGuiClass() en changeant son retour.
    Il faut retourner une classe qui hérite de GuiScreen et qui est notre Gui de configuration, modifiez-la de la façon suivante:

        @Override
        public Class mainConfigGuiClass()
        {
            return ModTutorielConfigGui.class;
        }
    

    Il faut créer cette classe, ce sera une classe statique interne qui héritera de GuiConfig.

    public class ModTutorielGuiFactory implements IModGuiFactory
    {
        // Fonctions de l'interface IModGuiFactory
    
        /**
            * Classe interne
            */
        public static class ModTutorielConfigGui extends GuiConfig
        {
            public ModTutorielConfigGui(GuiScreen parent)
            {
                super(parent, getConfigElements(), ModTutoriel.MODID, false, false, I18n.format("modtuto.config.gui.mainTitle", new Object[] {}));
            }
    
            private static List <iconfigelement>getConfigElements()
            {
                List <iconfigelement>list = new ArrayList<iconfigelement>();
                return list;
            }
        }
    }
    

    Intéressons-nous au constructeur de la classe ModTutorielConfigGui, dans le constructeur nous appelons le constructeur de GuiConfig
    qui prend pour arguments GuiConfig(GuiScreen parent, List <iconfigelement>configElements, String modID, boolean allRequireWorldRestart, boolean allRequireMinecraftRestart, String title).
    Un par un, on a :

    • parent : le Gui à ouvrir lorsque l'on quitte celui-ci
    • configElements : les éléments de configuration à afficher, on va y revenir
    • modId : le MODID du mod auquel appartient le Gui de configuration
    • allRequireWorldRestart : à mettre sur true si tous les éléments de configuration demandent de redémarrer le monde une fois changés
    • allRequireMinecraftRestart : même chose mais cette fois c'est redémarrer Minecraft qui est demandé
    • title : le titre qui sera affiché
      Maintenant parlons des objets implémentants IConfigElement, ils sont au nombre de 3
    • ConfigElement : possède 2 constructeurs, un qui prend une ConfigCategory en argument et un deuxième qui prend une Property,
      Ajouter cet objet à la liste aura pour effet, soit d'afficher la propriété, soit de créer un bouton pour accéder aux propriétés contenues dans la catégorie.
    • DummyCategoryElement : permet une plus grande liberté au niveau de ce que l'on affiche, permet entre autres de mettre un nom personnalisé à l'élément.
    • DummyListElement : permet d'afficher des listes et de les modifier en cliquant sur le bouton.
      Le plus simple reste d'utiliser un objet ConfigElement, pour notre exemple nous allons utiliser un objet DummyCategoryElement
        private static List <iconfigelement>getConfigElements()
        {
            List <iconfigelement>list = new ArrayList<iconfigelement>();
            list.add(new DummyCategoryElement("modtutoConfig", "modtuto.config.gui.generalTitle", new ConfigElement(ModTutorielConfig.getConfig().getCategory(Configuration.CATEGORY_GENERAL)).getChildElements()));
            return list;
        }
    

    On crée un élément de type DummyCategoryElement qui demande une liste de IConfigElement en paramètre, on obtient cette liste en créant un objet
    de type ConfigElement dont on récupère les éléments enfants.

    La classe principale :

    Dans la classe principale nous allons ajouter un champs à l'annotation Mod, actuellement j'ai ceci.

    @Mod(modid = ModTutoriel.MODID)
    

    Je vais rajouter le chemin vers mon Gui Factory.

    @Mod(modid = ModTutoriel.MODID, guiFactory="modtuto.client.gui.ModTutorielGuiFactory")
    

    Dans la fonction preInit(FMLPreInitializationEvent event) rajoutez ceci.

    ModTutorielConfig.preInit(event);
    

    C'est pour charger la configuration, rappelez-vous.

    La classe des events clients :

    Enregistrez l'event suivant dans votre classe.

        @SubscribeEvent
        public void onConfigChanged(OnConfigChangedEvent event)
        {
            ModTutorielConfig.onConfigChanged(event);
        }
    

    On ne fait que diriger l'event vers notre classe de configuration afin de synchroniser les données

    La classe des events communs :

    Nous allons juste illustrer notre exemple avec l'event suivant.

        @SubscribeEvent
        public void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event)
        {
            if(ModTutorielConfig.showWelcomeMessage)
            {
                event.player.addChatComponentMessage(new TextComponentString("[ModTutoriel] " + ModTutorielConfig.welcomeMessage.replaceAll("%player%", event.player.getDisplayNameString())));
            }
        }
    

    Ce code sert juste à afficher le message de bienvenue quand le joueur se connecte.

    Résultat

    Lancez le jeu, rendez-vous dans l'onglet "Mods", sélectionnez votre mod, cliquez sur le bouton "Config" et là vous verrez votre Gui de configuration.
    Essayez de modifier les valeurs, puis connectez-vous au monde, le message aura changé. Si vous désactivez la fonctionnalité, le message ne s'affichera plus.
    0_1529701494955_configMainGui.PNG
    0_1529701511417_configGeneralGui.PNG

    Voir le commit sur GitHub

    Crédits

    Rédaction : BrokenSwing

    Correction : Folgansky

    Creative Commons
    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

    retourRetour vers le sommaire des tutoriels



  • Chapeaux * - * pile se que je voulait 🙂


  • Rédacteurs

    Je pourrai l'améliorer, dites si il faut que je rajoute des explications, des exemple avec des tableaux, etc …



  • Merci du tuto, j'avais déjà un Gui de configuration mais il y a avait certains éléments dont je ne connaissais pas l'utilité et que j'avais pris sur le gui de Forge ^^

    Envoyé de mon RAINBOW LITE 4G en utilisant Tapatalk


  • Rédacteurs

    T'inquiète pas, j'ai tout pris du gui de config de forge moi aussi, j'ai pas déviné comment ça marchait xD



  • "DummyCategoryElement : permet une plus grande liberté au niveau de ce que l'on affiche, permet entre autres de mettre un nom personnalisé à l'élément." A moins qu'ils aient mis de la javadoc depuis, t'a deviné (ou plus cherché que moi) ^^


  • Rédacteurs

    La javadoc :

    
    /**
    * This class provides a Dummy Category IConfigElement. It can be used to define a custom list of GUI entries that will
    * appear on the child screen or to specify a custom IGuiConfigListEntry for a special category.
    */
    
    

    Je regarderai de plus prêt les possibilités et je rajouterai au tuto
    Finalement je le rajouterai pas, ce n'est pas indispensable


  • Rédacteurs

    Tutoriel valide, fonctionne aussi en 1.12 en changeant une fonction :

    
    @Override
    public Class mainConfigGuiClass() {
    return ModTutorielConfigGui.class;
    }
    
    

    Devient :

    
    @Override
    public boolean hasConfigGui() {
    return true;
    }
    
    @Override
    public GuiScreen createConfigGui(GuiScreen parentScreen) {
    return new ModTutorielConfigGui(parentScreen);
    }