2
\$\begingroup\$

I am trying to figure out how to make a game like Pong in multiplayer. I have two devices that run the same simulation. Now I want to make sure that each of them start at the exact same time and send packets from the host to the client that confirm the position of the ball.

I am not sure if this would be the right approach, but how do I make sure that both devices are running the same game?

\$\endgroup\$
3
\$\begingroup\$

The first fact that any multiplayer game needs to accept is: "No matter how good your connection is, there will always be some unknown delay between the server and clients."

Because of this, contemporary real time networked games "run" the game on the server and the clients are just snapshots that are as accurate as possible, given any network latency. Because of the speed of networks, it just appears as though both clients are completely synchronized.

In the case of a pong game, the server would keep track of the ball and paddle's coordinates and periodically push this data to the clients. The server is also the one running the movement of the ball and collisions with the boundaries or paddles. Whenever the clients receive updates from the server, they override whatever data they have with the server's data and then render the game.

In return, whenever the clients receive input from the user, like a command to move the paddle, they send the inputs to the server and then let the server take care of the details of the result of that input.

For fast networks, this is usually enough, but what if you have a bit of a laggy connection? Do you want your game to freeze or appear choppy when the network slows down?

Many clients solve this problem by using Client Side Prediction. In a nutshell, this means that you are actually running your game on the client AND the server. Note that the server is the authority and any updates that the server sends overrides the data that the client has. If the network slows down, your client will continue to show a smooth experience to the user, but when a server update comes in and disagrees with the client, then the client will correct itsself by using the server's data. Really bad lag will still certainly make for jumpy movements in this case, but this smooths out most of the minor latency issues.

Here is the most helpful article I have found on the subject. Also here is a post I asked about a similar issue which had some pretty good responses.

\$\endgroup\$
  • \$\begingroup\$ Hi, thank you for your answer! I have been working on this problem and read the article you posted. Currently the game is running in sync, and the server is authoritative. However, sometimes the clients get corrected and I see stuttering effects, even on a fast connection. What would be the best solution to achieve a smooth experience on the client side, while keeping the clients in sync? \$\endgroup\$ – Z0q Sep 28 '16 at 7:42
  • \$\begingroup\$ Did you do Client Side prediction? If so, sometimes games will use a threshold when getting server corrections. If the client is "close enough" then no correction occurs. I.E. if the ball has already gone past the server location by 1 pixel, we wouldn't want the ball to snap back causing stutter if it's only one pixel. \$\endgroup\$ – spectacularbob Sep 28 '16 at 16:29
  • \$\begingroup\$ Thank you. I thought about that. However, once the ball accelerates, the stuttering becomes more noticable. It will start to jump longer distances. \$\endgroup\$ – Z0q Sep 29 '16 at 10:04
1
\$\begingroup\$

Alternative approach, roughly:

  1. Synchronize the clients against a public time server. Works over UDP packets. If you do it carefully and grab enough samples, you'll get down to 1 or a few ms of accuracy. You bacically grab the external timestamps, average/process/etc. them, and compare those to your internal crystal's (see the QueryPerformance API if Windows) current timestamp (= crystal count), and resolve an offset plus a crystal drift (typically a few secs / 24h). Your time function must take that into account. Do NOT use the system clock of the OS - it may change (slowly, gradually) as the OS adjusts itself.
  2. At startup, issue a command to all clients (an agreement) to start the simulation at a specific, nearby timestamp.
  3. Sync the frame paints of the clients to happen at for example n*0.02 seconds (if 50 fps), n*0.025 seconds (if 40 fps) etc. Do NOT use time deltas, only absolute time/elapsed time since start.
  4. Send the UDP packets between clients, always together with the sender's local (and correct) timestamp.
  5. Always store your own client's most recent actions for the last say 2 seconds.
  6. At each client, run the simulation of others using extrapolation/prediction/deadreckoning/etc, but when a packet comes in, rewind to the timestamp of that packet and redo the simulation up to "now", this time taking the clients' most recent actions, as well as your own ones, into account.

Clients should now perform in sync. But this is not at all an easy task, and will require a lot of tuning. You may still need an authorative element, depending on the nature of the simulation.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.