cant find a good movement scheme (2D action MORPG)
hi, im kind of stuck on how to handle the movement in my game. the game is a 2d tile based action MORPG. there are bullets and other projectles flying around in real time so i have to keep things in as good as sync as possible but still in the happy middle of not using a crap load of bandwith [grin]. anyway, so far my movement has worked like this: the game uses the mouse to move, and when you click, your character walks in a straight line to where he clicked. when a player clicks, he sends to the server "this is where i clicked, this is what time it is". the client starts moving the moment he clicks, and does not wait for an ack or anything. the server gets this message and then starts the movement on his machine, using the timestamp to "catch up" to where that client really is. he also uses this timestep to first "back-track" the player to put him in the position he was in when he actually clicked. he then sends to the clients on the same map "here is the guys position, where he clicked, and the time". they do the same thing the server does, sets position and "catches up". anyway, this works fine as long as everyone has a decent ping (e.g. somewhere under ~300). anything higher and it really sucks for the person with high ping, since everyone is pretty much just bouncing around him.. and to other people, you are bouncing around yourself.. this is because the response time for other players is based on your ping AND the other persons ping. this could more then double your latency. i also just think its stupid to have other players connections have an effect on how you see their movement.. i feel your connection should be the only thing that effects how you perceive the world, not your connection + the other persons connection. well i tried a few things. first, i tried having the client send the same message, except instead of moving immediately, he waits for an ack from the server before starting to move. so basically its more like "this is the time, this is where i clicked, may i please move"? the server gets this, starts the movement on _his_ simulation instantly, and then sends to the client "ok, you can start moving. this is what time it is". the client gets his movement ack, and then uses the timestamp to "catch up" to the server. ok, so now the way other people appear to move is based on *your* connection only, not anybody else's connection. however, this too has a major downside. now, you have to catch up to your own movement. if you have a high ping, this just plain sucks because each time you go to move theres a delay and then you have to "jump" to catch up. with a 300 ping, this jump gets really annoying. one thing i tried to do was start moving at half my speed as soon as i sent the request to move. however with a high ping, this meant that i was constantly moving at half my speed. if my ping/2 was higher the "you must wait this long before clicking to move again" variable, then i would appear to be constantly moving at half my speed, since by the time i get the ack, im clicking to move again (unless i dont hold the mouse to move, and just rapidly click the mouse when i want to move.. but this gets annoying). i also tried one other method. instead of everyone playing "catch up" with the server like the last method, what i do is this: player clicks to move, and sends "here is where i clicked, and this is the time, may i please move?". the server then gets this message, but does not immediately start moving yet. instead, he waits the players average ping / 2 and THEN executes the movement. when he gets this message, he sends back to the client "ok, you can start moving at T + P2", where T is the current time and P2 is that clients average ping / 2. the client gets this ack and then says "did T+P2 already pass? if so, then "jump" to catch up. else, wait untill T+P2 comes and then start moving". the server also relays this message to the other clients, which do the same exact thing. so basically, instead of everyone playing catch up with the server, everyone executes the movement at the same time, or if that time has passed, they catch up. what this *should* result in is very little to no "jumping", but with a decreased reponse time to movement, e.g. a longer delay from the time you click to the time you start moving. i thought this would be the best method, however i couldnt get it to work. however im 99% sure that it was just a bug in my code, however im still not even sure if this is a good way to do it or not. anyway, sorry for the long rambling. im just really lost on what to do about this and could really use some help. also, any suggestions on how to make this more secure is welcomed. as of now, the player could spoof a timestamp to make himself "back track" more on the server. this would make him able to jump forwards an backwords, however the server will do checks to make sure the timestamp is decently accurate, so IMO this is an acceptable security hole. im still open for suggestions though. thanks ALOT for any help.
FTA, my 2D futuristic action MMORPG
It feels like we've gone around this already once or twice :-)
The one resource that I don't remember whether it was mentioned or not was the OpenTNL whitepaper that talks about movement extrapolation, where you basically display an entity on a position that lerps between two extrapolated positions.
However, it sounds to me like you want everybody to see the same entities in the same places at the same time, AND you want movement to happen immediately. You can't have both. Choose which one you want to trade off. If you don't want immediate movement, but everyone in the same place at the same time, then do this:
This is very simple, and it does not need any timing to work right. Basically, the entire world is referenced on the server, and you see the world on the server delayed by your one-way transmission time. Commands have a latency of your RTT.
The "everysooften" thing is to make sure that entities get back in sync if you should lose a packet -- fully updating 10 entities per second for a 100-entity world should be quite sufficient for these needs.
If you want immediate movement, then you have to do what you're doing now, which includes accepting the snapping for people on laggy connections.
Other trade-offs include not displaying everyone in the same position on all clients, so that the client code becomes simply "walk towards the desired point", and you can update the desired point of the player entity when the player clicks. Entity-Entity collisions become harder in this mode.
Last, you can display different entities in different time frames, so that you display the player RTT/2 ahead of the master clock, and other entities RTT/2 behind the master clock. Everybody will be in the right places/trajectories, but not at the same time except on the server. However, if you do this, and start playing clever games with specific times for specific entities, you are straying very close to technology that the company I work for patented in 2000 (just for full disclosure).
So, pick your poison.
The one resource that I don't remember whether it was mentioned or not was the OpenTNL whitepaper that talks about movement extrapolation, where you basically display an entity on a position that lerps between two extrapolated positions.
However, it sounds to me like you want everybody to see the same entities in the same places at the same time, AND you want movement to happen immediately. You can't have both. Choose which one you want to trade off. If you don't want immediate movement, but everyone in the same place at the same time, then do this:
client::playerclick: send new position to serverclient::packetreceived: update position/velocity of entityserver::receivedplayerclick: echo playerclick to everyoneserver::everysooften: send full pos/velocity/desired pos for entities
This is very simple, and it does not need any timing to work right. Basically, the entire world is referenced on the server, and you see the world on the server delayed by your one-way transmission time. Commands have a latency of your RTT.
The "everysooften" thing is to make sure that entities get back in sync if you should lose a packet -- fully updating 10 entities per second for a 100-entity world should be quite sufficient for these needs.
If you want immediate movement, then you have to do what you're doing now, which includes accepting the snapping for people on laggy connections.
Other trade-offs include not displaying everyone in the same position on all clients, so that the client code becomes simply "walk towards the desired point", and you can update the desired point of the player entity when the player clicks. Entity-Entity collisions become harder in this mode.
Last, you can display different entities in different time frames, so that you display the player RTT/2 ahead of the master clock, and other entities RTT/2 behind the master clock. Everybody will be in the right places/trajectories, but not at the same time except on the server. However, if you do this, and start playing clever games with specific times for specific entities, you are straying very close to technology that the company I work for patented in 2000 (just for full disclosure).
So, pick your poison.
enum Bool { True, False, FileNotFound };
hi hplus,
thanks for the reply. i should have been more clear, yes, i am perfectly happy with there being a delay in movement. i much prefer delayed movement over snappy / fast responding movement. im trying to digest everything your saying but am having a hard time on your first example. you said:
isnt this basically what im doing now? actually, im just really confused.. does the client::packetreceived also act as an "ack" to the players own movement? if not, when to they start moving? also, are things timestamped at all? if so, isnt this basically what im doing now? and if not, doesnt that mean things will be out of sync and a bullet might hit someone on one simulation and not on another? (i know this will alwas happen sometimes, but i mean it will happen more often..).
thanks for the reply. i should have been more clear, yes, i am perfectly happy with there being a delay in movement. i much prefer delayed movement over snappy / fast responding movement. im trying to digest everything your saying but am having a hard time on your first example. you said:
Quote:
client::playerclick: send new position to serverclient::packetreceived: update position/velocity of entityserver::receivedplayerclick: echo playerclick to everyoneserver::everysooften: send full pos/velocity/desired pos for entities
isnt this basically what im doing now? actually, im just really confused.. does the client::packetreceived also act as an "ack" to the players own movement? if not, when to they start moving? also, are things timestamped at all? if so, isnt this basically what im doing now? and if not, doesnt that mean things will be out of sync and a bullet might hit someone on one simulation and not on another? (i know this will alwas happen sometimes, but i mean it will happen more often..).
FTA, my 2D futuristic action MMORPG
Using this simple method: Nothing is timestamped. The player starts moving when receiving the movement packet back. Bullets are fired the same way -- with delay. Everything is in the same place at the same time on each client -- but at different wallclock times depending on server ping times.
Suppose I am at position 0,0 at time 0 ms, and you are at position 10,0. I click on 0,3 and you click on 7,0. My one-way delay is 200, and your one-way delay is 50 (and you step 20 times a second, so 50 == one step). Movement is 1 unit/second.
From the server, it'll look like this:
As you see, everything on my machine happens 200 ms after it happens on the server, and everything on your machine happens 50 ms after it happens on the server, so each frame displayed is a correct replica of the server -- just delayed by your one-way transmission delay.
As you also see, you have a significant reaction time advantage to me :-)
Suppose I am at position 0,0 at time 0 ms, and you are at position 10,0. I click on 0,3 and you click on 7,0. My one-way delay is 200, and your one-way delay is 50 (and you step 20 times a second, so 50 == one step). Movement is 1 unit/second.
From the server, it'll look like this:
time mypos yourpos actions0 0,0 10,0 I click 0,3; you click 7,050 0,0 10,0 Your click arrives, and you start moving on server100 0,0 9.95,0 Your click arrives back at you, and you start moving on your client200 0,0 9.85,0 My click arrives on the server and I start moving on the server250 0,0.05 9.8,0 My click arrives on your machine and I start moving on your machine Your click arrives on my machine, and you start moving on my machine400 0,0.2 9.65,0 My click arrives on my machine, and I start moving on your machine1000 0,0.8 9.05,0 yourpos@you = 9.1,0 yourpos@me = 9.25,02000 0,1.8 8.05,0 mypos@you = 0,1.75 mypos@me = 0,1.63000 0,2.8 7.05,03050 0,2.85 7,0 You stop moving on the server...
As you see, everything on my machine happens 200 ms after it happens on the server, and everything on your machine happens 50 ms after it happens on the server, so each frame displayed is a correct replica of the server -- just delayed by your one-way transmission delay.
As you also see, you have a significant reaction time advantage to me :-)
enum Bool { True, False, FileNotFound };
ok, i think i get. basically, everyone runs the exact same simulation, except you see it a different time depending on your ping. makes sence...
with this scheme, couldnt i switch to using the keyboard for movement fairly easily? i would rather have movement based on keyboard, since mouse movement in an action game is kind of.. "bleh". i was only using the mouse since it was easier to do.
thanks again.
with this scheme, couldnt i switch to using the keyboard for movement fairly easily? i would rather have movement based on keyboard, since mouse movement in an action game is kind of.. "bleh". i was only using the mouse since it was easier to do.
thanks again.
FTA, my 2D futuristic action MMORPG
You can do whatever you want :-)
There is a problem, though, in that if jitter is significant (and it will be), you will get out of sync. You can fix this by sending a full checkpoint every so often (already in the suggested code structure).
Also, you probably still want to detect actual bullet collisions on the server, and send the results back to the client, rather than having each client detect them on their own, to get the most consistent experience. When you're OK with delaying user response by RTT, that's pretty easy to do.
There is a problem, though, in that if jitter is significant (and it will be), you will get out of sync. You can fix this by sending a full checkpoint every so often (already in the suggested code structure).
Also, you probably still want to detect actual bullet collisions on the server, and send the results back to the client, rather than having each client detect them on their own, to get the most consistent experience. When you're OK with delaying user response by RTT, that's pretty easy to do.
enum Bool { True, False, FileNotFound };
Quote:
When you're OK with delaying user response by RTT, that's pretty easy to do.
Additionally, there are some things you can do to minimize or eliminate the perception of a delay on the initiating client:
- Play a sound that acknowledges the command. This lets the user know that the command was correctly issued, and it distracts from the visible delay. (common in RTS)
- Use graduated stop/start for movements such that speed builds up from 0-max over some time frame, like half a second, or even a full second. Vice-versa for stopping. This allows the client to begin the movement before confirmation by the server, but still requires minimal 'pop' or correction if the server replies with an alternate path or disallows the action.
hey guys,
thanks a lot for your help.. i think i finally found a movement scheme that i like, and its more simple then what i've been trying to do so far, which is awesome.
btw, doesnt this also pretty much take care of collision detection as well? since everyone is basically just "a window into the server", doesnt this mean literally everything will be in sync? if so, that is very nice [grin]. im still a little worried about having to send that timestamp when the player clicks though, or is that not nessasary? i mean, when the player clicks, while he's already moving, the server gets this click latency milliseconds later, then starts moving him on his simulation.. however, while the client is waiting for that "ack", he is still moving in his old direction.. wont there be some "snap" here then?
actually, i dont even think having the client timestamp his click is going to solve this problem in this situation.. unless maybe the client sends a timestamp, and then stops moving.. the server back-tracks the client based on the timestamp, and starts moving in the new direction.. the client gets the ack and starts moving on his simulation.. this would get annoying though since you would have to stop moving to switch directions...
thanks again.
thanks a lot for your help.. i think i finally found a movement scheme that i like, and its more simple then what i've been trying to do so far, which is awesome.
btw, doesnt this also pretty much take care of collision detection as well? since everyone is basically just "a window into the server", doesnt this mean literally everything will be in sync? if so, that is very nice [grin]. im still a little worried about having to send that timestamp when the player clicks though, or is that not nessasary? i mean, when the player clicks, while he's already moving, the server gets this click latency milliseconds later, then starts moving him on his simulation.. however, while the client is waiting for that "ack", he is still moving in his old direction.. wont there be some "snap" here then?
actually, i dont even think having the client timestamp his click is going to solve this problem in this situation.. unless maybe the client sends a timestamp, and then stops moving.. the server back-tracks the client based on the timestamp, and starts moving in the new direction.. the client gets the ack and starts moving on his simulation.. this would get annoying though since you would have to stop moving to switch directions...
thanks again.
FTA, my 2D futuristic action MMORPG
Quote:
literally everything will be in sync?
Things that can still bring you out of sync:
- dropped packets
- late packets
- differences in floating point environment between CPUs (80-bit vs 64-bit registers, rounding mode, etc)
- on-client machine jitter
Quote:
the server back-tracks the client based on the timestamp
No. The server assumes that the player transmission delay is the difference between his timestamp and the server timestamp. Nothing is back-tracked on the server. If the timestamp arrives with more delay than the server thinks the player should have, the server can ignore the command.
In the simple scheme I was describing initially, the player wouldn't actually stop moving (or start moving in another direction) until he/she got the command from the server, so there's no snapping, because the command from the server to move the player in another direction is as delayed as the rest of the world. The draw-back is that there's one RTT of lag between clicking and seeing the change.
If you want to immediately affect the player, then entities won't all be in sync (because the player is "prescient" WRT his own state) and the player would have to snap when changing direction on all other machines. Also, you'd have to know when to ignore the server updates about the player -- this gets more complicated.
enum Bool { True, False, FileNotFound };
It really seems like you ought to be able to do a client-side prediction scheme so that clients observe instant movement on click, and then use interpolation to fix any "popping" issues...
For the client-side prediction, you would send "tick" packets from the client to the server -- simply simulation time values for each frame with click locations interspersed -- something like:
Packet client->server:
Advanced 10 ms
Advanced 15 ms
-- Click at world pos 100, 500
Advance 10 ms
Advance 10 ms
Advance 20 ms
Advance 20 ms
Client object checksum: (some hash of the client's player position/velocity data)
When the server receives the packet, it would advance the movement simulation for just that client by the timestamp values in the packet (checking of course to see that the client is not advancing faster than real time). On the next server packet to the client, it can checksum that client object's position state and compare it with the client's own checksum. If they agree, then the client is in perfect "prediction" - otherwise the server can send the correct client position as well as a timer sequence value to the client. The client can then apply any subsequent time advancements that the server was not aware of at that point.
You'd then use interpolation to make the client object move smoothly from the client's view of it to the correct server adjusted position.
For an example of all of this in action, download the TNL source (http://www.opentnl.org/) and check out the Zap example program. The MoveObject class implements interpolation and networked physics, the ControlObjectConnection implements the move oredering from client to server, checksumming and so on.
Bottom line: you don't have to sacrifice instant reaction time for a consistent and smooth simulation.
For the client-side prediction, you would send "tick" packets from the client to the server -- simply simulation time values for each frame with click locations interspersed -- something like:
Packet client->server:
Advanced 10 ms
Advanced 15 ms
-- Click at world pos 100, 500
Advance 10 ms
Advance 10 ms
Advance 20 ms
Advance 20 ms
Client object checksum: (some hash of the client's player position/velocity data)
When the server receives the packet, it would advance the movement simulation for just that client by the timestamp values in the packet (checking of course to see that the client is not advancing faster than real time). On the next server packet to the client, it can checksum that client object's position state and compare it with the client's own checksum. If they agree, then the client is in perfect "prediction" - otherwise the server can send the correct client position as well as a timer sequence value to the client. The client can then apply any subsequent time advancements that the server was not aware of at that point.
You'd then use interpolation to make the client object move smoothly from the client's view of it to the correct server adjusted position.
For an example of all of this in action, download the TNL source (http://www.opentnl.org/) and check out the Zap example program. The MoveObject class implements interpolation and networked physics, the ControlObjectConnection implements the move oredering from client to server, checksumming and so on.
Bottom line: you don't have to sacrifice instant reaction time for a consistent and smooth simulation.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement