Updates to EZGame to allow games to store 'user cookies'.

Then intention is that other games besides EZGame may use this as well,
so it's semi-separated, but for now it's part of EZGame.


git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@123 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
Ray Greenwell
2006-11-15 03:01:49 +00:00
parent a4ae0dd2dc
commit cff4238b3e
19 changed files with 857 additions and 13 deletions
+37
View File
@@ -178,5 +178,42 @@ public interface EZGame
* End the game. The specified player indexes are winners! * End the game. The specified player indexes are winners!
*/ */
function endGame (winnerIndex :int, ... rest) :void; function endGame (winnerIndex :int, ... rest) :void;
// function getCurrentRoom () :int;
//
// function sendPlayerToRoom (playerIndex :int, room :int) :void
/**
* Get the user-specific game data for the specified user. The
* first time this is requested per game instance it will be retrieved
* from the database. After that, it will be returned from memory.
*/
function getUserCookie (playerIndex :int, callback :Function) :void;
/**
* Store persistent data that can later be retrieved by an instance
* of this game. The maximum size of this data is 4096 bytes AFTER
* AMF3 encoding.
*
* Note: there is no playerIndex parameter because a cookie may only
* be stored for the current player.
*
* @return false if the cookie could not be encoded to 4096 bytes
* or less; true if the cookie is going to try to be saved. There is
* no guarantee it will be saved and no way to find out if it failed,
* but if it fails it will be because the shit hit the fan so hard that
* there's nothing you can do anyway.
*/
function setUserCookie (cookie :Object) :Boolean;
//
// /**
// * Check to see if the user has the specified token.
// */
// function checkUserToken (token :String, callback :Function) :void;
//
// /**
// * Take the user to a purchase page.
// */
// function purchaseUserToken (token :String, callback :Function) :void;
} }
} }
@@ -21,6 +21,7 @@
package com.threerings.ezgame.client { package com.threerings.ezgame.client {
import flash.utils.ByteArray;
import com.threerings.ezgame.client.EZGameService; import com.threerings.ezgame.client.EZGameService;
import com.threerings.presents.client.Client; import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService; import com.threerings.presents.client.InvocationService;
@@ -42,6 +43,9 @@ public interface EZGameService extends InvocationService
// from Java interface EZGameService // from Java interface EZGameService
function endTurn (arg1 :Client, arg2 :int, arg3 :InvocationService_InvocationListener) :void; function endTurn (arg1 :Client, arg2 :int, arg3 :InvocationService_InvocationListener) :void;
// from Java interface EZGameService
function getCookie (arg1 :Client, arg2 :int, arg3 :InvocationService_InvocationListener) :void;
// from Java interface EZGameService // from Java interface EZGameService
function getFromCollection (arg1 :Client, arg2 :String, arg3 :Boolean, arg4 :int, arg5 :String, arg6 :int, arg7 :InvocationService_ConfirmListener) :void; function getFromCollection (arg1 :Client, arg2 :String, arg3 :Boolean, arg4 :int, arg5 :String, arg6 :int, arg7 :InvocationService_ConfirmListener) :void;
@@ -51,6 +55,9 @@ public interface EZGameService extends InvocationService
// from Java interface EZGameService // from Java interface EZGameService
function sendMessage (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void; function sendMessage (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void;
// from Java interface EZGameService
function setCookie (arg1 :Client, arg2 :ByteArray, arg3 :InvocationService_InvocationListener) :void;
// from Java interface EZGameService // from Java interface EZGameService
function setProperty (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void; function setProperty (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void;
@@ -7,6 +7,7 @@ import flash.events.EventDispatcher;
import flash.utils.IExternalizable; import flash.utils.IExternalizable;
import flash.utils.ByteArray; import flash.utils.ByteArray;
import flash.utils.Dictionary;
import com.threerings.io.TypedArray; import com.threerings.io.TypedArray;
@@ -18,11 +19,16 @@ import com.threerings.util.StringUtil;
import com.threerings.presents.client.ConfirmAdapter; import com.threerings.presents.client.ConfirmAdapter;
import com.threerings.presents.client.InvocationService_ConfirmListener; import com.threerings.presents.client.InvocationService_ConfirmListener;
import com.threerings.presents.dobj.EntryAddedEvent;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.SetAdapter;
import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.util.CrowdContext; import com.threerings.crowd.util.CrowdContext;
import com.threerings.ezgame.data.EZGameObject; import com.threerings.ezgame.data.EZGameObject;
import com.threerings.ezgame.data.PropertySetEvent; import com.threerings.ezgame.data.PropertySetEvent;
import com.threerings.ezgame.data.UserCookie;
import com.threerings.ezgame.util.EZObjectMarshaller; import com.threerings.ezgame.util.EZObjectMarshaller;
import com.threerings.ezgame.EZGame; import com.threerings.ezgame.EZGame;
@@ -41,6 +47,8 @@ public class GameObjectImpl extends EventDispatcher
_ctx = ctx; _ctx = ctx;
_ezObj = ezObj; _ezObj = ezObj;
_gameData = new GameData(this, _ezObj.getUserProps()); _gameData = new GameData(this, _ezObj.getUserProps());
_ezObj.addListener(new SetAdapter(entryAdded, entryUpdated, null));
} }
// from EZGame // from EZGame
@@ -259,6 +267,52 @@ public class GameObjectImpl extends EventDispatcher
return arr; return arr;
} }
// from EZGame
public function getUserCookie (playerIndex :int, callback :Function) :void
{
// see if that cookie is already published
if (_ezObj.userCookies != null) {
var uc :UserCookie =
(_ezObj.userCookies.get(playerIndex) as UserCookie);
if (uc != null) {
callback(uc.cookie);
return;
}
}
if (_cookieCallbacks == null) {
_cookieCallbacks = new Dictionary();
}
var arr :Array = (_cookieCallbacks[playerIndex] as Array);
if (arr == null) {
arr = [];
_cookieCallbacks[playerIndex] = arr;
}
arr.push(callback);
// request it to be made so by the server
_ezObj.ezGameService.getCookie(_ctx.getClient(), playerIndex,
createLoggingListener("getUserCookie"));
}
// from EZGame
public function setUserCookie (cookie :Object) :Boolean
{
var ba :ByteArray =
(EZObjectMarshaller.encode(cookie, false) as ByteArray);
if (ba.length > MAX_USER_COOKIE) {
// not saved!
return false;
}
_ezObj.ezGameService.setCookie(_ctx.getClient(), null, // ba,
// TODO
///
/// TODO
createLoggingListener("setUserCookie"));
return true;
}
// from EZGame // from EZGame
public function isMyTurn () :Boolean public function isMyTurn () :Boolean
{ {
@@ -480,10 +534,55 @@ public class GameObjectImpl extends EventDispatcher
} }
} }
/**
* Handle entry updated
*/
private function entryAdded (event :EntryAddedEvent) :void
{
if (EZGameObject.USER_COOKIES == event.getName()) {
receivedUserCookie(event.getEntry() as UserCookie);
}
}
/**
* Handle entry updated
*/
private function entryUpdated (event :EntryUpdatedEvent) :void
{
if (EZGameObject.USER_COOKIES == event.getName()) {
receivedUserCookie(event.getEntry() as UserCookie);
}
}
/**
* Handle the arrival of a new UserCookie.
*/
private function receivedUserCookie (cookie :UserCookie) :void
{
if (_cookieCallbacks != null) {
var arr :Array = (_cookieCallbacks[cookie.playerIndex] as Array);
if (arr != null) {
delete _cookieCallbacks[cookie.playerIndex];
for each (var fn :Function in arr) {
try {
fn(cookie.cookie);
} catch (err :Error) {
// cope
}
}
}
}
}
protected var _ctx :CrowdContext; protected var _ctx :CrowdContext;
protected var _ezObj :EZGameObject; protected var _ezObj :EZGameObject;
protected var _gameData :GameData; protected var _gameData :GameData;
/** playerIndex -> callback functions waiting for the cookie. */
protected var _cookieCallbacks :Dictionary;
protected static const MAX_USER_COOKIE :int = 4096;
} }
} }
@@ -29,6 +29,10 @@ public class EZGameConfig extends GameConfig
// For now, the configData is either a classname or url. // For now, the configData is either a classname or url.
public var configData :String; public var configData :String;
/** If non-zero, a game id used to persistently identify the game.
* This could be thought of as a new-style rating id. */
public var persistentGameId:int;
public function EZGameConfig () public function EZGameConfig ()
{ {
// nothing needed // nothing needed
@@ -89,6 +93,7 @@ public class EZGameConfig extends GameConfig
super.readObject(ins); super.readObject(ins);
configData = (ins.readField(String) as String); configData = (ins.readField(String) as String);
persistentGameId = ins.readInt();
} }
// from interface Streamable // from interface Streamable
@@ -97,6 +102,7 @@ public class EZGameConfig extends GameConfig
super.writeObject(out); super.writeObject(out);
out.writeField(configData); out.writeField(configData);
out.writeInt(persistentGameId);
} }
} }
} }
@@ -21,6 +21,7 @@
package com.threerings.ezgame.data { package com.threerings.ezgame.data {
import flash.utils.ByteArray;
import com.threerings.util.*; // for Float, Integer, etc. import com.threerings.util.*; // for Float, Integer, etc.
import com.threerings.ezgame.client.EZGameService; import com.threerings.ezgame.client.EZGameService;
@@ -80,8 +81,21 @@ public class EZGameMarshaller extends InvocationMarshaller
]); ]);
} }
/** The method id used to dispatch {@link #getCookie} requests. */
public static const GET_COOKIE :int = 4;
// from interface EZGameService
public function getCookie (arg1 :Client, arg2 :int, arg3 :InvocationService_InvocationListener) :void
{
var listener3 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener3.listener = arg3;
sendRequest(arg1, GET_COOKIE, [
Integer.valueOf(arg2), listener3
]);
}
/** The method id used to dispatch {@link #getFromCollection} requests. */ /** The method id used to dispatch {@link #getFromCollection} requests. */
public static const GET_FROM_COLLECTION :int = 4; public static const GET_FROM_COLLECTION :int = 5;
// from interface EZGameService // from interface EZGameService
public function getFromCollection (arg1 :Client, arg2 :String, arg3 :Boolean, arg4 :int, arg5 :String, arg6 :int, arg7 :InvocationService_ConfirmListener) :void public function getFromCollection (arg1 :Client, arg2 :String, arg3 :Boolean, arg4 :int, arg5 :String, arg6 :int, arg7 :InvocationService_ConfirmListener) :void
@@ -94,7 +108,7 @@ public class EZGameMarshaller extends InvocationMarshaller
} }
/** The method id used to dispatch {@link #mergeCollection} requests. */ /** The method id used to dispatch {@link #mergeCollection} requests. */
public static const MERGE_COLLECTION :int = 5; public static const MERGE_COLLECTION :int = 6;
// from interface EZGameService // from interface EZGameService
public function mergeCollection (arg1 :Client, arg2 :String, arg3 :String, arg4 :InvocationService_InvocationListener) :void public function mergeCollection (arg1 :Client, arg2 :String, arg3 :String, arg4 :InvocationService_InvocationListener) :void
@@ -107,7 +121,7 @@ public class EZGameMarshaller extends InvocationMarshaller
} }
/** The method id used to dispatch {@link #sendMessage} requests. */ /** The method id used to dispatch {@link #sendMessage} requests. */
public static const SEND_MESSAGE :int = 6; public static const SEND_MESSAGE :int = 7;
// from interface EZGameService // from interface EZGameService
public function sendMessage (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void public function sendMessage (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void
@@ -119,8 +133,21 @@ public class EZGameMarshaller extends InvocationMarshaller
]); ]);
} }
/** The method id used to dispatch {@link #setCookie} requests. */
public static const SET_COOKIE :int = 8;
// from interface EZGameService
public function setCookie (arg1 :Client, arg2 :ByteArray, arg3 :InvocationService_InvocationListener) :void
{
var listener3 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener3.listener = arg3;
sendRequest(arg1, SET_COOKIE, [
arg2, listener3
]);
}
/** The method id used to dispatch {@link #setProperty} requests. */ /** The method id used to dispatch {@link #setProperty} requests. */
public static const SET_PROPERTY :int = 7; public static const SET_PROPERTY :int = 9;
// from interface EZGameService // from interface EZGameService
public function setProperty (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void public function setProperty (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void
@@ -133,7 +160,7 @@ public class EZGameMarshaller extends InvocationMarshaller
} }
/** The method id used to dispatch {@link #setTicker} requests. */ /** The method id used to dispatch {@link #setTicker} requests. */
public static const SET_TICKER :int = 8; public static const SET_TICKER :int = 10;
// from interface EZGameService // from interface EZGameService
public function setTicker (arg1 :Client, arg2 :String, arg3 :int, arg4 :InvocationService_InvocationListener) :void public function setTicker (arg1 :Client, arg2 :String, arg3 :int, arg4 :InvocationService_InvocationListener) :void
@@ -10,6 +10,8 @@ import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream; import com.threerings.io.ObjectOutputStream;
import com.threerings.io.TypedArray; import com.threerings.io.TypedArray;
import com.threerings.presents.dobj.DSet;
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;
@@ -31,6 +33,9 @@ public class EZGameObject extends GameObject
/** The field name of the <code>turnHolder</code> field. */ /** The field name of the <code>turnHolder</code> field. */
public static const TURN_HOLDER :String = "turnHolder"; public static const TURN_HOLDER :String = "turnHolder";
/** The field name of the <code>userCookies</code> field. */
public static const USER_COOKIES :String = "userCookies";
/** The field name of the <code>ezGameService</code> field. */ /** The field name of the <code>ezGameService</code> field. */
public static const EZ_GAME_SERVICE :String = "ezGameService"; public static const EZ_GAME_SERVICE :String = "ezGameService";
// AUTO-GENERATED: FIELDS END // AUTO-GENERATED: FIELDS END
@@ -38,6 +43,9 @@ public class EZGameObject extends GameObject
/** The current turn holder. */ /** The current turn holder. */
public var turnHolder :Name; public var turnHolder :Name;
/** A set of loaded user cookies. */
public var userCookies :DSet;
/** The service interface for requesting special things from the server. */ /** The service interface for requesting special things from the server. */
public var ezGameService :EZGameMarshaller; public var ezGameService :EZGameMarshaller;
@@ -134,6 +142,7 @@ public class EZGameObject extends GameObject
// first read any regular bits // first read any regular bits
turnHolder = (ins.readObject() as Name); turnHolder = (ins.readObject() as Name);
userCookies = (ins.readObject() as DSet);
ezGameService = (ins.readObject() as EZGameMarshaller); ezGameService = (ins.readObject() as EZGameMarshaller);
// then user properties // then user properties
@@ -0,0 +1,44 @@
//
// $Id$
package com.threerings.ezgame.data {
import flash.utils.ByteArray;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.presents.dobj.DSet_Entry;
import com.threerings.ezgame.util.EZObjectMarshaller;
public class UserCookie
implements DSet_Entry
{
/** The index of the player that has this cookie. */
public var playerIndex :int;
/** The decoded cookie value. */
public var cookie :Object;
// from DSet_Entry
public function getKey () :Object
{
return playerIndex;
}
// from superinterface Streamable
public function readObject (ins :ObjectInputStream) :void
{
playerIndex = ins.readInt();
var ba :ByteArray = (ins.readObject() as ByteArray);
cookie = EZObjectMarshaller.decode(ba);
}
// from superinterface Streamable
public function writeObject (out :ObjectOutputStream) :void
{
throw new Error();
}
}
}
@@ -7,6 +7,8 @@ import flash.system.ApplicationDomain;
import flash.utils.ByteArray; import flash.utils.ByteArray;
import flash.utils.Endian; import flash.utils.Endian;
import com.threerings.util.StringUtil;
import com.threerings.io.TypedArray; import com.threerings.io.TypedArray;
/** /**
@@ -45,6 +47,8 @@ public class EZObjectMarshaller
bytes.endian = Endian.BIG_ENDIAN; bytes.endian = Endian.BIG_ENDIAN;
bytes.objectEncoding = ObjectEncoding.AMF3; bytes.objectEncoding = ObjectEncoding.AMF3;
bytes.writeObject(obj); bytes.writeObject(obj);
Log.getLog(EZObjectMarshaller).info(
"The encoded bytes are: " + StringUtil.hexlate(bytes));
return bytes; return bytes;
} }
@@ -83,4 +83,16 @@ public interface EZGameService extends InvocationService
public void setTicker ( public void setTicker (
Client client, String tickerName, int msOfDelay, Client client, String tickerName, int msOfDelay,
InvocationListener listener); InvocationListener listener);
/**
* Request to get the specified user's cookie.
*/
public void getCookie (
Client client, int playerIndex, InvocationListener listener);
/**
* Request to set our cookie.
*/
public void setCookie (
Client client, byte[] cookie, InvocationListener listener);
} }
@@ -24,6 +24,10 @@ public class EZGameConfig extends GameConfig
// For now, the configData is either a classname or url. // For now, the configData is either a classname or url.
public String configData; public String configData;
/** If non-zero, a game id used to persistently identify the game.
* This could be thought of as a new-style rating id. */
public int persistentGameId;
// from abstract GameConfig // from abstract GameConfig
public String getBundleName () public String getBundleName ()
{ {
@@ -76,8 +76,21 @@ public class EZGameMarshaller extends InvocationMarshaller
}); });
} }
/** The method id used to dispatch {@link #getCookie} requests. */
public static final int GET_COOKIE = 4;
// from interface EZGameService
public void getCookie (Client arg1, int arg2, InvocationService.InvocationListener arg3)
{
ListenerMarshaller listener3 = new ListenerMarshaller();
listener3.listener = arg3;
sendRequest(arg1, GET_COOKIE, new Object[] {
Integer.valueOf(arg2), listener3
});
}
/** The method id used to dispatch {@link #getFromCollection} requests. */ /** The method id used to dispatch {@link #getFromCollection} requests. */
public static final int GET_FROM_COLLECTION = 4; public static final int GET_FROM_COLLECTION = 5;
// from interface EZGameService // from interface EZGameService
public void getFromCollection (Client arg1, String arg2, boolean arg3, int arg4, String arg5, int arg6, InvocationService.ConfirmListener arg7) public void getFromCollection (Client arg1, String arg2, boolean arg3, int arg4, String arg5, int arg6, InvocationService.ConfirmListener arg7)
@@ -90,7 +103,7 @@ public class EZGameMarshaller extends InvocationMarshaller
} }
/** The method id used to dispatch {@link #mergeCollection} requests. */ /** The method id used to dispatch {@link #mergeCollection} requests. */
public static final int MERGE_COLLECTION = 5; public static final int MERGE_COLLECTION = 6;
// from interface EZGameService // from interface EZGameService
public void mergeCollection (Client arg1, String arg2, String arg3, InvocationService.InvocationListener arg4) public void mergeCollection (Client arg1, String arg2, String arg3, InvocationService.InvocationListener arg4)
@@ -103,7 +116,7 @@ public class EZGameMarshaller extends InvocationMarshaller
} }
/** The method id used to dispatch {@link #sendMessage} requests. */ /** The method id used to dispatch {@link #sendMessage} requests. */
public static final int SEND_MESSAGE = 6; public static final int SEND_MESSAGE = 7;
// from interface EZGameService // from interface EZGameService
public void sendMessage (Client arg1, String arg2, Object arg3, int arg4, InvocationService.InvocationListener arg5) public void sendMessage (Client arg1, String arg2, Object arg3, int arg4, InvocationService.InvocationListener arg5)
@@ -115,8 +128,21 @@ public class EZGameMarshaller extends InvocationMarshaller
}); });
} }
/** The method id used to dispatch {@link #setCookie} requests. */
public static final int SET_COOKIE = 8;
// from interface EZGameService
public void setCookie (Client arg1, byte[] arg2, InvocationService.InvocationListener arg3)
{
ListenerMarshaller listener3 = new ListenerMarshaller();
listener3.listener = arg3;
sendRequest(arg1, SET_COOKIE, new Object[] {
arg2, listener3
});
}
/** The method id used to dispatch {@link #setProperty} requests. */ /** The method id used to dispatch {@link #setProperty} requests. */
public static final int SET_PROPERTY = 7; public static final int SET_PROPERTY = 9;
// from interface EZGameService // from interface EZGameService
public void setProperty (Client arg1, String arg2, Object arg3, int arg4, InvocationService.InvocationListener arg5) public void setProperty (Client arg1, String arg2, Object arg3, int arg4, InvocationService.InvocationListener arg5)
@@ -129,7 +155,7 @@ public class EZGameMarshaller extends InvocationMarshaller
} }
/** The method id used to dispatch {@link #setTicker} requests. */ /** The method id used to dispatch {@link #setTicker} requests. */
public static final int SET_TICKER = 8; public static final int SET_TICKER = 10;
// from interface EZGameService // from interface EZGameService
public void setTicker (Client arg1, String arg2, int arg3, InvocationService.InvocationListener arg4) public void setTicker (Client arg1, String arg2, int arg3, InvocationService.InvocationListener arg4)
@@ -14,6 +14,8 @@ 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.presents.dobj.DSet;
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;
@@ -38,6 +40,9 @@ public class EZGameObject extends GameObject
/** The field name of the <code>turnHolder</code> field. */ /** The field name of the <code>turnHolder</code> field. */
public static final String TURN_HOLDER = "turnHolder"; public static final String TURN_HOLDER = "turnHolder";
/** The field name of the <code>userCookies</code> field. */
public static final String USER_COOKIES = "userCookies";
/** The field name of the <code>ezGameService</code> field. */ /** The field name of the <code>ezGameService</code> field. */
public static final String EZ_GAME_SERVICE = "ezGameService"; public static final String EZ_GAME_SERVICE = "ezGameService";
// AUTO-GENERATED: FIELDS END // AUTO-GENERATED: FIELDS END
@@ -45,6 +50,9 @@ public class EZGameObject extends GameObject
/** The current turn holder. */ /** The current turn holder. */
public Name turnHolder; public Name turnHolder;
/** A set of loaded user cookies. */
public DSet<UserCookie> userCookies;
/** 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;
@@ -139,6 +147,54 @@ public class EZGameObject extends GameObject
this.turnHolder = value; this.turnHolder = value;
} }
/**
* Requests that the specified entry be added to the
* <code>userCookies</code> set. The set will not change until the event is
* actually propagated through the system.
*/
public void addToUserCookies (UserCookie elem)
{
requestEntryAdd(USER_COOKIES, userCookies, elem);
}
/**
* Requests that the entry matching the supplied key be removed from
* the <code>userCookies</code> set. The set will not change until the
* event is actually propagated through the system.
*/
public void removeFromUserCookies (Comparable key)
{
requestEntryRemove(USER_COOKIES, userCookies, key);
}
/**
* Requests that the specified entry be updated in the
* <code>userCookies</code> set. The set will not change until the event is
* actually propagated through the system.
*/
public void updateUserCookies (UserCookie elem)
{
requestEntryUpdate(USER_COOKIES, userCookies, elem);
}
/**
* Requests that the <code>userCookies</code> field be set to the
* specified value. Generally one only adds, updates and removes
* entries of a distributed set, but certain situations call for a
* complete replacement of the set 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 setUserCookies (DSet<com.threerings.ezgame.data.UserCookie> value)
{
requestAttributeChange(USER_COOKIES, value, this.userCookies);
@SuppressWarnings("unchecked") DSet<com.threerings.ezgame.data.UserCookie> clone =
(value == null) ? null : value.typedClone();
this.userCookies = clone;
}
/** /**
* Requests that the <code>ezGameService</code> field be set to the * Requests that the <code>ezGameService</code> field be set to the
* specified value. The local value will be updated immediately and an * specified value. The local value will be updated immediately and an
@@ -0,0 +1,51 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/vilya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.ezgame.data;
import com.threerings.presents.dobj.DSet;
/**
* Represents a user's game-specific cookie data.
*/
public class UserCookie
implements DSet.Entry
{
/** The index of the player that has this cookie. */
public int playerIndex;
/** The cookie value. */
public byte[] cookie;
/**
*/
public UserCookie (int playerIndex, byte[] cookie)
{
this.playerIndex = playerIndex;
this.cookie = cookie;
}
// from DSet.Entry
public Comparable getKey ()
{
return playerIndex;
}
}
@@ -77,6 +77,13 @@ public class EZGameDispatcher extends InvocationDispatcher
); );
return; return;
case EZGameMarshaller.GET_COOKIE:
((EZGameProvider)provider).getCookie(
source,
((Integer)args[0]).intValue(), (InvocationService.InvocationListener)args[1]
);
return;
case EZGameMarshaller.GET_FROM_COLLECTION: case EZGameMarshaller.GET_FROM_COLLECTION:
((EZGameProvider)provider).getFromCollection( ((EZGameProvider)provider).getFromCollection(
source, source,
@@ -98,6 +105,13 @@ public class EZGameDispatcher extends InvocationDispatcher
); );
return; return;
case EZGameMarshaller.SET_COOKIE:
((EZGameProvider)provider).setCookie(
source,
(byte[])args[0], (InvocationService.InvocationListener)args[1]
);
return;
case EZGameMarshaller.SET_PROPERTY: case EZGameMarshaller.SET_PROPERTY:
((EZGameProvider)provider).setProperty( ((EZGameProvider)provider).setProperty(
source, source,
@@ -5,11 +5,15 @@ package com.threerings.ezgame.server;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import com.samskivert.util.ArrayIntSet;
import com.samskivert.util.ArrayUtil; import com.samskivert.util.ArrayUtil;
import com.samskivert.util.CollectionUtil; import com.samskivert.util.CollectionUtil;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.Interval; import com.samskivert.util.Interval;
import com.samskivert.util.RandomUtil; import com.samskivert.util.RandomUtil;
import com.samskivert.util.ResultListener;
import com.threerings.util.Name; import com.threerings.util.Name;
@@ -17,6 +21,7 @@ import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationCodes; import com.threerings.presents.data.InvocationCodes;
import com.threerings.presents.dobj.AccessController; import com.threerings.presents.dobj.AccessController;
import com.threerings.presents.dobj.DObjectManager; import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.DSet;
import com.threerings.presents.dobj.MessageEvent; import com.threerings.presents.dobj.MessageEvent;
import com.threerings.presents.client.InvocationService; import com.threerings.presents.client.InvocationService;
import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationException;
@@ -30,9 +35,13 @@ import com.threerings.parlor.game.server.GameManager;
import com.threerings.parlor.turn.server.TurnGameManager; import com.threerings.parlor.turn.server.TurnGameManager;
import com.threerings.ezgame.data.EZGameConfig;
import com.threerings.ezgame.data.EZGameObject; import com.threerings.ezgame.data.EZGameObject;
import com.threerings.ezgame.data.EZGameMarshaller; import com.threerings.ezgame.data.EZGameMarshaller;
import com.threerings.ezgame.data.PropertySetEvent; import com.threerings.ezgame.data.PropertySetEvent;
import com.threerings.ezgame.data.UserCookie;
import static com.threerings.ezgame.server.Log.log;
/** /**
* A manager for "ez" games. * A manager for "ez" games.
@@ -238,6 +247,90 @@ public class EZGameManager extends GameManager
} }
} }
// from EZGameProvider
public void getCookie (
ClientObject caller, final int playerIndex,
InvocationService.InvocationListener listener)
throws InvocationException
{
GameCookieManager gcm = getCookieManager();
if (_gameObj.userCookies.containsKey(playerIndex)) {
// already loaded: we do nothing
return;
}
if (_cookieLookups == null) {
_cookieLookups = new ArrayIntSet();
}
// we only start looking up the cookie if nobody else already is
if (!_cookieLookups.contains(playerIndex)) {
gcm.getCookie(getPersistentGameId(), getPlayer(playerIndex),
new ResultListener<byte[]>() {
public void requestCompleted (byte[] result) {
// Result may be null: that's ok, it means
// we've looked up the user's nonexistant cookie.
// Only set the cookie if the playerIndex is
// still in the lookup set, otherwise they left!
if (_cookieLookups.remove(playerIndex) &&
_gameObj.isActive()) {
_gameObj.addToUserCookies(
new UserCookie(playerIndex, result));
}
}
public void requestFailed (Exception cause) {
log.warning("Unable to retrieve cookie " +
"[cause=" + cause + "].");
requestCompleted(null);
}
});
// indicate that we're looking up a cookie
_cookieLookups.add(playerIndex);
}
}
// from EZGameProvider
public void setCookie (
ClientObject caller, byte[] value,
InvocationService.InvocationListener listener)
throws InvocationException
{
int playerIndex = getPresentPlayerIndex(caller.getOid());
if (playerIndex == -1) {
throw new InvocationException(ACCESS_DENIED);
}
GameCookieManager gcm = getCookieManager();
UserCookie cookie = new UserCookie(playerIndex, value);
if (_gameObj.userCookies.containsKey(playerIndex)) {
_gameObj.updateUserCookies(cookie);
} else {
_gameObj.addToUserCookies(cookie);
}
gcm.setCookie(getPersistentGameId(), caller, value);
}
/**
* Get the cookie manager, and do a bit of other setup.
*/
protected GameCookieManager getCookieManager ()
throws InvocationException
{
GameCookieManager gcm = GameCookieManager.getInstance();
if (gcm == null) {
log.warning("GameCookieManager not initialized.");
throw new InvocationException(INTERNAL_ERROR);
}
if (_gameObj.userCookies == null) {
// lazy-init this
_gameObj.setUserCookies(new DSet<UserCookie>());
}
return gcm;
}
/** /**
* Helper method to send a private message to the specified player * Helper method to send a private message to the specified player
* index (must already be verified). * index (must already be verified).
@@ -266,6 +359,19 @@ public class EZGameManager extends GameManager
new PropertySetEvent(_gameObj.getOid(), propName, value, index)); new PropertySetEvent(_gameObj.getOid(), propName, value, index));
} }
/**
* Get the game id of this ezgame, as set in the config.
*/
protected int getPersistentGameId ()
throws InvocationException
{
int id = ((EZGameConfig) _config).persistentGameId;
if (id == 0) {
throw new InvocationException("Persistent game id not set.");
}
return id;
}
/** /**
* Validate that the specified user has access to do things in the game. * Validate that the specified user has access to do things in the game.
*/ */
@@ -328,6 +434,22 @@ public class EZGameManager extends GameManager
super.gameDidEnd(); super.gameDidEnd();
} }
@Override
protected void playerGameDidEnd (int pidx)
{
super.playerGameDidEnd(pidx);
// kill any of their cookies
if (_gameObj.userCookies != null &&
_gameObj.userCookies.containsKey(pidx)) {
_gameObj.removeFromUserCookies(pidx);
}
// halt the loading of their cookie, if in progress
if (_cookieLookups != null) {
_cookieLookups.remove(pidx);
}
}
@Override @Override
protected void assignWinners (boolean[] winners) protected void assignWinners (boolean[] winners)
{ {
@@ -411,15 +533,21 @@ public class EZGameManager extends GameManager
/** Our turn delegate. */ /** Our turn delegate. */
protected EZGameTurnDelegate _turnDelegate; protected EZGameTurnDelegate _turnDelegate;
/** The array of winners, after the user has filled it in. */
protected int[] _winnerIndexes;
/** The map of collections, lazy-initialized. */ /** The map of collections, lazy-initialized. */
protected HashMap<String, ArrayList<byte[]>> _collections; protected HashMap<String, ArrayList<byte[]>> _collections;
/** The map of tickers, lazy-initialized. */ /** The map of tickers, lazy-initialized. */
protected HashMap<String, Ticker> _tickers; protected HashMap<String, Ticker> _tickers;
/** Tracks which cookies are currently being retrieved from the db. */
protected ArrayIntSet _cookieLookups;
// /** User tokens, lazy-initialized. */
// protected HashIntMap<HashSet<String>> _tokens;
/** The array of winners, after the user has filled it in. */
protected int[] _winnerIndexes;
/** The minimum delay a ticker can have. */ /** The minimum delay a ticker can have. */
protected static final int MIN_TICKER_DELAY = 50; protected static final int MIN_TICKER_DELAY = 50;
@@ -51,6 +51,12 @@ public interface EZGameProvider extends InvocationProvider
public void endTurn (ClientObject caller, int arg1, InvocationService.InvocationListener arg2) public void endTurn (ClientObject caller, int arg1, InvocationService.InvocationListener arg2)
throws InvocationException; throws InvocationException;
/**
* Handles a {@link EZGameService#getCookie} request.
*/
public void getCookie (ClientObject caller, int arg1, InvocationService.InvocationListener arg2)
throws InvocationException;
/** /**
* Handles a {@link EZGameService#getFromCollection} request. * Handles a {@link EZGameService#getFromCollection} request.
*/ */
@@ -69,6 +75,12 @@ public interface EZGameProvider extends InvocationProvider
public void sendMessage (ClientObject caller, String arg1, Object arg2, int arg3, InvocationService.InvocationListener arg4) public void sendMessage (ClientObject caller, String arg1, Object arg2, int arg3, InvocationService.InvocationListener arg4)
throws InvocationException; throws InvocationException;
/**
* Handles a {@link EZGameService#setCookie} request.
*/
public void setCookie (ClientObject caller, byte[] arg1, InvocationService.InvocationListener arg2)
throws InvocationException;
/** /**
* Handles a {@link EZGameService#setProperty} request. * Handles a {@link EZGameService#setProperty} request.
*/ */
@@ -0,0 +1,142 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/vilya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.ezgame.server;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.RepositoryListenerUnit;
import com.samskivert.util.Invoker;
import com.samskivert.util.ResultListener;
import com.threerings.presents.data.ClientObject;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.ezgame.server.persist.GameCookieRepository;
import static com.threerings.ezgame.server.Log.log;
/**
* Manages access to game cookies.
*/
public class GameCookieManager
{
/**
* An interface for identifying users.
*/
public interface UserIdentifier
{
/** Return the persistent user id for the specified player,
* or 0 if they're not a valid user, or a guest,
* or something like that (they'll have no cookies). */
public int getUserId (ClientObject clientObj);
}
/**
* Called to set up game cookie services for the server.
*/
public static void init (ConnectionProvider conprov, UserIdentifier ider)
throws PersistenceException
{
_singleton = new GameCookieManager(conprov, ider);
}
/**
* Get an instance of the GameCookieManager.
*/
public static GameCookieManager getInstance ()
{
return _singleton;
}
/**
* Protected constructor.
*/
protected GameCookieManager
(ConnectionProvider conprov, UserIdentifier identifier)
throws PersistenceException
{
_repo = new GameCookieRepository(conprov);
_identifier = identifier;
if (_identifier == null) {
throw new IllegalArgumentException(
"UserIdentifier must be non-null");
}
}
/**
* Get the specified user's cookie.
*/
public void getCookie (
final int gameId, ClientObject cliObj, ResultListener<byte[]> rl)
{
final int userId = _identifier.getUserId(cliObj);
if (userId == 0) {
rl.requestCompleted(null);
return;
}
CrowdServer.invoker.postUnit(
new RepositoryListenerUnit<byte[]>("getGameCookie", rl) {
public byte[] invokePersistResult ()
throws PersistenceException
{
return _repo.getCookie(gameId, userId);
}
});
}
/**
* Set the specified user's cookie.
*/
public void setCookie (
final int gameId, ClientObject cliObj, final byte[] cookie)
{
final int userId = _identifier.getUserId(cliObj);
if (userId == 0) {
// fail to save, silently
return;
}
CrowdServer.invoker.postUnit(new Invoker.Unit("setGameCookie") {
public boolean invoke ()
{
try {
_repo.setCookie(gameId, userId, cookie);
} catch (PersistenceException pe) {
log.warning("Unable to save game cookie [pe=" + pe + "].");
}
return false;
}
});
}
/** Our repository. */
protected GameCookieRepository _repo;
/** The entity we ask to identify users. */
protected UserIdentifier _identifier;
/** A reference to the single GameCookieManager instantiated. */
protected static GameCookieManager _singleton;
}
@@ -0,0 +1,33 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/vilya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.ezgame.server;
import java.util.logging.Logger;
/**
* Contains the log object.
*/
public class Log
{
/** We dispatch our log messages through this logger. */
public static Logger log = Logger.getLogger("com.threerings.ezgame");
}
@@ -0,0 +1,133 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/vilya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.ezgame.server.persist;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.JORARepository;
import com.samskivert.jdbc.SimpleRepository;
import com.samskivert.jdbc.TransitionRepository;
/**
* Provides storage services for user cookies used in games.
*/
public class GameCookieRepository extends SimpleRepository
{
/** The database identifier used when establishing a connection. */
public static final String COOKIE_DB_IDENT = "gameCookiedb";
public GameCookieRepository (ConnectionProvider conprov)
throws PersistenceException
{
super(conprov, COOKIE_DB_IDENT);
maintenance("analyze", "GAME_COOKIES");
}
@Override
protected void migrateSchema (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
super.migrateSchema(conn, liaison);
JDBCUtil.createTableIfMissing(conn, "GAME_COOKIES", new String[] {
"GAME_ID integer not null",
"USER_ID integer not null",
"COOKIE blob not null",
"primary key (GAME_ID, USER_ID)" }, "");
}
/**
* Get the specified game cookie, or null if none.
*/
public byte[] getCookie (final int gameId, final int userId)
throws PersistenceException
{
return execute(new Operation<byte[]>() {
public byte[] invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
Statement stmt = conn.createStatement();
try {
ResultSet rs = stmt.executeQuery("select COOKIE " +
"from GAME_COOKIES where GAME_ID=" + gameId +
" and USER_ID=" + userId);
if (rs.next()) {
return rs.getBytes(1);
}
return null;
} finally {
JDBCUtil.close(stmt);
}
}
});
}
/**
* Set the specified user's game cookie.
*/
public void setCookie (
final int gameId, final int userId, final byte[] cookie)
throws PersistenceException
{
executeUpdate(new Operation<Void>() {
public Void invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
if (cookie == null) {
Statement stmt = conn.createStatement();
try {
stmt.executeUpdate("delete from GAME_COOKIES" +
" where GAME_ID=" + gameId +
" and USER_ID=" + userId);
return null;
} finally {
JDBCUtil.close(stmt);
}
} else {
PreparedStatement stmt = conn.prepareStatement(
"update GAME_COOKIES set COOKIE=?" +
" where GAME_ID=" + gameId +
" and USER_ID=" + userId);
try {
stmt.setBytes(1, cookie);
stmt.executeUpdate();
return null;
} finally {
JDBCUtil.close(stmt);
}
}
}
});
}
}