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 ();
}