10
\$\begingroup\$

I am currently working on a rather simple multiplayer platformer. I read quite a bit of articles on the techniques used to hide latency, but I still fail to get my head around certain of the concepts. I find the topic very interesting and like to try ideas by myself, but I think asking gamedev stackexchange will be more efficient for my question. I will try my best to describe my current situation and what question arose along the way.

Right now, I only want to have a single player synced with the server. Theoretically, I assumed a player by himself with client-side prediction would not require server corrections, since there are no external factors that influence his movement. Therefore, my prototype currently only has one player synced with a server without server corrections being sent.

If you are familiar with game networking, I think you may skip the context sections, though I may have done something wrong along the way as well.

The client loop (once per frame, once every ~16.67ms)

The simplified client loop looks like:

  1. Check for local input (WASD) and package them as actions (e.g. Type=MoveLeft, Time=132.0902, ID=15). We keep the packaged actions to eventually send them later. Also, we directly apply the desired action to the local physics simulation of the game. For example, if we have a MoveLeft action, we apply a force towards the left on the player's velocity.

  2. Check to send actions. To prevent from abusing the bandwidth of the client, only send the packaged actions at certain intervals (e.g. 30ms).

  3. Apply server modifications. At a certain point this will handle deltas and corrections received by the server and apply them to the local simulation of the game. For this particular question, this is not used.

  4. Update local physics. Run the physics loop on the main player. Basically, this does the client-side prediction of the player's movement. This adds gravity to the player's velocity, applies the player's velocity to his position, fixes collisions along the way, etc. I should specify that the physics simulation is always executed with a fixed delta seconds (called multiple times depending on the real delta seconds).

I'm skipping a few specific details about the physics and other sections because I feel they are not required for the question, but feel free to let me know if they would be relevant to the question.

The server loop (every 15ms)

The simplified server loop looks like:

  1. Handle actions. Check for received action packages from clients and apply them to the server physics simulation. For example, we could receive 5 MoveLeft actions, and we would apply the force to the velocity 5 times. It is important to note, a whole action package is executed on one "frame", contrarily to the client where it is applied as soon as the action happens.

  2. Update the game logic. We update the game physics, moving players and fixing collisions, etc. We also package any important events that happened to be sent to players (e.g. a player's health dropped, a player died, etc.) later on.

  3. Send corrections. We regularly (e.g. once every 35ms) send deltas to other players (e.g. player positions, health, etc.) if they recently changed. This part is not currently implemented, as I want the simulation of a single player to give the same results on the client and server without corrections, to make sure that the client-side prediction works fine.

The problem

The current system works fine under simple circumstances, and I was happily surprised to see that it gave very similar results with simple horizontal movements(the inaccuracies are due to floating point precision errors, I believe):

The sync works fine with simple collisions/movement

Please ignore the prototype graphics. White rectangle = player, Red rectangles = obstacles, Blue = background

However, I'm getting sync errors after doing time sensitive movements, such as jumping and moving close to an isolated obstacle:

The sync doesn't work because I jumped around the specified obstacle at time-sensitive moments

In theory, I would expect both to always end up with the same results, as there are no external factors to the client influencing his position. In practice, though, I think I understand the problem.

Since jumping around an obstacle like that is very dependant on the timing of the player, small variations of when the velocity is applied to the position will have repercutions on the outcome (e.g. the client could move away just in time to avoid a collision with the obstacle, while the server would do it as it receives the whole action package later on and stay stuck on the obstacle for a small amount of time, changing the final result). The difference between how the client and server handle it is mainly that the client does all of its actions as they happen, while the server does them all in bulks as it receives them.

The question

This long context finally leads to my question (thank you for reading this far): Is it normal to require server corrections even when there is only one player synced with the server, or should I use certain techniques to avoid desynchronization on time-sensitive situations?

I have thought of certain possible solutions, some of which I am less comfortable with:

  1. Implement server correction. Simply assume that this is normal behavior and correct errors as they happen. I wanted to implement it anyway, but I just wanted to make sure that what I have done so far is acceptable.

  2. Use the provided client time to apply the desired actions. I guess this would be similar to lag compensation, requiring to "go back in time" and check for movement. Kind of like applying server corrections, go back in time and reapply the next actions after that. I really dislike the idea. It looks complex, expensive in resources and requires trusting the client's given time (although I do plan to really check that the time looks relatively legit).

  3. Ask GameDevelopment StackExchange for a great new idea that will fix all of my problems.

I am just starting out in the world of game networking, so please feel free to correct/criticize/insult any of the concepts above or give ideas/resources that could help me along my journey in the Wonderful World of Networking. Pardon me if I could have found my answer elsewhere, I have failed at that.

Thank you very much for your precious time.

\$\endgroup\$
2
  • \$\begingroup\$ Your server and client run frames at different rates. What happens if the client performs two actions on consecutive frames, but the server sees a one-frame gap between them? \$\endgroup\$
    – user253751
    Sep 18, 2015 at 5:24
  • \$\begingroup\$ @immibis based myself off of gafferongames.com/game-physics/fix-your-timestep to minimize this effect. \$\endgroup\$ Sep 18, 2015 at 11:18

1 Answer 1

11
\$\begingroup\$

In such cases, you may be better off letting the client be slightly authoritative. For such precise controls you're extremely unlikely to get good behavior even with really advanced correction and prediction.

The client needs to extend from just sending "I jumped" messages to sending "I jumped from X,Y at time T" messages. The server then checks that the location is within close proximity to what it thinks the player was at at time T (which you can limit to a reasonably small time in the past) to protect against cheating, then simulate the jump from the position the client sent. The server only corrects the client when it is far out of whack (usually due to lag or the like).

This sort of technique is used in conjunction with correction and interpolation in order to make the game feel responsive on the local client and look smooth for remote clients.

\$\endgroup\$
3
  • \$\begingroup\$ Very interesting. I will try to implement this and see how it goes. Thank you very much for taking the time to answer. By the way, you mentioned "within close proximity" and "reasonably small time in the past", would you simply check with a constant distance and time, respectively? Or you would use more sophisticated techniques like keeping an history of positions and using the client's average Round-Trip Time (or anything else, really)? \$\endgroup\$ Aug 22, 2013 at 22:37
  • \$\begingroup\$ Whatever works for your game. Start simple, complicate it only to the degree you find necessary. Some servers do keep a snapshot history of sorts around for ~max(RTT) server ticks in the past, but whether you need to for your game specifically I don't know. It can be even more handy for shooter-style games where you also want to do some level of hit/shot detection on the client and need to not only know where a player was 46ms ago but also where his target was 46ms ago and where any moving occluding platforms where. \$\endgroup\$ Aug 22, 2013 at 23:09
  • \$\begingroup\$ Perfect. I'll experiment and see what works best. Marking it as the accepted answer, thanks again! \$\endgroup\$ Aug 23, 2013 at 13:52

You must log in to answer this question.

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