Jump to content

  • Log In with Google      Sign In   
  • Create Account


Player Movement - Input + Network Messages


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
17 replies to this topic

#1 Chumble   Members   -  Reputation: 141

Like
0Likes
Like

Posted 20 August 2013 - 01:18 PM

Preface: I am making a 2D networked game where each player controls a single character. Primary goal is getting more comfortable with Java, but I'm having fun making it too.

 

 

This topic has two parts. The first deals strictly with Java and implementing the key listeners to move a player.

 

The second I would like to pretty much detach from Java completely and deal strictly with pseudo-code or simply documentation.

 

 

 

1) Player Input in Java

 

Currently, my client GUI has a JPanel with addKeyListener(new ClientInput(this));

My ClientInput extends KeyAdapter and has functions for keyPressed, keyReleased, and keyTyped.

 

First problem is with continuous input. Just like when typing on here, if you hold a letter down, it is subject to your input delay. Press and hold "f" and you see it types one "f", you get a delay, then a steady stream of "f"s. At first I thought it was because I was using keyPressed instead of keyTyped, but it seems Typed does the same thing. How can I deal with this issue? I'm assuming I'll need to use something other than KeyAdapter.

 

 

 

 

2) Networked Movement Architecture

 

Preface note: I am using a TCP connection.

 

I have started everything with the most basic and straightforward implementation. As of now, this is how my client/server function:

 

a) Client presses "up" - sends "move up" message to server

b) Server moves the player up, sends "move up" message to ALL clients

c) All clients move the player up

 

As I said, this is the most "Brute force" method I could come up with. That was the idea - start simple, optimize later (as I've been told on several occasions). Well, I had 2 friends connect and we all started running around .. took about 5 seconds before all the clients crashed. It seems the server sent a null network message to everyone for some reason and everyone crashed. I am certain this is related to the high volume of messages as when we all re-connected and moved slowly, it worked. But moving steadily faster over time.. null message again. The server stays up and running, which was weird.

 

Firstly, does it make sense that 3 players connected to a central server, sending about 30 messages per second and the server with a thread for each client responding with 90 messages per second would cause problems? I know it seems like a lot but I kinda would have thought it could handle much more.

 

Secondly, I think it's time to look into a couple optimizations. I think if I can figure part 1 out, I can at least slow the player input down a little bit and lessen the number of messages sent. After reading through some other posts, I've gathered that this is a popular architecture:

 

a) Client presses "up"

b) Client performs client-side collision check. If it fails, end here. Else..

c) Client moves up (1). Sends "Moving up" message to server

d) Server gets "Moving up" message. Sets a "moving" flag to indicate this player is moving up.

e) ??? Update Thread ??? on server - every X ms, check for moving flags, update positions, broadcast updated coordinates to all clients

f) Clients get message from update thread. Update coordinates to what is broadcast.

g) Client releases "up"

h) Client stops locally, sends "Stopped" message to server

i) Server gets "Stopped" message. Clears movement flag.

 

This would involve adding a new thread to my server, the Update thread from step "e". I guess all it would do is store the "old" value of player positions and check the movement flags for each player (every X ms). If it is set, update the position of the player and send a message to all clients letting them know the player had moved.

 

(1)* I realize in this step ©, I would need to implement some prediction. Depending on the current latency, this may cause the player to stutter along as the server constantly corrects the player's position. I may end up needing to force a delay in here to make sure the client doesn't update the position too quickly.

 

 

This adds three levels of optimization. The first is the client doesn't even bother sending a move message if client-side collision fails. I realize there is a chance for error here - if the client has bad information, the client could fail collision even if there's really nothing there.

 

The second is limiting the number of messages sent to the server. The client will only send a message to indicate they've started moving (in a direction) or stopped moving (in a direction). The client will probably be moving a lot and updating their direction constantly, but probably not 30 times a second, thus limiting the number of messages sent to the server.

 

The third is capping the number of messages sent to the client. The update thread will only broadcast a message every X ms instead of on every single client move message. While this might cause a slight delay, I don't think it will affect my game too badly.

 

 

Anyone have any thoughts on this? Again, please keep anything pertaining to point 2 as pseudo-code or just steps. Not only does it make it easier for me to read, but it allows me to figure out the syntax myself (and allows non-Java readers to make use of it too). If there's nothing wrong with this.. great! Hopefully this can serve as a resource for others with the same issue. If there is though, please don't hesitate to point it out.

 

Thanks,

Jeremy



Sponsor:

#2 morbik   Members   -  Reputation: 363

Like
1Likes
Like

Posted 20 August 2013 - 02:42 PM

Just going to take a crack at answering #1...

 

The way I got around this before is by keeping track of a boolean that is set to true on keyPressed, and false on keyReleased.  In your "keyPressed" event handler, just set the variable to true, and the keyReleased, set it to false.

 

Then, in your game's update loop, you simply check whether that key is down and if it is take the appropriate action.

 

When I implemented it, if I recall correctly, I had a 256 element boolean array, and used the Keycode to directly insert into the array.



#3 NightCreature83   Crossbones+   -  Reputation: 2672

Like
0Likes
Like

Posted 20 August 2013 - 03:49 PM

For network data you do not send all the input message to all clients you send position updates and interpolate between the previous and current one to make a prediction for future movement. TCP connections have a high overhead as all messages need to arrive and processed in order, miss one and the server has to resend it, UDP is a better solution. In the case of UDP if you get a message that has a older id than the latest you receive you ignore it instead.

 

Your second outline is more along the lines of how XBL and PSN games work.


Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, Mad Max

#4 Chumble   Members   -  Reputation: 141

Like
1Likes
Like

Posted 20 August 2013 - 04:02 PM

For network data you do not send all the input message to all clients you send position updates and interpolate between the previous and current one to make a prediction for future movement. TCP connections have a high overhead as all messages need to arrive and processed in order, miss one and the server has to resend it, UDP is a better solution. In the case of UDP if you get a message that has a older id than the latest you receive you ignore it instead.

 

Your second outline is more along the lines of how XBL and PSN games work.

Sorry, I guess I wasn't clear. I am not sending input messages to the clients, I am sending updated coordinates.

 

Yeah, I know UDP is generally reccomended .. I went through lots and lots of "TCP vs UDP" threads before sticking with TCP...Basically because TCP was easier. I'm starting with easy stuff, getting comfortable with it, then once I understand its limitations, I'm moving on to more complicated optimizations.

 

 

 

Just going to take a crack at answering #1...

 

The way I got around this before is by keeping track of a boolean that is set to true on keyPressed, and false on keyReleased.  In your "keyPressed" event handler, just set the variable to true, and the keyReleased, set it to false.

 

Then, in your game's update loop, you simply check whether that key is down and if it is take the appropriate action.

 

When I implemented it, if I recall correctly, I had a 256 element boolean array, and used the Keycode to directly insert into the array.

 

 

Hmm.. yeah, that could work, I'll give it a shot. Thanks!


Edited by Chumble, 20 August 2013 - 04:03 PM.


#5 Glass_Knife   Moderators   -  Reputation: 3459

Like
0Likes
Like

Posted 20 August 2013 - 04:09 PM

For the input part, check this out:

http://www.gamedev.net/page/resources/_/technical/general-programming/java-games-keyboard-and-mouse-r2439


I think, therefore I am. I think? - "George Carlin"
Indie Game Programming

#6 Satharis   Members   -  Reputation: 949

Like
0Likes
Like

Posted 20 August 2013 - 04:20 PM

Hmm.. yeah, that could work, I'll give it a shot. Thanks!

I can't speak with JPanel experience but language agnostic generality is that the OS sends keydown events with that delay.

Depending on what you're doing that can either be good or bad, for something like a GUI you'd want to forward the keypress events to your gui and basically treat it normally. For something like character movement you usually want to create a keystate class and have a class repesenting each key and it's state, then update that based on OS events.

I.e. I press letter w down then set the keydown bool for w to true, then I keep it that way until a key up event is received from the listener, then you reverse it. Using that methodology you can just poll your keystate class to see if a key is still down at the moment you check it.

Essentially a long winded explanation of what neonic said.

Edited by Satharis, 20 August 2013 - 04:20 PM.


#7 runnerjm87   Members   -  Reputation: 177

Like
1Likes
Like

Posted 20 August 2013 - 04:33 PM

On the networking issue, I agree with NightCreature that UDP is a better protocol, but that has no real bearing on finding a "best solution" solution to your problem. It might throw up some roadblocks along the way, but that's all.

 

As far as how the client-server model should work, the most efficient way that I've seen to handle and dispatch client events to a server is to track each player object's states, depending on what type of game you're making this might only be a movement vector, and transmit those to the server only when they change. So, to break this down a little further:

 

(Assume only one player is actually playing and everyone else is watching, just for ease of typing this out)

 

*Player presses the left arrow key, movement vector is now (-5, 0).

*Vector (-5,0) is pushed to the server, stored player vector is updated.

*Player position is updated and transmitted to all connected clients.

*Player continues press the left arrow key, movement vector remains (-5,0)

*No data is transmitted to server

*Server uses stored player vector to update position and transmits to all connected clients.

*Player releases left arrow key, movement vector is now (0,0)

... and so on.

 

Every now and then, you'll also need to do a full state check for each connected client, ensuring that the server's calculated position for that player is correct, since any dropped packets could result in a miscalculation.

 

And as for your first question, neonic's solution is absolutely the way to go. My input manager classes always have a private boolean array with 256 indexes. It uses minimal memory and means that you don't need any custom handlers for each key.



#8 Chumble   Members   -  Reputation: 141

Like
0Likes
Like

Posted 21 August 2013 - 07:06 AM

Thanks everyone for the replies!

 

@Satharis, I accidentally pressed the down arrow on your comment instead of the up arrow and it doesn't seem to let me change it :(

 


*Player presses the left arrow key, movement vector is now (-5, 0).
*Vector (-5,0) is pushed to the server, stored player vector is updated.

 

Maybe I'm over-thinking it but I think I'd rather just say "Move + <Direction>" rather than pass in an actual value. This way I think it helps prevent cheating maybe?

 


*Player continues press the left arrow key, movement vector remains (-5,0)
*No data is transmitted to server
*Server uses stored player vector to update position and transmits to all connected clients.

 

If I do a check server side and confirm that the player did not change direction/speed, do I even need to send the updated position to the clients? If the clients all know a player is moving, I can use some client-side prediction to show their position.. I guess that could get out of sync pretty quickly. I wonder how often I would need to update the clients with the position?



#9 runnerjm87   Members   -  Reputation: 177

Like
0Likes
Like

Posted 21 August 2013 - 08:59 AM


Maybe I'm over-thinking it but I think I'd rather just say "Move + " rather than pass in an actual value. This way I think it helps prevent cheating maybe?

 

This is a good idea. You should still pass data to the server only when there's a delta, but you're right, this should make it harder to cheat.

 


If I do a check server side and confirm that the player did not change direction/speed, do I even need to send the updated position to the clients? If the clients all know a player is moving, I can use some client-side prediction to show their position.. I guess that could get out of sync pretty quickly. I wonder how often I would need to update the clients with the position?

 

You'll want to do the server side check on a very limited basis. The example that I gave was probably a little too limited, but when there are multiple players at once, each client will send any input changes to the server while the server is busy doing all of the physics calculations and pushing updated positional "snapshots" of the scene at set intervals of a few milliseconds. The snapshots should contain all information related to position for each non-static object so that the clients are synced to the server's version of the game. The server side check is to ensure that the server's version is accurate and doesn't need to be done nearly as often, once every one to two seconds should keep any interpolation error correction from being very noticeable.



#10 BeerNutts   Crossbones+   -  Reputation: 2564

Like
0Likes
Like

Posted 21 August 2013 - 09:48 AM


Maybe I'm over-thinking it but I think I'd rather just say "Move + " rather than pass in an actual value. This way I think it helps prevent cheating maybe?

 

Is your player, and all other animate objects in the game only going to move at one speed?  Will you not have a speed buff, or a slow debuff for the players?  Keep it simple, and use the actual velocities the object is moving at (bullets match this too).

 

Yes, your clients should also interpolate the other players positions every frame based on the last known information (position and velocity), but your server should still send a full game state every "so often" (how often can be determine based on how frenetic your game will be, how many players there are, how much traffic there already will be, etc.). 

 

This also means you should have time-stamped messages, so clients know, at "this point in time" player X was at Y location, moving at this Z velocity, so you can make sure the player is at the correct state at your clients time.

 

Good luck, and have fun!


Edited by BeerNutts, 21 August 2013 - 09:50 AM.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#11 Chumble   Members   -  Reputation: 141

Like
0Likes
Like

Posted 21 August 2013 - 09:59 AM


Is your player, and all other animate objects in the game only going to move at one speed?  Will you not have a speed buff, or a slow debuff for the players?

 

No, but I figured I'd just have the server keep track of that. If the player gets some kind of speed buff, the server will know.. it will adjust the speed server-side so when the player requests to move, it uses the current server-side speed.

 

As this game is really just my own little training project, I realize it will never be flooded with people, much less flooded with people who feel the need to cheat, but I'm trying to incorporate some "best practices" so that when I do work on something legitimate, I'm used to doing it right.

 


your server should still send a full game state every "so often" (how often can be determine based on how frenetic your game will be, how many players there are, how much traffic there already will be, etc.). 

 

That's gonna be a tough one. I can't imagine more than 10 or so objects will be moving on a player's screen at a time. I've already tried to limit how much traffic is sent to the players by only updating their surroundings rather than the entire game, but it needs more work. I guess this will be a trial + error effort.

 


This also means you should have time-stamped messages, so clients know, at "this point in time" player X was at Y location, moving at this Z velocity, so you can make sure the player is at the correct state at your clients time.

 

That.. is interesting. I'm having a little bit of a tough time wrapping my head around that. Why wouldn't I just use NOW as my time? As long as the server is sending the most recent information and the clients are updating with the most recent information, why do I need to check times? I'm not trying to contest you, I just don't understand it. Could you possibly give an example?

 

 

Thanks,

Jeremy



#12 BeerNutts   Crossbones+   -  Reputation: 2564

Like
0Likes
Like

Posted 21 August 2013 - 11:37 AM


That.. is interesting. I'm having a little bit of a tough time wrapping my head around that. Why wouldn't I just use NOW as my time? As long as the server is sending the most recent information and the clients are updating with the most recent information, why do I need to check times? I'm not trying to contest you, I just don't understand it. Could you possibly give an example?

 

What happens if the message takes 400 mS to get from the server to the client(it will happen, and it will take longer sometimes because of latency and packet retries), and a player has changed velocity/direction.  When the client gets the message, it will place the player at the given location, and given velocity/direction, but it will be 400 mS behind where the actual player is.

 

If you don't simulate him moving velocity*.4 distance immediately, then you'll be seeing him behind, and when you shoot at the player, it'll be behind where he actually is, causing issues with when U believe you've hit, him, but the server sees the bullet go behind him.

 

That's the idea behind having everything synced for a certain time.  There is no such thing as NOW when you're talking about latency and possible packet loss/retries.


My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#13 Chumble   Members   -  Reputation: 141

Like
0Likes
Like

Posted 21 August 2013 - 01:15 PM

Maybe I'm being a bit dense about this, but my point about NOW is that the server has the final say as to who is where. The best the server can do is try to relay this information as accurately as possible.

 

Yes, players will lag, and their position will be slightly off at times.. seems the best I can do is have the server update them at regular intervals to make sure they're on track.

 

You obviously know what you're talking about and again, I'm not trying to simply contest you. I accept that I am wrong, but in order to come to the correct understanding, I need to relay my thoughts.

 

I'm going to set up an example. There are two players, both are standing still.

 

 

--------------------

T=0

Player 1 starts moving right. Begins moving immediately client-side *(1)

 

T=1

Server receives the "Player Move Right" message. Broadcasts to all clients that Player 1 is moving right.

Player 2 still sees player 1 at the original location. Shoots at player 1 (Sends "Shoot" message to server).

 

T=2

Player 1 and player 2 get server's "Player 1 is moving right" message. Both player's screens are updated appropriately (Player 1's screen is adjusted, if needed).

Server gets the "Shoot" message from Player 2. Calculates that player 2 missed player 1.

Server sends the .. animate bullet message?.. to all clients to draw the bullet..?

 

T=3

Player 2 watches the bullet miss and cries in frustration

--------------------

 

*(1) - Using as accurate of prediction as I can come up with.

 

This seems like a very possible but understandable situation. This is a networked game and networks have lag. I don't see how adding a timestamp to each message helps. ...... unless you're saying that the CLIENT sends a timestamp to the server as well?

 

I don't think it would help the situation above because that is really an extreme situation, but if it was a situation where Player 2 fires at player 1 at T=1 (and sent this message to the server), even if the server didn't get the message until T=3, the server would remember that player 1 was at the fired upon position at T=1 and generate a "hit" anyways? The more I type this the more I think I still have this wrong.

 

:-/


Edited by Chumble, 21 August 2013 - 01:16 PM.


#14 Satharis   Members   -  Reputation: 949

Like
0Likes
Like

Posted 21 August 2013 - 01:42 PM

I think his point was more at using dead reckoning in your code to where the client will draw other players based on where it "assumes" their position is, and by timestamping messages from the server you can read them and go "Hrm this player should be here at this point and moving this direction, well if I compare that to his current position then I know he should have been at that spot 400 ms ago, now I can guess where he should be at this second and draw him there. This point relies on the idea that the client still assumes it know what is best and just modifies that based on concrete information received from the server.

To be honest I haven't done much realtime gameplay with networking so I'm not entirely sure what the most common design patterns are for guessing movement in different games, but. Usually with games the client will assume that it is correct and in control and just relay its movements to the server, which checks them for validity and relays that information to other clients.

It's really a bit of a guessing game and this network logic changes quite a bit depending on the type of game, for instance an mmo assumes far less precision of location than does an FPS or something where getting a shot off at the right moment is the difference between terrible and good gameplay.

I'm sure we've all played a shooter that was coded horridly to where you were expected to shoot miles ahead of or behind players because their updating was so awfully designed. Gunz is a good example.

Edited by Satharis, 21 August 2013 - 01:45 PM.


#15 Chumble   Members   -  Reputation: 141

Like
0Likes
Like

Posted 21 August 2013 - 01:57 PM


this player should be here at this point and moving this direction, well if I compare that to his current position then I know he should have been at that spot 400 ms ago, now I can guess where he should be at this second and draw him there.

 

 

o.o

 

 

.. seems much more complex than what I need. I don't plan on doing anything with projectiles and shooting bullets, etc. I think.. this is one optimization I will hold off on for now because I'm either going to go crazy trying to understand it or code it. I think I should be mostly OK without it.



#16 BeerNutts   Crossbones+   -  Reputation: 2564

Like
0Likes
Like

Posted 21 August 2013 - 01:57 PM

I realize you are trying to make a fun multiplayer game, and I suggest you don't worry about these inconsistnecies I'm describing then.  You can do what you were planning, and you'll probably have a fun game, with some noticeable inconsistencies, but as  a 1st networking game, I bet it will be fun.

 

You can find information on the internet about client-side prediction and dead-reckoning if you want to try and improve accuracy of the objects in the multiplayer game to better understand what I'm trying to describe.

 

Good luck!


My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#17 Chumble   Members   -  Reputation: 141

Like
0Likes
Like

Posted 23 August 2013 - 07:01 AM

Ok - I have added the above features to varying success.

 

 

When a player presses a directional arrow key

1) Check to see that the corresponding "keyDown" flag is not set.

2) Set the "keyDown" flag for that direction

3) If there is a "keyDown" flag already set for an opposite direction, clear it.

 

When a player releases a directional arrow key

1) Check to see that the corresponding "keyDown" flag is set.

2) Clear the "keyDown" flag for that direction

 

After either of the above occur, I "compile" the direction to a single int. 0 = no direction, 1 = Up, 2 = Up/Right (etc, clockwise until 8=Up/Left).

 

I create a new network packet with this int and send it to the server. The server gets this message and updates a new field on the player object called moveDirection.

 

 

There is a new thread running on the server called UpdateThread.

1) Loop through all entities. If the entity has a "moveDirection" > 0, update their position server-side.

2) For each entity, if it is found that their moveDirection had changed since the last loop, broadcast this new moveDirection to all clients.

3) Increment a counter. If the counter is > 100, reset the counter and broadcast the actual entity coordinates to all clients (to ensure they are synced).

4) Sleep for 10ms

 

The client's main update loop performs a very similar process to the server's UpdateThread

1) Loop through all entities. If the entity has a "moveDirection" > 1, update their position client-side.

2) "Sleep" for 10ms (since it's not in a thread I needed to make my own sleep function .. just uses a while loop to pause execution for X ms)

 

 

So.. the result..

When connecting to my own server using 127.0.0.1, it seemed moderately OK. A little twitchy as the server corrected my position.. slightly more than I would have guessed but tolerable I suppose.

When connecting a second client to the server, suddenly performance was cut in half. I cannot imagine why as the loop running through two entities instead of just one should still only take a minuscule amount of time. But the end result was the players seemed to move at about 50% normal speed.

Speed was decreased by another 50% with a third client.

 

Also, when connecting to myself using my external IP address, performance right off the bat (even with a single client) was very sloppy. I could move in a direction but I was jumping around almost constantly.

 

 

The more I type, the more I think something has to be wrong client-side. If the client is just getting a message that "Player 1 is moving left", there should be no reason why that player would move slower between the server synchronization updates that only happen once a second.

 

Anyways, if anyone has any further thoughts, feel free to post them. If not, I will continue plugging away at this to see if I can improve it. I'm hoping I haven't already hit the limits of TCP.



#18 Chumble   Members   -  Reputation: 141

Like
0Likes
Like

Posted 24 August 2013 - 12:05 PM

I have uploaded a video of this. In this video, I connect to the server, can run around... generally fine. Still not perfect.

 

Then I connect several simulated clients that just move in circles. As you can see, for each client connected, the performance just gets completely shot.

 

http://www.youtube.com/watch?v=iB_63m-HTMM

 

 

 

Edit: I'm an idiot. Had an extra sleep inside the loop on the client side. No idea why it was there. Got rid of it and I'm good.


Edited by Chumble, 29 August 2013 - 12:47 PM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS