331 lines
11 KiB
Java
331 lines
11 KiB
Java
|
|
import java.io.*;
|
|
import java.util.*;
|
|
import java.net.*;
|
|
|
|
public class BlobWarsServer
|
|
{
|
|
public static final int PORT = 38491;
|
|
|
|
private ServerSocket m_socket;
|
|
private BlobWarsWorld m_world;
|
|
private Vector<ClientUpdate> m_clientUpdates;
|
|
private HashMap<Socket, String> m_socketToPlayerName;
|
|
private HashMap<String, Player> m_lastPlayers;
|
|
private HashMap<Integer, Shot> m_lastShots;
|
|
private int m_lastNumPlayersAlive;
|
|
|
|
public static void main(String[] args)
|
|
{
|
|
BlobWarsServer bws = new BlobWarsServer();
|
|
bws.mainloop();
|
|
}
|
|
|
|
public BlobWarsServer()
|
|
{
|
|
m_lastNumPlayersAlive = 0;
|
|
m_world = new BlobWarsWorld();
|
|
m_clientUpdates = new Vector<ClientUpdate>();
|
|
m_socketToPlayerName = new HashMap<Socket, String>();
|
|
try {
|
|
m_socket = new ServerSocket(PORT);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
return;
|
|
}
|
|
}
|
|
|
|
private class ClientUpdate
|
|
{
|
|
public Socket socket;
|
|
public String message;
|
|
}
|
|
|
|
public void mainloop()
|
|
{
|
|
if (m_socket == null)
|
|
return;
|
|
|
|
new Thread(new ServerGameLoop()).start();
|
|
|
|
/* listen loop to accept new connections */
|
|
for (;;)
|
|
{
|
|
Socket client;
|
|
try {
|
|
client = m_socket.accept();
|
|
} catch (Exception e) {
|
|
System.out.println("Socket error!");
|
|
e.printStackTrace();
|
|
break;
|
|
}
|
|
|
|
/* handle the client in a separate thread */
|
|
Thread t = new Thread(new ClientHandler(client));
|
|
t.start();
|
|
}
|
|
}
|
|
|
|
private void processClientUpdate(ClientUpdate cu)
|
|
{
|
|
StringTokenizer st = new StringTokenizer(cu.message, ":");
|
|
Vector<String> tokens = new Vector<String>();
|
|
while (st.hasMoreTokens())
|
|
{
|
|
tokens.add(st.nextToken());
|
|
}
|
|
String[] arr = tokens.toArray(new String[1]);
|
|
if (arr.length < 1)
|
|
return;
|
|
if (arr[0].equals("NAME"))
|
|
{
|
|
if (arr.length < 2)
|
|
return;
|
|
System.out.println("Player '" + arr[1] + "' signed on.");
|
|
m_world.addPlayer(arr[1]);
|
|
m_socketToPlayerName.put(cu.socket, arr[1]);
|
|
return;
|
|
}
|
|
/* the rest of the commands all require a player to be registered */
|
|
if (!m_socketToPlayerName.containsKey(cu.socket))
|
|
return;
|
|
String playerName = m_socketToPlayerName.get(cu.socket);
|
|
if (arr[0].equals("SHOOT")) {
|
|
m_world.shoot(playerName);
|
|
} else if (arr[0].equals("UP")) {
|
|
m_world.moveUp(playerName);
|
|
} else if (arr[0].equals("DOWN")) {
|
|
m_world.moveDown(playerName);
|
|
} else if (arr[0].equals("LEFT")) {
|
|
m_world.moveLeft(playerName);
|
|
} else if (arr[0].equals("RIGHT")) {
|
|
m_world.moveRight(playerName);
|
|
}
|
|
}
|
|
|
|
private class ServerGameLoop implements Runnable
|
|
{
|
|
public void run()
|
|
{
|
|
for (;;)
|
|
{
|
|
try {
|
|
Thread.sleep(45);
|
|
} catch (InterruptedException ie) {
|
|
System.out.println("ServerGameLoop: interrupted!");
|
|
return;
|
|
}
|
|
|
|
/* apply changes from clients */
|
|
synchronized (m_clientUpdates)
|
|
{
|
|
for (ClientUpdate cu : m_clientUpdates)
|
|
{
|
|
processClientUpdate(cu);
|
|
}
|
|
m_clientUpdates.clear();
|
|
}
|
|
|
|
/* step the BlobWars world */
|
|
m_world.step();
|
|
|
|
/* calculated updated players */
|
|
HashMap<String, Player> pushPlayers =
|
|
new HashMap<String, Player>();
|
|
HashMap<String, Player> players = m_world.getPlayers();
|
|
if (m_lastPlayers == null)
|
|
pushPlayers.putAll(m_world.getPlayers());
|
|
else
|
|
{
|
|
for (String name : players.keySet())
|
|
{
|
|
if (!m_lastPlayers.containsKey(name))
|
|
pushPlayers.put(name, players.get(name));
|
|
else if (!players.get(name).equals(
|
|
m_lastPlayers.get(name)))
|
|
pushPlayers.put(name, players.get(name));
|
|
}
|
|
}
|
|
|
|
/* calculate updated shots */
|
|
HashMap<Integer, Shot> pushShots =
|
|
new HashMap<Integer, Shot>();
|
|
HashMap<Integer, Shot> shots = m_world.getShots();
|
|
if (m_lastShots == null)
|
|
pushShots.putAll(m_world.getShots());
|
|
else
|
|
{
|
|
for (Integer shotid : shots.keySet())
|
|
{
|
|
if (!m_lastShots.containsKey(shotid))
|
|
pushShots.put(shotid, shots.get(shotid));
|
|
else if (!shots.get(shotid).equals(
|
|
m_lastShots.get(shotid)))
|
|
pushShots.put(shotid, shots.get(shotid));
|
|
}
|
|
}
|
|
|
|
/* build up the update string */
|
|
String sendLine = "";
|
|
for (Player p : pushPlayers.values())
|
|
{
|
|
sendLine += "PLAYER:" + p + "\n";
|
|
}
|
|
for (Shot s : pushShots.values())
|
|
{
|
|
sendLine += "SHOT:" + s + "\n";
|
|
}
|
|
|
|
/* add remove messages for all players / shots that are gone */
|
|
if (m_lastPlayers != null)
|
|
{
|
|
for (String playerName : m_lastPlayers.keySet())
|
|
{
|
|
if (!players.containsKey(playerName))
|
|
{
|
|
/* player playerName has left since last update */
|
|
sendLine += "RMPLAYER:" + playerName + "\n";
|
|
}
|
|
}
|
|
}
|
|
if (m_lastShots != null)
|
|
{
|
|
for (Integer id : m_lastShots.keySet())
|
|
{
|
|
if (!shots.containsKey(id))
|
|
{
|
|
/* shot has expired since last update */
|
|
sendLine += "RMSHOT:" + id + "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check number of players alive, possibly declare winner */
|
|
Player alivePlayer = null;
|
|
int alivePlayers = 0;
|
|
for (Player p : players.values())
|
|
{
|
|
if (p.health > 0.0)
|
|
{
|
|
alivePlayers++;
|
|
alivePlayer = p;
|
|
}
|
|
}
|
|
if (alivePlayers == 1 && alivePlayers < m_lastNumPlayersAlive)
|
|
{
|
|
/* game is over, alivePlayer won */
|
|
sendLine += "WINNER:" + alivePlayer.name + "\n";
|
|
m_world.resetPlayers();
|
|
}
|
|
m_lastNumPlayersAlive = alivePlayers;
|
|
|
|
/* write updated info to each client */
|
|
synchronized (m_socketToPlayerName)
|
|
{
|
|
for (Socket client : m_socketToPlayerName.keySet())
|
|
{
|
|
try {
|
|
BufferedWriter bw = new BufferedWriter(
|
|
new OutputStreamWriter(
|
|
client.getOutputStream()));
|
|
bw.write(sendLine, 0, sendLine.length());
|
|
bw.flush();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* save the player / shot data into the "last" variables */
|
|
m_lastPlayers = PlayerClone(players);
|
|
m_lastShots = ShotClone(shots);
|
|
}
|
|
}
|
|
}
|
|
|
|
private class ClientHandler implements Runnable
|
|
{
|
|
private Socket m_socket;
|
|
|
|
public ClientHandler(Socket socket)
|
|
{
|
|
m_socket = socket;
|
|
System.out.println("Incoming connection from " +
|
|
m_socket.getInetAddress().getHostAddress());
|
|
}
|
|
|
|
public void run()
|
|
{
|
|
BufferedReader br;
|
|
try {
|
|
br = new BufferedReader(
|
|
new InputStreamReader(m_socket.getInputStream()));
|
|
} catch (IOException ioe) {
|
|
ioe.printStackTrace();
|
|
return;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
String line;
|
|
try {
|
|
line = br.readLine();
|
|
} catch (Exception e) {
|
|
// e.printStackTrace();
|
|
break;
|
|
}
|
|
if (line == null)
|
|
break;
|
|
// System.out.println("SERVER GOT LINE: " + line);
|
|
if (line.equals("QUIT"))
|
|
break;
|
|
ClientUpdate cu = new ClientUpdate();
|
|
cu.socket = m_socket;
|
|
cu.message = line;
|
|
synchronized (m_clientUpdates)
|
|
{
|
|
m_clientUpdates.add(cu);
|
|
}
|
|
}
|
|
|
|
/* end of client handling loop, remove client from world */
|
|
synchronized (m_socketToPlayerName)
|
|
{
|
|
if (m_socketToPlayerName.containsKey(m_socket))
|
|
{
|
|
System.out.println("Player '" +
|
|
m_socketToPlayerName.get(m_socket) +
|
|
"' signed off.");
|
|
m_world.removePlayer(m_socketToPlayerName.get(m_socket));
|
|
m_socketToPlayerName.remove(m_socket);
|
|
}
|
|
try {
|
|
m_socket.close();
|
|
} catch (Exception e) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
public HashMap<String, Player> PlayerClone(HashMap<String, Player> orig)
|
|
{
|
|
HashMap<String, Player> newhm = new HashMap<String, Player>();
|
|
for (String name : orig.keySet())
|
|
{
|
|
Player p = orig.get(name).clone();
|
|
newhm.put(name, p);
|
|
}
|
|
return newhm;
|
|
}
|
|
|
|
public HashMap<Integer, Shot> ShotClone(HashMap<Integer, Shot> orig)
|
|
{
|
|
HashMap<Integer, Shot> newhm = new HashMap<Integer, Shot>();
|
|
for (Integer id : orig.keySet())
|
|
{
|
|
Shot s = orig.get(id).clone();
|
|
newhm.put(id, s);
|
|
}
|
|
return newhm;
|
|
}
|
|
}
|