How the Chat System Works in Online Multiplayer Games: Inside DD Poker's Client-Server Architecture
DD Poker implements a three-layer client-server architecture where OnlineManager processes OnlineMessage objects to route chat through TCP or UDP networks, delivering them to decoupled UI components via the ChatHandler interface to support global, table-scoped, and private messaging.
The chat system in online multiplayer games requires careful separation between network transport and UI rendering to maintain real-time performance across varying connection qualities. In the dougdonohoe/ddpoker repository, this challenge is solved through a publish/subscribe model that processes chat messages through distinct network, message-processing, and UI layers while supporting both TCP production traffic and UDP testing environments.
Three-Layer Architecture Overview
DD Poker's chat implementation follows a strict decoupling principle across three distinct layers:
- Network Layer – Handles raw packet ingress through
ChatServer(UDP testing) or the standard P2P messenger (TCP production), forwarding data to the processing layer without touching UI code. - Message-Processing Layer – The
OnlineManagerclass validates incomingOnlineMessageobjects of categoryCAT_CHAT, determines broadcast scope, and queues messages for the UI. - UI Layer – Components implementing the
ChatHandlerinterface (such asChatPanelandChatLobbyPanel) receive processed messages viachatReceived()and render them using Swing.
This architecture ensures that networking code never directly references Swing classes, allowing the system to queue messages when UI components are not yet initialized.
Network Ingress and Transport Handling
Raw chat messages enter the system through two primary pathways depending on the deployment context.
UDP Testing Entry Point
In code/pokerserver/src/main/java/com/donohoedigital/games/poker/server/ChatServer.java (lines 31‑44), the ChatServer class implements UDPLinkHandler and UDPManagerMonitor to receive low-level UDP packets. When a packet arrives with user type PokerConstants.USERTYPE_CHAT, the server forwards the raw data into the host's OnlineManager through the standard P2P messaging pipeline.
Production TCP Transport
For standard online play, chat travels over the same TCP-based peer-to-peer messenger that carries all other game state. The PokerUDPServer class provides additional UDP handling for beta features, but production chat relies on the reliable OnlineMessage transport already established for game synchronization.
Message Routing and Processing in OnlineManager
The OnlineManager class in code/poker/src/main/java/com/donohoedigital/games/poker/online/OnlineManager.java serves as the central hub for all chat logic, implementing the ChatManager interface to coordinate message flow.
Message Reception and Validation
When an OnlineMessage with category CAT_CHAT arrives, the processChat method (lines 671‑682) validates the sender and determines routing strategy:
private void processChat(OnlineMessage omsg) {
boolean bDisplayLocally = true;
if (isHost()) {
// Forward to all participants unless it is a private message.
if (omsg.getChatType() != PokerConstants.CHAT_PRIVATE) {
PokerPlayer sender = getPlayer(omsg, true);
bDisplayLocally = sendMessageChat(omsg, sender);
}
} else {
// Client‑side: handle possible clock‑pause command.
Boolean bPauseClock = omsg.isClockPaused();
if (bPauseClock != null) {
if (bPauseClock) game_.getGameClock().pause();
else game_.getGameClock().unpause();
}
}
if (bDisplayLocally) deliverChat(omsg);
}
Broadcast and Table Scoping
The sendMessageChat method (lines 712‑731) implements the routing logic that distinguishes between global lobby chat and table-specific conversations. If chat.getTableNumber() equals OnlineMessage.NO_TABLE, the method calls sendMessageAllExcept to broadcast to all connected clients; otherwise, it invokes sendMessageTable to target only players seated at the specified table.
Delivery to UI Handlers
The deliverChat method (lines 737‑766) manages the final handoff to the presentation layer. If a ChatHandler is currently registered via setChatHandler, the message passes immediately to chatReceived(). Otherwise, the system stores the message in chatQueue_ until a UI component registers itself.
UI Decoupling via ChatHandler
The chat system achieves true decoupling through the ChatHandler interface defined in code/poker/src/main/java/com/donohoedigital/games/poker/online/ChatHandler.java (lines 47‑50):
public interface ChatHandler {
void chatReceived(OnlineMessage omsg);
}
Handler Registration and Message Flushing
When UI components such as ChatPanel or ChatLobbyPanel initialize, they register themselves with OnlineManager.setChatHandler (lines 771‑788). This method synchronizes on the pending queue and immediately flushes any messages that arrived before the UI was ready:
public void setChatHandler(ChatHandler chat) {
synchronized (chatQueue_) {
chat_ = chat;
for (OnlineMessage pending : chatQueue_) {
chat_.chatReceived(pending);
}
chatQueue_.clear();
}
}
Concrete UI Implementation
The ChatPanel class (line 161 in code/poker/src/main/java/com/donohoedigital/games/poker/online/ChatPanel.java) registers itself in its constructor:
if (mgr_ != null) mgr_.setChatHandler(this);
When users press Enter, the panel captures input and forwards it to the manager:
protected void sendChat() {
String sMsg = chatInputField.getText().trim();
if (!sMsg.isEmpty() && mgr_ != null) {
// Send to the host; the current table scope is used.
mgr_.sendChat(sMsg, game_.getCurrentTable(), null);
chatInputField.setText("");
}
}
Client-to-Server Message Flow
The complete transmission path from user input to remote clients follows this sequence:
- User Input:
ChatPanel.sendChat()(line 504) constructs the message string. - Manager Transmission:
OnlineManager.sendChat(String sMessage, PokerTable table, String sTestData)(lines 2041‑2050) creates anOnlineMessage, sets the table number (orNO_TABLEfor global chat), and transmits it over the network. - Host Processing: The server-side
OnlineManagerreceives theOnlineMessage, executesprocessChat(), and callssendMessageChat()to determine routing. - Client Delivery: Remote clients receive the broadcast message, their local
OnlineManagerprocesses it, and the registeredChatHandlerupdates the Swing text area.
Special Message Types and Host Privileges
The system distinguishes between player-generated chat and administrative system messages through type constants defined in PokerConstants (e.g., CHAT_PRIVATE, CHAT_ADMIN_JOIN, CHAT_ADMIN_ERROR).
Dealer and Administrative Chat
Only the host may broadcast dealer chat messages using sendDealerChat (lines 521‑558), which enforces this privilege strictly:
public void sendDealerChat(int nType, String sMessage, PokerTable table) {
if (!local.isHost())
throw new ApplicationError(ErrorCodes.ERROR_UNSUPPORTED,
"sendDealerChat only allowed from host", sMessage);
OnlineMessage chat = new OnlineMessage(OnlineMessage.CAT_CHAT_ADMIN);
chat.setChat(sMessage);
chat.setChatType(nType);
chat.setPlayerInfo(local.getPlayerInfo()); // host info
// Target a specific table or all
chat.setTableNumber(table != null ? table.getNumber() : OnlineMessage.NO_TABLE);
sendMessageChat(chat, null);
deliverChat(chat); // also show locally
}
Clock Control Integration
Chat messages can carry game control commands. In processChat (lines 683‑702), clients check omsg.isClockPaused() to pause or unpause the game clock, enabling hosts to manage game flow through chat commands.
Summary
- Decoupled Architecture: The system strictly separates network transport (
ChatServer), message processing (OnlineManager), and UI rendering (ChatPanel) to prevent threading issues and support message queuing. - Flexible Routing:
sendMessageChatdistinguishes between global broadcast (NO_TABLE) and table-scoped delivery using the table number field inOnlineMessage. - Registration Pattern: UI components implement
ChatHandlerand register viasetChatHandler, which atomically flushes thechatQueue_of any pending messages. - Privileged Operations: Host-only methods like
sendDealerChatenforce authority through explicit checks, while special chat types inPokerConstantsenable private and administrative messaging.
Frequently Asked Questions
How does DD Poker route chat messages to specific tables versus all players?
The sendMessageChat method in OnlineManager checks chat.getTableNumber() to determine scope. If the value is OnlineMessage.NO_TABLE, the system calls sendMessageAllExcept to broadcast globally; otherwise, it invokes sendMessageTable to deliver only to clients associated with that specific table number.
What prevents non-host players from sending dealer chat messages?
The sendDealerChat method (lines 521‑558) contains an explicit guard clause that verifies local.isHost(). If a non-host client attempts to invoke this method, the system throws an ApplicationError with code ERROR_UNSUPPORTED, ensuring only the host can broadcast administrative messages.
How does the chat system handle UI components that aren't ready to receive messages?
OnlineManager maintains a synchronized chatQueue_ list. When deliverChat is called but no handler is registered (via setChatHandler), the message stores in the queue. Once a UI component registers itself, setChatHandler atomically flushes all queued messages to the new handler before clearing the queue.
Does DD Poker use TCP or UDP for production chat traffic?
While the codebase includes a ChatServer that handles UDP packets for testing purposes (lines 31‑44 in ChatServer.java), production online multiplayer games use the standard TCP-based P2P messenger that carries all OnlineMessage objects, ensuring reliable delivery of chat alongside game state synchronization.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →