Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (475)
Games in Android Showcase (106)
games submitted by our members
Games in WIP (529)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Client + Server, "Same" code running at different speeds  (Read 806 times)
0 Members and 1 Guest are viewing this topic.
Offline Chumble

Junior Newbie





« Posted 2013-09-02 22:46:24 »

Ok, so I have a little 2d networked java game I'm working on. Right now I'm trying to synchronize player-side movement "guessing" with server side movement. The short story is: My player-side movement calculation is somehow running slower than my server-side calculation.. the clients constantly see themselves moving slower than they are actually moving. I have the server sync the absolute coordinates every 1 second, and at that time the player jumps ahead to their actual position.

Quick overview of my model:

1) Player changes direction to "up", sends this to server
2) Server flags this player as moving "up"
3) Server - In a section of code called every 10ms by a swing timer, update all player coordinates based on current direction and check to see if any player updated their direction. If so, tell all clients which player updated and what direction.
4) Client gets player direction update, flags the player as moving "up"
5) Client - In a section of code called every 10ms by a swing timer, update all player coordinates (locally) based on current direction and re-draw the screen.

Also, every 1 second, the coords of all players are sent to all players to make sure the client and server are in sync. The problem is, they never are.

#3 and #5 should be running just about the same time, the client fairly accurately guessing where they should be based on the last direction from the server.

So I guess now I need some code. Here is a section of my "Area" object which is handling movement and collision for all objects in that "area". Both the client and server use this exact same code (they each create an instance of the Area)

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
public boolean updateMoves()
   {
      System.out.println(counter++);
      ArrayList<Entity> ents = entities;
      boolean change = false;
      for(int i = 0;i<ents.size();i++)
      {
         Entity ent = (Entity) ents.get(i);
         int dX = 0;
         int dY = 0;
         if(ent.moveCounter())
         {
            switch(ent.getMoveDirection())
            {
            case 0:
               break;
            case 1://U
              dY=-2;
               break;
            case 2://UR
              dX=1;
               dY=-1;
               break;
            case 3://R
              dX=2;
               break;
            case 4://RD
              dX=1;
               dY=1;
               break;
            case 5://D
              dY=2;
               break;
            case 6://DL
              dX=-1;
               dY=1;
               break;
            case 7://L
              dX=-2;
               break;
            case 8://LU
              dX=-1;
               dY=-1;
               break;
            }
            if(dX!=0)
            {
               if(noCollide(ent,dX,0))
                  getEntity(ent).setX(ent.getX()+dX);
            }
            if(dY!=0)
            {
               if(noCollide(ent,0,dY))
                  getEntity(ent).setY(ent.getY()+dY);
            }
            if((dX!=0)||(dY!=0))
            {
               change = true;
            }
         }
      }
      return change;
   }


The "counter" variable is part of a debugging measure I took. Ideally, they should both be counting up at about the same speed. However, the client's counter increases at about 50% of the server's counter.

Also, the moveCounter thing.. it increments a counter by 1, and if it's greater than 10 it returns true and resets the counter. Was supposed to be my way of slowing down the player. I thought it might be part of the problem so right noiw it just returns "true" no matter what.

Ok, here's where I set up the server timer to call that update code:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
      ActionListener updateMoveListener = new ActionListener()
      {
         @Override
         public void actionPerformed(ActionEvent e)
         {
            map.getArea(0, 0).updateMoves();
            try
            {
               broadcastNewDirs();
            } catch (IOException e1)
            {
               e1.printStackTrace();
            }
         }
      };
      Timer updateTimer = new Timer(10, updateMoveListener);
      updateTimer.start();
           


And the same thing on the client side:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
      ActionListener updateMoveListener = new ActionListener()
      {
         @Override
         public void actionPerformed(ActionEvent e)
         {
            if(map.getArea(0, 0).updateMoves())
            {
               iDrawListener.redraw();
            }
         }
      };
      Timer displayTimer = new Timer(10, updateMoveListener);
      displayTimer.start();


the iDrawListener.redraw just makes calls to my drawscreen routine telling it to re-paint the screen.
For the client side, I use the updateMoves code return value to make sure I only re-draw the screen when something has changed.



Can anyone make anything of this? I really don't think it has anything to do with networking, it's just two programs that should be running pretty much the same code at the same interval but one is going much faster than the other.

Thanks,
Jeremy
Offline h3ckboy

JGO Coder


Medals: 5



« Reply #1 - Posted 2013-09-03 13:25:38 »

I think I understand the problem you are having. I don't really have a solution, but something you might try is changing the time between updates (eg 20 ms instead of 10ms) just to see what happens, if the ratio between the client and server's speed changes it might eliminate the timer as the issue.
Offline ClickerMonkey

JGO Coder


Medals: 20


Game Engineer


« Reply #2 - Posted 2013-09-03 14:38:53 »

Yeah.. if it is 10ms that's 100 times a second. COMPLETE overkill... it will most likely flood your network and the connection will suffer from lost packets.

You might not experience this problem locally, but as soon as you have the server sitting outside your local network and you try to play it will be very apparent.

I would send a timestamp for each "packet", to update the position you take the difference in seconds between two packets and increase the position by the velocity * elapsedTime.

Good luck!

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Chumble

Junior Newbie





« Reply #3 - Posted 2013-09-03 14:56:58 »

I think I understand the problem you are having. I don't really have a solution, but something you might try is changing the time between updates (eg 20 ms instead of 10ms) just to see what happens, if the ratio between the client and server's speed changes it might eliminate the timer as the issue.

Hmm, ok. I think I had it at 50 or 100 ms earlier. I will try to increase it again to see what happens. Can you give an explanation as to what you think the problem might be?

Yeah.. if it is 10ms that's 100 times a second. COMPLETE overkill... it will most likely flood your network and the connection will suffer from lost packets.


Actually.. I'm using TCP ... =X . I'm still pretty new to networking and TCP seemed like the simpler option. Given that it's a fairly low-scale game, I thought I could get away with it. My thought process on most of this is "Do it the easy way until I find out why some things don't work for all situations, then optimize".

I would send a timestamp for each "packet", to update the position you take the difference in seconds between two packets and increase the position by the velocity * elapsedTime.

Good luck!

That is a very interesting process. This would only be done client-side right? No need for the server to calculate it in this way. I know it makes more sense to do this in a UDP scenario, but I'm probably going to end up converting to UDP at some point anyways. I would need to write this out as it would happen in steps.. it helps me to visualize the process.


I'm at work now but I'll up the time to 20ms or so and report back with the result.
Thanks,
Jeremy
Offline jonjava
« Reply #4 - Posted 2013-09-03 18:11:21 »

Read this: http://gafferongames.com/networking-for-game-programmers/

Offline Chumble

Junior Newbie





« Reply #5 - Posted 2013-09-03 18:19:53 »

Will do.

I gotta say though, I should have pointed it out in my earlier post.. that 10ms doesn't necessarily mean it's sending a message over the network every 10ms. It just means

"Every 10ms, check to see if a player changed direction. If they did, send out the changing player's direction to everyone else"

So unless the player was changing directions 100 times a second, they wouldn't get that many messages.

Also, considering I see this issue while not changing directions, it means that the code being shown is not sending ANY network messages while I see the issue.. this is why I mentioned that I don't think it's a networking issue.

Thanks,
Jeremy
Offline jonjava
« Reply #6 - Posted 2013-09-03 18:32:11 »

10ms is still way too much. even 30 times per sec might be considered overkill. Depends on what networking scheme you implement but you should be more than fine with ~20 per sec.

Offline Chumble

Junior Newbie





« Reply #7 - Posted 2013-09-04 16:19:20 »

Ok.. I increased it to 35 and it worked. I have no idea why.

It kind of brings me back to an issue I was having earlier though (which was why I lowered it to begin with) which is that accuracy seems to have been reduced too much.

I can accept that there will be lag.. that a player will always show up slightly behind where they actually are. My problem is.. let's say a player is running at an object. They decide to start moving up just before hitting the object.

Crude drawing on what I'm talking about:

  /\
O|
  |_______

Player is running left, they turn up just before they would pass object O. This looks fine client-side, but due to that small delay between when the client checks collision and the server checks collision, it is possible the server sees the player go below the object, THEN go up.

 
O
 /\_______

So when the server syncs the player's position, they jump down to below the object.

I guess one possible fix for this would be .. if the server sees the player collide with an object, sync their coordinates. Seems like a messy "band-aid" fix though. I feel like the key may be in synchronizing the times at which the client and server run the "update".

Maybe now would be a good time to read through the "networked movement" documents posted above.

Thanks for your help, everyone
Offline jonjava
« Reply #8 - Posted 2013-09-04 16:54:55 »

That's why there are client- and serverside predictions.

Read some of the history about it in the already linked links..

http://gafferongames.com/networking-for-game-programmers/what-every-programmer-needs-to-know-about-game-networking/

Offline netikan

Senior Newbie


Medals: 1


Zombies!


« Reply #9 - Posted 2013-09-04 17:36:02 »

Here is a great article explaining your issue (kind of)
https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
Skip down to the section marked "Lag compensation"


If that doesn't help you, think of things this way with this pseudo code.

Server:

if hasPlayerMoved
   if collision
      reject movement request and send origonal coordinates to player
   else
      broadcast new position to all
   end
end


Client:

thread 1 //used for input
   float x, y;
   float drawX, drawY;

   if moveUp
      y = y++;
   if moveDown
      y = y--;
   if moveLeft
      x = x--;
   if moveRight
      x = x++;

   if hasPlayerMoved
      sendUpdateToServer(x, y)
   end

   if drawX != x
      move drawX towards x
   end

   if drawY != y
      move drawY towards y
   end
end thread 1

thread 2 //used for networking
   if recievedServerMovementInfo
      x = serverX
      y = serverY
   end
end thread 2




So essentially, you have added interpolation/lag compensation, a LOT less packet flow, and an enforced means of avoiding collisions.
So, you'll be drawing the image on drawX/Y and updating X/Y.

Let me know if this isn't clear enough. I'm typing this from a tablet.

Check out my project Land of Zom - Multiplatform Zombie MMO/RPG!
Soon to be at Eurogamer 2013.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Chumble

Junior Newbie





« Reply #10 - Posted 2013-09-04 17:44:04 »

If I'm interpreting this correctly, this is what I had implemented originally.

Every single player movement was checked client-side. If it was fine, it sent the update to the server which updated it server-side (after checking) and broadcast the update to all clients to re-draw.

The problem was WAY too many messages. With 3 people moving, each one was sending maybe 30 movement messages a second to the server and the server was broadcasting 90 messages a second.. something in there was too much and all the clients crashed out almost immediately (I think the server sent a null message to all clients, crashing when they tried to interpret it). Sending coordinates for every single move just did not seem to be effective.

This was when I changed the entire setup to what it is now.. where the player sends a direction to the server and the server sends a direction to the client.

Please note I still haven't read the above, I'm just responding to your second part. I'm at work and can't slack off for too long just yet Wink
Offline netikan

Senior Newbie


Medals: 1


Zombies!


« Reply #11 - Posted 2013-09-04 17:51:23 »

If I'm interpreting this correctly, this is what I had implemented originally.

Every single player movement was checked client-side. If it was fine, it sent the update to the server which updated it server-side (after checking) and broadcast the update to all clients to re-draw.

The problem was WAY too many messages. With 3 people moving, each one was sending maybe 30 movement messages a second to the server and the server was broadcasting 90 messages a second.. something in there was too much and all the clients crashed out almost immediately (I think the server sent a null message to all clients, crashing when they tried to interpret it). Sending coordinates for every single move just did not seem to be effective.

This was when I changed the entire setup to what it is now.. where the player sends a direction to the server and the server sends a direction to the client.

Please note I still haven't read the above, I'm just responding to your second part. I'm at work and can't slack off for too long just yet Wink

If you've read what code I've used before, and fully understand it, then I can honestly recommend that you only update 3-5 times a second.

That's essentially how I can handle a few hundred players, and a few hundred NPCs in a small part of my game's world. (See the Land of Zom banner on the right hand side, and footer of this site)

Check out my project Land of Zom - Multiplatform Zombie MMO/RPG!
Soon to be at Eurogamer 2013.
Offline Chumble

Junior Newbie





« Reply #12 - Posted 2013-09-04 18:14:08 »

If you've read what code I've used before, and fully understand it, then I can honestly recommend that you only update 3-5 times a second.

That's essentially how I can handle a few hundred players, and a few hundred NPCs in a small part of my game's world. (See the Land of Zom banner on the right hand side, and footer of this site)

I trust that you know what you're talking about and I in no way want to offend you by contesting it, but I think something is missing from the pseudocode .. maybe it's inferred and I'm just missing it.

What I see from your pseudocode is every single time the client registers an input from the player (if un-capped, can be at least 30/second), it sends a message to the server. And every time the server gets a message, it sends one to each client informing them of the initiating client's move.

I see nothing there that would restrict player input or messages to the server. If you just left that part out, then I understand.

As far as restricting the input/messages, would you recommend only checking every X ms in each thread to see if there was an update?
Ex: (T = Time in MS), check player input every 200ms
T=0 - Nothing happening
T=50 - Player presses "Down", nothing happens
T=200 - Client sees player has "Down" pressed, send "down" to server
T=400 - Client sees player has "Down" pressed, send "down" to server
T=500 - Player releases "Down", nothing happens
T=600 - Client sees there is no input, sends nothing
T=800 - Client sees there is no input, sends nothing

In this scenario, the player can only make 5 moves a second. I feel like if the player moves any faster than 3 pixels per move, the movement will look incredibly stuttered. Some smoothing could be incorporated to say .. if I see a player moved up, keep drawing them moving up. That could work... but is this what you were talking about?


Edit: I realize it's not very smart of me to keep posting and asking questions when I haven't even read the provided material. I'll stop posting replies here until I have time to read a little bit.
Offline netikan

Senior Newbie


Medals: 1


Zombies!


« Reply #13 - Posted 2013-09-04 18:25:41 »

Give me an hour to get home, and I'll make a functioning example.

Check out my project Land of Zom - Multiplatform Zombie MMO/RPG!
Soon to be at Eurogamer 2013.
Offline jonjava
« Reply #14 - Posted 2013-09-04 18:47:53 »

Why don't you read some of the well written articles based on actual real games instead of dicking around? Just a friendly suggestion.

Re-inventing the wheel can be fun and meaningful but not when you outsource all the work to the forums.

Offline Chumble

Junior Newbie





« Reply #15 - Posted 2013-09-04 18:53:46 »

Why don't you read some of the well written articles based on actual real games instead of dicking around? Just a friendly suggestion.

Re-inventing the wheel can be fun and meaningful but not when you outsource all the work to the forums.

Please see my last edit Wink

On the other hand, it can be useful to learn by doing rather than by reading. Get a better understanding as to how people came to conclusions rather than just taking them for granted. But yes, I will read.


Edit: So it turns out I've already read the gafferon game articles, several months ago. They describe general techniques which I have incorporated already. However, I'm down to specifics.

The Valve article was very informative, and from that I have deduced 3 major points:

1) Perform a client-side collision check prior to sending a message to the server. This allows the current player to move in "real time", although they may jump backwards if the server disagrees with their collision check.

2) Draw entity updates "in the past". I'm a little fuzzy on this still. This is an example of what I've gathered (Player0 is the observer, Player1 is another player, T = Time in milliseconds)
T=0 - Server says Player1 is at coordinates 5,5
T=200 - Server says Player1 has moved to coordinates 7,7.
Rather than just draw Player1 at coordinates 7,7, perform a client-side smoothing to draw them transitioning from 5,5 to 7,7. If the server is sending updates at an interval of 200ms, then the "smooth" effect will last 200ms.
T=200, the player is still drawn at 5,5
T=300, the player is drawn at 6,6
t=400, the player is drawn at 7,7
This creates a constant 200ms lag.

3) Valve talks about incorporating a "lag compensation" where the server keeps a history of movements. In the example above, if you (Player0) fired a gun at Player1 at T=200, On the server side Player 1 is at 7,7 already although Player0 wont see that until T=400. To compensate, the server re-winds a bit to simulate what would have happened at that time and generates a "hit" if they would have hit.



I want to take this one step at a time. Here is what I have so far.
a) Client - Press "UP" arrow key.
b) Client Movement - This code is called every Xms. Get the client's "Moving In" direction. If moving, calculate collision client-side. If collision is fine, move the client, then send a "Move Up" message to the server.
c) Server - Get "Move" message from client. Flag the player as "move up".
d) Server Update - This code is called every Yms. Go through each player and look for flag to say they've moved. If they have and if they did not collide, calculate their new coordinates and send the coordinates to all players. If they do collide, send the coordinates to the colliding player.
e) Client - Get coordinates from server. Create a new thread to "animate" the movement of the moving player. This is done in a thread so that the client will not need to wait while animating other player's movement.

Concerns:
Part b) I'm not sure about this. Firstly, I'm only processing player input every Xms to restrict the amount of message I send to the server. It goes both ways though. If I'm trying to draw the inputting client in real time, it really can't have a delay. How am I supposed to update the client in realtime as well as limit messages sent to the server?

Part d) If this is checking at a regular interval (Yms) to reduce the number of messages sent to the client, it *will* be out of sync with the Xms for each client. Haven't wrapped my head around what this will cause for problems.

I am continuously looking for other articles about this but many seem to be a specific question about their code rather than the general model.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

ctomni231 (31 views)
2014-07-18 06:55:21

Zero Volt (27 views)
2014-07-17 23:47:54

danieldean (23 views)
2014-07-17 23:41:23

MustardPeter (24 views)
2014-07-16 23:30:00

Cero (39 views)
2014-07-16 00:42:17

Riven (41 views)
2014-07-14 18:02:53

OpenGLShaders (28 views)
2014-07-14 16:23:47

Riven (28 views)
2014-07-14 11:51:35

quew8 (25 views)
2014-07-13 13:57:52

SHC (61 views)
2014-07-12 17:50:04
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!