import java.awt.*; import java.awt.event.*; import dbg.*; import dbg.GamePackage.*; import playerWindow.*; // CORBA packages import org.omg.CORBA.*; import org.omg.CosNaming.NamingContextPackage.*; import org.omg.CosNaming.*; /** * Class PlayerImpl implements the interface Player (through extending PlayerPOA, * which adds CORBA functionality to the object). * * It also implements the interface PlayerWindowListener, to catch events * sent by the PlayerWindow. * * @author Erik Brandstadmoen 20010928 */ public class PlayerImpl extends PlayerPOA implements PlayerWindowListener, WindowListener { /* Constructors */ /** * This constructor is just for testing. It just prints out the * supplied message * * @param message The message to print out */ public PlayerImpl(String testMessage){ System.out.println(testMessage); } /** * This constructor is used if one wishes to have a federation * of game servers. Then we need a reference to the naming service * to fetch a list of available servers from. * @param pw The player window which communicates with the player * @param gs The gameserver to contact * @param orb The orb to contact */ public PlayerImpl(PlayerWindow pw, Gameserver gs, ORB orb){ this(pw, gs); this.orb = orb; // We do not want the alive-checking to start yet. We are not // registered at a gameserver. this.shouldCheckIfServerIsAlive = false; new Thread(){ public void run(){ while(true){ if(shouldCheckIfServerIsAlive) if(!serverIsAlive()){ opponent.disableServerCheck(); changeServer(); opponent.enableServerCheck(); } try{ Thread.sleep(5000); }catch (InterruptedException ie){} } }}.start(); // For testing Gameserver[] availableServers = getAvailableGameServers(); for (int i=0; i < availableServers.length; i++){ System.out.println("Server "+i+": "+availableServers[i]); } // End testing } /** * This is the preferred constructor to call, it sets both the player window * and the gameserver. * * @param pw The player window which communicates with the player * @param gs The gameserver to contact */ public PlayerImpl(PlayerWindow pw, Gameserver gs){ this.setPlayerWindow(pw); this.setGameServer(gs); /* For testing only */ //this.dummy_init(); } /** * This constructor just sets the gameserver. * * @param gs The gameserver to contact */ public PlayerImpl(Gameserver gs){ this(null,gs); } /** * This constructor just sets the player window. * * @param pw The player window to use */ public PlayerImpl(PlayerWindow pw){ this(pw, null); } /** * This constructor creates an "empty" object. You need to * explicitly call both setPlayerWindow() and setGameServer(). * */ public PlayerImpl(){ this(null,null); } /* Accessors */ /** * Returns the player window assiciated with this PlayerImpl object. * *@return The current player window */ public PlayerWindow getPlayerWindow(){ return playerWindow; } /** * Sets the current player window for this object. * *@param pw The player window to use */ public void setPlayerWindow(PlayerWindow pw){ this.playerWindow = pw; } /** * Returns the gameserver associated with this object * *@return The gameserver currently associated with the object. */ public Gameserver getGameServer(){ return gameserver; } /** * Associates a gameserver with this player object * *@param gs The new gameserver */ public void setGameServer(Gameserver gs){ this.gameserver=gs; } /* CORBA methods */ /** * Gets a list of all the available game servers */ private Gameserver [] getAvailableGameServers(){ Gameserver [] retVal = null; NamingContext nc = null; try { nc = NamingContextHelper.narrow( orb.resolve_initial_references("NameService")); } catch(org.omg.CORBA.BAD_PARAM ex) { System.err.println("\"NameService\" is not " + "a NamingContext object reference"); } catch(org.omg.CORBA.ORBPackage.InvalidName in){ System.out.println("No such name: \"NameService\": "+ in.getMessage()); } BindingListHolder bl = new BindingListHolder(); BindingIteratorHolder bi = new BindingIteratorHolder(); nc.list(100, bl, bi); retVal = new Gameserver[bl.value.length]; NameComponent[] aName; for(int i = 0 ; i < bl.value.length ; i++){ aName = bl.value[i].binding_name; try{ retVal[i] = GameserverHelper.narrow(nc.resolve(aName)); }catch(NotFound ex) { System.err.print("Got a `NotFound' exception ("); switch(ex.why.value()) { case NotFoundReason._missing_node: System.err.print("missing node"); break; case NotFoundReason._not_context: System.err.print("not context"); break; case NotFoundReason._not_object: System.err.print("not object"); break; } System.err.println(")"); ex.printStackTrace(); } catch(UserException ue){ System.out.println("PlayerImpl.getAvailableServers(): "+ ue.getMessage()); } } if(bi == null) throw new RuntimeException(); return retVal; } /** * Checks if the game object is alive, and, if not, assume the * server is down, and contact another server. */ private boolean serverIsAlive(){ boolean alive = false; try{ alive = (this.game != null && !this.game._non_existent()); }catch(org.omg.CORBA.COMM_FAILURE ccf){ alive = false; }catch(org.omg.CORBA.TRANSIENT t){ alive = false; } if (!alive) System.out.println("Game server is dead"); else System.out.print("."); return alive; } /** * Locates an available server and registers this game to that server * as an 'ongoing game'. Reassigns values to the global references * gameserver and game (which are remote objects). Also gives theese new * references to the opponent. */ private void changeServer(){ this.playerWindow.disableBoard(); this.playerWindow.message("Locating new server..."); Gameserver[] availableServers = getAvailableGameServers(); if(availableServers.length == 0){ this.playerWindow.message("No available server. Try later!"); return; // this makes the opponent enable serverchecking, and eventually // finding that the server is down. } //Selects the first available server. //It should be possible to select the "closest" server, or the server //with the smallest number of ongoing games. Can the nameservice handle //this? Well, this works for now: this.gameserver = availableServers[0]; this.game = gameserver.register_ongoing_game(_this(orb), opponent, current_position); opponent.setGame(game); opponent.setServer(gameserver); this.playerWindow.message("Changed server"); if(myturn) this.playerWindow.enableBoard(); } /* Methods that communicate with the player window. */ /** * Called when the user clicks on the board * * @param e The event that happened */ public void onBoardClicked(PlayerWindowEvent e){ playerWindow.disableBoard(); /* Send the click to the game */ try{ game.place_piece((short)e.getX(),(short)e.getY(), this.localColor); } catch(dbg.GamePackage.Not_your_turn nyt){ playerWindow.message("Not your turn! "+nyt.message); } catch(dbg.GamePackage.Not_allowed_to_place_now natpn){ playerWindow.message("Not allowed to place now! "+natpn.message); } catch(dbg.GamePackage.Square_occupied so){ playerWindow.message("Square Occupied! "+so.message); } /* For testing */ //pos.the_board[e.getX()][e.getY()] = square.from_int(this.localColor.value()); //new_position(pos); } /** * Called when the user exits the game * * Notifies the GameServer object that this player doesn't want to play anymore * and exits * * @param e The event that happened */ public void onExitGame(PlayerWindowEvent e){ if (game == null) { playerWindow.dispose(); System.out.println("Death by Schwamba!"); System.exit(0); } else { playerWindow.disableBoard(); gracefulExit(); } } /** * Called when the user starts a new game. If the checker color * turns out red, there is something wrong. * (The color assigned by the server is then something other than black or white) * * @param e The event that happened */ public void onNewGame(PlayerWindowEvent e){ /* Disable the board to avoid that the player sends events */ playerWindow.disableBoard(); playerWindow.resetBoard(); /* Start a new game */ this.myturn = false; // initially not my turn playerWindow.message("Waiting for another player."); colorHolder colorholder = new colorHolder(); if (game != null){ game.player_finished(_this()); } this.game = gameserver.register_player(_this(), colorholder); this.localColor = colorholder.value; this.shouldCheckIfServerIsAlive = true; if(localColor.value() == color._black){ playerWindow.message("Found other player. Wait..."); playerWindow.setCheckerColor(Color.black); }else if(localColor.value() == color._white){ playerWindow.setCheckerColor(Color.white); }else{ // Indicates that something is wrong. playerWindow.setCheckerColor(Color.red); } } /* Methods that communicate with the Game object */ /** * Called from the Game when the game position changes * * @param the_position The new position */ public void new_position(position the_position){ /* Save the gamestate */ this.current_position = the_position; /* Disable the board while filling it */ playerWindow.disableBoard(); /* Fill the board with the correct checkers */ fillBoard(the_position); /* Enable the board to that the player can move again if it is his turn */ if (the_position.whos_turn.value() == this.localColor.value()){ myturn = true; playerWindow.enableBoard(); playerWindow.message("Your turn!!!"); }else { myturn = false; playerWindow.disableBoard(); playerWindow.message("Wait...(It's not your turn)"); } } /** * Called from the Game when the game is finished for some reason. * This could either be because the other user exited, or when the game * is over. * *@param winner The square that won the game */ public void game_over(square winner){ this.shouldCheckIfServerIsAlive = false; playerWindow.disableBoard(); if(winner.value() == 2){ playerWindow.message("Game was interrupted"); } else if(winner.value() == localColor.value()){ playerWindow.message("You're a winner!!!"); } else if(winner.value() != localColor.value()){ playerWindow.message("You're a looooooser!"); } gameserver.removeGame(game); // Set the game reference to null so that it can be collected by // the garbage collector game = null; opponent = null; } /** * Signal from gameserver that this player has been transfered to another * server whith a "waiting a player". This player then have to change color to * black. */ public void onRemoteServer(boolean moved1){ moved = moved1; // no need for this variabale at all. //changing color to black when moving to another server: this.localColor = color.black; playerWindow.setCheckerColor(Color.black); } public void setGame (Game game1){ this.game = game1; } public void setOpponent ( Player opponent ) { this.opponent = opponent; } public void setServer(Gameserver gameserver1){ this.gameserver = gameserver1; } public void enableServerCheck(){ this.playerWindow.message("Changed server"); if(myturn) playerWindow.enableBoard(); this.shouldCheckIfServerIsAlive = true; } public void disableServerCheck(){ this.shouldCheckIfServerIsAlive = false; this.playerWindow.disableBoard(); this.playerWindow.message("Opponent locating new server.."); } /* Methods that catch window events (from interface java.awt.event.WindowListener) */ public void windowOpened(WindowEvent e){ } public void windowClosing(WindowEvent e){ gracefulExit(); } public void windowClosed(WindowEvent e){ } public void windowIconified(WindowEvent e){ } public void windowDeiconified(WindowEvent e){ } public void windowActivated(WindowEvent e){ } public void windowDeactivated(WindowEvent e){ } /* Private/protected methods */ /** * Gracefully exits */ protected void gracefulExit(){ playerWindow.message("Exiting..."); gameserver.player_done(_this(orb)); if(game != null) game.player_finished(_this()); gameserver.checkWaitingPlayer(_this()); playerWindow.dispose(); System.out.println("Death by Schwamba!"); System.exit(0); } /** * Fills the board of the associated player window with the pieces * from the current position. * * @param pos The position to place */ protected void fillBoard(position pos){ for (int i=0; i < pos.the_board.length; i++){ for (int j=0; j < pos.the_board[i].length; j++){ if (pos.the_board[i][j].value()==square._new_white){ playerWindow.placeChecker(Color.white, i, j); }else if (pos.the_board[i][j].value()==square._new_black){ playerWindow.placeChecker(Color.black, i, j); } } } } /* Local variables */ /** * The orb containing the naming service to get a list * of other servers from. */ ORB orb = null; /** * Says whether to check if the server is alive. * This is diabled from the other player to make sure * not both players try to connect to another server at the * same time, and everything gets messed up. */ protected boolean shouldCheckIfServerIsAlive = false; /** * The opponent */ protected Player opponent; /** * The user interface associated with this player object. */ protected PlayerWindow playerWindow; /** * The server to contact to start a new game */ protected Gameserver gameserver; /** * The game currently playing */ protected Game game; /** * True if it is this player's turn. This is used to finding out if * the board should be enabled or not after a serverchange */ private boolean myturn = false; /** * The color this client is playing with. */ protected color localColor; protected boolean moved = false; /** * Need a position object to save the state of the game in case of * the gameserver going down. */ private position current_position; /* TESTING STUFF */ /** * For testing purposes only */ private void dummy_init(){ //Make a square array for the board square[][] board = new square[12][12]; for (int i=0; i < 12; i++){ for (int j=0; j < 12; j++){ board[i][j] = square.none; } } // Fill a position object pos = new position(color.from_int(color._white),board); } /** * Just for testing purposes */ private position pos; }