I am making a multi-player snake game in java using sockets. All the transmission is done through a server to all the connected clients. The code for the same is yet not completely finished but it does the basic job of moving the snakes around and increasing scores if a particular client eats its food.
I generate random numbers for food coordinates from the server side and relay it to all the clients. If a client presses a key the requested movement is calculated and the direction of movement is sent to the server which then relays the movement to ALL clients (including the one who sent it) and only on receipt of the movement info do the clients make changes to the snake which moved. So every movement is tracked over the network and no movement decision is made by the client itself until it receives that, say client 'player1' has asked to move.
The problem I am facing is that even with two players there seems to be a difference in coordinates after moving about the snakes a little.
What possible remedies could I apply to my code so as to remove this apparent lag between the position of snakes?
This is the client code:
package mycode;
import java.awt.Point;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Map;
import javax.swing.JOptionPane;
public class ConnectionManager implements Runnable {
Socket socket;
boolean start = false;
DataInputStream in;
DataOutputStream out;
Map<String, Snake> map;
ConnectionManager(String name, String IP, Map<String, Snake> m) {
this.map = m;
try {
socket = new Socket(IP, 9977);
in = new DataInputStream(new BufferedInputStream(
socket.getInputStream()));
out = new DataOutputStream(new BufferedOutputStream(
socket.getOutputStream()));
out.writeUTF(name);
out.flush();
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "Could Not Find Server",
"ERROR", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
}
void populateMap() {
try {
String name = in.readUTF();
System.out.println("Name received: " + name);
if (name.equals("start_game_9977")) {
start = true;
System.out.println("Game Started");
return;
} else if (name.equals("food_coord")) {
Game.foodx = in.readInt();
Game.foody = in.readInt();
return;
}
map.put(name, new Snake(5));
} catch (Exception e) {
e.printStackTrace();
}
}
boolean start() {
return start;
}
void increaseSnakeLength(String thisname){
Snake temp = map.get(thisname);
Point temp1=new Point(0,0);
temp.length++;
switch (temp.move) {
case DOWN:
temp1= new Point(temp.p[temp.length - 2].x,
temp.p[temp.length - 2].y+6);
break;
case LEFT:
temp1= new Point(temp.p[temp.length - 2].x-6,
temp.p[temp.length - 2].y);
break;
case RIGHT:
temp1= new Point(temp.p[temp.length - 2].x+6,
temp.p[temp.length - 2].y);
break;
case UP:
temp1= new Point(temp.p[temp.length - 2].x,
temp.p[temp.length - 2].y-6);
break;
default:
break;
}
if(temp1.y>Game.max)
temp1.y=Game.min;
if(temp1.x>Game.max)
temp1.x=Game.min;
if(temp1.y<Game.min)
temp1.y=Game.max;
if(temp1.x<Game.min)
temp1.x=Game.max;
temp.p[temp.length-1]=temp1;
}
void readMotion() {
try {
while (true) {
if (Game.changedirection) {
String mov = "";
mov = Game.move.name();
// System.out.println(Game.move);
out.writeUTF(mov);
out.flush();
Game.changedirection = false;
}
if (Game.foodeaten) {
out.writeUTF("food_eaten");
out.flush();
Game.foodeaten = false;
}
Thread.sleep(50);
}
} catch (Exception e) {
e.printStackTrace();
}
}
void otherRunMethod() {
try {
while (true) {
String mname = in.readUTF();
String mov = in.readUTF();
if (mov.equals("Resigned")) {
map.remove(mname);
} else if (mov.length() >= 10) {
if (mov.substring(0, 10).equals("food_eaten")) {
String[] s = mov.split(",");
Game.foodx = Integer.parseInt(s[1]);
Game.foody = Integer.parseInt(s[2]);
int score = ++map.get(mname).score;
increaseSnakeLength(mname);
System.out.println(mname + ":" + score+" Length:"+map.get(mname).length);
}
} else {
Game.move = Direction.valueOf(mov);
map.get(mname).move = Game.move;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
if (!start) {
populateMap();
} else if (start) {
new Thread(new Runnable() {
public void run() {
otherRunMethod();
}
}).start();
readMotion();
break;
}
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
The code is pretty long so I am just putting up the server side of code that manages connections.
package mycode;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.Map;
public class Playerhandler implements Runnable {
Socket player;
String thisname;
Map<String, Socket> map;
DataInputStream in = null;
DataOutputStream out = null;
ObjectInputStream ob;
Snake snake;
Playerhandler(Socket player, Map<String, Socket> m) {
this.player = player;
this.map = m;
try {
in = new DataInputStream(new BufferedInputStream(
player.getInputStream()));
thisname = in.readUTF();
map.put(thisname, this.player);
populatePlayers();
System.out.println("Connected Client " + thisname);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
void populatePlayers() {
try {
out = new DataOutputStream(new BufferedOutputStream(
player.getOutputStream()));
for (String name : map.keySet()) {
out.writeUTF(name);
out.flush();
}
for (String name : map.keySet()) {
out = new DataOutputStream(new BufferedOutputStream(map.get(
name).getOutputStream()));
out.writeUTF(thisname);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
void relay(String move) {
try {
if (move.equals("food_eaten")) {
move = move + ","
+ (Snakeserver.randomGenerator.nextInt(100) * 6) + ","
+ (Snakeserver.randomGenerator.nextInt(100) * 6);
}
for (String name : map.keySet()) {
out = new DataOutputStream(new BufferedOutputStream(map.get(
name).getOutputStream()));
out.writeUTF(thisname);
out.flush();
out.writeUTF(move);
// System.out.println(Direction.valueOf(move));
out.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
while (true) {
try {
relay(in.readUTF());
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Player " + thisname + " Resigned");
map.remove(thisname);
relay("Resigned");
return;
}
}
}
}