views:

144

answers:

6

Okay, So I'm working on a game (like not even alpha yet) and it's suppose to be a 1 on 1 fighting game where one person hosts the server and the other one connects. But right now the code is too laggy to do anything with. Can someone take a look at it and tell me how to speed it up? PS: I'm also using the Slick2D lib.

Server:

import java.net.*;
import java.io.*;

public class SlickServer{
    public static void main(String[] args) throws IOException {

        int MAX_PLAYERS = 3;
        int playerNum = 0;
        Player[] players = new Player[MAX_PLAYERS];
        players[0] = new Player(25,25);
        players[1] = new Player(125,125);
        players[2] = new Player(225,225);
        ServerSocket serverSocket = new ServerSocket(4444);
        boolean listening = true;

        while(listening){
            System.out.println("Waiting to connect with: " + playerNum);
            new ClientThread(serverSocket.accept(), players, playerNum).start();
            //stops here.
            System.out.println("Connected with: " + playerNum + " Now incrementing");
            playerNum++;
            System.out.println("Incremented to: " + playerNum);
        }



        serverSocket.close();
        System.exit(0);
    }
}

Server Thread:

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.*;
import java.io.*;

public class ClientThread extends Thread implements Runnable{
    Socket acceptedSocket;
    Player[] players;
    int playerNum;

    public ClientThread(Socket acceptedSocket, Player[] players, int playerNum){
     super("ClientThread");
     this.acceptedSocket = acceptedSocket;
     this.players = players;
     this.playerNum = playerNum;
    }

    public void run(){
     try{

      Socket clientSocket = acceptedSocket;
      System.out.println("Accepted. Now creating I/O.");
      ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
            ObjectOutputStream out = new ObjectOutputStream(clientSocket.getOutputStream());
            System.out.println("I/O with: " + playerNum + " working.");
            out.writeInt(playerNum);
            out.flush();

            while(true){
             if(playerNum == 0){
              players[0].x = in.readInt();
              players[0].y = in.readInt();
              out.writeInt(players[1].x);
              out.writeInt(players[1].y);
              out.flush();
             }

             else if(playerNum == 1){
              players[1].x = in.readInt();
              players[1].y = in.readInt();
              out.writeInt(players[0].x);
              out.writeInt(players[0].y);
              out.flush();
             }

             else if(playerNum == 2){
              players[2].x = in.readInt();
              players[2].y = in.readInt();
              out.writeInt(players[0].x);
              out.writeInt(players[0].y);
              out.flush();
             }
            }

     }

     catch(Exception e){
      e.printStackTrace();
      System.exit(1);
     }


    }


}

Client:

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;

import java.io.*;
import java.net.*;


public class SlickClient extends BasicGame{

    int MAX_PLAYERS = 3;
    int playerNum = 0;
    Player[] players;
    ClientThread ct;

    int serverDelay = 15;

    public SlickClient()
    {
        super("Client");
    }

    @Override
    public void init(GameContainer gc)
      throws SlickException {
        try{
         players = new Player[MAX_PLAYERS];
         players[0] = new Player(25,25);
         players[1] = new Player(125,125);

         ct = new ClientThread(players);
         ct.start();
         ct.setPriority(Thread.MAX_PRIORITY);

         playerNum = ct.playerNum;   
        }

        catch(Exception e){
      e.printStackTrace();
     }

    }

    @Override
    public void update(GameContainer gc, int delta)
      throws SlickException
    {
        Input input = gc.getInput();

        if(input.isKeyDown(Input.KEY_A))
        {
            players[playerNum].x-=5;
        }

        if(input.isKeyDown(Input.KEY_D))
        {
            players[playerNum].x+=5;
        }

        if(input.isKeyDown(Input.KEY_W))
        {
            players[playerNum].y-=5;
        }

        if(input.isKeyDown(Input.KEY_S))
        {
            players[playerNum].y+=5;
        }



    }

    public void render(GameContainer gc, Graphics g)
      throws SlickException
    {
        g.fillRect(players[0].x, players[0].y, 50, 50);
        g.fillRect(players[1].x, players[1].y, 50, 50);

    }

    public static void main(String[] args)
      throws SlickException
    {
         AppGameContainer app =
      new AppGameContainer( new SlickClient() );

         app.setAlwaysRender(true);
         app.setTargetFrameRate(30);
         app.setDisplayMode(800, 600, false);
         app.start();
    }
}

class ClientThread extends Thread implements Runnable{
    Socket socket;
    Player[] players;
    int playerNum;
    ObjectOutputStream out;
    ObjectInputStream in;

    public ClientThread(Player[] players){
     super("ClientThread");

     try{

      socket = new Socket("98.203.176.196", 4444);
      out = new ObjectOutputStream(socket.getOutputStream());
            in = new ObjectInputStream(socket.getInputStream());
      this.players = players;
      playerNum = in.readInt();
         System.out.println(playerNum);

     }

     catch(Exception e){
      e.printStackTrace();
     }
    }

    public void run(){
     try{
      while(true){
       if(playerNum == 0){
              try{
               out.writeInt(players[0].x);
               out.writeInt(players[0].y);
               out.flush();
               players[1].x = in.readInt();
               players[1].y = in.readInt();
              }

              catch(Exception e){
            e.printStackTrace();
           }
             }

             else if(playerNum == 1){
              try{
               out.writeInt(players[1].x);
               out.writeInt(players[1].y);
               out.flush();
               players[0].x = in.readInt();
               players[0].y = in.readInt();
              }

              catch(Exception e){
            e.printStackTrace();
           }   
             } 
      }
     }

     catch(Exception e){
      e.printStackTrace();
     }
    }
}
+1  A: 

Have you tried using something other than ObjectOutputStream and ObjectInputStream to send and receive the data? Using these involves serializing and deserializing objects, and it seems (just a hunch though) that using DataOutputStream might be faster.

Kaleb Brasee
Did you mean a DataInputStream and DataOutputStream rather than a ByteArrayOutputStream since the BAOS wouldn't actually do anything with the data?
jt
Oops yeah, that's what I was thinking of, thanks.
Kaleb Brasee
+1  A: 

Well the code you provide is incomplete...I was going to do this and then walk you through how to do it so I could be sure of what you would see... however we will work with what we have.

1) Run it on the same machine for both the client and the server - is it still slow? 2) Ping between the machines and see how the speed is 3) profile the app.

#1 helps see if it is a networking issue. Both the client and the server on the same machine eliminates the network.

#2 helps see if it is a networking issue. If the ping is bad then the game will be slow.

#3 is harder to do but will give you the results of where in your code it is slow.

To profile take a look at the Visual VM (or if you are using netbeans just profile it in there... eclipse probably has something too, perhaps as a plugin).

For profiling I would start with the server and see what is slow there and then move onto the client. I suggest that only because there is less going on in the server so it should be easier to see what is going on.

TofuBeer
+1  A: 

Hmmm, several things you can try right away, but it'll need some work to do what you want.

First, both your client and your server are in very tight loops. Normally an loop like you're using should sleep for a minimal amount of time on each pass to keep the loop from consuming too much CPU doing nothing.

Second, you should probably use non-blocking I/O (check for input from each client before trying to read it).

Finally, unless you want to embark on a learning exercise regarding Java networking, or unless you have specific objectives that require a nonstandard communications protocol, you might want to consider using a server framework that you can simply extend. An easy way to do this would be to build your server side with Jetty or Tomcat and use the Apache HTTPClient for your client side.

If you chose to stay with a dedicated server, the simple server frameworks in the Core Java book or the Java Networking (O'Reilly) book would give you a better base.

Hope that helps.

Steve Robenalt
+2  A: 

Is it only laggy when the client is on a remote machine from the server? If so and you have a good ping time between the boxes then I'm going to take a wild guess and say you're possibly running into Nagle's algorithm here? The data you're pushing is very small (writeInt).

You can try using a BufferedOutputStream intermediary though if you want to try a quick fix, you can also just try disabling Nagle on your sockets.

Socket.setTcpNoDelay(true);

spdaley
It's fine if I have the socket connect to "localhost" but if I have it set to my IP it goes laggy. the set no delay helped a little.
William
+1  A: 

It depends on why it's slow. Here's a couple of suggestions

  1. Only use non-blocking I/O. As a specialization of this rule, never use RPC.
  2. Consider doing client-side prediction where appropriate. Client side prediction is assuming that what the client wants to do will be okay, and drawing on the client as if it was okay. Most if not all networked games use this.
  3. Accept that you do not have 'synchronized' state between your players in a real-time game. It just won't happen.

Also, define what you mean by "gets laggy." Do you mean that the code gives poor framerates? That it takes a long time for moves to register? Figuring this out will be the first step to optimizing.

And good luck. Getting a fighting game to be responsive over the network will be something of a difficult task, even for professional developers with experience in network game programming. You will probably need to modify your game design to an extent to help hide the lag - certain things are just impossible over a network.

kyoryu
+1  A: 

If you want you could try using datagram sockets. If the underlying packet gets dropped, it just goes away, but since you're just updating position it should be fine.

Chad Okere