Moved the serverless game stuff out of msoy and back into the vilya library.
We'll be using this in game gardens, at least. Note that the actionscript side currently doesn't compile because of limitations in building a .swc file, but that'll be fixed soon. git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@47 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package com.threerings.ezgame {
|
||||
|
||||
/**
|
||||
* A simple card deck that encodes cards as a string like "Ac" for the
|
||||
* ace of clubs, or "Td" for the 10 of diamonds.
|
||||
*/
|
||||
public class CardDeck
|
||||
{
|
||||
public function CardDeck (gameObj :GameObject, deckName :String = "deck")
|
||||
{
|
||||
_gameObj = gameObj;
|
||||
_deckName = deckName;
|
||||
|
||||
var deck :Array = new Array();
|
||||
for each (var rank :String in ["2", "3", "4", "5", "6", "7", "8",
|
||||
"9", "T", "J", "Q", "K", "A"]) {
|
||||
for each (var suit :String in ["c", "d", "h", "s"]) {
|
||||
deck.push(rank + suit);
|
||||
}
|
||||
}
|
||||
|
||||
_gameObj.setCollection(_deckName, deck);
|
||||
}
|
||||
|
||||
public function dealToPlayer (
|
||||
playerIdx :int, count :int, msgName :String) :void
|
||||
{
|
||||
// TODO: support the callback
|
||||
_gameObj.dealFromCollection(_deckName, count, msgName, null, playerIdx);
|
||||
}
|
||||
|
||||
public function dealToData (count :int, propName :String) :void
|
||||
{
|
||||
_gameObj.dealFromCollection(_deckName, count, propName, null);
|
||||
}
|
||||
|
||||
/** The game object. */
|
||||
protected var _gameObj :GameObject;
|
||||
|
||||
/** The name of our deck. */
|
||||
protected var _deckName :String;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
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 :GameObject) :void;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.threerings.ezgame {
|
||||
|
||||
import flash.events.IEventDispatcher;
|
||||
|
||||
/**
|
||||
* The game object that you'll be using to manage your game.
|
||||
*/
|
||||
public interface GameObject
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
// 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.
|
||||
*/
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.threerings.ezgame {
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
public class MessageReceivedEvent extends Event
|
||||
{
|
||||
/** The type of all MessageReceivedEvents. */
|
||||
public static const TYPE :String = "msgReceived";
|
||||
|
||||
/**
|
||||
* Access the message name.
|
||||
*/
|
||||
public function get name () :String
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the message value.
|
||||
*/
|
||||
public function get value () :Object
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
public function MessageReceivedEvent (messageName :String, value :Object)
|
||||
{
|
||||
super(TYPE);
|
||||
_name = messageName;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
override public function toString () :String
|
||||
{
|
||||
return "[MessageReceivedEvent name=" + _name +
|
||||
", value=" + _value + "]";
|
||||
}
|
||||
|
||||
override public function clone () :Event
|
||||
{
|
||||
return new MessageReceivedEvent(_name, _value);
|
||||
}
|
||||
|
||||
protected var _name :String;
|
||||
protected var _value :Object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.threerings.ezgame {
|
||||
|
||||
import flash.display.Sprite;
|
||||
|
||||
import flash.text.StyleSheet;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
|
||||
/**
|
||||
* A sample component that displays the players of a game.
|
||||
* If the game has a turn holder, the current turn holder will be highlighted.
|
||||
*
|
||||
* This class demonstrates that the 'Game' interface may be implemented
|
||||
* by any DisplayObject that want access to the GameObject, not just the
|
||||
* actual DisplayObject that is displaying the game. Here, all we are
|
||||
* interested in is the names of the players and the current turn holder.
|
||||
*
|
||||
* You may use this, with any modifications you desire, in your game. Feel
|
||||
* free to copy/modify or extend this class.
|
||||
*/
|
||||
public class PlayersDisplay extends Sprite
|
||||
implements Game
|
||||
{
|
||||
// implementation of Game method
|
||||
public function setGameObject (gameObj :GameObject) :void
|
||||
{
|
||||
_gameObj = gameObj;
|
||||
_gameObj.addEventListener(StateChangedEvent.TURN_CHANGED, recheckTurn);
|
||||
_gameObj.addEventListener(StateChangedEvent.GAME_ENDED, recheckTurn);
|
||||
|
||||
setupPlayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the player labels and configure the look of the entire UI.
|
||||
*/
|
||||
protected function setupPlayers () :void
|
||||
{
|
||||
var y :Number = BORDER;
|
||||
// create a label at the top, above the player names
|
||||
var label :TextField = new TextField();
|
||||
// damn stylesheet doesn't seem to actually -work-
|
||||
// var style :StyleSheet = new StyleSheet();
|
||||
// style.fontWeight = "bold";
|
||||
// style.color = "#0000FF";
|
||||
// style.fontFamily = "serif";
|
||||
// style.fontSize = 18;
|
||||
// label.styleSheet = style;
|
||||
label.autoSize = TextFieldAutoSize.LEFT;
|
||||
label.selectable = false;
|
||||
label.text = "Players";
|
||||
label.x = BORDER;
|
||||
label.y = y;
|
||||
addChild(label);
|
||||
y += label.textHeight;
|
||||
|
||||
var maxWidth :Number = label.textWidth;
|
||||
|
||||
// create a label for each player
|
||||
for each (var name :String in _gameObj.getPlayerNames()) {
|
||||
y += PAD;
|
||||
label = new TextField();
|
||||
label.autoSize = TextFieldAutoSize.LEFT;
|
||||
label.background = true;
|
||||
label.selectable = false;
|
||||
label.text = name;
|
||||
label.x = BORDER;
|
||||
label.y = y;
|
||||
addChild(label);
|
||||
y += label.textHeight;
|
||||
maxWidth = Math.max(maxWidth, label.textWidth);
|
||||
|
||||
_playerLabels.push(label);
|
||||
}
|
||||
|
||||
// make all the player labels the same width
|
||||
// (looks nice when highlighted)
|
||||
for each (label in _playerLabels) {
|
||||
label.autoSize = TextFieldAutoSize.NONE;
|
||||
label.width = maxWidth;
|
||||
}
|
||||
|
||||
// draw a blue rectangle around everything
|
||||
graphics.clear();
|
||||
graphics.lineStyle(1, 0x0000FF);
|
||||
graphics.drawRect(0, 0, maxWidth + (BORDER * 2), y + BORDER);
|
||||
|
||||
displayCurrentTurn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-set the background color for every player label, highlighting
|
||||
* only the player who has the turn.
|
||||
*/
|
||||
protected function displayCurrentTurn () :void
|
||||
{
|
||||
var idx :int = _gameObj.isInPlay() ? _gameObj.getTurnHolderIndex() : -1;
|
||||
for (var ii :int = 0; ii < _playerLabels.length; ii++) {
|
||||
var label :TextField = (_playerLabels[ii] as TextField);
|
||||
label.backgroundColor = (ii == idx) ? TURN_BACKGROUND
|
||||
: NORMAL_BACKGROUND;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registered to receive TURN_CHANGED and GAME_ENDED events
|
||||
* from the GameObject.
|
||||
*/
|
||||
protected function recheckTurn (event :StateChangedEvent) :void
|
||||
{
|
||||
displayCurrentTurn();
|
||||
}
|
||||
|
||||
/** Our game object. */
|
||||
protected var _gameObj :GameObject;
|
||||
|
||||
/** An array of labels, one for each player name. */
|
||||
protected var _playerLabels :Array = [];
|
||||
|
||||
/** The background color for players that doesn't have the turn. */
|
||||
protected static const NORMAL_BACKGROUND :uint = 0xFFFFFF;
|
||||
|
||||
/** The background color for players that do have the turn. */
|
||||
protected static const TURN_BACKGROUND :uint = 0xFF9999;
|
||||
|
||||
/** The number of pixels to border around all the names. */
|
||||
protected static const BORDER :int = 6;
|
||||
|
||||
/** The additional padding inserted between names. */
|
||||
protected static const PAD :int = 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.threerings.ezgame {
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* Property change events are dispatched after the property change was
|
||||
* validated on the server.
|
||||
*/
|
||||
public class PropertyChangedEvent extends Event
|
||||
{
|
||||
/** The type of a property change event. */
|
||||
public static const TYPE :String = "PropChanged";
|
||||
|
||||
/**
|
||||
* Get the name of the property that changed.
|
||||
*/
|
||||
public function get name () :String
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property's new value.
|
||||
*/
|
||||
public function get newValue () :Object
|
||||
{
|
||||
return _newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property's previous value (handy!).
|
||||
*/
|
||||
public function get oldValue () :Object
|
||||
{
|
||||
return _oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* If an array element was updated, get the index, or -1 if not applicable.
|
||||
*/
|
||||
public function get index () :int
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function PropertyChangedEvent (
|
||||
propName :String, newValue :Object, oldValue :Object, index :int = -1)
|
||||
{
|
||||
super(TYPE);
|
||||
_name = propName;
|
||||
_newValue = newValue;
|
||||
_oldValue = oldValue;
|
||||
_index = index;
|
||||
}
|
||||
|
||||
override public function toString () :String
|
||||
{
|
||||
return "[PropertyChangedEvent name=" + _name + ", value=" + _newValue +
|
||||
((index < 0) ? "" : (", index=" + index)) + "]";
|
||||
}
|
||||
|
||||
override public function clone () :Event
|
||||
{
|
||||
return new PropertyChangedEvent(_name, _newValue, _oldValue, _index);
|
||||
}
|
||||
|
||||
/** Our implementation details. */
|
||||
protected var _name :String;
|
||||
protected var _newValue :Object;
|
||||
protected var _oldValue :Object;
|
||||
protected var _index :int;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.threerings.ezgame {
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* Dispatched when the state of the game has changed.
|
||||
*/
|
||||
public class StateChangedEvent extends Event
|
||||
{
|
||||
/** Indicates that the game has transitioned to a started state. */
|
||||
public static const GAME_STARTED :String = "GameStarted";
|
||||
|
||||
/** Indicates that the game has transitioned to a ended state. */
|
||||
public static const GAME_ENDED :String = "GameEnded";
|
||||
|
||||
/** Indicates that the turn has changed. */
|
||||
// TODO: move to own event?
|
||||
public static const TURN_CHANGED :String = "TurnChanged";
|
||||
|
||||
public function StateChangedEvent (type :String)
|
||||
{
|
||||
super(type);
|
||||
}
|
||||
|
||||
override public function toString () :String
|
||||
{
|
||||
return "[StateChangedEvent type=" + type + "]";
|
||||
}
|
||||
|
||||
override public function clone () :Event
|
||||
{
|
||||
return new StateChangedEvent(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.threerings.ezgame.client {
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
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;
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
import com.threerings.parlor.game.client.GameController;
|
||||
|
||||
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
|
||||
{
|
||||
/** The implementation of the GameObject interface for users. */
|
||||
public var gameObjImpl :GameObjectImpl;
|
||||
|
||||
/**
|
||||
*/
|
||||
public function EZGameController ()
|
||||
{
|
||||
addDelegate(_turnDelegate = new TurnGameControllerDelegate(this));
|
||||
}
|
||||
|
||||
override public function willEnterPlace (plobj :PlaceObject) :void
|
||||
{
|
||||
_ezObj = (plobj as EZGameObject);
|
||||
gameObjImpl = new GameObjectImpl(_ctx, _ezObj);
|
||||
|
||||
_ctx.getClient().getClientObject().addListener(_userListener);
|
||||
|
||||
super.willEnterPlace(plobj);
|
||||
}
|
||||
|
||||
override public function didLeavePlace (plobj :PlaceObject) :void
|
||||
{
|
||||
super.didLeavePlace(plobj);
|
||||
|
||||
_ctx.getClient().getClientObject().removeListener(_userListener);
|
||||
|
||||
_ezObj = null;
|
||||
}
|
||||
|
||||
// from TurnGameController
|
||||
public function turnDidChange (turnHolder :Name) :void
|
||||
{
|
||||
dispatchUserEvent(
|
||||
new StateChangedEvent(StateChangedEvent.TURN_CHANGED));
|
||||
}
|
||||
|
||||
// from PropertySetListener
|
||||
public function propertyWasSet (event :PropertySetEvent) :void
|
||||
{
|
||||
// notify the user game
|
||||
dispatchUserEvent(new PropertyChangedEvent(
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
(args[0] as String),
|
||||
EZObjectMarshaller.decode(args[1])));
|
||||
}
|
||||
|
||||
override protected function createPlaceView (ctx :CrowdContext) :PlaceView
|
||||
{
|
||||
return new EZGamePanel(ctx, this);
|
||||
}
|
||||
|
||||
override protected function gameDidStart () :void
|
||||
{
|
||||
super.gameDidStart();
|
||||
dispatchUserEvent(
|
||||
new StateChangedEvent(StateChangedEvent.GAME_STARTED));
|
||||
}
|
||||
|
||||
override protected function gameDidEnd () :void
|
||||
{
|
||||
super.gameDidEnd();
|
||||
dispatchUserEvent(
|
||||
new StateChangedEvent(StateChangedEvent.GAME_ENDED));
|
||||
}
|
||||
|
||||
protected function dispatchUserEvent (event :Event) :void
|
||||
{
|
||||
gameObjImpl.dispatch(event);
|
||||
}
|
||||
|
||||
protected var _ezObj :EZGameObject;
|
||||
|
||||
protected var _turnDelegate :TurnGameControllerDelegate;
|
||||
|
||||
protected var _userListener :MessageAdapter =
|
||||
new MessageAdapter(messageReceivedOnUserObject);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.threerings.ezgame.client {
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.display.DisplayObjectContainer;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
import mx.containers.Canvas;
|
||||
import mx.containers.VBox;
|
||||
|
||||
import mx.core.Container;
|
||||
import mx.core.IChildList;
|
||||
|
||||
import mx.utils.DisplayUtil;
|
||||
|
||||
import com.threerings.crowd.client.PlaceView;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
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
|
||||
{
|
||||
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);
|
||||
|
||||
// TODO: figure out how we're going to add the client game
|
||||
var cfg :EZGameConfig = (ctrl.getPlaceConfig() as EZGameConfig);
|
||||
//_gameView = new MsoySprite(cfg.game);
|
||||
//addChild(_gameView);
|
||||
|
||||
// TODO: move chat component into narya?
|
||||
//addChild(new ChatTextArea(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);
|
||||
|
||||
notifyOfGame(this);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// from PlaceView
|
||||
public function didLeavePlace (plobj :PlaceObject) :void
|
||||
{
|
||||
_ezObj = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ADDED events.
|
||||
*/
|
||||
protected function childAdded (event :Event) :void
|
||||
{
|
||||
if (_ezObj != null) {
|
||||
notifyOfGame(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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected var _ctx :CrowdContext;
|
||||
protected var _ctrl :EZGameController;
|
||||
|
||||
/** A weak-key hash of the Game interfaces we've already seen. */
|
||||
protected var _seenGames :Dictionary = new Dictionary(true);
|
||||
|
||||
protected var _ezObj :EZGameObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.client {
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
import com.threerings.io.TypedArray;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.client.InvocationService_ConfirmListener;
|
||||
import com.threerings.presents.client.InvocationService_InvocationListener;
|
||||
|
||||
/**
|
||||
* Provides services for ez games.
|
||||
*/
|
||||
public interface EZGameService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Request to set the specified property.
|
||||
*/
|
||||
function setProperty (
|
||||
client :Client, propName :String, value :Object, index :int,
|
||||
listener :InvocationService_InvocationListener) :void;
|
||||
|
||||
/**
|
||||
* Request to end the turn, possibly futzing the next turn holder unless
|
||||
* -1 is specified for the nextPlayerIndex.
|
||||
*/
|
||||
function endTurn (
|
||||
client :Client, nextPlayerIndex :int,
|
||||
listener :InvocationService_InvocationListener) :void;
|
||||
|
||||
/**
|
||||
* Request to end the game, with the specified player indices assigned
|
||||
* as winners.
|
||||
*/
|
||||
function endGame (
|
||||
client :Client, winners :TypedArray /* of int */,
|
||||
listener :InvocationService_InvocationListener) :void;
|
||||
|
||||
/**
|
||||
* Request to send a private message to one other player in
|
||||
* the game.
|
||||
*/
|
||||
function sendMessage (
|
||||
client :Client, msgName :String, value :Object, playerIdx :int,
|
||||
listener :InvocationService_InvocationListener) :void;
|
||||
|
||||
/**
|
||||
* Add to the specified named collection.
|
||||
*
|
||||
* @param clearExisting if true, wipe the old contents.
|
||||
*/
|
||||
function addToCollection (
|
||||
client :Client, collName :String, data :TypedArray /* of ByteArray */,
|
||||
clearExisting :Boolean, listener :InvocationService_InvocationListener)
|
||||
:void;
|
||||
|
||||
/**
|
||||
* Merge the specified collection into the other.
|
||||
*/
|
||||
function mergeCollection (
|
||||
client :Client, srcColl :String, intoColl :String,
|
||||
listener :InvocationService_InvocationListener) :void;
|
||||
|
||||
/**
|
||||
* Pick or deal some number of elements from the specified collection,
|
||||
* and either set a property in the flash object, or delivery the
|
||||
* picks to the specified player index via a game message.
|
||||
*/
|
||||
function getFromCollection (
|
||||
client :Client, collName :String, consume :Boolean, count :int,
|
||||
msgOrPropName :String, playerIndex :int,
|
||||
listener :InvocationService_ConfirmListener) :void;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.threerings.ezgame.client {
|
||||
|
||||
import flash.errors.IllegalOperationError;
|
||||
|
||||
import flash.utils.Proxy;
|
||||
|
||||
import flash.utils.flash_proxy;
|
||||
|
||||
use namespace flash_proxy;
|
||||
|
||||
public class GameData extends Proxy
|
||||
{
|
||||
public function GameData (gameObjImpl :GameObjectImpl, props :Object)
|
||||
{
|
||||
_gameObjImpl = gameObjImpl;
|
||||
_props = props;
|
||||
}
|
||||
|
||||
public function hasOwnProperty (propName :String) :Boolean
|
||||
{
|
||||
// pass-through
|
||||
return _props.hasOwnProperty(propName);
|
||||
}
|
||||
|
||||
public function propertyIsEnumerable (propName :String) :Boolean
|
||||
{
|
||||
// pass-through
|
||||
return _props.propertyIsEnumerable(propName);
|
||||
}
|
||||
|
||||
public function setPropertyIsEnumerable (
|
||||
propName :String, isEnum :Boolean = true) :void
|
||||
{
|
||||
// pass-through
|
||||
_props.setPropertyIsEnumerable(propName, isEnum);
|
||||
}
|
||||
|
||||
override flash_proxy function callProperty (propName :*, ... rest) :*
|
||||
{
|
||||
// don't allow function calls
|
||||
throw new IllegalOperationError();
|
||||
}
|
||||
|
||||
override flash_proxy function getDescendants (name :*) :*
|
||||
{
|
||||
// we don't need this XML business
|
||||
throw new IllegalOperationError();
|
||||
}
|
||||
|
||||
override flash_proxy function isAttribute (name :*) :Boolean
|
||||
{
|
||||
// we don't need this XML business
|
||||
throw new IllegalOperationError();
|
||||
}
|
||||
|
||||
override flash_proxy function getProperty (propName :*) :*
|
||||
{
|
||||
// pass-through
|
||||
return _props[propName];
|
||||
}
|
||||
|
||||
override flash_proxy function hasProperty (propName :*) :Boolean
|
||||
{
|
||||
// pass-through
|
||||
return (_props[propName] !== undefined);
|
||||
}
|
||||
|
||||
override flash_proxy function setProperty (propName :*, value :*) :void
|
||||
{
|
||||
_gameObjImpl.set(String(propName), value);
|
||||
}
|
||||
|
||||
override flash_proxy function deleteProperty (propName :*) :Boolean
|
||||
{
|
||||
var hasProp :Boolean = hasProperty(propName);
|
||||
_gameObjImpl.set(String(propName), null);
|
||||
return hasProp;
|
||||
}
|
||||
|
||||
override flash_proxy function nextNameIndex (index :int) :int
|
||||
{
|
||||
// possibly set up the property list on the first call
|
||||
if (index == 0) {
|
||||
_propertyList = [];
|
||||
for (var prop :String in _props) {
|
||||
_propertyList.push(prop);
|
||||
}
|
||||
}
|
||||
|
||||
// return a 1-based index to indicate that there is a property
|
||||
if (index < _propertyList.length) {
|
||||
return index + 1;
|
||||
|
||||
} else {
|
||||
// we're done, clear the prop list
|
||||
_propertyList = null;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
override flash_proxy function nextName (index :int) :String
|
||||
{
|
||||
// the index is 1-based, so subtract one
|
||||
return (_propertyList[index - 1] as String);
|
||||
}
|
||||
|
||||
override flash_proxy function nextValue (index :int) :*
|
||||
{
|
||||
return _props[nextName(index)];
|
||||
}
|
||||
|
||||
/** The GameObject that controls things. */
|
||||
protected var _gameObjImpl :GameObjectImpl;
|
||||
|
||||
/** The object we're proxying. */
|
||||
protected var _props :Object;
|
||||
|
||||
/** Used temporarily while iterating over our names or values. */
|
||||
protected var _propertyList :Array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
package com.threerings.ezgame.client {
|
||||
|
||||
import flash.errors.IllegalOperationError;
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
|
||||
import flash.utils.IExternalizable;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
import com.threerings.io.TypedArray;
|
||||
|
||||
import com.threerings.util.ClassUtil;
|
||||
import com.threerings.util.MessageBundle;
|
||||
import com.threerings.util.Name;
|
||||
import com.threerings.util.StringUtil;
|
||||
|
||||
import com.threerings.presents.client.ConfirmAdapter;
|
||||
import com.threerings.presents.client.InvocationService_ConfirmListener;
|
||||
|
||||
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.util.EZObjectMarshaller;
|
||||
|
||||
import com.threerings.ezgame.GameObject;
|
||||
import com.threerings.ezgame.PropertyChangedEvent;
|
||||
|
||||
public class GameObjectImpl extends EventDispatcher
|
||||
implements GameObject
|
||||
{
|
||||
public function GameObjectImpl (ctx :CrowdContext, ezObj :EZGameObject)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_ezObj = ezObj;
|
||||
_gameData = new GameData(this, _ezObj.getUserProps());
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function get data () :Object
|
||||
{
|
||||
return _gameData;
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
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 GameObject
|
||||
public function set (propName :String, value :Object, index :int = -1) :void
|
||||
{
|
||||
validatePropertyChange(propName, value, index);
|
||||
|
||||
var encoded :Object = EZObjectMarshaller.encode(value);
|
||||
_ezObj.ezGameService.setProperty(
|
||||
_ctx.getClient(), propName, encoded, index,
|
||||
createLoggingListener("setProperty"));
|
||||
|
||||
// set it immediately in the game object
|
||||
_ezObj.applyPropertySet(propName, value, index);
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function setCollection (collName :String, values :Array) :void
|
||||
{
|
||||
populateCollection(collName, values, true);
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function addToCollection (collName :String, values :Array) :void
|
||||
{
|
||||
populateCollection(collName, values, false);
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function pickFromCollection (
|
||||
collName :String, count :int, msgOrPropName :String,
|
||||
playerIndex :int = -1) :void
|
||||
{
|
||||
getFromCollection(collName, count, msgOrPropName, playerIndex,
|
||||
false, null);
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function dealFromCollection (
|
||||
collName :String, count :int, msgOrPropName :String,
|
||||
callback :Function = null, playerIndex :int = -1) :void
|
||||
{
|
||||
getFromCollection(collName, count, msgOrPropName, playerIndex,
|
||||
true, callback);
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function mergeCollection (srcColl :String, intoColl :String) :void
|
||||
{
|
||||
validateName(srcColl);
|
||||
validateName(intoColl);
|
||||
_ezObj.ezGameService.mergeCollection(_ctx.getClient(),
|
||||
srcColl, intoColl, createLoggingListener("mergeCollection"));
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function sendMessage (
|
||||
messageName :String, value :Object, playerIndex :int = -1) :void
|
||||
{
|
||||
validateName(messageName);
|
||||
validateValue(value);
|
||||
|
||||
var encoded :Object = EZObjectMarshaller.encode(value);
|
||||
_ezObj.ezGameService.sendMessage(_ctx.getClient(),
|
||||
messageName, encoded, playerIndex,
|
||||
createLoggingListener("sendMessage"));
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function sendChat (msg :String) :void
|
||||
{
|
||||
validateChat(msg);
|
||||
// Post a message to the game object, the controller
|
||||
// will listen and call localChat().
|
||||
_ezObj.postMessage(EZGameObject.GAME_CHAT, [ msg ]);
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function localChat (msg :String) :void
|
||||
{
|
||||
validateChat(msg);
|
||||
// The sendChat() messages will end up being routed
|
||||
// through this method on each client.
|
||||
// TODO: make this look distinct from other system chat
|
||||
_ctx.getChatDirector().displayInfo(null, MessageBundle.taint(msg));
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function getPlayerNames () :Array
|
||||
{
|
||||
var names :Array = new Array();
|
||||
for each (var name :Name in _ezObj.players) {
|
||||
names.push((name == null) ? null : name.toString());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function getMyIndex () :int
|
||||
{
|
||||
return _ezObj.getPlayerIndex(getUsername());
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function getTurnHolderIndex () :int
|
||||
{
|
||||
return _ezObj.getPlayerIndex(_ezObj.turnHolder);
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function getWinnerIndexes () :Array /* of int */
|
||||
{
|
||||
var arr :Array = new Array();
|
||||
if (_ezObj.winners != null) {
|
||||
for (var ii :int = 0; ii < _ezObj.winners.length; ii++) {
|
||||
if (_ezObj.winners[ii]) {
|
||||
arr.push(ii);
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function isMyTurn () :Boolean
|
||||
{
|
||||
return getUsername().equals(_ezObj.turnHolder);
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function isInPlay () :Boolean
|
||||
{
|
||||
return _ezObj.isInPlay();
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function endTurn (nextPlayerIndex :int = -1) :void
|
||||
{
|
||||
_ezObj.ezGameService.endTurn(_ctx.getClient(), nextPlayerIndex,
|
||||
createLoggingListener("endTurn"));
|
||||
}
|
||||
|
||||
// from GameObject
|
||||
public function endGame (winnerIndex :int, ... rest) :void
|
||||
{
|
||||
var winners :TypedArray = TypedArray.create(int);
|
||||
winners.push(winnerIndex);
|
||||
while (rest.length > 0) {
|
||||
winners.push(int(rest.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(GameObject).warning("Service failure " +
|
||||
"[service=" + service + ", cause=" + cause + "].");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for setCollection and addToCollection.
|
||||
*/
|
||||
private function populateCollection (
|
||||
collName :String, values :Array, clearExisting :Boolean) :void
|
||||
{
|
||||
validateName(collName);
|
||||
if (values == null) {
|
||||
throw new ArgumentError("Collection values may not be null.");
|
||||
}
|
||||
validateValue(values);
|
||||
|
||||
var encodedValues :TypedArray =
|
||||
(EZObjectMarshaller.encode(values) as TypedArray);
|
||||
|
||||
_ezObj.ezGameService.addToCollection(
|
||||
_ctx.getClient(), collName, encodedValues, clearExisting,
|
||||
createLoggingListener("populateCollection"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for pickFromCollection and dealFromCollection.
|
||||
*/
|
||||
private function getFromCollection(
|
||||
collName :String, count :int, msgOrPropName :String, playerIndex :int,
|
||||
consume :Boolean, callback :Function) :void
|
||||
{
|
||||
validateName(collName);
|
||||
validateName(msgOrPropName);
|
||||
if (count < 1) {
|
||||
throw new ArgumentError("Must retrieve at least one element!");
|
||||
}
|
||||
|
||||
var listener :InvocationService_ConfirmListener;
|
||||
if (callback != null) {
|
||||
// TODO: Figure out the method sig of the callback, and what it
|
||||
// means
|
||||
var fn :Function = function (cause :String = null) :void {
|
||||
if (cause == null) {
|
||||
callback(count);
|
||||
} else {
|
||||
callback(parseInt(cause));
|
||||
}
|
||||
};
|
||||
listener = new ConfirmAdapter(fn, fn);
|
||||
|
||||
} else {
|
||||
listener = createLoggingListener("getFromCollection");
|
||||
}
|
||||
|
||||
_ezObj.ezGameService.getFromCollection(
|
||||
_ctx.getClient(), collName, consume, count, msgOrPropName,
|
||||
playerIndex, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the property name / value are valid.
|
||||
*/
|
||||
private function validatePropertyChange (
|
||||
propName :String, value :Object, index :int) :void
|
||||
{
|
||||
validateName(propName);
|
||||
|
||||
// check that we're setting an array element on an array
|
||||
if (index >= 0) {
|
||||
if (!(_gameData[propName] is Array)) {
|
||||
throw new ArgumentError("Property " + propName +
|
||||
" is not an Array.");
|
||||
}
|
||||
}
|
||||
|
||||
// validate the value too
|
||||
validateValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the specified name is valid.
|
||||
*/
|
||||
private function validateName (name :String) :void
|
||||
{
|
||||
if (name == null) {
|
||||
throw new ArgumentError(
|
||||
"Property, message, and collection names must not be null.");
|
||||
}
|
||||
}
|
||||
|
||||
private function validateChat (msg :String) :void
|
||||
{
|
||||
if (StringUtil.isBlank(msg)) {
|
||||
throw new ArgumentError(
|
||||
"Empty chat may not be displayed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the value is legal to be streamed to other clients.
|
||||
*/
|
||||
private function validateValue (value :Object) :void
|
||||
{
|
||||
if (value == null) {
|
||||
return;
|
||||
|
||||
} else if (value is IExternalizable) {
|
||||
throw new ArgumentError(
|
||||
"IExternalizable is not yet supported");
|
||||
|
||||
} else if (value is Array) {
|
||||
if (ClassUtil.getClass(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
|
||||
// create the right class on the other side.
|
||||
throw new ArgumentError(
|
||||
"Custom array subclasses are not supported");
|
||||
}
|
||||
// then, continue on with the sub-properties check (below)
|
||||
|
||||
} else {
|
||||
var type :String = typeof(value);
|
||||
if (type == "number" || type == "string" || type == "boolean" ) {
|
||||
// kosher!
|
||||
return;
|
||||
}
|
||||
if (ClassUtil.getClass(value) != Object) {
|
||||
throw new ArgumentError(
|
||||
"Non-simple properties may not be set.");
|
||||
}
|
||||
// fall through and verify the object's sub-properties
|
||||
}
|
||||
|
||||
// check sub-properties (of arrays and objects)
|
||||
for each (var arrValue :Object in (value as Array)) {
|
||||
validateValue(arrValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected var _ctx :CrowdContext;
|
||||
|
||||
protected var _ezObj :EZGameObject;
|
||||
|
||||
protected var _gameData :GameData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.data {
|
||||
|
||||
import com.threerings.util.MessageBundle;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
import com.threerings.crowd.client.PlaceController;
|
||||
|
||||
import com.threerings.parlor.game.client.GameConfigurator;
|
||||
import com.threerings.parlor.game.data.GameConfig;
|
||||
import com.threerings.parlor.game.data.PartyGameConfig;
|
||||
import com.threerings.parlor.game.data.PartyGameCodes;
|
||||
|
||||
import com.threerings.ezgame.client.EZGameController;
|
||||
|
||||
/**
|
||||
* A game config for a simple multiplayer ez game.
|
||||
*/
|
||||
public /* abstract */ class EZGameConfig extends GameConfig
|
||||
implements PartyGameConfig
|
||||
{
|
||||
/** A creator-submitted name of the game. */
|
||||
public var gameName :String;
|
||||
|
||||
override public function createController () :PlaceController
|
||||
{
|
||||
return new EZGameController();
|
||||
}
|
||||
|
||||
override public function getBundleName () :String
|
||||
{
|
||||
return "general";
|
||||
}
|
||||
|
||||
// TODO
|
||||
//override public function createConfigurator () :GameConfigurator
|
||||
|
||||
override public function getGameName () :String
|
||||
{
|
||||
return MessageBundle.taint(gameName);
|
||||
}
|
||||
|
||||
// from PartyGameConfig
|
||||
public function getPartyGameType () :int
|
||||
{
|
||||
// TODO
|
||||
return PartyGameCodes.NOT_PARTY_GAME;
|
||||
}
|
||||
|
||||
override public function equals (other :Object) :Boolean
|
||||
{
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var that :EZGameConfig = (other as EZGameConfig);
|
||||
return (this.gameName === that.gameName);
|
||||
}
|
||||
|
||||
override public function hashCode () :int
|
||||
{
|
||||
return super.hashCode(); // TODO: incorporate gamename?
|
||||
}
|
||||
|
||||
override public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
super.writeObject(out);
|
||||
|
||||
out.writeField(gameName);
|
||||
}
|
||||
|
||||
override public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
|
||||
gameName = (ins.readField(String) as String);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.data {
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
import com.threerings.util.langBoolean;
|
||||
import com.threerings.util.Integer;
|
||||
|
||||
import com.threerings.io.TypedArray;
|
||||
|
||||
import com.threerings.ezgame.client.EZGameService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.client.InvocationService_ConfirmListener;
|
||||
import com.threerings.presents.client.InvocationService_InvocationListener;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.data.InvocationMarshaller_ConfirmMarshaller;
|
||||
import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller;
|
||||
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link EZGameService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
public class EZGameMarshaller extends InvocationMarshaller
|
||||
implements EZGameService
|
||||
{
|
||||
/** The method id used to dispatch {@link #addToCollection} requests. */
|
||||
public static const ADD_TO_COLLECTION :int = 1;
|
||||
|
||||
// documentation inherited from interface
|
||||
public function addToCollection (arg1 :Client, arg2 :String, arg3 :TypedArray /* of ByteArray */, arg4 :Boolean, arg5 :InvocationService_InvocationListener) :void
|
||||
{
|
||||
var listener5 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
|
||||
listener5.listener = arg5;
|
||||
sendRequest(arg1, ADD_TO_COLLECTION, [
|
||||
arg2, arg3, langBoolean.valueOf(arg4), listener5
|
||||
]);
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #endGame} requests. */
|
||||
public static const END_GAME :int = 2;
|
||||
|
||||
// documentation inherited from interface
|
||||
public function endGame (arg1 :Client, arg2 :TypedArray, arg3 :InvocationService_InvocationListener) :void
|
||||
{
|
||||
var listener3 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
|
||||
listener3.listener = arg3;
|
||||
sendRequest(arg1, END_GAME, [
|
||||
arg2, listener3
|
||||
]);
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #endTurn} requests. */
|
||||
public static const END_TURN :int = 3;
|
||||
|
||||
// documentation inherited from interface
|
||||
public function endTurn (arg1 :Client, arg2 :int, arg3 :InvocationService_InvocationListener) :void
|
||||
{
|
||||
var listener3 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
|
||||
listener3.listener = arg3;
|
||||
sendRequest(arg1, END_TURN, [
|
||||
Integer.valueOf(arg2), listener3
|
||||
]);
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #getFromCollection} requests. */
|
||||
public static const GET_FROM_COLLECTION :int = 4;
|
||||
|
||||
// documentation inherited from interface
|
||||
public function getFromCollection (arg1 :Client, arg2 :String, arg3 :Boolean, arg4 :int, arg5 :String, arg6 :int, arg7 :InvocationService_ConfirmListener) :void
|
||||
{
|
||||
var listener7 :InvocationMarshaller_ConfirmMarshaller = new InvocationMarshaller_ConfirmMarshaller();
|
||||
listener7.listener = arg7;
|
||||
sendRequest(arg1, GET_FROM_COLLECTION, [
|
||||
arg2, langBoolean.valueOf(arg3), Integer.valueOf(arg4), arg5, Integer.valueOf(arg6), listener7
|
||||
]);
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #mergeCollection} requests. */
|
||||
public static const MERGE_COLLECTION :int = 5;
|
||||
|
||||
// documentation inherited from interface
|
||||
public function mergeCollection (arg1 :Client, arg2 :String, arg3 :String, arg4 :InvocationService_InvocationListener) :void
|
||||
{
|
||||
var listener4 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
|
||||
listener4.listener = arg4;
|
||||
sendRequest(arg1, MERGE_COLLECTION, [
|
||||
arg2, arg3, listener4
|
||||
]);
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #sendMessage} requests. */
|
||||
public static const SEND_MESSAGE :int = 6;
|
||||
|
||||
// documentation inherited from interface
|
||||
public function sendMessage (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void
|
||||
{
|
||||
var listener5 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
|
||||
listener5.listener = arg5;
|
||||
sendRequest(arg1, SEND_MESSAGE, [
|
||||
arg2, arg3, Integer.valueOf(arg4), listener5
|
||||
]);
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #setProperty} requests. */
|
||||
public static const SET_PROPERTY :int = 7;
|
||||
|
||||
// documentation inherited from interface
|
||||
public function setProperty (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void
|
||||
{
|
||||
var listener5 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
|
||||
listener5.listener = arg5;
|
||||
sendRequest(arg1, SET_PROPERTY, [
|
||||
arg2, arg3, Integer.valueOf(arg4), listener5
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.threerings.ezgame.data {
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.TypedArray;
|
||||
|
||||
import com.threerings.parlor.game.data.GameObject;
|
||||
import com.threerings.parlor.turn.data.TurnGameObject;
|
||||
|
||||
import com.threerings.ezgame.util.EZObjectMarshaller;
|
||||
|
||||
public class EZGameObject extends GameObject
|
||||
implements TurnGameObject
|
||||
{
|
||||
/** The identifier for a MessageEvent containing a user message. */
|
||||
public static const USER_MESSAGE :String = "Umsg";
|
||||
|
||||
/** The identifier for a MessageEvent containing game-system chat. */
|
||||
public static const GAME_CHAT :String = "Uchat";
|
||||
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The field name of the <code>turnHolder</code> field. */
|
||||
public static const TURN_HOLDER :String = "turnHolder";
|
||||
|
||||
/** The field name of the <code>ezGameService</code> field. */
|
||||
public static const EZ_GAME_SERVICE :String = "ezGameService";
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** The current turn holder. */
|
||||
public var turnHolder :Name;
|
||||
|
||||
/** The service interface for requesting special things from the server. */
|
||||
public var ezGameService :EZGameMarshaller;
|
||||
|
||||
/**
|
||||
* Access the underlying user properties.
|
||||
*/
|
||||
public function getUserProps () :Object
|
||||
{
|
||||
return _props;
|
||||
}
|
||||
|
||||
// from TurnGameObject
|
||||
public function getTurnHolderFieldName () :String
|
||||
{
|
||||
return TURN_HOLDER;
|
||||
}
|
||||
|
||||
// from TurnGameObject
|
||||
public function getTurnHolder () :Name
|
||||
{
|
||||
return turnHolder;
|
||||
}
|
||||
|
||||
// from TurnGameObject
|
||||
public function getPlayers () :TypedArray /* of Name */
|
||||
{
|
||||
return players;
|
||||
}
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Requests that the <code>turnHolder</code> 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
|
||||
{
|
||||
var oldValue :Object = _props[propName];
|
||||
if (index >= 0) {
|
||||
// set an array element
|
||||
var arr :Array = (oldValue as Array);
|
||||
if (arr == null) {
|
||||
arr = [];
|
||||
_props[propName] = arr;
|
||||
}
|
||||
oldValue = arr[index];
|
||||
arr[index] = value;
|
||||
|
||||
} else if (value != null) {
|
||||
// normal property set
|
||||
_props[propName] = value;
|
||||
|
||||
} else {
|
||||
// remove a property
|
||||
delete _props[propName];
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
override public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
super.writeObject(out);
|
||||
|
||||
throw new Error("Un-needed");
|
||||
}
|
||||
|
||||
override public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
|
||||
// first read any regular bits
|
||||
turnHolder = (ins.readObject() as Name);
|
||||
ezGameService = (ins.readObject() as EZGameMarshaller);
|
||||
|
||||
// then user properties
|
||||
var count :int = ins.readInt();
|
||||
while (count-- > 0) {
|
||||
var key :String = ins.readUTF();
|
||||
var value :Object = EZObjectMarshaller.decode(ins.readObject());
|
||||
_props[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/** The raw properties set by the game. */
|
||||
protected var _props :Object = new Object();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.data {
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.IExternalizable;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamer;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.NamedEvent;
|
||||
|
||||
import com.threerings.ezgame.util.EZObjectMarshaller;
|
||||
|
||||
/**
|
||||
* Represents a property change on the actionscript object we
|
||||
* use in FlashGameObject.
|
||||
*/
|
||||
public class PropertySetEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Create a PropertySetEvent.
|
||||
*/
|
||||
public function PropertySetEvent () // unserialize-only
|
||||
{
|
||||
super(0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value that was set for the property.
|
||||
*/
|
||||
public function getValue () :Object
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old value.
|
||||
*/
|
||||
public function getOldValue () :Object
|
||||
{
|
||||
return _oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index, or -1 if not applicable.
|
||||
*/
|
||||
public function getIndex () :int
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
|
||||
override public function applyToObject (target :DObject) :Boolean
|
||||
{
|
||||
_oldValue =
|
||||
EZGameObject(target).applyPropertySet(_name, _data, _index);
|
||||
return true;
|
||||
}
|
||||
|
||||
override protected function notifyListener (listener :Object) :void
|
||||
{
|
||||
if (listener is PropertySetListener) {
|
||||
(listener as PropertySetListener).propertyWasSet(this);
|
||||
}
|
||||
}
|
||||
|
||||
override public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
|
||||
_index = ins.readInt();
|
||||
_data = EZObjectMarshaller.decode(ins.readObject());
|
||||
}
|
||||
|
||||
/** The index of the property, if applicable. */
|
||||
protected var _index :int;
|
||||
|
||||
/** The client-side data that is assigned to this property. */
|
||||
protected var _data :Object;
|
||||
|
||||
/** The old value. */
|
||||
protected var _oldValue :Object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.threerings.ezgame.data {
|
||||
|
||||
import com.threerings.presents.dobj.ChangeListener;
|
||||
|
||||
/**
|
||||
* A listener that will be notified of PropertySet events.
|
||||
*/
|
||||
public interface PropertySetListener extends ChangeListener
|
||||
{
|
||||
/**
|
||||
* Called when a property was set on a flash game object.
|
||||
* This will be called <em>after</em> the event has been applied
|
||||
* to the object.
|
||||
*/
|
||||
function propertyWasSet (event :PropertySetEvent) :void;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.threerings.ezgame.util {
|
||||
|
||||
import flash.net.ObjectEncoding;
|
||||
|
||||
import flash.system.ApplicationDomain;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
import com.threerings.io.TypedArray;
|
||||
|
||||
/**
|
||||
* Utility methods for transferring flash properties via
|
||||
* the presents dobj system.
|
||||
*/
|
||||
public class EZObjectMarshaller
|
||||
{
|
||||
/**
|
||||
* Encode the specified object as either a byte[] or a byte[][] if it
|
||||
* is an array. The specific mechanism of encoding is not important,
|
||||
* as long as decode returns a clone of the original object.
|
||||
*
|
||||
* Currently, cycles in the object graph are preserved on the other end.
|
||||
* TODO: serialize IExternalizable implementations, and take
|
||||
* into account the ApplicationDomain.
|
||||
*/
|
||||
public static function encode (
|
||||
obj :Object, encodeArrayElements :Boolean = true) :Object
|
||||
{
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
if (encodeArrayElements && obj is Array) {
|
||||
var src :Array = (obj as Array);
|
||||
var dest :TypedArray = TypedArray.create(ByteArray);
|
||||
for each (var o :Object in src) {
|
||||
dest.push(encode(o, false));
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// TODO: Our own encoding, that takes into account
|
||||
// the ApplicationDomain
|
||||
var bytes :ByteArray = new ByteArray();
|
||||
bytes.endian = Endian.BIG_ENDIAN;
|
||||
bytes.objectEncoding = ObjectEncoding.AMF3;
|
||||
bytes.writeObject(obj);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static function decode (encoded :Object) :Object
|
||||
{
|
||||
if (encoded == null) {
|
||||
return null;
|
||||
}
|
||||
if (encoded is TypedArray) {
|
||||
var src :TypedArray = (encoded as TypedArray);
|
||||
var dest :Array = [];
|
||||
for each (var b :ByteArray in src) {
|
||||
dest.push(decode(b));
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
var bytes :ByteArray = (encoded as ByteArray);
|
||||
|
||||
// TODO: Our own decoding, that takes into account
|
||||
// the ApplicationDomain
|
||||
bytes.endian = Endian.BIG_ENDIAN;
|
||||
bytes.objectEncoding = ObjectEncoding.AMF3;
|
||||
return bytes.readObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.client;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
/**
|
||||
* Provides services for ez games.
|
||||
*/
|
||||
public interface EZGameService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Request to set the specified property.
|
||||
*/
|
||||
public void setProperty (
|
||||
Client client, String propName, Object value, int index,
|
||||
InvocationListener listener);
|
||||
|
||||
/**
|
||||
* Request to end the turn, possibly futzing the next turn holder unless
|
||||
* -1 is specified for the nextPlayerIndex.
|
||||
*/
|
||||
public void endTurn (
|
||||
Client client, int nextPlayerIndex, InvocationListener listener);
|
||||
|
||||
/**
|
||||
* Request to end the game, with the specified player indices assigned
|
||||
* as winners.
|
||||
*/
|
||||
public void endGame (
|
||||
Client client, int[] winners, InvocationListener listener);
|
||||
|
||||
/**
|
||||
* Request to send a private message to one other player in
|
||||
* the game.
|
||||
*/
|
||||
public void sendMessage (
|
||||
Client client, String msgName, Object value, int playerIdx,
|
||||
InvocationListener listener);
|
||||
|
||||
/**
|
||||
* Add to the specified named collection.
|
||||
*
|
||||
* @param clearExisting if true, wipe the old contents.
|
||||
*/
|
||||
public void addToCollection (
|
||||
Client client, String collName, byte[][] data, boolean clearExisting,
|
||||
InvocationListener listener);
|
||||
|
||||
/**
|
||||
* Merge the specified collection into the other.
|
||||
*/
|
||||
public void mergeCollection (
|
||||
Client client, String srcColl, String intoColl,
|
||||
InvocationListener listener);
|
||||
|
||||
/**
|
||||
* Pick or deal some number of elements from the specified collection,
|
||||
* and either set a property in the flash object, or delivery the
|
||||
* picks to the specified player index via a game message.
|
||||
*/
|
||||
public void getFromCollection (
|
||||
Client client, String collName, boolean consume, int count,
|
||||
String msgOrPropName, int playerIndex, ConfirmListener listener);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.data;
|
||||
|
||||
import com.threerings.util.MessageBundle;
|
||||
|
||||
import com.threerings.parlor.game.client.GameConfigurator;
|
||||
import com.threerings.parlor.game.data.GameConfig;
|
||||
import com.threerings.parlor.game.data.PartyGameConfig;
|
||||
|
||||
/**
|
||||
* A game config for a simple multiplayer game.
|
||||
*/
|
||||
public class EZGameConfig extends GameConfig
|
||||
implements PartyGameConfig
|
||||
{
|
||||
/** A creator-submitted name of the game. */
|
||||
public String gameName;
|
||||
|
||||
// from abstract GameConfig
|
||||
public String getBundleName ()
|
||||
{
|
||||
return "general";
|
||||
}
|
||||
|
||||
// from abstract GameConfig
|
||||
public GameConfigurator createConfigurator ()
|
||||
{
|
||||
return null; // nothing here on the java side
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGameName ()
|
||||
{
|
||||
return MessageBundle.taint(gameName);
|
||||
}
|
||||
|
||||
// from abstract PlaceConfig
|
||||
public String getManagerClassName ()
|
||||
{
|
||||
return "com.threerings.ezgame.server.EZGameManager";
|
||||
}
|
||||
|
||||
// from PartyGameConfig
|
||||
public byte getPartyGameType ()
|
||||
{
|
||||
// TODO
|
||||
return NOT_PARTY_GAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EZGameConfig that = (EZGameConfig) other;
|
||||
return this.gameName.equals(that.gameName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
return super.hashCode(); // TODO: incorp game name?
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.data;
|
||||
|
||||
import com.threerings.ezgame.client.EZGameService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link EZGameService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
public class EZGameMarshaller extends InvocationMarshaller
|
||||
implements EZGameService
|
||||
{
|
||||
/** The method id used to dispatch {@link #addToCollection} requests. */
|
||||
public static final int ADD_TO_COLLECTION = 1;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void addToCollection (Client arg1, String arg2, byte[][] arg3, boolean arg4, InvocationService.InvocationListener arg5)
|
||||
{
|
||||
ListenerMarshaller listener5 = new ListenerMarshaller();
|
||||
listener5.listener = arg5;
|
||||
sendRequest(arg1, ADD_TO_COLLECTION, new Object[] {
|
||||
arg2, arg3, Boolean.valueOf(arg4), listener5
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #endGame} requests. */
|
||||
public static final int END_GAME = 2;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void endGame (Client arg1, int[] arg2, InvocationService.InvocationListener arg3)
|
||||
{
|
||||
ListenerMarshaller listener3 = new ListenerMarshaller();
|
||||
listener3.listener = arg3;
|
||||
sendRequest(arg1, END_GAME, new Object[] {
|
||||
arg2, listener3
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #endTurn} requests. */
|
||||
public static final int END_TURN = 3;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void endTurn (Client arg1, int arg2, InvocationService.InvocationListener arg3)
|
||||
{
|
||||
ListenerMarshaller listener3 = new ListenerMarshaller();
|
||||
listener3.listener = arg3;
|
||||
sendRequest(arg1, END_TURN, new Object[] {
|
||||
Integer.valueOf(arg2), listener3
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #getFromCollection} requests. */
|
||||
public static final int GET_FROM_COLLECTION = 4;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void getFromCollection (Client arg1, String arg2, boolean arg3, int arg4, String arg5, int arg6, InvocationService.ConfirmListener arg7)
|
||||
{
|
||||
InvocationMarshaller.ConfirmMarshaller listener7 = new InvocationMarshaller.ConfirmMarshaller();
|
||||
listener7.listener = arg7;
|
||||
sendRequest(arg1, GET_FROM_COLLECTION, new Object[] {
|
||||
arg2, Boolean.valueOf(arg3), Integer.valueOf(arg4), arg5, Integer.valueOf(arg6), listener7
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #mergeCollection} requests. */
|
||||
public static final int MERGE_COLLECTION = 5;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void mergeCollection (Client arg1, String arg2, String arg3, InvocationService.InvocationListener arg4)
|
||||
{
|
||||
ListenerMarshaller listener4 = new ListenerMarshaller();
|
||||
listener4.listener = arg4;
|
||||
sendRequest(arg1, MERGE_COLLECTION, new Object[] {
|
||||
arg2, arg3, listener4
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #sendMessage} requests. */
|
||||
public static final int SEND_MESSAGE = 6;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void sendMessage (Client arg1, String arg2, Object arg3, int arg4, InvocationService.InvocationListener arg5)
|
||||
{
|
||||
ListenerMarshaller listener5 = new ListenerMarshaller();
|
||||
listener5.listener = arg5;
|
||||
sendRequest(arg1, SEND_MESSAGE, new Object[] {
|
||||
arg2, arg3, Integer.valueOf(arg4), listener5
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #setProperty} requests. */
|
||||
public static final int SET_PROPERTY = 7;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void setProperty (Client arg1, String arg2, Object arg3, int arg4, InvocationService.InvocationListener arg5)
|
||||
{
|
||||
ListenerMarshaller listener5 = new ListenerMarshaller();
|
||||
listener5.listener = arg5;
|
||||
sendRequest(arg1, SET_PROPERTY, new Object[] {
|
||||
arg2, arg3, Integer.valueOf(arg4), listener5
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.data;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
import com.threerings.parlor.game.data.GameObject;
|
||||
import com.threerings.parlor.turn.data.TurnGameObject;
|
||||
|
||||
/**
|
||||
* Contains the data for an ez game.
|
||||
*/
|
||||
public class EZGameObject extends GameObject
|
||||
implements TurnGameObject
|
||||
{
|
||||
/** The identifier for a MessageEvent containing a user message. */
|
||||
public static final String USER_MESSAGE = "Umsg";
|
||||
|
||||
/** The identifier for a MessageEvent containing game-system chat. */
|
||||
public static final String GAME_CHAT = "Uchat";
|
||||
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The field name of the <code>turnHolder</code> field. */
|
||||
public static final String TURN_HOLDER = "turnHolder";
|
||||
|
||||
/** The field name of the <code>ezGameService</code> field. */
|
||||
public static final String EZ_GAME_SERVICE = "ezGameService";
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** The current turn holder. */
|
||||
public Name turnHolder;
|
||||
|
||||
/** The service interface for requesting special things from the server. */
|
||||
public EZGameMarshaller ezGameService;
|
||||
|
||||
// from TurnGameObject
|
||||
public String getTurnHolderFieldName ()
|
||||
{
|
||||
return TURN_HOLDER;
|
||||
}
|
||||
|
||||
// from TurnGameObject
|
||||
public Name getTurnHolder ()
|
||||
{
|
||||
return turnHolder;
|
||||
}
|
||||
|
||||
// from TurnGameObject
|
||||
public Name[] getPlayers ()
|
||||
{
|
||||
return players;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by PropertySetEvent to effect the property update.
|
||||
*/
|
||||
protected void applyPropertySet (String propName, Object data, int index)
|
||||
{
|
||||
if (index >= 0) {
|
||||
Object something = _props.get(propName);
|
||||
byte[][] arr = (something instanceof byte[][])
|
||||
? (byte[][]) something : null;
|
||||
if (arr == null || arr.length <= index) {
|
||||
// TODO: in case a user sets element 0 and element 90000,
|
||||
// we might want to store elements in a hash
|
||||
byte[][] newArr = new byte[index + 1][];
|
||||
if (arr != null) {
|
||||
System.arraycopy(arr, 0, newArr, 0, arr.length);
|
||||
}
|
||||
_props.put(propName, newArr);
|
||||
arr = newArr;
|
||||
}
|
||||
arr[index] = (byte[]) data;
|
||||
|
||||
} else if (data != null) {
|
||||
_props.put(propName, data);
|
||||
|
||||
} else {
|
||||
_props.remove(propName);
|
||||
}
|
||||
}
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Requests that the <code>turnHolder</code> 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 setTurnHolder (Name value)
|
||||
{
|
||||
Name ovalue = this.turnHolder;
|
||||
requestAttributeChange(
|
||||
TURN_HOLDER, value, ovalue);
|
||||
this.turnHolder = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>ezGameService</code> 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 setEZGameService (EZGameMarshaller value)
|
||||
{
|
||||
EZGameMarshaller ovalue = this.ezGameService;
|
||||
requestAttributeChange(
|
||||
EZ_GAME_SERVICE, value, ovalue);
|
||||
this.ezGameService = value;
|
||||
}
|
||||
// AUTO-GENERATED: METHODS END
|
||||
|
||||
/**
|
||||
* A custom serialization method.
|
||||
*/
|
||||
public void writeObject (ObjectOutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
out.defaultWriteObject();
|
||||
|
||||
// write the number of properties, followed by each one
|
||||
out.writeInt(_props.size());
|
||||
for (Map.Entry<String, Object> entry : _props.entrySet()) {
|
||||
out.writeUTF(entry.getKey());
|
||||
out.writeObject(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom serialization method.
|
||||
*/
|
||||
public void readObject (ObjectInputStream ins)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
ins.defaultReadObject();
|
||||
|
||||
_props.clear();
|
||||
int count = ins.readInt();
|
||||
while (count-- > 0) {
|
||||
String key = ins.readUTF();
|
||||
_props.put(key, ins.readObject());
|
||||
}
|
||||
}
|
||||
|
||||
/** The current state of game data, opaque to us here on the server. */
|
||||
protected transient HashMap<String, Object> _props =
|
||||
new HashMap<String, Object>();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.data;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
import com.threerings.io.Streamer;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.NamedEvent;
|
||||
|
||||
/**
|
||||
* Represents a property change on the actionscript object we
|
||||
* use in EZGameObject.
|
||||
*/
|
||||
public class PropertySetEvent extends NamedEvent
|
||||
{
|
||||
/** Suitable for unserialization. */
|
||||
public PropertySetEvent ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PropertySetEvent.
|
||||
*/
|
||||
public PropertySetEvent (
|
||||
int targetOid, String propName, Object value, int index)
|
||||
{
|
||||
super(targetOid, propName);
|
||||
_data = value;
|
||||
_index = index;
|
||||
}
|
||||
|
||||
// from abstract DEvent
|
||||
public boolean applyToObject (DObject target)
|
||||
{
|
||||
((EZGameObject) target).applyPropertySet(_name, _data, _index);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** The index. */
|
||||
protected int _index;
|
||||
|
||||
/** The client-side data that is assigned to this property. */
|
||||
protected Object _data;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.server;
|
||||
|
||||
import com.threerings.ezgame.client.EZGameService;
|
||||
import com.threerings.ezgame.data.EZGameMarshaller;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
/**
|
||||
* Dispatches requests to the {@link EZGameProvider}.
|
||||
*/
|
||||
public class EZGameDispatcher extends InvocationDispatcher
|
||||
{
|
||||
/**
|
||||
* Creates a dispatcher that may be registered to dispatch invocation
|
||||
* service requests for the specified provider.
|
||||
*/
|
||||
public EZGameDispatcher (EZGameProvider provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public InvocationMarshaller createMarshaller ()
|
||||
{
|
||||
return new EZGameMarshaller();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void dispatchRequest (
|
||||
ClientObject source, int methodId, Object[] args)
|
||||
throws InvocationException
|
||||
{
|
||||
switch (methodId) {
|
||||
case EZGameMarshaller.ADD_TO_COLLECTION:
|
||||
((EZGameProvider)provider).addToCollection(
|
||||
source,
|
||||
(String)args[0], (byte[][])args[1], ((Boolean)args[2]).booleanValue(), (InvocationService.InvocationListener)args[3]
|
||||
);
|
||||
return;
|
||||
|
||||
case EZGameMarshaller.END_GAME:
|
||||
((EZGameProvider)provider).endGame(
|
||||
source,
|
||||
(int[])args[0], (InvocationService.InvocationListener)args[1]
|
||||
);
|
||||
return;
|
||||
|
||||
case EZGameMarshaller.END_TURN:
|
||||
((EZGameProvider)provider).endTurn(
|
||||
source,
|
||||
((Integer)args[0]).intValue(), (InvocationService.InvocationListener)args[1]
|
||||
);
|
||||
return;
|
||||
|
||||
case EZGameMarshaller.GET_FROM_COLLECTION:
|
||||
((EZGameProvider)provider).getFromCollection(
|
||||
source,
|
||||
(String)args[0], ((Boolean)args[1]).booleanValue(), ((Integer)args[2]).intValue(), (String)args[3], ((Integer)args[4]).intValue(), (InvocationService.ConfirmListener)args[5]
|
||||
);
|
||||
return;
|
||||
|
||||
case EZGameMarshaller.MERGE_COLLECTION:
|
||||
((EZGameProvider)provider).mergeCollection(
|
||||
source,
|
||||
(String)args[0], (String)args[1], (InvocationService.InvocationListener)args[2]
|
||||
);
|
||||
return;
|
||||
|
||||
case EZGameMarshaller.SEND_MESSAGE:
|
||||
((EZGameProvider)provider).sendMessage(
|
||||
source,
|
||||
(String)args[0], (Object)args[1], ((Integer)args[2]).intValue(), (InvocationService.InvocationListener)args[3]
|
||||
);
|
||||
return;
|
||||
|
||||
case EZGameMarshaller.SET_PROPERTY:
|
||||
((EZGameProvider)provider).setProperty(
|
||||
source,
|
||||
(String)args[0], (Object)args[1], ((Integer)args[2]).intValue(), (InvocationService.InvocationListener)args[3]
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchRequest(source, methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.CollectionUtil;
|
||||
import com.samskivert.util.RandomUtil;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
import com.threerings.presents.dobj.AccessController;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import com.threerings.crowd.server.CrowdServer;
|
||||
|
||||
import com.threerings.parlor.game.server.GameManager;
|
||||
|
||||
import com.threerings.parlor.turn.server.TurnGameManager;
|
||||
|
||||
import com.threerings.ezgame.data.EZGameObject;
|
||||
import com.threerings.ezgame.data.EZGameMarshaller;
|
||||
import com.threerings.ezgame.data.PropertySetEvent;
|
||||
|
||||
/**
|
||||
* A manager for "ez" games.
|
||||
*/
|
||||
public class EZGameManager extends GameManager
|
||||
implements EZGameProvider, TurnGameManager
|
||||
{
|
||||
public EZGameManager ()
|
||||
{
|
||||
addDelegate(_turnDelegate = new EZGameTurnDelegate(this));
|
||||
}
|
||||
|
||||
// from TurnGameManager
|
||||
public void turnWillStart ()
|
||||
{
|
||||
}
|
||||
|
||||
// from TurnGameManager
|
||||
public void turnDidStart ()
|
||||
{
|
||||
}
|
||||
|
||||
// from TurnGameManager
|
||||
public void turnDidEnd ()
|
||||
{
|
||||
}
|
||||
|
||||
// from EZGameProvider
|
||||
public void endTurn (
|
||||
ClientObject caller, int nextPlayerIndex,
|
||||
InvocationService.InvocationListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
validateStateModification(caller);
|
||||
_turnDelegate.endTurn(nextPlayerIndex);
|
||||
}
|
||||
|
||||
// from EZGameProvider
|
||||
public void endGame (
|
||||
ClientObject caller, int[] winners,
|
||||
InvocationService.InvocationListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
validateStateModification(caller);
|
||||
|
||||
_winnerIndexes = winners;
|
||||
endGame();
|
||||
}
|
||||
|
||||
// from EZGameProvider
|
||||
public void sendMessage (
|
||||
ClientObject caller, String msg, Object data, int playerIdx,
|
||||
InvocationService.InvocationListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
validateUser(caller);
|
||||
|
||||
if (playerIdx < 0 || playerIdx >= _gameObj.players.length) {
|
||||
_gameObj.postMessage(EZGameObject.USER_MESSAGE,
|
||||
new Object[] { msg, data });
|
||||
|
||||
} else {
|
||||
sendPrivateMessage(playerIdx, msg, data);
|
||||
}
|
||||
}
|
||||
|
||||
// from EZGameProvider
|
||||
public void setProperty (
|
||||
ClientObject caller, String propName, Object data, int index,
|
||||
InvocationService.InvocationListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
validateUser(caller);
|
||||
setProperty(propName, data, index);
|
||||
}
|
||||
|
||||
// from EZGameProvider
|
||||
public void addToCollection (
|
||||
ClientObject caller, String collName, byte[][] data,
|
||||
boolean clearExisting, InvocationService.InvocationListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
validateUser(caller);
|
||||
if (_collections == null) {
|
||||
_collections = new HashMap<String, ArrayList<byte[]>>();
|
||||
}
|
||||
|
||||
// figure out if we're adding to an existing collection
|
||||
// or creating a new one
|
||||
ArrayList<byte[]> list = null;
|
||||
if (!clearExisting) {
|
||||
list = _collections.get(collName);
|
||||
}
|
||||
if (list == null) {
|
||||
list = new ArrayList<byte[]>();
|
||||
_collections.put(collName, list);
|
||||
}
|
||||
|
||||
CollectionUtil.addAll(list, data);
|
||||
}
|
||||
|
||||
// from EZGameProvider
|
||||
public void getFromCollection (
|
||||
ClientObject caller, String collName, boolean consume, int count,
|
||||
String msgOrPropName, int playerIndex,
|
||||
InvocationService.ConfirmListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
validateUser(caller);
|
||||
|
||||
int srcSize = 0;
|
||||
if (_collections != null) {
|
||||
ArrayList<byte[]> src = _collections.get(collName);
|
||||
srcSize = (src == null) ? 0 : src.size();
|
||||
if (srcSize >= count) {
|
||||
byte[][] result = new byte[count][];
|
||||
for (int ii=0; ii < count; ii++) {
|
||||
int pick = RandomUtil.getInt(srcSize);
|
||||
if (consume) {
|
||||
result[ii] = src.remove(pick);
|
||||
srcSize--;
|
||||
|
||||
} else {
|
||||
result[ii] = src.get(pick);
|
||||
}
|
||||
}
|
||||
|
||||
if (playerIndex >= 0 && playerIndex < _gameObj.players.length) {
|
||||
sendPrivateMessage(playerIndex, msgOrPropName, result);
|
||||
|
||||
} else {
|
||||
setProperty(msgOrPropName, result, -1);
|
||||
}
|
||||
// SUCCESS!
|
||||
listener.requestProcessed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: decide what we want to return here
|
||||
throw new InvocationException(String.valueOf(srcSize));
|
||||
}
|
||||
|
||||
// from EZGameProvider
|
||||
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
|
||||
if (_collections != null) {
|
||||
ArrayList<byte[]> src = _collections.remove(srcColl);
|
||||
if (src != null) {
|
||||
ArrayList<byte[]> dest = _collections.get(intoColl);
|
||||
if (dest == null) {
|
||||
_collections.put(intoColl, src);
|
||||
} else {
|
||||
dest.addAll(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to send a private message to the specified player
|
||||
* index (must already be verified).
|
||||
*/
|
||||
protected void sendPrivateMessage (
|
||||
int playerIdx, String msg, Object data)
|
||||
throws InvocationException
|
||||
{
|
||||
BodyObject target = getPlayer(playerIdx);
|
||||
if (target == null) {
|
||||
// TODO: this code has no corresponding translation
|
||||
throw new InvocationException("m.player_not_around");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_gameObj.postEvent(
|
||||
new PropertySetEvent(_gameObj.getOid(), propName, value, index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the specified user has access to do things in the game.
|
||||
*/
|
||||
protected void validateUser (ClientObject caller)
|
||||
throws InvocationException
|
||||
{
|
||||
if (getPresentPlayerIndex(caller.getOid()) == -1) {
|
||||
throw new InvocationException(InvocationCodes.ACCESS_DENIED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the specified listener has access to make a
|
||||
* change.
|
||||
*/
|
||||
protected void validateStateModification (ClientObject caller)
|
||||
throws InvocationException
|
||||
{
|
||||
validateUser(caller);
|
||||
|
||||
Name holder = _gameObj.turnHolder;
|
||||
if (holder != null &&
|
||||
!holder.equals(((BodyObject) caller).getVisibleName())) {
|
||||
throw new InvocationException(InvocationCodes.ACCESS_DENIED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends PlaceObject> getPlaceObjectClass ()
|
||||
{
|
||||
return EZGameObject.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void didStartup ()
|
||||
{
|
||||
super.didStartup();
|
||||
|
||||
_gameObj = (EZGameObject) _plobj;
|
||||
|
||||
_gameObj.setEZGameService(
|
||||
(EZGameMarshaller) CrowdServer.invmgr.registerDispatcher(
|
||||
new EZGameDispatcher(this), false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void didShutdown ()
|
||||
{
|
||||
CrowdServer.invmgr.clearDispatcher(_gameObj.ezGameService);
|
||||
|
||||
super.didShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assignWinners (boolean[] winners)
|
||||
{
|
||||
if (_winnerIndexes != null) {
|
||||
for (int index : _winnerIndexes) {
|
||||
if (index >= 0 && index < winners.length) {
|
||||
winners[index] = true;
|
||||
}
|
||||
}
|
||||
_winnerIndexes = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** A nice casted reference to the game object. */
|
||||
protected EZGameObject _gameObj;
|
||||
|
||||
/** Our turn delegate. */
|
||||
protected EZGameTurnDelegate _turnDelegate;
|
||||
|
||||
/** The array of winners, after the user has filled it in. */
|
||||
protected int[] _winnerIndexes;
|
||||
|
||||
/** The map of collections, lazy-initialized. */
|
||||
protected HashMap<String, ArrayList<byte[]>> _collections;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.server;
|
||||
|
||||
import com.threerings.ezgame.client.EZGameService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.InvocationProvider;
|
||||
|
||||
/**
|
||||
* Defines the server-side of the {@link EZGameService}.
|
||||
*/
|
||||
public interface EZGameProvider extends InvocationProvider
|
||||
{
|
||||
/**
|
||||
* Handles a {@link EZGameService#addToCollection} request.
|
||||
*/
|
||||
public void addToCollection (ClientObject caller, String arg1, byte[][] arg2, boolean arg3, InvocationService.InvocationListener arg4)
|
||||
throws InvocationException;
|
||||
|
||||
/**
|
||||
* Handles a {@link EZGameService#endGame} request.
|
||||
*/
|
||||
public void endGame (ClientObject caller, int[] arg1, InvocationService.InvocationListener arg2)
|
||||
throws InvocationException;
|
||||
|
||||
/**
|
||||
* Handles a {@link EZGameService#endTurn} request.
|
||||
*/
|
||||
public void endTurn (ClientObject caller, int arg1, InvocationService.InvocationListener arg2)
|
||||
throws InvocationException;
|
||||
|
||||
/**
|
||||
* Handles a {@link EZGameService#getFromCollection} request.
|
||||
*/
|
||||
public void getFromCollection (ClientObject caller, String arg1, boolean arg2, int arg3, String arg4, int arg5, InvocationService.ConfirmListener arg6)
|
||||
throws InvocationException;
|
||||
|
||||
/**
|
||||
* Handles a {@link EZGameService#mergeCollection} request.
|
||||
*/
|
||||
public void mergeCollection (ClientObject caller, String arg1, String arg2, InvocationService.InvocationListener arg3)
|
||||
throws InvocationException;
|
||||
|
||||
/**
|
||||
* Handles a {@link EZGameService#sendMessage} request.
|
||||
*/
|
||||
public void sendMessage (ClientObject caller, String arg1, Object arg2, int arg3, InvocationService.InvocationListener arg4)
|
||||
throws InvocationException;
|
||||
|
||||
/**
|
||||
* Handles a {@link EZGameService#setProperty} request.
|
||||
*/
|
||||
public void setProperty (ClientObject caller, String arg1, Object arg2, int arg3, InvocationService.InvocationListener arg4)
|
||||
throws InvocationException;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.ezgame.server;
|
||||
|
||||
import com.threerings.parlor.turn.server.TurnGameManagerDelegate;
|
||||
|
||||
/**
|
||||
* A special turn delegate for ez games.
|
||||
*/
|
||||
public class EZGameTurnDelegate extends TurnGameManagerDelegate
|
||||
{
|
||||
public EZGameTurnDelegate (EZGameManager mgr)
|
||||
{
|
||||
super(mgr);
|
||||
}
|
||||
|
||||
/**
|
||||
* A form of endTurn where you can specify the next turn holder index.
|
||||
*/
|
||||
public void endTurn (int playerIndex)
|
||||
{
|
||||
_nextPlayerIndex = playerIndex;
|
||||
endTurn();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setNextTurnHolder ()
|
||||
{
|
||||
// if the user-supplied value seems to make sense, use it!
|
||||
if ((_nextPlayerIndex >= 0) &&
|
||||
(_nextPlayerIndex < _turnGame.getPlayers().length)) {
|
||||
_turnIdx = _nextPlayerIndex;
|
||||
|
||||
} else {
|
||||
// otherwise, do the default behavior
|
||||
super.setNextTurnHolder();
|
||||
}
|
||||
|
||||
// always clear out the override
|
||||
_nextPlayerIndex = -1;
|
||||
}
|
||||
|
||||
/** An override next turn holder, or -1. */
|
||||
protected int _nextPlayerIndex = -1;
|
||||
}
|
||||
Reference in New Issue
Block a user