// // Othecko 1.1 // // Steve Matuszek // Jack Freelander // University of North Carolina at Chapel Hill // // // GameServer.java // Steve Matuszek 1.1 // // // This class is the main server for our distributed game. Its function is // to collect votes from the clients, and, once it has received a quorum, // to resolve the game state accordingly and broadcast its results. // // Upon startup, the server outputs an html that contains the host and port // parameters on which it is running. An applet can connect directly to this // file, or a MiddleMan can use the host and port information and the applet // can connect to the MiddleMan. // // Please note that this is very much a proof-of-concept server. It does not // currently do things like end the game. The current implementation of // GameState does not support ending the game either. // import java.io.*; import java.util.*; import java.net.*; import java.awt.*; public class GameServer { // ------- Sockets and I/O static ServerSocket mySocket; static InetAddress myAddress; static String myHostName; static int myPort; static Socket connection; static ObjectOutputStream output; static ObjectInputStream input; static InputStream inputstream; static int numConnections = 0; static RemoteSystem lastrs; static Vector subscribers; static String codebase; static String filename; // These two constants are default values for the location of the applet code and // the location of the output html files. You should definitely change them if you // plan to use these; or you can ignore them if you always pass these values on the // command line. Currently this code requires these arguments on the command line, // so you would have to change that too. public static final String DEFAULT_CODEBASE = "http://www.cs.unc.edu/~matuszek/othecko/classes/"; public static final String DEFAULT_OUTPUTFILE = "~/www/othecko/play/mainserver.html"; // ------- Game stuff static GameState gamestate; public static final boolean never = false; static Vector votes; static int quorum, counted; public static void main(String[] args) { votes = new Vector(); subscribers = new Vector(); if (args.length < 2) { System.out.println("Usage: java GameServer applet-codebase html-file-pathname [quorum]"); System.exit(0); // If allowing defaults, uncomment these and comment out the exit. // // codebase = DEFAULT_CODEBASE; // filename = DEFAULT_OUTPUTFILE; } else { codebase = args[0]; filename = args[1]; } if (args.length > 2) { quorum = Integer.parseInt(args[2]); } else { System.out.println("Usage: java GameServer applet-codebase html-file-pathname [quorum]"); System.out.println("Using quorum == 1."); quorum = 1; } counted = 0; // For debugging, obviously // // if (false) // { // doSomeTests(); // return; // } // First, get an address. System.out.println("Getting my address..."); try { myAddress = InetAddress.getLocalHost(); myHostName = myAddress.getHostName(); System.out.println("My address is " + myAddress); } catch (UnknownHostException e) { System.out.println("Could not find an IP address."); } // Now initialize the GameState. try { gamestate = new GameState(); // This line is specific for this tic-tac-toe implementation of GameState. // It causes the first move to be in the center square. // gamestate.setValue('x', 1, 1); gamestate.computerGoFirst(); } catch (Exception e) { System.out.println("Couldn't create a world."); System.out.println(e + ": " + e.getMessage()); } // Open the server socket. try { System.out.println("Opening server socket..."); mySocket = new ServerSocket(0); myPort = mySocket.getLocalPort(); System.out.println("Socket opened on port " + myPort); } catch (Exception e) { System.out.println("Couldn't open socket. " + e.getMessage()); System.exit(0); } createHTMLFile(); // The wait-accept-handle loop. try { while (true) { // Block on accepting a connection connection = mySocket.accept(); numConnections++; System.out.println("Socket opened."); // Open an InputStream, turn it into an ObjectInputStream System.out.println("Opening InputStream..."); inputstream = connection.getInputStream(); System.out.println("Opening ObjectInputStream..."); input = new ObjectInputStream(inputstream); System.out.println("Opened ObjectInputStream. Waiting for Object..."); // Read in the Object, which ought to be a MessageObject MessageObject inMessage = (MessageObject) (input.readObject()); System.out.println("Read an Object."); // Now we close the socket... connection.close(); System.out.println("Return connection closed."); // And finally, handle the request. handleRequest(inMessage); if (weAreDone()) { System.out.println("That's the game. Thanks for playing!"); break; } } } catch (Exception e) { System.out.println(e); } finally { // Even if there's an Exception, close the socket. try { mySocket.close(); System.out.println("Socket closed."); } catch (Exception e) { System.out.println(e + e.getMessage()); } finally { System.out.println(numConnections + " handled."); } } } // This function is called to handle a MessageObject once we've gotten // it. Note that it could be extended to handle other types of messages. private static void handleRequest(MessageObject mo) { System.out.println("handleRequest called."); System.out.println("Message was: " + mo + "."); int type = mo.getType(); RemoteSystem rs = mo.getRemoteSystem(); //lastrs = rs; System.out.println("1. Remote system address is..."); System.out.println(rs.getAddress()); System.out.println("1. Remote system port is " + rs.getPort()); // Depending what the message type is, handle appropriately. switch (type) { case MessageObject.GAME_STATE: { // Tell the remote system the game state. sendGameMessage(rs); break; } case MessageObject.SUBSCRIBE: { // Add the remote system as a subscriber. //addSubscriber(rs); subscribers.addElement(rs); sendGameMessage(rs); break; } case MessageObject.VOTE: { // Add a vote. This message type is no longer used -- a // VOTE_COLLECTION containing only one vote should be used. System.out.println("Adding a single vote (deprecated)"); Vector trash = new Vector(); trash.addElement(mo.getObject()); addVotes(trash); break; } case MessageObject.VOTE_COLLECTION: { // Add a collection of votes. System.out.println("Got a big vote collection"); addVotes((Vector) mo.getObject()); break; } default: { // This should also not happen. System.out.println("Oops, what was that? Message was:"); System.out.println(mo); break; } } } private static void clearVotes() { votes.removeAllElements(); counted = 0; } private static void addVotes(Vector newvotes) { // Save the votes, and do the resolution // if the quorum has been reached. Vote.absorbVoteVector(votes, newvotes); counted = Vote.totalVotesIn(votes); printVotes(); if (counted >= quorum) { resolveVotes(); } } private static void printVotes() { System.out.println("We now have a total of " + counted + " votes."); System.out.println(votes); } private static void resolveVotes() { System.out.println("Quorum (" + quorum + ") reached (" + counted + " votes). Resolving move."); try { // Note that it is up to the Vote object to determine the // winning Vote from a collection of Votes, and that it is // up to the GameState to respond to that Vote. Vote winner = Vote.winningVote(votes); gamestate.applyUserMove(winner); gamestate.chooseAMove(); clearVotes(); } catch (Exception e) { System.out.println("Problem resolving votes... " + e); } try { // Now send the new game state out. System.out.println("Broadcasting new game state."); for (int a=0; a\n"); html.append("\n"); html.append("\n"); html.append("\n"); html.append("\n"); html.append("No java. "); String out = html.toString(); f.write(out, 0, out.length()); f.flush(); f.close(); System.out.println("Done creating HTML file."); System.out.println(); System.out.println("Ready."); System.out.println(); } catch (Exception e) { System.out.println("Couldn't write index.html. " + e.getMessage()); System.exit(0); } } static boolean weAreDone() { // return gamestate.gameIsOver(); return never; // Currently, we don't want the server to quit even if the game is // over. Why not? Because we don't have a GAME_OVER message set up. // This could be added. } //static public void doSomeTests() //{ // GameState.testChooseAMove(); // // for (int i=0; i<5; i++) // { // GameState g = GameState.randomState(); // System.out.println(g); // } // // //Vote.testCVV(); //} }