diff --git a/src/as/com/threerings/ezgame/EZGame.as b/src/as/com/threerings/ezgame/EZGame.as
index 8a557be8..56b0e074 100644
--- a/src/as/com/threerings/ezgame/EZGame.as
+++ b/src/as/com/threerings/ezgame/EZGame.as
@@ -178,5 +178,42 @@ public interface EZGame
* End the game. The specified player indexes are winners!
*/
function endGame (winnerIndex :int, ... rest) :void;
+
+// function getCurrentRoom () :int;
+//
+// function sendPlayerToRoom (playerIndex :int, room :int) :void
+
+ /**
+ * Get the user-specific game data for the specified user. The
+ * first time this is requested per game instance it will be retrieved
+ * from the database. After that, it will be returned from memory.
+ */
+ function getUserCookie (playerIndex :int, callback :Function) :void;
+
+ /**
+ * Store persistent data that can later be retrieved by an instance
+ * of this game. The maximum size of this data is 4096 bytes AFTER
+ * AMF3 encoding.
+ *
+ * Note: there is no playerIndex parameter because a cookie may only
+ * be stored for the current player.
+ *
+ * @return false if the cookie could not be encoded to 4096 bytes
+ * or less; true if the cookie is going to try to be saved. There is
+ * no guarantee it will be saved and no way to find out if it failed,
+ * but if it fails it will be because the shit hit the fan so hard that
+ * there's nothing you can do anyway.
+ */
+ function setUserCookie (cookie :Object) :Boolean;
+//
+// /**
+// * Check to see if the user has the specified token.
+// */
+// function checkUserToken (token :String, callback :Function) :void;
+//
+// /**
+// * Take the user to a purchase page.
+// */
+// function purchaseUserToken (token :String, callback :Function) :void;
}
}
diff --git a/src/as/com/threerings/ezgame/client/EZGameService.as b/src/as/com/threerings/ezgame/client/EZGameService.as
index d8cac4ed..bdb5996f 100644
--- a/src/as/com/threerings/ezgame/client/EZGameService.as
+++ b/src/as/com/threerings/ezgame/client/EZGameService.as
@@ -21,6 +21,7 @@
package com.threerings.ezgame.client {
+import flash.utils.ByteArray;
import com.threerings.ezgame.client.EZGameService;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
@@ -42,6 +43,9 @@ public interface EZGameService extends InvocationService
// from Java interface EZGameService
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
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
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
function setProperty (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :InvocationService_InvocationListener) :void;
diff --git a/src/as/com/threerings/ezgame/client/GameObjectImpl.as b/src/as/com/threerings/ezgame/client/GameObjectImpl.as
index 64686510..f9707edd 100644
--- a/src/as/com/threerings/ezgame/client/GameObjectImpl.as
+++ b/src/as/com/threerings/ezgame/client/GameObjectImpl.as
@@ -7,6 +7,7 @@ import flash.events.EventDispatcher;
import flash.utils.IExternalizable;
import flash.utils.ByteArray;
+import flash.utils.Dictionary;
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.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.util.CrowdContext;
import com.threerings.ezgame.data.EZGameObject;
import com.threerings.ezgame.data.PropertySetEvent;
+import com.threerings.ezgame.data.UserCookie;
import com.threerings.ezgame.util.EZObjectMarshaller;
import com.threerings.ezgame.EZGame;
@@ -41,6 +47,8 @@ public class GameObjectImpl extends EventDispatcher
_ctx = ctx;
_ezObj = ezObj;
_gameData = new GameData(this, _ezObj.getUserProps());
+
+ _ezObj.addListener(new SetAdapter(entryAdded, entryUpdated, null));
}
// from EZGame
@@ -259,6 +267,52 @@ public class GameObjectImpl extends EventDispatcher
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
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 _ezObj :EZGameObject;
protected var _gameData :GameData;
+
+ /** playerIndex -> callback functions waiting for the cookie. */
+ protected var _cookieCallbacks :Dictionary;
+
+ protected static const MAX_USER_COOKIE :int = 4096;
}
}
diff --git a/src/as/com/threerings/ezgame/data/EZGameConfig.as b/src/as/com/threerings/ezgame/data/EZGameConfig.as
index fe450388..f8a1a44f 100644
--- a/src/as/com/threerings/ezgame/data/EZGameConfig.as
+++ b/src/as/com/threerings/ezgame/data/EZGameConfig.as
@@ -29,6 +29,10 @@ public class EZGameConfig extends GameConfig
// For now, the configData is either a classname or url.
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 ()
{
// nothing needed
@@ -89,6 +93,7 @@ public class EZGameConfig extends GameConfig
super.readObject(ins);
configData = (ins.readField(String) as String);
+ persistentGameId = ins.readInt();
}
// from interface Streamable
@@ -97,6 +102,7 @@ public class EZGameConfig extends GameConfig
super.writeObject(out);
out.writeField(configData);
+ out.writeInt(persistentGameId);
}
}
}
diff --git a/src/as/com/threerings/ezgame/data/EZGameMarshaller.as b/src/as/com/threerings/ezgame/data/EZGameMarshaller.as
index 71ee6b7d..1fcb1023 100644
--- a/src/as/com/threerings/ezgame/data/EZGameMarshaller.as
+++ b/src/as/com/threerings/ezgame/data/EZGameMarshaller.as
@@ -21,6 +21,7 @@
package com.threerings.ezgame.data {
+import flash.utils.ByteArray;
import com.threerings.util.*; // for Float, Integer, etc.
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. */
- public static const GET_FROM_COLLECTION :int = 4;
+ public static const GET_FROM_COLLECTION :int = 5;
// from interface EZGameService
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. */
- public static const MERGE_COLLECTION :int = 5;
+ public static const MERGE_COLLECTION :int = 6;
// from interface EZGameService
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. */
- public static const SEND_MESSAGE :int = 6;
+ public static const SEND_MESSAGE :int = 7;
// from interface EZGameService
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. */
- public static const SET_PROPERTY :int = 7;
+ public static const SET_PROPERTY :int = 9;
// from interface EZGameService
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. */
- public static const SET_TICKER :int = 8;
+ public static const SET_TICKER :int = 10;
// from interface EZGameService
public function setTicker (arg1 :Client, arg2 :String, arg3 :int, arg4 :InvocationService_InvocationListener) :void
diff --git a/src/as/com/threerings/ezgame/data/EZGameObject.as b/src/as/com/threerings/ezgame/data/EZGameObject.as
index e22994c7..77b0710c 100644
--- a/src/as/com/threerings/ezgame/data/EZGameObject.as
+++ b/src/as/com/threerings/ezgame/data/EZGameObject.as
@@ -10,6 +10,8 @@ import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.TypedArray;
+import com.threerings.presents.dobj.DSet;
+
import com.threerings.parlor.game.data.GameObject;
import com.threerings.parlor.turn.data.TurnGameObject;
@@ -31,6 +33,9 @@ public class EZGameObject extends GameObject
/** The field name of the turnHolder field. */
public static const TURN_HOLDER :String = "turnHolder";
+ /** The field name of the userCookies field. */
+ public static const USER_COOKIES :String = "userCookies";
+
/** The field name of the ezGameService field. */
public static const EZ_GAME_SERVICE :String = "ezGameService";
// AUTO-GENERATED: FIELDS END
@@ -38,6 +43,9 @@ public class EZGameObject extends GameObject
/** The current turn holder. */
public var turnHolder :Name;
+ /** A set of loaded user cookies. */
+ public var userCookies :DSet;
+
/** The service interface for requesting special things from the server. */
public var ezGameService :EZGameMarshaller;
@@ -134,6 +142,7 @@ public class EZGameObject extends GameObject
// first read any regular bits
turnHolder = (ins.readObject() as Name);
+ userCookies = (ins.readObject() as DSet);
ezGameService = (ins.readObject() as EZGameMarshaller);
// then user properties
diff --git a/src/as/com/threerings/ezgame/data/UserCookie.as b/src/as/com/threerings/ezgame/data/UserCookie.as
new file mode 100644
index 00000000..a7a7a574
--- /dev/null
+++ b/src/as/com/threerings/ezgame/data/UserCookie.as
@@ -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();
+ }
+}
+}
diff --git a/src/as/com/threerings/ezgame/util/EZObjectMarshaller.as b/src/as/com/threerings/ezgame/util/EZObjectMarshaller.as
index 326fccbc..518b4cb1 100644
--- a/src/as/com/threerings/ezgame/util/EZObjectMarshaller.as
+++ b/src/as/com/threerings/ezgame/util/EZObjectMarshaller.as
@@ -7,6 +7,8 @@ import flash.system.ApplicationDomain;
import flash.utils.ByteArray;
import flash.utils.Endian;
+import com.threerings.util.StringUtil;
+
import com.threerings.io.TypedArray;
/**
@@ -45,6 +47,8 @@ public class EZObjectMarshaller
bytes.endian = Endian.BIG_ENDIAN;
bytes.objectEncoding = ObjectEncoding.AMF3;
bytes.writeObject(obj);
+ Log.getLog(EZObjectMarshaller).info(
+ "The encoded bytes are: " + StringUtil.hexlate(bytes));
return bytes;
}
diff --git a/src/java/com/threerings/ezgame/client/EZGameService.java b/src/java/com/threerings/ezgame/client/EZGameService.java
index d85a4e62..5c8ef239 100644
--- a/src/java/com/threerings/ezgame/client/EZGameService.java
+++ b/src/java/com/threerings/ezgame/client/EZGameService.java
@@ -83,4 +83,16 @@ public interface EZGameService extends InvocationService
public void setTicker (
Client client, String tickerName, int msOfDelay,
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);
}
diff --git a/src/java/com/threerings/ezgame/data/EZGameConfig.java b/src/java/com/threerings/ezgame/data/EZGameConfig.java
index a40bfa0b..759248ee 100644
--- a/src/java/com/threerings/ezgame/data/EZGameConfig.java
+++ b/src/java/com/threerings/ezgame/data/EZGameConfig.java
@@ -24,6 +24,10 @@ public class EZGameConfig extends GameConfig
// For now, the configData is either a classname or url.
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
public String getBundleName ()
{
diff --git a/src/java/com/threerings/ezgame/data/EZGameMarshaller.java b/src/java/com/threerings/ezgame/data/EZGameMarshaller.java
index c9146f9a..36550d63 100644
--- a/src/java/com/threerings/ezgame/data/EZGameMarshaller.java
+++ b/src/java/com/threerings/ezgame/data/EZGameMarshaller.java
@@ -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. */
- public static final int GET_FROM_COLLECTION = 4;
+ public static final int GET_FROM_COLLECTION = 5;
// from interface EZGameService
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. */
- public static final int MERGE_COLLECTION = 5;
+ public static final int MERGE_COLLECTION = 6;
// from interface EZGameService
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. */
- public static final int SEND_MESSAGE = 6;
+ public static final int SEND_MESSAGE = 7;
// from interface EZGameService
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. */
- public static final int SET_PROPERTY = 7;
+ public static final int SET_PROPERTY = 9;
// from interface EZGameService
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. */
- public static final int SET_TICKER = 8;
+ public static final int SET_TICKER = 10;
// from interface EZGameService
public void setTicker (Client arg1, String arg2, int arg3, InvocationService.InvocationListener arg4)
diff --git a/src/java/com/threerings/ezgame/data/EZGameObject.java b/src/java/com/threerings/ezgame/data/EZGameObject.java
index 9d5e8ec1..f379e82b 100644
--- a/src/java/com/threerings/ezgame/data/EZGameObject.java
+++ b/src/java/com/threerings/ezgame/data/EZGameObject.java
@@ -14,6 +14,8 @@ import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;
+import com.threerings.presents.dobj.DSet;
+
import com.threerings.parlor.game.data.GameObject;
import com.threerings.parlor.turn.data.TurnGameObject;
@@ -38,6 +40,9 @@ public class EZGameObject extends GameObject
/** The field name of the turnHolder field. */
public static final String TURN_HOLDER = "turnHolder";
+ /** The field name of the userCookies field. */
+ public static final String USER_COOKIES = "userCookies";
+
/** The field name of the ezGameService field. */
public static final String EZ_GAME_SERVICE = "ezGameService";
// AUTO-GENERATED: FIELDS END
@@ -45,6 +50,9 @@ public class EZGameObject extends GameObject
/** The current turn holder. */
public Name turnHolder;
+ /** A set of loaded user cookies. */
+ public DSet userCookies;
+
/** The service interface for requesting special things from the server. */
public EZGameMarshaller ezGameService;
@@ -139,6 +147,54 @@ public class EZGameObject extends GameObject
this.turnHolder = value;
}
+ /**
+ * Requests that the specified entry be added to the
+ * userCookies 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 userCookies 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
+ * userCookies 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 userCookies 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 value)
+ {
+ requestAttributeChange(USER_COOKIES, value, this.userCookies);
+ @SuppressWarnings("unchecked") DSet clone =
+ (value == null) ? null : value.typedClone();
+ this.userCookies = clone;
+ }
+
/**
* Requests that the ezGameService field be set to the
* specified value. The local value will be updated immediately and an
diff --git a/src/java/com/threerings/ezgame/data/UserCookie.java b/src/java/com/threerings/ezgame/data/UserCookie.java
new file mode 100644
index 00000000..920c9c23
--- /dev/null
+++ b/src/java/com/threerings/ezgame/data/UserCookie.java
@@ -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;
+ }
+}
diff --git a/src/java/com/threerings/ezgame/server/EZGameDispatcher.java b/src/java/com/threerings/ezgame/server/EZGameDispatcher.java
index 63406994..155851fb 100644
--- a/src/java/com/threerings/ezgame/server/EZGameDispatcher.java
+++ b/src/java/com/threerings/ezgame/server/EZGameDispatcher.java
@@ -77,6 +77,13 @@ public class EZGameDispatcher extends InvocationDispatcher
);
return;
+ case EZGameMarshaller.GET_COOKIE:
+ ((EZGameProvider)provider).getCookie(
+ source,
+ ((Integer)args[0]).intValue(), (InvocationService.InvocationListener)args[1]
+ );
+ return;
+
case EZGameMarshaller.GET_FROM_COLLECTION:
((EZGameProvider)provider).getFromCollection(
source,
@@ -98,6 +105,13 @@ public class EZGameDispatcher extends InvocationDispatcher
);
return;
+ case EZGameMarshaller.SET_COOKIE:
+ ((EZGameProvider)provider).setCookie(
+ source,
+ (byte[])args[0], (InvocationService.InvocationListener)args[1]
+ );
+ return;
+
case EZGameMarshaller.SET_PROPERTY:
((EZGameProvider)provider).setProperty(
source,
diff --git a/src/java/com/threerings/ezgame/server/EZGameManager.java b/src/java/com/threerings/ezgame/server/EZGameManager.java
index 51e875c1..60d5f1c6 100644
--- a/src/java/com/threerings/ezgame/server/EZGameManager.java
+++ b/src/java/com/threerings/ezgame/server/EZGameManager.java
@@ -5,11 +5,15 @@ package com.threerings.ezgame.server;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import com.samskivert.util.ArrayIntSet;
import com.samskivert.util.ArrayUtil;
import com.samskivert.util.CollectionUtil;
+import com.samskivert.util.HashIntMap;
import com.samskivert.util.Interval;
import com.samskivert.util.RandomUtil;
+import com.samskivert.util.ResultListener;
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.dobj.AccessController;
import com.threerings.presents.dobj.DObjectManager;
+import com.threerings.presents.dobj.DSet;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.presents.client.InvocationService;
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.ezgame.data.EZGameConfig;
import com.threerings.ezgame.data.EZGameObject;
import com.threerings.ezgame.data.EZGameMarshaller;
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.
@@ -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() {
+ 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());
+ }
+ return gcm;
+ }
+
/**
* Helper method to send a private message to the specified player
* index (must already be verified).
@@ -266,6 +359,19 @@ public class EZGameManager extends GameManager
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.
*/
@@ -328,6 +434,22 @@ public class EZGameManager extends GameManager
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
protected void assignWinners (boolean[] winners)
{
@@ -411,15 +533,21 @@ public class EZGameManager extends GameManager
/** Our turn delegate. */
protected EZGameTurnDelegate _turnDelegate;
- /** The array of winners, after the user has filled it in. */
- protected int[] _winnerIndexes;
-
/** The map of collections, lazy-initialized. */
protected HashMap> _collections;
/** The map of tickers, lazy-initialized. */
protected HashMap _tickers;
+ /** Tracks which cookies are currently being retrieved from the db. */
+ protected ArrayIntSet _cookieLookups;
+
+// /** User tokens, lazy-initialized. */
+// protected HashIntMap> _tokens;
+
+ /** The array of winners, after the user has filled it in. */
+ protected int[] _winnerIndexes;
+
/** The minimum delay a ticker can have. */
protected static final int MIN_TICKER_DELAY = 50;
diff --git a/src/java/com/threerings/ezgame/server/EZGameProvider.java b/src/java/com/threerings/ezgame/server/EZGameProvider.java
index da2e932b..94e2ef83 100644
--- a/src/java/com/threerings/ezgame/server/EZGameProvider.java
+++ b/src/java/com/threerings/ezgame/server/EZGameProvider.java
@@ -51,6 +51,12 @@ public interface EZGameProvider extends InvocationProvider
public void endTurn (ClientObject caller, int arg1, InvocationService.InvocationListener arg2)
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.
*/
@@ -69,6 +75,12 @@ public interface EZGameProvider extends InvocationProvider
public void sendMessage (ClientObject caller, String arg1, Object arg2, int arg3, InvocationService.InvocationListener arg4)
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.
*/
diff --git a/src/java/com/threerings/ezgame/server/GameCookieManager.java b/src/java/com/threerings/ezgame/server/GameCookieManager.java
new file mode 100644
index 00000000..1524d740
--- /dev/null
+++ b/src/java/com/threerings/ezgame/server/GameCookieManager.java
@@ -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 rl)
+ {
+ final int userId = _identifier.getUserId(cliObj);
+ if (userId == 0) {
+ rl.requestCompleted(null);
+ return;
+ }
+ CrowdServer.invoker.postUnit(
+ new RepositoryListenerUnit("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;
+
+}
diff --git a/src/java/com/threerings/ezgame/server/Log.java b/src/java/com/threerings/ezgame/server/Log.java
new file mode 100644
index 00000000..05247bfb
--- /dev/null
+++ b/src/java/com/threerings/ezgame/server/Log.java
@@ -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");
+}
diff --git a/src/java/com/threerings/ezgame/server/persist/GameCookieRepository.java b/src/java/com/threerings/ezgame/server/persist/GameCookieRepository.java
new file mode 100644
index 00000000..fdbaefc1
--- /dev/null
+++ b/src/java/com/threerings/ezgame/server/persist/GameCookieRepository.java
@@ -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() {
+ 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() {
+ 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);
+ }
+ }
+ }
+ });
+ }
+}