// // $Id: GameManager.java,v 1.19 2002/02/13 03:21:28 mdb Exp $ package com.threerings.parlor.game; import com.threerings.presents.dobj.AttributeChangeListener; import com.threerings.presents.dobj.AttributeChangedEvent; import com.threerings.presents.dobj.MessageEvent; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.PlaceObject; import com.threerings.crowd.server.PlaceManager; import com.threerings.crowd.server.CrowdServer; import com.threerings.crowd.server.CrowdServer; import com.threerings.crowd.server.PlaceManagerDelegate; import com.threerings.parlor.Log; import com.threerings.parlor.client.ParlorCodes; /** * 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 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, AttributeChangeListener
{
// documentation inherited
protected Class getPlaceObjectClass ()
{
return GameObject.class;
}
// documentation inherited
protected void didInit ()
{
super.didInit();
// cast our configuration object (do we need to do this?)
_gconfig = (GameConfig)_config;
// register our message handlers
MessageHandler handler = new PlayerReadyHandler();
registerMessageHandler(PLAYER_READY_NOTIFICATION, handler);
}
/**
* Initializes the game manager with the supplied game configuration
* object. This happens before startup and before the game object has
* been created.
*
* @param players the usernames of all of the players in this game or
* null if the game has no specific set of players.
*/
public void setPlayers (String[] players)
{
// keep this around for now, we'll need it later
_players = players;
// instantiate a player oid array which we'll fill in later
_playerOids = new int[players.length];
}
/**
* Returns an array of the usernames of the players in this game.
*/
public String[] getPlayers ()
{
return _players;
}
// 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);
// let the players of this game know that we're ready to roll (if
// we have a specific set of players)
if (_players != null) {
Object[] args = new Object[] {
new Integer(_gameobj.getOid()) };
for (int i = 0; i < _players.length; i++) {
BodyObject bobj = CrowdServer.lookupBody(_players[i]);
if (bobj == null) {
Log.warning("Unable to deliver game ready to " +
"non-existent player " +
"[gameOid=" + _gameobj.getOid() +
", player=" + _players[i] + "].");
continue;
}
// deliver a game ready notification to the player
CrowdServer.invmgr.sendNotification(
bobj.getOid(), MODULE_NAME, GAME_READY_NOTIFICATION, args);
}
}
}
// documentation inherited
protected void bodyLeft (int bodyOid)
{
super.bodyLeft(bodyOid);
// deal with disappearing players
}
/**
* 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. " +
"[gameOid=" + _gameobj.getOid() + "].");
// shut down the place (which will destroy the game object and
// clean up after everything)
shutdown();
}
/**
* 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.
*/
protected void startGame ()
{
// let the derived class do its pre-start stuff
gameWillStart();
// transition the game to started
_gameobj.setState(GameObject.IN_PLAY);
// wait until we hear the game state transition on the game object
// to invoke our game did start code so that we can be sure that
// any events dispatched on the game object prior to or during the
// call to startGame() have been dispatched
}
/**
* 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.
*/
protected void gameWillStart ()
{
// let our delegates do their business
applyToDelegates(new DelegateOp() {
public void apply (PlaceManagerDelegate delegate) {
((GameManagerDelegate)delegate).gameWillStart();
}
});
}
/**
* 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
* issuing the game start notification is necessary).
*/
protected void gameDidStart ()
{
// let our delegates do their business
applyToDelegates(new DelegateOp() {
public void apply (PlaceManagerDelegate delegate) {
((GameManagerDelegate)delegate).gameDidStart();
}
});
}
/**
* 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 GAME_OVER state.
*/
protected void endGame ()
{
// figure out who won...
// transition to the game over state
_gameobj.setState(GameObject.GAME_OVER);
// 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
}
/**
* Called after the game has transitioned to the
* GAME_OVER state. Derived classes should override this
* to perform any post-game activities.
*/
protected void gameDidEnd ()
{
// let our delegates do their business
applyToDelegates(new DelegateOp() {
public void apply (PlaceManagerDelegate delegate) {
((GameManagerDelegate)delegate).gameDidEnd();
}
});
// calculate ratings and all that...
}
// documentation inherited
public void attributeChanged (AttributeChangedEvent event)
{
if (event.getName().equals(GameObject.STATE)) {
switch (event.getIntValue()) {
case GameObject.IN_PLAY:
// now we do our start of game processing
gameDidStart();
break;
case GameObject.GAME_OVER:
// now we do our end of game processing
gameDidEnd();
break;
}
}
}
/** Handles player ready notifications. */
protected class PlayerReadyHandler implements MessageHandler
{
public void handleEvent (MessageEvent event, PlaceManager pmgr)
{
int cloid = event.getSourceOid();
BodyObject body = (BodyObject)
CrowdServer.omgr.getObject(cloid);
if (body == null) {
Log.warning("Player sent am ready notification and then " +
"disappeared [event=" + event + "].");
return;
}
// make a note of this player's oid and check to see if we're
// all set at the same time
boolean allSet = true;
for (int i = 0; i < _players.length; i++) {
if (_players[i].equals(body.username)) {
_playerOids[i] = body.getOid();
}
if (_playerOids[i] == 0) {
allSet = false;
}
}
// if everyone is now ready to go, start up the game (if we
// haven't already)
if (allSet && _gameobj.state == GameObject.AWAITING_PLAYERS) {
startGame();
}
}
}
/** A reference to our game configuration. */
protected GameConfig _gconfig;
/** A reference to our game object. */
protected GameObject _gameobj;
/** The usernames of the players of this game. */
protected String[] _players;
/** The oids of our player's body objects. */
protected int[] _playerOids;
}