TicTacToe Game State in a Networked Game
The full Poker game state is known only to the PokerHub, not to the players.
Why One Shared Record Looks Tempting
When people first build a networked card game, they often choose the simplest design available: keep one complete record of the game, and send that record to everyone after each move. For games where all information is open to every participant, this design works well. The question this article answers is whether the same design still makes sense for Poker, and the source's answer is no. The reason lies in what a player is allowed to know.
The source's example is Poker. The chapter's simpler games, which the source refers to, have information that every participant can see, so one shared record fits them. Poker is considerably more complicated, and its game state grows in step with that complexity. Keep that contrast in mind as you read: the question is not whether a shared record is convenient, but who is allowed to see which part of it.
Tracing the Simple Design Through One Move
- 1Keep one complete record of the game.
- 2A player makes a move, such as placing a bet, and the record changes.
- 3The hub sends the whole record to every player after that move.
This design creates two separate problems in a networked Poker game. The first problem is efficiency. It is not obvious that the hub should send a brand new copy of the whole state to every player each time a small change happens, such as one bet being placed. Most of that copy would be unchanged, so the players would receive far more data than they need to update their screens.
The second problem is information, and it is the more serious of the two. A full copy of the game state would reveal the opponent's cards and the complete order of the deck. A player should not be able to see either of these during play. The efficiency problem makes the design wasteful, but the information problem makes it incorrect, because the design gives players knowledge they must not have.
Why Hiding Data on the Client Fails
A reasonable first thought is that the client program can simply hide the sensitive information from the screen. The player's client would receive everything, but it would only display what the player is meant to see. The source explains why this is not enough. A client program is software the player controls. A player who writes a substitute client can read the hidden information directly, because nothing stops that substitute from displaying data that the official client hides.
The lesson that follows is that security has to come from what the server sends, not from what the client chooses to display. This is the reason the Poker design changes what is sent, rather than relying on the client to filter it.
Splitting the Hub from the Player Views
The Poker application solves both problems by separating two ideas that the simpler design merged together. The full game state is kept in exactly one place, the PokerHub. The PokerHub is the only component that knows everything about the game. Everything a player is allowed to know is delivered through a different kind of object, called a PokerGameState. Each PokerGameState describes the game only from the perspective of one particular player.
PokerGameState: an object that is a view of the game from one player's point of view. Each player receives only their own view.
The two parts of this split are easier to understand when you look at each one separately. The first part is what the hub keeps. The second part is what a single player receives.
Tracing One Change to Two Objects
The process is triggered by change. When the state of the game changes, the PokerHub does not update one shared record and broadcast it. Instead, it builds two different PokerGameState objects, one for each player's point of view, and then sends each object to the player it was built for. Before reading the figure, try to predict the number of objects the hub builds for a single change.
What do you think happens?
After one bet is placed in a two-player Poker game, how many PokerGameState objects does the PokerHub build?
Reveal answer
Two objects, one for each player's point of view
The source states that when the game changes, the PokerHub creates two different PokerGameState objects and sends each one to the appropriate player. The hub does not broadcast a single shared record.
Read the diagram as a rule. One change produces two objects, and each object goes to exactly one player. The hub is the only place where the full state exists, and the arrows that leave it carry only the per-player views.
Tracing a bet through the hub
Player A places a bet during a two-player Poker hand. Trace what the PokerHub does, and state what each player receives.
- 1Hub holds the full state
Before the bet, the PokerHub holds everything: both players' hole cards, the deck order, and the rest of the game state.
- 2Change occurs
Player A's bet changes the game state. The hub does not copy the whole record to both players.
- 3Hub builds Player A's object
The hub builds a PokerGameState from Player A's point of view. It includes what Player A is allowed to know about the new state.
- 4Hub builds Player B's object
The hub builds a second, different PokerGameState from Player B's point of view. It is not a copy of Player A's object.
- 5Each object goes to one player
Player A receives only Player A's object, and Player B receives only Player B's object. Neither receives the other's object or the hub's full record.
One change produced two different objects, each sent to exactly one player. No player received information outside their own view.
Mistakes Beginners Make With Shared State
Common mistake Sending the full game state to every player and relying on the client to hide the hidden parts.
What it looks likeThe hub sends the complete record, including the opponent's cards, and the client simply does not draw them on screen.
Why it failsThe client is software the player controls. A player who writes a substitute client can read the hidden information directly.
Do this insteadBuild a separate PokerGameState for each player so the hidden information is never sent to that player.
Common mistake Assuming the efficiency problem is the only reason to change the design.
What it looks likeA developer only worries about sending too much data after each small bet, and ignores what the full copy reveals.
Why it failsThe source names two separate problems. The information problem is the more serious one, because it allows players to see what they must not see.
Do this insteadTreat efficiency and information as separate requirements, and check each design against both.
Common mistake Thinking the hub and the player views are two copies of the same data.
What it looks likeA developer stores one shared object and passes it to both players with small differences added afterward.
Why it failsThe source describes the player objects as different objects built from different points of view. The hub keeps the full state, and each player object is a projection of it.
Do this insteadKeep the full state in the hub only, and build each PokerGameState fresh from the hub's data for its player.
Practicing the Who-Can-See Check
A player in a Poker game wants to know what the opponent is holding. Using the split between the PokerHub and PokerGameState objects, explain where that information lives, which object would or would not contain it, and why the hub must not send it to that player.
Hints
- Ask who should be able to see the opponent's hole cards, and which component holds the full state.
- Think about whether hiding the card on the client would stop a player who writes their own client.
- Check whether the player's PokerGameState includes the opponent's cards.
Key Takeaways
- A Poker game cannot safely broadcast one shared copy of its state, because a full copy reveals hidden cards and the deck order, and it is also wasteful to resend mostly unchanged data.
- The PokerHub keeps the full game state in exactly one place and is the only component that knows everything about the game.
- Each player receives a PokerGameState object that describes the game only from that player's point of view, and the hub builds a different one for each player when the game changes.
- Hiding data in the client does not protect it, so security must come from what the server sends, not from what the client displays.