• Advertisement
Sign in to follow this  

Why send keypresses to a server instead of player position? (re: simple 2D game)

This topic is 3324 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to create a "simple" networked/multiplayer 2D game and so I've been trying to a read a lot about networking principles, player synchronization, etc. However, it seems like most articles (eg. gaffer.org) tell you to send keyboard/mouse inputs to the server, have the server process them, and send the results back to the client. Why? Is this only done for cheat protection? What is the disadvantage to sending actual player position to the server and doing a sanity check? It seems like this way is better because then players will know their position even while moving and won't require interpolation or prediction. Keep in mind that I'm talking about a simple 2D game with different objects moving around, etc.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
gaffer.org


Physics simulation (FPS) is defined via some initial state and a known and rigid set of rules (usually newtonian physics). Some of these rules are obvious, such as body in motion... etc.

Direct consequence of this is, that in absence of input, such (properly implemented) systems can be advanced for arbitrary amount of time, on different machines, and produce same result. Consequence of this is, such simulation is affected only as consequence of input.


RPGs however have different requirements. The rules are no longer simple, they also aren't consistent nor rigid (nor is it desirable for them to be). If all rules were known, client could wait for favorable RNG before looting.

State is also too large to replicate entirely. As such, client is unable to advance state on its own, and different clients advance state differently.

To ensure integrity, input (while it can be used) becomes cumbersome. Since input depends completely on exact client's state, server would need to maintain exact replica of each individual client.

So using something more client-agnostic than actual input is considerable simplification. By using context-independent actions to describe changes to the state, each client can run local simulation independently, yet still maintain consistent state.

Quote:
Is this only done for cheat protection?


Yes and no. Since such simulation likely requires entire state and all rules to be known to each peer, there is less security. It may however be easier to detect misbehaving client. This can be a problem in absence of malicious intent, due to network problems or participants which cannot keep up with others for various reasons.

[Edited by - Antheus on January 15, 2009 8:17:09 AM]

Share this post


Link to post
Share on other sites
Thank you so much for the detailed reply!

Quote:
Original post by Antheus
Physics simulation (FPS) is defined via some initial state and a known and rigid set of rules (usually newtonian physics). Some of these rules are obvious, such as body in motion... etc.

Unless the game has movement-affecting conditions (eg. a certain spell is cast upon a player in an RPG), then won't all games have the same movement rules? (eg. player holds up direction for 1 second which equals 20 tiles or whatever)

Quote:

Direct consequence of this is, that in absence of input, such (properly implemented) systems can be advanced for arbitrary amount of time, on different machines, and produce same result.

Don't physics simulators produce slightly different results on each machine? (thus the need for synchronization via a server) I agree that inputs are the only changing factor but the results will still be different due to float precision errors, etc, etc. Right?

Quote:

RPGs however have different requirements. The rules are no longer simple, they also aren't consistent nor rigid (nor is it desirable for them to be). If all rules were known, client could wait for favorable RNG before looting.

I agree that clients shouldn't be able to determine the results of certain actions, but wouldn't movement of objects be the same (again, unless certain movement-affecting conditions exist such as spells, etc)?

Quote:

State is also too large to replicate entirely. As such, client is unable to advance state on its own, and different clients advance state differently.

Why would entire state need to be replicated? Also, doesn't the server still need to keep track of each clients state to prevent cheating?

Sorry for so many questions! I'm wondering if you comments are based on an RPG having speed-affecting elements where as an FPS doesn't have this (although, some FPS games increase certain players speeds based on items they may have picked up, so really they both seem the same)...

Like I was saying above, I'm mostly interested in a simple 2D game like Mario Brothers or The Legend of Zelda (eg. characters moving around without too much complexity being involved)

Thanks so much for the help!

Share this post


Link to post
Share on other sites
Why is that though? :)

I understand that it's done for cheating, but I'm trying to determine if it's worth all the extra complexity just so I can make sure the clients are cheating. My game is going to be very small and I do with allowing determined hackers to cheat if they want to :)

However, if there are other reasons besides cheat protection than I'm trying to figure that out...

Share this post


Link to post
Share on other sites
Good programming practices.

You gain absolutely nothing from having a client designed with some modicum of security in mind, if not the knowledge and experience of designing it the right way, from the ground up. Allowing a client to make determining factors about anything (whether it is a game client, chat client, bittorrent client, mail client) leads to poor programming practices with regard to client/server structure. Why stop at allowing clients to determine where they are? Why not let them log in with the wrong password? Or say they have 1,000,000 hit points when they have only 1?

Bad:
I am at location 100,100, and I am moving to 101,100. (Client could be hacked to teleport through obstacles.)
Good:
I am requesting to move one tile in the positive X direction. (Server mandates where the client is at all times, and simply tells him whether his move request succeeded or failed.)

Bad:
I am logging in with my friend's account, username JoeSmith password IHatePasswords, so I can mess around with his characters. (Player can get away with blue murder, and all you can do is let him.)
Good:
I am logging in with my friend's account, username JoeSmith, and I hope this is the password. (Failed login attempts equate to temporarily locked accounts, notification via email to the account holder of the attempts, etc.)

Bad:
I am about to take one more point of damage, which will kill me. But hey, I actually have 999,999 left. Flame on! (Client modifies character stats, enabling him to ruin the game for other players.)
Good:
I died. (Server mandates at all times, all states of all clients.)

Don't forget that cheaters don't necessarily only make the game fun for themselves. With a little creativity, they ruin the fun for other players too. If your game isn't fun, then you've failed as a game designer.

Share this post


Link to post
Share on other sites
Quote:
Original post by sofakng
However, if there are other reasons besides cheat protection than I'm trying to figure that out...


World consistency, player a hits the go right button but do to lag/dropped packets/etc the server doesn't see him move right for another two seconds. Where is the player located? Two players shoot at each other at almost the same time now because of transmit time between the two each sees themselves as having shot first who is right? You can't just tell the server what happened the server must decide what happened on its own. It can't tell unless it gets inputs, not after the fact reports.

Share this post


Link to post
Share on other sites
Cheating is a good reason and doesn't justify the use of the word 'just', in my opinion. But in addition, if you're running any sort of physical model with collisions, or magic spells that can slow or speed up characters, or weaponry with recoil, all that simulation has to happen on the server and therefore it has to have the authoritative state. If the client just says 'I moved from (x,y) to (X,Y)', there's no way to know what it should have interacted with in between, even if you 'sanity check' it to make sure the move isn't downright impossible.

Share this post


Link to post
Share on other sites
Thank you so much for all of your help. I hope you folks know how much I appriciate it.

I now understand what you're saying about only sending inputs to the server for player movement. What about other actions such as opening a chest? I now know that I would send a request to open the chest, but would you typically wait for the server response before informing the client or would you let the client pretend it succeeded and "rollback" if neccessary? It seems like any action besides movement should wait for the server to respond before the client displays or does anything at all.

One last question (I hope!):

Let's say a client presses forward and informs the server. Could the client just start moving forward and wait for the server to respond? If you had to wait for the server to respond before displaying it on the client then it would be very ugly looking and would control very poorly.

So if we allow the client to move forward and tells the server, what would the server response be? Would the server just say "OK, I acknowledged your movement"?

Would the server then send out packets every xx milliseconds updating the clients position? The problem with this seems that if the client moved on it's own before the server started his movement, then by the time the client got the new position from the server, the client's position would be ahead of the server (since he started moving before the server started him moving).

Do I need to send velocity with the movement inputs or anything like that? (this seems very complicated) It also seems very complicated to keep track of "rollbacks" if I need to rollback actions, etc.

I'm really sorry for so many questions! I've honestly read many articles on this (and have searched) but you folks have helped me a MUCH more than any article.

Share this post


Link to post
Share on other sites
Sending key states is a lot smaller than sending state.

For example, if I have move 4 directions, run modifier, crouch modifier, and two weapons fire modes, then that's 8 bits. If I have XYZ with 32 bits precision per coordinate, plus velocity with 24 bits per coodinate, plus HP aim with 16 bits per axis, plus two weapons fire cycles with 16 bits of state each, sending that will take 37 bytes. Think of it from the point of view of the server. If you send updates 20 times a second, then you have the choice of sending, to each player, for each player, one byte times the number of players, or 37 bytes times the number of players.

It's common to send the full state every so often (say, the full state of one player, per player, on a round-robin fashion), and use the input command state as network compression for the intervening state sends.

If you have 32 players on a map, then the numbers are:

32 players * 31 full 37-byte states * 20 Hz send rate == 734 kB/sec (just for updates -- there are other things you want to send, too, plus header overhead!)

32 players * 30 key bytes + 1 37-byte state * 20 Hz send rate == 43 kB/sec

32 players * 31 key bytes * 20 Hz send rate == 20 kB/sec

Now, take a look at the upstream of your DSL or cable modem, and figure out which of those you can serve, and which you can't :-)

Share this post


Link to post
Share on other sites
Hmm, I think 734 kB/sec might be a bit much for almost anybody. Isn't that almost 6 megabits/sec? (My DSL is only 3 megabit down, 768 kiloits up) :)

I think I get your point but are you saying that you only send key state updates to the individual players too? I thought you updated the players position each few milliseconds?

Example:

Player tells server he wants to move forward
Server receives packet and tells all clients, player X is moving forward.
All clients start moving player X forward
Every 20 milliseconds, the server sends the exact position of player X and each clients synchronizes to this position. Otherwise, the clients wouldn't know the exact position of players until several... seconds?

It seems like I'm missing something though because it sounds like you're saying to only send keyboard/mouse states and nothing else except for "full-state" every so often?

Share this post


Link to post
Share on other sites
Computers are very deterministic. As long as you give them a given input, and run a fixed algorithm, they will always achieve the same output. Thus, forwarding the player controls is good enough, especially given that you correct the player state every few seconds. There are some small differences that can trip you up over longer games (having to do with FPU states, signals/exceptions, etc), but those can actually also be accounted for if you want. There's really no reason to send data other than the initial state and the player input, ever, unless you want to correct for a dropped packet.

If you read the Forum FAQ it actually explains a system that builds on that technique to do a lot of entities over a modem, called "1,500 archers on a 28,800 modem" (or something like that) -- almost every RTS game does it exactly that way.

Share this post


Link to post
Share on other sites
Quote:
Original post by hplus0603
Computers are very deterministic. As long as you give them a given input, and run a fixed algorithm, they will always achieve the same output.

Really? I thought that physics simulators, even when running with the same timestep, will produce slightly different results over time and this is why we need to synchronize positions. Is this not true?

Quote:
There's really no reason to send data other than the initial state and the player input, ever, unless you want to correct for a dropped packet.

So if you used reliable packets (TCP or reliable UDP) than you wouldn't ever need to synchronize player positions or send updates?

Share this post


Link to post
Share on other sites
That's only if you have a syncrhonized deterministic system on both sides, usually this isn't the case due to some source of random noise in the system ( ie floating update rates dependent upon hardware, forgotten random numbers seeding some calcuations or as hplus mentioned, drift in floating point calcualtions due to hardware differences, non-deterministic threading issues )

These are not insurmountable, people have developed fairly robust synchronized systems, there are even 3rd party solutions which can wrap programs so they can be re-executed determinsiticly ( used for beta testing and such ) using this basic techinqiue of only feeding inputs, all RTS networking are based off these techinqiues, and most MMO proably use these techniques extensivly.

The problem with this technique alone is its very sensitvie to network conditions and isn't responsive enough for all types of games. Its fine for RTS, MMOs, board/puzzle games, etc.. but for action games a combination of methods are used.

What kind of game are you aiming for? 2D action game? 2D turn based puzzle? 2D mmo? rts?

-ddn



Share this post


Link to post
Share on other sites
Quote:
Original post by sofakng
Quote:
Original post by hplus0603
Computers are very deterministic. As long as you give them a given input, and run a fixed algorithm, they will always achieve the same output.

Really? I thought that physics simulators, even when running with the same timestep, will produce slightly different results over time and this is why we need to synchronize positions. Is this not true?

If you want to involve physics you have to use a system that will run deterministically and maybe even one that can roll back to previous states. If you know exactly when everything is happening and are given floating numbers at those timestamps then whatever you do will match the server and won't drift. As long as you use the exact floating point values as the server does.

For instance, say you send key input to the server to move and then wait for the new velocity. Then when you get it you decide to start extrapolating forward the whole simulation. Then when you get the next delta packet of information you notice that there's a timestamp that you passed. So your simulation can't be reversed because subtracting and doing crazy stuff will make the floats drift. So you use the previous known positions at the last packet (which you'd store) then you'd step forward normally and apply the delta timestamps in order until you are back to where you should be and you'd update your old known states and begin extrapolating again.

I've been using a similar system for my limited physics in my current project and it seems to work flawlessly. Either that or just update all the positions and velocities. Both will give you a state to extrapolate from. One just costs less if you want to have hundreds of objects.

Quote:
Original post by sofakng
Quote:
Original post by hplus0603
There's really no reason to send data other than the initial state and the player input, ever, unless you want to correct for a dropped packet.

So if you used reliable packets (TCP or reliable UDP) than you wouldn't ever need to synchronize player positions or send updates?

Technically, however in practice bandwidth isn't wasted that much if you chose to send full state updates every update.

If you do some simulations on paper you'll realize that you only have to send what changes and when it changed to keep things deterministic if you're using reliable packets as hplus said.

Share this post


Link to post
Share on other sites
Quote:
Original post by ddn3
That's only if you have a syncrhonized deterministic system on both sides, usually this isn't the case due to some source of random noise in the system ( ie floating update rates dependent upon hardware, forgotten random numbers seeding some calcuations or as hplus mentioned, drift in floating point calcualtions due to hardware differences, non-deterministic threading issues )

How can any two systems produce the same results if floating point numbers drift due to hardware differences? If hardware differences can create problems than it seems like you could never rely on two systems to produce the same results even given the same input.

Quote:
The problem with this technique alone is its very sensitvie to network conditions and isn't responsive enough for all types of games. Its fine for RTS, MMOs, board/puzzle games, etc.. but for action games a combination of methods are used.

I'm not sure what you mean by this. Why would it sensitive to network conditions and not responsive enough? Are you assuming no interpolation or extrapolation or something like that?

Quote:
What kind of game are you aiming for? 2D action game? 2D turn based puzzle? 2D mmo? rts?

I'm looking to develop a 2D multiplayer Zelda type of game. (eg. players moving around on a 2D map (perhaps in all four directions like Zelda, or maybe even side-scrolling like Mario Brothers). The action will definitely not be fast paced like a FPS.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sirisian
If you want to involve physics you have to use a system that will run deterministically and maybe even one that can roll back to previous states. If you know exactly when everything is happening and are given floating numbers at those timestamps then whatever you do will match the server and won't drift. As long as you use the exact floating point values as the server does.

Are any physics systems really deterministic even on different hardware? I asked the Farseer Physics Engine developer and he said no (http://www.codeplex.com/FarseerPhysics/Thread/View.aspx?ThreadId=44137&ANCHOR#Post146855). Also, I don't think any physics simulators can rollback, can they? At least I don't think Farseer, Newton, or ODE can...

[quoteFor instance, say you send key input to the server to move and then wait for the new velocity. Then when you get it you decide to start extrapolating forward the whole simulation. Then when you get the next delta packet of information you notice that there's a timestamp that you passed. So your simulation can't be reversed because subtracting and doing crazy stuff will make the floats drift. So you use the previous known positions at the last packet (which you'd store) then you'd step forward normally and apply the delta timestamps in order until you are back to where you should be and you'd update your old known states and begin extrapolating again.[/quote]
Do you need to send velocity? If you just send the inputs couldn't the client figure it out? (eg. the character is moving up and has a movement speed of 3 units/millisecond)

Also, are you saying that you extrapolate until new data comes in, but once you receive the new data you go back to previously stored (eg. "validated") positions and step forward using the newly received data? (eg. this way you'd have a last_known_validated_position and a current_extrapolated_position) That seems to make sense. Could you also just stay "behind server time" and interpolate between positions and hopefully have a new "validated" position before you need to actually extrapolate?

[quoteI've been using a similar system for my limited physics in my current project and it seems to work flawlessly. Either that or just update all the positions and velocities. Both will give you a state to extrapolate from. One just costs less if you want to have hundreds of objects.[/quote]
What do you mean by just update all the positions and velocities? Do you mean to send positions/velocities instead of player inputs? (as an alternative [but slower/more bandwidth intensive]?)

[quoteIf you do some simulations on paper you'll realize that you only have to send what changes and when it changed to keep things deterministic if you're using reliable packets as hplus said.[/quote]
I think the deterministic part is throwing me off a little bit. Like I mentioned above, I've read that different computers will produce different results when using floating point numbers. The Farseer physics engine developers have confirmed this so it seems like I couldn't never count of two machines producing the same result, ever.

I'm really sorry for all of my questions and confusion. I hope you don't take my responses as me being critical or anything -- I'm just trying to learn and understand.

Thank you SO MUCH for the help.

Share this post


Link to post
Share on other sites
I was under the impression that hardware implementing IEEE 754 will always give the same result regardless of the hardware. So no matter how you add 0.5f + 0.5f it will always give you the same box of bits.

Share this post


Link to post
Share on other sites
On the x86 architecture, when using x87 floating point, you CAN make the floating point be deterministic across all CPUs (AMD and Intel). You'll have to set a common operand size (typically 64 bits, not 80, because AMD doesn't do 80 bits), a common rounding mode, and make sure that no library changes these values under the hood on you. Additionally, you have to run the same code on all CPUs -- using GCC for some, but MSVC for others will not always generate reproducible results. Finally, you have to make sure that you feed all inputs to all simulators in the same order. What this means, in reality, is that you have to delay the simulation until you have received inputs from all other participants, which adds a latency of one full round-trip to all commands. RTS games typically hide this through an acknowledgement animation ("Yes, sir!"). FPS games can't do that. Also, you need to have the same entities on all machines, and solve constraints/collisions/joints in the same order, which ends up being a problem for physics engines like ODE, PhysX or Havok. Things that depend on random numbers must use a deterministic random number generator, etc.

However, all is not lost. Most of the time, even if you don't take all of those precautions, the system will be so close that nobody will notice any difference. If you baseline the state of each entity every so often (once a second, or once every 5 seconds), you'll be good enough that nobody cares about the very small difference, especially if you baseline your own client entity more often (to avoid problems with "falling off a cliff" vs "not falling off a cliff" growing out of control). Note that the latency from other players, to the server, to you, is a much bigger source of error in general than any small deviation over a few seconds of simulation.

Share this post


Link to post
Share on other sites
Quote:
Original post by hplus0603
On the x86 architecture, when using x87 floating point, you CAN make the floating point be deterministic across all CPUs (AMD and Intel). You'll have to set a common operand size (typically 64 bits, not 80, because AMD doesn't do 80 bits), a common rounding mode, and make sure that no library changes these values under the hood on you...

This sounds almost impossible to depend upon if you're using a lot of third party libraries (eg. for physics, networking, rendering, etc). So therefore I guess you can't really depend upon 100% determinism (but like you are saying below, I guess it doesn't matter because it's close enough).

Quote:
If you baseline the state of each entity every so often (once a second, or once every 5 seconds), you'll be good enough that nobody cares about the very small difference, especially if you baseline your own client entity more often (to avoid problems with "falling off a cliff" vs "not falling off a cliff" growing out of control).

You mean that if you send the full-state of a particular entity once or few seconds?

Quote:
Note that the latency from other players, to the server, to you, is a much bigger source of error in general than any small deviation over a few seconds of simulation.

What does this mean? Do you still need to wait for the server to receive all inputs before processing? I'm not sure how the latency between players would have any effect on anything other than requiring more interpolation or extrapolation (and maybe more "snapping", etc).

Share this post


Link to post
Share on other sites
"baselining" == sending the full state every so often

Yes, extrapolation is a much bigger source of (visible) error on the client than potential floating point differences. The latency matters, because the higher the latency, the more you extrapolate.

Share this post


Link to post
Share on other sites
Ok, maybe I'm just overcomplicating this.

It just gets a little confusing when I hear different things... (eg. in my other thread in the beginners forum, http://www.gamedev.net/community/forums/topic.asp?topic_id=521132, there is a guy claiming that the PhysX system doesn't always produce the same results even given the same inputs due to floating point rounding errors)

Anyways, it sounds like it comes down to this (like you said before):

Send only inputs between client and server but send full-entity updates every few seconds.

Right?

What happens though if a client takes a physics step before having all of the data from the server? (eg. his connection is laggy) I guess his local physics simulation will become out of sync but get synced up via the next update packets?

Share this post


Link to post
Share on other sites
Quote:
there is a guy claiming that the PhysX system doesn't always produce the same results


That may very well be true, for many different reasons. On different machines, PhysX may use the GPU, or the CPU, or a PhysX card. Also, they may not be doing all the work necessary to ensure repeatable outcomes, even on the platforms where they could.

Regarding stepping before you have all the data, that in general shouldn't happen. If you require client data before stepping, then you should delay stepping until you have data. If there's lag, then delay stepping (which may mean you may need to increase the amount of lag compensation if it happens often).

In an RTS (that is fully input-synchronous), there is a fixed amount of allowed lag, and if some client is slower than that, the gameplay for everyone will slow down, or even pause.

Share this post


Link to post
Share on other sites
How would you know if there is data that you are waiting for before taking a step?

For example, suppose the clients and server are stepping along at fixed intervals. Let's say one player sends new input to the server but it gets lagged before getting to player #3. Player #1 and player #2 receive the data in time (before the next physics update) but player #3 does not. How would player #3 know that it's waiting for new data before taking it's next step?

Share this post


Link to post
Share on other sites
This is how I'm doing movement-related things in my 2d fps/action game.

The physics ticks happen at a fixed rate (every 50 ms).
The client always sends inputs only to the servers (commands).
The server receives inputs, processes them, and sends updates back to the client.
Meanwhile, client does client-side (or local) prediction, and doesn't wait to get the server's response before starting moving.
However, when the client does receive an update, it checks if the associated prediction it made was 'close enough'. If it was, nothing happens.
If it wasn't close enough, it uses the server's authoritative update and replays any following outstanding (predicted) inputs. This is rewind and replay as described in gaffer's networking articles.

The server's updates also include the latest states (positions at a given time) for other players as well.
The client then uses interpolation/extrapolation to render other players.
It actually renders the other players 100ms in the past (that way it can interpolate instead having to guess into the future, if the Round-Trip-Time or latency is <100 ms).
The server takes that into account and 'rewinds' players 100ms back when doing weapon hit/miss calculations, so that you don't have to lead your targets when shooting.

That is pretty much how Half-Life does it, and it works pretty well IMO.

Keep in mind that client-side prediction is absolutely key here to make this system work well. You want the player's own movements to feel very fluid and responsive. Assuming nothing weird happens (like two players collide), the client's prediction is always right, so you never see any 'snapping' happening yet everything feels very responsive.

So to answer one of OP's questions about a treasure chest... Yeah, let the client open the chest locally (predict), but the server should be authoritative over what happens. You don't want to let the client to say 'oh, btw, I've opened this chest and got 999999 bucks', but rather let him predict what's inside, but the server will still verify it.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement