From aad235c7f2fba3e1cc65ae9c4fb8f5a337f6e61e Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 24 Aug 2006 17:37:25 +0000 Subject: [PATCH] Nixed GameService as it can be more simply implemented with the new dynamically dispatched message event system. Rearranged the methods in GameManager into some semblance of order. git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@60 c613c5cb-e716-0410-b11b-feb51c14d237 --- src/java/com/threerings/parlor/Log.java | 15 +- .../parlor/game/client/GameController.java | 2 +- .../parlor/game/client/GameService.java | 38 - .../parlor/game/data/GameMarshaller.java | 50 -- .../parlor/game/data/GameObject.java | 22 - .../parlor/game/server/GameDispatcher.java | 69 -- .../parlor/game/server/GameManager.java | 731 +++++++++--------- .../parlor/game/server/GameProvider.java | 39 - 8 files changed, 376 insertions(+), 590 deletions(-) delete mode 100644 src/java/com/threerings/parlor/game/client/GameService.java delete mode 100644 src/java/com/threerings/parlor/game/data/GameMarshaller.java delete mode 100644 src/java/com/threerings/parlor/game/server/GameDispatcher.java delete mode 100644 src/java/com/threerings/parlor/game/server/GameProvider.java diff --git a/src/java/com/threerings/parlor/Log.java b/src/java/com/threerings/parlor/Log.java index 76411fe6..8cdbcfc5 100644 --- a/src/java/com/threerings/parlor/Log.java +++ b/src/java/com/threerings/parlor/Log.java @@ -21,19 +21,22 @@ package com.threerings.parlor; +import java.util.logging.Level; +import java.util.logging.Logger; + /** - * A placeholder class that contains a reference to the log object used by - * the Parlor services. + * Contains a reference to the log object used by the Parlor services. */ public class Log { - public static com.samskivert.util.Log log = - new com.samskivert.util.Log("parlor"); + /** We dispatch our log messages through this logger. */ + public static Logger log = + Logger.getLogger("com.threerings.vilya.parlor"); /** Convenience function. */ public static void debug (String message) { - log.debug(message); + log.fine(message); } /** Convenience function. */ @@ -51,6 +54,6 @@ public class Log /** Convenience function. */ public static void logStackTrace (Throwable t) { - log.logStackTrace(com.samskivert.util.Log.WARNING, t); + log.log(Level.WARNING, t.getMessage(), t); } } diff --git a/src/java/com/threerings/parlor/game/client/GameController.java b/src/java/com/threerings/parlor/game/client/GameController.java index 7cdc9005..b1a2c3fa 100644 --- a/src/java/com/threerings/parlor/game/client/GameController.java +++ b/src/java/com/threerings/parlor/game/client/GameController.java @@ -235,7 +235,7 @@ public abstract class GameController extends PlaceController protected void playerReady () { Log.info("Reporting ready " + _gobj.which() + "."); - _gobj.gameService.playerReady(_ctx.getClient()); + _gobj.manager.invoke("playerReady"); } /** diff --git a/src/java/com/threerings/parlor/game/client/GameService.java b/src/java/com/threerings/parlor/game/client/GameService.java deleted file mode 100644 index 15ec7c8a..00000000 --- a/src/java/com/threerings/parlor/game/client/GameService.java +++ /dev/null @@ -1,38 +0,0 @@ -// -// $Id: GameService.java 3600 2005-06-15 23:46:31Z ray $ -// -// Narya library - tools for developing networked games -// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved -// http://www.threerings.net/code/narya/ -// -// This library is free software; you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation; either version 2.1 of the License, or -// (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -package com.threerings.parlor.game.client; - -import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationService; - -/** - * Provides services used by game clients to request that actions be taken - * by the game manager. - */ -public interface GameService extends InvocationService -{ - /** - * Lets the game manager know that the calling player is in the game - * room and ready to play. - */ - public void playerReady (Client client); -} diff --git a/src/java/com/threerings/parlor/game/data/GameMarshaller.java b/src/java/com/threerings/parlor/game/data/GameMarshaller.java deleted file mode 100644 index 00fdffe8..00000000 --- a/src/java/com/threerings/parlor/game/data/GameMarshaller.java +++ /dev/null @@ -1,50 +0,0 @@ -// -// $Id: GameMarshaller.java 4145 2006-05-24 01:24:24Z ray $ -// -// Narya library - tools for developing networked games -// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved -// http://www.threerings.net/code/narya/ -// -// This library is free software; you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation; either version 2.1 of the License, or -// (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -package com.threerings.parlor.game.data; - -import com.threerings.parlor.game.client.GameService; -import com.threerings.presents.client.Client; -import com.threerings.presents.data.InvocationMarshaller; -import com.threerings.presents.dobj.InvocationResponseEvent; - -/** - * Provides the implementation of the {@link GameService} interface - * that marshalls the arguments and delivers the request to the provider - * on the server. Also provides an implementation of the response listener - * interfaces that marshall the response arguments and deliver them back - * to the requesting client. - */ -public class GameMarshaller extends InvocationMarshaller - implements GameService -{ - /** The method id used to dispatch {@link #playerReady} requests. */ - public static final int PLAYER_READY = 1; - - // documentation inherited from interface - public void playerReady (Client arg1) - { - sendRequest(arg1, PLAYER_READY, new Object[] { - - }); - } - -} diff --git a/src/java/com/threerings/parlor/game/data/GameObject.java b/src/java/com/threerings/parlor/game/data/GameObject.java index 5db78ffd..ea967eb7 100644 --- a/src/java/com/threerings/parlor/game/data/GameObject.java +++ b/src/java/com/threerings/parlor/game/data/GameObject.java @@ -40,9 +40,6 @@ import com.threerings.crowd.data.PlaceObject; public class GameObject extends PlaceObject { // AUTO-GENERATED: FIELDS START - /** The field name of the gameService field. */ - public static final String GAME_SERVICE = "gameService"; - /** The field name of the state field. */ public static final String STATE = "state"; @@ -86,9 +83,6 @@ public class GameObject extends PlaceObject * game prematurely. */ public static final int PLAYER_LEFT_GAME = 1; - /** Provides general game invocation services. */ - public GameMarshaller gameService; - /** The game state, one of {@link #PRE_GAME}, {@link #IN_PLAY}, * {@link #GAME_OVER}, or {@link #CANCELLED}. */ public int state = PRE_GAME; @@ -265,22 +259,6 @@ public class GameObject extends PlaceObject } // AUTO-GENERATED: METHODS START - /** - * Requests that the gameService 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 setGameService (GameMarshaller value) - { - GameMarshaller ovalue = this.gameService; - requestAttributeChange( - GAME_SERVICE, value, ovalue); - this.gameService = value; - } - /** * Requests that the state field be set to the * specified value. The local value will be updated immediately and an diff --git a/src/java/com/threerings/parlor/game/server/GameDispatcher.java b/src/java/com/threerings/parlor/game/server/GameDispatcher.java deleted file mode 100644 index f438b178..00000000 --- a/src/java/com/threerings/parlor/game/server/GameDispatcher.java +++ /dev/null @@ -1,69 +0,0 @@ -// -// $Id: GameDispatcher.java 4145 2006-05-24 01:24:24Z ray $ -// -// Narya library - tools for developing networked games -// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved -// http://www.threerings.net/code/narya/ -// -// This library is free software; you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation; either version 2.1 of the License, or -// (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -package com.threerings.parlor.game.server; - -import com.threerings.parlor.game.client.GameService; -import com.threerings.parlor.game.data.GameMarshaller; -import com.threerings.presents.client.Client; -import com.threerings.presents.data.ClientObject; -import com.threerings.presents.data.InvocationMarshaller; -import com.threerings.presents.server.InvocationDispatcher; -import com.threerings.presents.server.InvocationException; - -/** - * Dispatches requests to the {@link GameProvider}. - */ -public class GameDispatcher extends InvocationDispatcher -{ - /** - * Creates a dispatcher that may be registered to dispatch invocation - * service requests for the specified provider. - */ - public GameDispatcher (GameProvider provider) - { - this.provider = provider; - } - - // documentation inherited - public InvocationMarshaller createMarshaller () - { - return new GameMarshaller(); - } - - // documentation inherited - public void dispatchRequest ( - ClientObject source, int methodId, Object[] args) - throws InvocationException - { - switch (methodId) { - case GameMarshaller.PLAYER_READY: - ((GameProvider)provider).playerReady( - source - ); - return; - - default: - super.dispatchRequest(source, methodId, args); - return; - } - } -} diff --git a/src/java/com/threerings/parlor/game/server/GameManager.java b/src/java/com/threerings/parlor/game/server/GameManager.java index 9426d8ec..bff9357b 100644 --- a/src/java/com/threerings/parlor/game/server/GameManager.java +++ b/src/java/com/threerings/parlor/game/server/GameManager.java @@ -37,6 +37,7 @@ import com.threerings.presents.data.ClientObject; import com.threerings.presents.dobj.AttributeChangeListener; import com.threerings.presents.dobj.AttributeChangedEvent; import com.threerings.presents.dobj.DObject; +import com.threerings.presents.dobj.MessageEvent; import com.threerings.crowd.chat.server.SpeakProvider; @@ -51,7 +52,6 @@ import com.threerings.parlor.data.ParlorCodes; import com.threerings.parlor.game.data.GameAI; import com.threerings.parlor.game.data.GameCodes; import com.threerings.parlor.game.data.GameConfig; -import com.threerings.parlor.game.data.GameMarshaller; import com.threerings.parlor.game.data.GameObject; import com.threerings.parlor.game.data.PartyGameConfig; import com.threerings.parlor.server.ParlorSender; @@ -68,37 +68,8 @@ import com.threerings.util.MessageBundle; * bodies in that location. */ public class GameManager extends PlaceManager - implements ParlorCodes, GameCodes, GameProvider, AttributeChangeListener + implements ParlorCodes, GameCodes { - // documentation inherited - protected void didInit () - { - super.didInit(); - - // save off a casted reference to our config - _gameconfig = (GameConfig)_config; - - // register this game manager - _managers.add(this); - - // and start up a tick interval if we've not already got one - if (_tickInterval == null) { - _tickInterval = new Interval(CrowdServer.omgr) { - public void expired () { - tickAllGames(); - } - }; - _tickInterval.schedule(TICK_DELAY, true); - } - - // configure our AIs - for (int ii = 0; ii < _gameconfig.ais.length; ii++) { - if (_gameconfig.ais[ii] != null) { - setAI(ii, _gameconfig.ais[ii]); - } - } - } - /** * Returns the configuration object for the game being managed by this * manager. @@ -212,19 +183,6 @@ public class GameManager extends PlaceManager return true; } - /** - * 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 - * super.playerWasAdded(). - * - * @param player the username of the player added to the game. - * @param pidx the player index of the player added to the game. - */ - protected void playerWasAdded (Name player, int pidx) - { - } - /** * 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 @@ -268,20 +226,6 @@ public class GameManager extends PlaceManager return true; } - /** - * 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 - * super.playerWasRemoved(). - * - * @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. - */ - protected void playerWasRemoved (Name player, int pidx) - { - } - /** * Replaces the player at the specified index and calls {@link * #playerWasReplaced} to let derived classes and delegates know @@ -304,14 +248,6 @@ public class GameManager extends PlaceManager }); } - /** - * Called when a player has been replaced via a call to {@link - * #replacePlayer}. - */ - protected void playerWasReplaced (int pidx, Name oldPlayer, Name newPlayer) - { - } - /** * Returns the user object for the player with the specified index or * null if the player at that index is not online. @@ -467,6 +403,322 @@ public class GameManager extends PlaceManager SpeakProvider.sendInfo(_gameobj, msgbundle, msg); } + /** + * 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. + */ + 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=" + _gameobj.which() + "]."); + Thread.dumpStack(); + return false; + } + + // TEMP: clear out our game end tracker + _gameEndTracker.clear(); + + // make sure everyone has turned up + if (!allPlayersReady()) { + Log.warning("Requested to start a game that is still " + + "awaiting players [game=" + _gameobj.which() + + ", 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 (_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 " + + "[which=" + _gameobj.which() + "]."); + _postponedStart = false; + return false; + } + Log.info("Postponing start of still-ending game " + + "[which=" + _gameobj.which() + "]."); + _postponedStart = true; + // TEMP: track down weirdness + final Exception firstCall = new Exception(); + // End: temp + CrowdServer.omgr.postRunnable(new Runnable() { + public void run () { + boolean result = startGame(); + // TEMP: track down weirdness + if (!result && !_postponedStart) { + Log.warning("First call to startGame: "); + Log.logStackTrace(firstCall); + } + // End: temp + } + }); + return true; + } + + // Ah, good, not postponing. + _postponedStart = false; + + // let the derived class do its pre-start stuff + gameWillStart(); + + // transition the game to started + _gameobj.setState(GameObject.IN_PLAY); + + // when our events are applied, we'll call gameDidStart() + return true; + } + + /** + * Ends the game for the given player. + */ + public void endPlayerGame (int pidx) + { + // go for a little transactional efficiency + _gameobj.startTransaction(); + try { + // end the player's game + if (_gameobj.playerStatus != null) { + _gameobj.setPlayerStatusAt(GameObject.PLAYER_LEFT_GAME, pidx); + } + + // let derived classes do some business + playerGameDidEnd(pidx); + + announcePlayerGameOver(pidx); + + } finally { + _gameobj.commitTransaction(); + } + + // if it's time to end the game, then do so + if (shouldEndGame()) { + endGame(); + } else { + // 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. + */ + public void endGame () + { + // TEMP: debug pending rating repeat bug + if (_gameEndTracker.checkCall( + "Requested to end already ended game " + + "[game=" + _gameobj.which() + "].")) { + return; + } + // END TEMP + + if (!_gameobj.isInPlay()) { + Log.info("Refusing to end game that was not in play " + + "[game=" + _gameobj.which() + "]."); + return; + } + + _gameobj.startTransaction(); + try { + // let the derived class do its pre-end stuff + gameWillEnd(); + + // determine winners and set them in the game object + boolean[] winners = new boolean[getPlayerSlots()]; + assignWinners(winners); + _gameobj.setWinners(winners); + + // transition to the game over state + _gameobj.setState(GameObject.GAME_OVER); + + } finally { + _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 + } + + /** + * 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. + */ + public boolean cancelGame () + { + if (_gameobj.state != GameObject.GAME_OVER && + _gameobj.state != GameObject.CANCELLED) { + _gameobj.setState(GameObject.CANCELLED); + return true; + } + return false; + } + + /** + * 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 () + { + return (_gameobj.state == GameObject.GAME_OVER); + } + + /** + * 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 () + { + // let the derived class do its pre-reset stuff + gameWillReset(); + // do the standard game start processing + gameWillStart(); + // transition to in-play which will trigger a call to gameDidStart() + _gameobj.setState(GameObject.IN_PLAY); + } + + /** + * 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 + // a participant + if (!isPartyGame()) { + Log.warning("Received playerReady() from non-player? " + + "[caller=" + caller + "]."); + } + return; + } + + // make a note of this player's oid + _playerOids[pidx] = caller.getOid(); + + // if everyone is now ready to go, get things underway + if (allPlayersReady()) { + playersAllHere(); + } + } + + /** + * Returns true if all (non-AI) players have delivered their {@link + * #playerReady} notifications, false if they have not. + */ + public boolean allPlayersReady () + { + for (int ii = 0; ii < getPlayerSlots(); ii++) { + if (!playerIsReady(ii)) { + return false; + } + } + return true; + } + + /** + * 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) + { + return (!_gameobj.isOccupiedPlayer(pidx) || // unoccupied slot + _playerOids[pidx] != 0 || // player is ready + isAI(pidx)); // player is AI + } + + /** + * 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 !isPartyGame(); + } + + /** + * 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 + */ + protected boolean needsAITick () + { + return false; + } + + /** + * 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 + * super.playerWasAdded(). + * + * @param player the username of the player added to the game. + * @param pidx the player index of the player added to the game. + */ + protected void playerWasAdded (Name player, int pidx) + { + } + + /** + * 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 + * super.playerWasRemoved(). + * + * @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. + */ + protected void playerWasRemoved (Name player, int pidx) + { + } + + /** + * Called when a player has been replaced via a call to {@link + * #replacePlayer}. + */ + protected void playerWasReplaced (int pidx, Name oldPlayer, Name newPlayer) + { + } + /** * Report to the knocked-out player's room that they were knocked out. */ @@ -485,11 +737,41 @@ public class GameManager extends PlaceManager } } - // documentation inherited + @Override // from PlaceManager + protected void didInit () + { + super.didInit(); + + // save off a casted reference to our config + _gameconfig = (GameConfig)_config; + + // register this game manager + _managers.add(this); + + // and start up a tick interval if we've not already got one + if (_tickInterval == null) { + _tickInterval = new Interval(CrowdServer.omgr) { + public void expired () { + tickAllGames(); + } + }; + _tickInterval.schedule(TICK_DELAY, true); + } + + // configure our AIs + for (int ii = 0; ii < _gameconfig.ais.length; ii++) { + if (_gameconfig.ais[ii] != null) { + setAI(ii, _gameconfig.ais[ii]); + } + } + } + + @Override // from PlaceManager protected void didStartup () { // obtain a casted reference to our game object _gameobj = (GameObject)_plobj; + _gameobj.addListener(_stateListener); // stick the players into the game object _gameobj.setPlayers(_gameconfig.players); @@ -504,11 +786,6 @@ public class GameManager extends PlaceManager // instantiate a player oid array which we'll fill in later _playerOids = new int[getPlayerSlots()]; - // 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(); @@ -542,30 +819,7 @@ 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. - */ - protected boolean needsNoShowTimer () - { - return !isPartyGame(); - } - - /** - * 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 - */ - protected boolean needsAITick () - { - return false; - } - - // documentation inherited + @Override // from PlaceManager protected void didShutdown () { super.didShutdown(); @@ -579,11 +833,13 @@ public class GameManager extends PlaceManager _tickInterval = null; } - // clear out our service registration - _invmgr.clearDispatcher(_gameobj.gameService); + if (_gameobj != null) { + // remove our state listener + _gameobj.removeListener(_stateListener); + } } - // documentation inherited + @Override // from PlaceManager protected void bodyLeft (int bodyOid) { // first resign the player from the game @@ -704,86 +960,6 @@ 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. - * - * @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=" + _gameobj.which() + "]."); - Thread.dumpStack(); - return false; - } - - // TEMP: clear out our game end tracker - _gameEndTracker.clear(); - - // make sure everyone has turned up - if (!allPlayersReady()) { - Log.warning("Requested to start a game that is still " + - "awaiting players [game=" + _gameobj.which() + - ", 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 (_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 " + - "[which=" + _gameobj.which() + "]."); - _postponedStart = false; - return false; - } - Log.info("Postponing start of still-ending game " + - "[which=" + _gameobj.which() + "]."); - _postponedStart = true; - // TEMP: track down weirdness - final Exception firstCall = new Exception(); - // End: temp - CrowdServer.omgr.postRunnable(new Runnable() { - public void run () { - boolean result = startGame(); - // TEMP: track down weirdness - if (!result && !_postponedStart) { - Log.warning("First call to startGame: "); - Log.logStackTrace(firstCall); - } - // End: temp - } - }); - return true; - } - - // Ah, good, not postponing. - _postponedStart = false; - - // let the derived class do its pre-start stuff - gameWillStart(); - - // transition the game to started - _gameobj.setState(GameObject.IN_PLAY); - - // when our events are applied, we'll call gameDidStart() - return true; - } - /** * @return true if we should start the game even without any humans. * Default implementation always returns false. @@ -916,38 +1092,6 @@ public class GameManager extends PlaceManager applyToDelegates(_tickAIOp); } - /** - * Ends the game for the given player. - */ - public void endPlayerGame (int pidx) - { - // go for a little transactional efficiency - _gameobj.startTransaction(); - try { - // end the player's game - if (_gameobj.playerStatus != null) { - _gameobj.setPlayerStatusAt(GameObject.PLAYER_LEFT_GAME, pidx); - } - - // let derived classes do some business - playerGameDidEnd(pidx); - - announcePlayerGameOver(pidx); - - } finally { - _gameobj.commitTransaction(); - } - - // if it's time to end the game, then do so - if (shouldEndGame()) { - endGame(); - } else { - // otherwise report that the player was knocked out to other - // people in his/her room - reportPlayerKnockedOut(pidx); - } - } - /** * Announce to everyone in the game that a player's game has ended. */ @@ -982,66 +1126,6 @@ public class GameManager extends PlaceManager return (_gameobj.isInPlay() && _gameobj.getActivePlayerCount() == 1); } - /** - * 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=" + _gameobj.which() + "].")) { - return; - } - // END TEMP - - if (!_gameobj.isInPlay()) { - Log.info("Refusing to end game that was not in play " + - "[game=" + _gameobj.which() + "]."); - return; - } - - _gameobj.startTransaction(); - try { - // let the derived class do its pre-end stuff - gameWillEnd(); - - // determine winners and set them in the game object - boolean[] winners = new boolean[getPlayerSlots()]; - assignWinners(winners); - _gameobj.setWinners(winners); - - // transition to the game over state - _gameobj.setState(GameObject.GAME_OVER); - - } finally { - _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 - } - - /** - * 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. - */ - public boolean cancelGame () - { - if (_gameobj.state != GameObject.GAME_OVER && - _gameobj.state != GameObject.CANCELLED) { - _gameobj.setState(GameObject.CANCELLED); - return true; - } - return false; - } - /** * Assigns the final winning status for each player to their respect * player index in the supplied array. This will be called by {@link @@ -1054,17 +1138,6 @@ public class GameManager extends PlaceManager Arrays.fill(winners, false); } - /** - * 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 () - { - return (_gameobj.state == GameObject.GAME_OVER); - } - /** * Called when the game is about to end, but before the game end * notification has been delivered to the players. Derived classes @@ -1161,25 +1234,6 @@ 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. - */ - public void resetGame () - { - // let the derived class do its pre-reset stuff - gameWillReset(); - // do the standard game start processing - gameWillStart(); - // transition to in-play which will trigger a call to gameDidStart() - _gameobj.setState(GameObject.IN_PLAY); - } - /** * 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 @@ -1199,61 +1253,6 @@ public class GameManager extends PlaceManager }); } - // documentation inherited from interface - public void playerReady (ClientObject caller) - { - BodyObject plobj = (BodyObject)caller; - - // get the user's player index - int pidx = _gameobj.getPlayerIndex(plobj.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 - // a participant - if (!isPartyGame()) { - Log.warning("Received playerReady() from non-player? " + - "[caller=" + caller + "]."); - } - return; - } - - // make a note of this player's oid - _playerOids[pidx] = plobj.getOid(); - - // if everyone is now ready to go, get things underway - if (allPlayersReady()) { - playersAllHere(); - } - } - - /** - * Returns true if all (non-AI) players have delivered their {@link - * #playerReady} notifications, false if they have not. - */ - public boolean allPlayersReady () - { - for (int ii = 0; ii < getPlayerSlots(); ii++) { - if (!playerIsReady(ii)) { - return false; - } - } - return true; - } - - /** - * 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) - { - return (!_gameobj.isOccupiedPlayer(pidx) || // unoccupied slot - _playerOids[pidx] != 0 || // player is ready - isAI(pidx)); // player is AI - } - /** * Gives game managers an opportunity to perform periodic processing * that is not driven by events generated by the player. @@ -1263,15 +1262,6 @@ public class GameManager extends PlaceManager // nothing for now } - // documentation inherited - public void attributeChanged (AttributeChangedEvent event) - { - if (event.getName().equals(GameObject.STATE)) { - stateDidChange(_committedState = event.getIntValue(), - ((Integer)event.getOldValue()).intValue()); - } - } - /** * Called periodically to call {@link #tick} on all registered game * managers. @@ -1311,6 +1301,17 @@ public class GameManager extends PlaceManager protected GameAI _ai; } + /** Listens for game state changes. */ + protected AttributeChangeListener _stateListener = + new AttributeChangeListener() { + public void attributeChanged (AttributeChangedEvent event) { + if (event.getName().equals(GameObject.STATE)) { + stateDidChange(_committedState = event.getIntValue(), + ((Integer)event.getOldValue()).intValue()); + } + } + }; + /** A reference to our game config. */ protected GameConfig _gameconfig; diff --git a/src/java/com/threerings/parlor/game/server/GameProvider.java b/src/java/com/threerings/parlor/game/server/GameProvider.java deleted file mode 100644 index 13f7f90c..00000000 --- a/src/java/com/threerings/parlor/game/server/GameProvider.java +++ /dev/null @@ -1,39 +0,0 @@ -// -// $Id: GameProvider.java 4145 2006-05-24 01:24:24Z ray $ -// -// Narya library - tools for developing networked games -// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved -// http://www.threerings.net/code/narya/ -// -// This library is free software; you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation; either version 2.1 of the License, or -// (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -package com.threerings.parlor.game.server; - -import com.threerings.parlor.game.client.GameService; -import com.threerings.presents.client.Client; -import com.threerings.presents.data.ClientObject; -import com.threerings.presents.server.InvocationException; -import com.threerings.presents.server.InvocationProvider; - -/** - * Defines the server-side of the {@link GameService}. - */ -public interface GameProvider extends InvocationProvider -{ - /** - * Handles a {@link GameService#playerReady} request. - */ - public void playerReady (ClientObject caller); -}