Decoupling Client Input from the Render Loop

Started by
1 comment, last by PhilW 18 years, 6 months ago
Hi everyone, I am writing a multiplayer (client-server) space flight simulation, and I am trying to come up with a good way of decoupling my device input from the render loop, any advice would be appreciated. Currently I poll input, send the input data to the server, run a client-side prediction physics step, and render. I use the duration of the last render loop as my 'deltaTime', which is sent to the server along with the input data, and used as the basis of the prediction physics step (client and server share exactly the same physics engine, and so should both come out with the same results given the input and deltaTime - given no external interference, i.e. collisions) My concerns with this approach are: a) Inefficient use of datagram packet space in the message to the server (I am also piggybacking ACKs, reliable message resends and the like, but even so, I am sending LOTS of packets with very little data in them) b) Scalability, with a lot of clients connected this generates a swathe of incoming data at the server, exacerbating the natural server bottleneck c) Clients with more powerful hardware generally get through the render loop quicker, and so send more data - something I'd rather avoid The simple answer might be to poll input less frequently, based on the client clock, rather than once per loop iteration. I am worried that this will just make things seem sluggish at the client, and some input might be lost between polling (mouse clicks being pretty quick)... Is it wise to try using both input polling AND input event notification together? I'm pondering making 'important' input (like firing your big space guns) event based so this can be handled immediately (and sent to the server ASAP) and leave linear movement, which is generally perceived to be pretty slow anyway, to clock-based polling. This won't solve the packet use inefficiency however (point 'a' above). I toyed with the idea of buffering up more than one input state before sending, so I could fill more of the packet. Given the server broadcasts updates at a slower rate than it receives data from the clients anyway, I thought this might be an acceptable shortcut, but I can't help feeling this would cause problems at the server (packet loss could be critical?). Maybe I should just use the extra packet space to redundantly resend previous input to help combat packet loss... Maybe I shouldn't bother decoupling input at all? Any advice is welcome, this is my first post, and I am new to this game programming lark, so I am hoping to tap your collective wisdom a bit :) Thanks in advance, Phil.
Advertisement
For a start, I think you may need to abstract your client input away from the messages you are sending to the server (if indeed you are sending individual key presses and mouse clicks/movements). And I don't think time-based updates are quite what you want either... I think what would be advisable is an event-driven approach.

The main point is that you really only want to send a message to the server when something changes.

What I would suggest is polling client input each time around around the game loop, and using this to calculate changes in key properties such as velocity (i.e. speed and direction of the client's vessel), weapon fire state, etc. These changes are the events that the server really cares about (not actual keystate and the like). Stuff information about these events into a queue.

Then, on the networking side of things, just dequeue these events and send them to the server until the queue is empty. Process incoming messages from the server as usual. If you don't receive a ack/response before another event occurs, stick a number of them together (in order) in order to send a more efficient (read: closer to MTU) packet.

I suspect a number of action games employ an approach something along these lines (correct me if I am way off anyone).

Obviously, you will need to do some sanity checking on the server (so that hacking bastards don't go from 0 to a bazillion metres per second in the blink of an eye). You have to be mindful of what you send... position is generally not a good idea (let the server run the simulation and notify the client where it has moved to).


I apologise if I misunderstood how you are currently doing things, but hopefully you get the gist of what I am saying anyway :)
Thanks for the reply - your point makes a lot of sense - there is no need to spam the server with input messages when nothing is really changing at the client.

The data sent from the client to the server is very thinly abstracted from keypresses - I am sending force and torque 'percentages' along with actions like firing weapons, etc. For example, if they press the 'forward thrust' key, then this equates to 100% of their ship's z-axis thrust capability, if they move the mouse right by half the configured maximum deflection, then this equates to 50% of the ship's y-axis yaw thrust capability. The server knows their ship configuration (mass, inertia tensor, max engine output in each direction, etc.) and derives momentum/position and all other state properties via the physics engine (mirrored on the client for prediction purposes).

I am not sure I want the client to be in control of their own velocity, or in fact in control of any real numbers regarding their movement at all! If all they are able to control is thrust percentages: "I want to move forwards at my maximum possible thrust", "I want to yaw to the right at half my maximum possible thrust", then any attempt at knobling the numbers by a hacker would be counter-productive, as the server would just continually snap them back into place. The client is very thin in this regard, the server is very much authoritive on just about everything. I might be shooting myself in the foot and making extra work for myself here... Afterall, nobody is actually going to care enough to hack my little hobby game (much as I'd like to believe it's going to be the next CounterStrike ;)

That said, I believe your point about only sending messages to the server when something changes can be applied to my input method. I had briefly considered doing this, but gave up on the idea, primarily because sending input deltas means that all client input messages must have guaranteed delivery to the server (rather than brute forcing data to the server without acknowledgement). This is not an insurmountable problem, and I think you're right about it being the best approach now that I think it over a little more. My server currently uses delta compression (inspired by the forum FAQ link to an article about Quake3's network structure) when sending out game state updates, so there is no reason I couldn't use a similar system in the other direction to deliver input to the server reliably. So long as they don't constantly wiggle the mouse, there should be some good bandwidth saving :)

Thanks again for your advice.

This topic is closed to new replies.

Advertisement