Advertisement

"Parallel" Async

Started by June 23, 2006 09:02 AM
5 comments, last by markr 18 years, 7 months ago
Hi, I have some problems with my network frame design in a strategy game. There are network frames every 200ms (or 400ms, depending on the pings). So the clients execute their commands etc. always on these frames, so the game stay sync. They send their commands for the next frame always at the previous frame. E.g. when player 2 reach frame 22, he send his command for frame 23. The server send also a "done" command for each frame like the clients their commands because it shouldn't become async, too. When the command of one player is missing when they reach the next networkframe, they pause their games and wait until the missing command of a player or of the server arrive. This works finely. But there is a little problem: All the players and the server dont't reach the frame at exactly the same time, it isn't necessary of course but a situation like this often occurs: Say, there are 2 players (or a player and a server, it doesn't matter): player 1 and player 2. The loading of the graphics, sounds, map etc takes differnt times for each player. So it's possible that player 2 starts the game earlier than player 1. At frame 0 (i.e the start of the game), the players send their commands for frame 1 and so on, as i said. Player 1 is still at frame 0 and send the command for frame 1. Player is already at frame 1, when player 0 is still at frame 0, player 1 is at frame 1 when player 2 is at frame 2 etc. So player 2 gets always little "lags", no "real lags" of course. Are there any ideas to avoid this? I thought of making an additional pause if there is a lag, but there are always "real lags" sometimes and then it wouldn't be so nice... :(
Can't you wait until all players have finished loading before synchronising the gameplay?
Advertisement
How should I do it? Each player could send a message at the start of the game that he's ready but the clients won't get the message at the same time the player sent it because the sending takes time and its also possible that the others didnt finish loading when the message arrive...
You need two things:

1) Send a message to the server when the client is ready to play. The client doesn't actually start playing until it hears back from the server that everybody's ready to go. The server will send a message to everybody to start playing, when it's heard that everybody is ready to play.

2) Double-buffer your commands. When working on frame 1, all commands you make should take effect only during frame 2. Hide the latency by using command acknowledgement animations, etc. This is part of the "RTS model" described in the Forum FAQ.

If your game involves direct input things like piloting spaceships, then you might want to send commands more often -- if you send commands every 200 ms, there will be 200 ms lag, no matter what!
enum Bool { True, False, FileNotFound };
1) The problem is that the "ready, we can go - message" from the server doesn't arrive at each client at the same time. So each player will start at different times, depending how much time the message needs from server to client.

2) Yes, my system already works that way.
You need to synchronize your clocks within a reasonable limit. An easy way to do it (although not the best) is to send a timestamp to the server each frame, and have the server send back your timestamp and its timestamp when it received it in each response. That way, you can know how much the difference is between your and the server's clock (assuming RTT is symmetrical, which you really can't detect if it isn't). By calculating a weighted average of the clock differences, and using this to adjust the client clock, you can get to within a handful of milliseconds of precision (unless jitter is large).

Then, when you receive the signal to "go", you actually start going at the start of the next frame.

A better way to synchronize the clocks is to use the NTP algorithm between server and client, but that's too involved for this post :-)
enum Bool { True, False, FileNotFound };
Advertisement
The way I've seen it done is:

1. When a player joins the game, take a snapshot of the game state (say in frame 10)
2. From then onwards, create a server-side buffer of all commands executed since that snapshot.
3. Wait while the snapshot downloads to the client - this may take quite some time, perhaps many frames, especially if the server throttles the download. Say this finishes on frame 20.
4. When the snapshot has been received, the client then "replays" the game between frame 10 and 20.
5. Thereafter, the client buffers the commands themselves and replays as necessary if they get a bit behind.

All commands would be executed on all clients and the server. Clients never execute their own commands locally immediately, instead, they send them to the server, which chooses when to execute them (which could be many ticks later), then sends them back to the client in the appropriate place in the stream. Any visual / audible feedback from the command happens on the client when the command is received from the server (e.g. you order your troops to somewhere they can't get to , a message comes up "Can't get there"). Of course this only happens for the client player's OWN commands - errors / feedback from other players commands are ignored (but the commands are still carried out in any case if possible)

Most frames will in any case, contain very little data as nothing will happen. In an RTS, players typically don't take an action on every single tick, but rather, wait 10s or 100s of ticks between actions.

Finally, you should have some mechanism of detecting sync failures. Normally the approach is to periodically hash some (or all if you can afford the performance hit) of the game state and send it to the server. So you say "In frame 100, my hash of the game state was 0x12345678". The server also generates this, and if they're different, a desync has occurred.

Mark

This topic is closed to new replies.

Advertisement