From 3b08974f73363995e1d8c53ea827357bb0439269 Mon Sep 17 00:00:00 2001 From: Ray Greenwell Date: Fri, 5 Jan 2007 18:59:08 +0000 Subject: [PATCH] Revamped EZGame to work over the security boundary. Now, instead of implementing "Game" and having the EZGame object assigned to you, you create it yourself using your top-level component. You must register listeners manually, and keyboard focus is currently an issue that I'm working on. git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@137 c613c5cb-e716-0410-b11b-feb51c14d237 --- src/as/com/threerings/ezgame/CardDeck.as | 14 +- src/as/com/threerings/ezgame/EZEvent.as | 10 +- src/as/com/threerings/ezgame/EZGame.as | 219 --------- src/as/com/threerings/ezgame/EZGameControl.as | 376 +++++++++++++++ src/as/com/threerings/ezgame/Game.as | 16 - .../threerings/ezgame/MessageReceivedEvent.as | 2 +- .../com/threerings/ezgame/PlayersDisplay.as | 19 +- .../threerings/ezgame/PropertyChangedEvent.as | 2 +- .../threerings/ezgame/StateChangedEvent.as | 2 +- .../ezgame/client/EZGameController.as | 101 +---- .../threerings/ezgame/client/EZGamePanel.as | 91 +--- .../threerings/ezgame/client/GameContainer.as | 28 ++ ...ameObjectImpl.as => GameControlBackend.as} | 429 +++++++++--------- .../com/threerings/ezgame/client/GameData.as | 14 +- 14 files changed, 675 insertions(+), 648 deletions(-) delete mode 100644 src/as/com/threerings/ezgame/EZGame.as create mode 100644 src/as/com/threerings/ezgame/EZGameControl.as delete mode 100644 src/as/com/threerings/ezgame/Game.as create mode 100644 src/as/com/threerings/ezgame/client/GameContainer.as rename src/as/com/threerings/ezgame/client/{GameObjectImpl.as => GameControlBackend.as} (60%) diff --git a/src/as/com/threerings/ezgame/CardDeck.as b/src/as/com/threerings/ezgame/CardDeck.as index b4f29685..ee846ad8 100644 --- a/src/as/com/threerings/ezgame/CardDeck.as +++ b/src/as/com/threerings/ezgame/CardDeck.as @@ -6,9 +6,9 @@ package com.threerings.ezgame { */ public class CardDeck { - public function CardDeck (gameObj :EZGame, deckName :String = "deck") + public function CardDeck (gameCtrl :EZGameControl, deckName :String = "deck") { - _gameObj = gameObj; + _gameCtrl = gameCtrl; _deckName = deckName; var deck :Array = new Array(); @@ -19,23 +19,23 @@ public class CardDeck } } - _gameObj.setCollection(_deckName, deck); + _gameCtrl.setCollection(_deckName, deck); } public function dealToPlayer ( playerIdx :int, count :int, msgName :String) :void { // TODO: support the callback - _gameObj.dealFromCollection(_deckName, count, msgName, null, playerIdx); + _gameCtrl.dealFromCollection(_deckName, count, msgName, null, playerIdx); } public function dealToData (count :int, propName :String) :void { - _gameObj.dealFromCollection(_deckName, count, propName, null); + _gameCtrl.dealFromCollection(_deckName, count, propName, null); } - /** The game object. */ - protected var _gameObj :EZGame; + /** The game control. */ + protected var _gameCtrl :EZGameControl; /** The name of our deck. */ protected var _deckName :String; diff --git a/src/as/com/threerings/ezgame/EZEvent.as b/src/as/com/threerings/ezgame/EZEvent.as index aac3e757..487ea071 100644 --- a/src/as/com/threerings/ezgame/EZEvent.as +++ b/src/as/com/threerings/ezgame/EZEvent.as @@ -8,20 +8,20 @@ import flash.events.Event; public /*abstract*/ class EZEvent extends Event { /** - * Access the game object to which this event applies. + * Access the game control to which this event applies. */ - public function get gameObject () :EZGame + public function get gameControl () :EZGameControl { return _ezgame; } - public function EZEvent (type :String, ezgame :EZGame) + public function EZEvent (type :String, ezgame :EZGameControl) { super(type); _ezgame = ezgame; } - /** The game object for this event. */ - protected var _ezgame :EZGame; + /** The game control for this event. */ + protected var _ezgame :EZGameControl; } } diff --git a/src/as/com/threerings/ezgame/EZGame.as b/src/as/com/threerings/ezgame/EZGame.as deleted file mode 100644 index 56b0e074..00000000 --- a/src/as/com/threerings/ezgame/EZGame.as +++ /dev/null @@ -1,219 +0,0 @@ -package com.threerings.ezgame { - -import flash.events.IEventDispatcher; - -/** - * The game object that you'll be using to manage your game. - */ -public interface EZGame - extends IEventDispatcher -{ - /** - * Data accessor. - */ - function get data () :Object; - - /** - * Get a property from data. - */ - function get (propName :String, index :int = -1) :Object; - - /** - * Set a property that will be distributed. - */ - function set (propName :String, value :Object, index :int = -1) :void; - - /** - * Register an object to receive whatever events it should receive, - * based on which event listeners it implements. Note that it is not - * necessary to register any objects which appear on the display list, - * as they'll be registered automatically. - */ - function registerListener (obj :Object) :void; - - /** - * Unregister the specified object from receiving events. - */ - function unregisterListener (obj :Object) :void; - - /** - * Set the specified collection to contain the specified values, - * clearing any previous values. - */ - function setCollection (collName :String, values :Array) :void; - - /** - * Add to an existing collection. If it doesn't exist, it will - * be created. The new values will be inserted randomly into the - * collection. - */ - function addToCollection (collName :String, values :Array) :void; - - /** - * Pick (do not remove) the specified number of elements from a collection, - * and distribute them to a specific player or set them as a property - * in the game data. - * - * @param collName the collection name. - * @param count the number of elements to pick - * @param msgOrPropName the name of the message or property - * that will contain the picked elements. - * @param playerIndex if -1 (or unset), the picked elements should be - * set on the gameObject as a property for all to see. - * If a playerIndex is specified, only that player will receive - * the elements as a message. - */ - // TODO: a way to specify exclusive picks vs. duplicate-OK picks? - function pickFromCollection ( - collName :String, count :int, msgOrPropName :String, - playerIndex :int = -1) :void; - - /** - * Deal (remove) the specified number of elements from a collection, - * and distribute them to a specific player or set them as a property - * in the game data. - * - * @param collName the collection name. - * @param count the number of elements to pick - * @param msgOrPropName the name of the message or property - * that will contain the picked elements. - * @param playerIndex if -1 (or unset), the picked elements should be - * set on the gameObject as a property for all to see. - * If a playerIndex is specified, only that player will receive - * the elements as a message. - */ - // TODO: figure out the method signature of the callback - function dealFromCollection ( - collName :String, count :int, msgOrPropName :String, - callBack :Function = null, playerIndex :int = -1) :void; - - /** - * Merge the specified collection into the other collection. - * The source collection will be destroyed. The elements from - * The source collection will be shuffled and appended to the end - * of the destination collection. - */ - function mergeCollection (srcColl :String, intoColl :String) :void; - - /** - * 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 playerIndex if -1, sends to all players, otherwise - * the message will be private to just one player - */ - function sendMessage ( - messageName :String, value :Object, playerIndex :int = -1) :void; - - /** - * 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. - */ - function startTicker (tickerName :String, msOfDelay :int) :void; - - /** - * Stop the specified ticker. - */ - function stopTicker (tickerName :String) :void; - - /** - * Send a message that will be heard by everyone in the game room, - * even observers. - */ - function sendChat (msg :String) :void; - - /** - * Display the specified message immediately locally: not sent - * to any other players or observers in the game room. - */ - function localChat (msg :String) :void; - - /** - * Get the number of players currently in the game. - */ - function getPlayerCount () :int; - - /** - * Get the player names, as an array. - */ - function getPlayerNames () :Array /* of String */; - - /** - * Get the index into the player names array of the current player, - * or -1 if the user is not a player. - */ - function getMyIndex () :int; - - /** - * Get the turn holder's index, or -1 if it's nobody's turn. - */ - function getTurnHolderIndex () :int; - - /** - * Get the indexes of the winners - */ - function getWinnerIndexes () :Array /* of int */; - - /** - * A convenience method to just check if it's our turn. - */ - function isMyTurn () :Boolean; - - /** - * Is the game currently in play? - */ - function isInPlay () :Boolean; - - /** - * End the current turn. If no next player index is specified, - * then the next player after the current one is used. - */ - function endTurn (optionalNextPlayerIndex :int = -1) :void; - - /** - * End the game. The specified player indexes are winners! - */ - function endGame (winnerIndex :int, ... rest) :void; - -// function getCurrentRoom () :int; -// -// function sendPlayerToRoom (playerIndex :int, room :int) :void - - /** - * 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. - */ - function getUserCookie (playerIndex :int, callback :Function) :void; - - /** - * 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 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. - */ - function setUserCookie (cookie :Object) :Boolean; -// -// /** -// * Check to see if the user has the specified token. -// */ -// function checkUserToken (token :String, callback :Function) :void; -// -// /** -// * Take the user to a purchase page. -// */ -// function purchaseUserToken (token :String, callback :Function) :void; -} -} diff --git a/src/as/com/threerings/ezgame/EZGameControl.as b/src/as/com/threerings/ezgame/EZGameControl.as new file mode 100644 index 00000000..81f305ef --- /dev/null +++ b/src/as/com/threerings/ezgame/EZGameControl.as @@ -0,0 +1,376 @@ +package com.threerings.ezgame { + +import flash.errors.IllegalOperationError; + +import flash.events.Event; +import flash.events.EventDispatcher; + +import flash.display.DisplayObject; + +public class EZGameControl extends EventDispatcher +{ + /** + * Create an EZGameControl object using some display object currently + * on the hierarchy. + */ + public function EZGameControl (disp :DisplayObject) + { + var event :DynEvent = new DynEvent(); + event.userProps = new Object(); + populateProperties(event.userProps); + disp.root.loaderInfo.sharedEvents.dispatchEvent(event); + setEZProps(event.ezProps); + } + + // from EZGame + public function get data () :Object + { + return _gameData; + } + + // from EZGame + public function get (propName :String, index :int = -1) :Object + { + var value :Object = data[propName]; + if (index >= 0) { + if (value is Array) { + return (value as Array)[index]; + + } else { + throw new ArgumentError("Property " + propName + + " is not an array."); + } + } + return value; + } + + // from EZGame + public function set (propName :String, value :Object, index :int = -1) :void + { + callEZCode("setProperty_v1", propName, value, index); + } + + // from EZGame + public function registerListener (obj :Object) :void + { + if (obj is MessageReceivedListener) { + var mrl :MessageReceivedListener = (obj as MessageReceivedListener); + 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); + } + 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); + } + } + + // from EZGame + public function unregisterListener (obj :Object) :void + { + if (obj is MessageReceivedListener) { + var mrl :MessageReceivedListener = (obj as MessageReceivedListener); + removeEventListener( + MessageReceivedEvent.TYPE, mrl.messageReceived); + } + if (obj is PropertyChangedListener) { + var pcl :PropertyChangedListener = (obj as PropertyChangedListener); + removeEventListener( + PropertyChangedEvent.TYPE, pcl.propertyChanged); + } + if (obj is StateChangedListener) { + var scl :StateChangedListener = (obj as StateChangedListener); + removeEventListener( + StateChangedEvent.GAME_STARTED, scl.stateChanged); + removeEventListener( + StateChangedEvent.TURN_CHANGED, scl.stateChanged); + removeEventListener( + StateChangedEvent.GAME_ENDED, scl.stateChanged); + } + } + + // from EZGame + public function setCollection (collName :String, values :Array) :void + { + populateCollection(collName, values, true); + } + + // from EZGame + public function addToCollection (collName :String, values :Array) :void + { + populateCollection(collName, values, false); + } + + // from EZGame + public function pickFromCollection ( + collName :String, count :int, msgOrPropName :String, + playerIndex :int = -1) :void + { + getFromCollection(collName, count, msgOrPropName, playerIndex, + false, null); + } + + // from EZGame + public function dealFromCollection ( + collName :String, count :int, msgOrPropName :String, + callback :Function = null, playerIndex :int = -1) :void + { + getFromCollection(collName, count, msgOrPropName, playerIndex, + true, callback); + } + + // from EZGame + public function mergeCollection (srcColl :String, intoColl :String) :void + { + callEZCode("mergeCollection_v1", srcColl, intoColl); + } + + // from EZGame + public function sendMessage ( + messageName :String, value :Object, playerIndex :int = -1) :void + { + callEZCode("sendMessage_v1", messageName, value, playerIndex); + } + + // from EZGame + public function startTicker (tickerName :String, msOfDelay :int) :void + { + callEZCode("setTicker_v1", tickerName, msOfDelay); + } + + // from EZGame + public function stopTicker (tickerName :String) :void + { + startTicker(tickerName, 0); + } + + // from EZGame + public function sendChat (msg :String) :void + { + callEZCode("sendChat_v1", msg); + } + + // from EZGame + public function localChat (msg :String) :void + { + callEZCode("localChat_v1", msg); + } + + // from EZGame + public function getPlayerCount () :int + { + return int(callEZCode("getPlayerCount_v1")); + } + + // from EZGame + public function getPlayerNames () :Array + { + return (callEZCode("getPlayerNames_v1") as Array); + } + + // from EZGame + public function getMyIndex () :int + { + return int(callEZCode("getMyIndex_v1")); + } + + // from EZGame + public function getTurnHolderIndex () :int + { + return int(callEZCode("getTurnHolderIndex_v1")); + } + + // from EZGame + public function getWinnerIndexes () :Array /* of int */ + { + return (callEZCode("getWinnerIndexes_v1") as Array); + } + + // from EZGame + public function getUserCookie (playerIndex :int, callback :Function) :void + { + callEZCode("getUserCookie_v1", playerIndex, callback); + } + + // from EZGame + public function setUserCookie (cookie :Object) :Boolean + { + return Boolean(callEZCode("setUserCookie_v1", cookie)); + } + + // from EZGame + public function isMyTurn () :Boolean + { + return Boolean(callEZCode("isMyTurn_v1")); + } + + // from EZGame + public function isInPlay () :Boolean + { + return Boolean(callEZCode("isInPlay_v1")); + } + + // from EZGame + public function endTurn (nextPlayerIndex :int = -1) :void + { + callEZCode("endTurn_v1", nextPlayerIndex); + } + + // from EZGame + public function endGame (... winnerIndexes) :void + { + var args :Array = winnerIndexes; + args.unshift("endGame_v1"); + + // goddamn var-args complications in actionscript + callEZCode.apply(null, args); + } + + override public function willTrigger (type :String) :Boolean + { + throw new IllegalOperationError(); + } + + override public function dispatchEvent (event :Event) :Boolean + { + // Ideally we want to not be an IEventDispatcher so that people + // won't try to do this on us, but if we do that, then some other + // object will be the target during dispatch, and that's confusing. + // It's really nice to be able to + throw new IllegalOperationError(); + } + + /** + * Helper method for setCollection and addToCollection. + */ + protected function populateCollection ( + collName :String, values :Array, clearExisting :Boolean) :void + { + callEZCode("populateCollection_v1", collName, values, clearExisting); + } + + /** + * Helper method for pickFromCollection and dealFromCollection. + */ + protected function getFromCollection( + collName :String, count :int, msgOrPropName :String, playerIndex :int, + consume :Boolean, callback :Function) :void + { + callEZCode("getFromCollection_v1", collName, count, msgOrPropName, + playerIndex, consume, callback); + } + + /** + * 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["turnDidChange_v1"] = turnDidChange_v1; + o["messageReceived_v1"] = messageReceived_v1; + o["gameDidStart_v1"] = gameDidStart_v1; + o["gameDidEnd_v1"] = gameDidEnd_v1; + } + + private function propertyWasSet_v1 ( + name :String, newValue :Object, oldValue :Object, index :int) :void + { + dispatch( + new PropertyChangedEvent(this, name, newValue, oldValue, index)); + } + + private function turnDidChange_v1 () :void + { + dispatch(new StateChangedEvent(StateChangedEvent.TURN_CHANGED, this)); + } + + private function messageReceived_v1 (name :String, value :Object) :void + { + dispatch(new MessageReceivedEvent(this, name, value)); + } + + private function gameDidStart_v1 () :void + { + dispatch(new StateChangedEvent(StateChangedEvent.GAME_STARTED, this)); + } + + private function gameDidEnd_v1 () :void + { + dispatch(new StateChangedEvent(StateChangedEvent.GAME_ENDED, this)); + } + + /** + * Sets the properties we received from the EZ game framework + * on the other side of the security boundary. + */ + protected function setEZProps (o :Object) :void + { + // get our gamedata + _gameData = o.gameData; + + // and functions + _funcs = o; + } + + protected function callEZCode (name :String, ... args) :* + { + if (_funcs != null) { + try { + var func :Function = (_funcs[name] as Function); + if (func != null) { + return func.apply(null, args); + } + } catch (err :Error) { + trace("Unable to call ez code: " + err); + } + } + } + + /** + * Secret function to dispatch property changed events. + */ + internal function dispatch (event :Event) :void + { + try { + super.dispatchEvent(event); + } catch (err :Error) { + trace("Error dispatching event to user game."); + trace(err.getStackTrace()); + } + } + + protected var _gameData :Object; + + protected var _funcs :Object; +} +} + +import flash.events.Event; + +dynamic class DynEvent extends Event +{ + public function DynEvent () + { + super("ezgameQuery", true, false); + } + + override public function clone () :Event + { + return new DynEvent(); + } +} diff --git a/src/as/com/threerings/ezgame/Game.as b/src/as/com/threerings/ezgame/Game.as deleted file mode 100644 index e16b28fd..00000000 --- a/src/as/com/threerings/ezgame/Game.as +++ /dev/null @@ -1,16 +0,0 @@ -package com.threerings.ezgame { - -/** - * This interface should be implemented by some class that lives on the - * display list. When a DisplayObject that implements this interface - * is added, it will be notified of the GameObject. - */ -public interface Game -{ - /** - * Called by the mysterious powers of the cosmos to initialize - * your game with the GameObject. - */ - function setGameObject (gameObj :EZGame) :void; -} -} diff --git a/src/as/com/threerings/ezgame/MessageReceivedEvent.as b/src/as/com/threerings/ezgame/MessageReceivedEvent.as index 5736565d..2c4822fa 100644 --- a/src/as/com/threerings/ezgame/MessageReceivedEvent.as +++ b/src/as/com/threerings/ezgame/MessageReceivedEvent.as @@ -24,7 +24,7 @@ public class MessageReceivedEvent extends EZEvent } public function MessageReceivedEvent ( - ezgame :EZGame, messageName :String, value :Object) + ezgame :EZGameControl, messageName :String, value :Object) { super(TYPE, ezgame); _name = messageName; diff --git a/src/as/com/threerings/ezgame/PlayersDisplay.as b/src/as/com/threerings/ezgame/PlayersDisplay.as index ea796769..0c13466f 100644 --- a/src/as/com/threerings/ezgame/PlayersDisplay.as +++ b/src/as/com/threerings/ezgame/PlayersDisplay.as @@ -20,12 +20,15 @@ import flash.text.TextFieldAutoSize; * free to copy/modify or extend this class. */ public class PlayersDisplay extends Sprite - implements Game, StateChangedListener + implements StateChangedListener { - // implementation of Game method - public function setGameObject (gameObj :EZGame) :void + /** + * Set the game control that will be used with this display. + */ + public function setGameControl (gameCtrl :EZGameControl) :void { - _gameObj = gameObj; + _gameCtrl = gameCtrl; + _gameCtrl.registerListener(this); configureInterface(); } @@ -60,7 +63,7 @@ public class PlayersDisplay extends Sprite // create a label for each player var playerIndex :int = 0; - for each (var name :String in _gameObj.getPlayerNames()) { + for each (var name :String in _gameCtrl.getPlayerNames()) { label = createPlayerLabel(playerIndex, name); icon = createPlayerIcon(playerIndex, name); var iconW :int = 0; @@ -156,15 +159,15 @@ public class PlayersDisplay extends Sprite */ protected function displayCurrentTurn () :void { - var idx :int = _gameObj.isInPlay() ? _gameObj.getTurnHolderIndex() : -1; + var idx :int = _gameCtrl.isInPlay() ? _gameCtrl.getTurnHolderIndex() : -1; for (var ii :int = 0; ii < _playerLabels.length; ii++) { var label :TextField = (_playerLabels[ii] as TextField); label.backgroundColor = getBackground(ii == idx); } } - /** Our game object. */ - protected var _gameObj :EZGame; + /** Our game Control. */ + protected var _gameCtrl :EZGameControl; /** An array of labels, one for each player name. */ protected var _playerLabels :Array = []; diff --git a/src/as/com/threerings/ezgame/PropertyChangedEvent.as b/src/as/com/threerings/ezgame/PropertyChangedEvent.as index 961e2211..8620de92 100644 --- a/src/as/com/threerings/ezgame/PropertyChangedEvent.as +++ b/src/as/com/threerings/ezgame/PropertyChangedEvent.as @@ -47,7 +47,7 @@ public class PropertyChangedEvent extends EZEvent * Constructor. */ public function PropertyChangedEvent ( - ezgame :EZGame, propName :String, newValue :Object, + ezgame :EZGameControl, propName :String, newValue :Object, oldValue :Object, index :int = -1) { super(TYPE, ezgame); diff --git a/src/as/com/threerings/ezgame/StateChangedEvent.as b/src/as/com/threerings/ezgame/StateChangedEvent.as index 241d026b..e003eb73 100644 --- a/src/as/com/threerings/ezgame/StateChangedEvent.as +++ b/src/as/com/threerings/ezgame/StateChangedEvent.as @@ -17,7 +17,7 @@ public class StateChangedEvent extends EZEvent // TODO: move to own event? public static const TURN_CHANGED :String = "TurnChanged"; - public function StateChangedEvent (type :String, ezgame :EZGame) + public function StateChangedEvent (type :String, ezgame :EZGameControl) { super(type, ezgame); } diff --git a/src/as/com/threerings/ezgame/client/EZGameController.as b/src/as/com/threerings/ezgame/client/EZGameController.as index 5fd7df55..52a7049c 100644 --- a/src/as/com/threerings/ezgame/client/EZGameController.as +++ b/src/as/com/threerings/ezgame/client/EZGameController.as @@ -2,15 +2,8 @@ package com.threerings.ezgame.client { import flash.events.Event; -import flash.utils.ByteArray; - -import com.threerings.util.Integer; import com.threerings.util.Name; -import com.threerings.presents.dobj.MessageAdapter; -import com.threerings.presents.dobj.MessageListener; -import com.threerings.presents.dobj.MessageEvent; - import com.threerings.crowd.client.PlaceView; import com.threerings.crowd.data.PlaceConfig; import com.threerings.crowd.data.PlaceObject; @@ -22,23 +15,13 @@ import com.threerings.parlor.turn.client.TurnGameController; import com.threerings.parlor.turn.client.TurnGameControllerDelegate; import com.threerings.ezgame.data.EZGameObject; -import com.threerings.ezgame.data.PropertySetEvent; -import com.threerings.ezgame.data.PropertySetListener; -import com.threerings.ezgame.util.EZObjectMarshaller; - -import com.threerings.ezgame.MessageReceivedEvent; -import com.threerings.ezgame.PropertyChangedEvent; -import com.threerings.ezgame.StateChangedEvent; /** * A controller for flash games. */ public class EZGameController extends GameController - implements TurnGameController, PropertySetListener, MessageListener + implements TurnGameController { - /** The implementation of the GameObject interface for users. */ - public var gameObjImpl :GameObjectImpl; - /** */ public function EZGameController () @@ -49,9 +32,6 @@ public class EZGameController extends GameController override public function willEnterPlace (plobj :PlaceObject) :void { _ezObj = (plobj as EZGameObject); - gameObjImpl = new GameObjectImpl(_ctx, _ezObj); - - _ctx.getClient().getClientObject().addListener(_userListener); super.willEnterPlace(plobj); } @@ -60,100 +40,39 @@ public class EZGameController extends GameController { super.didLeavePlace(plobj); - _ctx.getClient().getClientObject().removeListener(_userListener); - _ezObj = null; } // from TurnGameController public function turnDidChange (turnHolder :Name) :void { - dispatchUserEvent( - new StateChangedEvent(StateChangedEvent.TURN_CHANGED, gameObjImpl)); - } - - // from PropertySetListener - public function propertyWasSet (event :PropertySetEvent) :void - { - // notify the user game - dispatchUserEvent(new PropertyChangedEvent( - gameObjImpl, event.getName(), event.getValue(), - event.getOldValue(), event.getIndex())); - } - - // from MessageListener - public function messageReceived (event :MessageEvent) :void - { - var name :String = event.getName(); - if (EZGameObject.USER_MESSAGE == name) { - dispatchUserMessage(event.getArgs()); - - } else if (EZGameObject.GAME_CHAT == name) { - // this is chat send by the game, let's route it like - // localChat, which is also sent by the game - gameObjImpl.localChat(String(event.getArgs()[0])); - - } else if (EZGameObject.TICKER == name) { - var args :Array = event.getArgs(); - dispatchUserEvent(new MessageReceivedEvent( - gameObjImpl, (args[0] as String), - (args[1] as Integer).value)); - } - } - - /** - * Called by our user listener when we receive a message event - * on the user object. - */ - protected function messageReceivedOnUserObject (event :MessageEvent) :void - { - // see if it's a message about user games - var msgName :String = - EZGameObject.USER_MESSAGE + ":" + _ezObj.getOid(); - if (msgName == event.getName()) { - dispatchUserMessage(event.getArgs()); - } - } - - /** - * Dispatch the user message. - */ - protected function dispatchUserMessage (args :Array) :void - { - dispatchUserEvent(new MessageReceivedEvent( - gameObjImpl, (args[0] as String), - EZObjectMarshaller.decode(args[1]))); - } - - override protected function createPlaceView (ctx :CrowdContext) :PlaceView - { - return new EZGamePanel(ctx, this); + _panel.backend.turnDidChange(); } override protected function gameDidStart () :void { super.gameDidStart(); - dispatchUserEvent( - new StateChangedEvent(StateChangedEvent.GAME_STARTED, gameObjImpl)); + + _panel.backend.gameDidStart(); } override protected function gameDidEnd () :void { super.gameDidEnd(); - dispatchUserEvent( - new StateChangedEvent(StateChangedEvent.GAME_ENDED, gameObjImpl)); + + _panel.backend.gameDidEnd(); } - protected function dispatchUserEvent (event :Event) :void + override protected function createPlaceView (ctx :CrowdContext) :PlaceView { - gameObjImpl.dispatch(event); + _panel = new EZGamePanel(ctx, this); + return _panel; } protected var _ezObj :EZGameObject; protected var _turnDelegate :TurnGameControllerDelegate; - protected var _userListener :MessageAdapter = - new MessageAdapter(messageReceivedOnUserObject); + protected var _panel :EZGamePanel; } } diff --git a/src/as/com/threerings/ezgame/client/EZGamePanel.as b/src/as/com/threerings/ezgame/client/EZGamePanel.as index ef13843f..94e931bf 100644 --- a/src/as/com/threerings/ezgame/client/EZGamePanel.as +++ b/src/as/com/threerings/ezgame/client/EZGamePanel.as @@ -2,6 +2,7 @@ package com.threerings.ezgame.client { import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; +import flash.display.Loader; import flash.events.Event; @@ -26,106 +27,48 @@ import com.threerings.crowd.util.CrowdContext; import com.threerings.ezgame.data.EZGameConfig; import com.threerings.ezgame.data.EZGameObject; -import com.threerings.ezgame.Game; - public class EZGamePanel extends VBox implements PlaceView { + /** The game object backend. */ + public var backend :GameControlBackend; + public function EZGamePanel (ctx :CrowdContext, ctrl :EZGameController) { _ctx = ctx; _ctrl = ctrl; - // add a listener so that we hear about all new children - addEventListener(Event.ADDED, childAdded); - addEventListener(Event.REMOVED, childRemoved); - - var cfg :EZGameConfig = (ctrl.getPlaceConfig() as EZGameConfig); - _gameView = new MediaContainer(cfg.configData); // TODO - addChild(_gameView); - //addChild(new ChatDisplayBox(ctx)); } // from PlaceView public function willEnterPlace (plobj :PlaceObject) :void { - // don't start notifying anything of the game until we've - // notified the game manager that we're in the game - // (done in GameController, and it uses callLater, so we do it twice!) - _ctx.getClient().callLater(function () :void { - _ctx.getClient().callLater(function () :void { - _ezObj = (plobj as EZGameObject); + var cfg :EZGameConfig = (_ctrl.getPlaceConfig() as EZGameConfig); - notifyOfGame(_gameView); - }); - }); + _ezObj = (plobj as EZGameObject); + backend = new GameControlBackend(_ctx, _ezObj); + + _gameView = new GameContainer(cfg.configData); // TODO + _gameView.tabEnabled = true; + backend.setSharedEvents( + Loader(_gameView.getMedia()).contentLoaderInfo.sharedEvents); + addChild(_gameView); } // from PlaceView public function didLeavePlace (plobj :PlaceObject) :void { - removeListeners(_gameView); - _ezObj = null; - } + _gameView.shutdown(true); + removeChild(_gameView); - /** - * Handle ADDED events. - */ - protected function childAdded (event :Event) :void - { - if (_ezObj != null) { - notifyOfGame(event.target as DisplayObject); - } - } - - /** - * Handle REMOVED events. - */ - protected function childRemoved (event :Event) :void - { - if (_ezObj != null) { - removeListeners(event.target as DisplayObject); - } - } - - /** - * Find any children of the specified object that implement - * com.metasoy.game.Game and provide them with the GameObject. - */ - protected function notifyOfGame (root :DisplayObject) :void - { - DisplayUtil.walkDisplayObjects(root, - function (disp :DisplayObject) :void - { - if (disp is Game) { - // only notify the Game if we haven't seen it before - if (null == _seenGames[disp]) { - (disp as Game).setGameObject(_ctrl.gameObjImpl); - _seenGames[disp] = true; - } - } - // always check to see if it's a listener - _ctrl.gameObjImpl.registerListener(disp); - }); - } - - protected function removeListeners (root :DisplayObject) :void - { - DisplayUtil.walkDisplayObjects(root, - function (disp :DisplayObject) :void - { - _ctrl.gameObjImpl.unregisterListener(disp); - }); + backend.shutdown(); } protected var _ctx :CrowdContext; protected var _ctrl :EZGameController; - protected var _gameView :MediaContainer; - - /** A weak-key hash of the Game interfaces we've already seen. */ - protected var _seenGames :Dictionary = new Dictionary(true); + protected var _gameView :GameContainer; protected var _ezObj :EZGameObject; } diff --git a/src/as/com/threerings/ezgame/client/GameContainer.as b/src/as/com/threerings/ezgame/client/GameContainer.as new file mode 100644 index 00000000..7772db10 --- /dev/null +++ b/src/as/com/threerings/ezgame/client/GameContainer.as @@ -0,0 +1,28 @@ +package com.threerings.ezgame.client { + +import flash.display.InteractiveObject; +import flash.display.Loader; + +import mx.managers.IFocusManagerComponent; + +import com.threerings.util.MediaContainer; + +public class GameContainer extends MediaContainer + implements IFocusManagerComponent +{ + public function GameContainer (url :String) + { + super(url); + } + + override public function setFocus () :void + { + super.setFocus(); + if (systemManager.stage.focus == this) { + // TODO + //systemManager.stage.focus = InteractiveObject(Loader(_media).content); + systemManager.stage.focus = Loader(_media); + } + } +} +} diff --git a/src/as/com/threerings/ezgame/client/GameObjectImpl.as b/src/as/com/threerings/ezgame/client/GameControlBackend.as similarity index 60% rename from src/as/com/threerings/ezgame/client/GameObjectImpl.as rename to src/as/com/threerings/ezgame/client/GameControlBackend.as index b00651bf..501932c6 100644 --- a/src/as/com/threerings/ezgame/client/GameObjectImpl.as +++ b/src/as/com/threerings/ezgame/client/GameControlBackend.as @@ -4,6 +4,7 @@ import flash.errors.IllegalOperationError; import flash.events.Event; import flash.events.EventDispatcher; +import flash.events.IEventDispatcher; import flash.utils.IExternalizable; import flash.utils.ByteArray; @@ -12,6 +13,7 @@ import flash.utils.Dictionary; import com.threerings.io.TypedArray; import com.threerings.util.ClassUtil; +import com.threerings.util.Integer; import com.threerings.util.MessageBundle; import com.threerings.util.Name; import com.threerings.util.StringUtil; @@ -20,61 +22,109 @@ import com.threerings.presents.client.ConfirmAdapter; import com.threerings.presents.client.InvocationService_ConfirmListener; import com.threerings.presents.dobj.EntryAddedEvent; +import com.threerings.presents.dobj.EntryRemovedEvent; import com.threerings.presents.dobj.EntryUpdatedEvent; -import com.threerings.presents.dobj.SetAdapter; +import com.threerings.presents.dobj.SetListener; +import com.threerings.presents.dobj.MessageAdapter; +import com.threerings.presents.dobj.MessageEvent; +import com.threerings.presents.dobj.MessageListener; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.util.CrowdContext; import com.threerings.ezgame.data.EZGameObject; import com.threerings.ezgame.data.PropertySetEvent; +import com.threerings.ezgame.data.PropertySetListener; import com.threerings.ezgame.data.UserCookie; import com.threerings.ezgame.util.EZObjectMarshaller; -import com.threerings.ezgame.EZGame; -import com.threerings.ezgame.MessageReceivedEvent; -import com.threerings.ezgame.MessageReceivedListener; -import com.threerings.ezgame.PropertyChangedEvent; -import com.threerings.ezgame.PropertyChangedListener; -import com.threerings.ezgame.StateChangedEvent; -import com.threerings.ezgame.StateChangedListener; - -public class GameObjectImpl extends EventDispatcher - implements EZGame +public class GameControlBackend + implements MessageListener, SetListener, PropertySetListener { - public function GameObjectImpl (ctx :CrowdContext, ezObj :EZGameObject) + public var log :Log = Log.getLog(this); + + public function GameControlBackend ( + ctx :CrowdContext, ezObj :EZGameObject) { _ctx = ctx; _ezObj = ezObj; - _gameData = new GameData(this, _ezObj.getUserProps()); + _gameData = new GameData(setProperty_v1, _ezObj.getUserProps()); - _ezObj.addListener(new SetAdapter(entryAdded, entryUpdated, null)); + _ezObj.addListener(this); + _ctx.getClient().getClientObject().addListener(_userListener); } - // from EZGame - public function get data () :Object + public function setSharedEvents (disp :IEventDispatcher) :void { - return _gameData; + disp.addEventListener("ezgameQuery", handleEZQuery); } - // from EZGame - public function get (propName :String, index :int = -1) :Object + public function shutdown () :void { - var value :Object = data[propName]; - if (index >= 0) { - if (value is Array) { - return (value as Array)[index]; + _ezObj.removeListener(this); + _ctx.getClient().getClientObject().removeListener(_userListener); + } - } else { - throw new ArgumentError("Property " + propName + - " is not an array."); + protected function handleEZQuery (evt :Object) :void + { + setUserCodeProperties(evt.userProps); + evt.ezProps = new Object(); + populateProperties(evt.ezProps); + } + + protected function setUserCodeProperties (o :Object) :void + { + // here we would handle adapting old functions to a new version + + _userFuncs = o; + } + + protected function callUserCode (name :String, ... args) :* + { + if (_userFuncs != null) { + try { + var func :Function = (_userFuncs[name] as Function); + if (func != null) { + return func.apply(null, args); + } + + } catch (err :Error) { + log.warning("Error in user-code: " + err); + log.logStackTrace(err); } } - return value; + return undefined; } - // from EZGame - public function set (propName :String, value :Object, index :int = -1) :void + protected function populateProperties (o :Object) :void + { + // straight data + o["gameData"] = _gameData; + + // functions + o["setProperty_v1"] = setProperty_v1; + o["mergeCollection_v1"] = mergeCollection_v1; + o["sendMessage_v1"] = sendMessage_v1; + o["setTicker_v1"] = setTicker_v1; + o["sendChat_v1"] = sendChat_v1; + o["localChat_v1"] = localChat_v1; + o["getPlayerCount_v1"] = getPlayerCount_v1; + o["getPlayerNames_v1"] = getPlayerNames_v1; + o["getMyIndex_v1"] = getMyIndex_v1; + o["getTurnHolderIndex_v1"] = getTurnHolderIndex_v1; + o["getWinnerIndexes_v1"] = getWinnerIndexes_v1; + o["getUserCookie_v1"] = getUserCookie_v1; + o["setUserCookie_v1"] = setUserCookie_v1; + o["isMyTurn_v1"] = isMyTurn_v1; + o["isInPlay_v1"] = isInPlay_v1; + o["endTurn_v1"] = endTurn_v1; + o["endGame_v1"] = endGame_v1; + o["populateCollection_v1"] = populateCollection_v1; + o["getFromCollection_v1"] = getFromCollection_v1; + } + + public function setProperty_v1 ( + propName :String, value :Object, index :int) :void { validatePropertyChange(propName, value, index); @@ -87,91 +137,8 @@ public class GameObjectImpl extends EventDispatcher _ezObj.applyPropertySet(propName, value, index); } - // from EZGame - public function registerListener (obj :Object) :void - { - if (obj is MessageReceivedListener) { - var mrl :MessageReceivedListener = (obj as MessageReceivedListener); - 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); - } - 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); - } - } - - // from EZGame - public function unregisterListener (obj :Object) :void - { - if (obj is MessageReceivedListener) { - var mrl :MessageReceivedListener = (obj as MessageReceivedListener); - removeEventListener( - MessageReceivedEvent.TYPE, mrl.messageReceived); - } - if (obj is PropertyChangedListener) { - var pcl :PropertyChangedListener = (obj as PropertyChangedListener); - removeEventListener( - PropertyChangedEvent.TYPE, pcl.propertyChanged); - } - if (obj is StateChangedListener) { - var scl :StateChangedListener = (obj as StateChangedListener); - removeEventListener( - StateChangedEvent.GAME_STARTED, scl.stateChanged); - removeEventListener( - StateChangedEvent.TURN_CHANGED, scl.stateChanged); - removeEventListener( - StateChangedEvent.GAME_ENDED, scl.stateChanged); - } - } - - // from EZGame - public function setCollection (collName :String, values :Array) :void - { - populateCollection(collName, values, true); - } - - // from EZGame - public function addToCollection (collName :String, values :Array) :void - { - populateCollection(collName, values, false); - } - - // from EZGame - public function pickFromCollection ( - collName :String, count :int, msgOrPropName :String, - playerIndex :int = -1) :void - { - getFromCollection(collName, count, msgOrPropName, playerIndex, - false, null); - } - - // from EZGame - public function dealFromCollection ( - collName :String, count :int, msgOrPropName :String, - callback :Function = null, playerIndex :int = -1) :void - { - getFromCollection(collName, count, msgOrPropName, playerIndex, - true, callback); - } - - // from EZGame - public function mergeCollection (srcColl :String, intoColl :String) :void + public function mergeCollection_v1 ( + srcColl :String, intoColl :String) :void { validateName(srcColl); validateName(intoColl); @@ -179,9 +146,8 @@ public class GameObjectImpl extends EventDispatcher srcColl, intoColl, createLoggingListener("mergeCollection")); } - // from EZGame - public function sendMessage ( - messageName :String, value :Object, playerIndex :int = -1) :void + public function sendMessage_v1 ( + messageName :String, value :Object, playerIndex :int) :void { validateName(messageName); validateValue(value); @@ -192,22 +158,14 @@ public class GameObjectImpl extends EventDispatcher createLoggingListener("sendMessage")); } - // from EZGame - public function startTicker (tickerName :String, msOfDelay :int) :void + public function setTicker_v1 (tickerName :String, msOfDelay :int) :void { validateName(tickerName); _ezObj.ezGameService.setTicker(_ctx.getClient(), tickerName, msOfDelay, createLoggingListener("setTicker")); } - // from EZGame - public function stopTicker (tickerName :String) :void - { - startTicker(tickerName, 0); - } - - // from EZGame - public function sendChat (msg :String) :void + public function sendChat_v1 (msg :String) :void { validateChat(msg); // Post a message to the game object, the controller @@ -215,8 +173,7 @@ public class GameObjectImpl extends EventDispatcher _ezObj.postMessage(EZGameObject.GAME_CHAT, [ msg ]); } - // from EZGame - public function localChat (msg :String) :void + public function localChat_v1 (msg :String) :void { validateChat(msg); // The sendChat() messages will end up being routed @@ -225,14 +182,12 @@ public class GameObjectImpl extends EventDispatcher _ctx.getChatDirector().displayInfo(null, MessageBundle.taint(msg)); } - // from EZGame - public function getPlayerCount () :int + public function getPlayerCount_v1 () :int { return _ezObj.getPlayerCount(); } - // from EZGame - public function getPlayerNames () :Array + public function getPlayerNames_v1 () :Array { var names :Array = new Array(); for each (var name :Name in _ezObj.players) { @@ -241,20 +196,17 @@ public class GameObjectImpl extends EventDispatcher return names; } - // from EZGame - public function getMyIndex () :int + public function getMyIndex_v1 () :int { return _ezObj.getPlayerIndex(getUsername()); } - // from EZGame - public function getTurnHolderIndex () :int + public function getTurnHolderIndex_v1 () :int { return _ezObj.getPlayerIndex(_ezObj.turnHolder); } - // from EZGame - public function getWinnerIndexes () :Array /* of int */ + public function getWinnerIndexes_v1 () :Array /* of int */ { var arr :Array = new Array(); if (_ezObj.winners != null) { @@ -267,8 +219,8 @@ public class GameObjectImpl extends EventDispatcher return arr; } - // from EZGame - public function getUserCookie (playerIndex :int, callback :Function) :void + public function getUserCookie_v1 ( + playerIndex :int, callback :Function) :void { // see if that cookie is already published if (_ezObj.userCookies != null) { @@ -295,8 +247,7 @@ public class GameObjectImpl extends EventDispatcher createLoggingListener("getUserCookie")); } - // from EZGame - public function setUserCookie (cookie :Object) :Boolean + public function setUserCookie_v1 (cookie :Object) :Boolean { var ba :ByteArray = (EZObjectMarshaller.encode(cookie, false) as ByteArray); @@ -310,91 +261,36 @@ public class GameObjectImpl extends EventDispatcher return true; } - // from EZGame - public function isMyTurn () :Boolean + public function isMyTurn_v1 () :Boolean { return getUsername().equals(_ezObj.turnHolder); } - // from EZGame - public function isInPlay () :Boolean + public function isInPlay_v1 () :Boolean { return _ezObj.isInPlay(); } - // from EZGame - public function endTurn (nextPlayerIndex :int = -1) :void + public function endTurn_v1 (nextPlayerIndex :int = -1) :void { _ezObj.ezGameService.endTurn(_ctx.getClient(), nextPlayerIndex, createLoggingListener("endTurn")); } - // from EZGame - public function endGame (winnerIndex :int, ... rest) :void + public function endGame_v1 (... winnerDexes) :void { var winners :TypedArray = TypedArray.create(int); - winners.push(winnerIndex); - while (rest.length > 0) { - winners.push(int(rest.shift())); + while (winnerDexes.length > 0) { + winners.push(int(winnerDexes.shift())); } _ezObj.ezGameService.endGame(_ctx.getClient(), winners, createLoggingListener("endGame")); } - override public function willTrigger (type :String) :Boolean - { - throw new IllegalOperationError(); - } - - override public function dispatchEvent (event :Event) :Boolean - { - // Ideally we want to not be an IEventDispatcher so that people - // won't try to do this on us, but if we do that, then some other - // object will be the target during dispatch, and that's confusing. - // It's really nice to be able to - throw new IllegalOperationError(); - } - - /** - * Secret function to dispatch property changed events. - */ - internal function dispatch (event :Event) :void - { - try { - super.dispatchEvent(event); - } catch (err :Error) { - var log :Log = Log.getLog(this); - log.warning("Error dispatching event to user game."); - log.logStackTrace(err); - } - } - - /** - * Convenience function to get our name. - */ - private function getUsername () :Name - { - var body :BodyObject = - (_ctx.getClient().getClientObject() as BodyObject); - return body.getVisibleName(); - } - - /** - * Create a listener for service requests. - */ - private function createLoggingListener ( - service :String) :InvocationService_ConfirmListener - { - return new ConfirmAdapter(function (cause :String) :void { - Log.getLog(this).warning("Service failure " + - "[service=" + service + ", cause=" + cause + "]."); - }); - } - /** * Helper method for setCollection and addToCollection. */ - private function populateCollection ( + public function populateCollection_v1 ( collName :String, values :Array, clearExisting :Boolean) :void { validateName(collName); @@ -414,7 +310,7 @@ public class GameObjectImpl extends EventDispatcher /** * Helper method for pickFromCollection and dealFromCollection. */ - private function getFromCollection( + public function getFromCollection_v1 ( collName :String, count :int, msgOrPropName :String, playerIndex :int, consume :Boolean, callback :Function) :void { @@ -446,10 +342,32 @@ public class GameObjectImpl extends EventDispatcher playerIndex, listener); } + /** + * Convenience function to get our name. + */ + protected function getUsername () :Name + { + var body :BodyObject = + (_ctx.getClient().getClientObject() as BodyObject); + return body.getVisibleName(); + } + + /** + * Create a listener for service requests. + */ + protected function createLoggingListener ( + service :String) :InvocationService_ConfirmListener + { + return new ConfirmAdapter(function (cause :String) :void { + Log.getLog(this).warning("Service failure " + + "[service=" + service + ", cause=" + cause + "]."); + }); + } + /** * Verify that the property name / value are valid. */ - private function validatePropertyChange ( + protected function validatePropertyChange ( propName :String, value :Object, index :int) :void { validateName(propName); @@ -469,7 +387,7 @@ public class GameObjectImpl extends EventDispatcher /** * Verify that the specified name is valid. */ - private function validateName (name :String) :void + protected function validateName (name :String) :void { if (name == null) { throw new ArgumentError( @@ -477,7 +395,7 @@ public class GameObjectImpl extends EventDispatcher } } - private function validateChat (msg :String) :void + protected function validateChat (msg :String) :void { if (StringUtil.isBlank(msg)) { throw new ArgumentError( @@ -488,7 +406,7 @@ public class GameObjectImpl extends EventDispatcher /** * Verify that the value is legal to be streamed to other clients. */ - private function validateValue (value :Object) :void + protected function validateValue (value :Object) :void { if (value == null) { return; @@ -498,7 +416,7 @@ public class GameObjectImpl extends EventDispatcher "IExternalizable is not yet supported"); } else if (value is Array) { - if (ClassUtil.getClass(value) != Array) { + if (ClassUtil.getClassName(value) != "Array") { // We can't allow arrays to be serialized as IExternalizables // because we need to know element values (opaquely) on the // server. Also, we don't allow other types because we wouldn't @@ -532,29 +450,99 @@ public class GameObjectImpl extends EventDispatcher } /** - * Handle entry updated + * Called by the EZGameController when the turn changes. */ - private function entryAdded (event :EntryAddedEvent) :void + public function turnDidChange () :void + { + callUserCode("turnDidChange_v1"); + } + + /** + * Called by the EZGameController when the game starts. + */ + public function gameDidStart () :void + { + callUserCode("gameDidStart_v1"); + } + + /** + * Called by the EZGameController when the game ends. + */ + public function gameDidEnd () :void + { + callUserCode("gameDidEnd_v1"); + } + + // from SetListener + public function entryAdded (event :EntryAddedEvent) :void { if (EZGameObject.USER_COOKIES == event.getName()) { receivedUserCookie(event.getEntry() as UserCookie); } } - /** - * Handle entry updated - */ - private function entryUpdated (event :EntryUpdatedEvent) :void + // from SetListener + public function entryUpdated (event :EntryUpdatedEvent) :void { if (EZGameObject.USER_COOKIES == event.getName()) { receivedUserCookie(event.getEntry() as UserCookie); } } + // from SetListener + public function entryRemoved (event :EntryRemovedEvent) :void + { + // nada + } + + // from PropertySetListener + public function propertyWasSet (event :PropertySetEvent) :void + { + callUserCode("propertyWasSet_v1", event.getName(), event.getValue(), + event.getOldValue(), event.getIndex()); + } + + public function messageReceived (event :MessageEvent) :void + { + var name :String = event.getName(); + if (EZGameObject.USER_MESSAGE == name) { + var args :Array = event.getArgs(); + callUserCode("messageReceived_v1", (args[0] as String), + EZObjectMarshaller.decode(args[1])); + + } else if (EZGameObject.GAME_CHAT == name) { + // this is chat send by the game, let's route it like + // localChat, which is also sent by the game + localChat_v1(String(event.getArgs()[0])); + + } else if (EZGameObject.TICKER == name) { + var targs :Array = event.getArgs(); + callUserCode("messageReceived_v1", (targs[0] as String), + (targs[1] as Integer).value); + } + } + + /** + * Called by our user listener when we receive a message event + * on the user object. + */ + protected function messageReceivedOnUserObject (event :MessageEvent) :void + { + // see if it's a message about user games + var msgName :String = + EZGameObject.USER_MESSAGE + ":" + _ezObj.getOid(); + if (msgName == event.getName()) { + var args :Array = event.getArgs(); + callUserCode("messageReceived_v1", (args[0] as String), + EZObjectMarshaller.decode(args[1])); + } + } + + /** * Handle the arrival of a new UserCookie. */ - private function receivedUserCookie (cookie :UserCookie) :void + protected function receivedUserCookie (cookie :UserCookie) :void { if (_cookieCallbacks != null) { var arr :Array = (_cookieCallbacks[cookie.playerIndex] as Array); @@ -573,8 +561,13 @@ public class GameObjectImpl extends EventDispatcher protected var _ctx :CrowdContext; + protected var _userListener :MessageAdapter = + new MessageAdapter(messageReceivedOnUserObject); + protected var _ezObj :EZGameObject; + protected var _userFuncs :Object; + protected var _gameData :GameData; /** playerIndex -> callback functions waiting for the cookie. */ diff --git a/src/as/com/threerings/ezgame/client/GameData.as b/src/as/com/threerings/ezgame/client/GameData.as index a59f8266..d408aec1 100644 --- a/src/as/com/threerings/ezgame/client/GameData.as +++ b/src/as/com/threerings/ezgame/client/GameData.as @@ -10,9 +10,9 @@ use namespace flash_proxy; public class GameData extends Proxy { - public function GameData (gameObjImpl :GameObjectImpl, props :Object) + public function GameData (propSetFn :Function, props :Object) { - _gameObjImpl = gameObjImpl; + _propSetFn = propSetFn; _props = props; } @@ -67,13 +67,13 @@ public class GameData extends Proxy override flash_proxy function setProperty (propName :*, value :*) :void { - _gameObjImpl.set(String(propName), value); + _propSetFn(String(propName), value, -1); } override flash_proxy function deleteProperty (propName :*) :Boolean { var hasProp :Boolean = hasProperty(propName); - _gameObjImpl.set(String(propName), null); + setProperty(propName, null); return hasProp; } @@ -109,11 +109,11 @@ public class GameData extends Proxy return _props[nextName(index)]; } - /** The GameObject that controls things. */ - protected var _gameObjImpl :GameObjectImpl; + /** The function which we pass property setting to. */ + protected var _propSetFn :Function; /** The object we're proxying. */ - protected var _props :Object; + protected var _props :Object = { }; /** Used temporarily while iterating over our names or values. */ protected var _propertyList :Array;