Java: Network Game Lag

Started by
7 comments, last by GameDev.net 18 years, 1 month ago
Hello all, I have created a multiplayer pong game and so far so good. However, I've come across a small problem... lag. The game is perfectly in-sync, however, the game lags way too much. I've tried using non-blocking sockets but that doesn't work either (same results). The game is set at 30 FPS (Frames Per Second), which is set using the tradional Thread.sleep(1000/fps); Here is my source-code if you need to take a look: http://www.jamisongames.info/MultiPong/MultiPong.java I would appreciate any help that you guys can give me. Thanks and regards, Jamison
Advertisement
I'm no expert, but i'll have a go anyway.

First thing - The java implementation of sockets is, in my opinion, slightly buggy. First, if you use it wrong it can go REALLY slowly. Second, there would seem to be some incompatibilities when using sockets to communicate with non-java programs.

Basically, If I remember right, the answer is - Only use socket programming if you're communicating java to non-java, otherwise use RMI - remote method invocation
Quote:
I've tried using non-blocking sockets but that doesn't work either (same results). The game is set at 30 FPS (Frames Per Second), which is set using the tradional Thread.sleep(1000/fps);


You most probably do want to use non blocking sockets, especially since you mix them in your gameloop (instead in a seperate thread).

So what you're basicaly doing now is this: for each frame drawn you now wait for some data to be available on your socket. And for each frame you send some data. (with the the number of frames being capped at 30/sec max)

My first advice would be to have your gamelogic updated 30 times a sec, but draw (call repaint) as many times as possible (so faster machines will have a smoother display, but the gamelogic runs at the same speed, I think gaffer.org has a nice tut on that).

Next you'll probably want to use non-blocking sockets or blocking sockets that run in their own thread. When something is available on the line, update your game with it. For a pong type of game 30 updates / sec is overkill. 10 updates / sec is probably fine (this is for the player / bat movement) because the ball motion is highly predictable. (once it bounces you can calculate the exact path it will take). You only have to check for collisions with non predictable objects (the player bats). As you're updating 30 times / sec but the bats / ball are only updated 10 times a sec you can calculate (predict their next pos) based on previous data (search for deadreckoning, i think gamedev has a nice explanation on it). This will keep the motion nice smooth.

As you use TCP for your communication you'll want to disable Naggle's algorithm (socket.setNoDelay(true) or something) which will prevent TCP from buffering data local before sending it over the wire... Note that TCP guarantees reliable in order messages, for updates like positions you might want to use UDP (Datagrams) as they wont stall when a message gets lost in the INternet...

Also, I noticed you use a PrinterWriter to communicate. You'll want to use a DataoutputStream in Java and write primitives like ints directly (like out.read/writeInt)

Quote:
First thing - The java implementation of sockets is, in my opinion, slightly buggy. First, if you use it wrong it can go REALLY slowly. Second, there would seem to be some incompatibilities when using sockets to communicate with non-java programs.


Java's sockets aren't buggy, at least not that im aware of...you have any proof to back that statement up? There arent any incompatibilities neither (at least that im aware of)... as long as you write raw data to the line, it's up to the other end on how to interprete this (byte orders etc). Maybe you're talking about the NIO classes? These had some bugs when they where relaesed but have matured since.

Quote:
Basically, If I remember right, the answer is - Only use socket programming if you're communicating java to non-java, otherwise use RMI - remote method invocation


IMHO RMI is something you should avoid at all times when developing a multiplayer game in Java. RMI is slow and bloathed and really not designed for that job. Java's socket to socket communication is fine, i dont know where you getting the idea from that its buggy.

Thijs
DaBookshah, I agree with the anonymous poster. I've worked with sockets only for a short time, and before I got into trying to make a multiplayer RTS with them, they've worked flawless. And even making a multiplayer RTS they should work flawless (I've seen it done), I just know I'm doing something wrong.

Anonymous poster, thanks for all of that great information. I saw something about using the Socket.setTcpNoDelay(true) when using sockets for a multiplayer RTS, and I tried it but it had no effect on blocking sockets. Maybe it should be used with non-blocking sockets?

I'll take a look at the DataOutputStream class.

Oh, and wouldn't Datagram Packets be a bad idea for a multiplayer RTS? Would the positions tend to jump around since the packets aren't guaranteed to arrive in the same order they were sent out? For example:

Frame 0 - Server - ballX = 0; ballY = 0;
Frame 0 - Client - ballX = 0; ballY = 0;
Frame 1 - Server - ballX = 5; ballY = 5;
Frame 1 - Client - ballX = 0; ballY = 0;
Frame 2 - Server - ballX = 10; ballY = 10;
Frame 2 - Client - ballX = 5; ballY = 5;
Frame 3 - Server - ballX = 15; ballY = 15;
Frame 3 - Client - ballX = 15; ballY = 15;
Frame 4 - Server - ballX = 20; ballY = 20;
Frame 4 - Client - ballX = 20; ballY = 20;

Notice how the ball would skip a position in Frame 0 and Frame 1 and then in Frame 2 it would appear to jump from Position 0 to 10.

I'm going to rewrite my run() method, and change my sockets back to non-blocking again. And I'll also try and repaint the screen whenever possible (I'll see if I can find that tutorial on how to do that).
Okay, I've changed my code and I've completely rewrote my run() method. I changed the FPS to 10 and I'm now using non-blocking sockets. The lag is completely gone now, however, now the game is completely out of sync (synchronization). When I test it on my localhost (with myself), it seems to work without lag and it's almost perfectly in-sync. But when I upload it and test it (with myself), it's completely out of sync. What can I do?

Take a look at my changed source-code:
http://www.jamisongames.info/MultiPong/MultiPong.java

Oh and I couldn't find anything even about Java on gaffer.org.

Maybe I should try and rewrite my code to use Packets?
Is it out of sync by the latency you have to that host (your 'ping')? If so, there's not a lot you can do. (Test it by typing 'ping www.myhost.com' in your command line.)

UDP is generally faster than TCP under Internet conditions, because dropped packets just disappear instead of lagging.

Quote:Oh, and wouldn't Datagram Packets be a bad idea for a multiplayer RTS? Would the positions tend to jump around since the packets aren't guaranteed to arrive in the same order they were sent out?


You include a time (or a frame number, which does the same job) in the packet, and discard it if the object it refers to (or the entire game state if it's all in one packet) has been updated by a packet with a later timestamp.
Hey. I admit that RMI is something that I have had no experience with - When searching for answers to similar questions, I always found posters(experts) on the java.sun.com forums saying that RMI was the way to go unless sockets were absolutely required(java to non-java). Ok, given, that wasn't about game programming, maybe its wrong.

I can't back my comments about java sockets being buggy with a concrete example (can't be bothered dragging out the code) BUT from personal experience I have had severe problems with them(I only used them once ok). Basically, when writing a program to connect with a server, I was unable to come up with one which didn't take 50 connection attempts to actually successfully connect. Other programs in different languages doing an identical job didn't have such problems. I eventually found a document on the sun site which mentioned this problem, as far as I could gather theres a problem with the implementation in some ways.

[Edited by - DaBookshah on March 18, 2006 6:40:12 AM]
I've been working with Sockets for about three weeks, and I haven't come across a single connection problem with them. Only lag and out-of-sync problems (in games).

Anyway, back on topic... it can't be latency. I had my friend ping me (through my router) and the time it took was between 100-105ms (milliseconds). And he lives in New York, so it's not like he's next door ;).

I tried using Datagram Sockets but those are much more confusing than TCP Sockets. I couldn't figure out how to use them to make an RTS.

I know there has gotta be some way to fix this...
Quote:
Anonymous poster, thanks for all of that great information. I saw something about using the Socket.setTcpNoDelay(true) when using sockets for a multiplayer RTS, and I tried it but it had no effect on blocking sockets. Maybe it should be used with non-blocking sockets?


The effect might or might not be noticed, because it buffers messages to a certain threshold locally. For games it's always a good idea to disable this option. Using blocking or non-blocking sockets doesnt matter for this to take effect.

Quote:
Oh, and wouldn't Datagram Packets be a bad idea for a multiplayer RTS?


I'd recommend using TCP for an RTS. The properties of UDP that make it nice for fast action games are not really suited for RTS games. There's a good overview on gamasutra on the network architecture used for age of empires: http://www.gamasutra.com/features/20010322/terrano_01.htm

Quote:
Oh and I couldn't find anything even about Java on gaffer.org.


Sorry, the link for the tutorial i was referring to is: http://www.gaffer.org/articles/Timestep.html

It's not Java specific, but it's a good idea to build your gameloop that way (especially for multiplayer games)


Okay, I've changed my code and I've completely rewrote my run() method. I changed the FPS to 10 and I'm now using non-blocking sockets. The lag is completely gone now, however, now the game is completely out of sync (synchronization). When I test it on my localhost (with myself), it seems to work without lag and it's almost perfectly in-sync. But when I upload it and test it (with myself), it's completely out of sync. What can I do?


Don't limit your fps to 10 to achieve 10 network updates a sec :) use the tutorial from gaffer to seperate gamelogic updates / drawing frames. Then in your main loop check if you should send network updates using a timer. Run your gamelogic at 30 or 60hz, draw frames as much as possible and update the network 10 times a sec or somethiing (test to see what works best for you).

It's obvious your game gets out of sync now you switched from blocking to non-blocking sockets... With the blocking sockets, your game ran lockstep. It waited for an update from the other side before continuing drawing another frame. Now you use non-blocking sockets the game continues wheter theres an update or not... so no "lag" (it doesnt wait) and your game gets updated asynchroniously.

To overcome objects that freeze between network updates; try to calculate the next ball/oponent position locally (maybe based on previous data like velocity etc), have the network updates correct this prediction where necesary. You could also look into interpolation to smoothly transit to the correct position if you mis-predicted.

Quote:
Hey. I admit that RMI is something that I have had no experience with - When searching for answers to similar questions, I always found posters(experts) on the java.sun.com forums saying that RMI was the way to go unless sockets were absolutely required(java to non-java). Ok, given, that wasn't about game programming, maybe its wrong.


RMI is a powerful tool, but too powerful for games ;)
It adds its own protocol on top of TCP and uses serialization / reflection everywhere. Check these post for a good discussion on the subject:
http://www.javagaming.org/forums/index.php?topic=11564.0
http://www.javagaming.org/forums/index.php?topic=11531.0

Quote:
I can't back my comments about java sockets being buggy with a concrete example (can't be bothered dragging out the code) BUT from personal experience I have had severe problems with them(I only used them once ok). Basically, when writing a program to connect with a server, I was unable to come up with one which didn't take 50 connection attempts to actually successfully connect. Other programs in different languages doing an identical job didn't have such problems. I eventually found a document on the sun site which mentioned this problem, as far as I could gather theres a problem with the implementation in some ways.


I've done a reasonable amount of work with them and never had any complaints, in fact the easy / robust networking capabilities is something many people like about java.

Anyways, if you'd like advice on Java game programming http://www.javagaming.org/forums/index.php is the place to be...its where all the Java tech heads that program games hang out...

Good luck!

Thijs

This topic is closed to new replies.

Advertisement