13
\$\begingroup\$

I'm implementing a multiplayer asteroids clone to learn about client/server network architecture in games. I have spent time reading GafferOnGames and Valve's publications on their client/server tech. I am having trouble with two concepts.

  1. Currently I have an authoritative game server simulating physics with box2d and sending out the state of the world to clients about 20 times per second. Each client keeps track of the last few snapshots it received and lerps between two states to smooth out the movement of sprites. However it's not that smooth. It can be smooth for a while, then jerky a bit, then back to smooth, etc. I have tried both TCP and UDP, both are about the same. Any idea what my problem might be? (Note: I implemented this for single player first, and the sprite movement is perfectly smooth at 60fps when updating the physics world only 20 times per second).

  2. In order to solve the first problem I thought maybe the client should run a box2d simulation as well and just update the positions of its sprites to match the server snapshots when they don't match. I thought this might be smoother since my single player implementation is smooth. Is this a good idea?

    Even if it won't fix the above problem, is it necessary for client-side prediction? For example, if a player attempts to move their ship, how will they know if they hit an asteroid, wall, or enemy ship without a physics simulation? It seems like their ship would appear to pass through the object it should collide with before they receive a snapshot from the server that says they hit the object.

Thanks!

\$\endgroup\$

4 Answers 4

10
\$\begingroup\$

Definitely run the simulation on both the clients and the server. Anything else have too long latency. You should be sure that the simulations match by inserting objects in the same order, using a fixed time step and avoiding pointer comparison. I haven't tried this with Box2D but it is generally possible to achieve the same behavior on all machines in a physics simulation. All math is usually based on IEEE 754 binary32 floats and their behavior is strictly defined for operations such as +-*/ to name a few. You need to be careful about sin, cos and the likes tough, since they can differ between runtimes (this is especially important when developing for multiple platforms). Also make sure that you use a strict setting for float optimizations in your compiler. You can still synchronize objects by periodically sending out the state of objects from the server. Don't update unless the difference is larger than a threshold to avoid unnecessary stuttering.

One issue that come to mind is the creation of new objects and how that will change the simulation between clients. One way to fix this is letting the server create all objects. If the current time step is t, the server will schedule a object to be added at t+d. Thus, a new-object list, with objects to add and when to add them, can be can be maintained at all the clients and updated by the server well in advance. If d is large enough, you minimize the risk of different results. If you really can't handle difference you can force a client to wait for information about new objects for a certain time step before simulating that time step.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks for your response. I don't think box2d is guaranteed to generate the same results across different CPUs, which would be the scenario for us since we're writing a desktop game. I would hope that the differences would be minute and easily correctable with periodic updates from an authoritative server, but I've never tried it. \$\endgroup\$ Commented Feb 7, 2013 at 4:32
  • \$\begingroup\$ Erin Catto thinks, that trying to keep the entire state of multiple Box2D worlds in sync is a losing battle (box2d.org/forum/viewtopic.php?f=3&t=8462) \$\endgroup\$
    – Pavel
    Commented Jun 22, 2013 at 2:07
  • \$\begingroup\$ The statement "IEEE 754 binary32 floats [..] behavior is strictly defined for operations such as +-*/" is completely false. All of those operations in IEEE-754 could vary based on the implementation. See here and here for more info. \$\endgroup\$ Commented Feb 21, 2014 at 22:20
  • 1
    \$\begingroup\$ No. It is completely true. The problem your link describes are related to different modes of the x87 fpu and implementations of transcendentals. IEEE 754 binary32 is strictly defined for the basic operations. It is up to you to set the correct modes and use the right instructions so that the standard is followed. Simply using SSE instructions and not the x87 fpu helps a lot. \$\endgroup\$
    – rasmus
    Commented Feb 24, 2014 at 14:06
4
\$\begingroup\$

It probably doesn't look so good since interpolating between them relies on always having the next set of data to interpolate to. This means that, if there's a short lag spike, everything has to wait to catch up.

There's an old article on GameDev about using cubic splines to predict the position of an object past the point where you last had data for it. What you then do is use that position and then adjust the spline when you get new data to account for its new position. It's also probably much cheaper than running a second physics simulation, and it means that you don't have to decide about who you trust, since you've explicitly implemented the client making it up as it goes along. :)

\$\endgroup\$
1
  • \$\begingroup\$ This could be the case. What I'm trying to do is delay until I have received 3 snapshots from the server. At the point I lerp from shot 1 to shot 2. Then from shot 2 to shot 3. If at any point I miss a packet I can lerp from 1 to 3, instead of 1 to 2, if that makes sense. I may not be implementing this correctly yet though. Thanks for the link to the article! \$\endgroup\$ Commented Feb 7, 2013 at 4:22
1
\$\begingroup\$

I've done some similar stuff myself, and I ran Box2D on only the clients. The way I did it was to let the client run its own simulation pretty much on its own, sending the current velocity (and rotation) on every sync packet to the server. The server then sends this information to other players, who set the newly-received velocities to the replicated entities. It was very smooth, without any noticeable differences between clients.

Of course, the problem here is that there's no centralized control over entities, but I think that could be done on the server side as well by doing server-side simulation of the physics as well

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your input. We will require centralized control to prevent cheating, so we have to have the server running a simulation at the least to know if what the clients say they are doing is possible or not. \$\endgroup\$ Commented Feb 7, 2013 at 4:25
1
\$\begingroup\$

I would personally prefer to run the simulations only on the server and have it broadcast any changes on linear/angular speeds/accelerations of the involved objects whenever they happen. It is, when a certain object, for any reason, changes any of it's physical properties (such as the aforementioned speeds and accelerations), this specific change will be sent from the server to the client, and the client will change it's side of the object's data accordingly.

The advantage of it over your current implementation is that it will void the necessity of clientside interpolations and will generate a very faithful behavior on the objects. The problem is that this method is quite very vulnerable to latencies, which become a very big problem when the players are geographically too far away from each other.

As for the question 1, I's say the problem would be fluctuations on the latency, because there is no absolute guarantee that there will be an exactly perfect 20 second interval between each receiving of the snapshot. Let me illustrate (being "t" the time measured in milliseconds):

1)At t = 20 since the game's start, the client received a snapshot and did the interpolation succesfully and smoothly.

2)At t = 40, there was a latency between the server and the client, and the snapshot happened to only actually arrive at t= 41.

3)At t= 60, the server sent another snapshot, but one second of the simulation was wasted clientside because of the latency. If the snapshot arrives at t= 60, the client will not do an interpolation of the 40 and 60 instants, but actually from instants 41 to 60, generating a different behavior. This inexactness on might be cause of the eventual "jerkiness".

As for question 2, your idea might work if you implement something that will efficiently track whether each object are really client-server synchronized without having to send packages each frame informing the position of the objects. Even if you do it at discrete intervals, you will not only run on the same problem of the question 1, but also have too big quantities of data to be transferred around (which is a bad thing).

\$\endgroup\$
2
  • \$\begingroup\$ I'm not sure I follow what you're saying in your first paragraph. If the simulation runs only on the server and you only broadcast changes in velocity/acceleration then how does the client know where the sprites should be drawn? The clients would have to simulate the objects based on the received velocity/acceleration in order to draw them properly. I think you may be right about receiving snapshots at intervals other than exactly what I expect. Any idea how to deal with that? \$\endgroup\$ Commented Feb 7, 2013 at 4:27
  • \$\begingroup\$ The clients know the initial and current positions, speeds and accelerations of the objects, and will update the position it thinks the objects are (independently of the server) accordingly. The server will eventually change such properties on the clients through messages, because it is the server which is doing the physics and collision detection (which are bound to change the speed/acceleration and direction of given object sooner or later) \$\endgroup\$
    – UBSophung
    Commented Feb 7, 2013 at 12:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .