Ez game stuff, javafied.
git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@63 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 CardDeck (EZGame gameObj)
|
||||||
|
{
|
||||||
|
this(gameObj, "deck");
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardDeck (EZGame gameObj, String deckName)
|
||||||
|
{
|
||||||
|
_gameObj = gameObj;
|
||||||
|
_deckName = deckName;
|
||||||
|
|
||||||
|
ArrayList<String> deck = new ArrayList<String>();
|
||||||
|
for (String rank : new String[] { "2", "3", "4", "5", "6", "7", "8",
|
||||||
|
"9", "T", "J", "Q", "K", "A" }) {
|
||||||
|
for (String suit : new String[] { "c", "d", "h", "s" }) {
|
||||||
|
deck.add(rank + suit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_gameObj.setCollection(_deckName, deck);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dealToPlayer (int playerIdx, int count, String msgName)
|
||||||
|
{
|
||||||
|
// TODO: support the callback
|
||||||
|
_gameObj.dealFromCollection(_deckName, count, msgName, null, playerIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dealToData (int count, String propName)
|
||||||
|
{
|
||||||
|
_gameObj.dealFromCollection(_deckName, count, propName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The game object. */
|
||||||
|
protected EZGame _gameObj;
|
||||||
|
|
||||||
|
/** The name of our deck. */
|
||||||
|
protected String _deckName;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
public interface DealListener
|
||||||
|
{
|
||||||
|
// TODO: figure out what I want to return here
|
||||||
|
public void dealt (int someValue);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
public class EZEvent
|
||||||
|
{
|
||||||
|
public EZEvent (EZGame ezgame)
|
||||||
|
{
|
||||||
|
_ezgame = ezgame;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the game object to which this event applies.
|
||||||
|
*/
|
||||||
|
public EZGame getGameObject ()
|
||||||
|
{
|
||||||
|
return _ezgame;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The game object for this event. */
|
||||||
|
protected EZGame _ezgame;
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The game object that you'll be using to manage your game.
|
||||||
|
*/
|
||||||
|
public interface EZGame
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get a property from data.
|
||||||
|
*/
|
||||||
|
public Object get (String propName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a property from data.
|
||||||
|
*/
|
||||||
|
public Object get (String propName, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a property that will be distributed.
|
||||||
|
*/
|
||||||
|
public void set (String propName, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a property that will be distributed.
|
||||||
|
*/
|
||||||
|
public void set (String propName, Object value, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public void registerListener (Object obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister the specified object from receiving events.
|
||||||
|
*/
|
||||||
|
public void unregisterListener (Object obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the specified collection to contain the specified values,
|
||||||
|
* clearing any previous values.
|
||||||
|
*
|
||||||
|
* @param values may be a Collection or array. Note that arrays
|
||||||
|
* of primitive types will be transmogrified into an Object[] containing
|
||||||
|
* wrapper objects.
|
||||||
|
*/
|
||||||
|
public void setCollection (String collName, Object values);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add to an existing collection. If it doesn't exist, it will
|
||||||
|
* be created. The new values will be inserted randomly into the
|
||||||
|
* collection.
|
||||||
|
*
|
||||||
|
* @param values may be a Collection or array. Note that arrays
|
||||||
|
* of primitive types will be transmogrified into an Object[] containing
|
||||||
|
* wrapper objects.
|
||||||
|
*/
|
||||||
|
public void addToCollection (String collName, Object values);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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?
|
||||||
|
public void pickFromCollection (
|
||||||
|
String collName, int count, String propName);
|
||||||
|
|
||||||
|
public void pickFromCollection (
|
||||||
|
String collName, int count, String msgName, int playerIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
public void dealFromCollection (
|
||||||
|
String collName, int count, String propName,
|
||||||
|
DealListener listener);
|
||||||
|
|
||||||
|
public void dealFromCollection (
|
||||||
|
String collName, int count, String msgName,
|
||||||
|
DealListener listener, int playerIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public void mergeCollection (String srcColl, String intoColl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
public void sendMessage (
|
||||||
|
String messageName, Object value);
|
||||||
|
|
||||||
|
public void sendMessage (
|
||||||
|
String messageName, Object value, int playerIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message that will be heard by everyone in the game room,
|
||||||
|
* even observers.
|
||||||
|
*/
|
||||||
|
public void sendChat (String msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified message immediately locally: not sent
|
||||||
|
* to any other players or observers in the game room.
|
||||||
|
*/
|
||||||
|
public void localChat (String msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the player names, as an array.
|
||||||
|
*/
|
||||||
|
public String[] getPlayerNames ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index into the player names array of the current player,
|
||||||
|
* or -1 if the user is not a player.
|
||||||
|
*/
|
||||||
|
public int getMyIndex ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the turn holder's index, or -1 if it's nobody's turn.
|
||||||
|
*/
|
||||||
|
public int getTurnHolderIndex ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the indexes of the winners
|
||||||
|
*/
|
||||||
|
public int[] getWinnerIndexes ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method to just check if it's our turn.
|
||||||
|
*/
|
||||||
|
public boolean isMyTurn ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the game currently in play?
|
||||||
|
*/
|
||||||
|
public boolean isInPlay ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End the current turn. If no next player index is specified,
|
||||||
|
* then the next player after the current one is used.
|
||||||
|
*/
|
||||||
|
public void endTurn ();
|
||||||
|
|
||||||
|
public void endTurn (int nextPlayerIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End the game. The specified player indexes are winners!
|
||||||
|
*/
|
||||||
|
public void endGame (int... winners);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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.
|
||||||
|
*/
|
||||||
|
public void setGameObject (EZGame gameObj);
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
public class MessageReceivedEvent extends EZEvent
|
||||||
|
{
|
||||||
|
public MessageReceivedEvent (
|
||||||
|
EZGame ezgame, String messageName, Object value)
|
||||||
|
{
|
||||||
|
super(ezgame);
|
||||||
|
_name = messageName;
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the message name.
|
||||||
|
*/
|
||||||
|
public String getName ()
|
||||||
|
{
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the message value.
|
||||||
|
*/
|
||||||
|
public Object getValue ()
|
||||||
|
{
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString ()
|
||||||
|
{
|
||||||
|
return "[MessageReceivedEvent name=" + _name +
|
||||||
|
", value=" + _value + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String _name;
|
||||||
|
protected Object _value;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this interface to automagically be registered to received
|
||||||
|
* MessageReceivedEvents.
|
||||||
|
*/
|
||||||
|
public interface MessageReceivedListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle a MessageReceivedEvent.
|
||||||
|
*/
|
||||||
|
public void messageReceived (MessageReceivedEvent event);
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property change events are dispatched after the property change was
|
||||||
|
* validated on the server.
|
||||||
|
*/
|
||||||
|
public class PropertyChangedEvent extends EZEvent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public PropertyChangedEvent (
|
||||||
|
EZGame ezgame, String propName, Object newValue, Object oldValue,
|
||||||
|
int index)
|
||||||
|
{
|
||||||
|
super(ezgame);
|
||||||
|
_name = propName;
|
||||||
|
_newValue = newValue;
|
||||||
|
_oldValue = oldValue;
|
||||||
|
_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the property that changed.
|
||||||
|
*/
|
||||||
|
public String getName ()
|
||||||
|
{
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the property's new value.
|
||||||
|
*/
|
||||||
|
public Object getNewValue ()
|
||||||
|
{
|
||||||
|
return _newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the property's previous value (handy!).
|
||||||
|
*/
|
||||||
|
public Object getOldValue ()
|
||||||
|
{
|
||||||
|
return _oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If an array element was updated, get the index, or -1 if not applicable.
|
||||||
|
*/
|
||||||
|
public int getIndex ()
|
||||||
|
{
|
||||||
|
return _index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString ()
|
||||||
|
{
|
||||||
|
return "[PropertyChangedEvent name=" + _name + ", value=" + _newValue +
|
||||||
|
((_index < 0) ? "" : (", index=" + _index)) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Our implementation details. */
|
||||||
|
protected String _name;
|
||||||
|
protected Object _newValue;
|
||||||
|
protected Object _oldValue;
|
||||||
|
protected int _index;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this interface to automagically be registered to received
|
||||||
|
* PropertyChangedEvents.
|
||||||
|
*/
|
||||||
|
public interface PropertyChangedListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle a PropertyChangedEvent.
|
||||||
|
*/
|
||||||
|
public void propertyChanged (PropertyChangedEvent event);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatched when the state of the game has changed.
|
||||||
|
*/
|
||||||
|
public class StateChangedEvent extends EZEvent
|
||||||
|
{
|
||||||
|
/** Indicates that the game has transitioned to a started state. */
|
||||||
|
public static final String GAME_STARTED = "GameStarted";
|
||||||
|
|
||||||
|
/** Indicates that the game has transitioned to a ended state. */
|
||||||
|
public static final String GAME_ENDED = "GameEnded";
|
||||||
|
|
||||||
|
/** Indicates that the turn has changed. */
|
||||||
|
// TODO: move to own event?
|
||||||
|
public static final String TURN_CHANGED = "TurnChanged";
|
||||||
|
|
||||||
|
public StateChangedEvent (EZGame ezgame, String type)
|
||||||
|
{
|
||||||
|
super(ezgame);
|
||||||
|
_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType ()
|
||||||
|
{
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString ()
|
||||||
|
{
|
||||||
|
return "[StateChangedEvent type=" + _type + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String _type;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.threerings.ezgame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this interface to automagically be registered to receive
|
||||||
|
* StateChangedEvents.
|
||||||
|
*/
|
||||||
|
public interface StateChangedListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle a StateChangedEvent.
|
||||||
|
*/
|
||||||
|
public void stateChanged (StateChangedEvent event);
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package com.threerings.ezgame.client;
|
||||||
|
|
||||||
|
import com.threerings.util.Name;
|
||||||
|
|
||||||
|
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.EZEvent;
|
||||||
|
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 GameObjectImpl gameObjImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public EZGameController ()
|
||||||
|
{
|
||||||
|
addDelegate(_turnDelegate = new TurnGameControllerDelegate(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void willEnterPlace (PlaceObject plobj)
|
||||||
|
{
|
||||||
|
_ezObj = (EZGameObject) plobj;
|
||||||
|
gameObjImpl = new GameObjectImpl(_ctx, _ezObj);
|
||||||
|
|
||||||
|
_ctx.getClient().getClientObject().addListener(_userListener);
|
||||||
|
|
||||||
|
super.willEnterPlace(plobj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void didLeavePlace (PlaceObject plobj)
|
||||||
|
{
|
||||||
|
super.didLeavePlace(plobj);
|
||||||
|
|
||||||
|
_ctx.getClient().getClientObject().removeListener(_userListener);
|
||||||
|
|
||||||
|
_ezObj = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from TurnGameController
|
||||||
|
public void turnDidChange (Name turnHolder)
|
||||||
|
{
|
||||||
|
dispatchUserEvent(
|
||||||
|
new StateChangedEvent(gameObjImpl, StateChangedEvent.TURN_CHANGED));
|
||||||
|
}
|
||||||
|
|
||||||
|
// from PropertySetListener
|
||||||
|
public void propertyWasSet (PropertySetEvent event)
|
||||||
|
{
|
||||||
|
// notify the user game
|
||||||
|
dispatchUserEvent(new PropertyChangedEvent(
|
||||||
|
gameObjImpl, event.getName(), event.getValue(),
|
||||||
|
event.getOldValue(), event.getIndex()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// from MessageListener
|
||||||
|
public void messageReceived (MessageEvent event)
|
||||||
|
{
|
||||||
|
String name = event.getName();
|
||||||
|
if (EZGameObject.USER_MESSAGE.equals(name)) {
|
||||||
|
dispatchUserMessage(event.getArgs());
|
||||||
|
|
||||||
|
} else if (EZGameObject.GAME_CHAT.equals(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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch the user message.
|
||||||
|
*/
|
||||||
|
protected void dispatchUserMessage (Object[] args)
|
||||||
|
{
|
||||||
|
dispatchUserEvent(new MessageReceivedEvent(
|
||||||
|
gameObjImpl, (String) args[0],
|
||||||
|
EZObjectMarshaller.decode(args[1])));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PlaceView createPlaceView (CrowdContext ctx)
|
||||||
|
{
|
||||||
|
return new EZGamePanel(ctx, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void gameDidStart ()
|
||||||
|
{
|
||||||
|
super.gameDidStart();
|
||||||
|
dispatchUserEvent(
|
||||||
|
new StateChangedEvent(gameObjImpl, StateChangedEvent.GAME_STARTED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void gameDidEnd ()
|
||||||
|
{
|
||||||
|
super.gameDidEnd();
|
||||||
|
dispatchUserEvent(
|
||||||
|
new StateChangedEvent(gameObjImpl, StateChangedEvent.GAME_ENDED));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void dispatchUserEvent (EZEvent event)
|
||||||
|
{
|
||||||
|
gameObjImpl.dispatch(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EZGameObject _ezObj;
|
||||||
|
|
||||||
|
protected TurnGameControllerDelegate _turnDelegate;
|
||||||
|
|
||||||
|
/** Listens for message events on the user object. */
|
||||||
|
protected MessageListener _userListener = new MessageListener() {
|
||||||
|
public void messageReceived (MessageEvent event) {
|
||||||
|
// see if it's a message about user games
|
||||||
|
String msgName = EZGameObject.USER_MESSAGE + ":" + _ezObj.getOid();
|
||||||
|
if (msgName.equals(event.getName())) {
|
||||||
|
dispatchUserMessage(event.getArgs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package com.threerings.ezgame.client;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
|
||||||
|
import java.awt.event.ContainerEvent;
|
||||||
|
import java.awt.event.ContainerListener;
|
||||||
|
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import com.samskivert.swing.VGroupLayout;
|
||||||
|
import com.samskivert.swing.util.SwingUtil;
|
||||||
|
|
||||||
|
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 JPanel
|
||||||
|
implements ContainerListener, PlaceView
|
||||||
|
{
|
||||||
|
public EZGamePanel (CrowdContext ctx, EZGameController ctrl)
|
||||||
|
{
|
||||||
|
super(new VGroupLayout(VGroupLayout.STRETCH));
|
||||||
|
|
||||||
|
_ctx = ctx;
|
||||||
|
_ctrl = ctrl;
|
||||||
|
|
||||||
|
// add a listener so that we hear about all new children
|
||||||
|
addContainerListener(this);
|
||||||
|
|
||||||
|
EZGameConfig cfg = (EZGameConfig) ctrl.getPlaceConfig();
|
||||||
|
try {
|
||||||
|
// TODO
|
||||||
|
_gameView = (Component) Class.forName(cfg.configData).newInstance();
|
||||||
|
add(_gameView);
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
throw re;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add a standard chat display?
|
||||||
|
//addChild(new ChatDisplayBox(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// from PlaceView
|
||||||
|
public void willEnterPlace (final PlaceObject plobj)
|
||||||
|
{
|
||||||
|
// 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().getRunQueue().postRunnable(new Runnable() {
|
||||||
|
public void run () {
|
||||||
|
_ctx.getClient().getRunQueue().postRunnable(new Runnable() {
|
||||||
|
public void run () {
|
||||||
|
_ezObj = (EZGameObject) plobj;
|
||||||
|
notifyOfGame(_gameView);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// from PlaceView
|
||||||
|
public void didLeavePlace (PlaceObject plobj)
|
||||||
|
{
|
||||||
|
removeListeners(_gameView);
|
||||||
|
_ezObj = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from ContainerListener
|
||||||
|
public void componentAdded (ContainerEvent event)
|
||||||
|
{
|
||||||
|
if (_ezObj != null) {
|
||||||
|
notifyOfGame(event.getChild());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from ContainerListener
|
||||||
|
public void componentRemoved (ContainerEvent event)
|
||||||
|
{
|
||||||
|
if (_ezObj != null) {
|
||||||
|
removeListeners(event.getChild());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find any children of the specified object that implement
|
||||||
|
* com.metasoy.game.Game and provide them with the GameObject.
|
||||||
|
*/
|
||||||
|
protected void notifyOfGame (Component root)
|
||||||
|
{
|
||||||
|
SwingUtil.applyToHierarchy(root, new SwingUtil.ComponentOp() {
|
||||||
|
public void apply (Component comp) {
|
||||||
|
if (comp instanceof Game) {
|
||||||
|
// only notify the Game if we haven't seen it before
|
||||||
|
if (null == _seenGames.put(comp, Boolean.TRUE)) {
|
||||||
|
((Game) comp).setGameObject(_ctrl.gameObjImpl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// always check to see if it's a listener
|
||||||
|
_ctrl.gameObjImpl.registerListener(comp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeListeners (Component root)
|
||||||
|
{
|
||||||
|
SwingUtil.applyToHierarchy(root, new SwingUtil.ComponentOp() {
|
||||||
|
public void apply (Component comp) {
|
||||||
|
_ctrl.gameObjImpl.unregisterListener(comp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CrowdContext _ctx;
|
||||||
|
protected EZGameController _ctrl;
|
||||||
|
|
||||||
|
protected Component _gameView;
|
||||||
|
|
||||||
|
/** A weak-key hash of the Game interfaces we've already seen. */
|
||||||
|
protected WeakHashMap<Component, Boolean> _seenGames =
|
||||||
|
new WeakHashMap<Component, Boolean>();
|
||||||
|
|
||||||
|
protected EZGameObject _ezObj;
|
||||||
|
}
|
||||||
@@ -13,6 +13,10 @@ public interface EZGameService extends InvocationService
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Request to set the specified property.
|
* Request to set the specified property.
|
||||||
|
*
|
||||||
|
* @param value either a byte[] if setting a non-array property
|
||||||
|
* or a property at an array index, or a byte[][] if setting
|
||||||
|
* an array property where index is -1.
|
||||||
*/
|
*/
|
||||||
public void setProperty (
|
public void setProperty (
|
||||||
Client client, String propName, Object value, int index,
|
Client client, String propName, Object value, int index,
|
||||||
@@ -35,6 +39,10 @@ public interface EZGameService extends InvocationService
|
|||||||
/**
|
/**
|
||||||
* Request to send a private message to one other player in
|
* Request to send a private message to one other player in
|
||||||
* the game.
|
* the game.
|
||||||
|
*
|
||||||
|
* @param value either a byte[] if setting a non-array property
|
||||||
|
* or a property at an array index, or a byte[][] if setting
|
||||||
|
* an array property where index is -1.
|
||||||
*/
|
*/
|
||||||
public void sendMessage (
|
public void sendMessage (
|
||||||
Client client, String msgName, Object value, int playerIdx,
|
Client client, String msgName, Object value, int playerIdx,
|
||||||
|
|||||||
@@ -0,0 +1,473 @@
|
|||||||
|
package com.threerings.ezgame.client;
|
||||||
|
|
||||||
|
import java.io.Externalizable;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.samskivert.util.CompactIntListUtil;
|
||||||
|
import com.samskivert.util.ObserverList;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import com.threerings.util.MessageBundle;
|
||||||
|
import com.threerings.util.Name;
|
||||||
|
|
||||||
|
import com.threerings.presents.client.InvocationService;
|
||||||
|
|
||||||
|
import com.threerings.crowd.data.BodyObject;
|
||||||
|
import com.threerings.crowd.util.CrowdContext;
|
||||||
|
|
||||||
|
import com.threerings.parlor.Log; // well, fine
|
||||||
|
|
||||||
|
import com.threerings.ezgame.data.EZGameObject;
|
||||||
|
import com.threerings.ezgame.data.PropertySetEvent;
|
||||||
|
import com.threerings.ezgame.util.EZObjectMarshaller;
|
||||||
|
|
||||||
|
import com.threerings.ezgame.EZGame;
|
||||||
|
import com.threerings.ezgame.EZEvent;
|
||||||
|
import com.threerings.ezgame.DealListener;
|
||||||
|
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
|
||||||
|
implements EZGame
|
||||||
|
{
|
||||||
|
public GameObjectImpl (CrowdContext ctx, EZGameObject ezObj)
|
||||||
|
{
|
||||||
|
_ctx = ctx;
|
||||||
|
_ezObj = ezObj;
|
||||||
|
_props = _ezObj.getUserProps();
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public Object get (String propName)
|
||||||
|
{
|
||||||
|
return _props.get(propName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public Object get (String propName, int index)
|
||||||
|
{
|
||||||
|
return ((Object[]) get(propName))[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void set (String propName, Object value)
|
||||||
|
{
|
||||||
|
set(propName, value, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void set (String propName, Object value, int index)
|
||||||
|
{
|
||||||
|
validatePropertyChange(propName, value, -1);
|
||||||
|
|
||||||
|
Object encoded = EZObjectMarshaller.encode(value);
|
||||||
|
Object reconstituted = EZObjectMarshaller.decode(encoded);
|
||||||
|
_ezObj.ezGameService.setProperty(
|
||||||
|
_ctx.getClient(), propName, encoded, index,
|
||||||
|
createLoggingListener("setProperty"));
|
||||||
|
|
||||||
|
// set it immediately in the game object
|
||||||
|
_ezObj.applyPropertySet(propName, reconstituted, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void registerListener (Object obj)
|
||||||
|
{
|
||||||
|
if ((obj instanceof MessageReceivedListener) ||
|
||||||
|
(obj instanceof PropertyChangedListener) ||
|
||||||
|
(obj instanceof StateChangedListener)) {
|
||||||
|
|
||||||
|
// silently ignore requests to listen twice
|
||||||
|
if (!_listeners.contains(obj)) {
|
||||||
|
_listeners.add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void unregisterListener (Object obj)
|
||||||
|
{
|
||||||
|
_listeners.remove(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void setCollection (String collName, Object values)
|
||||||
|
{
|
||||||
|
populateCollection(collName, values, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void addToCollection (String collName, Object values)
|
||||||
|
{
|
||||||
|
populateCollection(collName, values, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void pickFromCollection (
|
||||||
|
String collName, int count, String propName)
|
||||||
|
{
|
||||||
|
getFromCollection(collName, count, propName, -1, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void pickFromCollection (
|
||||||
|
String collName, int count, String msgName, int playerIndex)
|
||||||
|
{
|
||||||
|
getFromCollection(collName, count, msgName, playerIndex, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void dealFromCollection (
|
||||||
|
String collName, int count, String propName,
|
||||||
|
DealListener listener)
|
||||||
|
{
|
||||||
|
getFromCollection(collName, count, propName, -1, true, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void dealFromCollection (
|
||||||
|
String collName, int count, String msgName,
|
||||||
|
DealListener listener, int playerIndex)
|
||||||
|
{
|
||||||
|
getFromCollection(collName, count, msgName, playerIndex, true, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void mergeCollection (String srcColl, String intoColl)
|
||||||
|
{
|
||||||
|
validateName(srcColl);
|
||||||
|
validateName(intoColl);
|
||||||
|
_ezObj.ezGameService.mergeCollection(_ctx.getClient(),
|
||||||
|
srcColl, intoColl, createLoggingListener("mergeCollection"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void sendMessage (String messageName, Object value)
|
||||||
|
{
|
||||||
|
sendMessage(messageName, value, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void sendMessage (String messageName, Object value, int playerIndex)
|
||||||
|
{
|
||||||
|
validateName(messageName);
|
||||||
|
validateValue(value);
|
||||||
|
|
||||||
|
Object encoded = EZObjectMarshaller.encode(value);
|
||||||
|
_ezObj.ezGameService.sendMessage(_ctx.getClient(),
|
||||||
|
messageName, encoded, playerIndex,
|
||||||
|
createLoggingListener("sendMessage"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void sendChat (String msg)
|
||||||
|
{
|
||||||
|
validateChat(msg);
|
||||||
|
// Post a message to the game object, the controller
|
||||||
|
// will listen and call localChat().
|
||||||
|
_ezObj.postMessage(EZGameObject.GAME_CHAT, new Object[] { msg });
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void localChat (String msg)
|
||||||
|
{
|
||||||
|
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 EZGame
|
||||||
|
public String[] getPlayerNames ()
|
||||||
|
{
|
||||||
|
String[] names = new String[_ezObj.players.length];
|
||||||
|
int index = 0;
|
||||||
|
for (Name name : _ezObj.players) {
|
||||||
|
names[index++] = (name == null) ? null : name.toString();
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public int getMyIndex ()
|
||||||
|
{
|
||||||
|
return _ezObj.getPlayerIndex(getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public int getTurnHolderIndex ()
|
||||||
|
{
|
||||||
|
return _ezObj.getPlayerIndex(_ezObj.turnHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public int[] getWinnerIndexes ()
|
||||||
|
{
|
||||||
|
int[] winners = new int[0];
|
||||||
|
if (_ezObj.winners != null) {
|
||||||
|
for (int ii = 0; ii < _ezObj.winners.length; ii++) {
|
||||||
|
if (_ezObj.winners[ii]) {
|
||||||
|
winners = CompactIntListUtil.add(winners, ii);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return winners;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public boolean isMyTurn ()
|
||||||
|
{
|
||||||
|
return getUsername().equals(_ezObj.turnHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public boolean isInPlay ()
|
||||||
|
{
|
||||||
|
return _ezObj.isInPlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void endTurn ()
|
||||||
|
{
|
||||||
|
endTurn(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void endTurn (int nextPlayerIndex)
|
||||||
|
{
|
||||||
|
_ezObj.ezGameService.endTurn(_ctx.getClient(), nextPlayerIndex,
|
||||||
|
createLoggingListener("endTurn"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// from EZGame
|
||||||
|
public void endGame (int... winners)
|
||||||
|
{
|
||||||
|
_ezObj.ezGameService.endGame(_ctx.getClient(), winners,
|
||||||
|
createLoggingListener("endGame"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Secret function to dispatch property changed events.
|
||||||
|
*/
|
||||||
|
void dispatch (EZEvent event)
|
||||||
|
{
|
||||||
|
ObserverList.ObserverOp<Object> op;
|
||||||
|
|
||||||
|
if (event instanceof PropertyChangedEvent) {
|
||||||
|
final PropertyChangedEvent pce = (PropertyChangedEvent) event;
|
||||||
|
op = new ObserverList.ObserverOp<Object>() {
|
||||||
|
public boolean apply (Object obs) {
|
||||||
|
if (obs instanceof PropertyChangedListener) {
|
||||||
|
((PropertyChangedListener) obs).propertyChanged(pce);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} else if (event instanceof StateChangedEvent) {
|
||||||
|
final StateChangedEvent sce = (StateChangedEvent) event;
|
||||||
|
op = new ObserverList.ObserverOp<Object>() {
|
||||||
|
public boolean apply (Object obs) {
|
||||||
|
if (obs instanceof StateChangedListener) {
|
||||||
|
((StateChangedListener) obs).stateChanged(sce);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} else if (event instanceof MessageReceivedEvent) {
|
||||||
|
final MessageReceivedEvent mre = (MessageReceivedEvent) event;
|
||||||
|
op = new ObserverList.ObserverOp<Object>() {
|
||||||
|
public boolean apply (Object obs) {
|
||||||
|
if (obs instanceof MessageReceivedListener) {
|
||||||
|
((MessageReceivedListener) obs).messageReceived(mre);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Please implement");
|
||||||
|
}
|
||||||
|
|
||||||
|
// and apply the operation
|
||||||
|
_listeners.apply(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to get our name.
|
||||||
|
*/
|
||||||
|
private Name getUsername ()
|
||||||
|
{
|
||||||
|
BodyObject body = (BodyObject) _ctx.getClient().getClientObject();
|
||||||
|
return body.getVisibleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a listener for service requests.
|
||||||
|
*/
|
||||||
|
private InvocationService.ConfirmListener createLoggingListener (
|
||||||
|
final String service)
|
||||||
|
{
|
||||||
|
return new InvocationService.ConfirmListener() {
|
||||||
|
public void requestFailed (String cause)
|
||||||
|
{
|
||||||
|
Log.warning("Service failure " +
|
||||||
|
"[service=" + service + ", cause=" + cause + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestProcessed ()
|
||||||
|
{
|
||||||
|
// nada
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for setCollection and addToCollection.
|
||||||
|
*/
|
||||||
|
private void populateCollection (
|
||||||
|
String collName, Object values, boolean clearExisting)
|
||||||
|
{
|
||||||
|
validateName(collName);
|
||||||
|
if (values == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Collection values may not be null.");
|
||||||
|
}
|
||||||
|
validateValue(values);
|
||||||
|
|
||||||
|
byte[][] encodedValues = (byte[][]) EZObjectMarshaller.encode(values);
|
||||||
|
|
||||||
|
_ezObj.ezGameService.addToCollection(
|
||||||
|
_ctx.getClient(), collName, encodedValues, clearExisting,
|
||||||
|
createLoggingListener("populateCollection"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for pickFromCollection and dealFromCollection.
|
||||||
|
*/
|
||||||
|
private void getFromCollection(
|
||||||
|
String collName, final int count, String msgOrPropName, int playerIndex,
|
||||||
|
boolean consume, final DealListener dealy)
|
||||||
|
{
|
||||||
|
validateName(collName);
|
||||||
|
validateName(msgOrPropName);
|
||||||
|
if (count < 1) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Must retrieve at least one element!");
|
||||||
|
}
|
||||||
|
|
||||||
|
InvocationService.ConfirmListener listener;
|
||||||
|
if (dealy != null) {
|
||||||
|
// TODO: Figure out the method sig of the callback, and what it
|
||||||
|
// means
|
||||||
|
listener = new InvocationService.ConfirmListener() {
|
||||||
|
public void requestFailed (String cause) {
|
||||||
|
try {
|
||||||
|
dealy.dealt(Integer.parseInt(cause));
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
// nada
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestProcessed () {
|
||||||
|
dealy.dealt(count);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
listener = createLoggingListener("getFromCollection");
|
||||||
|
}
|
||||||
|
|
||||||
|
_ezObj.ezGameService.getFromCollection(
|
||||||
|
_ctx.getClient(), collName, consume, count, msgOrPropName,
|
||||||
|
playerIndex, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that the property name / value are valid.
|
||||||
|
*/
|
||||||
|
private void validatePropertyChange (
|
||||||
|
String propName, Object value, int index)
|
||||||
|
{
|
||||||
|
validateName(propName);
|
||||||
|
|
||||||
|
// check that we're setting an array element on an array
|
||||||
|
if (index >= 0) {
|
||||||
|
if (!(get(propName) instanceof Object[])) {
|
||||||
|
throw new IllegalArgumentException("Property " + propName +
|
||||||
|
" is not an Array.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate the value too
|
||||||
|
validateValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that the specified name is valid.
|
||||||
|
*/
|
||||||
|
private void validateName (String name)
|
||||||
|
{
|
||||||
|
if (name == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Property, message, and collection names must not be null.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateChat (String msg)
|
||||||
|
{
|
||||||
|
if (StringUtil.isBlank(msg)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Empty chat may not be displayed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that the value is legal to be streamed to other clients.
|
||||||
|
*/
|
||||||
|
private void validateValue (Object value)
|
||||||
|
{
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else if (value instanceof Externalizable) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"IExternalizable is not yet supported");
|
||||||
|
|
||||||
|
} else if (value.getClass().isArray()) {
|
||||||
|
int length = Array.getLength(value);
|
||||||
|
for (int ii=0; ii < length; ii++) {
|
||||||
|
validateValue(Array.get(value, ii));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (value instanceof Iterable) {
|
||||||
|
for (Object o : (Iterable) value) {
|
||||||
|
validateValue(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (!(value instanceof Serializable)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Non-serializable properties may not be set.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CrowdContext _ctx;
|
||||||
|
|
||||||
|
protected EZGameObject _ezObj;
|
||||||
|
|
||||||
|
protected HashMap<String, Object> _props;
|
||||||
|
|
||||||
|
protected ObserverList<Object> _listeners =
|
||||||
|
new ObserverList<Object>(ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||||
|
}
|
||||||
@@ -29,7 +29,8 @@ public class EZGameConfig extends GameConfig
|
|||||||
// from abstract GameConfig
|
// from abstract GameConfig
|
||||||
public GameConfigurator createConfigurator ()
|
public GameConfigurator createConfigurator ()
|
||||||
{
|
{
|
||||||
return null; // nothing here on the java side
|
// TODO
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import com.threerings.io.Streamable;
|
|||||||
import com.threerings.parlor.game.data.GameObject;
|
import com.threerings.parlor.game.data.GameObject;
|
||||||
import com.threerings.parlor.turn.data.TurnGameObject;
|
import com.threerings.parlor.turn.data.TurnGameObject;
|
||||||
|
|
||||||
|
import com.threerings.ezgame.util.EZObjectMarshaller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the data for an ez game.
|
* Contains the data for an ez game.
|
||||||
*/
|
*/
|
||||||
@@ -43,6 +45,14 @@ public class EZGameObject extends GameObject
|
|||||||
/** The service interface for requesting special things from the server. */
|
/** The service interface for requesting special things from the server. */
|
||||||
public EZGameMarshaller ezGameService;
|
public EZGameMarshaller ezGameService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the underlying user properties
|
||||||
|
*/
|
||||||
|
public HashMap<String, Object> getUserProps ()
|
||||||
|
{
|
||||||
|
return _props;
|
||||||
|
}
|
||||||
|
|
||||||
// from TurnGameObject
|
// from TurnGameObject
|
||||||
public String getTurnHolderFieldName ()
|
public String getTurnHolderFieldName ()
|
||||||
{
|
{
|
||||||
@@ -64,23 +74,40 @@ public class EZGameObject extends GameObject
|
|||||||
/**
|
/**
|
||||||
* Called by PropertySetEvent to effect the property update.
|
* Called by PropertySetEvent to effect the property update.
|
||||||
*/
|
*/
|
||||||
protected void applyPropertySet (String propName, Object data, int index)
|
public Object applyPropertySet (String propName, Object data, int index)
|
||||||
{
|
{
|
||||||
|
Object oldValue = _props.get(propName);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
Object something = _props.get(propName);
|
if (isOnServer()) {
|
||||||
byte[][] arr = (something instanceof byte[][])
|
byte[][] arr = (oldValue instanceof byte[][])
|
||||||
? (byte[][]) something : null;
|
? (byte[][]) oldValue : null;
|
||||||
if (arr == null || arr.length <= index) {
|
if (arr == null || arr.length <= index) {
|
||||||
// TODO: in case a user sets element 0 and element 90000,
|
// TODO: in case a user sets element 0 and element 90000,
|
||||||
// we might want to store elements in a hash
|
// we might want to store elements in a hash
|
||||||
byte[][] newArr = new byte[index + 1][];
|
byte[][] newArr = new byte[index + 1][];
|
||||||
if (arr != null) {
|
if (arr != null) {
|
||||||
System.arraycopy(arr, 0, newArr, 0, arr.length);
|
System.arraycopy(arr, 0, newArr, 0, arr.length);
|
||||||
|
}
|
||||||
|
_props.put(propName, newArr);
|
||||||
|
arr = newArr;
|
||||||
}
|
}
|
||||||
_props.put(propName, newArr);
|
oldValue = arr[index];
|
||||||
arr = newArr;
|
arr[index] = (byte[]) data;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Object[] arr = (oldValue instanceof Object[])
|
||||||
|
? (Object[]) oldValue : null;
|
||||||
|
if (arr == null || arr.length <= index) {
|
||||||
|
Object[] newArr = new Object[index + 1];
|
||||||
|
if (arr != null) {
|
||||||
|
System.arraycopy(arr, 0, newArr, 0, arr.length);
|
||||||
|
}
|
||||||
|
_props.put(propName, newArr);
|
||||||
|
arr = newArr;
|
||||||
|
}
|
||||||
|
oldValue = arr[index];
|
||||||
|
arr[index] = data;
|
||||||
}
|
}
|
||||||
arr[index] = (byte[]) data;
|
|
||||||
|
|
||||||
} else if (data != null) {
|
} else if (data != null) {
|
||||||
_props.put(propName, data);
|
_props.put(propName, data);
|
||||||
@@ -88,6 +115,8 @@ public class EZGameObject extends GameObject
|
|||||||
} else {
|
} else {
|
||||||
_props.remove(propName);
|
_props.remove(propName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return oldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// AUTO-GENERATED: METHODS START
|
// AUTO-GENERATED: METHODS START
|
||||||
@@ -132,11 +161,15 @@ public class EZGameObject extends GameObject
|
|||||||
{
|
{
|
||||||
out.defaultWriteObject();
|
out.defaultWriteObject();
|
||||||
|
|
||||||
// write the number of properties, followed by each one
|
if (isOnServer()) {
|
||||||
out.writeInt(_props.size());
|
// write the number of properties, followed by each one
|
||||||
for (Map.Entry<String, Object> entry : _props.entrySet()) {
|
out.writeInt(_props.size());
|
||||||
out.writeUTF(entry.getKey());
|
for (Map.Entry<String, Object> entry : _props.entrySet()) {
|
||||||
out.writeObject(entry.getValue());
|
out.writeUTF(entry.getKey());
|
||||||
|
out.writeObject(entry.getValue());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,13 +183,31 @@ public class EZGameObject extends GameObject
|
|||||||
|
|
||||||
_props.clear();
|
_props.clear();
|
||||||
int count = ins.readInt();
|
int count = ins.readInt();
|
||||||
|
boolean onClient = !isOnServer();
|
||||||
while (count-- > 0) {
|
while (count-- > 0) {
|
||||||
String key = ins.readUTF();
|
String key = ins.readUTF();
|
||||||
_props.put(key, ins.readObject());
|
Object o = ins.readObject();
|
||||||
|
if (onClient) {
|
||||||
|
o = EZObjectMarshaller.decode(o);
|
||||||
|
}
|
||||||
|
_props.put(key, o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The current state of game data, opaque to us here on the server. */
|
/**
|
||||||
|
* Called internally and by PropertySetEvent to determine if we're
|
||||||
|
* on the server or on the client.
|
||||||
|
*/
|
||||||
|
boolean isOnServer ()
|
||||||
|
{
|
||||||
|
return _omgr.isManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The current state of game data.
|
||||||
|
* On the server, this will be a byte[] for normal properties
|
||||||
|
* and a byte[][] for array properties.
|
||||||
|
* On the client, the actual values are kept whole.
|
||||||
|
*/
|
||||||
protected transient HashMap<String, Object> _props =
|
protected transient HashMap<String, Object> _props =
|
||||||
new HashMap<String, Object>();
|
new HashMap<String, Object>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import com.threerings.io.ObjectInputStream;
|
|||||||
import com.threerings.io.ObjectOutputStream;
|
import com.threerings.io.ObjectOutputStream;
|
||||||
import com.threerings.io.Streamable;
|
import com.threerings.io.Streamable;
|
||||||
import com.threerings.io.Streamer;
|
import com.threerings.io.Streamer;
|
||||||
|
|
||||||
import com.threerings.presents.dobj.DObject;
|
import com.threerings.presents.dobj.DObject;
|
||||||
import com.threerings.presents.dobj.NamedEvent;
|
import com.threerings.presents.dobj.NamedEvent;
|
||||||
|
|
||||||
|
import com.threerings.ezgame.util.EZObjectMarshaller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a property change on the actionscript object we
|
* Represents a property change on the actionscript object we
|
||||||
* use in EZGameObject.
|
* use in EZGameObject.
|
||||||
@@ -35,16 +37,55 @@ public class PropertySetEvent extends NamedEvent
|
|||||||
_index = index;
|
_index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value that was set for the property.
|
||||||
|
*/
|
||||||
|
public Object getValue ()
|
||||||
|
{
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the old value.
|
||||||
|
*/
|
||||||
|
public Object getOldValue ()
|
||||||
|
{
|
||||||
|
return _oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index, or -1 if not applicable.
|
||||||
|
*/
|
||||||
|
public int getIndex ()
|
||||||
|
{
|
||||||
|
return _index;
|
||||||
|
}
|
||||||
|
|
||||||
// from abstract DEvent
|
// from abstract DEvent
|
||||||
public boolean applyToObject (DObject target)
|
public boolean applyToObject (DObject target)
|
||||||
{
|
{
|
||||||
((EZGameObject) target).applyPropertySet(_name, _data, _index);
|
EZGameObject ezObj = (EZGameObject) target;
|
||||||
|
if (!ezObj.isOnServer()) {
|
||||||
|
_data = EZObjectMarshaller.decode(_data);
|
||||||
|
}
|
||||||
|
_oldValue = ezObj.applyPropertySet(_name, _data, _index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void notifyListener (Object listener)
|
||||||
|
{
|
||||||
|
if (listener instanceof PropertySetListener) {
|
||||||
|
((PropertySetListener) listener).propertyWasSet(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** The index. */
|
/** The index. */
|
||||||
protected int _index;
|
protected int _index;
|
||||||
|
|
||||||
/** The client-side data that is assigned to this property. */
|
/** The client-side data that is assigned to this property. */
|
||||||
protected Object _data;
|
protected Object _data;
|
||||||
|
|
||||||
|
/** The old value. */
|
||||||
|
protected transient Object _oldValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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.
|
||||||
|
*/
|
||||||
|
public void propertyWasSet (PropertySetEvent event);
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package com.threerings.ezgame.util;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Externalizable implementations, and take
|
||||||
|
* into account the ClassLoader.
|
||||||
|
*/
|
||||||
|
public static Object encode (Object obj)
|
||||||
|
{
|
||||||
|
return encode(obj, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Object encode (Object obj, boolean encodeArrayElements)
|
||||||
|
{
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (encodeArrayElements) {
|
||||||
|
if (obj instanceof Iterable) {
|
||||||
|
ArrayList<byte[]> list = new ArrayList<byte[]>();
|
||||||
|
for (Object o : (Iterable) obj) {
|
||||||
|
list.add((byte[]) encode(o, false));
|
||||||
|
}
|
||||||
|
byte[][] retval = new byte[list.size()][];
|
||||||
|
list.toArray(retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.getClass().isArray()) {
|
||||||
|
int length = Array.getLength(obj);
|
||||||
|
byte[][] retval = new byte[length][];
|
||||||
|
for (int ii=0; ii < length; ii++) {
|
||||||
|
retval[ii] = (byte[]) encode(Array.get(obj, ii), false);
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Our own encoding?
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||||
|
oos.writeObject(obj);
|
||||||
|
oos.flush();
|
||||||
|
return baos.toByteArray();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
// TODO: pass outward?
|
||||||
|
return null; // just crush the object into nothingness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: we may extend this to take the client ezgame's ClassLoader
|
||||||
|
// and reconstitute custom classes that implement Externalizable
|
||||||
|
public static Object decode (Object encoded)
|
||||||
|
{
|
||||||
|
if (encoded == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (encoded instanceof byte[][]) {
|
||||||
|
byte[][] src = (byte[][]) encoded;
|
||||||
|
Object[] retval = new Object[src.length];
|
||||||
|
for (int ii=0; ii < src.length; ii++) {
|
||||||
|
retval[ii] = decode(src[ii]);
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteArrayInputStream bais =
|
||||||
|
new ByteArrayInputStream((byte[]) encoded);
|
||||||
|
ObjectInputStream ois = new ObjectInputStream(bais);
|
||||||
|
return ois.readObject();
|
||||||
|
|
||||||
|
} catch (ClassNotFoundException cnse) {
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
// TODO: pass outward?
|
||||||
|
return null; // must not have been that important!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user