package apps.apollo; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import external.JSON.JSONArray; import external.JSON.JSONException; import external.JSON.JSONObject; import server.GameServer; import util.game.Game; import util.game.RemoteGameRepository; import util.http.HttpReader; import util.http.HttpWriter; import util.match.Match; /** * The Apollo Backend Server is a multi-threaded web server that runs matches * and reports back match information on behalf of remote clients. It serves as * a backend for intermediary systems that, due to restrictions on the length of * HTTP connections, cannot run matches themselves. * * If you don't know what the GGP Apollo project is, this system is not likely * going to be interesting to you. Feel free to ignore it. * * SAMPLE INVOCATION (when running locally): * * ResourceLoader.load_raw('http://127.0.0.1:9124/' + escape(JSON.stringify({"playClock":5, * "startClock":5, "gameURL":"http://games.ggp.org/games/connectFour/", * "matchId":"apollo.sample_2", "players":["127.0.0.1:3333", "player.ggp.org:80"]}))) * * Apollo Backend Server replies with the URL of the match on the spectator server. * * @author Sam Schreiber */ public final class ApolloBackend { public static final int SERVER_PORT = 9124; // Matches are run asynchronously in their own threads. static class RunMatchThread extends Thread { int playClock, startClock; String gameURL, matchId; List<String> names, hosts; List<Integer> ports; Game theGame; Match theMatch; GameServer theServer; private static final String spectatorServerURL = "http://matches.ggp.org/"; public RunMatchThread(Socket connection) throws IOException, JSONException { System.out.println("Got connection from client."); String line = HttpReader.readAsServer(connection); System.out.println("Read line from client: " + line); JSONObject theJSON = new JSONObject(line); playClock = theJSON.getInt("playClock"); startClock = theJSON.getInt("startClock"); gameURL = theJSON.getString("gameURL"); matchId = theJSON.getString("matchId"); names = new ArrayList<String>(); hosts = new ArrayList<String>(); ports = new ArrayList<Integer>(); JSONArray thePlayers = theJSON.getJSONArray("players"); for (int i = 0; i < thePlayers.length(); i++) { String[] splitAddress = thePlayers.getString(i).split(":"); hosts.add(splitAddress[0]); ports.add(Integer.parseInt(splitAddress[1])); names.add(""); } // Get the match into a state where we can publish it to // the spectator server, so that we have a spectator server // URL to return for this request. theGame = RemoteGameRepository.loadSingleGame(gameURL); theMatch = new Match(matchId, startClock, playClock, theGame); theServer = new GameServer(theMatch, hosts, ports, names); String theSpectatorURL = theServer.startPublishingToSpectatorServer(spectatorServerURL); // Limit the rate at which the match advances, to avoid overloading // the players and the spectator server with many requests. theServer.setForceUsingEntireClock(); HttpWriter.writeAsServer(connection, spectatorServerURL + "matches/" + theSpectatorURL + "/"); connection.close(); } @Override public void run() { System.out.println("Starting match: " + matchId); theServer.start(); try { theServer.join(); } catch (InterruptedException e) { e.printStackTrace(); return; } } } public static void main(String[] args) { ServerSocket listener = null; try { listener = new ServerSocket(SERVER_PORT); } catch (IOException e) { System.err.println("Could not open server on port " + SERVER_PORT + ": " + e); e.printStackTrace(); return; } while (true) { try { Socket connection = listener.accept(); Thread handlerThread = new RunMatchThread(connection); handlerThread.start(); } catch (Exception e) { System.err.println(e); } } } }