Coverage report

  %line %branch
org.cb.jset.JSetGameImpl$GameConnectionImpl
0% 
0% 

 1  
 // START LICENSE
 2  
 // JSet - a Java JSet card board game implementation
 3  
 // Copyright (C) 2004 Jerome Lacoste
 4  
 //
 5  
 // This program is free software; you can redistribute it and/or modify
 6  
 // it under the terms of the GNU General Public License as published by
 7  
 // the Free Software Foundation; either version 2 of the License, or (at
 8  
 // your option) any later version.
 9  
 //
 10  
 // This program is distributed in the hope that it will be useful, but
 11  
 // WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU General Public License
 16  
 // along with this program; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 18  
 // END LICENSE
 19  
 
 20  
 package org.cb.jset;
 21  
 
 22  
 import org.apache.log4j.Logger;
 23  
 import org.cb.util.Assert;
 24  
 
 25  
 import java.io.Serializable;
 26  
 import java.util.Timer;
 27  
 import java.util.TimerTask;
 28  
 
 29  
 /**
 30  
  * @author jerome@coffeebreaks.org - last modified by $LastChangedBy: jerome $
 31  
  * @version $Id: JSetGameImpl.java 129 2004-04-15 05:00:43Z jerome $
 32  
  */
 33  
 public class JSetGameImpl implements SetGame
 34  
 {
 35  
   private Logger _logger = Logger.getLogger(this.getClass().getName());
 36  
   private static int gameId = 0;
 37  
 
 38  
   private String _name;
 39  
   private int _id;
 40  
   private Players _players;
 41  
   private final CardStack _cardStack = new CardStack();
 42  
   private SetGameBoard _board = new BoardImpl();
 43  
 
 44  
   private GameThread _gameThread;
 45  
   private Timer _turnTimer;
 46  
   private boolean _endedTurn = false;
 47  
   private int _turnLengthInSeconds = 60 * 3;
 48  
 
 49  
   /**
 50  
    * Keeps track of the various connected players for that game.
 51  
    */
 52  
   class Players
 53  
   {
 54  
     private GameConnectionImpl[] _players;
 55  
     private int _nbPlayers;
 56  
 
 57  
     /**
 58  
      * Contructs an instance of Players, given the specified required number of playes.
 59  
      * @param nbPlayers the number of players for that game.
 60  
      */
 61  
     Players(final int nbPlayers)
 62  
     {
 63  
       _players = new GameConnectionImpl[nbPlayers];
 64  
       _nbPlayers = 0;
 65  
     }
 66  
 
 67  
     /**
 68  
      * @return the maximum number of players.
 69  
      */
 70  
     synchronized int getMaxNbPlayers()
 71  
     {
 72  
       return _players.length;
 73  
     }
 74  
 
 75  
     /**
 76  
      * @return the current number of registered players.
 77  
      */
 78  
     synchronized int getCurrentNbPlayers()
 79  
     {
 80  
       return _nbPlayers;
 81  
     }
 82  
 
 83  
     /**
 84  
      * Adds the specified player to the game.
 85  
      * @param player the player to add
 86  
      * @return the game connection related to that player.
 87  
      */
 88  
     synchronized GameConnectionImpl addPlayer(final SetGamePlayer player)
 89  
     {
 90  
       Assert.assertTrue(_nbPlayers < _players.length, "All players are already registered.");
 91  
 
 92  
       if (_logger.isInfoEnabled())
 93  
       {
 94  
         _logger.info("Players.addPlayer: current (" + _nbPlayers + " of " + _players.length + ")");
 95  
       }
 96  
       final GameConnectionImpl connection = new GameConnectionImpl(player);
 97  
       _players[_nbPlayers] = connection;
 98  
       _nbPlayers++;
 99  
       return connection;
 100  
     }
 101  
 
 102  
     /**
 103  
      * Removes the specified player from the game.
 104  
      * @param player the player to remove
 105  
      */
 106  
     synchronized void removePlayer(final SetGamePlayer player)
 107  
     {
 108  
       if (_logger.isInfoEnabled())
 109  
       {
 110  
         _logger.info("Players.removePlayer: current (" + _nbPlayers + " of " + _players.length + ")");
 111  
       }
 112  
       for (int i = 0; i < _players.length; i++)
 113  
       {
 114  
         final GameConnectionImpl gameConnection = _players[i];
 115  
         if (gameConnection != null)
 116  
         {
 117  
           if (gameConnection.getPlayer() == player)
 118  
           {
 119  
             // FIXME check that strange test
 120  
             if (!gameConnection.isClosed() && !(gameConnection.getPlayer() == null))
 121  
             {
 122  
               final String msg = "Implementation error: removing player while GC is non closed and/or gameConnection.getPlayer() not null";
 123  
               _logger.error(msg);
 124  
               throw new IllegalStateException(msg);
 125  
             }
 126  
             _players[i] = null;
 127  
             _nbPlayers--;
 128  
             return;
 129  
           }
 130  
         }
 131  
       }
 132  
       // not found?
 133  
     }
 134  
 
 135  
     /**
 136  
      * Fire a cards added event for all registered players.
 137  
      * @see SetGamePlayer#cardsAdded(org.cb.jset.CardProperties[])
 138  
      * @param cards the added cards
 139  
      */
 140  
     void fireAddedCardsEvent(final CardProperties[] cards)
 141  
     {
 142  
       if (_logger.isInfoEnabled())
 143  
       {
 144  
         _logger.info("Notifying players of added cards on board " + CardProperties.toString(cards));
 145  
       }
 146  
       for (int i = 0; i < _nbPlayers; i++)
 147  
       {
 148  
         Assert.assertTrue(_players[i] != null && _players[i].getPlayer() != class="keyword">null,
 149  
                           "Null player or null GameConnectionImpl");
 150  
         final SetGamePlayer player = _players[i].getPlayer();
 151  
         player.cardsAdded(cards);
 152  
       }
 153  
     }
 154  
 
 155  
     /**
 156  
      * Fire a set removed event for all registered players.
 157  
      * @see SetGamePlayer#setRemoved(org.cb.jset.CardSet)
 158  
      * @param set the removed set
 159  
      */
 160  
     void fireRemovedSetEvent(final CardSet set)
 161  
     {
 162  
       if (_logger.isInfoEnabled())
 163  
       {
 164  
         _logger.info("Notifying players of removed set from board" + CardProperties.toString(set.getCards()));
 165  
       }
 166  
       for (int i = 0; i < _nbPlayers; i++)
 167  
       {
 168  
         Assert.assertTrue(_players[i] != null && _players[i].getPlayer() != class="keyword">null,
 169  
                           "Null player or null GameConnectionImpl");
 170  
         final SetGamePlayer player = _players[i].getPlayer();
 171  
         player.setRemoved(set);
 172  
       }
 173  
 //      endTurn();  // FIXME - not exactly the spec...
 174  
     }
 175  
 
 176  
     /**
 177  
      * @return <code>true</code> if all expected players are connected.
 178  
      */
 179  
     synchronized boolean allConnected()
 180  
     {
 181  
       return _nbPlayers == _players.length;
 182  
     }
 183  
 
 184  
     /**
 185  
      * @return <code>true</code> if no players are connected.
 186  
      */
 187  
     synchronized boolean noneConnected()
 188  
     {
 189  
       return _nbPlayers == 0;
 190  
     }
 191  
 
 192  
     /**
 193  
      * Force the closing of the connections for all registered players.
 194  
      * @see GameConnectionImpl#internalClose()
 195  
      */
 196  
     synchronized void close()
 197  
     {
 198  
       // check that game is being closed?
 199  
 
 200  
       // note: going reverse on the _nbPlayers index, as it is being modified
 201  
       for (int i = _nbPlayers - 1; i >= 0; i--)
 202  
       {
 203  
         synchronized (_players[i])
 204  
         {
 205  
           if (!_players[i].isClosed())
 206  
           {
 207  
             _players[i].internalClose();
 208  
           }
 209  
           _nbPlayers--;
 210  
         }
 211  
       }
 212  
     }
 213  
   }
 214  
 
 215  
   /**
 216  
    * A GameConnectionImpl instance acts as a broker between a Player and the Game
 217  
    * Useful to keep track which player does what.
 218  
    * @todo we should probably turn this inner into a static inner class and make the reference
 219  
    * to the associated Game transient
 220  
    */
 221  
   public class GameConnectionImpl implements SetGameConnection, Serializable, Runnable
 222  
   {
 223  
     private transient SetGamePlayer _player;
 224  0
     private boolean _isClosed = false;
 225  
     // FIXME we probably don't need threads in local connections...
 226  
     private Thread _me;
 227  
     private final transient Logger _logger;
 228  
 
 229  
     /**
 230  
      * Creates a new instance of GameConnectionImpl
 231  
      */
 232  
     public GameConnectionImpl(final SetGamePlayer player)
 233  0
     {
 234  0
       _logger = Logger.getLogger("GCon-" + player.getName());
 235  0
       _player = player;
 236  0
       _me = new Thread(this);
 237  0
       init();
 238  0
     }
 239  
 
 240  
     void init()
 241  
     {
 242  
       //            _me.start();
 243  0
     }
 244  
 
 245  
     public void matchSet(final CardSet set) throws MatchingException
 246  
     {
 247  0
       if (_logger.isInfoEnabled())
 248  
       {
 249  0
         _logger.info(getPlayer().getName() + " try to match a set: " + set);
 250  
       }
 251  
       try
 252  
       {
 253  0
         removeMatchedSet(set);
 254  
       }
 255  0
       catch (MatchingException e)
 256  
       {
 257  0
         _logger.error("removeSet() failed: ", e);
 258  0
         _logger.error("Blocking player");
 259  0
         getPlayer().block();
 260  0
         throw e;
 261  
       }
 262  0
       catch(BoardException e)
 263  
       {
 264  0
         _logger.error("removeSet() failed: ", e);
 265  0
         _logger.error("Blocking player");
 266  0
         getPlayer().block();
 267  0
         throw new MatchingException(e.getMessage());
 268  0
       }
 269  0
     }
 270  
 
 271  
     SetGamePlayer getPlayer()
 272  
     {
 273  0
       return _player;
 274  
     }
 275  
 
 276  
     // FIXME not clean: if called directly, _players doens't know about it
 277  
     // add a parameter: with call back.
 278  
     public void close()
 279  
     {
 280  0
       internalClose();
 281  0
       endGame();
 282  0
     }
 283  
 
 284  
     void internalClose()
 285  
     {
 286  0
       if (_logger.isInfoEnabled())
 287  
       {
 288  0
         _logger.info("Closing connection for player - " + this);
 289  
       }
 290  
       // final Thread meTemp = _me;
 291  0
       synchronized (this)
 292  
       {
 293  0
         _me = null;
 294  0
         _isClosed = true;
 295  0
         notify();
 296  0
       }
 297  
       /*
 298  
       while (meTemp.isAlive()) {
 299  
     try{
 300  
         _logger.info("Wait for thread to Die...");
 301  
         Thread.currentThread().sleep(500);
 302  
     } catch  (InterruptedException e) {}
 303  
       }
 304  
       */
 305  
 
 306  
       // FIXME we could avoid calling back those who asked for disconnection or encountered a failure.
 307  0
       _logger.info("Trying to disconnect properly....");
 308  0
       _player.disconnect();
 309  0
       _logger.info("Disconnected properly....");
 310  0
     }
 311  
 
 312  
     public synchronized boolean isClosed()
 313  
     {
 314  0
       return _isClosed;
 315  
     }
 316  
 
 317  
     public void run()
 318  
     {
 319  0
       if (_logger.isInfoEnabled())
 320  
       {
 321  0
         _logger.info("Starting GameConnectionImpl thread");
 322  
       }
 323  0
       final Thread thisThread = Thread.currentThread();
 324  0
       while (_me == thisThread)
 325  
       {
 326  0
         synchronized (this)
 327  
         {
 328  
           try
 329  
           {
 330  0
             wait();
 331  
           }
 332  0
           catch (InterruptedException e)
 333  
           {
 334  0
             _logger.error("boum ", e);
 335  0
           }
 336  0
         }
 337  
       }
 338  0
       if (_logger.isInfoEnabled())
 339  
       {
 340  0
         _logger.info("Ending GameConnectionImpl thread");
 341  
       }
 342  0
     }
 343  
   }
 344  
 
 345  
 
 346  
   public JSetGameImpl(final String name, class="keyword">final int nbPlayers)
 347  
   {
 348  
     _name = name;
 349  
     _players = new Players(nbPlayers);
 350  
     _id = gameId++;
 351  
     _gameThread = new GameThread();
 352  
 
 353  
     _board.addBoardListener(new SetGameBoardListener()
 354  
     {
 355  
       public void cardsAdded(final CardProperties[] cards)
 356  
       {
 357  
         notifyAddedCards(cards);
 358  
       }
 359  
 
 360  
       public void setRemoved(final CardSet set)
 361  
       {
 362  
         notifyRemovedSet(set);
 363  
       }
 364  
     });
 365  
   }
 366  
 
 367  
   class GameThread extends Thread
 368  
   {
 369  
     public void run()
 370  
     {
 371  
       final Thread thisThread = Thread.currentThread();
 372  
 
 373  
       waitForAllPlayersToConnect();
 374  
 
 375  
 /*
 376  
         // precondition
 377  
         if (! _players.allConnected()) {
 378  
 	String msg = "Implementation error: Not all players registered!";
 379  
 	if (_logger.isInfoEnabled())
 380  
 	_logger.info(msg);
 381  
 	throw new IllegalStateException(msg);
 382  
 	}*/
 383  
 
 384  
       if (_logger.isInfoEnabled())
 385  
       {
 386  
         _logger.info("-- starting game --");
 387  
       }
 388  
 
 389  
       int turn = 0;
 390  
       while (_gameThread == thisThread &&
 391  
              !_cardStack.empty() && _players.allConnected())
 392  
       {
 393  
 
 394  
         if (_logger.isInfoEnabled())
 395  
         {
 396  
           _logger.info("-- starting turn " + turn++ + " --");
 397  
         }
 398  
 
 399  
         int nbCardToAdd = 3;
 400  
         if (_board.getNbCards() < 12)
 401  
         {
 402  
           nbCardToAdd = Math.min(12 - _board.getNbCards(), _cardStack.size());
 403  
         }
 404  
         pushCardOnBoard(nbCardToAdd);
 405  
 
 406  
         startTurn();
 407  
 
 408  
         waitForTurnToEnd();
 409  
 
 410  
         if (_logger.isInfoEnabled())
 411  
         {
 412  
           _logger.info("-- ending turn --");
 413  
         }
 414  
       }
 415  
       if (_logger.isInfoEnabled())
 416  
       {
 417  
         _logger.info("-- Ending Game -- ");
 418  
       }
 419  
       // FIXME we probably don't want to terminate here.
 420  
       // we want the user to play until he is tired or there is a timeout.
 421  
       terminate();
 422  
     }
 423  
   }
 424  
 
 425  
   public void start()
 426  
   {
 427  
     _cardStack.shuffle();
 428  
     _gameThread.setDaemon(true);
 429  
     _gameThread.start();
 430  
   }
 431  
 
 432  
   public int getTurnLength()
 433  
   {
 434  
     return _turnLengthInSeconds;
 435  
   }
 436  
 
 437  
   public void setTurnLength(final int turnInSeconds)
 438  
   {
 439  
     _turnLengthInSeconds = turnInSeconds;
 440  
   }
 441  
 
 442  
   void startTurn()
 443  
   {
 444  
     _endedTurn = false;
 445  
     final int seconds = getTurnLength();
 446  
     _turnTimer = new Timer();
 447  
     _turnTimer.schedule(new TimerTask()
 448  
     {
 449  
       public void run()
 450  
       {
 451  
         endTurn();
 452  
 //        _turnTimer.cancel(); //Terminate the timer thread
 453  
       }
 454  
     }, seconds * 1000);
 455  
   }
 456  
 
 457  
   synchronized boolean endedTurn()
 458  
   {
 459  
     return _endedTurn;
 460  
   }
 461  
 
 462  
   synchronized void endGame()
 463  
   {
 464  
     if (_logger.isInfoEnabled())
 465  
     {
 466  
       _logger.info("endGame()!");
 467  
     }
 468  
     _gameThread = null;
 469  
     notifyAll();
 470  
   }
 471  
 
 472  
   synchronized void endTurn()
 473  
   {
 474  
     if (_logger.isInfoEnabled())
 475  
     {
 476  
       _logger.info("Turn's up!");
 477  
     }
 478  
     _endedTurn = true;
 479  
     _turnTimer.cancel();
 480  
     notifyAll();
 481  
   }
 482  
 
 483  
   private void waitForAllPlayersToConnect()
 484  
   {
 485  
     synchronized (this)
 486  
     {
 487  
       while (!_players.allConnected())
 488  
       {
 489  
         try
 490  
         {
 491  
           wait();
 492  
         }
 493  
         catch (InterruptedException e)
 494  
         {
 495  
         }
 496  
       }
 497  
     }
 498  
   }
 499  
 
 500  
   /*
 501  
   private void waitForAllPlayersToBeDisconnected() {
 502  
       if (_logger.isInfoEnabled())
 503  
           _logger.info("waitForAllPlayersToBeDisconnected");
 504  
       synchronized  (this) {
 505  
           while (! _players.noneConnected()) {
 506  
               if (_logger.isInfoEnabled())
 507  
                   _logger.info("nbPlayersConnected:" + _players.getCurrentNbPlayers());
 508  
               try {
 509  
                   wait();
 510  
               } catch (InterruptedException e) {
 511  
                   _logger.error("IE",e);  // FIXME - cleanup
 512  
               }
 513  
           }
 514  
       }
 515  
   }
 516  
   */
 517  
   void waitForTurnToEnd()
 518  
   {
 519  
     while (!endedTurn())
 520  
     {
 521  
       synchronized (this)
 522  
       {
 523  
         try
 524  
         {
 525  
           wait();
 526  
         }
 527  
         catch (InterruptedException e)
 528  
         {
 529  
           _logger.error("IE", e);   // FIXME - cleanup
 530  
         }
 531  
       }
 532  
     }
 533  
   }
 534  
 
 535  
   private void pushCardOnBoard(final int nbCards)
 536  
   {
 537  
     // precondition
 538  
     if (nbCards > _cardStack.size())
 539  
     {
 540  
       final String msg = "Can't add more cards than present in the stack!";
 541  
       if (_logger.isInfoEnabled())
 542  
       {
 543  
         _logger.info(msg);
 544  
       }
 545  
       throw new IllegalStateException(msg);
 546  
     }
 547  
 
 548  
     final CardProperties[] cardsToAdd = _cardStack.pop(nbCards);
 549  
     if (_logger.isInfoEnabled())
 550  
     {
 551  
       _logger.info("-- adding " + nbCards + " on board --");
 552  
       _logger.info("-- " + CardProperties.toString(_board.getCards()) +
 553  
                    " - " + CardProperties.toString(cardsToAdd) + " --");
 554  
     }
 555  
     _board.addCards(cardsToAdd);
 556  
   }
 557  
 
 558  
   private void removeMatchedSet(final CardSet set) throws MatchingException, BoardException
 559  
   {
 560  
     if (_logger.isInfoEnabled())
 561  
     {
 562  
       if (_logger.isInfoEnabled())
 563  
       {
 564  
         _logger.info("trying to remove a set:" + CardProperties.toString(set.getCards()));
 565  
       }
 566  
     }
 567  
     _board.matchSet(set);
 568  
 //            if (_logger.isInfoEnabled())
 569  
 //                _logger.info("CardSet removed");
 570  
   }
 571  
 
 572  
   public void terminate()
 573  
   {
 574  
     if (_logger.isInfoEnabled())
 575  
     {
 576  
       _logger.info("Closing connections");
 577  
     }
 578  
     _players.close();
 579  
     //         waitForAllPlayersToBeDisconnected();
 580  
     // disconnecting from JSetGameServer?
 581  
     // FIXME
 582  
   }
 583  
 
 584  
   public String getName()
 585  
   {
 586  
     return _name;
 587  
   }
 588  
 
 589  
   public int getCurrentNbPlayers()
 590  
   {
 591  
     return _players.getCurrentNbPlayers();
 592  
   }
 593  
 
 594  
   public int getMaxNbPlayers()
 595  
   {
 596  
     return _players.getMaxNbPlayers();
 597  
   }
 598  
 
 599  
   public SetGameConnection connect(final SetGamePlayer player)
 600  
   {
 601  
     if (_logger.isInfoEnabled())
 602  
     {
 603  
       _logger.info("Connecting player:" + player.getName() + ">");
 604  
     }
 605  
     final GameConnectionImpl connection;
 606  
     synchronized (this)
 607  
     {
 608  
       connection = _players.addPlayer(player);
 609  
       if (_logger.isInfoEnabled())
 610  
       {
 611  
         _logger.info("Added player " + player);
 612  
       }
 613  
       notifyAll();
 614  
     }
 615  
     return connection;
 616  
   }
 617  
 
 618  
   void notifyAddedCards(final CardProperties[] cards)
 619  
   {
 620  
     _players.fireAddedCardsEvent(cards);
 621  
   }
 622  
 
 623  
   void notifyRemovedSet(final CardSet set)
 624  
   {
 625  
     _players.fireRemovedSetEvent(set);
 626  
   }
 627  
 
 628  
   public int getID()
 629  
   {
 630  
     return _id;
 631  
   }
 632  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.