For EZ property sets, added a setter that performs an "atomic" test-and-set:

it's atomic in the sense that a single server event will test the variable, 
and only set it if the previous value was null (i.e. didn't exist). 
This allows for a level of elementary synchronization between the clients.

The new function on EZGameControl is:
  _gameCtrl.testAndSet (propertyName, newValue[, index])

I hoped to generalize this to test against arbitrary values, but that's 
significantly harder, since properties accept numerous types as values,
and those can have different representations on the client and the server.
So it remains a check against null until we need to generalize it. :)





git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@193 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
Robert Zubeck
2007-02-15 21:55:49 +00:00
parent 720a409185
commit bdfcd156ab
17 changed files with 136 additions and 91 deletions
+15 -2
View File
@@ -153,11 +153,24 @@ public class EZGameControl extends EventDispatcher
}
/**
* Set a property that will be distributed.
* Set a property that will be distributed.
*/
public function set (propName :String, value :Object, index :int = -1) :void
{
callEZCode("setProperty_v1", propName, value, index);
callEZCode("setProperty_v2", propName, value, index, false);
}
/**
* Set a property that will be distributed, but only if it was null before.
*
* The operation is 'atomic', in the sense that testing and setting take place
* during the same server event. In comparison, a separate 'get' followed by
* a 'set' operation would involve two events with two network round-trips,
* and no guarantee that the value won't change between the events.
*/
public function testAndSet (propName :String, value :Object, index :int = -1) :void
{
callEZCode("setProperty_v2", propName, value, index, true);
}
/**
@@ -67,7 +67,7 @@ public interface EZGameService extends InvocationService
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;
function setProperty (arg1 :Client, arg2 :String, arg3 :Object, arg4 :int, arg5 :Boolean, arg6 :InvocationService_InvocationListener) :void;
// from Java interface EZGameService
function setTicker (arg1 :Client, arg2 :String, arg3 :int, arg4 :InvocationService_InvocationListener) :void;
@@ -72,7 +72,7 @@ public class GameControlBackend
{
_ctx = ctx;
_ezObj = ezObj;
_gameData = new GameData(setProperty_v1, _ezObj.getUserProps());
_gameData = new GameData(setProperty_v2, _ezObj.getUserProps());
_ezObj.addListener(this);
_ctx.getClient().getClientObject().addListener(_userListener);
@@ -136,7 +136,7 @@ public class GameControlBackend
o["gameData"] = _gameData;
// functions
o["setProperty_v1"] = setProperty_v1;
o["setProperty_v2"] = setProperty_v2;
o["mergeCollection_v1"] = mergeCollection_v1;
o["setTicker_v1"] = setTicker_v1;
o["sendChat_v1"] = sendChat_v1;
@@ -163,18 +163,18 @@ public class GameControlBackend
o["getPlayers_v1"] = getPlayers_v1;
}
public function setProperty_v1 (
propName :String, value :Object, index :int) :void
public function setProperty_v2 (
propName :String, value :Object, index :int, testAndSet :Boolean) :void
{
validatePropertyChange(propName, value, index);
var encoded :Object = EZObjectMarshaller.encode(value, (index == -1));
_ezObj.ezGameService.setProperty(
_ctx.getClient(), propName, encoded, index,
_ctx.getClient(), propName, encoded, index, testAndSet,
createLoggingConfirmListener("setProperty"));
// set it immediately in the game object
_ezObj.applyPropertySet(propName, value, index);
_ezObj.applyPropertySet(propName, value, index, testAndSet);
}
public function mergeCollection_v1 (
@@ -67,7 +67,7 @@ public class GameData extends Proxy
override flash_proxy function setProperty (propName :*, value :*) :void
{
_propSetFn(String(propName), value, -1);
_propSetFn(String(propName), value, -1, false);
}
override flash_proxy function deleteProperty (propName :*) :Boolean
@@ -178,12 +178,12 @@ public class EZGameMarshaller extends InvocationMarshaller
public static const SET_PROPERTY :int = 11;
// 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 :Boolean, arg6 :InvocationService_InvocationListener) :void
{
var listener5 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener5.listener = arg5;
var listener6 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener6.listener = arg6;
sendRequest(arg1, SET_PROPERTY, [
arg2, arg3, Integer.valueOf(arg4), listener5
arg2, arg3, Integer.valueOf(arg4), langBoolean.valueOf(arg5), listener6
]);
}
@@ -98,26 +98,30 @@ public class EZGameObject extends GameObject
* @return the old value
*/
public function applyPropertySet (
propName :String, value :Object, index :int) :Object
propName :String, value :Object, index :int, testAndSet :Boolean)
:Object
{
var oldValue :Object = _props[propName];
if (index >= 0) {
// set an array element
var arr :Array = (oldValue as Array);
if (arr == null) {
arr = [];
_props[propName] = arr;
if ((testAndSet && oldValue == null) || ! testAndSet)
{
if (index >= 0) {
// set an array element
var arr :Array = (oldValue as Array);
if (arr == null) {
arr = [];
_props[propName] = arr;
}
oldValue = arr[index];
arr[index] = value;
} else if (value != null) {
// normal property set
_props[propName] = value;
} else {
// remove a property
delete _props[propName];
}
oldValue = arr[index];
arr[index] = value;
} else if (value != null) {
// normal property set
_props[propName] = value;
} else {
// remove a property
delete _props[propName];
}
return oldValue;
}
@@ -35,7 +35,7 @@ public class PropertySetEvent extends NamedEvent
override public function applyToObject (target :DObject) :Boolean
{
_oldValue =
EZGameObject(target).applyPropertySet(_name, _data, _index);
EZGameObject(target).applyPropertySet(_name, _data, _index, _testAndSet);
return true;
}
@@ -69,6 +69,7 @@ public class PropertySetEvent extends NamedEvent
super.readObject(ins);
_index = ins.readInt();
_data = EZObjectMarshaller.decode(ins.readObject());
_testAndSet = ins.readBoolean();
}
// from interface Streamable
@@ -77,6 +78,7 @@ public class PropertySetEvent extends NamedEvent
super.writeObject(out);
out.writeInt(_index);
out.writeObject(_data);
out.writeBoolean(_testAndSet);
}
override protected function notifyListener (listener :Object) :void
@@ -99,6 +101,9 @@ public class PropertySetEvent extends NamedEvent
/** The client-side data that is assigned to this property. */
protected var _data :Object;
/** When true, the property will only be set if its previous value was null. */
protected var _testAndSet :Boolean;
/** The old value. */
protected var _oldValue :Object;
}