Config System

Le Config System d'EriAPI est un module de configuration par annotations qui permet de définir, lire, sauvegarder et synchroniser des paramètres de mod sans écrire de code boilerplate. Tu annotes des champs Java avec @EriConfig, @Category, @Range et autres — le framework fait le reste.

Le module repose sur le système de configuration Forge standard (fichiers .cfg) tout en y ajoutant plusieurs couches :

  • Parsing par réflexion — lecture et écriture automatiques des champs annotés
  • GUI auto-généré — un écran de configuration complet créé à la volée depuis les annotations
  • Validation intégrée — plages numériques (@Range), regex (@Pattern), couleurs (@ColorField)
  • Synchronisation réseau — envoi automatique des valeurs entre client et serveur via ConfigSyncHandler
  • Compatibilité Forge — les fichiers .cfg générés sont lisibles par n'importe quel éditeur de texte
Compatibilité

Le Config System est conçu pour Minecraft Forge 1.12.2 (14.23.5.2864). Il s'appuie sur net.minecraftforge.common.config.Configuration pour la persistance des fichiers.

Démarrage rapide

Voici tout ce dont tu as besoin pour avoir une config fonctionnelle avec GUI et synchronisation réseau en moins de cinq minutes.

1. Créer la classe de configuration

Crée une classe avec des champs public static. Annote la classe avec @EriConfig et les champs avec les annotations de ton choix.

Java — MyConfig.java
package fr.eri.eriapi.config;

import fr.eri.eriapi.config.annotations.*;

@EriConfig(modid = "monmod", filename = "monmod")
public class MyConfig {

    @Category("general")
    @Comment("Activer les fonctionnalites experimentales")
    public static boolean enableExperimental = false;

    @Category("general")
    @Comment("Nombre maximum de tentatives de connexion")
    @Range(min = 1, max = 10)
    public static int maxRetries = 3;

    @Category("display")
    @Comment("Couleur principale de l'interface")
    @ColorField
    public static int primaryColor = 0xFF00CFCF;

    @Category("display")
    @Comment("Prefixe affiche dans le chat")
    @Pattern("^[a-zA-Z0-9_-]{1,16}$")
    public static String chatPrefix = "monmod";
}

2. Enregistrer la config dans preInit

Dans ta classe principale annotée @Mod, enregistre la config lors de l'événement FMLPreInitializationEvent.

Java — enregistrement dans preInit
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
    // Enregistre et charge immediatement la config depuis le disque
    ConfigParser.register(MyConfig.class, event.getModConfigurationDirectory());
}

3. Initialiser la synchronisation réseau

Appelle ConfigSyncHandler.init() dans le même preInit (ou dans init), après l'enregistrement du canal réseau principal du mod.

Java — init du sync réseau
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
    // Initialise le canal de synchronisation config (client <-> serveur)
    ConfigSyncHandler.init();
}

4. Ouvrir le GUI de configuration

Depuis n'importe quel endroit côté client (commande, bouton dans un menu...), ouvre le GUI généré automatiquement.

Java — ouvrir le GUI config
// Depuis le thread client (EventHandler, commande client, etc.)
Minecraft.getMinecraft().displayGuiScreen(
    ConfigGuiFactory.createGui(MyConfig.class)
);

// Ou avec un ecran parent (pour revenir en arriere avec Echap) :
Minecraft.getMinecraft().displayGuiScreen(
    ConfigGuiFactory.createGui(MyConfig.class, Minecraft.getMinecraft().currentScreen)
);
Resultat

Un fichier config/monmod.cfg est cree automatiquement dans le dossier Minecraft. Les valeurs sont chargees au demarrage et sauvegardees des que le joueur clique sur "Sauvegarder" dans le GUI.

Annotations

Toutes les annotations se trouvent dans le package fr.eri.eriapi.config.annotations. Elles s'appliquent aux champs public static d'une classe annotee @EriConfig.

!
Champs public static obligatoires

Le Config System utilise la reflexion Java pour lire et modifier les valeurs. Les champs doivent absolument etre public static (non-final) pour que le parsing fonctionne.

@EriConfig

Annote la classe de configuration. Indique l'identifiant du mod et le nom du fichier .cfg a generer. Cette annotation est obligatoire sur la classe racine.

Java — @EriConfig
// Package : fr.eri.eriapi.config.annotations
// Cible   : ElementType.TYPE (classe)
// Retention : RUNTIME

@EriConfig(
    modid    = "monmod",    // ID du mod (pour les metadonnees Forge)
    filename = "monmod"     // Nom du fichier : config/monmod.cfg
)
public class MyConfig {
    // champs...
}
Attribut Type Description
modid String Identifiant du mod. Utilise dans les metadonnees Forge du fichier .cfg.
filename String Nom du fichier sans extension. Le fichier cree sera config/<filename>.cfg.

@Category

Regroupe les champs dans une section du fichier .cfg et dans le GUI. Si omis, le champ est place dans la categorie "general" par defaut.

Java — @Category
// Package : fr.eri.eriapi.config.annotations
// Cible   : ElementType.FIELD
// Retention : RUNTIME

@Category("general")
public static boolean enableFeature = true;

@Category("display")
public static int uiScale = 100;

@Category("network")
public static String serverUrl = "localhost";
Sections dans le fichier .cfg

Chaque valeur unique de @Category cree une section [nom] dans le fichier .cfg Forge. Le GUI regroupe les champs par categorie dans des panneaux separes.

@Range

Definit la plage de valeurs autorisees pour un champ numerique (int, float, double). Le GUI affiche un Slider ou un NumericField contraint entre min et max.

Java — @Range
// Package : fr.eri.eriapi.config.annotations
// Cible   : ElementType.FIELD
// Retention : RUNTIME

@Category("general")
@Range(min = 1, max = 20)
public static int maxPlayers = 8;      // Slider 1 -> 20

@Category("display")
@Range(min = 0.1, max = 3.0)
public static float renderScale = 1.0f;  // NumericField decimal 0.1 -> 3.0

@Category("network")
@Range(min = 1000, max = 30000)
public static double timeoutMs = 5000;   // NumericField decimal
Attribut Type Par defaut Description
min double Double.MIN_VALUE Valeur minimale autorisee (inclusive).
max double Double.MAX_VALUE Valeur maximale autorisee (inclusive).

@Comment

Ajoute une description au champ. Ce texte apparait comme commentaire dans le fichier .cfg et comme infobulle dans le GUI de configuration.

Java — @Comment
// Package : fr.eri.eriapi.config.annotations
// Cible   : ElementType.FIELD
// Retention : RUNTIME

@Category("general")
@Comment("Active le mode debug. Affiche des informations supplementaires dans les logs.")
public static boolean debugMode = false;

// Dans le fichier .cfg genere, cela produira :
# Active le mode debug. Affiche des informations supplementaires dans les logs.
# [par defaut : false]
B:debugMode=false

@ColorField

Marque un champ int comme une couleur ARGB. Le GUI remplace le champ numerique standard par un ColorPicker complet (roue HSB + slider luminosite + preview hex).

Java — @ColorField
// Package : fr.eri.eriapi.config.annotations
// Cible   : ElementType.FIELD (int uniquement)
// Retention : RUNTIME

@Category("display")
@Comment("Couleur principale de l'interface (format ARGB)")
@ColorField
public static int primaryColor = 0xFF00CFCF;  // Cyan opaque

@Category("display")
@Comment("Couleur de fond des panneaux")
@ColorField
public static int panelBackground = 0xCC1A1A2E; // Bleu fonce semi-transparent
Format ARGB

Les couleurs EriAPI sont au format 0xAARRGGBB. Le byte de poids fort est l'alpha (transparence) : 0xFF = completement opaque, 0x00 = completement transparent. Voir la classe Colors du GUI Framework pour des constantes et helpers.

@Pattern

Valide un champ String contre une expression reguliere. Si la valeur saisie ne correspond pas au pattern, le GUI affiche un message d'erreur et refuse la sauvegarde.

Java — @Pattern
// Package : fr.eri.eriapi.config.annotations
// Cible   : ElementType.FIELD (String uniquement)
// Retention : RUNTIME

@Category("general")
@Comment("Prefixe du chat (1 a 16 caracteres alphanumeriques)")
@Pattern("^[a-zA-Z0-9_-]{1,16}$")
public static String chatPrefix = "monmod";

@Category("network")
@Comment("Adresse IP du serveur de statistiques")
@Pattern("^(\\d{1,3}\\.){3}\\d{1,3}$")
public static String statsServer = "127.0.0.1";

@Category("network")
@Comment("Cle API (UUID format)")
@Pattern("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
public static String apiKey = "00000000-0000-0000-0000-000000000000";

Types supportes

Le Config System supporte les types Java suivants pour les champs de configuration. Chaque type est automatiquement mappe vers un composant GUI adapte.

Type Java Annotations possibles Composant GUI genere Cle Forge
boolean / Boolean @Comment Checkbox B:
int / Integer @Range, @Comment Slider (si @Range) ou NumericField I:
int + @ColorField @ColorField, @Comment ColorPicker (roue HSB + hex) I:
float / Float @Range, @Comment NumericField (decimal) S: (stocke comme String)
double / Double @Range, @Comment NumericField (decimal) S: (stocke comme String)
String @Pattern, @Comment TextField (avec validation si @Pattern) S:
enum (toute enum Java) @Comment RadioGroup (une option par valeur d'enum) S: (nom de la constante)

Exemple de mapping visuel

Java — tous les types dans une config
@EriConfig(modid = "monmod", filename = "monmod")
public class MyConfig {

    // boolean -> Checkbox dans le GUI
    @Category("general")
    @Comment("Activer le mode experimental")
    public static boolean experimentalMode = false;

    // int sans @Range -> NumericField entier
    @Category("general")
    @Comment("Quantite de ressources allouees")
    public static int resourceAmount = 100;

    // int avec @Range -> Slider
    @Category("general")
    @Comment("Nombre de threads (1 a 8)")
    @Range(min = 1, max = 8)
    public static int threadCount = 2;

    // int avec @ColorField -> ColorPicker
    @Category("display")
    @Comment("Couleur principale")
    @ColorField
    public static int mainColor = 0xFF00CFCF;

    // float avec @Range -> NumericField decimal contraint
    @Category("display")
    @Comment("Echelle de rendu (0.5 a 2.0)")
    @Range(min = 0.5, max = 2.0)
    public static float renderScale = 1.0f;

    // String sans @Pattern -> TextField libre
    @Category("general")
    @Comment("Nom affiche dans l'interface")
    public static String displayName = "Joueur";

    // String avec @Pattern -> TextField avec validation
    @Category("network")
    @Comment("Adresse du serveur (format IP)")
    @Pattern("^(\\d{1,3}\\.){3}\\d{1,3}$")
    public static String serverIp = "127.0.0.1";

    // enum -> RadioGroup
    @Category("general")
    @Comment("Mode de difficulte")
    public static Difficulty difficulty = Difficulty.NORMAL;

    public enum Difficulty { EASY, NORMAL, HARD }
}

ConfigParser

ConfigParser est la classe centrale qui gere l'enregistrement, le chargement et la sauvegarde des configurations. Elle maintient une registry interne des classes enregistrees.

Package : fr.eri.eriapi.config

Methodes principales

register(Class<?>, File)

Enregistre une classe de configuration et charge immediatement les valeurs depuis le fichier .cfg. Si le fichier n'existe pas encore, il est cree avec les valeurs par defaut des champs Java.

Java — register
// Dans preInit :
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
    File configDir = event.getModConfigurationDirectory();

    // Enregistre MyConfig -> cree/lit config/monmod.cfg
    ConfigParser.register(MyConfig.class, configDir);

    // Tu peux enregistrer plusieurs configs
    ConfigParser.register(NetworkConfig.class, configDir);
    ConfigParser.register(DisplayConfig.class, configDir);
}

reload(Class<?>)

Relit le fichier .cfg depuis le disque et met a jour les champs Java avec les nouvelles valeurs. Utile si le fichier a ete modifie manuellement pendant que le jeu tournait.

Java — reload
// Recharger la config depuis le fichier (ex: apres edition manuelle)
ConfigParser.reload(MyConfig.class);

// Les champs sont maintenant mis a jour :
System.out.println(MyConfig.maxRetries); // Nouvelle valeur du .cfg

save(Class<?>)

Ecrit les valeurs actuelles des champs Java dans le fichier .cfg. Appele automatiquement par le GUI quand le joueur clique sur "Sauvegarder", mais tu peux aussi l'appeler manuellement.

Java — save
// Modifier une valeur programmatiquement puis sauvegarder :
MyConfig.maxRetries = 5;
ConfigParser.save(MyConfig.class); // Ecrit dans config/monmod.cfg

getFields(Class<?>)

Retourne la liste de tous les FieldInfo d'une config enregistree. Chaque FieldInfo contient le champ Java, ses annotations, sa categorie et sa valeur courante. Utile pour construire des interfaces personnalisees ou inspecter la config programmatiquement.

Java — getFields
List<ConfigParser.FieldInfo> fields = ConfigParser.getFields(MyConfig.class);

for (ConfigParser.FieldInfo info : fields) {
    System.out.println(
        "[" + info.getCategory() + "] " +
        info.getField().getName() + " = " +
        info.getValue()
    );
}
// Affiche par exemple :
// [general] maxRetries = 3
// [display] primaryColor = -16724913
// [display] chatPrefix = monmod

getForgeConfig(Class<?>)

Retourne l'objet Configuration Forge sous-jacent associe a la classe. Permet d'acceder a l'API Forge de bas niveau si necessaire (lecture de sections brutes, etc.).

Java — getForgeConfig
import net.minecraftforge.common.config.Configuration;

Configuration forgeCfg = ConfigParser.getForgeConfig(MyConfig.class);

// Lecture directe d'une propriete Forge :
String rawValue = forgeCfg.getString("chatPrefix", "general", "monmod", "Prefixe chat");
System.out.println("Valeur brute : " + rawValue);

isRegistered(Class<?>)

Verifie si une classe de configuration a deja ete enregistree dans le ConfigParser.

Java — isRegistered
if (ConfigParser.isRegistered(MyConfig.class)) {
    System.out.println("Config enregistree, pret a l'emploi.");
} else {
    // Ne pas appeler reload/save sans enregistrement prealable
    System.out.println("Config non enregistree !");
}

ConfigGuiFactory

ConfigGuiFactory genere automatiquement un ecran de configuration complet a partir des annotations d'une classe enregistree. Le GUI est entierement base sur les composants du GUI Framework EriAPI.

Package : fr.eri.eriapi.config

Methodes

createGui(Class<?>)

Cree le GUI de configuration sans ecran parent. Appuyer sur Echap fermera le GUI et retournera au menu principal.

Java — createGui simple
// Ouvrir le GUI de configuration pour MyConfig
Minecraft.getMinecraft().displayGuiScreen(
    ConfigGuiFactory.createGui(MyConfig.class)
);

createGui(Class<?>, GuiScreen)

Cree le GUI avec un ecran parent. Quand le joueur appuie sur Echap ou clique sur "Annuler", il retourne a l'ecran parent au lieu du menu principal.

Java — createGui avec parent
// Depuis une GUI existante :
GuiScreen parent = Minecraft.getMinecraft().currentScreen;
Minecraft.getMinecraft().displayGuiScreen(
    ConfigGuiFactory.createGui(MyConfig.class, parent)
);
// Echap ou "Annuler" -> retour a l'ecran parent

Mapping type vers composant GUI

Voici comment chaque type de champ est traduit en composant visuel dans le GUI genere :

Type / Annotation Composant GUI Interaction
boolean Checkbox Clic pour basculer vrai/faux
int + @Range Slider Glisser entre min et max
int sans @Range NumericField Saisie directe (entiers uniquement)
int + @ColorField ColorPicker Roue HSB, slider luminosite, hex
float / double NumericField (decimal) Saisie directe, point decimal autorise
String TextField Saisie libre (bordure rouge si @Pattern invalide)
enum RadioGroup Un RadioButton par constante d'enum

Boutons du GUI

Le GUI genere contient toujours trois boutons en bas de l'ecran :

  • Sauvegarder — valide les champs, ecrit dans le .cfg via ConfigParser.save(), puis envoie la config au serveur via ConfigSyncHandler.sendToServer()
  • Annuler — ferme le GUI sans sauvegarder, retourne a l'ecran parent si defini
  • Reinitialiser — restaure toutes les valeurs aux valeurs par defaut Java definies dans la classe, puis sauvegarde
Java — exemple complet d'ouverture depuis une commande
// Dans un CommandBase cote client (ou un EventHandler client) :
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) {
    // Verifier que c'est bien le client (pas le serveur dedié)
    if (!(sender instanceof EntityPlayerSP)) return;

    Minecraft mc = Minecraft.getMinecraft();
    mc.addScheduledTask(() -> {
        mc.displayGuiScreen(
            ConfigGuiFactory.createGui(MyConfig.class, mc.currentScreen)
        );
    });
}

ConfigSyncHandler

ConfigSyncHandler gere la synchronisation des valeurs de configuration entre le client et le serveur. Il utilise un canal SimpleNetworkWrapper dedie, distinct du canal principal du GUI Framework.

Package : fr.eri.eriapi.config

Pourquoi synchroniser les configs ?

En Minecraft Forge, les configurations sont lues depuis le disque de chaque machine separement. Si un joueur modifie une valeur dans le GUI, le serveur n'en sait rien. ConfigSyncHandler resout ce probleme en envoyant les valeurs via le reseau des que l'utilisateur sauvegarde.

Methodes

init()

Enregistre le canal reseau dedie et les packets de synchronisation. Doit etre appele une seule fois dans init() ou preInit(), apres l'enregistrement des configs.

Java — init()
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
    ConfigSyncHandler.init(); // A appeler une seule fois
}

sendToServer(Class<?>)

Envoie les valeurs d'une config depuis le client vers le serveur. Appele automatiquement par ConfigGuiFactory lors d'une sauvegarde, mais peut aussi etre utilise manuellement.

Java — sendToServer
// Envoyer manuellement apres modification programmatique :
MyConfig.maxRetries = 7;
ConfigParser.save(MyConfig.class);
ConfigSyncHandler.sendToServer(MyConfig.class);
// Le serveur recoit et met a jour ses champs Java MyConfig.maxRetries = 7

sendToClient(Class<?>, EntityPlayerMP)

Envoie les valeurs d'une config depuis le serveur vers un joueur specifique. Utile pour forcer la synchronisation d'un joueur qui vient de rejoindre ou apres une modification cote serveur.

Java — sendToClient
// Dans un EventHandler serveur (ex: PlayerLoggedInEvent) :
@SubscribeEvent
public void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
    EntityPlayerMP player = (EntityPlayerMP) event.player;
    // Envoyer la config du serveur au joueur qui rejoint
    ConfigSyncHandler.sendToClient(MyConfig.class, player);
}

sendAllToClient(EntityPlayerMP)

Envoie toutes les configs enregistrees vers un joueur specifique. Pratique dans PlayerLoggedInEvent pour synchroniser toute la configuration d'un coup.

Java — sendAllToClient
@SubscribeEvent
public void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
    EntityPlayerMP player = (EntityPlayerMP) event.player;
    // Synchronise TOUTES les configs enregistrees vers ce joueur
    ConfigSyncHandler.sendAllToClient(player);
}

Synchronisation automatique au login

Si tu enregistres le ConfigSyncHandler comme listener Forge avec MinecraftForge.EVENT_BUS.register(), il intercepte automatiquement l'evenement PlayerLoggedInEvent et envoie toutes les configs au joueur connecte.

Java — auto-sync au login
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
    ConfigSyncHandler.init();
    // Enregistre le handler d'evenements Forge pour l'auto-sync au login
    MinecraftForge.EVENT_BUS.register(ConfigSyncHandler.class);
}

Broadcast vers tous les clients

Pour diffuser une modification de config a tous les joueurs connectes (par exemple apres une commande admin), parcours la liste des joueurs et envoie individuellement.

Java — broadcast a tous les joueurs
// Modifier une valeur cote serveur et la diffuser :
MyConfig.maxRetries = 10;
ConfigParser.save(MyConfig.class);

MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance();
for (EntityPlayerMP player : server.getPlayerList().getPlayers()) {
    ConfigSyncHandler.sendToClient(MyConfig.class, player);
}

Exemple complet

Voici un exemple de bout en bout : une classe de configuration complete avec plusieurs categories, tous les types d'annotations, l'enregistrement dans la classe principale, et une commande pour ouvrir le GUI.

1. La classe de configuration

Java — ServerConfig.java
package fr.eri.eriapi.config;

import fr.eri.eriapi.config.annotations.*;

/**
 * Configuration principale du mod EriAPI.
 * Fichier genere : config/eriapi.cfg
 */
@EriConfig(modid = "eriapi", filename = "eriapi")
public class ServerConfig {

    // ========================
    // Categorie : general
    // ========================

    @Category("general")
    @Comment("Active les fonctionnalites experimentales (instables)")
    public static boolean experimentalFeatures = false;

    @Category("general")
    @Comment("Niveau de log (0=SILENT, 1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)")
    @Range(min = 0, max = 4)
    public static int logLevel = 3;

    @Category("general")
    @Comment("Prefixe affiche dans les messages de chat du mod")
    @Pattern("^[a-zA-Z0-9_-]{1,16}$")
    public static String chatPrefix = "EriAPI";

    @Category("general")
    @Comment("Mode de performance global")
    public static PerformanceMode performanceMode = PerformanceMode.BALANCED;

    public enum PerformanceMode { LOW, BALANCED, HIGH, ULTRA }

    // ========================
    // Categorie : display
    // ========================

    @Category("display")
    @Comment("Couleur principale de l'interface (ARGB)")
    @ColorField
    public static int primaryColor = 0xFF00CFCF;

    @Category("display")
    @Comment("Couleur d'accent pour les boutons et highlights (ARGB)")
    @ColorField
    public static int accentColor = 0xFFFF6B6B;

    @Category("display")
    @Comment("Opacite des fonds de panneau (0.0 = transparent, 1.0 = opaque)")
    @Range(min = 0.0, max = 1.0)
    public static float panelOpacity = 0.85f;

    @Category("display")
    @Comment("Taille de police de l'interface (pourcentage, 80 a 150)")
    @Range(min = 80, max = 150)
    public static int fontScale = 100;

    // ========================
    // Categorie : network
    // ========================

    @Category("network")
    @Comment("Activer la synchronisation automatique des configs au login")
    public static boolean autoSync = true;

    @Category("network")
    @Comment("Timeout des requetes reseau en millisecondes")
    @Range(min = 1000, max = 30000)
    public static int networkTimeout = 5000;

    @Category("network")
    @Comment("Adresse du serveur de telemetrie (laisser vide pour desactiver)")
    public static String telemetryServer = "";
}

2. Enregistrement dans la classe principale

Java — EriAPI.java (extrait)
@Mod(modid = Reference.MODID, version = Reference.VERSION, name = Reference.NAME)
public class EriAPI {

    @Mod.EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        // Enregistre la config -> lit/cree config/eriapi.cfg
        ConfigParser.register(ServerConfig.class, event.getModConfigurationDirectory());
    }

    @Mod.EventHandler
    public void init(FMLInitializationEvent event) {
        // Initialise le canal de sync reseau
        ConfigSyncHandler.init();
        // Auto-sync au login des joueurs
        MinecraftForge.EVENT_BUS.register(ConfigSyncHandler.class);
    }
}

3. Commande pour ouvrir le GUI

Java — CommandOpenConfig.java
package fr.eri.eriapi.command;

import fr.eri.eriapi.config.ConfigGuiFactory;
import fr.eri.eriapi.config.ServerConfig;
import net.minecraft.client.Minecraft;
import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayerSP;
import net.minecraft.server.MinecraftServer;

public class CommandOpenConfig extends CommandBase {

    @Override
    public String getName() {
        return "ericonfig";
    }

    @Override
    public String getUsage(ICommandSender sender) {
        return "/ericonfig - Ouvre le panneau de configuration";
    }

    @Override
    public void execute(MinecraftServer server, ICommandSender sender, String[] args) {
        if (!(sender instanceof EntityPlayerSP)) return;

        Minecraft mc = Minecraft.getMinecraft();
        mc.addScheduledTask(() -> {
            // Ouvre le GUI avec l'ecran courant comme parent (Echap -> retour)
            mc.displayGuiScreen(
                ConfigGuiFactory.createGui(ServerConfig.class, mc.currentScreen)
            );
        });
    }

    @Override
    public int getRequiredPermissionLevel() {
        return 0; // Accessible a tous les joueurs
    }
}

Fichier .cfg genere

Voici a quoi ressemble le fichier config/eriapi.cfg genere automatiquement :

INI — config/eriapi.cfg
####################
# display
####################

display {
    # Couleur d'accent pour les boutons et highlights (ARGB)
    # [par defaut: -10066]\
    I:accentColor=-10066

    # Taille de police de l'interface (pourcentage, 80 a 150)
    # Min: 80 | Max: 150
    # [par defaut: 100]
    I:fontScale=100

    # Opacite des fonds de panneau (0.0 = transparent, 1.0 = opaque)
    # Min: 0.0 | Max: 1.0
    # [par defaut: 0.85]
    S:panelOpacity=0.85

    # Couleur principale de l'interface (ARGB)
    # [par defaut: -16724913]
    I:primaryColor=-16724913
}

####################
# general
####################

general {
    # Prefixe affiche dans les messages de chat du mod
    # [par defaut: EriAPI]
    S:chatPrefix=EriAPI

    # Active les fonctionnalites experimentales (instables)
    # [par defaut: false]
    B:experimentalFeatures=false

    # Niveau de log (0=SILENT, 1=ERROR, 2=WARN, 3=INFO, 4=DEBUG)
    # Min: 0 | Max: 4
    # [par defaut: 3]
    I:logLevel=3

    # Mode de performance global
    # Valeurs valides: LOW, BALANCED, HIGH, ULTRA
    # [par defaut: BALANCED]
    S:performanceMode=BALANCED
}

####################
# network
####################

network {
    # Activer la synchronisation automatique des configs au login
    # [par defaut: true]
    B:autoSync=true

    # Timeout des requetes reseau en millisecondes
    # Min: 1000 | Max: 30000
    # [par defaut: 5000]
    I:networkTimeout=5000

    # Adresse du serveur de telemetrie (laisser vide pour desactiver)
    # [par defaut: ]
    S:telemetryServer=
}
Modification manuelle du fichier .cfg

Le fichier .cfg peut etre edite directement dans un editeur de texte quand le jeu est ferme. Les commentaires (lignes commencant par #) sont regeneres a chaque sauvegarde. Apres edition manuelle, appelle ConfigParser.reload(MyConfig.class) pour que les nouvelles valeurs soient prises en compte sans redemarrage.