home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / pc / java / enxle1f6 / src / games / battle / server / battlegame / battlegame.java next >
Encoding:
Java Source  |  1996-08-14  |  8.7 KB  |  358 lines

  1. /*
  2.  * @(#)BattleGame.java
  3.  */
  4. package games.Battle.server.BattleGame;
  5.  
  6. import java.io.*;
  7. import java.net.*;
  8.  
  9. import games.Battle.shared.comm.*;
  10. import games.Battle.shared.sys.*;
  11. import games.Battle.server.ServerBoard.*;
  12.  
  13. /** 
  14.  * BattleGame implements the server thread for one particular game running
  15.  * on the server. Many games may be simultanously in progress, and each one
  16.  * is an instance of a BattleGame with its own thread.
  17.  *
  18.  * @version 1.00
  19.  * @author Alex Nicolaou
  20.  * @author Jay Steele
  21.  */
  22.  
  23. public class BattleGame implements Runnable {
  24.     /**
  25.      * the thread of this particular game.
  26.      */
  27.     Thread gameThread;
  28.  
  29.     /**
  30.      * the board that this game is being played on
  31.      */
  32.     ServerBoard board = null;
  33.  
  34.     /**
  35.      * an array of sockets to the players who are participating in the game
  36.      */
  37.     Socket player[];
  38.     /**
  39.      * an array of output streams to the players who are participating
  40.      */
  41.     OutputStream out[];
  42.     /**
  43.      * an array of input threads, one input thread for each player
  44.      */
  45.     Piper receiver[];
  46.  
  47.  
  48.     /**
  49.      * the log file service, used to record game events and debug output
  50.      */
  51.     Logger logfile;
  52.     /**
  53.      * the game referee, who is notified of when players die and when the
  54.      * game ends.
  55.      */
  56.     GameReferee referee;
  57.     /**
  58.      * a unique game id for this game, the same game id as the gameinfo
  59.      * packet that represents this game to both the client and the server
  60.      */
  61.     int gameId;
  62.  
  63.     /** 
  64.      * time for each board update in milliseconds. the server is real time
  65.      * and tries to meet this time bound exactly on every turn.
  66.      */
  67.     static final int DELAY = 550;
  68.  
  69.     /**
  70.      * returns the board that the game is being played on
  71.      */
  72.     public ServerBoard getBoard() { return board; }
  73.  
  74.     /**
  75.      * construct a new game
  76.      * @param socket an array of sockets to all participants
  77.      * @param gameId the id of the GameInfo packet
  78.      * @param r the referee for this game
  79.      * @param l the logfile service for this game
  80.      */
  81.     public BattleGame(Socket socket[], int gameId, GameReferee r, Logger l) {
  82.         this.gameId = gameId;
  83.         this.logfile = l;
  84.         this.referee = r;
  85.         player = socket;
  86.  
  87.         out = new OutputStream[player.length];
  88.         for (int p = 0; p < player.length; p++) {
  89.             try {
  90.                 out[p] = player[p].getOutputStream();
  91.             }
  92.             catch (Exception e) {
  93.                 log("Couldn't set up player " + p);
  94.             }
  95.         }
  96.     }
  97.  
  98.     /**
  99.      * a convenient log function that identifies which game we are as well
  100.      * as logging the message.
  101.      * @param msg the message to be logged for this game
  102.      */
  103.     public void log(String msg) {
  104.         logfile.log("Game #"+gameId+" - "+msg);
  105.     }
  106.  
  107.     /**
  108.      * initialize a game board from scratch for this game.
  109.      */
  110.     public void initBoard() {
  111.         board = new ServerBoard(referee, logfile);
  112.         board.makeRandomTerrain();
  113.         for (int p = 0; p < player.length; p++) {
  114.             board.placePlayer(Symbols.PLAYER_SYMBOL[p]);
  115.             board.placePlayer(Symbols.PLAYER_SYMBOL[p]);
  116.         }
  117.         
  118.         for (int p = 0; p < player.length; p++) 
  119.             initPlayer(p);
  120.     }
  121.  
  122.     /**
  123.      * return a new player socket. used to test the game thread outside the
  124.      * full server, as a standalone application.
  125.      */
  126.     static public Socket getPlayer(ServerSocket ss) {
  127.         // accept an incoming connection
  128.         Socket s = null;
  129.         try {
  130.             s = ss.accept();
  131.         } catch (Exception e) {
  132.             System.out.println( "accepting connection: " + e );
  133.             e.printStackTrace();
  134.             System.exit(1);
  135.         }
  136.  
  137.         return s;
  138.     }
  139.  
  140.     /**
  141.      * place the given player on the game board, and set up an input thread
  142.      * for the player
  143.      * @param p the player number of the player to set up.
  144.      */
  145.     public void initPlayer(int p) {
  146.         // Set up a receiver to get player commands
  147.         if (receiver == null)
  148.             receiver = new Piper[player.length];
  149.  
  150.         try {
  151.             receiver[p] = new Piper(board, player[p].getInputStream(), 
  152.                                     gameId, p, logfile);
  153.         }
  154.         catch (Exception e) {
  155.             log("lost player " + p + " while setting up.");
  156.             return;
  157.         }
  158.  
  159.         // send the player a game init
  160.         GameInit ginit = new GameInit(Symbols.PLAYER_SYMBOL[p], 
  161.                                       GameInit.DEFAULT_COLORS);
  162.  
  163.         OutputStream out = null;
  164.         try {
  165.             out = player[p].getOutputStream();
  166.             ginit.writeTo(out);
  167.         } catch (Exception e) {
  168.             log("lost player " + p + " while sending game init.");
  169.         }
  170.  
  171.         // send the player a terrain init
  172.         TerrainInit tinit = new TerrainInit(board);
  173.         try {
  174.             tinit.writeTo(out);
  175.         } catch (Exception e) {
  176.             log("lost player " + p + " while sending game init.");
  177.         }
  178.  
  179.     }
  180.  
  181.     /** 
  182.      * a mainline that allows this to run as a standalone application to debug
  183.      * the game thread outside of the full server
  184.      */
  185.     public static void main(String argv[]) {
  186.         ServerSocket ss = null;
  187.         int PORT = 5000;
  188.  
  189.         Logger logfile = new Logger();
  190.  
  191.         // open a new server socket on port PORT
  192.         try {
  193.             ss = new ServerSocket(PORT);
  194.         } catch (Exception e) {
  195.             logfile.log("couldn't start server; port #" + PORT + " in use");
  196.             System.exit(1);
  197.         }
  198.  
  199.         int numPlayers = 2;
  200.         Socket player[] = new Socket[numPlayers];
  201.  
  202.         for (int i = 0; i < numPlayers; i++) {
  203.             logfile.log("waiting for player " + i);
  204.             player[i] = getPlayer(ss);
  205.         }
  206.  
  207.         BattleGame server = new BattleGame(player, 0, new DummyRef(), logfile);
  208.         server.start();
  209.     }
  210.  
  211.     /**
  212.      * start up the server game thread
  213.      */
  214.     public void start() {
  215.         if (gameThread != null)
  216.             return;
  217.  
  218.         // initialize the game board
  219.         initBoard();
  220.  
  221.         // give the clients a pause to set up the game.
  222.         try {
  223.             Thread.currentThread().sleep(1000);
  224.         } catch ( Exception e ) {
  225.             log("interrupted while trying to give clients time to start up");
  226.         }
  227.  
  228.         gameThread = new Thread(this);
  229.         gameThread.setPriority(Thread.MAX_PRIORITY);
  230.         gameThread.start();
  231.     }
  232.  
  233.     /**
  234.      * halt this game
  235.      */
  236.     public void stop() {
  237.         try {
  238.             // brief pause in case anything needs to wrap up
  239.             Thread.sleep(500);
  240.         }
  241.         catch (Exception e) {
  242.         }
  243.  
  244.         log("all the players are dead. game over");
  245.         referee.gameOver();
  246.         gameThread.stop();
  247.     }
  248.  
  249.  
  250.  
  251.     /**
  252.      * the main game loop is in the run thread. it updates the board, sends
  253.      * all the players the differences, and tris at all times to maintain
  254.      * a constant game pace.
  255.      */
  256.     public void run() {
  257.         long startTime = System.currentTimeMillis();
  258.         long oldtime, lastPacketTime, diff;
  259.         boolean gameover = false;
  260.  
  261.         lastPacketTime = startTime;
  262.         while (true) {
  263.             // update the board an immediately send the results to all 
  264.             // the players (all one of them)
  265.             oldtime = System.currentTimeMillis();
  266.             startTime = oldtime;
  267.  
  268.             board.update();
  269.             oldtime = howMuchTime(oldtime, "updating board");
  270.  
  271.             boolean atLeastOnePlayer = false;
  272.             for (int p = 0; p < player.length; p++) {
  273.                 if (player[p] == null)
  274.                     continue;
  275.  
  276.                 atLeastOnePlayer = true;
  277.  
  278.                 oldtime = System.currentTimeMillis();
  279.  
  280.                 byte message = TurnDiff.NORMAL;
  281.  
  282.                 gameover = board.isGameOver();
  283.                 if (gameover) {
  284.                     log("SENDING GAMEOVER");
  285.                     message = TurnDiff.GAMEOVER;
  286.                 }
  287.  
  288.                 TurnDiff diffp = new TurnDiff(board, Symbols.PLAYER_SYMBOL[p], 
  289.                                               message);
  290.                 diffp.makeBytes();
  291.  
  292.                 oldtime = howMuchTime(oldtime, "making diff packet");
  293.  
  294.  
  295.                 try {
  296.                     diffp.writeTo(out[p]);
  297.                 } catch ( Exception e ) {
  298.                     log("player " + p + " has vanished.");
  299.                     player[p] = null;
  300.                     board.killPlayer(p);
  301.                 }
  302.                 oldtime = howMuchTime(oldtime, "sending turn diff");
  303.             }
  304.  
  305.             if (!atLeastOnePlayer || gameover) {
  306.                 stop();
  307.                 return;
  308.             }
  309.  
  310.             // compute how long it has been since we started working on this
  311.             // turn
  312.             diff = System.currentTimeMillis() - startTime;
  313.             lastPacketTime = startTime;
  314.  
  315.             if (diff > DELAY) {
  316.                 // we've gone overbudget on time
  317.                 log("** warning: update and communication > " + 
  318.                                         DELAY + " = "+diff);
  319.                 try {
  320.                     // pause to give other threads a chance to run
  321.                     Thread.sleep(10);
  322.                 }
  323.                 catch (Exception e) {
  324.                 }
  325.             } else {
  326.                 // twiddle thumbs until we're allowed to send the packet 
  327.                 try {
  328.                     long sleepFor = DELAY-diff;
  329.                     long time =  System.currentTimeMillis();
  330.                     System.gc();
  331.                     long gcTime = System.currentTimeMillis() - time;
  332.                     if (sleepFor - gcTime > 0)
  333.                         Thread.currentThread().sleep(sleepFor-gcTime);
  334.                     else
  335.                         log("GC threw game over budget: "+(sleepFor - gcTime));
  336.                 } catch (Exception e) {
  337.                     log("interrupted delaying for turn");
  338.                 }
  339.             }
  340.  
  341.             long actualTime = System.currentTimeMillis() - startTime;
  342.             // uncomment for more info on timing
  343.             //log("actual time spent on turn: "+actualTime);
  344.         }
  345.     }
  346.  
  347.     /**
  348.      * a function to time portions of the server to debug problems meeting
  349.      * the time bound
  350.      */
  351.     public long howMuchTime(long oldtime, String msg) {
  352.         long time = System.currentTimeMillis();
  353.         // uncomment for more timing info
  354.         //log(msg + ": " + (time - oldtime));
  355.         return time;
  356.     }
  357. }
  358.