Network GUI

Communication client ↔ serveur pour les GUIs EriAPI — envoyer des actions, recevoir des donnees, et ouvrir des ecrans depuis le serveur.

fr.eri.eriapi.network Forge 1.12.2 Java 8

Introduction

Le module Network fournit un systeme simplifie pour communiquer entre tes GUIs EriAPI et le serveur Minecraft. Sans lui, les GUIs sont purement visuelles — elles ne peuvent pas lire des donnees serveur ni declencher des actions cote serveur.

Client vs Serveur dans Minecraft

Minecraft fait tourner deux cotes simultanement : le client (ce que le joueur voit — la fenetre de jeu, les GUIs, le rendu) et le serveur (ce qui gere le monde, les items, les joueurs, l'economie). Ils communiquent via des paquets.

Quand un joueur clique sur "Acheter" dans ta GUI, le clic a lieu cote client. Mais le transfert d'item doit avoir lieu cote serveur. Tu dois envoyer un paquet pour dire au serveur "ce joueur a clique sur Acheter". C'est a ca que sert le systeme reseau.

Quand as-tu besoin du systeme reseau ?

  • Quand un clic sur un bouton doit donner ou retirer des items a un joueur
  • Quand tu dois lire des donnees cote serveur (les stats d'un autre joueur, une base de donnees, etc.)
  • Quand tu veux ouvrir une GUI a distance (le serveur demande au client d'ouvrir un ecran)
  • Quand plusieurs joueurs doivent voir les memes donnees se mettre a jour en temps reel
Pas besoin de reseau ? Super !

Si ta GUI affiche uniquement des informations stockees localement (un tableau de scores, un ecran de parametres), tu n'as pas besoin de ce module du tout. Le systeme reseau n'est necessaire que quand des actions doivent etre validees ou executees sur le serveur.

Classe / Interface Role
GuiNetworkHandler Point central — initialisation, envoi/reception de paquets, registres
IGuiDataReceiver Interface a implementer dans une GUI pour recevoir des donnees du serveur
PacketGuiAction Paquet client → serveur pour les actions (clic de bouton, etc.)
PacketGuiData Paquet bidirectionnel pour la synchronisation de donnees cle/valeur
PacketGuiOpen Paquet serveur → client pour ouvrir une GUI avec des donnees initiales

Initialisation

Le systeme reseau doit etre initialise exactement une fois dans FMLPreInitializationEvent. Enregistre ensuite tes factories de GUI et handlers d'actions dans FMLInitializationEvent.

Java — configuration dans la classe @Mod
@Mod(modid = "monmod")
public class MonMod {

    @Mod.EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        // Initialiser le systeme reseau en premier
        GuiNetworkHandler.init("monmod");
    }

    @Mod.EventHandler
    public void init(FMLInitializationEvent event) {
        // Enregistrer une factory de GUI (cote client)
        // Quand le serveur appelle openGuiFor("boutique", ...), cette factory cree la GUI
        GuiNetworkHandler.registerGui("boutique", data ->
            new GuiBoutique(data.getInteger("type_boutique"))
        );

        // Enregistrer un handler d'action (cote serveur)
        // Quand un client appelle sendAction("boutique", "btn_acheter", "acheter_item", "42"),
        // ce handler est appele sur le serveur
        GuiNetworkHandler.registerActionHandler("boutique", (player, params) -> {
            String action = params.get("action");       // "acheter_item"
            String data   = params.get("data");         // "42"
            String compId = params.get("component");    // "btn_acheter"
            if ("acheter_item".equals(action)) {
                GestionBoutique.acheterItem((EntityPlayerMP) player, Integer.parseInt(data));
            }
        });
    }
}
preInit obligatoire

GuiNetworkHandler.init(modId) doit etre appele dans FMLPreInitializationEvent avant qu'un paquet soit envoye. Appeler sendAction() ou openGuiFor() avant l'initialisation provoque une NullPointerException.

GuiNetworkHandler

Point central du systeme reseau GUI. Toutes les methodes sont statiques. Le canal reseau est cree sous le nom {modId}_gui.

Package

fr.eri.eriapi.network.GuiNetworkHandler

Initialisation

SignatureDescription
static void init(String modId) Initialise le systeme reseau et enregistre les 4 paquets internes (action, donnees serveur, donnees client, ouverture). Doit etre appele exactement une fois dans FMLPreInitializationEvent. Le canal sera nomme {modId}_gui.
static boolean isInitialized() Retourne true si le systeme a ete initialise. Utile pour les verifications defensives.

Registres

SignatureDescription
static void registerGui(String guiId,
Function<NBTTagCompound, GuiScreen> factory)
[Cote client] Enregistre une factory de GUI identifiee par guiId. Quand le serveur appelle openGuiFor(player, guiId, data), la factory est invoquee avec les donnees NBT et le GuiScreen retourne est affiche. Enregistrer dans FMLInitializationEvent.
static void registerActionHandler(String guiId,
BiConsumer<EntityPlayer, Map<String,String>> handler)
[Cote serveur] Enregistre un handler pour les actions envoyees depuis une GUI. La Map<String,String> params contient : "component" (identifiant du composant source), "action" (type d'action), "data" (payload supplementaire).
static void registerDataHandler(String guiId,
BiConsumer<EntityPlayer, Map<String,String>> handler)
[Cote serveur] Enregistre un handler pour les donnees envoyees depuis une GUI. La Map<String,String> params contient : "component", "key", "value".

Envoi — Client vers Serveur

SignatureDescription
static void sendAction(String guiId, String componentId,
String action, Object data)
[Client uniquement — @SideOnly(CLIENT)] Envoie une action au serveur. guiId identifie la GUI (doit correspondre a un handler enregistre). componentId identifie le composant source (ex. "btn_acheter"). action est le type d'action (ex. "acheter_item"). data est le payload supplementaire — converti en String via toString(), ou chaine vide si null.
static void sendData(String guiId, String componentId,
String key, Object value)
[Client uniquement] Envoie une paire cle/valeur au serveur. Utile pour les champs de saisie (ex. la valeur d'un TextField quand l'utilisateur valide). value est converti en String via toString().

Envoi — Serveur vers Client

SignatureDescription
static void openGuiFor(EntityPlayerMP player,
String guiId, NBTTagCompound data)
[Serveur uniquement] Envoie un PacketGuiOpen au joueur. Cote client, la factory enregistree sous guiId est appelee avec data et la GUI resultante est affichee. data peut etre un new NBTTagCompound() vide si aucune donnee initiale n'est necessaire.
static void sendDataToClient(EntityPlayerMP player,
String guiId, String componentId,
String key, Object value)
[Serveur uniquement] Envoie une paire cle/valeur au client du joueur. Si la GUI actuellement ouverte chez le joueur implemente IGuiDataReceiver, sa methode onDataUpdate(componentId, key, value) est appelee. value est converti en String.
Execution thread-safe

Les handlers enregistres via registerActionHandler et registerDataHandler sont appeles sur le thread principal du serveur (via addScheduledTask dans le PacketHandler). Tu peux modifier les donnees joueur directement sans blocs synchronized supplementaires.

De meme, la distribution des donnees cote client est appelee sur le thread principal Minecraft (via addScheduledTask). Tu peux modifier les composants GUI directement.

IGuiDataReceiver

Interface a implementer dans un EriGuiScreen pour recevoir des mises a jour de donnees envoyees par le serveur via sendDataToClient().

Quand le serveur envoie un PacketGuiData au client, GuiNetworkHandler verifie si la GUI actuellement ouverte implemente IGuiDataReceiver et appelle onDataUpdate().

Package

fr.eri.eriapi.network.IGuiDataReceiver

Methode a implementer

SignatureDescription
void onDataUpdate(String componentId, String key, String value) Appelee cote client quand des donnees sont recues du serveur. componentId : identifiant du composant cible (tel que passe a sendDataToClient). key : cle de la donnee. value : valeur de la donnee (toujours une String).
Java — IGuiDataReceiver dans un EriGuiScreen
public class GuiBoutique extends EriGuiScreen implements IGuiDataReceiver {

    private Label labelSolde;
    private Label labelStatut;

    @Override
    protected void buildGui() {
        labelSolde = new Label(20, 20, 400, 30, "Chargement...")
            .scale(1.4f)
            .color(0xFFFFD700);
        getRoot().add(labelSolde);

        labelStatut = new Label(20, 60, 400, 24, "")
            .scale(1.0f)
            .color(0xFFFFFFFF);
        getRoot().add(labelStatut);
    }

    @Override
    public void onDataUpdate(String componentId, String key, String value) {
        // Filtrer par ID de composant (utile quand plusieurs composants recoivent des donnees)
        if ("affichage_solde".equals(componentId)) {
            if ("or".equals(key)) {
                labelSolde.text("Or : " + value);
            }
        } else if ("resultat_achat".equals(componentId)) {
            if ("succes".equals(key)) {
                boolean ok = Boolean.parseBoolean(value);
                labelStatut.text(ok ? "Achat reussi !" : "Fonds insuffisants.")
                           .color(ok ? 0xFF44FF88 : 0xFFFF4444);
            }
        }
    }
}

Packets

Le systeme utilise trois paquets internes. En general tu n'interagis jamais directement avec les classes de paquets — passe toujours par GuiNetworkHandler. Cette section documente leur format pour reference.

PacketGuiAction — Client → Serveur

Envoye par GuiNetworkHandler.sendAction() quand un composant client declenche une action (clic de bouton, etc.).

ChampTypeDescription
guiIdStringIdentifiant de la GUI source (ex. "boutique")
componentIdStringIdentifiant du composant source (ex. "btn_acheter")
actionStringType d'action (ex. "acheter_item", "annuler")
dataStringPayload supplementaire (ex. "42" pour un ID d'item)

Le handler serveur recoit une Map<String, String> avec les cles "component", "action", "data".

PacketGuiData — Bidirectionnel

Utilise par sendData() (client → serveur) et sendDataToClient() (serveur → client) pour synchroniser une paire cle/valeur.

ChampTypeDescription
guiIdStringIdentifiant de la GUI concernee
componentIdStringIdentifiant du composant concerne
keyStringCle de la donnee
valueStringValeur (toujours une String)

PacketGuiOpen — Serveur → Client

Envoye par openGuiFor() pour ouvrir une GUI cote client.

ChampTypeDescription
guiIdStringIdentifiant de la GUI a ouvrir (doit etre enregistree via registerGui)
initialDataNBTTagCompoundDonnees initiales passees a la factory de GUI
Serialisation

Tous les paquets utilisent ByteBufUtils.writeUTF8String / readUTF8String pour les champs String, et ByteBufUtils.writeTag / readTag pour le NBT. La taille maximale d'une String est limitee par le MTU des paquets Minecraft (environ 32 767 caracteres).

Exemple complet — Boutique Client/Serveur

Un exemple complet montrant les deux cotes : la GUI client envoie une action, le serveur la traite et repond avec des donnees mises a jour.

1. Configuration (dans la classe @Mod)

Java — MonMod.java
@Mod(modid = "monmod")
public class MonMod {

    @Mod.EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        GuiNetworkHandler.init("monmod");
    }

    @Mod.EventHandler
    public void init(FMLInitializationEvent event) {
        // Factory de GUI : le serveur peut ouvrir cette GUI cote client
        GuiNetworkHandler.registerGui("boutique", data -> {
            String nomBoutique  = data.getString("nom");
            int orInitial       = data.getInteger("or");
            return new GuiBoutique(nomBoutique, orInitial);
        });

        // Handler d'action : le serveur traite les achats
        GuiNetworkHandler.registerActionHandler("boutique", (player, params) -> {
            EntityPlayerMP mp = (EntityPlayerMP) player;
            String action = params.get("action");
            String data   = params.get("data");

            if ("acheter".equals(action)) {
                int itemId   = Integer.parseInt(data);
                boolean ok   = GestionBoutique.traiterAchat(mp, itemId);
                int nouvelOr = GestionBoutique.getOr(mp);

                // Repondre au client
                GuiNetworkHandler.sendDataToClient(mp, "boutique", "resultat", "succes", String.valueOf(ok));
                GuiNetworkHandler.sendDataToClient(mp, "boutique", "solde",    "or",     String.valueOf(nouvelOr));
            }
        });
    }
}

2. Ouvrir la GUI depuis le serveur

Java — BoutiqueNPC.java (interaction joueur)
// Quand un joueur fait un clic droit sur le PNJ Boutique cote serveur :
NBTTagCompound data = new NBTTagCompound();
data.setString("nom", "Boutique de Pierre");
data.setInteger("or", GestionBoutique.getOr(player));
GuiNetworkHandler.openGuiFor(player, "boutique", data);

3. GUI cote client

Java — GuiBoutique.java
public class GuiBoutique extends EriGuiScreen implements IGuiDataReceiver {

    private final String nomBoutique;
    private int orActuel;

    private Label labelOr;
    private Label labelStatut;

    public GuiBoutique(String nomBoutique, int orInitial) {
        this.nomBoutique = nomBoutique;
        this.orActuel    = orInitial;
    }

    @Override
    protected void buildGui() {
        ContainerComponent root = getRoot();

        // Fond
        root.add(new Rectangle(0, 0, 1920, 1080).fillColor(0x88000000));

        // Panneau central
        ContainerComponent panel = new ContainerComponent(660, 240, 600, 600);
        panel.add(new Rectangle(0, 0, 600, 600).fillColor(0xCC1A1A2E).cornerRadius(16));
        panel.add(new Label(0, 24, 600, 40, nomBoutique).scale(2.0f).color(0xFFFFD700)
            .align(Label.Align.CENTER));

        // Solde en or
        labelOr = new Label(20, 80, 560, 30, "Or : " + orActuel);
        labelOr.scale(1.3f).color(0xFFFFD700);
        panel.add(labelOr);

        // Label de statut
        labelStatut = new Label(20, 120, 560, 24, "");
        labelStatut.scale(1.0f).color(0xFFFFFFFF);
        panel.add(labelStatut);

        // Bouton acheter
        panel.add(new Button(200, 180, 200, 44, "Acheter Epee (100 or)")
            .colorScheme(0xFF1A6A3A)
            .cornerRadius(8)
            .onClick(() -> {
                GuiNetworkHandler.sendAction("boutique", "btn_acheter", "acheter", "1");
            })
        );

        root.add(panel);
    }

    @Override
    public void onDataUpdate(String componentId, String key, String value) {
        if ("resultat".equals(componentId) && "succes".equals(key)) {
            boolean ok = Boolean.parseBoolean(value);
            labelStatut.text(ok ? "Achat reussi !" : "Or insuffisant !")
                       .color(ok ? 0xFF44FF88 : 0xFFFF4444);
        } else if ("solde".equals(componentId) && "or".equals(key)) {
            orActuel = Integer.parseInt(value);
            labelOr.text("Or : " + orActuel);
        }
    }
}
Flux complet
  1. Le joueur interagit avec le PNJ → le serveur appelle openGuiFor()
  2. Le client recoit PacketGuiOpen → la factory cree GuiBoutique
  3. Le joueur clique "Acheter" → sendAction() envoie PacketGuiAction
  4. Le serveur recoit l'action → le handler traite l'achat
  5. Le serveur appelle sendDataToClient() deux fois (resultat + solde)
  6. Le client recoit PacketGuiDataonDataUpdate() met a jour la GUI