Networking
Embers Text API uses server-to-client packets for all message delivery. This page covers the NetworkHelper API, packet types, and client-side message management.
Package: net.tysontheember.emberstextapi.network
Quick Start
Section titled “Quick Start”For the most common case — send a new message to a player:
import net.tysontheember.emberstextapi.EmbersTextAPI;
EmbersTextAPI.sendMessage(serverPlayer, immersiveMessage);import net.tysontheember.emberstextapi.fabric.EmbersTextAPIFabric;
EmbersTextAPIFabric.sendMessage(serverPlayer, immersiveMessage);NetworkHelper API
Section titled “NetworkHelper API”For full control over the message lifecycle, use NetworkHelper:
NetworkHelper net = NetworkHelper.getInstance();Individual Message Methods
Section titled “Individual Message Methods”// Send a new message (generates UUID automatically)net.sendMessage(ServerPlayer player, ImmersiveMessage message)
// Send with explicit UUID trackingnet.sendOpenMessage(ServerPlayer player, ImmersiveMessage message)
// Update an existing message before it expiresnet.sendUpdateMessage(ServerPlayer player, String messageId, ImmersiveMessage message)
// Close a specific message immediatelynet.sendCloseMessage(ServerPlayer player, String messageId)
// Close all active messages across all channelsnet.sendCloseAllMessages(ServerPlayer player)Queue Methods
Section titled “Queue Methods”// Send an ordered sequence of steps on a named channel// steps: outer list = sequential steps, inner list = simultaneous messages per stepnet.sendQueue(ServerPlayer player, String channel, List<List<ImmersiveMessage>> steps)
// Clear pending (not-yet-started) steps from a channelnet.sendClearQueue(ServerPlayer player, String channel)
// Clear all channels and close all messages immediatelynet.sendClearAllQueues(ServerPlayer player)Packet Types
Section titled “Packet Types”All communication is server-to-client (S2C). There are 6 packet types:
S2C_OpenMessagePacket
Section titled “S2C_OpenMessagePacket”Creates a new on-screen message.
public record S2C_OpenMessagePacket(UUID id, CompoundTag nbt)| Field | Description |
|---|---|
id | Unique identifier for this message — used for updates and removal |
nbt | Serialized ImmersiveMessage (all spans, effects, positioning, etc.) |
Handler: ClientMessageManager.open(id, message)
S2C_UpdateMessagePacket
Section titled “S2C_UpdateMessagePacket”Replaces an existing message’s content without removing and re-creating it.
public record S2C_UpdateMessagePacket(UUID id, CompoundTag nbt)Use this to refresh a message before it expires, or to change content/effects mid-display.
Handler: ClientMessageManager.update(id, message)
S2C_CloseMessagePacket
Section titled “S2C_CloseMessagePacket”Removes a specific message.
public record S2C_CloseMessagePacket(UUID id)Handler: ClientMessageManager.close(id)
S2C_CloseAllMessagesPacket
Section titled “S2C_CloseAllMessagesPacket”Removes all active messages and clears all queues.
public record S2C_CloseAllMessagesPacket()Handler: ClientMessageManager.closeAll()
S2C_OpenQueuePacket
Section titled “S2C_OpenQueuePacket”Sends a full ordered sequence of steps on a named channel.
| Field | Type | Description |
|---|---|---|
channel | String | Named channel identifier |
ids | List<List<UUID>> | Per-step, per-message UUIDs |
stepData | List<List<CompoundTag>> | Per-step, per-message serialized messages |
If the channel already has an active queue, the new steps are appended.
Handler: ClientMessageManager.enqueueSteps(channel, steps)
S2C_ClearQueuePacket
Section titled “S2C_ClearQueuePacket”Clears pending steps from a channel, or all channels.
public record S2C_ClearQueuePacket(String channel)- Named channel: Removes pending steps; current step plays to completion.
- Empty string: Clears all channels and closes all active messages immediately.
Handler: ClientMessageManager.clearQueue(channel) or ClientMessageManager.clearAllQueues()
Network Channel Registration
Section titled “Network Channel Registration”The network channel is registered automatically during mod initialization. The registration method differs by loader:
| Loader | Mechanism |
|---|---|
| Forge 1.20.1 | Forge SimpleChannel during FMLCommonSetupEvent |
| NeoForge 1.21.1 | NeoForge StreamCodec system at mod init |
| Fabric | Fabric Networking API with custom packet codecs |
The packet payload structure and client handling are identical across all loaders.
ClientMessageManager
Section titled “ClientMessageManager”The client-side manager that stores and renders all active messages.
Package: net.tysontheember.emberstextapi.client
Message Lifecycle
Section titled “Message Lifecycle”ClientMessageManager.open(UUID id, ImmersiveMessage message) // Add to active setClientMessageManager.update(UUID id, ImmersiveMessage message) // Replace existingClientMessageManager.close(UUID id) // Remove specificClientMessageManager.closeAll() // Remove allQueue Operations
Section titled “Queue Operations”// Enqueue steps; starts immediately if channel is idleClientMessageManager.enqueueSteps(String channel, List<QueueStep> steps)
// Cancel pending steps (current step plays to completion)ClientMessageManager.clearQueue(String channel)
// Cancel all queues and close all messagesClientMessageManager.clearAllQueues()Queue Data Types
Section titled “Queue Data Types”// One message within a queue steppublic record QueuedMessage(UUID id, ImmersiveMessage message)
// One sequential step — all messages in this list display simultaneouslypublic record QueueStep(List<QueuedMessage> messages)Queue Advancement Logic
Section titled “Queue Advancement Logic”On each tick, after processing message expiry:
- If all UUIDs tracked for a channel have expired, the next
QueueStepis dequeued. - The step’s messages are opened via
open(), and their UUIDs become the channel’s active set. - When the queue is empty, the channel entry is removed.
Individual messages and queue messages coexist freely — channels only advance based on their own tracked UUIDs.
Messages are stored in a ConcurrentHashMap<UUID, ActiveMessage>.
Effect Serialization
Section titled “Effect Serialization”Effects within spans are serialized as strings:
effectName key1=value1 key2=value2On deserialization, EffectRegistry.parseTag() recreates the effect instance. If an effect name is unrecognized on the receiving client (e.g., a custom effect from a mod the client doesn’t have), it is logged and skipped — the message still renders without that effect.
Serialization Limits
Section titled “Serialization Limits”These limits are enforced during TextSpan serialization to prevent malicious data:
| Property | Limit |
|---|---|
| Content length | 65,536 characters |
| Item/Entity ID length | 256 characters |
| Effect tag string length | 512 characters |
| Array sizes | 256 entries |
| Scale values | 100.0 |
| Offset values | 10,000.0 |
Values exceeding limits are clamped. No exception is thrown.