Hold on to your seats kids, it's another installment of "A Boy and His

Blowtorch". Refactored GameConfig and the EZ game framework, cleaning up some
old cruft from GameConfig (which will break other projects and which I'll fix
ASAP) and moved the XML based configuration system from ToyBox into EZGame.


git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@286 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
Michael Bayne
2007-05-01 22:26:25 +00:00
parent 04dd58d759
commit 7538ddcc7f
47 changed files with 2392 additions and 1114 deletions
@@ -40,24 +40,20 @@ public interface ParlorCodes extends InvocationCodes
/** The response code for a countered invitation. */
public static final int INVITATION_COUNTERED = 2;
/** An error code explaining that an invitation was rejected because
* the invited user was not online at the time the invitation was
* received. */
/** An error code explaining that an invitation was rejected because the invited user was not
* online at the time the invitation was received. */
public static final String INVITEE_NOT_ONLINE = "m.invitee_not_online";
/** An error code returned when a user requests to join a table that
* doesn't exist. */
/** An error code returned when a user requests to join a table that doesn't exist. */
public static final String NO_SUCH_TABLE = "m.no_such_table";
/** An error code returned when a user requests to join a table at a
* position that is not valid. */
public static final String INVALID_TABLE_POSITION =
"m.invalid_table_position";
/** An error code returned when a user requests to join a table at a position that is not
* valid. */
public static final String INVALID_TABLE_POSITION = "m.invalid_table_position";
/** An error code returned when a user requests to join a table in a
* position that is already occupied. */
public static final String TABLE_POSITION_OCCUPIED =
"m.table_position_occupied";
/** An error code returned when a user requests to join a table in a position that is already
* occupied. */
public static final String TABLE_POSITION_OCCUPIED = "m.table_position_occupied";
/** An error code returned when a user requests to leave a table that
* they were not sitting at in the first place. */
+50 -60
View File
@@ -36,8 +36,7 @@ import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.game.data.GameConfig;
/**
* This class represents a table that is being used to matchmake a game by
* the Parlor services.
* This class represents a table that is being used to matchmake a game by the Parlor services.
*/
public class Table
implements DSet.Entry, ParlorCodes
@@ -45,28 +44,25 @@ public class Table
/** The unique identifier for this table. */
public int tableId;
/** The object id of the lobby object with which this table is
* associated. */
/** The object id of the lobby object with which this table is associated. */
public int lobbyOid;
/** The oid of the game that was created from this table or -1 if the
* table is still in matchmaking mode. */
/** The oid of the game that was created from this table or -1 if the table is still in
* matchmaking mode. */
public int gameOid = -1;
/** An array of the usernames of the occupants of this table (some
* slots may not be filled), or null if a party game. */
/** An array of the usernames of the occupants of this table (some slots may not be filled), or
* null if a party game. */
public Name[] occupants;
/** The body oids of the occupants of this table, or null if a party game.
* (This is not propagated to remote instances.) */
/** The body oids of the occupants of this table, or null if a party game. (This is not
* propagated to remote instances.) */
public transient int[] bodyOids;
/** For a running game, the total number of players. For FFA party games,
* this is everyone. */
/** For a running game, the total number of players. For FFA party games, this is everyone. */
public short playerCount;
/** For a running game, the total number of watchers. For FFA party games,
* this is always 0. */
/** For a running game, the total number of watchers. For FFA party games, this is always 0. */
public short watcherCount;
/** The game config for the game that is being matchmade. */
@@ -83,15 +79,13 @@ public class Table
}
/**
* Initializes a new table instance, and assigns it the next monotonically
* increasing table id.
* Initializes a new table instance, and assigns it the next monotonically increasing table id.
*
* @param lobbyOid the object id of the lobby in which this table is
* to live.
* @param lobbyOid the object id of the lobby in which this table is to live.
* @param tconfig the table configuration for this table.
* @param config the configuration of the game being matchmade by this
* table.
* @param config the configuration of the game being matchmade by this table.
*/
@ActionScript(omit=true)
public void init (int lobbyOid, TableConfig tconfig, GameConfig config)
{
// assign a unique table id
@@ -105,7 +99,7 @@ public class Table
this.config = config;
// make room for the maximum number of players
if (config.getGameType() != GameConfig.PARTY) {
if (config.getMatchType() != GameConfig.PARTY) {
occupants = new Name[tconfig.desiredPlayerCount];
bodyOids = new int[occupants.length];
@@ -121,6 +115,7 @@ public class Table
/**
* Returns true if there is no one sitting at this table.
*/
@ActionScript(omit=true)
public boolean isEmpty ()
{
for (int i = 0; i < bodyOids.length; i++) {
@@ -148,21 +143,20 @@ public class Table
}
/**
* Once a table is ready to play (see {@link #mayBeStarted} and {@link
* #shouldBeStarted}), the players array can be fetched using this
* method. It will return an array containing the usernames of all of
* the players in the game, sized properly and with each player in the
* Once a table is ready to play (see {@link #mayBeStarted} and {@link #shouldBeStarted}), the
* players array can be fetched using this method. It will return an array containing the
* usernames of all of the players in the game, sized properly and with each player in the
* appropriate position.
*/
public Name[] getPlayers ()
{
// seated party games need a spot for every seat
if (GameConfig.SEATED_CONTINUOUS == config.getGameType()) {
if (GameConfig.SEATED_CONTINUOUS == config.getMatchType()) {
return new Name[tconfig.desiredPlayerCount];
}
// FFA party games have 0-length players array, and non-party
// games will have the players who are ready-to-go for the game start.
// FFA party games have 0-length players array, and non-party games will have the players
// who are ready-to-go for the game start.
Name[] players = new Name[getOccupiedCount()];
if (occupants != null) {
for (int ii = 0, dex = 0; ii < occupants.length; ii++) {
@@ -176,8 +170,8 @@ public class Table
}
/**
* For a team game, get the team member indices of the compressed
* players array returned by getPlayers().
* For a team game, get the team member indices of the compressed players array returned by
* getPlayers().
*/
public int[][] getTeamMemberIndices ()
{
@@ -205,16 +199,15 @@ public class Table
}
/**
* Requests to seat the specified user at the specified position in
* this table.
* Requests to seat the specified user at the specified position in this table.
*
* @param position the position in which to seat the user.
* @param occupant the occupant to set.
*
* @return null if the user was successfully seated, a string error
* code explaining the failure if the user was not able to be seated
* at that position.
* @return null if the user was successfully seated, a string error code explaining the failure
* if the user was not able to be seated at that position.
*/
@ActionScript(omit=true)
public String setOccupant (int position, BodyObject occupant)
{
// make sure the requested position is a valid one
@@ -233,10 +226,10 @@ public class Table
}
/**
* This method is used for party games, it does no bounds checking
* or verification of the player's ability to join, if you are unsure
* you should call 'setOccupant'.
* This method is used for party games, it does no bounds checking or verification of the
* player's ability to join, if you are unsure you should call 'setOccupant'.
*/
@ActionScript(omit=true)
public void setOccupantPos (int position, BodyObject occupant)
{
occupants[position] = occupant.getVisibleName();
@@ -244,13 +237,12 @@ public class Table
}
/**
* Requests that the specified user be removed from their seat at this
* table.
* Requests that the specified user be removed from their seat at this table.
*
* @return true if the user was seated at the table and has now been
* removed, false if the user was never seated at the table in the
* first place.
* @return true if the user was seated at the table and has now been removed, false if the user
* was never seated at the table in the first place.
*/
@ActionScript(omit=true)
public boolean clearOccupant (Name username)
{
if (occupants != null) {
@@ -265,13 +257,13 @@ public class Table
}
/**
* Requests that the user identified by the specified body object id
* be removed from their seat at this table.
* Requests that the user identified by the specified body object id be removed from their seat
* at this table.
*
* @return true if the user was seated at the table and has now been
* removed, false if the user was never seated at the table in the
* first place.
* @return true if the user was seated at the table and has now been removed, false if the user
* was never seated at the table in the first place.
*/
@ActionScript(omit=true)
public boolean clearOccupantByOid (int bodyOid)
{
if (bodyOids != null) {
@@ -286,9 +278,10 @@ public class Table
}
/**
* Called to clear an occupant at the specified position.
* Only call this method if you know what you're doing.
* Called to clear an occupant at the specified position. Only call this method if you know
* what you're doing.
*/
@ActionScript(omit=true)
public void clearOccupantPos (int position)
{
occupants[position] = null;
@@ -296,12 +289,12 @@ public class Table
}
/**
* Returns true if this table has a sufficient number of occupants
* that the game can be started.
* Returns true if this table has a sufficient number of occupants that the game can be
* started.
*/
public boolean mayBeStarted ()
{
switch (config.getGameType()) {
switch (config.getMatchType()) {
case GameConfig.SEATED_CONTINUOUS:
case GameConfig.PARTY:
return true;
@@ -330,12 +323,11 @@ public class Table
}
/**
* Returns true if sufficient seats are occupied that the game should
* be automatically started.
* Returns true if sufficient seats are occupied that the game should be automatically started.
*/
public boolean shouldBeStarted ()
{
switch (config.getGameType()) {
switch (config.getMatchType()) {
case GameConfig.SEATED_CONTINUOUS:
case GameConfig.PARTY:
return true;
@@ -346,8 +338,7 @@ public class Table
}
/**
* Returns true if this table is in play, false if it is still being
* matchmade.
* Returns true if this table is in play, false if it is still being matchmade.
*/
public boolean inPlay ()
{
@@ -363,8 +354,7 @@ public class Table
// documentation inherited
public boolean equals (Object other)
{
return (other instanceof Table) &&
(tableId == ((Table) other).tableId);
return (other instanceof Table) && (tableId == ((Table) other).tableId);
}
// documentation inherited
@@ -24,24 +24,21 @@ package com.threerings.parlor.data;
import com.threerings.io.SimpleStreamableObject;
/**
* Table configuration parameters for a game that is to be matchmade
* using the table services.
* Table configuration parameters for a game that is to be matchmade using the table services.
*/
public class TableConfig extends SimpleStreamableObject
{
/** The total number of players that are desired for the table.
* For team games, this should be set to the total number of players
* overall, as teams may be unequal. */
/** The total number of players that are desired for the table. For team games, this should be
* set to the total number of players overall, as teams may be unequal. */
public int desiredPlayerCount;
/** The minimum number of players needed overall (or per-team if a
* team-based game) for the game to start at the creator's discretion. */
/** The minimum number of players needed overall (or per-team if a team-based game) for the
* game to start at the creator's discretion. */
public int minimumPlayerCount;
/** If non-null, indicates that this is a team-based game and contains
* the team assignments for each player. For example, a game with
* three players in two teams- players 0 and 2 versus player 1- would
* have { {0, 2}, {1} }; */
/** If non-null, indicates that this is a team-based game and contains the team assignments for
* each player. For example, a game with three players in two teams- players 0 and 2 versus
* player 1- would have { {0, 2}, {1} }; */
public int[][] teamMemberIndices;
/** Whether the table is "private". */
@@ -25,20 +25,18 @@ import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.util.ParlorContext;
/**
* Provides the base from which interfaces can be built to configure games
* prior to starting them. Derived classes would extend the base
* configurator adding interface elements and wiring them up properly to
* allow the user to configure an instance of their game.
* Provides the base from which interfaces can be built to configure games prior to starting
* them. Derived classes would extend the base configurator adding interface elements and wiring
* them up properly to allow the user to configure an instance of their game.
*
* <p> Clients that use the game configurator will want to instantiate one
* based on the class returned from the {@link GameConfig} and then
* initialize it with a call to {@link #init}.
* <p> Clients that use the game configurator will want to instantiate one based on the class
* returned from the {@link GameConfig} and then initialize it with a call to {@link #init}.
*/
public abstract class GameConfigurator
{
/**
* Initializes this game configurator, creates its user interface
* elements and prepares it for display.
* Initializes this game configurator, creates its user interface elements and prepares it for
* display.
*/
public void init (ParlorContext ctx)
{
@@ -57,8 +55,8 @@ public abstract class GameConfigurator
}
/**
* Provides this configurator with its configuration. It should set up
* all of its user interface elements to reflect the configuration.
* Provides this configurator with its configuration. It should set up all of its user
* interface elements to reflect the configuration.
*/
public void setGameConfig (GameConfig config)
{
@@ -68,8 +66,8 @@ public abstract class GameConfigurator
}
/**
* Derived classes will likely want to override this method and
* configure their user interface elements accordingly.
* Derived classes will likely want to override this method and configure their user interface
* elements accordingly.
*/
protected void gotGameConfig ()
{
@@ -86,10 +84,9 @@ public abstract class GameConfigurator
}
/**
* Derived classes will want to override this method, flushing values
* from the user interface to the game config object so that it is
* properly configured prior to being returned to the {@link
* #getGameConfig} caller.
* Derived classes will want to override this method, flushing values from the user interface
* to the game config object so that it is properly configured prior to being returned to the
* {@link #getGameConfig} caller.
*/
protected void flushGameConfig ()
{
@@ -40,24 +40,21 @@ import com.threerings.parlor.game.data.GameObject;
import com.threerings.parlor.util.ParlorContext;
/**
* The game controller manages the flow and control of a game on the
* client side. This class serves as the root of a hierarchy of controller
* classes that aim to provide functionality shared between various
* similar games. The base controller provides functionality for starting
* and ending the game and for calculating ratings adjustements when a
* game ends normally. It also handles the basic house keeping like
* subscription to the game object and dispatch of commands and
* distributed object events.
* The game controller manages the flow and control of a game on the client side. This class serves
* as the root of a hierarchy of controller classes that aim to provide functionality shared
* between various similar games. The base controller provides functionality for starting and
* ending the game and for calculating ratings adjustements when a game ends normally. It also
* handles the basic house keeping like subscription to the game object and dispatch of commands
* and distributed object events.
*/
public abstract class GameController extends PlaceController
implements AttributeChangeListener
{
/**
* Initializes this game controller with the game configuration that
* was established during the match making process. Derived classes
* may want to override this method to initialize themselves with
* game-specific configuration parameters but they should be sure to
* call <code>super.init</code> in such cases.
* Initializes this game controller with the game configuration that was established during the
* match making process. Derived classes may want to override this method to initialize
* themselves with game-specific configuration parameters but they should be sure to call
* <code>super.init</code> in such cases.
*
* @param ctx the client context.
* @param config the configuration of the game we are intended to
@@ -65,9 +62,8 @@ public abstract class GameController extends PlaceController
*/
public void init (CrowdContext ctx, PlaceConfig config)
{
// cast our references before we call super.init() so that when
// super.init() calls createPlaceView(), we have our casted
// references already in place
// cast our references before we call super.init() so that when super.init() calls
// createPlaceView(), we have our casted references already in place
_ctx = (ParlorContext)ctx;
_config = (GameConfig)config;
@@ -75,9 +71,8 @@ public abstract class GameController extends PlaceController
}
/**
* Adds this controller as a listener to the game object (thus derived
* classes need not do so) and lets the game manager know that we are
* now ready to go.
* Adds this controller as a listener to the game object (thus derived classes need not do so)
* and lets the game manager know that we are now ready to go.
*/
public void willEnterPlace (PlaceObject plobj)
{
@@ -86,27 +81,24 @@ public abstract class GameController extends PlaceController
// obtain a casted reference
_gobj = (GameObject)plobj;
// if this place object is not our current location we'll need to
// add it as an auxiliary chat source
// if this place object is not our current location we'll need to add it as an auxiliary
// chat source
BodyObject bobj = (BodyObject)_ctx.getClient().getClientObject();
if (bobj.location != plobj.getOid()) {
_ctx.getChatDirector().addAuxiliarySource(
_gobj, GameCodes.GAME_CHAT_TYPE);
_ctx.getChatDirector().addAuxiliarySource(_gobj, GameCodes.GAME_CHAT_TYPE);
}
// and add ourselves as a listener
_gobj.addListener(this);
// we don't want to claim to be finished until any derived classes
// that overrode this method have executed, so we'll queue up a
// runnable here that will let the game manager know that we're
// ready on the next pass through the distributed event loop
// we don't want to claim to be finished until any derived classes that overrode this
// method have executed, so we'll queue up a runnable here that will let the game manager
// know that we're ready on the next pass through the distributed event loop
Log.info("Entering game " + _gobj.which() + ".");
if (_gobj.getPlayerIndex(bobj.getVisibleName()) != -1) {
_ctx.getClient().getRunQueue().postRunnable(new Runnable() {
public void run () {
// finally let the game manager know that we're ready
// to roll
// finally let the game manager know that we're ready to roll
playerReady();
}
});
@@ -114,8 +106,7 @@ public abstract class GameController extends PlaceController
}
/**
* Removes our listener registration from the game object and cleans
* house.
* Removes our listener registration from the game object and cleans house.
*/
public void didLeavePlace (PlaceObject plobj)
{
@@ -131,9 +122,9 @@ public abstract class GameController extends PlaceController
/**
* Convenience method to determine the type of game.
*/
public byte getGameType ()
public int getMatchType ()
{
return _config.getGameType();
return _config.getMatchType();
}
/**
@@ -141,15 +132,13 @@ public abstract class GameController extends PlaceController
*/
public boolean isGameOver ()
{
boolean gameOver = (_gobj == null) ||
(_gobj.state != GameObject.IN_PLAY);
boolean gameOver = (_gobj == null) || (_gobj.state != GameObject.IN_PLAY);
return (_gameOver || gameOver);
}
/**
* Sets the client game over override. This is used in situations
* where we determine that the game is over before the server has
* informed us of such.
* Sets the client game over override. This is used in situations where we determine that the
* game is over before the server has informed us of such.
*/
public void setGameOver (boolean gameOver)
{
@@ -157,13 +146,11 @@ public abstract class GameController extends PlaceController
}
/**
* Calls {@link #gameWillReset}, ends the current game (locally, it
* does not tell the server to end the game), and waits to receive a
* reset notification (which is simply an event setting the game state
* to <code>IN_PLAY</code> even though it's already set to
* <code>IN_PLAY</code>) from the server which will start up a new
* game. Derived classes should override {@link #gameWillReset} to
* perform any game-specific animations.
* Calls {@link #gameWillReset}, ends the current game (locally, it does not tell the server to
* end the game), and waits to receive a reset notification (which is simply an event setting
* the game state to <code>IN_PLAY</code> even though it's already set to <code>IN_PLAY</code>)
* from the server which will start up a new game. Derived classes should override {@link
* #gameWillReset} to perform any game-specific animations.
*/
public void resetGame ()
{
@@ -183,9 +170,8 @@ public abstract class GameController extends PlaceController
}
/**
* Handles basic game controller action events. Derived classes should
* be sure to call <code>super.handleAction</code> for events they
* don't specifically handle.
* Handles basic game controller action events. Derived classes should be sure to call
* <code>super.handleAction</code> for events they don't specifically handle.
*/
public boolean handleAction (ActionEvent action)
{
@@ -197,8 +183,7 @@ public abstract class GameController extends PlaceController
*/
public void systemMessage (String bundle, String msg)
{
_ctx.getChatDirector().displayInfo(
bundle, msg, GameCodes.GAME_CHAT_TYPE);
_ctx.getChatDirector().displayInfo(bundle, msg, GameCodes.GAME_CHAT_TYPE);
}
// documentation inherited
@@ -208,16 +193,16 @@ public abstract class GameController extends PlaceController
if (event.getName().equals(GameObject.STATE)) {
int newState = event.getIntValue();
if (!stateDidChange(newState)) {
Log.warning("Game transitioned to unknown state " +
"[gobj=" + _gobj + ", state=" + newState + "].");
Log.warning("Game transitioned to unknown state [gobj=" + _gobj +
", state=" + newState + "].");
}
}
}
/**
* Derived classes can override this method if they add additional game
* states and should handle transitions to those states, returning true to
* indicate they were handled and calling super for the normal game states.
* Derived classes can override this method if they add additional game states and should
* handle transitions to those states, returning true to indicate they were handled and calling
* super for the normal game states.
*/
protected boolean stateDidChange (int state)
{
@@ -236,8 +221,8 @@ public abstract class GameController extends PlaceController
}
/**
* Called after we've entered the game and everything has initialized
* to notify the server that we, as a player, are ready to play.
* Called after we've entered the game and everything has initialized to notify the server that
* we, as a player, are ready to play.
*/
protected void playerReady ()
{
@@ -246,9 +231,8 @@ public abstract class GameController extends PlaceController
}
/**
* Called when the game transitions to the <code>IN_PLAY</code>
* state. This happens when all of the players have arrived and the
* server starts the game.
* Called when the game transitions to the <code>IN_PLAY</code> state. This happens when all of
* the players have arrived and the server starts the game.
*/
protected void gameDidStart ()
{
@@ -269,9 +253,8 @@ public abstract class GameController extends PlaceController
}
/**
* Called when the game transitions to the <code>GAME_OVER</code>
* state. This happens when the game reaches some end condition by
* normal means (is not cancelled or aborted).
* Called when the game transitions to the <code>GAME_OVER</code> state. This happens when the
* game reaches some end condition by normal means (is not cancelled or aborted).
*/
protected void gameDidEnd ()
{
@@ -297,9 +280,8 @@ public abstract class GameController extends PlaceController
}
/**
* Called to give derived classes a chance to display animations, send
* a final packet, or do any other business they care to do when the
* game is about to reset.
* Called to give derived classes a chance to display animations, send a final packet, or do
* any other business they care to do when the game is about to reset.
*/
protected void gameWillReset ()
{
@@ -317,12 +299,10 @@ public abstract class GameController extends PlaceController
/** Our game configuration information. */
protected GameConfig _config;
/** A reference to the game object for the game that we're
* controlling. */
/** A reference to the game object for the game that we're controlling. */
protected GameObject _gobj;
/** A local flag overriding the game over state for situations where
* the client knows the game is over before the server has
* transitioned the game object accordingly. */
/** A local flag overriding the game over state for situations where the client knows the game
* is over before the server has transitioned the game object accordingly. */
protected boolean _gameOver;
}
@@ -31,74 +31,81 @@ import com.threerings.parlor.client.TableConfigurator;
import com.threerings.parlor.game.client.GameConfigurator;
/**
* The game config class encapsulates the configuration information for a
* particular type of game. The hierarchy of game config objects mimics the
* hierarchy of game managers and controllers. Both the game manager and game
* controller are provided with the game config object when the game is
* The game config class encapsulates the configuration information for a particular type of game.
* The hierarchy of game config objects mimics the hierarchy of game managers and controllers. Both
* the game manager and game controller are provided with the game config object when the game is
* created.
*
* <p> The game config object is also the mechanism used to instantiate the
* appropriate game manager and controller. Every game must have an associated
* game config derived class that overrides {@link #createController} and
* {@link #getManagerClassName}, returning the appropriate game controller and
* manager class for that game. Thus the entire chain of events that causes a
* particular game to be created is the construction of the appropriate game
* config instance which is provided to the server as part of an invitation or
* via some other matchmaking mechanism.
* <p> The game config object is also the mechanism used to instantiate the appropriate game
* manager and controller. Every game must have an associated game config derived class that
* overrides {@link #createController} and {@link #getManagerClassName}, returning the appropriate
* game controller and manager class for that game. Thus the entire chain of events that causes a
* particular game to be created is the construction of the appropriate game config instance which
* is provided to the server as part of an invitation or via some other matchmaking mechanism.
*/
public abstract class GameConfig extends PlaceConfig implements Cloneable
{
/** Game type constant: a game that is started with a list of players,
* and those are the only players that may play. */
public static final byte SEATED_GAME = 0;
/** Matchmaking type constant: a game that is started with a list of players, and those are the
* only players that may play. */
public static final int SEATED_GAME = 0;
/** Game type constant: a game that starts immediately, but only has
* a certain number of player slots. Users enter the game room, and
* then choose where to sit. */
public static final byte SEATED_CONTINUOUS = 1;
/** Matchmaking type constant: a game that starts immediately, but only has a certain number of
* player slots. Users enter the game room, and then choose where to sit. */
public static final int SEATED_CONTINUOUS = 1;
/** Game type constant: a game that starts immediately, and every
* user that enters is a player. */
public static final byte PARTY = 2;
/** Matchmaking type constant: a game that starts immediately, and every user that enters is a
* player. */
public static final int PARTY = 2;
/** The usernames of the players involved in this game, or an empty
* array if such information is not needed by this particular game. */
/** Maps the matchmaking type codes into strings used in our XML configuration. */
public static final String[] TYPE_STRINGS = { "table", "entersit", "party" };
/** The usernames of the players involved in this game, or an empty array if such information
* is not needed by this particular game. */
public Name[] players = new Name[0];
/** Indicates whether or not this game is rated. */
public boolean rated = true;
/** Configurations for AIs to be used in this game. Slots with real
* players should be null and slots with AIs should contain
* configuration for those AIs. A null array indicates no use of AIs
* at all. */
/** Configurations for AIs to be used in this game. Slots with real players should be null and
* slots with AIs should contain configuration for those AIs. A null array indicates no use of
* AIs at all. */
public GameAI[] ais = new GameAI[0];
/**
* Get the type of game.
* Returns a numeric identifier for this game class. This may be used to track persisent
* information on a per-game basis.
*/
public byte getGameType ()
public abstract int getGameId ();
/**
* Returns a string identifier for this game class (e.g. "spades"). This may be used to
* identify a message bundle for which to obtain translations for this game configuration and
* to look up the name of the game in said bundle.
*/
public abstract String getGameIdent ();
/**
* Returns the matchmaking type of this game: {@link #SEATED_GAME}, etc.
*/
public int getMatchType ()
{
return SEATED_GAME;
}
/**
* Returns the message bundle identifier for the bundle that should be
* used to translate the translatable strings used to describe the
* game config parameters.
* Creates a configurator that can be used to create a user interface for configuring this
* instance prior to starting the game. If no configuration is necessary, this method should
* return null.
*/
public abstract String getBundleName ();
public GameConfigurator createConfigurator ()
{
return null;
}
/**
* Creates a configurator that can be used to create a user interface
* for configuring this instance prior to starting the game. If no
* configuration is necessary, this method should return null.
*/
public abstract GameConfigurator createConfigurator ();
/**
* Creates a table configurator for initializing 'table' properties
* of the game. The default implementation returns null.
* Creates a table configurator for initializing 'table' properties of the game. The default
* implementation returns null.
*/
public TableConfigurator createTableConfigurator ()
{
@@ -106,27 +113,8 @@ public abstract class GameConfig extends PlaceConfig implements Cloneable
}
/**
* Returns a translatable label describing this game.
*/
public String getGameName ()
{
// the whole getRatingTypeId(), getGameName(), getBundleName()
// business should be cleaned up. we should have getGameIdent()
// and everything should have a default implementation using that
return "m." + getBundleName();
}
/**
* Returns the game rating type, if the system uses such things.
*/
public byte getRatingTypeId ()
{
return (byte)-1;
}
/**
* Returns a List of strings that describe the configuration of this
* game. Default implementation returns an empty list.
* Returns a List of strings that describe the configuration of this game. Default
* implementation returns an empty list.
*/
public List<String> getDescription ()
{
@@ -134,26 +122,21 @@ public abstract class GameConfig extends PlaceConfig implements Cloneable
}
/**
* Returns true if this game config object is equal to the supplied
* object (meaning it is also a game config object and its
* configuration settings are the same as ours).
* Returns true if this game config object is equal to the supplied object (meaning it is a
* game config for the same game and its configuration settings are the same as ours).
*/
public boolean equals (Object other)
{
// make sure they're of the same class
if (other.getClass() == this.getClass()) {
GameConfig that = (GameConfig) other;
return this.rated == that.rated;
} else {
if (!(other instanceof GameConfig)) {
return false;
}
GameConfig that = (GameConfig) other;
return (this.getGameId() == that.getGameId()) && (this.rated == that.rated);
}
/**
* Computes a hashcode for this game config object that supports our
* {@link #equals} implementation. Objects that are equal should have
* the same hashcode.
* Computes a hashcode for this game config object that supports our {@link #equals}
* implementation. Objects that are equal should have the same hashcode.
*/
public int hashCode ()
{
@@ -58,20 +58,17 @@ import com.threerings.parlor.server.ParlorSender;
import com.threerings.util.MessageBundle;
/**
* The game manager handles the server side management of a game. It
* manipulates the game state in accordance with the logic of the game
* flow and generally manages the whole game playing process.
* The game manager handles the server side management of a game. It manipulates the game state in
* accordance with the logic of the game flow and generally manages the whole game playing process.
*
* <p> The game manager extends the place manager because games are
* implicitly played in a location, the players of the game implicitly
* bodies in that location.
* <p> The game manager extends the place manager because games are implicitly played in a
* location, the players of the game implicitly bodies in that location.
*/
public class GameManager extends PlaceManager
implements ParlorCodes, GameCodes
{
/**
* Returns the configuration object for the game being managed by this
* manager.
* Returns the configuration object for the game being managed by this manager.
*/
public GameConfig getGameConfig ()
{
@@ -81,19 +78,19 @@ public class GameManager extends PlaceManager
/**
* A convenience method for getting the game type.
*/
public byte getGameType ()
public int getMatchType ()
{
return _gameconfig.getGameType();
return _gameconfig.getMatchType();
}
/**
* 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.
* 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
* <code>-1</code> if the player could not be added to the game.
* @return the player index at which the player was added, or <code>-1</code> if the player
* could not be added to the game.
*/
public int addPlayer (Name player)
{
@@ -108,10 +105,9 @@ public class GameManager extends PlaceManager
// sanity-check the player index
if (pidx == -1) {
Log.warning("Couldn't find free player index for player " +
"[game=" + where() + ", player=" + player +
", players=" + StringUtil.toString(_gameobj.players) +
"].");
Log.warning("Couldn't find free player index for player [game=" + where() +
", player=" + player +
", players=" + StringUtil.toString(_gameobj.players) + "].");
return -1;
}
@@ -120,9 +116,8 @@ public class GameManager extends PlaceManager
}
/**
* Adds the given player to the game at the specified 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.
* Adds the given player to the game at the specified 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.
* @param pidx the player index at which the player is to be added.
@@ -132,34 +127,31 @@ public class GameManager extends PlaceManager
{
// make sure the specified player index is valid
if (pidx < 0 || pidx >= getPlayerSlots()) {
Log.warning("Attempt to add player at an invalid index " +
"[game=" + where() + ", player=" + player +
", pidx=" + pidx + "].");
Log.warning("Attempt to add player at an invalid index [game=" + where() +
", player=" + player + ", pidx=" + pidx + "].");
return false;
}
// make sure the player index is available
if (_gameobj.players[pidx] != null) {
Log.warning("Attempt to add player at occupied index " +
"[game=" + where() + ", player=" + player +
", pidx=" + pidx + "].");
Log.warning("Attempt to add player at occupied index [game=" + where() +
", player=" + player + ", pidx=" + pidx + "].");
return false;
}
// make sure the player isn't already somehow a part of the game
// to avoid any potential badness that might ensue if we added
// them more than once
// make sure the player isn't already somehow a part of the game to avoid any potential
// badness that might ensue if we added them more than once
if (_gameobj.getPlayerIndex(player) != -1) {
Log.warning("Attempt to add player to game that they're already " +
"playing [game=" + where() + ", player=" + player + "].");
Log.warning("Attempt to add player to game that they're already playing " +
"[game=" + where() + ", player=" + player + "].");
return false;
}
// get the player's body object
BodyObject bobj = CrowdServer.lookupBody(player);
if (bobj == null) {
Log.warning("Unable to get body object while adding player " +
"[game=" + where() + ", player=" + player + "].");
Log.warning("Unable to get body object while adding player [game=" + where() +
", player=" + player + "].");
return false;
}
@@ -179,9 +171,9 @@ public class GameManager extends PlaceManager
}
/**
* Removes the given player from the game. This is most likely to be
* used to allow players involved in a party game to leave the game
* early-on if they realize they'd rather not play for some reason.
* Removes the given player from the game. This is most likely to be used to allow players
* involved in a party game to leave the game 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.
@@ -193,10 +185,9 @@ public class GameManager extends PlaceManager
// sanity-check the player index
if (pidx == -1) {
Log.warning("Attempt to remove non-player from players list " +
"[game=" + where() + ", player=" + player +
", players=" + StringUtil.toString(_gameobj.players) +
"].");
Log.warning("Attempt to remove non-player from players list [game=" + where() +
", player=" + player +
", players=" + StringUtil.toString(_gameobj.players) + "].");
return false;
}
@@ -221,9 +212,8 @@ public class GameManager extends PlaceManager
}
/**
* Replaces the player at the specified index and calls {@link
* #playerWasReplaced} to let derived classes and delegates know
* what's going on.
* Replaces the player at the specified index and calls {@link #playerWasReplaced} to let
* derived classes and delegates know what's going on.
*/
public void replacePlayer (final int pidx, final Name player)
{
@@ -236,15 +226,14 @@ public class GameManager extends PlaceManager
// notify our delegates
applyToDelegates(new DelegateOp() {
public void apply (PlaceManagerDelegate delegate) {
((GameManagerDelegate)delegate).playerWasReplaced(
pidx, oplayer, player);
((GameManagerDelegate)delegate).playerWasReplaced(pidx, oplayer, player);
}
});
}
/**
* Returns the user object for the player with the specified index or
* null if the player at that index is not online.
* Returns the user object for the player with the specified index or null if the player at
* that index is not online.
*/
public BodyObject getPlayer (int playerIdx)
{
@@ -259,9 +248,8 @@ public class GameManager extends PlaceManager
}
/**
* Sets the specified player as an AI with the specified
* configuration. It is assumed that this will be set soon after the
* player names for all AIs present in the game. (It should be done
* Sets the specified player as an AI with the specified configuration. It is assumed that this
* will be set soon after the player names for all AIs present in the game. (It should be done
* before human players start trickling into the game.)
*
* @param pidx the player index of the AI.
@@ -288,8 +276,8 @@ public class GameManager extends PlaceManager
}
/**
* Returns the name of the player with the specified index or null if
* no player exists at that index.
* Returns the name of the player with the specified index or null if no player exists at that
* index.
*/
public Name getPlayerName (int index)
{
@@ -297,8 +285,8 @@ public class GameManager extends PlaceManager
}
/**
* Returns the player index of the given user in the game, or
* <code>-1</code> if the player is not involved in the game.
* Returns the player index of the given user in the game, or <code>-1</code> if the player is
* not involved in the game.
*/
public int getPlayerIndex (Name username)
{
@@ -306,14 +294,12 @@ public class GameManager extends PlaceManager
}
/**
* Get the player index of the specified oid, or -1 if the oid is
* not a player or is a player that is not presently in the game.
* Get the player index of the specified oid, or -1 if the oid is not a player or is a player
* that is not presently in the game.
*/
public int getPresentPlayerIndex (int bodyOid)
{
return (_playerOids == null)
? -1
: IntListUtil.indexOf(_playerOids, bodyOid);
return (_playerOids == null) ? -1 : IntListUtil.indexOf(_playerOids, bodyOid);
}
/**
@@ -349,13 +335,11 @@ public class GameManager extends PlaceManager
}
/**
* Returns whether the player at the specified player index is actively
* playing the game
* Returns whether the player at the specified player index is actively playing the game
*/
public boolean isActivePlayer (int pidx)
{
return _gameobj.isActivePlayer(pidx) &&
(getPlayerOid(pidx) > 0 || isAI(pidx));
return _gameobj.isActivePlayer(pidx) && (getPlayerOid(pidx) > 0 || isAI(pidx));
}
/**
@@ -377,14 +361,12 @@ public class GameManager extends PlaceManager
/**
* Sends a system message to the players in the game room.
*
* @param waitForStart if true, the message will not be sent until the
* game has started.
* @param waitForStart if true, the message will not be sent until the game has started.
*/
public void systemMessage (
String msgbundle, String msg, boolean waitForStart)
{
if (waitForStart &&
((_gameobj == null) || (_gameobj.state == GameObject.PRE_GAME))) {
if (waitForStart && ((_gameobj == null) || (_gameobj.state == GameObject.PRE_GAME))) {
// queue up the message.
if (_startmsgs == null) {
_startmsgs = new ArrayList<Tuple<String,String>>();
@@ -398,23 +380,20 @@ public class GameManager extends PlaceManager
}
/**
* This is called when the game is ready to start (all players
* involved have delivered their "am ready" notifications). It calls
* {@link #gameWillStart}, sets the necessary wheels in motion and
* then calls {@link #gameDidStart}. Derived classes should override
* one or both of the calldown functions (rather than this function)
* if they need to do things before or after the game starts.
* This is called when the game is ready to start (all players involved have delivered their
* "am ready" notifications). It calls {@link #gameWillStart}, sets the necessary wheels in
* motion and then calls {@link #gameDidStart}. Derived classes should override one or both of
* the calldown functions (rather than this function) if they need to do things before or after
* the game starts.
*
* @return true if the game was started, false if it could not be
* started because it was already in play or because all players have
* not yet reported in.
* @return true if the game was started, false if it could not be started because it was
* already in play or because all players have not yet reported in.
*/
public boolean startGame ()
{
// complain if we're already started
if (_gameobj.state == GameObject.IN_PLAY) {
Log.warning("Requested to start an already in-play game " +
"[game=" + where() + "].");
Log.warning("Requested to start an already in-play game [game=" + where() + "].");
Thread.dumpStack();
return false;
}
@@ -424,27 +403,24 @@ public class GameManager extends PlaceManager
// make sure everyone has turned up
if (!allPlayersReady()) {
Log.warning("Requested to start a game that is still " +
"awaiting players [game=" + where() +
", pnames=" + StringUtil.toString(_gameobj.players) +
Log.warning("Requested to start a game that is still awaiting players " +
"[game=" + where() + ", pnames=" + StringUtil.toString(_gameobj.players) +
", poids=" + StringUtil.toString(_playerOids) + "].");
return false;
}
// if we're still waiting for a call to endGame() to propagate,
// queue up a runnable to start the game which will allow the
// endGame() to propagate before we start things up
// if we're still waiting for a call to endGame() to propagate, queue up a runnable to
// start the game which will allow the endGame() to propagate before we start things up
if (_committedState == GameObject.IN_PLAY) {
if (_postponedStart) {
// We've already tried postponing once, doesn't do us any
// good to throw ourselves into a frenzy trying again.
Log.warning("Tried to postpone the start of a still-ending game " +
"multiple times [game=" + where() + "].");
// We've already tried postponing once, doesn't do us any good to throw ourselves
// into a frenzy trying again.
Log.warning("Tried to postpone the start of a still-ending game multiple times " +
"[game=" + where() + "].");
_postponedStart = false;
return false;
}
Log.info("Postponing start of still-ending game " +
"[game=" + where() + "].");
Log.info("Postponing start of still-ending game [game=" + where() + "].");
_postponedStart = true;
// TEMP: track down weirdness
final Exception firstCall = new Exception();
@@ -500,30 +476,27 @@ public class GameManager extends PlaceManager
if (shouldEndGame()) {
endGame();
} else {
// otherwise report that the player was knocked out to other
// people in his/her room
// otherwise report that the player was knocked out to other people in his/her room
reportPlayerKnockedOut(pidx);
}
}
/**
* Called when the game is known to be over. This will call some
* calldown functions to determine the winner of the game and then
* transition the game to the {@link GameObject#GAME_OVER} state.
* Called when the game is known to be over. This will call some calldown functions to
* determine the winner of the game and then transition the game to the {@link
* GameObject#GAME_OVER} state.
*/
public void endGame ()
{
// TEMP: debug pending rating repeat bug
if (_gameEndTracker.checkCall(
"Requested to end already ended game " +
"[game=" + where() + "].")) {
"Requested to end already ended game [game=" + where() + "].")) {
return;
}
// END TEMP
if (!_gameobj.isInPlay()) {
Log.info("Refusing to end game that was not in play " +
"[game=" + where() + "].");
Log.info("Refusing to end game that was not in play [game=" + where() + "].");
return;
}
@@ -544,22 +517,19 @@ public class GameManager extends PlaceManager
_gameobj.commitTransaction();
}
// wait until we hear the game state transition on the game object
// to invoke our game over code so that we can be sure that any
// final events dispatched on the game object prior to the call to
// endGame() have been dispatched
// wait until we hear the game state transition on the game object to invoke our game over
// code so that we can be sure that any final events dispatched on the game object prior to
// the call to endGame() have been dispatched
}
/**
* Sets the state of the game to {@link GameObject#CANCELLED}.
*
* @return true if the game was cancelled, false if it was already over or
* cancelled.
* @return true if the game was cancelled, false if it was already over or cancelled.
*/
public boolean cancelGame ()
{
if (_gameobj.state != GameObject.GAME_OVER &&
_gameobj.state != GameObject.CANCELLED) {
if (_gameobj.state != GameObject.GAME_OVER && _gameobj.state != GameObject.CANCELLED) {
_gameobj.setState(GameObject.CANCELLED);
return true;
}
@@ -567,10 +537,9 @@ public class GameManager extends PlaceManager
}
/**
* Returns whether game conclusion antics such as rating updates
* should be performed when an in-play game is ended. Derived classes
* may wish to override this method to customize the conditions under
* which the game is concluded.
* Returns whether game conclusion antics such as rating updates should be performed when an
* in-play game is ended. Derived classes may wish to override this method to customize the
* conditions under which the game is concluded.
*/
public boolean shouldConcludeGame ()
{
@@ -578,13 +547,11 @@ public class GameManager extends PlaceManager
}
/**
* Called when the game is to be reset to its starting state in
* preparation for a new game without actually ending the current
* game. It calls {@link #gameWillReset} followed by the standard game
* start processing ({@link #gameWillStart} and {@link
* #gameDidStart}). Derived classes should override these calldown
* functions (rather than this function) if they need to do things
* before or after the game resets.
* Called when the game is to be reset to its starting state in preparation for a new game
* without actually ending the current game. It calls {@link #gameWillReset} followed by the
* standard game start processing ({@link #gameWillStart} and {@link #gameDidStart}). Derived
* classes should override these calldown functions (rather than this function) if they need to
* do things before or after the game resets.
*/
public void resetGame ()
{
@@ -597,21 +564,20 @@ public class GameManager extends PlaceManager
}
/**
* Called by the client when the player is ready for the game to start.
* This method is dispatched dynamically by {@link #messageReceived}.
* Called by the client when the player is ready for the game to start. This method is
* dispatched dynamically by {@link #messageReceived}.
*/
public void playerReady (BodyObject caller)
{
// get the user's player index
int pidx = _gameobj.getPlayerIndex(caller.getVisibleName());
if (pidx == -1) {
// only complain if this is not a party game, since it's
// perfectly normal to receive a player ready notification
// from a user entering a party game in which they're not yet
// only complain if this is not a party game, since it's perfectly normal to receive a
// player ready notification from a user entering a party game in which they're not yet
// a participant
if (needsNoShowTimer()) {
Log.warning("Received playerReady() from non-player? " +
"[game=" + where() + ", who=" + caller.who() + "].");
Log.warning("Received playerReady() from non-player? [game=" + where() +
", who=" + caller.who() + "].");
}
return;
}
@@ -626,8 +592,8 @@ public class GameManager extends PlaceManager
}
/**
* Returns true if all (non-AI) players have delivered their {@link
* #playerReady} notifications, false if they have not.
* Returns true if all (non-AI) players have delivered their {@link #playerReady}
* notifications, false if they have not.
*/
public boolean allPlayersReady ()
{
@@ -640,10 +606,9 @@ public class GameManager extends PlaceManager
}
/**
* Returns true if the player at the specified slot is ready (or if
* there is meant to be no player in that slot), false if there is
* meant to be a player in the specified slot and they have not yet
* reported that they are ready.
* Returns true if the player at the specified slot is ready (or if there is meant to be no
* player in that slot), false if there is meant to be a player in the specified slot and they
* have not yet reported that they are ready.
*/
public boolean playerIsReady (int pidx)
{
@@ -653,20 +618,19 @@ public class GameManager extends PlaceManager
}
/**
* Returns true if this game requires a no-show timer. The default
* implementation returns true for non-party games and false for party
* games. Derived classes may wish to change or augment this behavior.
* Returns true if this game requires a no-show timer. The default implementation returns true
* for non-party games and false for party games. Derived classes may wish to change or augment
* this behavior.
*/
protected boolean needsNoShowTimer ()
{
return (getGameType() == GameConfig.SEATED_GAME);
return (getMatchType() == GameConfig.SEATED_GAME);
}
/**
* Derived classes that need their AIs to be ticked periodically
* should override this method and return true. Many AIs can act
* entirely in reaction to game state changes and need no periodic
* ticking which is why ticking is disabled by default.
* Derived classes that need their AIs to be ticked periodically should override this method
* and return true. Many AIs can act entirely in reaction to game state changes and need no
* periodic ticking which is why ticking is disabled by default.
*
* @see #tickAIs
*/
@@ -676,9 +640,8 @@ public class GameManager extends PlaceManager
}
/**
* Called when a player was added to the game. Derived classes may
* override this method to perform any game-specific actions they
* desire, but should be sure to call
* Called when a player was added to the game. Derived classes may override this method to
* perform any game-specific actions they desire, but should be sure to call
* <code>super.playerWasAdded()</code>.
*
* @param player the username of the player added to the game.
@@ -689,22 +652,19 @@ public class GameManager extends PlaceManager
}
/**
* Called when a player was removed from the game. Derived classes
* may override this method to perform any game-specific actions they
* desire, but should be sure to call
* Called when a player was removed from the game. Derived classes may override this method to
* perform any game-specific actions they desire, but should be sure to call
* <code>super.playerWasRemoved()</code>.
*
* @param player the username of the player removed from the game.
* @param pidx the player index of the player before they were removed
* from the game.
* @param pidx the player index of the player before they were removed from the game.
*/
protected void playerWasRemoved (Name player, int pidx)
{
}
/**
* Called when a player has been replaced via a call to {@link
* #replacePlayer}.
* Called when a player has been replaced via a call to {@link #replacePlayer}.
*/
protected void playerWasReplaced (int pidx, Name oldPlayer, Name newPlayer)
{
@@ -717,14 +677,12 @@ public class GameManager extends PlaceManager
{
BodyObject user = getPlayer(pidx);
if (user == null) {
// body object can be null for ai players
return;
return; // body object can be null for ai players
}
DObject place = CrowdServer.omgr.getObject(user.location);
if (place != null) {
place.postMessage(PLAYER_KNOCKED_OUT,
new Object[] { new int[] { user.getOid() } });
place.postMessage(PLAYER_KNOCKED_OUT, new Object[] { new int[] { user.getOid() } });
}
}
@@ -770,8 +728,8 @@ public class GameManager extends PlaceManager
// set up an initial player status array
_gameobj.setPlayerStatus(new int[getPlayerSlots()]);
// save off the number of players so that we needn't repeatedly
// iterate through the player name array server-side unnecessarily
// 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
@@ -780,8 +738,8 @@ public class GameManager extends PlaceManager
// 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)
// let the players of this game know that we're ready to roll (if we have a specific set of
// players)
for (int ii = 0; ii < getPlayerSlots(); ii++) {
// skip non-existent players and AIs
if (!_gameobj.isOccupiedPlayer(ii) || isAI(ii)) {
@@ -790,8 +748,7 @@ public class GameManager extends PlaceManager
BodyObject bobj = CrowdServer.lookupBody(_gameobj.players[ii]);
if (bobj == null) {
Log.warning("Unable to deliver game ready to non-existent " +
"player [game=" + where() +
Log.warning("Unable to deliver game ready to non-existent player [game=" + where() +
", player=" + _gameobj.players[ii] + "].");
continue;
}
@@ -841,22 +798,21 @@ public class GameManager extends PlaceManager
endPlayerGame(pidx);
}
// then complete the bodyLeft() processing which may result in a call
// to placeBecameEmpty() which will shut the game down
// then complete the bodyLeft() processing which may result in a call to placeBecameEmpty()
// which will shut the game down
super.bodyLeft(bodyOid);
}
/**
* When a game room becomes empty, we cancel the game if it's still in
* progress and close down the game room.
* When a game room becomes empty, we cancel the game if it's still in progress and close down
* the game room.
*/
protected void placeBecameEmpty ()
{
// Log.info("Game room empty. Going away. [game=" + where() + "].");
// if we're in play then move to game over
if (_gameobj.state != GameObject.PRE_GAME &&
_gameobj.state != GameObject.GAME_OVER &&
if (_gameobj.state != GameObject.PRE_GAME && _gameobj.state != GameObject.GAME_OVER &&
_gameobj.state != GameObject.CANCELLED) {
_gameobj.setState(GameObject.GAME_OVER);
// and shutdown directly
@@ -870,23 +826,21 @@ public class GameManager extends PlaceManager
}
/**
* Called when all players have arrived in the game room. By default,
* this starts up the game, but a manager may wish to override this
* and start the game according to different criterion.
* Called when all players have arrived in the game room. By default, this starts up the game,
* but a manager may wish to override this and start the game according to different criterion.
*/
protected void playersAllHere ()
{
// if we're a seated game and we haven't already started, start.
if ((getGameType() == GameConfig.SEATED_GAME) && (_gameobj.state == GameObject.PRE_GAME)) {
if ((getMatchType() == GameConfig.SEATED_GAME) && (_gameobj.state == GameObject.PRE_GAME)) {
startGame();
}
}
/**
* Called after the no-show delay has expired following the delivery
* of notifications to all players that the game is ready.
* <em>Note:</em> this is not called for party games. Those games have
* a human who decides when to start the game.
* Called after the no-show delay has expired following the delivery of notifications to all
* players that the game is ready. <em>Note:</em> this is not called for party games. Those
* games have a human who decides when to start the game.
*/
protected void checkForNoShows ()
{
@@ -914,14 +868,13 @@ public class GameManager extends PlaceManager
}
/**
* This is called when some, but not all, players failed to show up
* for a game. The default implementation simply cancels the game.
* This is called when some, but not all, players failed to show up for a game. The default
* implementation simply cancels the game.
*/
protected void handlePartialNoShow ()
{
// mark the no-show players; this will cause allPlayersReady() to
// think that everyone has arrived, but still allow us to tell who
// has not shown up in gameDidStart()
// mark the no-show players; this will cause allPlayersReady() to think that everyone has
// arrived, but still allow us to tell who has not shown up in gameDidStart()
int humansHere = 0;
for (int ii = 0; ii < _playerOids.length; ii++) {
if (_playerOids[ii] == 0) {
@@ -938,9 +891,8 @@ public class GameManager extends PlaceManager
cancelGame();
} else {
// go ahead and report that everyone is ready (which will start the
// game); gameDidStart() will take care of giving the boot to
// anyone who isn't around
// go ahead and report that everyone is ready (which will start the game);
// gameDidStart() will take care of giving the boot to anyone who isn't around
Log.info("Forcing start of partial no-show game [game=" + where() +
", poids=" + StringUtil.toString(_playerOids) + "].");
playersAllHere();
@@ -948,8 +900,8 @@ public class GameManager extends PlaceManager
}
/**
* @return true if we should start the game even without any humans.
* Default implementation always returns false.
* @return true if we should start the game even without any humans. Default implementation
* always returns false.
*/
protected boolean startWithoutHumans ()
{
@@ -957,11 +909,9 @@ public class GameManager extends PlaceManager
}
/**
* Called when the game is about to start, but before the game start
* notification has been delivered to the players. Derived classes
* should override this if they need to perform some pre-start
* activities, but should be sure to call
* <code>super.gameWillStart()</code>.
* Called when the game is about to start, but before the game start notification has been
* delivered to the players. Derived classes should override this if they need to perform some
* pre-start activities, but should be sure to call <code>super.gameWillStart()</code>.
*/
protected void gameWillStart ()
{
@@ -977,8 +927,8 @@ public class GameManager extends PlaceManager
}
/**
* Called when the game state changes. This happens after the attribute
* change event has propagated.
* Called when the game state changes. This happens after the attribute change event has
* propagated.
*
* @param state the new game state.
* @param oldState the previous game state.
@@ -991,8 +941,8 @@ public class GameManager extends PlaceManager
break;
case GameObject.GAME_OVER:
// we do some jiggery pokery to allow derived game objects to have
// different notions of what it means to be in play
// we do some jiggery pokery to allow derived game objects to have different notions of
// what it means to be in play
_gameobj.state = oldState;
boolean wasInPlay = _gameobj.isInPlay();
_gameobj.state = state;
@@ -1016,11 +966,10 @@ public class GameManager extends PlaceManager
}
/**
* Called after the game start notification was dispatched. Derived
* classes can override this to put whatever wheels they might need
* into motion now that the game is started (if anything other than
* transitioning the game to {@link GameObject#IN_PLAY} is necessary),
* but should be sure to call <code>super.gameDidStart()</code>.
* Called after the game start notification was dispatched. Derived classes can override this
* to put whatever wheels they might need into motion now that the game is started (if anything
* other than transitioning the game to {@link GameObject#IN_PLAY} is necessary), but should be
* sure to call <code>super.gameDidStart()</code>.
*/
protected void gameDidStart ()
{
@@ -1034,8 +983,7 @@ public class GameManager extends PlaceManager
// inform the players of any pending messages.
if (_startmsgs != null) {
for (Tuple<String,String> mtup : _startmsgs) {
systemMessage(mtup.left, // bundle
mtup.right); // message
systemMessage(mtup.left, /* bundle */ mtup.right /* message */);
}
_startmsgs = null;
}
@@ -1045,8 +993,7 @@ public class GameManager extends PlaceManager
AIGameTicker.registerAIGame(this);
}
// any players who have not claimed that they are ready should now
// be given le boote royale
// any players who have not claimed that they are ready should now be given le boote royale
for (int ii = 0; ii < _playerOids.length; ii++) {
if (_playerOids[ii] == -1) {
Log.info("Booting no-show player [game=" + where() +
@@ -1058,8 +1005,7 @@ public class GameManager extends PlaceManager
}
/**
* Called by the {@link AIGameTicker} if we're registered as an AI
* game.
* Called by the {@link AIGameTicker} if we're registered as an AI game.
*/
protected void tickAIs ()
{
@@ -1097,10 +1043,9 @@ public class GameManager extends PlaceManager
}
/**
* Called when a player has been marked as knocked out but before the
* knock-out status update has been sent to the players. Any status
* information that needs be updated in light of the knocked out
* player can be updated here.
* Called when a player has been marked as knocked out but before the knock-out status update
* has been sent to the players. Any status information that needs be updated in light of the
* knocked out player can be updated here.
*/
protected void playerGameDidEnd (int pidx)
{
@@ -1109,13 +1054,11 @@ public class GameManager extends PlaceManager
}
/**
* Called when a player leaves the game in order to determine whether
* the game should be ended based on its current state, which will
* include updated player status for the player in question. The
* default implementation returns true if the game is in play and
* there is only one player left. Derived classes may wish to
* override this method in order to customize the required end-game
* conditions.
* Called when a player leaves the game in order to determine whether the game should be ended
* based on its current state, which will include updated player status for the player in
* question. The default implementation returns true if the game is in play and there is only
* one player left. Derived classes may wish to override this method in order to customize the
* required end-game conditions.
*/
protected boolean shouldEndGame ()
{
@@ -1123,11 +1066,10 @@ public class GameManager extends PlaceManager
}
/**
* Assigns the final winning status for each player to their respect
* player index in the supplied array. This will be called by {@link
* #endGame} when the game is over. The default implementation marks
* no players as winners. Derived classes should override this method
* in order to customize the winning conditions.
* Assigns the final winning status for each player to their respect player index in the
* supplied array. This will be called by {@link #endGame} when the game is over. The default
* implementation marks no players as winners. Derived classes should override this method in
* order to customize the winning conditions.
*/
protected void assignWinners (boolean[] winners)
{
@@ -1135,11 +1077,9 @@ public class GameManager extends PlaceManager
}
/**
* Called when the game is about to end, but before the game end
* notification has been delivered to the players. Derived classes
* should override this if they need to perform some pre-end
* activities, but should be sure to call
* <code>super.gameWillEnd()</code>.
* Called when the game is about to end, but before the game end notification has been
* delivered to the players. Derived classes should override this if they need to perform some
* pre-end activities, but should be sure to call <code>super.gameWillEnd()</code>.
*/
protected void gameWillEnd ()
{
@@ -1152,9 +1092,8 @@ public class GameManager extends PlaceManager
}
/**
* Called after the game has transitioned to the {@link
* GameObject#GAME_OVER} state. Derived classes should override this
* to perform any post-game activities, but should be sure to call
* Called after the game has transitioned to the {@link GameObject#GAME_OVER} state. Derived
* classes should override this to perform any post-game activities, but should be sure to call
* <code>super.gameDidEnd()</code>.
*/
protected void gameDidEnd ()
@@ -1181,15 +1120,13 @@ public class GameManager extends PlaceManager
}
/**
* Called to let the manager know that the game was cancelled (and may be
* about to be shutdown if there's no one in the room). In the base
* framework a game will only be canceled if no one shows up, so {@link
* #gameWillStart}, etc. will never have been called and thus {@link
* #gameWillEnd}, etc. will not be called. However, if a game chooses to
* cancel itself for whatever reason, no effort will be made to call {@link
* #endGame} and the game ending call backs so that game can override this
* method to do anything it needs. Note that {@link #didShutdown} will be
* called in every case and that's generally the best place to free
* Called to let the manager know that the game was cancelled (and may be about to be shutdown
* if there's no one in the room). In the base framework a game will only be canceled if no one
* shows up, so {@link #gameWillStart}, etc. will never have been called and thus {@link
* #gameWillEnd}, etc. will not be called. However, if a game chooses to cancel itself for
* whatever reason, no effort will be made to call {@link #endGame} and the game ending call
* backs so that game can override this method to do anything it needs. Note that {@link
* #didShutdown} will be called in every case and that's generally the best place to free
* resources so this method may not be needed.
*/
protected void gameWasCancelled ()
@@ -1198,8 +1135,7 @@ public class GameManager extends PlaceManager
}
/**
* Report winner and loser oids to each room that any of the
* winners/losers is in.
* Report winner and loser oids to each room that any of the winners/losers is in.
*/
protected void reportWinnersAndLosers ()
{
@@ -1218,8 +1154,7 @@ public class GameManager extends PlaceManager
}
}
Object[] args =
new Object[] { winners.toIntArray(), losers.toIntArray() };
Object[] args = new Object[] { winners.toIntArray(), losers.toIntArray() };
// now send a message event to each room
for (int ii=0, nn = places.size(); ii < nn; ii++) {
@@ -1231,10 +1166,9 @@ public class GameManager extends PlaceManager
}
/**
* Called when the game is about to reset, but before the board has
* been re-initialized or any other clearing out of game data has
* taken place. Derived classes should override this if they need to
* perform some pre-reset activities.
* Called when the game is about to reset, but before the board has been re-initialized or any
* other clearing out of game data has taken place. Derived classes should override this if
* they need to perform some pre-reset activities.
*/
protected void gameWillReset ()
{
@@ -1250,8 +1184,8 @@ public class GameManager extends PlaceManager
}
/**
* Gives game managers an opportunity to perform periodic processing
* that is not driven by events generated by the player.
* Gives game managers an opportunity to perform periodic processing that is not driven by
* events generated by the player.
*/
protected void tick (long tickStamp)
{
@@ -1259,8 +1193,7 @@ public class GameManager extends PlaceManager
}
/**
* Called periodically to call {@link #tick} on all registered game
* managers.
* Called periodically to call {@link #tick} on all registered game managers.
*/
protected static void tickAllGames ()
{
@@ -1271,8 +1204,7 @@ public class GameManager extends PlaceManager
try {
gmgr.tick(now);
} catch (Exception e) {
Log.warning(
"Game manager choked during tick [gmgr=" + gmgr + "].");
Log.warning("Game manager choked during tick [gmgr=" + gmgr + "].");
Log.logStackTrace(e);
}
}
@@ -1298,12 +1230,11 @@ public class GameManager extends PlaceManager
}
/** Listens for game state changes. */
protected AttributeChangeListener _stateListener =
new AttributeChangeListener() {
protected AttributeChangeListener _stateListener = new AttributeChangeListener() {
public void attributeChanged (AttributeChangedEvent event) {
if (event.getName().equals(GameObject.STATE)) {
stateDidChange(_committedState = event.getIntValue(),
((Integer)event.getOldValue()).intValue());
((Integer)event.getOldValue()).intValue());
}
}
};
@@ -1320,19 +1251,17 @@ public class GameManager extends PlaceManager
/** The oids of our player and AI body objects. */
protected int[] _playerOids;
/** If AIs are present, contains their configuration, or null at human
* player indexes. */
/** If AIs are present, contains their configuration, or null at human player indexes. */
protected GameAI[] _AIs;
/** If non-null, contains bundles and messages that should be sent as
* system messages once the game has started. */
/** If non-null, contains bundles and messages that should be sent as system messages once the
* game has started. */
protected ArrayList<Tuple<String,String>> _startmsgs;
/** Our delegate operator to tick AIs. */
protected TickAIDelegateOp _tickAIOp;
/** The state of the game that has been propagated to our
* subscribers. */
/** The state of the game that has been propagated to our subscribers. */
protected int _committedState;
/** TEMP: debugging the pending rating double release bug. */
@@ -1342,14 +1271,13 @@ public class GameManager extends PlaceManager
protected boolean _postponedStart = false;
/** A list of all currently active game managers. */
protected static ArrayList<GameManager> _managers =
new ArrayList<GameManager>();
protected static ArrayList<GameManager> _managers = new ArrayList<GameManager>();
/** The interval for the game manager tick. */
protected static Interval _tickInterval;
/** We give players 30 seconds to turn up in a game; after that,
* they're considered a no show. */
/** We give players 30 seconds to turn up in a game; after that, they're considered a no
* show. */
protected static final long NOSHOW_DELAY = 30 * 1000L;
/** The delay in milliseconds between ticking of all game managers. */