• Sommaire

    Introduction

    Depuis le build 1146, bspkrs a ajouté toute une série de classes qui permettent de créer facilement une interface graphique pour la configuration de votre Mod.

    Je ne vais pas parler ici des utilisations simples de ces classes qui permettent de créer des champs Texte, Booléen, Entier ou Double qui sont mis à jour en écoutant les évènements onconfigchanged par exemple.

    Non, je vais vous montrer comment arriver à utiliser quelques fonctionnalités cachées.

    Quand vous lancez le jeu minecraft et que vous allez sur le bouton MODS et que vous cliquez sur Minecraft Forge et enfin sur CONFIG.
    Vous pouvez naviguez dans toutes sortes de boutons et champs texte, numérique, variable tableau, variable boléenne et autre.

    Ce sont les utilisations basique des classes du package net.minecraftforge.common.config

    Maintenant, si vous allez dans la fenêtre config de Forge Mod Loader, là vous avez d'autres possibilités qui ne sont pas présentes dans le package net.minecraftforge.common.config

    Par exemple, j'avais envie de laisser la possibilité à l'utilisateur de choisir la résolution des textures. Par défaut, Minecraft utilise le 16x16 et des ressources pack sont disponibles en 32x32, 64x64, 128x128, 256x256 et 512x512.
    Hors si j'utilise les propriétés disponibles dans le package net.minecraftforge.common.config, je laisse trop de possibilité à l'utilisateur ou je dois passer par un array de valeur qui n'est pas joli visuellement.
    Par contre, si j'utilisais le bouton cycle texte présenté sur l'écran de config de Forge Mod Loader, là ça m'intéresse.

    J'ai donc programmer mon interface pour utiliser cet élément et il fonctionne très bien 🙂
    Bon ça m'a même permis de trouver un bug dans Forge concernant cet élément mais je l'ai contourné d'une autre manière. (je le dirai pendant le tuto).

    Je vous présente donc un tuto sur le DummyConfigElement CycleValueEntry

    Pré-requis

    Un Workspace minecraft forge (j'utilise la version 10.13.0.1180) (C'est la version recommandée)
    La Classe Basique d'un Mod Minecraft

    Code

    D'abord nous allons créer le fichier configuration et la propriété qui enregistrera les valeurs ainsi que la méthode pour sauvegarder les données dans ce fichier de configuration.

    Pour celà, rendez vous dans la classe principale de votre mod.

    Importons d'abord la classe Configuration. Elle va nous servir à définir le fichier de Configuration et les valeurs que nous enregistrerons.

    import net.minecraftforge.common.config.Configuration;
    

    Maintenant il faut initier une instance de cette classe. Pour ce faire nous avons besoin d'une variable

    public static Configuration testmodConfig;
    

    Et il nous faut une variable pour la propriété que nous allons sauvegarder

    public static int testmodProperty = 0;
    

    Il est temps d'instancier notre Configuration. Nous faisons cela dans la Pre-Initialisation de Forge.

    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        testmodConfig = new Configuration(event.getSuggestedConfigurationFile());
    }
    

    Nous allons maintenant créer une méthode pour synchroniser le mod avec le fichier de configuration

    public static void syncConfig() {
        testmodProperty = testmodConfig.getInt("testmodPropertyname", Configuration.CATEGORY_GENERAL, 16, 16, 512, "Un entier");
        if (testmodConfig.hasChanged())
            testmodConfig.save();
    }
    

    getInt(String name, String category, int default_value, int min_value, int max_value, String comment);
    est une methode qui recherche la propriété nommée "testmodPropertyname". Si celle-ci existe, elle met sa valeur dans testmodProperty sinon elle crée cette propriété avec la valeur par défaut indiquée. (default_value)
    Ensuite, la méthode .hasChanged vérifie si l'instance de Configuration a changé. et sauvegarde les changements si besoin.

    Maintenant que la méthode existe, nous allons l'appeler. On peut le faire dans l'évènement Pre-Init, Init ou Post-Init. ça n'a aucune importance. On pourrait même le faire via une autre classe. (D'ailleurs, je le ferai plus tard). Mais bon. Pour nous faciliter la vie, ajoutons l'appel dans notre pre-init qui existe déjà

    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        testmodConfig = new Configuration(event.getSuggestedConfigurationFile());
        syncConfig();
    }
    

    Voilà nous avons maintenant une instance de configuration qui cherchera le fichier dans son constructeur (Configuration(file)) et nous avons une categorie nommée "général" (constante Configuration.CATEGORY_GENERAL) et une propriété (enfant de cette categorie) nommée "testmodPropertyname"

    Il est important de différencier le fichier de configuration et l'instance de configuration. L'instance, c'est comme la mémoire RAM et le fichier comme le HD. Si je ferme le jeu, la RAM se vide (l'instance) et seul reste la version HD (le fichier). Les propriétés et catégories peuvent être mises à jour constamment dans la mémoire RAM (l'instance) mais elles ne seront à jour dans le HD (le fichier) que via la méthode save. Imaginons que je suis sur l'écran de config que je change une valeur et que je kill le process minecraft. Ma valeur ne sera pas mise à jour dans le fichier.

    On pourrait démarrer le jeu comme ça. Il n'y a pas d'erreur. Il créerait le fichier de configuration avec la catégorie et la propriété créée. Mais voilà. Il manque encore tout l'écran pouvant manipuler les valeurs de cette propriété.

    Pour ce faire, il faut indiquer à Forge que nous avons une fabrique d'interface utilisateur. (guifactory). Nous faisons cela dans la ligne @Mod de notre classe de base.

    @Mod(modid = Constants.MODID ,name = Constants.MODNAME, version = Constants.MODVERSION, guiFactory = Constants.MODGUYFACTORY)
    

    Comme vous le voyez, j'utilise des variables pour donner les valeurs. Ceci me permet généralement de pouvoir utiliser mes classes dans d'autres projets en modifiant uniquement mes constantes.
    Je vous le conseille vivement, j'ai donc fait une petite classe Constants toute simple

    package testmod.lib;
    
    public class Constants {
        public static final String MODID = "testmod";
        public static final String MODNAME = "Mod Test";
        public static final String MODVERSION = "1.0";
        public static final String MODGUYFACTORY = "testmod.client.guifactory.GuiFactory";
    }
    

    Comme vous le voyez guifactory contient le chemin complet de la classe. (le package testmod.client.guifactory) la classe (GuiFactory)

    Voilà, notre classe de base est maintenant terminée. Dans d'autres tutoriaux, vous pouvez voir que certains enregistre un handler pour l'évènement onConfigChanged mais personnellement je trouve que c'est utiliser des ressources pour rien. J'utilise un override de la méthode onGuiClosed.

    Nous sommes prêt pour créer notre classe GuiFactory

    GuiFactory

    Cette classe implémente l'interface IModGuiFactory

    commencez donc par importer IModGuiFactory

    package testmod.client.guifactory;
    
    import cpw.mods.fml.client.IModGuiFactory;
    
    public class GuiFactory implements IModGuiFactory {
    

    Comme nous implémentons une interface il nous faut redéfinir les methodes de l'interface dans notre classe. Il y en a 4.

        @Override
        public void initialize(Minecraft minecraftInstance){
        }
    

    Ceci initialise votre classe avec l'instance actuelle de minecraft. Minecraft étant indéfini, il nous faut importer la classe correspondante.

    Ajoutons l'import

    import net.minecraft.client.Minecraft;
    

    La seconde méthode introduit la classe qui construit les écrans de notre interface graphique utilisateur (GuiScreen)

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

    Nous voyons que cette méthode retourne ConfigGui.class comme étant la classe principale de notre interface graphique pour la configuration du mod. C'est la prochaine classe que nous créerons. (voir ConfigGui)
    Comme GuiScreen est indéfini, nous importons la classe correspondante

    import net.minecraft.client.gui.GuiScreen;
    

    La méthode suivante Etabli une entrée runtime pour nos catégories dans les options de notre interface graphique de configuration.

        @Override
        public Set<RuntimeOptionCategoryElement> runtimeGuiCategories(){
            return null;
        }
    

    Set est une interface du package java.util. Nous l'importons donc.

    import java.util.Set;
    

    Nous arrivons à notre dernière methode qui crée un handler pour l'entrée créée dans la methode précédente.

        public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
            return null;
        }
    

    Voilà En route pour la partie fun 🙂
    Le classe principale de notre interface graphique

    ConfigGui

    Nous allons créer notre classe pour étendre la classe GuiConfig. Il nous faut donc importer GuiConfig

    package testmod.client.guifactory;
    
    import cpw.mods.fml.client.config.GuiConfig;
    
    public class ConfigGui extends GuiConfig{
    

    En faisant cela, il nous faut un constructeur qui crée l'instance de notre classe selon les spécifications de la classe étendue. Il y a 4 constructeurs possible.

    Je choisis celui-ci
    GuiConfig(GuiScreen parentScreen, List <IConfigElement>configElements, String modID, boolean allRequireWorldRestart, boolean allRequireMcRestart, String title)

    Donc parentScreen sera l'écran Parent (celui qui appelle notre écran de config - l'écran des mods où il y a les boutons config)
    D'ailleurs, si vous essayer de connaitre son nom, il vous enverra une valeur du style cpw.mods.fml.client.GuiModList@.....
    configElements. ce sont tous les éléments définis dans notre interface graphique.
    modID. c'est l'identifiant de votre mod
    allRequireWorldRestart est une valeur boléenne qui défini si le monde doit redémarrer après une modification. (comme c'est un écran d'option et que le monde n'a pas encore démarré, la valeur sera fausse)
    allRequireMcRestart est une valeur boléenne qui défini si une modification requiert un redémarrage de minecraft. (Pour notre exemple, la valeur sera fausse).
    title : le titre de la fenêtre.

    Etablissons donc notre constructeur.

        public ConfigGui(GuiScreen parent){
            super(parent, getElements(), Constants.MODID, false, false, I18n.format("Main.Config"));
        }
    

    Comme GuiScreen est indéfini, nous devons importer la classe dans notre package.

    import net.minecraft.client.gui.GuiScreen;
    

    getElements() est une méthode que j'ai créé. Elle remplace new ConfigElement(TestMod.configFile.getCategory(Configuration.CATEGORY_GENERAL)).getChildElements() qui est la version d'utilisation de base et qui crée les éléments pour vous selon les propriétés que vous avez créé dans la méthod syncConfig qui a été appelée lors de la pré initialisation de votre mod. Mais comme nous voulons customiser, nous oublions cette façon de faire.
    Constants.MODID renvoie l'identifiant de votre Mod (Vous voyez l'utilisation de la classe Constants de notre package testmod.lib)
    false et false pour les RequireRestart.
    Pour le titre, j'introduis ici I18n.format qui est une méthode de la classe I18n. Cette méthode est très utile puisqu'elle va chercher dans le fichier lang la correspondance de la variable transmise par format.

    Ainsi, si vous ne l'avez pas encore fait, créer un fichier language dans le package assets.testmod.lang.
    Ce fichier défini le language utilisé par votre minecraft. (voir les options de minecraft)
    si vous utilisez le language anglais américain, votre fichier doit s'appeler en_US.lang (il vaut toujours mieux créer ce fichier car quand minecraft ne trouve pas de fichier language correspondant à la langue, il choisi en_US.lang par défaut)
    si vous utilisez le language français de france, votre fichier doit s'appeler fr_FR.lang
    dans ce fichier, c'est simple, vous indiquez la variable suivi de sa valeur
    Dans le fichier en_US.lang : ```java
    Main.Config=Main Config Screen

    Dans le fichier fr_FR.lang : ```java
    Main.Config=Ecran de Configuration Principal
    

    Comme nous utilisons I18n, il faut aussi l'importer dans notre package

    import net.minecraft.client.resources.I18n;
    

    J'ai aussi utilisé la variable Constants.MODID donc j'importe la classe

    import testmod.lib.Constants;
    

    Nous allons maintenant créer la méthode getElements(). Rappelez vous que selon notre super constructeur, nous devons avoir une variable de type List<IConfigElement>. Donc notre méthode doit renvoyer ce type de variable.

        private static List<IConfigElement> getElements() {
            List<IConfigElement> list = new ArrayList<IConfigElement>();
            return list;
        }
    

    Ceci suffirait déjà mais renvoie une liste vide et donc un écran tout vide 🙂
    D'abord il faut que nous importions IConfigElement puisque notre package ne le connait pas encore.

    import cpw.mods.fml.client.config.IConfigElement;
    

    Il nous faut aussi List et ArrayList de java

    import java.util.ArrayList;
    import java.util.List;
    
    

    Je crée maintenant mon élément DummyConfigElement de type CycleValueEntry et l'ajoute à la liste des éléments

    Ma méthode devient :

    private static List<IConfigElement> getElements() {
            List<IConfigElement> list = new ArrayList<IConfigElement>();
            DummyConfigElement resolutionElement = new DummyConfigElement<String>("testmodcycle", "16", ConfigGuiType.STRING, "Main.Config.Resolution", new String[] {"16", "32", "64", "128", "256", "512"});
            list.add(resolutionElement);
            return list;
        }
    

    La Méthode est simple et comprend ces éléments :
    Name : on indique un nom pour la propriété DummyConfigElement<String>. Je l'ai appelé "testmodcycle" (le nom n'a pas beaucoup d'importance)
    DefaultValue : la valeur par défaut au format texte "16"
    Type : Le type de valeur introduit : ConfigGuiType.STRING
    langKey : la variable du label de notre Element qui sera renvoyée par notre fichier language
    validValues : une variable tableau de type string reprenant toutes les valeurs possible pour notre DummyConfigElement de type CycleValueEntry

    La création du DummyConfigElement <String>impose d'importer sa classe
    De plus dans sa construction, il utilise la variable ConfigGuiType.STRING. Je dois donc aussi importer ConfigGuiType

    import cpw.mods.fml.client.config.DummyConfigElement;
    import cpw.mods.fml.client.config.ConfigGuiType;
    

    comme nous avons créé une variable de language, nous allons directement l'ajouter à notre fichier language.
    notre fichier en_US.lang devient

    Main.Config=Main Config Screen
    Main.Config.Resolution=Resolution Configuration
    

    et notre fichier fr_FR.lang devient

    Main.Config=Ecran de Configuration Principal
    Main.Config.Resolution=Configuration de la Résolution
    

    Oui, il faut respecter l'UTF-8 mais il y a de bon convertisseur sur le net et notepad++ le fait aussi.

    On peut lancer le jeu et aller dans l'écran de config et on a déjà notre cycle 🙂

    Maintenant, c'est un problème comment cet élément va recevoir et envoyer les données correspondant à notre propriété testmodPropertyname

    On va devoir modifier un peu notre méthode.
    D'abord, il faut savoir que cet élément dit dans sa construction DefaultValue. En vérité, il s'agit de la valeur courante affichée au moment de l'ouverture de la fenêtre config et dans sa construction, il utilise aussi cette valeur pour les boutons "Reset To Default" de l'écran de config. Mais ce n'est pas vrai. En fait il est tout a fait mal construit dans le code dans sa construction et on va passer par la porte à coté.

    Premièrement, on va récupérer la valeur actuelle de notre propriété testmodPropertyname et l'utiliser à la place de la DefaultValue du constructeur du DummyElement

    Ma méthode devient :

        private static List<IConfigElement> getElements() {
            List<IConfigElement> list = new ArrayList<IConfigElement>();
            int resolutionValue = TestMod.testmodConfig.getCategory(Configuration.CATEGORY_GENERAL).get("testmodPropertyname").getInt();
            DummyConfigElement resolutionElement = new DummyConfigElement<String>("cycleString", String.valueOf(resolutionValue), ConfigGuiType.STRING, "Main.Config.Resolution", new String[] {"16", "32", "64", "128", "256", "512"});
            list.add(resolutionElement);
            return list;
        }
    

    Je crée donc l'entier resolutionValue, j'appelle l'instance de ma Configuration (testmodConfig). J'appelle la méthode getCategory avec le paramètre Configuration.CATEGORY_GENERAL (string "general") pour obtenir un objet ConfigCategory sur lequel j'appelle la méthode get pour avoir un objet Property nommé "testmodPropertyname" et j'utilise alors la méthode getInt() pour obtenir sa valeur.
    J'utilise enfin cette valeur dans mon element DummyConfigElement en la transformant en texte.

    comme TestMod n'est pas connu dans mon package, je vais l'importer

    import testmod.TestMod;
    

    Etant donné que j'appelle la classe Configuration via Configuration.CATEGORY_GENERAL, j'ai aussi besoin d'importer la classe

    import net.minecraftforge.common.config.Configuration;
    

    On peut lancer le jeu, on a le même écran qu'avant sauf que maintenant, la valeur vient de notre Propriété.
    De plus, quand on quitte l'écran et qu'on revient, la valeur reprend toujours cette valeur ce qui est très ennuyeux.

    Il nous faut donc une méthode pour enregistrer les valeurs.

    Créons la méthode onGuiClosed qui existe dans la classe GuiConfig

        @Override
        public void onGuiClosed() {
    }
    

    Ici j'ai vu à quel point DummyConfigElement était buggé. Par exemple, lorsque vous utilisez la méthode .get, vous vous attendez à obtenir la valeur courante. Et bien non, il s'agit de la DefaultValue de son constructeur. En fait maintenant la valeur courante de notre propriété "testmodPropertyname". De plus, lorsque vous utilisez la méthode .getDefault, vous vous attendez à obtenir la valeur par défaut. Et bien non, il s'agit de la valeur courante sauf dans les cas où vous ne cliquez pas sur l'élément et que vous quittez la fenêtre config sans y avoir touché. là, il a toujours la valeur par défaut. et quand je dit valeur par défaut, je ne parle pas de la DefaultValue du constructeur... non, il y a une valeur par défaut que vous pouvez ajoutez avec la méthode .set ... enfin, je m'attendais à autre chose de cette méthode mais non, c'est bien cette méthode qui donne la valeur par défaut. Enfin... Maintenant que l'on sait tout cela, il faut le contourner.

    Donc, je vais directement le contourner pour pas faire le tuto trop long. Sinon j'aurais montré les limites de la chose 🙂

    Nous allons créé une variable entier pour récupérer la valeur de notre DummyConfigElement.
    Puis nous allons créer une variable objet CycleValueEntry
    Enfin nous allons regarder toutes les entrées de notre fenêtre de config pour trouver l'instance de notre DummyConfigElement qui est un objet CycleValueEntry.
    Enfin nous récupérerons la valeur de cet objet CycleValueEntry qui est beaucoup plus juste 🙂
    Et nous utiliserons cette variable pour modifier la valeur de notre Propriété "testmodPropertyName"
    Et nous terminerons par synchroniser notre Configuration.

    Notre méthode onGuiClosed devient

        @Override
        public void onGuiClosed() {
            int resolutionValue = 16;
            CycleValueEntry cycleEntry;
            List listEntries = this.entryList.listEntries;
            for(Object object : listEntries) {
                if (object instanceof CycleValueEntry) {
                    cycleEntry = (CycleValueEntry) object;
                    resolutionValue = Integer.valueOf(cycleEntry.getCurrentValue());
    
                }
            }
                    TestMod.testmodConfig.getCategory(Configuration.CATEGORY_GENERAL).get("testmodPropertyName").set(resolutionValue);
            TestMod.syncConfig();
        }
    

    Alors nous allons devoir importer CycleValueEntry

    import cpw.mods.fml.client.config.GuiConfigEntries.CycleValueEntry;
    

    En fait la valeur cycleEntry.getCurrentValue() retourne toujours la bonne valeur. Peu importe qu'on clique ou pas sur l'élément.

    Maintenant, si vous testez, vous avez déjà un élément fonctionnel mais il reste un problème. Quand je reviens sur l'écran. Il prend bien la valeur que j'ai laissé mais il pense aussi que c'est la valeur par défaut. Nous allons donc modifier la méthode getElements() pour que tous les boutons soient fonctionnels.

    Ma méthode devient :

        private static List <IConfigElement> getElements() {
            List<IConfigElement> list = new ArrayList<IConfigElement>();
            int resolutionValue = TestMod.testmodConfig.getCategory(Configuration.CATEGORY_GENERAL).get("testmodPropertyname").getInt();
            DummyConfigElement resolutionElement = new DummyConfigElement<String>("cycleString", String.valueOf(resolutionValue), ConfigGuiType.STRING, "Main.Config.Resolution", new String[] {"16", "32", "64", "128", "256", "512"});
            list.add(resolutionElement);
                    String resolutionDefault = TestMod.testmodConfig.getCategory(Configuration.CATEGORY_GENERAL).get("testmodPropertyName").getDefault();
                    resolutionElement.set(resolutionDefault);
            return list;
        }
    

    Maintenant, comme par magie. J'arrive sur l'écran et tout fonctionne. XD

    Bonus

    Pour avoir une petite explication quand le pointeur de souris passe sur le label de votre Element, ajoutez ceci dans vos fichiers lang
    en_US.lang```java
    Main.Config.Resolution.tooltip=Here you can choose the texture resolution

    fr_FR.lang```java
    Main.Config.Resolution.tooltip=Ici vous pouvez modifier le résolution de la texture
    

    Les Classes

    Les Classes :

    Base Classe

    package testmod;
    
    import cpw.mods.fml.common.Mod;
    import cpw.mods.fml.common.Mod.Instance;
    import cpw.mods.fml.common.event.FMLPreInitializationEvent;
    import net.minecraftforge.common.config.Configuration;
    
    @Mod(modid = Constants.MODID ,name = Constants.MODNAME, version = Constants.MODVERSION, guiFactory = Constants.MODGUYFACTORY)
    public class Generic {
    
            @Instance(Constants.MODID)
            public static Generic instance;
    
            public static Configuration testmodConfig;
            public static int testmodProperty = 0;
    
            public static void syncConfig() {
                testmodProperty = testmodConfig.getInt("testmodPropertyname", Configuration.CATEGORY_GENERAL, 16, 16, 512, "Un entier");
                if (testmodConfig.hasChanged())
                testmodConfig.save();
            }
    
            @EventHandler
            public void preInit(FMLPreInitializationEvent event) {
                testmodConfig = new Configuration(event.getSuggestedConfigurationFile());
                syncConfig();
            }
    }
    

    Classe Constants

    package testmod.lib;
    
    public class Constants {
            public static final String MODID = "testmod";
            public static final String MODNAME = "Mod Test";
            public static final String MODVERSION = "1.0";
            public static final String MODGUYFACTORY = "testmod.client.guifactory.GuiFactory";
    }
    

    Classe GuiFactory

    package testmod.client.guifactory;
    
    import java.util.Set;
    
    import net.minecraft.client.Minecraft;
    import net.minecraft.client.gui.GuiScreen;
    import cpw.mods.fml.client.IModGuiFactory;
    
    public class GuiFactory implements IModGuiFactory {
    
        @Override
        public void initialize(Minecraft minecraftInstance){
        }
    
        @Override
        public Class mainConfigGuiClass(){
            return ConfigGui.class;
        }
    
        @Override
        public Set<RuntimeOptionCategoryElement> runtimeGuiCategories(){
            return null;
        }
    
        public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
            return null;
        }
    
    }
    

    Classe ConfigGui

    package testmod.client.guifactory;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import net.minecraft.client.gui.GuiScreen;
    import net.minecraft.client.resources.I18n;
    import net.minecraftforge.common.config.Configuration;
    import testmod.TestMod;
    import testmod.lib.Constants;
    import cpw.mods.fml.client.config.ConfigGuiType;
    import cpw.mods.fml.client.config.DummyConfigElement;
    import cpw.mods.fml.client.config.GuiConfig;
    import cpw.mods.fml.client.config.GuiConfigEntries.CycleValueEntry;
    import cpw.mods.fml.client.config.IConfigElement;
    
    public class ConfigGui extends GuiConfig{
    
        public ConfigGui(GuiScreen parent){
            super(parent, getElements(), Constants.MODID, false, false, I18n.format("Main.Config"));
        }
    
        private static List <IConfigElement>getElements() {
            List<IConfigElement> list = new ArrayList<IConfigElement>();
            int resolutionValue = TestMod.testmodConfig.getCategory(Configuration.CATEGORY_GENERAL).get("testmodPropertyName").getInt();
            DummyConfigElement resolutionElement = new DummyConfigElement<String>("cycleString", String.valueOf(resolutionValue), ConfigGuiType.STRING, "Main.Config.Resolution", new String[] {"16", "32", "64", "128", "256", "512"});
            list.add(resolutionElement);
            String resolutionDefault = TestMod.testmodConfig.getCategory(Configuration.CATEGORY_GENERAL).get("testmodPropertyName").getDefault();
            resolutionElement.set(resolutionDefault);
            return list;
        }
    
        @Override
        public void onGuiClosed() {
            int resolutionValue = 16;
            CycleValueEntry cycleEntry;
            List listEntries = this.entryList.listEntries;
            for(Object object : listEntries) {
                if (object instanceof CycleValueEntry) {
                    cycleEntry = (CycleValueEntry) object;
                    resolutionValue = Integer.valueOf(cycleEntry.getCurrentValue());
                }
            }
        TestMod.testmodConfig.getCategory(Configuration.CATEGORY_GENERAL).get("testmodPropertyName").set(resolutionValue);
            TestMod.syncConfig();
        }
    }
    

    ScreenShot

    0_1529786276503_41260220140906010217.png

    Credits

    Merci à moi même 😄 lol


  • Très bon tutoriel qui va être très utile à beaucoup de monde !


  • J'ai ajouté un screenshot dans le tuto

    Je vais peut-être faire un tutoriel encore plus avancé sur l'interface graphique des options du Mod 😄

    Voilà ce que commence à donner ma nouvelle interface build from scratch 🙂 en utilsant quelques fonctions reconstruites 🙂
    Mon bouton test fonctionne comme le bouton "DONE" actuellement 🙂

    0_1529786342765_15783320140906004332.png

    Il faut savoir que ça ressemble car j'ai voulu que ça ressemble. Mais en fait, il y a bien plus de liberté qu'on ne le croit 😄


  • Regarde la façons de faire les autres tutoriel et ajoute un bonus.


  • As-tu lu le tuto et les autres tutos sur les interfaces pour faire ce commentaire ?

    http://www.minecraftforgefrance.fr/forumdisplay.php?fid=17

    Je pense pas que mon tuto soit moins bien que ceux déjà présentés.

    Je le trouve même mieux

    Enfin c'est pas grave, je cherche juste à donner des conseils pour ma part. Si ça plait pas, tu peux même le supprimer mais je prendrai plus une partie de mon temps à la rédaction de tutos.

  • Moddeurs confirmés Rédacteurs Administrateurs

    Je vois pas de problème avec sa partie bonus diangle.
    Le seul truc différent c'est qu'il a remplacé la partie résultat par classe + screenshot vu qu'il n'y a pas de commit sur github.

    @'XcentY':

    As-tu lu le tuto et les autres tutos sur les interfaces pour faire ce commentaire ?

    http://www.minecraftforgefrance.fr/forumdisplay.php?fid=17

    Je pense pas que mon tuto soit moins bien que ceux déjà présentés.

    Ces anciens tutoriels ont été fait avant que la template soit conçu.


  • @'robin4002':

    Je vois pas de problème avec sa partie bonus diangle.
    Le seul truc différent c'est qu'il a remplacé la partie résultat par classe + screenshot vu qu'il n'y a pas de commit sur github.

    @'XcentY':

    As-tu lu le tuto et les autres tutos sur les interfaces pour faire ce commentaire ?

    http://www.minecraftforgefrance.fr/forumdisplay.php?fid=17

    Je pense pas que mon tuto soit moins bien que ceux déjà présentés.

    Ces anciens tutoriels ont été fait avant que la template soit conçu.

    Question a quand un tuto sur GUI en 1.7 ? ^^
    car je galère pour en faire un

  • Moddeurs confirmés Rédacteurs Administrateurs

    Aucune idée, avec la reprise des cours et mon emploie du temps super chargé, ça va mettre beaucoup de temps.


  • Oups j'avais pas vue le minuscule bonus, mais se que je veux dire c'est la présentation, le tuto est très bien mais je veux dire pour suivre les autre tuto, le code si tu le met fait comme les autres tuto (dans le résultat tous comme screen shot) ensuite je pense que le staff mettra en lien le tuto sur la classe de base (j'avais oublier l'importance des point de suspension (encore une parenthèse je ne voulais pas te vexés))