Accurate Server-side Input Processing

Started by
6 comments, last by kdev 10 years, 3 months ago

Hi there,

I've been developing a small networking demo for quite a while now and I'm at the point where searching the net doesn't seem to solve my problem. I understand the techniques described by the Valve papers and the various articles by the likes of Glenn Fiedler and Gabriel Gambetta, however I believe I'm running into a problem that they either are not covering or I just don't understand. Hopefully the wizards of gamedev.net can help me out.

My problem lies with how input is handled in a client / server, fast paced, 2D networked game. I send an input packet to the server every frame as well as running both the client and server at 60 fps. The server sends state updates every 50ms and the clients keep a 100ms buffer which they smoothly interpolate between.

On the client the player can press the 'left' key and the square that represents them locally moves left 16 pixels per frame. This means that if they held it down for say 50 frames, they would expect the box to now be 50 x 16 pixels to the left of the original position. My problem is that the server may simulate more than 50 frames due to the latency induced by the internet (and on my LAN it seems). If the server simulates an extra frame the server-player is now out of sync by 16 pixels with the correctly placed client-player. Sure I could do some reconciliation on the client-side, but not only would it be incorrect, the client would noticeably 'keep going'. I'm wondering to if my environment is too demanding. A 1280 x 720 window means that a frame of 16 pixels is very noticeable, let alone a couple of frames.

I could simply attach a timestamp (or frame count) to the input packets and at the server end, check if it has simulated more frames with a particular input packet than it should have and backup if needed. If world-state updates to clients are around 20 times per second, there is a chance that their use of a 100ms buffer would hide any artifacts, but again I'm not 100% on that. This seems like this might be adding unnecessary complexity. Others seem to hint that they timestamp their input packets but none seem to talk in-depth about how they utilise it.

I'm wondering if I'm simply asking too much. Quite a few games seem to hide these kind of problems with either animations and/or interpolating to the position reported by the server. I get the impression that the biggest movement an entity can make in one frame inside these other games is very small compared to my 16px out of a total of 1280px when moving horizontally. From what I played of Terraria, players tend to walk quite slowly, so an extra frame isn't very noticeable.

I appreciate any feedback you guys can give, hopefully I've explained the problem clearly enough!

Kev

Advertisement
You should measure time in "ticks," not milliseconds.
Input sent to the server is time stamped for a particular, numbered, "tick."
The goal of the game clock is to keep the rate of ticks advancing the same on the client and the server.
Typically, this is done by each update from the server containing the tick number(s) for which data is returned.
The client can then decide how far ahead or behind to keep its own tick rate.
enum Bool { True, False, FileNotFound };

Thank you for your reply hplus. So I was on the right lines with the timestamp (or tickstamp I suppose), but am I right in thinking that if the server receives input that has arrived late, it has to rewind and adjust the current state of things? I attached a quick diagram of the problem which in hindsight it should have included with the original post...

[attachment=19243:Networking1.png]

Thanks again,

Kev

if the server receives input that has arrived late, it has to rewind and adjust the current state of things?


That will allow the client to cheat, by sending late inputs once it already sees what the outcome is without those inputs. This actually was a developed cheat for some early FPS games (!)
A more common solution is to reject inputs that arrive late, and let the client know that it's sending data too late. Additionally, the client will then be out of sync, and will have to be put back into sync somehow.
The easiest way to do all this is to not display anything on the client until the commands have been received back from the server -- basically, each client displays only the simulation based on the inputs that are sent by the server, so they all display the same thing. The draw-back is that there is one round-trip of latency for every change in movement or other command.
enum Bool { True, False, FileNotFound };

Ah yes of course, that would allow cheating. Out of interest, what were those games?

So if I understand this correctly, it would be impossible for the client to hold down the left key for 200 ticks and the server simulate it for exactly 200 ticks also? As per my diagram, any difference in the time it takes for an input packet to reach the server will end up causing a difference between the client and the server. This problem would also be seen in the example you describe towards the end of your post. I'm wondering if I'm asking too much and I should be looking to hide the problem (by correcting the client to the server position) instead of trying to fix it.

Again I appreciate the feedback.

what were those games?


I think the original Counter-Strike was one of them, but memory is hazy.

it would be impossible for the client to hold down the left key for 200 ticks and the server simulate it for exactly 200 ticks also?


Not at all impossible. The client just has to send the packets to the server with enough time buffer to compensate for expected jitter.
enum Bool { True, False, FileNotFound };


As per my diagram, any difference in the time it takes for an input packet to reach the server will end up causing a difference between the client and the server.

Wouldn't the difference be both ways though? I mean if you press the left key and it took 50ms to get that information to the server then the client is 50ms ahead of the server. The server then puts that client into the correct state (by "pressing the left key" on the server version of that client). The client then lets go of the left key and that packet command is sent. The server then receives that (most likely around 50ms or so unless a major spike in lag happens), and then the server stops. Ideally where they stop should be basically the same location. All the time while this is happening the server is sending that clients server location on each tick. The client would then be taking that value and comparing it to where it really is on the client side. This most likely won't match up and since the server knows all, the client needs to adjust to match the server. The client needs to spread this adjustment up over multiple frames so it's not a snap.

The Input Prediction section talks about this

https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking

What I find interesting is the going back in time for rendering. Meaning all the other players you see moving around are 100ms old. This gives your client time to smoothly move them around via interpolation. Then when you shoot this 100ms is taken into account on the server and it gets all the positions at the time of your shooting and checks against collisions at that point in time. Crazy stuff :)

@hplus Thanks, I'll give it a go.

@rpiller Cheers also.

This topic is closed to new replies.

Advertisement