Networking to Slow - Java Multiplayer Pong

Started by
11 comments, last by Kaptein 11 years, 4 months ago
Hey all!
I created a Pong Game in Java and added Networking,
but it seems to slow the whole game down signifficantly.

I use a thread for the networking routine, on both server and the two clients.
The conversion is as follows:

send a marker (so the receiver knows which value will be sent next)
send the value
send the next marker, etc...

This is the Code for the Server:


/**
*
* Klasse (Thread) welche die Netzwerkfunktionen abhandelt
*
*/
class Network implements Runnable {
Network() {
}
public void run() {
// mit Host verbinden und Kommunizieren
try {
ServerSocket server = new ServerSocket(8888);
System.out.println("Warte auf Verbindung auf Port 8888");
Socket Client1 = server.accept();
System.out.println("Server: Client1 verbunden");
Socket Client2 = server.accept();
System.out.println("Server: Client2 verbunden");

//Client1 einrichten
OutputStream out1 = Client1.getOutputStream();
PrintStream ps1 = new PrintStream(out1, true); // Second param: auto-flush on write = true
InputStream in1 = Client1.getInputStream();
BufferedReader br1 = new BufferedReader(new InputStreamReader(in1));
//Client2 einrichten
OutputStream out2 = Client2.getOutputStream();
PrintStream ps2 = new PrintStream(out2, true); // Second param: auto-flush on write = true
InputStream in2 = Client2.getInputStream();
BufferedReader br2 = new BufferedReader(new InputStreamReader(in2));

String line="";
ps1.println("run");
ps2.println("run");

while(true){

//Einlesen Client1
for(int i=0;i<1;i++){
line = br1.readLine();

if(line!=null){
if(line.contains("v1")){
line = br1.readLine();
player1Y=Integer.parseInt(line);
}
}
}//end Einlesen for-Schleife

//Einlesen Client2
for(int i=0;i<1;i++){
line = br2.readLine();

if(line!=null){
if(line.contains("v1")){
line = br2.readLine();
player2Y=Integer.parseInt(line);
}
}
}//end Einlesen for-Schleife

//Ausgabe an Client1
ps1.println("v2");
ps1.println(""+player2Y);

ps1.println("v4");
ps1.println(""+ballY);

ps1.println("v3");
ps1.println(""+ballX);

ps1.println("v5");
ps1.println(""+player1Score);

ps1.println("v6");
ps1.println(""+player2Score);

//Ausgabe an Client2
ps2.println("v2"); //gespiegelt für Player 2
ps2.println(""+player1Y);

ps2.println("v4");
ps2.println(""+ballY);

ps2.println("v3");
ball2X=MAXX-ballX; //gespiegelt für Player2
ps2.println(""+ball2X);

ps2.println("v5"); //gespiegelt für Player2
ps2.println(""+player2Score);

ps2.println("v6"); //gespiegelt für Player2
ps2.println(""+player1Score);

}


} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("Verbindungsfehler aufgetreten");
}
}
}


And the Client:


/**
*
* Klasse (Thread) welche die Netzwerkfunktionen abhandelt
*
*/
class Network implements Runnable {
String hostAdress;
Network(String adress) {
this.hostAdress = adress;
}
public void run() {
try {

Socket connectionToTheServer = new Socket(hostAdress, 8888);
System.out.println("Client: Verbindung zu Server hergestellt");
InputStream in = connectionToTheServer.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
OutputStream out = connectionToTheServer.getOutputStream();
PrintStream ps = new PrintStream(out, true);
String line="";
while(!(line.equals("run"))){line=br.readLine();}//warten auf Start des Spiels

while(true){

//Ausgeben

ps.println("v1");
ps.println(""+player1Y);

//Einlesen
for(int i=0;i<5;i++){
line = br.readLine();
if(line!=null){
if(line.contains("v2")){
line = br.readLine();
player2Y=Integer.parseInt(line);
}else
if(line.equals("v3")){
line = br.readLine();
ballX=Double.parseDouble(line);
}else
if(line.equals("v4")){
line = br.readLine();
ballY=Double.parseDouble(line);
}else
if(line.contains("v5")){
line = br.readLine();
player1Score=Integer.parseInt(line);
}else
if(line.contains("v6")){
line = br.readLine();
player2Score=Integer.parseInt(line);
}

}
}//end Einlesen for-Schleife
}


} catch (UnknownHostException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("Verbindungsfehler aufgetreten");
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("Verbindungsfehler aufgetreten");
}

}


Logically everything works fine, but its very slow.

I would appreciate some recommendations on how to improve the speed!

Thanks in advance!
Advertisement
Have you tried setTcpNoDelay?

Have you tried setTcpNoDelay?

Thank you so much!
I added it and its much better now :)
Is there anything else i can do?

[Thank you so much!
I added it and its much better now smile.png
Is there anything else i can do?


- UDP
- Better network/packet structure
For the issue at hand the choice between TCP and UDP are irrelevant, especially in any local test.

- Better network/packet structure

You mean not to send single lines with auto-flush?
ideally, your code should handle 250-500ms lag, and thus you'll need to interpolate between the data sent to you from another player
this is a game where lag will affect gameplay, and we don't care about cheating, so, i would just do very simple interpolation in between updates
and not worry so much about the actual amount of latency or updates..
the biggest problem you will face is "invariance," here being desynchs, but since you have a centralized server that makes all the decisions
this problem is already solved =)

i would implement fast interpolation pad_y = (0.9 * pad_y + 0.1 * pad_last_y); pad_last_y = pad_y;

nodelay is good in high-speed games (disabling nagle algo), but it might not always be enabled..
the drivers are free to do nothing, after all, and never tell you otherwise
udp isn't really any faster, it's just potentially smaller packets, and widely used for "positioning" that are high-frequent updates
when working with networking, you will never have a cpu bottleneck compared to high latency
high latency is measured in hundreds of milliseconds, and there isn't much you can do about it
increasing the amount of updates you are doing by too much will just cause the socket to throttle (and die) because its write buffer would be exceeded
the normal latency for local loopback is 0ms, LAN is 1ms+ and internet is at least 60ms
60ms is alot considering 30fps is 33ms, 60ms is ~16fps
that's why you'll always _always_ need to interpolate between moving visuals with high-frequency updates

ideally, your code should handle 250-500ms lag, and thus you'll need to interpolate between the data sent to you from another player
this is a game where lag will affect gameplay, and we don't care about cheating, so, i would just do very simple interpolation in between updates
and not worry so much about the actual amount of latency or updates..
the biggest problem you will face is "invariance," here being desynchs, but since you have a centralized server that makes all the decisions
this problem is already solved =)

i would implement fast interpolation pad_y = (0.9 * pad_y + 0.1 * pad_last_y); pad_last_y = pad_y;


So i should let the Clients kinda estimate where the ball will be on the next update?
Or insert an interpolated point between two of the updates?

[quote name='Kaptein' timestamp='1355399101' post='5010161']
ideally, your code should handle 250-500ms lag, and thus you'll need to interpolate between the data sent to you from another player
this is a game where lag will affect gameplay, and we don't care about cheating, so, i would just do very simple interpolation in between updates
and not worry so much about the actual amount of latency or updates..
the biggest problem you will face is "invariance," here being desynchs, but since you have a centralized server that makes all the decisions
this problem is already solved =)

i would implement fast interpolation pad_y = (0.9 * pad_y + 0.1 * pad_last_y); pad_last_y = pad_y;


So i should let the Clients kinda estimate where the ball will be on the next update?
Or insert an interpolated point between two of the updates?
[/quote]

yep, you have to. it's not a decision or design choice.. i updated my post and added some context as to why we have to do this and make do
i used 60ms in my example, but i've never had such a low latency on my connection :) it's usually in the 90's (97ms for example)
what this means is usually only that the response from me to the server will be delayed by 97ms, and that the movement will STILL be fluid
but ANY point in between me and the server could delay data unreliably before sending it
which means that it won't always be fluid, especially if the nodes wait too long to send and just gather your incoming data,
then send it in one bigger package (causing the server to get 10-20 updates in a single read())

Note: i didn't speak about advantages of UDP, such as congestion controls, but i'm sure you can read about it on your own
TCP has delayed ACK (which can be delayed a long time, even 200ms) and nagle algorithm isn't reliably disabled (and you shouldn't want to disable it either)
in short: use UDP for your game if you want the fastest throughput of small updates
but, personally i wouldn't go that far :)
You could also send/receive a byte array instead of using character stream as you seems to be only sending Numbers. I know it prolly won't improve a lot the speed but
optimization is always good. Go read about java object serialization/deserialization. You will save time by not creating a String and parsing it as you just want to send numbers.

This topic is closed to new replies.

Advertisement