Network GUI
Client ↔ server communication for EriAPI GUIs — send actions, receive data, and open screens from the server.
Introduction
The Network module provides a simplified system for communicating between your EriAPI GUIs and the Minecraft server. Without it, GUIs are purely visual — they cannot read server data or trigger server-side actions.
Minecraft runs two sides simultaneously: the client (what the player sees — the game window, GUIs, rendering) and the server (what manages the world, items, players, economy). They communicate via packets.
When a player clicks "Buy" in your GUI, the click happens on the client. But the item transfer must happen on the server. You need to send a packet to tell the server "this player clicked Buy". That is what the network system is for.
When do you need the network system?
- When a button click should give or take items from a player
- When you need to read server-side data (another player's stats, a database, etc.)
- When you want to open a GUI remotely (the server tells a client to open a screen)
- When multiple players need to see the same data update in real time
If your GUI only displays information stored locally (a scoreboard, a settings screen), you don't need this module at all. The network system is only needed when actions must be validated or executed on the server.
| Class / Interface | Role |
|---|---|
GuiNetworkHandler |
Central hub — initialization, sending/receiving packets, registries |
IGuiDataReceiver |
Interface to implement in a GUI to receive data from the server |
PacketGuiAction |
Packet client → server for actions (button click, etc.) |
PacketGuiData |
Bidirectional packet for key/value data synchronization |
PacketGuiOpen |
Packet server → client to open a GUI with initial data |
Initialization
The network system must be initialized exactly once in
FMLPreInitializationEvent. Then register your GUI factories and action handlers
in FMLInitializationEvent.
@Mod(modid = "mymod")
public class MyMod {
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
// Initialize the network system first
GuiNetworkHandler.init("mymod");
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
// Register a GUI factory (client-side)
// When the server calls openGuiFor("shop_menu", ...), this factory creates the GUI
GuiNetworkHandler.registerGui("shop_menu", data ->
new GuiShop(data.getInteger("shop_type"))
);
// Register an action handler (server-side)
// When a client calls sendAction("shop_menu", "buy_btn", "buy_item", "42"),
// this handler is called on the server
GuiNetworkHandler.registerActionHandler("shop_menu", (player, params) -> {
String action = params.get("action"); // "buy_item"
String data = params.get("data"); // "42"
String compId = params.get("component"); // "buy_btn"
if ("buy_item".equals(action)) {
ShopManager.buyItem((EntityPlayerMP) player, Integer.parseInt(data));
}
});
}
}
GuiNetworkHandler.init(modId) must be called in
FMLPreInitializationEvent before any packet is sent. Calling
sendAction() or openGuiFor() before initialization causes a NullPointerException.
GuiNetworkHandler
Central hub of the GUI network system. All methods are static.
The network channel is created under the name {modId}_gui.
fr.eri.eriapi.network.GuiNetworkHandler
Initialization
| Signature | Description |
|---|---|
static void init(String modId) |
Initializes the network system and registers the 4 internal packets (action,
server data, client data, open). Must be called exactly once in
FMLPreInitializationEvent. The channel will be named {modId}_gui.
|
static boolean isInitialized() |
Returns true if the system has been initialized. Useful for defensive checks. |
Registries
| Signature | Description |
|---|---|
static void registerGui(String guiId, |
[Client-side] Registers a GUI factory identified by guiId.
When the server calls openGuiFor(player, guiId, data), the factory is invoked
with the NBT data and the returned GuiScreen is displayed.
Register in FMLInitializationEvent.
|
static void registerActionHandler(String guiId, |
[Server-side] Registers a handler for actions sent from a GUI.
The Map<String,String> params contains:
"component" (source component ID),
"action" (action type),
"data" (additional payload).
|
static void registerDataHandler(String guiId, |
[Server-side] Registers a handler for data sent from a GUI.
The Map<String,String> params contains:
"component", "key", "value".
|
Sending — Client to Server
| Signature | Description |
|---|---|
static void sendAction(String guiId, String componentId, |
[Client only — @SideOnly(CLIENT)]
Sends an action to the server. guiId identifies the GUI (must match a registered handler).
componentId identifies the source component (e.g. "buy_btn").
action is the action type (e.g. "buy_item").
data is additional payload — converted to String via toString(),
or an empty string if null.
|
static void sendData(String guiId, String componentId, |
[Client only]
Sends a key/value pair to the server. Useful for input fields (e.g. the value
of a TextField when the user submits).
value is converted to String via toString().
|
Sending — Server to Client
| Signature | Description |
|---|---|
static void openGuiFor(EntityPlayerMP player, |
[Server only]
Sends a PacketGuiOpen to the player. On the client, the factory registered
under guiId is called with data and the resulting GUI is displayed.
data may be an empty new NBTTagCompound() if no initial data is needed.
|
static void sendDataToClient(EntityPlayerMP player, |
[Server only]
Sends a key/value pair to the player's client. If the player's currently open GUI
implements IGuiDataReceiver, its
onDataUpdate(componentId, key, value) method is called.
value is converted to String.
|
Handlers registered via registerActionHandler and registerDataHandler
are called on the server main thread (via addScheduledTask in the
PacketHandler). You can modify player data directly without extra
synchronized blocks.
Likewise, the client-side data dispatch is called on the Minecraft main thread
(via addScheduledTask). You can modify GUI components directly.
IGuiDataReceiver
Interface to implement in an EriGuiScreen to receive data updates
sent by the server via sendDataToClient().
When the server sends a PacketGuiData to the client,
GuiNetworkHandler checks whether the currently open GUI implements
IGuiDataReceiver and calls onDataUpdate().
fr.eri.eriapi.network.IGuiDataReceiver
Method to implement
| Signature | Description |
|---|---|
void onDataUpdate(String componentId, String key, String value) |
Called on the client when data is received from the server.
componentId: identifier of the targeted component (as passed to sendDataToClient).
key: data key.
value: data value (always a String).
|
public class GuiShop extends EriGuiScreen implements IGuiDataReceiver {
private Label balanceLabel;
private Label statusLabel;
@Override
protected void buildGui() {
balanceLabel = new Label(20, 20, 400, 30, "Loading...")
.scale(1.4f)
.color(0xFFFFD700);
getRoot().add(balanceLabel);
statusLabel = new Label(20, 60, 400, 24, "")
.scale(1.0f)
.color(0xFFFFFFFF);
getRoot().add(statusLabel);
}
@Override
public void onDataUpdate(String componentId, String key, String value) {
// Filter by component ID (useful when multiple components receive data)
if ("balance_display".equals(componentId)) {
if ("gold".equals(key)) {
balanceLabel.text("Gold: " + value);
}
} else if ("purchase_result".equals(componentId)) {
if ("success".equals(key)) {
boolean ok = Boolean.parseBoolean(value);
statusLabel.text(ok ? "Purchase successful!" : "Insufficient funds.")
.color(ok ? 0xFF44FF88 : 0xFFFF4444);
}
}
}
}
Packets
The system uses three internal packets. In general you never interact with the packet
classes directly — always go through GuiNetworkHandler.
This section documents their format for reference.
PacketGuiAction — Client → Server
Sent by GuiNetworkHandler.sendAction() when a client component triggers
an action (button click, etc.).
| Field | Type | Description |
|---|---|---|
guiId | String | Source GUI identifier (e.g. "shop_menu") |
componentId | String | Source component identifier (e.g. "buy_btn") |
action | String | Action type (e.g. "buy_item", "cancel") |
data | String | Additional payload (e.g. "42" for an item ID) |
The server handler receives a Map<String, String> with keys
"component", "action", "data".
PacketGuiData — Bidirectional
Used by sendData() (client → server) and
sendDataToClient() (server → client) to synchronize a key/value pair.
| Field | Type | Description |
|---|---|---|
guiId | String | Identifier of the GUI concerned |
componentId | String | Identifier of the component concerned |
key | String | Data key |
value | String | Value (always a String) |
PacketGuiOpen — Server → Client
Sent by openGuiFor() to open a GUI on the client.
| Field | Type | Description |
|---|---|---|
guiId | String | GUI identifier to open (must be registered via registerGui) |
initialData | NBTTagCompound | Initial data passed to the GUI factory |
All packets use ByteBufUtils.writeUTF8String /
readUTF8String for String fields, and
ByteBufUtils.writeTag / readTag for NBT.
The maximum String size is limited by the Minecraft packet MTU
(approximately 32 767 characters).
Full Example — Client/Server Shop
A complete example showing both sides: the client GUI sends an action, the server processes it and replies with updated data.
1. Setup (in the @Mod class)
@Mod(modid = "mymod")
public class MyMod {
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
GuiNetworkHandler.init("mymod");
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
// GUI factory: the server can open this GUI on the client
GuiNetworkHandler.registerGui("shop", data -> {
String shopName = data.getString("name");
int initialGold = data.getInteger("gold");
return new GuiShop(shopName, initialGold);
});
// Action handler: the server processes purchases
GuiNetworkHandler.registerActionHandler("shop", (player, params) -> {
EntityPlayerMP mp = (EntityPlayerMP) player;
String action = params.get("action");
String data = params.get("data");
if ("buy".equals(action)) {
int itemId = Integer.parseInt(data);
boolean ok = ShopManager.processPurchase(mp, itemId);
int newGold = ShopManager.getGold(mp);
// Reply to the client
GuiNetworkHandler.sendDataToClient(mp, "shop", "result", "success", String.valueOf(ok));
GuiNetworkHandler.sendDataToClient(mp, "shop", "balance", "gold", String.valueOf(newGold));
}
});
}
}
2. Opening the GUI from the server
// When a player right-clicks the Shop NPC on the server:
NBTTagCompound data = new NBTTagCompound();
data.setString("name", "Pierre's Shop");
data.setInteger("gold", ShopManager.getGold(player));
GuiNetworkHandler.openGuiFor(player, "shop", data);
3. Client GUI
public class GuiShop extends EriGuiScreen implements IGuiDataReceiver {
private final String shopName;
private int currentGold;
private Label labelGold;
private Label labelStatus;
public GuiShop(String shopName, int initialGold) {
this.shopName = shopName;
this.currentGold = initialGold;
}
@Override
protected void buildGui() {
ContainerComponent root = getRoot();
// Background
root.add(new Rectangle(0, 0, 1920, 1080).fillColor(0x88000000));
// Center panel
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, shopName).scale(2.0f).color(0xFFFFD700)
.align(Label.Align.CENTER));
// Gold balance
labelGold = new Label(20, 80, 560, 30, "Gold: " + currentGold);
labelGold.scale(1.3f).color(0xFFFFD700);
panel.add(labelGold);
// Status label
labelStatus = new Label(20, 120, 560, 24, "");
labelStatus.scale(1.0f).color(0xFFFFFFFF);
panel.add(labelStatus);
// Buy button
panel.add(new Button(200, 180, 200, 44, "Buy Sword (100 gold)")
.colorScheme(0xFF1A6A3A)
.cornerRadius(8)
.onClick(() -> {
GuiNetworkHandler.sendAction("shop", "buy_btn", "buy", "1");
})
);
root.add(panel);
}
@Override
public void onDataUpdate(String componentId, String key, String value) {
if ("result".equals(componentId) && "success".equals(key)) {
boolean ok = Boolean.parseBoolean(value);
labelStatus.text(ok ? "Purchase successful!" : "Not enough gold!")
.color(ok ? 0xFF44FF88 : 0xFFFF4444);
} else if ("balance".equals(componentId) && "gold".equals(key)) {
currentGold = Integer.parseInt(value);
labelGold.text("Gold: " + currentGold);
}
}
}
- Player interacts with the NPC → server calls
openGuiFor() - Client receives
PacketGuiOpen→ factory createsGuiShop - Player clicks "Buy" →
sendAction()sendsPacketGuiAction - Server receives the action → handler processes the purchase
- Server calls
sendDataToClient()twice (result + balance) - Client receives
PacketGuiData→onDataUpdate()updates the GUI