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 m_clientUpdates; private HashMap m_socketToPlayerName; private HashMap m_lastPlayers; private HashMap 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(); m_socketToPlayerName = new HashMap(); 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 tokens = new Vector(); 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; if (m_world.getPlayers().containsKey(arr[1])) { /* this player name is already registered */ try { BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( cu.socket.getOutputStream())); String sendLine = "NAMEEXISTS\n"; bw.write(sendLine, 0, sendLine.length()); bw.flush(); cu.socket.close(); } catch (Exception e) {} return; } System.out.println("Player '" + arr[1] + "' signed on."); m_world.addPlayer(arr[1]); m_socketToPlayerName.put(cu.socket, arr[1]); m_lastPlayers.clear(); // clearing the last information will m_lastShots.clear(); // push everything to the new player 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); } else if (arr[0].equals("NOSPIN")) { m_world.stopSpinning(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 pushPlayers = new HashMap(); HashMap 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 pushShots = new HashMap(); HashMap 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 */ Player alivePlayer = null; int alivePlayers = 0; for (Player p : players.values()) { if (p.health > 0.0) { alivePlayers++; alivePlayer = p; } } /* save the player / shot data into the "last" variables */ m_lastPlayers = PlayerClone(players); m_lastShots = ShotClone(shots); /* declare a winner if there is one and reset the players */ 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(); } } } } } } 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 PlayerClone(HashMap orig) { HashMap newhm = new HashMap(); for (String name : orig.keySet()) { Player p = orig.get(name).clone(); newhm.put(name, p); } return newhm; } public HashMap ShotClone(HashMap orig) { HashMap newhm = new HashMap(); for (Integer id : orig.keySet()) { Shot s = orig.get(id).clone(); newhm.put(id, s); } return newhm; } }