State Synchronization Events vs Deltas

Started by
4 comments, last by itay9 3 years, 9 months ago

Hello folks

I have researched about State Synchronization in networked games, and couldn't find a material that could solve the specific requirements that I have, my goal through this discussion is to understand High Level how such an algorithm would work to accommodate my needs.

The game I'm working on is an Action/RPG built in a coop mode, I have logics such as: “Perks”, “Skills”, “Items” etc.

Currently I'm using an event-driven approach to sync the clients, whenever I get a command packet from the client I process it (without locks) and queue a task that will send the data to nearby players.

The above approach allows for race conditions between state modification - I think I should also queue up the task that modifies the state as well (what are your thoughts about this?)

And I wonder if shouldn't I create a “StateDelta” that contains all the modifications that have been performed during this tick, and then extract from it a matching “delta” for each player.

Advertisement

If you're going to send the new state anyway, why do you also need to send the event?

There are two and a half modes of networking:

  1. Send all inputs, and make simulation fully deterministic, and use some kind of lossless, pause-when losses-occur, kind of simulation. RTS-es do this – the classic Age of Empires article is a good case. The GGPO library for fighting games is another (which trades re-simulation for latency.)
  2. Send all outcomes (states) as they happen. Events only go from controlling client to the server. This has the benefit of trending towards eventual consistency even when you temporarily get something wrong. It has the draw-back that states can be pretty big, and various smart game-dependent compression approaches end up being useful.
    2.5 One of the ways of compressing, is to send events opportunistically, but occasionally baselining anyway (send a state snapshot every N seconds, where N might be smaller for people in the same group, or physically nearby, or who have seen in some sense “larger” changes, for example.)
enum Bool { True, False, FileNotFound };

@hplus0603

hplus0603 said:
Send all inputs, and make simulation fully deterministic, and use some kind of lossless, pause-when losses-occur, kind of simulation. RTS-es do this – the classic Age of Empires article is a good case. The GGPO library for fighting games is another (which trades re-simulation for latency.)

If I understand this point correctly:

  • Perform each state mutation in a deterministic fashion (make sure that no race condition can occur)
    • Would queuing the mutation tasks be considered a better practice than simply locking the mutation itself?
  • “and use some kind of lossless, pause-when losses-occur, kind of simulation.” - Do you mean that the input packets themselves are stored and then during the next tick are processed and later sent?

hplus0603 said:
Send all outcomes (states) as they happen. Events only go from controlling client to the server. This has the benefit of trending towards eventual consistency even when you temporarily get something wrong. It has the draw-back that states can be pretty big, and various smart game-dependent compression approaches end up being useful.

Why does states have the chance to become big in this scenario?

For example:

  • I sent a packet at tick X
  • The server received it between X and X +1
  • The server processed the packet and sent an output before X + 1

Does the above flow match the point you've mentioned?

hplus0603 said:
One of the ways of compressing, is to send events opportunistically, but occasionally baselining anyway (send a state snapshot every N seconds, where N might be smaller for people in the same group, or physically nearby, or who have seen in some sense “larger” changes, for example.)

This is the current method that I use, does it suffice for the “Action/RPG ”Coop (fast pace)?

Thanks for your input

For the full “input synchronous deterministic simulation” scoop, please google and read the “1500 archers on a 28.8k modem” article about Age of Empires networking. It's a classic, and describes the model very well.

In very brief, every event must happen in the same, deterministic order on all clients. This means that, to actually execute simulation step N, all clients must have the inputs from all other clients for step N. Generally, this is arranged by sending commands for some time in the future when the command is issued, and the queue is appropriately sorted on each client. Simulation of step N CANNOT start until you get all the inputs for step N from each client. In RTS games, this delay is hidden behind a locally-played “order received!” animation in the UI.

[quote]Why does states have the chance to become big in this scenario?[/quote]

Because game state is big. Even simple rigid body dynamics for a single body is at least 12 floating point values – position, velocity, orientation, and spin. Compare that to the few bits needed for input – typically aim orientation and a few bits for move/strafe/jump/crouch/sprint. But in general, the state for the player will also include a bunch of other parameters, somewhat depending on game type and specifics.

[quote]This is the current method that I use, does it suffice for the “Action/RPG ”Coop (fast pace)?[/quote]

Yes, as long as you don't have too many players in the same map at the same time.

enum Bool { True, False, FileNotFound };

@hplus0603

As always thanks for your knowledge, I'll dive deeper into the reference you've mentioned.

This topic is closed to new replies.

Advertisement