Synchronization

Now we’re at the 2nd stop from roadmap. I’ll talk about how to synchronize the the game across different clients.
Synchronization should consider 2 factors: data and graphic display.

Data Structure

Here the term data structure refers to organizing data in a structured way, and is different from the term used in computer science.
The data inside this structure is pure logical data needed to run game system, and not the scene with many objects. In other words, not relating to graphic display directly.
Ideally, this data structure should have the following properties:

  • Serialize with easy so that can be sent/received through socket
  • Can take substructure from it for optimization purpose
  • Can hold arbitrary data so that it is a general solution

Json is the perfect candidate.
You can jump/parse json. You can take a node from json. You can stuff anything inside json.
To be exact, since this data structure is pure logical data, i.e. number and string, you can use json to store all things for game system. We are not sending pointers through socket anyway.
And you usually don’t use binary data for game system. You are not storing an image in the structure. Rather you store the path to the image as string. For multiplayer games, assets should be prepared before launch in local device.

For example, the 2-player rock-scissor-paper game will have a data structure like this.

One node can be a representation to an object in scene. The “Score” node can have name, position, text, layer…
If data is organized in such a way, we can synchronize them easily. The game system just sends a portion of json together with the path. The clients receive the content and update local data structure accordingly.

Graphic Display

Data structure only deals with logic, how about visual effects? You can define your instruction set to achieve the desired result.
You can define the instruction set anyway you like, as long as it solves your problem.
For example, you can define a move instruction like this.

op:mv_node,name:AAA,x:0,y:0,dur:1000

Semantically, move the node AAA to (0, 0) within 1000 milliseconds.

When changes involve the graphics display, the game system will generate corresponding instructions and send to clients.
When clients receive the instructions, they decode and perform the actions on local device.

Putting it Together

This is a game created with World_Creator. A movie-themed DiXit.

Let’s just focus on the first move: when player1 clicks on a hand card it goes to the center of the table. What’s happening under the hood.

I hope you still remember the discussion about system&session.
The game system runs a step, notifies P1 to make a choice. P1 clicks on a card. This choice is sent back to game system. Session doesn’t change the scene.
Now when game system gets the choice, say C15, what happens next?

Node “Hand” and “Guess” represents the hand card of P1 and the center card display.
System gets these two nodes, takes the C15 card from “Hand” and add it to “Guess”. Then it sends these two nodes to clients. The clients update local data structure.
System also generates the instruction for this action. For example “op:mv_node,name:C15,x:50,y:300,dur:1000”, and possibly other related instructions. Then it sends te instructions to clients. The clients update local graphic display.
Now everything is set on logical level and graphic level. System proceeds to next phase. Same story again.

Benefit

  • This is a general solution that can be applied to any game. What’s more, the instruction set you define serves as an abstraction between your game and game engine. You can pick any engine you want and implement the instruction set. That will do.
  • You can pause/resume/replay the game with no hassle. Given a series of data updates and instructions in order, you’ll always get to the same game state.
    This is very important for online games. For example A, B and C are playing. At state 15 B suddenly drops. No big deal. The game can proceeds due to system&session design. Now at state 20 B returns. You just send the data updates and instructions B is missing in order. B now catches up and rejoins the game.
  • Minimal bandwidth requirement. You are just sending a bunch of strings after all, which can be further compressed by zlib.