diff --git a/src/java/com/threerings/parlor/game/GameManager.java b/src/java/com/threerings/parlor/game/GameManager.java index cb14dea22..93152f3df 100644 --- a/src/java/com/threerings/parlor/game/GameManager.java +++ b/src/java/com/threerings/parlor/game/GameManager.java @@ -1,5 +1,5 @@ // -// $Id: GameManager.java,v 1.47 2002/10/07 18:42:21 mdb Exp $ +// $Id: GameManager.java,v 1.48 2002/10/15 23:07:23 shaper Exp $ package com.threerings.parlor.game; @@ -7,8 +7,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; -import com.samskivert.util.ArrayUtil; -import com.samskivert.util.ListUtil; import com.samskivert.util.StringUtil; import com.threerings.presents.data.ClientObject; @@ -49,52 +47,70 @@ public class GameManager extends PlaceManager // save off a casted reference to our config _gameconfig = (GameConfig)_config; - - // keep this around for now, we'll need it later - _players = _gameconfig.players; - - // instantiate a player oid array which we'll fill in later - _playerOids = new int[_players.length]; } /** - * Adds the given player to the game. This should only be called - * before the game is started, and is most likely to be used to add - * players to party games. + * Adds the given player to the game at the first available player + * index. This should only be called before the game is started, and + * is most likely to be used to add players to party games. * * @param player the username of the player to add to this game. + * @return the player index at which the player was added, or + * -1 if the player could not be added to the game. */ - public void addPlayer (String player) + public int addPlayer (String player) { - // append the player to the player list - int pcount = _players.length; - int npcount = pcount + 1; - String[] nplayers = new String[npcount]; - System.arraycopy(_players, 0, nplayers, 0, pcount); - nplayers[pcount] = player; - _players = nplayers; - - // expand the player oids list - int[] nplayerOids = new int[npcount]; - System.arraycopy(_playerOids, 0, nplayerOids, 0, pcount); - _playerOids = nplayerOids; - - if (_AIs != null) { - // expand the AI list - byte[] nAIs = new byte[npcount]; - System.arraycopy(_AIs, 0, nAIs, 0, pcount); - _AIs = nAIs; + // make sure we've space to add another player + int size = _gameobj.players.length; + if (_playerCount == size) { + Log.warning("Attempt to add player to full game " + + "[game=" + _gameobj.which() + ", player=" + player + + ", players=" + StringUtil.toString(_gameobj.players) + + "]."); + return -1; } - // set the new players list in the game object - _gameobj.setPlayers(_players); + // determine the first available player index + int pidx = -1; + for (int ii = 0; ii < size; ii++) { + if (!_gameobj.isOccupiedPlayer(ii)) { + pidx = ii; + break; + } + } + + // sanity-check the player index + if (pidx == -1) { + Log.warning("Couldn't find free player index for player who " + + "ought to be addable?! [game=" + _gameobj.which() + + ", player=" + player + + ", players=" + StringUtil.toString(_gameobj.players) + + "]."); + return -1; + } + + // fill in the player's information + _gameobj.setPlayersAt(player, pidx); + + // increment the number of players in the game + _playerCount++; // deliver a game ready notification to the player BodyObject bobj = CrowdServer.lookupBody(player); - ParlorSender.gameIsReady(bobj, _gameobj.getOid()); + if (bobj == null) { + Log.warning("Newly added player's body object went away before " + + "game ready notification could be sent " + + "[game=" + _gameobj.which() + ", player=" + player + + ", players=" + StringUtil.toString(_gameobj.players) + + "]."); + } else { + ParlorSender.gameIsReady(bobj, _gameobj.getOid()); + } // let derived classes do what they like - playerWasAdded(player, pcount); + playerWasAdded(player, pidx); + + return pidx; } /** @@ -116,34 +132,41 @@ public class GameManager extends PlaceManager * early-on if they realize they'd rather not play for some reason. * * @param player the username of the player to remove from this game. + * @return true if the player was successfully removed, false if not. */ - public void removePlayer (String player) + public boolean removePlayer (String player) { // get the player's index in the player list - int pidx = ListUtil.indexOfEqual(_players, player); + int pidx = _gameobj.getPlayerIndex(player); + + // sanity-check the player index if (pidx == -1) { Log.warning("Attempt to remove non-player from players list " + "[game=" + _gameobj.which() + - ", player=" + player + "]."); - return; + ", player=" + player + + ", players=" + StringUtil.toString(_gameobj.players) + + "]."); + return false; } // remove the player from the players list - _players = ArrayUtil.splice(_players, pidx, 1); + _gameobj.setPlayersAt(null, pidx); - // remove the player slot from the player oid list - _playerOids = ArrayUtil.splice(_playerOids, pidx, 1); + // clear out the player's entry in the player oid list + _playerOids[pidx] = 0; if (_AIs != null) { - // remove the player slot from the AI list - _AIs = ArrayUtil.splice(_AIs, pidx, 1); + // clear out the player's entry in the AI list + _AIs[pidx] = -1; } - // set the new players list in the game object - _gameobj.setPlayers(_players); + // decrement the number of players in the game + _playerCount--; // let derived classes do what they like playerWasRemoved(player, pidx); + + return true; } /** @@ -173,7 +196,7 @@ public class GameManager extends PlaceManager { if (_AIs == null) { // create and initialize the AI skill level array - _AIs = new byte[_players.length]; + _AIs = new byte[_gameobj.players.length]; Arrays.fill(_AIs, (byte)-1); // set up a delegate op for AI ticking _tickAIOp = new TickAIDelegateOp(); @@ -195,7 +218,16 @@ public class GameManager extends PlaceManager */ public String getPlayerName (int index) { - return _players[index]; + return _gameobj.players[index]; + } + + /** + * Returns the player index of the given user in the game, or + * -1 if the player is not involved in the game. + */ + public int getPlayerIndex (String username) + { + return _gameobj.getPlayerIndex(username); } /** @@ -211,7 +243,7 @@ public class GameManager extends PlaceManager */ public int getPlayerCount () { - return _players.length; + return _playerCount; } /** @@ -272,28 +304,41 @@ public class GameManager extends PlaceManager // documentation inherited protected void didStartup () { - super.didStartup(); - // obtain a casted reference to our game object _gameobj = (GameObject)_plobj; // stick the players into the game object - _gameobj.setPlayers(_players); + _gameobj.setPlayers(_gameconfig.players); + + // save off the number of players so that we needn't repeatedly + // iterate through the player name array server-side unnecessarily + _playerCount = _gameobj.getPlayerCount(); + + // instantiate a player oid array which we'll fill in later + int size = _gameobj.players.length; + _playerOids = new int[size]; // create and fill in our game service object GameMarshaller service = (GameMarshaller) _invmgr.registerDispatcher(new GameDispatcher(this), false); _gameobj.setGameService(service); + // give delegates a chance to do their thing + super.didStartup(); + // let the players of this game know that we're ready to roll (if // we have a specific set of players) - int pcount = (_players != null) ? _players.length : 0; - for (int ii = 0; ii < pcount; ii++) { - BodyObject bobj = CrowdServer.lookupBody(_players[ii]); + for (int ii = 0; ii < size; ii++) { + // skip non-existent players + if (!_gameobj.isOccupiedPlayer(ii)) { + continue; + } + + BodyObject bobj = CrowdServer.lookupBody(_gameobj.players[ii]); if (bobj == null) { Log.warning("Unable to deliver game ready to non-existent " + "player [game=" + _gameobj.which() + - ", player=" + _players[ii] + "]."); + ", player=" + _gameobj.players[ii] + "]."); continue; } @@ -316,6 +361,9 @@ public class GameManager extends PlaceManager { super.bodyLeft(bodyOid); + // TODO: if this is a party game not yet in play and the creator + // left, choose a new creator if at least one player still remains + // deal with disappearing players } @@ -378,7 +426,7 @@ public class GameManager extends PlaceManager if (!allPlayersReady()) { Log.warning("Requested to start a game that is still " + "awaiting players [game=" + _gameobj.which() + - ", pnames=" + StringUtil.toString(_players) + + ", pnames=" + StringUtil.toString(_gameobj.players) + ", poids=" + StringUtil.toString(_playerOids) + "]."); return false; } @@ -594,9 +642,11 @@ public class GameManager extends PlaceManager */ protected boolean allPlayersReady () { - for (int ii = 0; ii < _players.length; ii++) { - if ((_playerOids[ii] == 0) && - ((_AIs == null) || (_AIs[ii] == -1))) { + int pcount = _gameobj.players.length; + for (int ii = 0; ii < pcount; ii++) { + if (_gameobj.isOccupiedPlayer(ii) && + (_playerOids[ii] == 0) && + (_AIs == null || _AIs[ii] == -1)) { return false; } } @@ -616,9 +666,11 @@ public class GameManager extends PlaceManager // make sure the caller is the creating player BodyObject plobj = (BodyObject)caller; - if (!plobj.username.equals(_players[0])) { + int pidx = _gameobj.getPlayerIndex(plobj.username); + if (pidx != _gameobj.creator) { Log.warning("Attempt to start party game by non-creating player " + "[game=" + _gameobj.which() + + ", creator=" + getPlayerName(_gameobj.creator) + ", caller=" + caller + "]."); return; } @@ -686,8 +738,8 @@ public class GameManager extends PlaceManager /** A reference to our game object. */ protected GameObject _gameobj; - /** The usernames of the players of this game. */ - protected String[] _players; + /** The number of players in the game. */ + protected int _playerCount; /** The oids of our player and AI body objects. */ protected int[] _playerOids; diff --git a/src/java/com/threerings/parlor/game/GameObject.dobj b/src/java/com/threerings/parlor/game/GameObject.dobj index ed4e3dd29..b354fed79 100644 --- a/src/java/com/threerings/parlor/game/GameObject.dobj +++ b/src/java/com/threerings/parlor/game/GameObject.dobj @@ -1,5 +1,5 @@ // -// $Id: GameObject.dobj,v 1.14 2002/09/20 04:53:42 mdb Exp $ +// $Id: GameObject.dobj,v 1.15 2002/10/15 23:07:23 shaper Exp $ package com.threerings.parlor.game; @@ -43,20 +43,43 @@ public class GameObject extends PlaceObject /** Indicates whether or not this game is rated. */ public boolean isRated; - /** The username of the players involved in this game. */ + /** The usernames of the players involved in this game. */ public String[] players; /** The unique round identifier for the current round. */ public int roundId; + /** The player index of the creating player if this is a party game. */ + public int creator; + + /** + * Returns the number of players in the game. + */ + protected int getPlayerCount () + { + int count = 0; + int size = players.length; + for (int ii = 0; ii < size; ii++) { + if (players[ii] != null) { + count++; + } + } + return count; + } + /** * Returns the player index of the given user in the game, or * -1 if the player is not involved in the game. */ public int getPlayerIndex (String username) { - return (players == null) ? -1 : - ListUtil.indexOfEqual(players, username); + int size = (players == null) ? 0 : players.length; + for (int ii = 0; ii < size; ii++) { + if (players[ii] != null && players[ii].equals(username)) { + return ii; + } + } + return -1; } /** @@ -67,4 +90,12 @@ public class GameObject extends PlaceObject { return (state == IN_PLAY); } + + /** + * Returns whether the given player index in the game is occupied. + */ + public boolean isOccupiedPlayer (int index) + { + return (players[index] != null); + } } diff --git a/src/java/com/threerings/parlor/game/GameObject.java b/src/java/com/threerings/parlor/game/GameObject.java index 07b336c10..015411d7e 100644 --- a/src/java/com/threerings/parlor/game/GameObject.java +++ b/src/java/com/threerings/parlor/game/GameObject.java @@ -1,5 +1,5 @@ // -// $Id: GameObject.java,v 1.9 2002/09/20 04:53:42 mdb Exp $ +// $Id: GameObject.java,v 1.10 2002/10/15 23:07:23 shaper Exp $ package com.threerings.parlor.game; @@ -35,6 +35,9 @@ public class GameObject extends PlaceObject /** The field name of the roundId field. */ public static final String ROUND_ID = "roundId"; + /** The field name of the creator field. */ + public static final String CREATOR = "creator"; + /** A game state constant indicating that the game has not yet started * and is still awaiting the arrival of all of the players. */ public static final int AWAITING_PLAYERS = 0; @@ -58,20 +61,43 @@ public class GameObject extends PlaceObject /** Indicates whether or not this game is rated. */ public boolean isRated; - /** The username of the players involved in this game. */ + /** The usernames of the players involved in this game. */ public String[] players; /** The unique round identifier for the current round. */ public int roundId; + /** The player index of the creating player if this is a party game. */ + public int creator; + + /** + * Returns the number of players in the game. + */ + protected int getPlayerCount () + { + int count = 0; + int size = players.length; + for (int ii = 0; ii < size; ii++) { + if (players[ii] != null) { + count++; + } + } + return count; + } + /** * Returns the player index of the given user in the game, or * -1 if the player is not involved in the game. */ public int getPlayerIndex (String username) { - return (players == null) ? -1 : - ListUtil.indexOfEqual(players, username); + int size = (players == null) ? 0 : players.length; + for (int ii = 0; ii < size; ii++) { + if (players[ii] != null && players[ii].equals(username)) { + return ii; + } + } + return -1; } /** @@ -83,6 +109,14 @@ public class GameObject extends PlaceObject return (state == IN_PLAY); } + /** + * Returns whether the given player index in the game is occupied. + */ + public boolean isOccupiedPlayer (int index) + { + return (players[index] != null); + } + /** * Requests that the gameService field be set to the specified * value. The local value will be updated immediately and an event @@ -166,4 +200,18 @@ public class GameObject extends PlaceObject this.roundId = roundId; requestAttributeChange(ROUND_ID, new Integer(roundId)); } + + /** + * Requests that the creator field be set to the specified + * value. The local value will be updated immediately and an event + * will be propagated through the system to notify all listeners that + * the attribute did change. Proxied copies of this object (on + * clients) will apply the value change when they received the + * attribute changed notification. + */ + public void setCreator (int creator) + { + this.creator = creator; + requestAttributeChange(CREATOR, new Integer(creator)); + } } diff --git a/src/java/com/threerings/parlor/turn/TurnGameControllerDelegate.java b/src/java/com/threerings/parlor/turn/TurnGameControllerDelegate.java index 313107cb7..4527c2ede 100644 --- a/src/java/com/threerings/parlor/turn/TurnGameControllerDelegate.java +++ b/src/java/com/threerings/parlor/turn/TurnGameControllerDelegate.java @@ -1,5 +1,5 @@ // -// $Id: TurnGameControllerDelegate.java,v 1.3 2002/08/07 21:16:08 shaper Exp $ +// $Id: TurnGameControllerDelegate.java,v 1.4 2002/10/15 23:07:23 shaper Exp $ package com.threerings.parlor.turn; @@ -59,13 +59,7 @@ public class TurnGameControllerDelegate extends GameControllerDelegate */ public int getTurnHolderIndex () { - String holder = _turnGame.getTurnHolder(); - for (int i = 0; i < _gameObj.players.length; i++) { - if (_gameObj.players[i].equals(holder)) { - return i; - } - } - return -1; + return _gameObj.getPlayerIndex(_turnGame.getTurnHolder()); } // documentation inherited diff --git a/src/java/com/threerings/parlor/turn/TurnGameManager.java b/src/java/com/threerings/parlor/turn/TurnGameManager.java index c742e9118..f41fe2c55 100644 --- a/src/java/com/threerings/parlor/turn/TurnGameManager.java +++ b/src/java/com/threerings/parlor/turn/TurnGameManager.java @@ -1,5 +1,5 @@ // -// $Id: TurnGameManager.java,v 1.7 2002/08/06 23:23:39 shaper Exp $ +// $Id: TurnGameManager.java,v 1.8 2002/10/15 23:07:23 shaper Exp $ package com.threerings.parlor.turn; @@ -34,6 +34,12 @@ public interface TurnGameManager */ public String getPlayerName (int index); + /** + * Extending {@link GameManager} should automatically handle + * implementing this method. + */ + public int getPlayerIndex (String username); + /** * Extending {@link GameManager} should automatically handle * implementing this method. diff --git a/src/java/com/threerings/parlor/turn/TurnGameManagerDelegate.java b/src/java/com/threerings/parlor/turn/TurnGameManagerDelegate.java index 3fad8aae8..222fc2d2f 100644 --- a/src/java/com/threerings/parlor/turn/TurnGameManagerDelegate.java +++ b/src/java/com/threerings/parlor/turn/TurnGameManagerDelegate.java @@ -1,5 +1,5 @@ // -// $Id: TurnGameManagerDelegate.java,v 1.7 2002/08/07 21:17:50 shaper Exp $ +// $Id: TurnGameManagerDelegate.java,v 1.8 2002/10/15 23:07:23 shaper Exp $ package com.threerings.parlor.turn; @@ -8,7 +8,7 @@ import com.threerings.crowd.data.PlaceObject; import com.threerings.parlor.Log; import com.threerings.parlor.game.GameManager; import com.threerings.parlor.game.GameManagerDelegate; -import com.threerings.parlor.util.MathUtil; +import com.threerings.util.RandomUtil; /** * Performs the server-side turn-based game processing for a turn based @@ -36,17 +36,12 @@ public class TurnGameManagerDelegate extends GameManagerDelegate * game object. * * @return the index into the players array of the current turn holder - * or -1 if there is no current turn holder. + * or -1 if there is no current turn holder. */ public int getTurnHolderIndex () { String holder = _turnGame.getTurnHolder(); - for (int i = 0; i < _tgmgr.getPlayerCount(); i++) { - if (_tgmgr.getPlayerName(i).equals(holder)) { - return i; - } - } - return -1; + return _tgmgr.getPlayerIndex(_turnGame.getTurnHolder()); } /** @@ -64,18 +59,26 @@ public class TurnGameManagerDelegate extends GameManagerDelegate public void startTurn () { // sanity check - if (_turnIdx < 0 || _turnIdx >= _tgmgr.getPlayerCount()) { + if (_turnIdx < 0 || _turnIdx >= _turnGame.getPlayers().length) { Log.warning("startTurn() called with invalid turn index " + "[turnIdx=" + _turnIdx + "]."); // abort, abort return; } + // get the player name and sanity-check again + String name = _tgmgr.getPlayerName(_turnIdx); + if (name == null) { + Log.warning("startTurn() called with invalid player " + + "[turnIdx=" + _turnIdx + "]."); + return; + } + // do pre-start processing _tgmgr.turnWillStart(); // and set the turn indicator accordingly - _turnGame.setTurnHolder(_tgmgr.getPlayerName(_turnIdx)); + _turnGame.setTurnHolder(name); // do post-start processing _tgmgr.turnDidStart(); @@ -89,19 +92,19 @@ public class TurnGameManagerDelegate extends GameManagerDelegate * *

If the next turn should not be started immediately after this * turn, the game manager should arrange for {@link - * #setNextTurnHolder} to set the {@link #_turnIdx} field to -1 which - * will cause us not to start the next turn. It can then call {@link - * GameManager#endGame} if the game is over or do whatever else it - * needs to do outside the context of the turn flow. To start things - * back up again it would set {@link #_turnIdx} to the next turn - * holder and call {@link #startTurn} itself. + * #setNextTurnHolder} to set the {@link #_turnIdx} field to + * -1 which will cause us not to start the next turn. It + * can then call {@link GameManager#endGame} if the game is over or do + * whatever else it needs to do outside the context of the turn flow. + * To start things back up again it would set {@link #_turnIdx} to the + * next turn holder and call {@link #startTurn} itself. */ public void endTurn () { // let the manager know that the turn is over _tgmgr.turnDidEnd(); - // figure out whose up next + // figure out who's up next setNextTurnHolder(); // and start the next turn if desired @@ -137,9 +140,10 @@ public class TurnGameManagerDelegate extends GameManagerDelegate */ protected void setFirstTurnHolder () { - // TODO: sort out a better random number generator and make it - // available via the parlor services - _turnIdx = MathUtil.random(_tgmgr.getPlayerCount()); + int size = _turnGame.getPlayers().length; + do { + _turnIdx = RandomUtil.getInt(size); + } while (_tgmgr.getPlayerName(_turnIdx) == null); } /** @@ -151,8 +155,17 @@ public class TurnGameManagerDelegate extends GameManagerDelegate */ protected void setNextTurnHolder () { - // next! - _turnIdx = (_turnIdx + 1) % _tgmgr.getPlayerCount(); + // stick with the current player if they're the only participant + if (_tgmgr.getPlayerCount() == 1) { + return; + } + + // find the next occupied player slot + int size = _turnGame.getPlayers().length; + int oturnIdx = _turnIdx; + do { + _turnIdx = (_turnIdx + 1) % size; + } while (_tgmgr.getPlayerName(_turnIdx) == null); } /** The game manager for which we are delegating. */ @@ -161,7 +174,7 @@ public class TurnGameManagerDelegate extends GameManagerDelegate /** A reference to our game object. */ protected TurnGameObject _turnGame; - /** The offset into the _players array of the current turn holder or - * -1 if it's no one's turn. */ + /** The player index of the current turn holder or -1 if + * it's no one's turn. */ protected int _turnIdx = -1; } diff --git a/src/java/com/threerings/parlor/turn/TurnGameObject.java b/src/java/com/threerings/parlor/turn/TurnGameObject.java index ed4662746..9a4652996 100644 --- a/src/java/com/threerings/parlor/turn/TurnGameObject.java +++ b/src/java/com/threerings/parlor/turn/TurnGameObject.java @@ -1,5 +1,5 @@ // -// $Id: TurnGameObject.java,v 1.3 2002/02/12 07:01:54 mdb Exp $ +// $Id: TurnGameObject.java,v 1.4 2002/10/15 23:07:23 shaper Exp $ package com.threerings.parlor.turn; @@ -20,8 +20,8 @@ public interface TurnGameObject /** * Returns the username of the player who is currently taking their - * turn in this turn-based game or null if no user currently holds the - * turn. + * turn in this turn-based game or null if no user + * currently holds the turn. */ public String getTurnHolder (); @@ -30,4 +30,9 @@ public interface TurnGameObject * value. */ public void setTurnHolder (String turnHolder); + + /** + * Returns the array of player names involved in the game. + */ + public String[] getPlayers (); }