diff --git a/build.xml b/build.xml index 750b6d59..38c054d0 100644 --- a/build.xml +++ b/build.xml @@ -149,7 +149,7 @@ - + Please note that, unlike in the standard set() function, the property will not be + * updated right away, but will require a request to the server and a response back. For this + * reason, there may be a considerable delay between calling testAndSet, and seeing the result + * of the update. * - * The operation is 'atomic', in the sense that testing and setting take place - * during the same server event. In comparison, a separate 'get' followed by - * a 'set' operation would involve two events with two network round-trips, - * and no guarantee that the value won't change between the events. + *

The operation is 'atomic', in the sense that testing and setting take place during the + * same server event. In comparison, a separate 'get' followed by a 'set' operation would + * involve two events with two network round-trips, and no guarantee that the value won't + * change between the events. */ public function testAndSet ( propName :String, newValue :Object, testValue :Object, index :int = -1) :void @@ -236,41 +237,31 @@ public class EZGameControl extends BaseControl } /** - * Register an object to receive whatever events it should receive, - * based on which event listeners it implements. + * Register an object to receive whatever events it should receive, based on which event + * listeners it implements. */ public function registerListener (obj :Object) :void { if (obj is MessageReceivedListener) { var mrl :MessageReceivedListener = (obj as MessageReceivedListener); - addEventListener( - MessageReceivedEvent.TYPE, mrl.messageReceived, - false, 0, true); + addEventListener(MessageReceivedEvent.TYPE, mrl.messageReceived, false, 0, true); } if (obj is PropertyChangedListener) { var pcl :PropertyChangedListener = (obj as PropertyChangedListener); - addEventListener( - PropertyChangedEvent.TYPE, pcl.propertyChanged, - false, 0, true); + addEventListener(PropertyChangedEvent.TYPE, pcl.propertyChanged, false, 0, true); } if (obj is StateChangedListener) { var scl :StateChangedListener = (obj as StateChangedListener); - addEventListener( - StateChangedEvent.GAME_STARTED, scl.stateChanged, - false, 0, true); - addEventListener( - StateChangedEvent.TURN_CHANGED, scl.stateChanged, - false, 0, true); - addEventListener( - StateChangedEvent.GAME_ENDED, scl.stateChanged, - false, 0, true); + addEventListener(StateChangedEvent.CONTROL_CHANGED, scl.stateChanged, false, 0, true); + addEventListener(StateChangedEvent.GAME_STARTED, scl.stateChanged, false, 0, true); + addEventListener(StateChangedEvent.TURN_CHANGED, scl.stateChanged, false, 0, true); + addEventListener(StateChangedEvent.GAME_ENDED, scl.stateChanged, false, 0, true); } if (obj is OccupantChangedListener) { var ocl :OccupantChangedListener = (obj as OccupantChangedListener); addEventListener(OccupantChangedEvent.OCCUPANT_ENTERED, ocl.occupantEntered, - false, 0, true); - addEventListener(OccupantChangedEvent.OCCUPANT_LEFT, ocl.occupantLeft, - false, 0, true); + false, 0, true); + addEventListener(OccupantChangedEvent.OCCUPANT_LEFT, ocl.occupantLeft, false, 0, true); } } @@ -306,61 +297,54 @@ public class EZGameControl extends BaseControl } /** - * Requests a set of random letters from the dictionary service. - * The letters will arrive in a separate message with the specified key, - * as an array of strings. + * Requests a set of random letters from the dictionary service. The letters will arrive in a + * separate message with the specified key, as an array of strings. * * @param locale RFC 3066 string that represents language settings * @param count the number of letters to be produced * @param callback the function that will process the results, of the form: - * function (letters :Array) :void - * where letters is an array of strings containing letters - * for the given language settings (potentially empty). + *

function (letters :Array) :void
+ * where letters is an array of strings containing letters for the given language settings + * (potentially empty). */ - public function getDictionaryLetterSet ( - locale :String, count :int, callback :Function) :void + public function getDictionaryLetterSet (locale :String, count :int, callback :Function) :void { callEZCode("getDictionaryLetterSet_v1", locale, count, callback); } /** - * Requests a set of random letters from the dictionary service. - * The letters will arrive in a separate message with the specified key, - * as an array of strings. + * Requests a set of random letters from the dictionary service. The letters will arrive in a + * separate message with the specified key, as an array of strings. * * @param RFC 3066 string that represents language settings * @param word the string contains the word to be checked * @param callback the function that will process the results, of the form: - * function (word :String, result :Boolean) :void - * where word is a copy of the word that was requested, and result - * specifies whether the word is valid given language settings + *
function (word :String, result :Boolean) :void
+ * where word is a copy of the word that was requested, and result specifies whether the word + * is valid given language settings */ - public function checkDictionaryWord ( - locale :String, word :String, callback :Function) :void + public function checkDictionaryWord (locale :String, word :String, callback :Function) :void { callEZCode("checkDictionaryWord_v1", locale, word, callback); } /** - * Send a "message" to other clients subscribed to the game. - * These is similar to setting a property, except that the - * value will not be saved- it will merely end up coming out - * as a MessageReceivedEvent. + * Send a "message" to other clients subscribed to the game. These is similar to setting a + * property, except that the value will not be saved- it will merely end up coming out as a + * MessageReceivedEvent. * - * @param playerId if 0 (or unset), sends to all players, otherwise - * the message will be private to just one player + * @param playerId if 0 (or unset), sends to all players, otherwise the message will be private + * to just one player */ - public function sendMessage ( - messageName :String, value :Object, playerId :int = 0) :void + public function sendMessage (messageName :String, value :Object, playerId :int = 0) :void { callEZCode("sendMessage_v2", messageName, value, playerId); } /** - * Start the ticker with the specified name. It will deliver - * messages to the game object at the specified delay, - * the value of each message being a single integer, starting with 0 - * and increasing by one with each messsage. + * Start the ticker with the specified name. It will deliver messages to the game object at the + * specified delay, the value of each message being a single integer, starting with 0 and + * increasing by one with each messsage. */ public function startTicker (tickerName :String, msOfDelay :int) :void { @@ -376,8 +360,7 @@ public class EZGameControl extends BaseControl } /** - * Send a message that will be heard by everyone in the game room, - * even observers. + * Send a message that will be heard by everyone in the game room, even observers. */ public function sendChat (msg :String) :void { @@ -385,25 +368,25 @@ public class EZGameControl extends BaseControl } /** - * Display the specified message immediately locally: not sent - * to any other players or observers in the game room. + * Display the specified message immediately locally: not sent to any other players or + * observers in the game room. */ public function localChat (msg :String) :void { callEZCode("localChat_v1", msg); } - // TODO: NEW + /** + * Returns the player ids of all occupants in the game room. + */ public function getOccupants () :Array /* of playerId */ { return (callEZCode("getOccupants_v1") as Array); } - // TODO: NEW /** - * Get the display name of the specified occupant. - * Two players may have the same name: always use playerId to - * purposes of identification and comparison. The name is for display + * Get the display name of the specified occupant. Two players may have the same name: always + * use playerId to purposes of identification and comparison. The name is for display * only. Will be null is the specified playerId is not in the game. */ public function getOccupantName (playerId :int) :String @@ -411,21 +394,42 @@ public class EZGameControl extends BaseControl return String(callEZCode("getOccupantName_v1", playerId)); } - // TODO: NEW + /** + * Returns this client's player id. + */ public function getMyId () :int { return int(callEZCode("getMyId_v1")); } + /** + * Returns true if we are in control of this game. False if another client is in control. + */ + public function amInControl () :Boolean + { + return getControllerId() == getMyId(); + } + + /** + * Returns the player id of the client that is in control of this game. + */ + public function getControllerId () :int + { + return int(callEZCode("getControllerId_v1")); + } + + /** + * Returns the player id of the current turn holder. + */ public function getTurnHolder () :int { return int(callEZCode("getTurnHolder_v1")); } /** - * Get the user-specific game data for the specified user. The - * first time this is requested per game instance it will be retrieved - * from the database. After that, it will be returned from memory. + * Get the user-specific game data for the specified user. The first time this is requested per + * game instance it will be retrieved from the database. After that, it will be returned from + * memory. */ public function getUserCookie (occupantId :int, callback :Function) :void { @@ -433,18 +437,14 @@ public class EZGameControl extends BaseControl } /** - * Store persistent data that can later be retrieved by an instance - * of this game. The maximum size of this data is 4096 bytes AFTER - * AMF3 encoding. + * Store persistent data that can later be retrieved by an instance of this game. The maximum + * size of this data is 4096 bytes AFTER AMF3 encoding. Note: there is no playerId parameter + * because a cookie may only be stored for the current player. * - * Note: there is no playerIndex parameter because a cookie may only - * be stored for the current player. - * - * @return false if the cookie could not be encoded to 4096 bytes - * or less; true if the cookie is going to try to be saved. There is - * no guarantee it will be saved and no way to find out if it failed, - * but if it fails it will be because the shit hit the fan so hard that - * there's nothing you can do anyway. + * @return false if the cookie could not be encoded to 4096 bytes or less; true if the cookie + * is going to try to be saved. There is no guarantee it will be saved and no way to find out + * if it failed, but if it fails it will be because the shit hit the fan so hard that there's + * nothing you can do anyway. */ public function setUserCookie (cookie :Object) :Boolean { @@ -468,8 +468,8 @@ public class EZGameControl extends BaseControl } /** - * End the current turn. If no next player id is specified, - * then the next player after the current one is used. + * End the current turn. If no next player id is specified, then the next player after the + * current one is used. */ public function endTurn (nextPlayerId :int = 0) :void { @@ -489,12 +489,13 @@ public class EZGameControl extends BaseControl } /** - * Populate any properties or functions we want to expose to - * the other side of the ezgame security boundary. + * Populate any properties or functions we want to expose to the other side of the ezgame + * security boundary. */ protected function populateProperties (o :Object) :void { o["propertyWasSet_v1"] = propertyWasSet_v1; + o["controlDidChange_v1"] = controlDidChange_v1; o["turnDidChange_v1"] = turnDidChange_v1; o["messageReceived_v1"] = messageReceived_v1; o["gameDidStart_v1"] = gameDidStart_v1; @@ -513,6 +514,14 @@ public class EZGameControl extends BaseControl new PropertyChangedEvent(this, name, newValue, oldValue, index)); } + /** + * Private method to post a StateChangedEvent. + */ + private function controlDidChange_v1 () :void + { + dispatch(new StateChangedEvent(StateChangedEvent.CONTROL_CHANGED, this)); + } + /** * Private method to post a StateChangedEvent. */ @@ -551,14 +560,13 @@ public class EZGameControl extends BaseControl private function occupantChanged_v1 (occupantId :int, player :Boolean, enter :Boolean) :void { dispatch(new OccupantChangedEvent( - enter ? OccupantChangedEvent.OCCUPANT_ENTERED - : OccupantChangedEvent.OCCUPANT_LEFT, - this, occupantId, player)); + enter ? OccupantChangedEvent.OCCUPANT_ENTERED : + OccupantChangedEvent.OCCUPANT_LEFT, this, occupantId, player)); } /** - * Sets the properties we received from the EZ game framework - * on the other side of the security boundary. + * Sets the properties we received from the EZ game framework on the other side of the security + * boundary. */ protected function setEZProps (o :Object) :void { @@ -606,9 +614,9 @@ public class EZGameControl extends BaseControl protected function checkIsConnected () :void { if (!isConnected()) { - throw new IllegalOperationError("The game is not connected " + - "to The Whirled, please check isConnected(). If false, your " + - "game is being viewed standalone and should adjust."); + throw new IllegalOperationError( + "The game is not connected to The Whirled, please check isConnected(). " + + "If false, your game is being viewed standalone and should adjust."); } } diff --git a/src/as/com/threerings/ezgame/StateChangedEvent.as b/src/as/com/threerings/ezgame/StateChangedEvent.as index fe579c8f..69572faf 100644 --- a/src/as/com/threerings/ezgame/StateChangedEvent.as +++ b/src/as/com/threerings/ezgame/StateChangedEvent.as @@ -34,6 +34,9 @@ public class StateChangedEvent extends EZEvent /** Indicates that the game has transitioned to a ended state. */ public static const GAME_ENDED :String = "GameEnded"; + /** Indicates that a new controller has been assigned. */ + public static const CONTROL_CHANGED :String = "ControlChanged"; + /** Indicates that the turn has changed. */ // TODO: move to own event? public static const TURN_CHANGED :String = "TurnChanged"; diff --git a/src/as/com/threerings/ezgame/client/EZGameController.as b/src/as/com/threerings/ezgame/client/EZGameController.as index ae7e51f4..6fd524f1 100644 --- a/src/as/com/threerings/ezgame/client/EZGameController.as +++ b/src/as/com/threerings/ezgame/client/EZGameController.as @@ -25,6 +25,8 @@ import flash.events.Event; import com.threerings.util.Name; +import com.threerings.presents.dobj.AttributeChangedEvent; + import com.threerings.crowd.client.PlaceView; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.PlaceConfig; @@ -44,22 +46,21 @@ import com.threerings.ezgame.data.EZGameObject; public class EZGameController extends GameController implements TurnGameController { - /** - */ public function EZGameController () { addDelegate(_turnDelegate = new TurnGameControllerDelegate(this)); } /** - * This is called by the GameControlBackend once it has initialized - * and made contact with usercode. + * This is called by the GameControlBackend once it has initialized and made contact with + * usercode. */ public function userCodeIsConnected () :void { playerReady(); } + // from PlaceController override public function willEnterPlace (plobj :PlaceObject) :void { _ezObj = (plobj as EZGameObject); @@ -67,6 +68,7 @@ public class EZGameController extends GameController super.willEnterPlace(plobj); } + // from PlaceController override public function didLeavePlace (plobj :PlaceObject) :void { super.didLeavePlace(plobj); @@ -80,13 +82,24 @@ public class EZGameController extends GameController _panel.backend.turnDidChange(); } + // from GameController + override public function attributeChanged (event :AttributeChangedEvent) :void + { + var name :String = event.getName(); + if (EZGameObject.CONTROLLER_OID == name) { + _panel.backend.controlDidChange(); + } else { + super.attributeChanged(event); + } + } + + // from GameController override protected function playerReady () :void { - // we require the user to be connected, and we redundantly only - // do this if the user is in the players array + // we require the user to be connected, and we redundantly only do this if the user is in + // the players array if (_panel.backend.isConnected()) { - var bobj :BodyObject = - (_ctx.getClient().getClientObject() as BodyObject); + var bobj :BodyObject = (_ctx.getClient().getClientObject() as BodyObject); if (_gobj.getPlayerIndex(bobj.getVisibleName()) != -1) { super.playerReady(); } @@ -96,36 +109,28 @@ public class EZGameController extends GameController } } + // from GameController override protected function gameDidStart () :void { super.gameDidStart(); - _panel.backend.gameDidStart(); } + // from GameController override protected function gameDidEnd () :void { super.gameDidEnd(); - _panel.backend.gameDidEnd(); } + // from PlaceController override protected function createPlaceView (ctx :CrowdContext) :PlaceView { - return new EZGamePanel(ctx, this); - } - - override protected function didInit () :void - { - super.didInit(); - // retain a casted reference to our panel - _panel = (_view as EZGamePanel); + return _panel = new EZGamePanel(ctx, this); } protected var _ezObj :EZGameObject; - protected var _turnDelegate :TurnGameControllerDelegate; - protected var _panel :EZGamePanel; } } diff --git a/src/as/com/threerings/ezgame/client/GameControlBackend.as b/src/as/com/threerings/ezgame/client/GameControlBackend.as index 71de775c..580998a9 100644 --- a/src/as/com/threerings/ezgame/client/GameControlBackend.as +++ b/src/as/com/threerings/ezgame/client/GameControlBackend.as @@ -189,6 +189,7 @@ public class GameControlBackend o["sendMessage_v2"] = sendMessage_v2; o["getOccupants_v1"] = getOccupants_v1; o["getMyId_v1"] = getMyId_v1; + o["getControllerId_v1"] = getControllerId_v1; o["getUserCookie_v2"] = getUserCookie_v2; o["endTurn_v2"] = endTurn_v2; o["endGame_v2"] = endGame_v2; @@ -316,6 +317,11 @@ public class GameControlBackend return _ctx.getClient().getClientObject().getOid(); } + public function getControllerId_v1 () :int + { + return _ezObj.controllerOid; + } + // TODO: table games only public function getPlayerPosition_v1 (playerId :int) :int { @@ -659,6 +665,14 @@ public class GameControlBackend } } + /** + * Called by the EZGameController when the controller changes. + */ + public function controlDidChange () :void + { + callUserCode("controlDidChange_v1"); + } + /** * Called by the EZGameController when the turn changes. */ diff --git a/src/as/com/threerings/ezgame/data/EZGameObject.as b/src/as/com/threerings/ezgame/data/EZGameObject.as index 49f23a13..995579a5 100644 --- a/src/as/com/threerings/ezgame/data/EZGameObject.as +++ b/src/as/com/threerings/ezgame/data/EZGameObject.as @@ -51,6 +51,9 @@ public class EZGameObject extends GameObject public static const TICKER :String = "Utick"; // AUTO-GENERATED: FIELDS START + /** The field name of the controllerOid field. */ + public static const CONTROLLER_OID :String = "controllerOid"; + /** The field name of the turnHolder field. */ public static const TURN_HOLDER :String = "turnHolder"; @@ -61,6 +64,10 @@ public class EZGameObject extends GameObject public static const EZ_GAME_SERVICE :String = "ezGameService"; // AUTO-GENERATED: FIELDS END + /** The client that is in control of this game. The first client to enter will be assigned + * control and control will subsequently be reassigned if that client disconnects or leaves. */ + public var controllerOid :int; + /** The current turn holder. */ public var turnHolder :Name; @@ -96,30 +103,11 @@ public class EZGameObject extends GameObject return players; } -// // AUTO-GENERATED: METHODS START -// /** -// * Requests that the turnHolder 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 function setTurnHolder (value :Name) :void -// { -// var ovalue :Name = this.turnHolder; -// requestAttributeChange( -// TURN_HOLDER, value, ovalue); -// this.turnHolder = value; -// } -// // AUTO-GENERATED: METHODS END - /** * Called by a PropertySetEvent to enact a property change. * @return the old value */ - public function applyPropertySet ( - propName :String, value :Object, index :int) :Object + public function applyPropertySet (propName :String, value :Object, index :int) :Object { var oldValue :Object = _props[propName]; if (index >= 0) { @@ -143,20 +131,6 @@ public class EZGameObject extends GameObject return oldValue; } -// override public function writeObject (out :ObjectOutputStream) :void -// { -// super.writeObject(out); -// -// out.writeObject(turnHolder); -// out.writeObject(ezGameService); -// -// out.writeInt(_props.length); -// for (var key :String in _props) { -// out.writeUTF(key); -// out.writeObject(EZObjectMarshaller.encode(_props[key])); -// } -// } - override public function readObject (ins :ObjectInputStream) :void { super.readObject(ins); @@ -178,6 +152,7 @@ public class EZGameObject extends GameObject */ protected function readDefaultFields (ins :ObjectInputStream) :void { + controllerOid = ins.readInt(); turnHolder = (ins.readObject() as Name); userCookies = (ins.readObject() as DSet); ezGameService = (ins.readObject() as EZGameMarshaller); diff --git a/src/java/com/threerings/ezgame/data/EZGameObject.java b/src/java/com/threerings/ezgame/data/EZGameObject.java index 881c184e..15cec29f 100644 --- a/src/java/com/threerings/ezgame/data/EZGameObject.java +++ b/src/java/com/threerings/ezgame/data/EZGameObject.java @@ -58,6 +58,9 @@ public class EZGameObject extends GameObject public static final String TICKER = "Utick"; // AUTO-GENERATED: FIELDS START + /** The field name of the controllerOid field. */ + public static final String CONTROLLER_OID = "controllerOid"; + /** The field name of the turnHolder field. */ public static final String TURN_HOLDER = "turnHolder"; @@ -68,6 +71,10 @@ public class EZGameObject extends GameObject public static final String EZ_GAME_SERVICE = "ezGameService"; // AUTO-GENERATED: FIELDS END + /** The client that is in control of this game. The first client to enter will be assigned + * control and control will subsequently be reassigned if that client disconnects or leaves. */ + public int controllerOid; + /** The current turn holder. */ public Name turnHolder; @@ -223,6 +230,22 @@ public class EZGameObject extends GameObject // AUTO-GENERATED: METHODS START + /** + * Requests that the controllerOid 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 setControllerOid (int value) + { + int ovalue = this.controllerOid; + requestAttributeChange( + CONTROLLER_OID, Integer.valueOf(value), Integer.valueOf(ovalue)); + this.controllerOid = value; + } + /** * Requests that the turnHolder field be set to the * specified value. The local value will be updated immediately and an diff --git a/src/java/com/threerings/ezgame/server/EZGameManager.java b/src/java/com/threerings/ezgame/server/EZGameManager.java index 76aae144..985438f2 100644 --- a/src/java/com/threerings/ezgame/server/EZGameManager.java +++ b/src/java/com/threerings/ezgame/server/EZGameManager.java @@ -47,6 +47,7 @@ import com.threerings.presents.client.InvocationService; import com.threerings.presents.server.InvocationException; import com.threerings.crowd.data.BodyObject; +import com.threerings.crowd.data.OccupantInfo; import com.threerings.crowd.data.PlaceObject; import com.threerings.crowd.server.CrowdServer; @@ -92,9 +93,8 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void endTurn ( - ClientObject caller, int nextPlayerId, - InvocationService.InvocationListener listener) + public void endTurn (ClientObject caller, int nextPlayerId, + InvocationService.InvocationListener listener) throws InvocationException { validateStateModification(caller); @@ -111,9 +111,8 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void endGame ( - ClientObject caller, int[] winnerOids, - InvocationService.InvocationListener listener) + public void endGame (ClientObject caller, int[] winnerOids, + InvocationService.InvocationListener listener) throws InvocationException { if (!_gameObj.isInPlay()) { @@ -126,27 +125,23 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void sendMessage ( - ClientObject caller, String msg, Object data, int playerId, - InvocationService.InvocationListener listener) + public void sendMessage (ClientObject caller, String msg, Object data, int playerId, + InvocationService.InvocationListener listener) throws InvocationException { validateUser(caller); if (playerId == 0) { - _gameObj.postMessage(EZGameObject.USER_MESSAGE, - new Object[] { msg, data }); - + _gameObj.postMessage(EZGameObject.USER_MESSAGE, new Object[] { msg, data }); } else { sendPrivateMessage(playerId, msg, data); } } // from EZGameProvider - public void setProperty ( - ClientObject caller, String propName, Object data, int index, - boolean testAndSet, Object testValue, - InvocationService.InvocationListener listener) + public void setProperty (ClientObject caller, String propName, Object data, int index, + boolean testAndSet, Object testValue, + InvocationService.InvocationListener listener) throws InvocationException { validateUser(caller); @@ -157,9 +152,8 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void getDictionaryLetterSet ( - ClientObject caller, String locale, int count, - InvocationService.ResultListener listener) + public void getDictionaryLetterSet (ClientObject caller, String locale, int count, + InvocationService.ResultListener listener) throws InvocationException { DictionaryManager dictionary = getDictionaryManager (); @@ -167,9 +161,8 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void checkDictionaryWord ( - ClientObject caller, String locale, String word, - InvocationService.ResultListener listener) + public void checkDictionaryWord (ClientObject caller, String locale, String word, + InvocationService.ResultListener listener) throws InvocationException { DictionaryManager dictionary = getDictionaryManager (); @@ -191,9 +184,9 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void addToCollection ( - ClientObject caller, String collName, byte[][] data, - boolean clearExisting, InvocationService.InvocationListener listener) + public void addToCollection (ClientObject caller, String collName, byte[][] data, + boolean clearExisting, + InvocationService.InvocationListener listener) throws InvocationException { validateUser(caller); @@ -201,8 +194,7 @@ public class EZGameManager extends GameManager _collections = new HashMap>(); } - // figure out if we're adding to an existing collection - // or creating a new one + // figure out if we're adding to an existing collection or creating a new one ArrayList list = null; if (!clearExisting) { list = _collections.get(collName); @@ -216,10 +208,9 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void getFromCollection ( - ClientObject caller, String collName, boolean consume, int count, - String msgOrPropName, int playerId, - InvocationService.ConfirmListener listener) + public void getFromCollection (ClientObject caller, String collName, boolean consume, int count, + String msgOrPropName, int playerId, + InvocationService.ConfirmListener listener) throws InvocationException { validateUser(caller); @@ -243,12 +234,10 @@ public class EZGameManager extends GameManager if (playerId == 0) { setProperty(msgOrPropName, result, -1); - } else { sendPrivateMessage(playerId, msgOrPropName, result); } - // SUCCESS! - listener.requestProcessed(); + listener.requestProcessed(); // SUCCESS! return; } } @@ -258,15 +247,14 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void mergeCollection ( - ClientObject caller, String srcColl, String intoColl, - InvocationService.InvocationListener listener) + public void mergeCollection (ClientObject caller, String srcColl, String intoColl, + InvocationService.InvocationListener listener) throws InvocationException { validateUser(caller); - // non-existent collections are treated as empty, so if the - // source doesn't exist, we silently accept it + // non-existent collections are treated as empty, so if the source doesn't exist, we + // silently accept it if (_collections != null) { ArrayList src = _collections.remove(srcColl); if (src != null) { @@ -281,9 +269,8 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void setTicker ( - ClientObject caller, String tickerName, int msOfDelay, - InvocationService.InvocationListener listener) + public void setTicker (ClientObject caller, String tickerName, int msOfDelay, + InvocationService.InvocationListener listener) throws InvocationException { validateUser(caller); @@ -292,11 +279,11 @@ public class EZGameManager extends GameManager if (msOfDelay >= MIN_TICKER_DELAY) { if (_tickers != null) { t = _tickers.get(tickerName); - } else { _tickers = new HashMap(); t = null; } + if (t == null) { if (_tickers.size() >= MAX_TICKERS) { throw new InvocationException(ACCESS_DENIED); @@ -320,9 +307,8 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void getCookie ( - ClientObject caller, final int playerId, - InvocationService.InvocationListener listener) + public void getCookie (ClientObject caller, final int playerId, + InvocationService.InvocationListener listener) throws InvocationException { GameCookieManager gcm = getCookieManager(); @@ -338,31 +324,25 @@ public class EZGameManager extends GameManager if (!_cookieLookups.contains(playerId)) { BodyObject body = getOccupantByOid(playerId); if (body == null) { - log.fine("getCookie() called with invalid occupantId " + - "[occupantId=" + playerId + "]."); + log.fine("getCookie() called with invalid occupant [occupantId=" + playerId + "]."); throw new InvocationException(INTERNAL_ERROR); } - gcm.getCookie(getPersistentGameId(), body, - new ResultListener() { - public void requestCompleted (byte[] result) { - // Result may be null: that's ok, it means - // we've looked up the user's nonexistant cookie. - // Only set the cookie if the playerIndex is - // still in the lookup set, otherwise they left! - if (_cookieLookups.remove(playerId) && - _gameObj.isActive()) { - _gameObj.addToUserCookies( - new UserCookie(playerId, result)); - } + gcm.getCookie(getPersistentGameId(), body, new ResultListener() { + public void requestCompleted (byte[] result) { + // Result may be null: that's ok, it means we've looked up the user's + // nonexistant cookie. Only set the cookie if the playerIndex is still in the + // lookup set, otherwise they left! + if (_cookieLookups.remove(playerId) && _gameObj.isActive()) { + _gameObj.addToUserCookies(new UserCookie(playerId, result)); } + } - public void requestFailed (Exception cause) { - log.warning("Unable to retrieve cookie " + - "[cause=" + cause + "]."); - requestCompleted(null); - } - }); + public void requestFailed (Exception cause) { + log.warning("Unable to retrieve cookie [cause=" + cause + "]."); + requestCompleted(null); + } + }); // indicate that we're looking up a cookie _cookieLookups.add(playerId); @@ -370,9 +350,8 @@ public class EZGameManager extends GameManager } // from EZGameProvider - public void setCookie ( - ClientObject caller, byte[] value, - InvocationService.InvocationListener listener) + public void setCookie (ClientObject caller, byte[] value, + InvocationService.InvocationListener listener) throws InvocationException { validateUser(caller); @@ -408,8 +387,8 @@ public class EZGameManager extends GameManager } /** - * Helper method to send a private message to the specified player oid - * (must already be verified). + * Helper method to send a private message to the specified player oid (must already be + * verified). */ protected void sendPrivateMessage ( int playerId, String msg, Object data) @@ -421,21 +400,19 @@ public class EZGameManager extends GameManager throw new InvocationException("m.player_not_around"); } - target.postMessage( - EZGameObject.USER_MESSAGE + ":" + _gameObj.getOid(), - new Object[] { msg, data }); + target.postMessage(EZGameObject.USER_MESSAGE + ":" + _gameObj.getOid(), + new Object[] { msg, data }); } /** * Helper method to post a property set event. */ - protected void setProperty ( - String propName, Object value, int index) + protected void setProperty (String propName, Object value, int index) { // apply the property set immediately Object oldValue = _gameObj.applyPropertySet(propName, value, index); - _gameObj.postEvent(new PropertySetEvent( - _gameObj.getOid(), propName, value, index, oldValue)); + _gameObj.postEvent( + new PropertySetEvent(_gameObj.getOid(), propName, value, index, oldValue)); } /** @@ -470,8 +447,7 @@ public class EZGameManager extends GameManager } /** - * Validate that the specified listener has access to make a - * change. + * Validate that the specified listener has access to make a change. */ protected void validateStateModification (ClientObject caller) throws InvocationException @@ -479,8 +455,7 @@ public class EZGameManager extends GameManager validateUser(caller); Name holder = _gameObj.turnHolder; - if (holder != null && - !holder.equals(((BodyObject) caller).getVisibleName())) { + if (holder != null && !holder.equals(((BodyObject) caller).getVisibleName())) { throw new InvocationException(InvocationCodes.ACCESS_DENIED); } } @@ -539,6 +514,44 @@ public class EZGameManager extends GameManager } } + @Override // from PlaceManager + protected void bodyEntered (int bodyOid) + { + super.bodyEntered(bodyOid); + + // if we have no controller, then our new friend gets control + if (_gameObj.controllerOid == 0) { + _gameObj.setControllerOid(bodyOid); + } + } + + @Override // from PlaceManager + protected void bodyUpdated (OccupantInfo info) + { + super.bodyUpdated(info); + + // if the controller just disconnected, reassign control + if (info.status == OccupantInfo.DISCONNECTED && info.bodyOid == _gameObj.controllerOid) { + _gameObj.setControllerOid(getControllerOid()); + + // if everyone in the room was disconnected and this client just reconnected, it becomes + // the new controller + } else if (_gameObj.controllerOid == 0) { + _gameObj.setControllerOid(info.bodyOid); + } + } + + @Override // from PlaceManager + protected void bodyLeft (int bodyOid) + { + super.bodyLeft(bodyOid); + + // if this player was the controller, reassign control + if (bodyOid == _gameObj.controllerOid) { + _gameObj.setControllerOid(getControllerOid()); + } + } + @Override protected void didShutdown () { @@ -562,8 +575,7 @@ public class EZGameManager extends GameManager super.playerGameDidEnd(pidx); // kill any of their cookies - if (_gameObj.userCookies != null && - _gameObj.userCookies.containsKey(pidx)) { + if (_gameObj.userCookies != null && _gameObj.userCookies.containsKey(pidx)) { _gameObj.removeFromUserCookies(pidx); } // halt the loading of their cookie, if in progress @@ -599,6 +611,20 @@ public class EZGameManager extends GameManager } } + /** + * Returns the oid of a player to whom to assign control of the game or zero if no players + * qualify for control. + */ + protected int getControllerOid () + { + for (OccupantInfo info : _gameObj.occupantInfo) { + if (info.status != OccupantInfo.DISCONNECTED) { + return info.bodyOid; + } + } + return 0; + } + /** * A timer that fires message events to a game. */ @@ -610,8 +636,7 @@ public class EZGameManager extends GameManager public Ticker (String name, EZGameObject gameObj) { _name = name; - // once we are constructed, we want to avoid calling - // methods on dobjs. + // once we are constructed, we want to avoid calling methods on dobjs. _oid = gameObj.getOid(); _omgr = gameObj.getManager(); } @@ -627,20 +652,17 @@ public class EZGameManager extends GameManager _interval.cancel(); } - /** The interval that does our work. Note well that this is - * not a 'safe' interval that operates using a RunQueue. - * This interval instead does something that we happen to know - * is safe for any thread: posting an event to the dobj manager. - * If we were using a RunQueue it would be the same event queue - * and we would be posted there, wait our turn, and then do the same - * thing: post this event. We just expedite the process. + /** + * The interval that does our work. Note well that this is not a 'safe' interval that + * operates using a RunQueue. This interval instead does something that we happen to know + * is safe for any thread: posting an event to the dobj manager. If we were using a + * RunQueue it would be the same event queue and we would be posted there, wait our turn, + * and then do the same thing: post this event. We just expedite the process. */ protected Interval _interval = new Interval() { - public void expired () - { + public void expired () { _omgr.postEvent( - new MessageEvent(_oid, EZGameObject.TICKER, - new Object[] { _name, _value++ })); + new MessageEvent(_oid, EZGameObject.TICKER, new Object[] { _name, _value++ })); } };