Wednesday, July 8, 2015

architecture - How would a game-state snapshot system be implemented for networked real-time games?


I want to create a simple client-server real-time multiplayer game as a project for my networking class.


I've read a lot about real-time multiplayer network models and I understand the relationships between the client and the server and lag-compensation techniques.


What I want to do is something similar to the Quake 3 network model: basically, the server stores a snapshot of the whole game state; upon receiving input from the clients, the server creates a new snapshot reflecting the changes. Then, it calculates the differences between the new snapshot and the last one and sends them to the clients, so that they can be in sync.


This approach seems really solid to me - if the client and server have a stable connection, only the minimal necessary amount of data will be sent to keep them in sync. If the client gets out of sync, a full snapshot can be requested as well.


I cannot, however, find a good way to implement the snapshot system. I find it really hard to move away from single-player programming architecture and think about how I could store the game state in such a way that:



  • All the data is separated from the logic


  • Differences can be calculated between snapshot of game states

  • Game entities can be still easily manipulated via code


How is a snapshot class implemented? How are the entities and their data stored? Does every client entity have an ID that matches an ID on the server?


How are snapshot differences calculated?


In general: how would a game-state snapshot system be implemented?



Answer



You can calculate snapshot delta (changes to its previous synced state) by keeping two snapshots instances: current one and last synced one.


When client input arrives you modify current snapshot. Then when it's time to send delta to clients, you calculate last synced snapshot with current one field-by-field (recursively) and calculate and serialise delta. For serialisation you can assign unique ID to each field in scope of its class (as opposed to global state scope). Client and server should share the same data structure for global state so client understands what a particular ID is applied to.


Then, when delta is calculated you clone current state and make it the last synced one, so now you have identical current and last synced state but different instances so you can modify current state and not affect the other.



This approach can be easier to implement, especially with help of reflection (if you have such a luxury), but can be slow, even if you highly opitimise reflection part (by building your data schema to cache most reflection calls). Mainly because you need to compare two copies of potentially large state. Of course it depends how you implement comparison and your language. It can be fast in C++ with hardcoded comparator but not so flexible: any change of your global state structure requires modification of this comparator, and these changes are so frequent on initial project stages.


Another approach is to use dirty flags. Each time client input arrives you apply it to your single copy of global state and flag corresponding field(s) as dirty. Then when it's time to sync clients you serialise dirty fields (recursively) using the same unique IDs. (Minor) drawback is that sometimes you send more data than strictly required: e.g. int field1 was initially 0, then assigned 1 (and flagged dirty) and after that assigned 0 again (but remains dirty). Benefit is that having huge hierarchical data structure you don't need to analyse it completely to calculate delta, only dirty paths.


In general, this task can be quite complicated, depends how flexible should be final solution. E.g. Unity3D 5 (upcoming) will use attributes to specify data that should be auto-synced to clients (very flexible approach, you don't need to do anything except adding an attribute to your field(s)) and then generate code as a post-build step. More details here.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...