home *** CD-ROM | disk | FTP | other *** search
Java Source | 1996-08-14 | 8.7 KB | 358 lines |
- /*
- * @(#)BattleGame.java
- */
- package games.Battle.server.BattleGame;
-
- import java.io.*;
- import java.net.*;
-
- import games.Battle.shared.comm.*;
- import games.Battle.shared.sys.*;
- import games.Battle.server.ServerBoard.*;
-
- /**
- * BattleGame implements the server thread for one particular game running
- * on the server. Many games may be simultanously in progress, and each one
- * is an instance of a BattleGame with its own thread.
- *
- * @version 1.00
- * @author Alex Nicolaou
- * @author Jay Steele
- */
-
- public class BattleGame implements Runnable {
- /**
- * the thread of this particular game.
- */
- Thread gameThread;
-
- /**
- * the board that this game is being played on
- */
- ServerBoard board = null;
-
- /**
- * an array of sockets to the players who are participating in the game
- */
- Socket player[];
- /**
- * an array of output streams to the players who are participating
- */
- OutputStream out[];
- /**
- * an array of input threads, one input thread for each player
- */
- Piper receiver[];
-
-
- /**
- * the log file service, used to record game events and debug output
- */
- Logger logfile;
- /**
- * the game referee, who is notified of when players die and when the
- * game ends.
- */
- GameReferee referee;
- /**
- * a unique game id for this game, the same game id as the gameinfo
- * packet that represents this game to both the client and the server
- */
- int gameId;
-
- /**
- * time for each board update in milliseconds. the server is real time
- * and tries to meet this time bound exactly on every turn.
- */
- static final int DELAY = 550;
-
- /**
- * returns the board that the game is being played on
- */
- public ServerBoard getBoard() { return board; }
-
- /**
- * construct a new game
- * @param socket an array of sockets to all participants
- * @param gameId the id of the GameInfo packet
- * @param r the referee for this game
- * @param l the logfile service for this game
- */
- public BattleGame(Socket socket[], int gameId, GameReferee r, Logger l) {
- this.gameId = gameId;
- this.logfile = l;
- this.referee = r;
- player = socket;
-
- out = new OutputStream[player.length];
- for (int p = 0; p < player.length; p++) {
- try {
- out[p] = player[p].getOutputStream();
- }
- catch (Exception e) {
- log("Couldn't set up player " + p);
- }
- }
- }
-
- /**
- * a convenient log function that identifies which game we are as well
- * as logging the message.
- * @param msg the message to be logged for this game
- */
- public void log(String msg) {
- logfile.log("Game #"+gameId+" - "+msg);
- }
-
- /**
- * initialize a game board from scratch for this game.
- */
- public void initBoard() {
- board = new ServerBoard(referee, logfile);
- board.makeRandomTerrain();
- for (int p = 0; p < player.length; p++) {
- board.placePlayer(Symbols.PLAYER_SYMBOL[p]);
- board.placePlayer(Symbols.PLAYER_SYMBOL[p]);
- }
-
- for (int p = 0; p < player.length; p++)
- initPlayer(p);
- }
-
- /**
- * return a new player socket. used to test the game thread outside the
- * full server, as a standalone application.
- */
- static public Socket getPlayer(ServerSocket ss) {
- // accept an incoming connection
- Socket s = null;
- try {
- s = ss.accept();
- } catch (Exception e) {
- System.out.println( "accepting connection: " + e );
- e.printStackTrace();
- System.exit(1);
- }
-
- return s;
- }
-
- /**
- * place the given player on the game board, and set up an input thread
- * for the player
- * @param p the player number of the player to set up.
- */
- public void initPlayer(int p) {
- // Set up a receiver to get player commands
- if (receiver == null)
- receiver = new Piper[player.length];
-
- try {
- receiver[p] = new Piper(board, player[p].getInputStream(),
- gameId, p, logfile);
- }
- catch (Exception e) {
- log("lost player " + p + " while setting up.");
- return;
- }
-
- // send the player a game init
- GameInit ginit = new GameInit(Symbols.PLAYER_SYMBOL[p],
- GameInit.DEFAULT_COLORS);
-
- OutputStream out = null;
- try {
- out = player[p].getOutputStream();
- ginit.writeTo(out);
- } catch (Exception e) {
- log("lost player " + p + " while sending game init.");
- }
-
- // send the player a terrain init
- TerrainInit tinit = new TerrainInit(board);
- try {
- tinit.writeTo(out);
- } catch (Exception e) {
- log("lost player " + p + " while sending game init.");
- }
-
- }
-
- /**
- * a mainline that allows this to run as a standalone application to debug
- * the game thread outside of the full server
- */
- public static void main(String argv[]) {
- ServerSocket ss = null;
- int PORT = 5000;
-
- Logger logfile = new Logger();
-
- // open a new server socket on port PORT
- try {
- ss = new ServerSocket(PORT);
- } catch (Exception e) {
- logfile.log("couldn't start server; port #" + PORT + " in use");
- System.exit(1);
- }
-
- int numPlayers = 2;
- Socket player[] = new Socket[numPlayers];
-
- for (int i = 0; i < numPlayers; i++) {
- logfile.log("waiting for player " + i);
- player[i] = getPlayer(ss);
- }
-
- BattleGame server = new BattleGame(player, 0, new DummyRef(), logfile);
- server.start();
- }
-
- /**
- * start up the server game thread
- */
- public void start() {
- if (gameThread != null)
- return;
-
- // initialize the game board
- initBoard();
-
- // give the clients a pause to set up the game.
- try {
- Thread.currentThread().sleep(1000);
- } catch ( Exception e ) {
- log("interrupted while trying to give clients time to start up");
- }
-
- gameThread = new Thread(this);
- gameThread.setPriority(Thread.MAX_PRIORITY);
- gameThread.start();
- }
-
- /**
- * halt this game
- */
- public void stop() {
- try {
- // brief pause in case anything needs to wrap up
- Thread.sleep(500);
- }
- catch (Exception e) {
- }
-
- log("all the players are dead. game over");
- referee.gameOver();
- gameThread.stop();
- }
-
-
-
- /**
- * the main game loop is in the run thread. it updates the board, sends
- * all the players the differences, and tris at all times to maintain
- * a constant game pace.
- */
- public void run() {
- long startTime = System.currentTimeMillis();
- long oldtime, lastPacketTime, diff;
- boolean gameover = false;
-
- lastPacketTime = startTime;
- while (true) {
- // update the board an immediately send the results to all
- // the players (all one of them)
- oldtime = System.currentTimeMillis();
- startTime = oldtime;
-
- board.update();
- oldtime = howMuchTime(oldtime, "updating board");
-
- boolean atLeastOnePlayer = false;
- for (int p = 0; p < player.length; p++) {
- if (player[p] == null)
- continue;
-
- atLeastOnePlayer = true;
-
- oldtime = System.currentTimeMillis();
-
- byte message = TurnDiff.NORMAL;
-
- gameover = board.isGameOver();
- if (gameover) {
- log("SENDING GAMEOVER");
- message = TurnDiff.GAMEOVER;
- }
-
- TurnDiff diffp = new TurnDiff(board, Symbols.PLAYER_SYMBOL[p],
- message);
- diffp.makeBytes();
-
- oldtime = howMuchTime(oldtime, "making diff packet");
-
-
- try {
- diffp.writeTo(out[p]);
- } catch ( Exception e ) {
- log("player " + p + " has vanished.");
- player[p] = null;
- board.killPlayer(p);
- }
- oldtime = howMuchTime(oldtime, "sending turn diff");
- }
-
- if (!atLeastOnePlayer || gameover) {
- stop();
- return;
- }
-
- // compute how long it has been since we started working on this
- // turn
- diff = System.currentTimeMillis() - startTime;
- lastPacketTime = startTime;
-
- if (diff > DELAY) {
- // we've gone overbudget on time
- log("** warning: update and communication > " +
- DELAY + " = "+diff);
- try {
- // pause to give other threads a chance to run
- Thread.sleep(10);
- }
- catch (Exception e) {
- }
- } else {
- // twiddle thumbs until we're allowed to send the packet
- try {
- long sleepFor = DELAY-diff;
- long time = System.currentTimeMillis();
- System.gc();
- long gcTime = System.currentTimeMillis() - time;
- if (sleepFor - gcTime > 0)
- Thread.currentThread().sleep(sleepFor-gcTime);
- else
- log("GC threw game over budget: "+(sleepFor - gcTime));
- } catch (Exception e) {
- log("interrupted delaying for turn");
- }
- }
-
- long actualTime = System.currentTimeMillis() - startTime;
- // uncomment for more info on timing
- //log("actual time spent on turn: "+actualTime);
- }
- }
-
- /**
- * a function to time portions of the server to debug problems meeting
- * the time bound
- */
- public long howMuchTime(long oldtime, String msg) {
- long time = System.currentTimeMillis();
- // uncomment for more timing info
- //log(msg + ": " + (time - oldtime));
- return time;
- }
- }
-