More progress.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3861 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -11,6 +11,8 @@ Design decisions
|
||||
- I am embracing flash's event distribution model because it saved me a bunch
|
||||
of work.
|
||||
|
||||
- We could use the setter methods on DObject properties to generate dobj
|
||||
events, but so far I haven't gone there.
|
||||
|
||||
|
||||
Notes
|
||||
|
||||
@@ -63,7 +63,7 @@ public class ObjectInputStream
|
||||
|
||||
var target :*;
|
||||
if (cmap.streamer === null) {
|
||||
var clazz :Class = flash.util.getClassByName(cmap.cname);
|
||||
var clazz :Class = flash.util.getClassByName(cmap.classname);
|
||||
target = new clazz();
|
||||
|
||||
} else {
|
||||
@@ -87,8 +87,7 @@ public class ObjectInputStream
|
||||
{
|
||||
// streamable objects
|
||||
if (streamer == null) {
|
||||
var sable :Streamable = (obj as Streamable);
|
||||
sable.readObject(this);
|
||||
obj.readObject(this); // obj is a Streamable.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +116,7 @@ public class ObjectInputStream
|
||||
public function defaultReadObject () :void
|
||||
//throws IOError
|
||||
{
|
||||
_streamer.readObject(_current, this, false);
|
||||
_streamer.readObject(_current, this);
|
||||
}
|
||||
|
||||
public function readBoolean () :Boolean
|
||||
|
||||
@@ -29,6 +29,7 @@ public class ObjectOutputStream
|
||||
// create a class mapping if we've not got one
|
||||
if (cmap === undefined) {
|
||||
var streamer :Streamer = Streamer.getStreamer(obj);
|
||||
// streamer may be null to indicate a Streamable object
|
||||
if (streamer === undefined) {
|
||||
// TODO
|
||||
trace("OMG, cannot stream ", cname);
|
||||
@@ -60,8 +61,7 @@ public class ObjectOutputStream
|
||||
{
|
||||
// if it's Streamable, it goes straight through
|
||||
if (streamer == null) {
|
||||
var sable :Streamable = (obj as Streamable);
|
||||
sable.writeObject(this);
|
||||
obj.writeObject(this); // obj is a Streamable
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class ObjectOutputStream
|
||||
}
|
||||
|
||||
// write the instance data
|
||||
_streamer.writeObject(_current, this, false)
|
||||
_streamer.writeObject(_current, this);
|
||||
}
|
||||
|
||||
public function writeBoolean (value :Boolean) :void
|
||||
|
||||
@@ -40,11 +40,9 @@ public class Streamer
|
||||
createStreamers();
|
||||
}
|
||||
|
||||
var s :Streamer;
|
||||
for (var ii :int = 0; ii < _streamers.lenght; ii++) {
|
||||
s = (_streamers[ii] as Streamer);
|
||||
if (s.isStreamerFor(obj)) {
|
||||
return s;
|
||||
if (_streamers[ii].isStreamerFor(obj)) {
|
||||
return _streamers[ii];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,11 +55,9 @@ public class Streamer
|
||||
createStreamers();
|
||||
}
|
||||
|
||||
var s :Streamer;
|
||||
for (var ii :int = 0; ii < _streamers.length; ii++) {
|
||||
s = (_streamers[ii] as Streamer);
|
||||
if (s.getJavaClassName() === jname) {
|
||||
return s;
|
||||
if (_streamers[ii].getJavaClassName() === jname) {
|
||||
return _streamers[ii];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,8 +109,12 @@ public class Communicator
|
||||
protected function inputFrameReceived (event :FrameAvailableEvent) :void
|
||||
{
|
||||
// convert the frame data into a message from the server
|
||||
_inStream.setSource(event.getFrameData());
|
||||
var frameData :ByteArray = event.getFrameData();
|
||||
_inStream.setSource(frameData);
|
||||
var msg :DownstreamMessage = _inStream.readObject();
|
||||
if (frameData.bytesAvailable > 0) {
|
||||
trace("OMG, we didn't fully read the frame. Surely there's a bug");
|
||||
}
|
||||
|
||||
if (_omgr != null) {
|
||||
// if we're logged on, then just do the normal thing
|
||||
@@ -119,7 +123,7 @@ public class Communicator
|
||||
}
|
||||
|
||||
// Otherwise, this would be the AuthResponse to our logon attempt.
|
||||
var rsp :AuthResponse = (msg as AuthResponse); // TODO: as correct?
|
||||
var rsp :AuthResponse = (msg as AuthResponse);
|
||||
var data :AuthResponseData = rsp.getData();
|
||||
if (data.code !== AuthResponseData.SUCCESS) {
|
||||
shutdown(new Error(data.code));
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.threerings.presents.client {
|
||||
|
||||
import com.threerings.presents.dobj.DObjectManager;
|
||||
|
||||
public class InvocationDirector
|
||||
{
|
||||
public function init (omgr :DObjectManager, cloid :int, client :Client) :void
|
||||
{
|
||||
if (_clobj != null) {
|
||||
trace("Zoiks, client object around during invmgr init!");
|
||||
cleanup();
|
||||
}
|
||||
|
||||
_omgr = omgr;
|
||||
_client = client;
|
||||
|
||||
// TODO : lots more
|
||||
}
|
||||
|
||||
public function cleanup () :void
|
||||
{
|
||||
_clobj = null;
|
||||
|
||||
// TODO: lots more
|
||||
}
|
||||
|
||||
protected var _omgr :DObjectManager;
|
||||
|
||||
protected var _client :Client;
|
||||
|
||||
protected var _clobj :ClientObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
/**
|
||||
* An attribute changed event is dispatched when a single attribute of a
|
||||
* distributed object has changed. It can also be constructed to request
|
||||
* an attribute change on an object and posted to the dobjmgr.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class AttributeChangedEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Returns the new value of the attribute.
|
||||
*/
|
||||
public function getValue () :*
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the attribute prior to the application of this
|
||||
* event.
|
||||
*/
|
||||
public function getOldValue () :*
|
||||
{
|
||||
return _oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this attribute change to the object.
|
||||
*/
|
||||
public override function applyToObject (target :DObject) :Boolean
|
||||
//throws ObjectAccessException
|
||||
{
|
||||
// if we have no old value, that means we're not running on the
|
||||
// master server and we have not already applied this attribute
|
||||
// change to the object, so we must grab the previous value and
|
||||
// actually apply the attribute change
|
||||
if (_oldValue === UNSET_OLD_ENTRY) {
|
||||
_oldValue = target[_name]; // fucking wack-ass language
|
||||
}
|
||||
target[_name] = _value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new attribute changed event on the specified target
|
||||
* object with the supplied attribute name and value. <em>Do not
|
||||
* construct these objects by hand.</em> Use {@link
|
||||
* DObject#changeAttribute} instead.
|
||||
*
|
||||
* @param targetOid the object id of the object whose attribute has
|
||||
* changed.
|
||||
* @param name the name of the attribute (data member) that has
|
||||
* changed.
|
||||
* @param value the new value of the attribute (in the case of
|
||||
* primitive types, the reflection-defined object-alternative is
|
||||
* used).
|
||||
*/
|
||||
protected function AttributeChangedEvent (
|
||||
targetOid :int, name :String, value :*, oldValue :*)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_value = value;
|
||||
_oldValue = oldValue;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function notifyListener (listener :*) :void
|
||||
{
|
||||
if (listener is AttributeChangeListener) {
|
||||
listener.attributeChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function toString (buf :StringBuilder) :void
|
||||
{
|
||||
buf.append("CHANGE:");
|
||||
super.toString(buf);
|
||||
buf.append(", value=", _value);
|
||||
}
|
||||
|
||||
protected var _value :*;
|
||||
protected var _oldValue :* = UNSET_OLD_ENTRY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
/**
|
||||
* The various listener interfaces (e.g. {@link EventListener}, {@link
|
||||
* AttributeChangeListener}, etc.) all extend this base interface so that
|
||||
* the distributed object can check to make sure when an object is adding
|
||||
* itself as a listener of some sort that it actually implements at least
|
||||
* one of the listener interfaces. Thus, all listener interfaces must
|
||||
* extend this one.
|
||||
*/
|
||||
public interface ChangeListener {
|
||||
// nada
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,13 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
public class DEvent extends Object
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
public class DEvent
|
||||
implements Streamable
|
||||
{
|
||||
public function DEvent ()
|
||||
{
|
||||
@@ -11,12 +18,91 @@ public class DEvent extends Object
|
||||
_toid = targetOid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the oid of the object that is the target of this event.
|
||||
*/
|
||||
public function getTargetOid () :int
|
||||
{
|
||||
return _toid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Some events are used only internally on the server and need not be
|
||||
* broadcast to subscribers, proxy or otherwise. Such events can
|
||||
* return true here and short-circuit the normal proxy event dispatch
|
||||
* mechanism.
|
||||
*/
|
||||
public function applyToObject (target :DObject) :void
|
||||
{
|
||||
trace("TODO: abstract methods?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Events with associated listener interfaces should implement this
|
||||
* function and notify the supplied listener if it implements their
|
||||
* event listening interface. For example, the {@link
|
||||
* AttributeChangedEvent} will notify listeners that implement {@link
|
||||
* AttributeChangeListener}.
|
||||
*/
|
||||
protected function notifyListener (listener :*) :void
|
||||
{
|
||||
// the default is to do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and returns a string representation of this event.
|
||||
*/
|
||||
public override function toString () :String
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[");
|
||||
toString(buf);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
out.writeInt(_toid);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
_toid = ins.readInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be overridden by derived classes (which should be sure
|
||||
* to call <code>super.toString()</code>) to append the derived class
|
||||
* specific event information to the string buffer.
|
||||
*/
|
||||
protected function toString (buf :StringBuilder) :void
|
||||
{
|
||||
buf.append("targetOid=", _toid);
|
||||
}
|
||||
|
||||
/** The oid of the object that is the target of this event. */
|
||||
protected var _toid :int;
|
||||
|
||||
protected static const UNSET_OLD_ENTRY :DSetEntry = new DummyEntry();
|
||||
}
|
||||
}
|
||||
|
||||
class DummyEntry implements DSetEntry
|
||||
{
|
||||
public function getKey () :Comparable
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
}
|
||||
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ package com.threerings.presents.dobj {
|
||||
|
||||
import flash.events.EventDispatcher;
|
||||
|
||||
import mx.collections.ArrayCollection;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
public class DObject extends EventDispatcher
|
||||
public class DObject // extends EventDispatcher
|
||||
implements Streamable
|
||||
{
|
||||
public function getOid ():int
|
||||
@@ -14,9 +16,152 @@ public class DObject extends EventDispatcher
|
||||
return _oid;
|
||||
}
|
||||
|
||||
public function getManager () :DObjectManager
|
||||
{
|
||||
return _omgr;
|
||||
}
|
||||
|
||||
public function addListener (listener :ChangeListener) :void
|
||||
{
|
||||
if (_listeners == null) {
|
||||
_listeners = new ArrayCollection();
|
||||
|
||||
} else if (_listeners.contains(listener)) {
|
||||
trace("Refusing repeat listener registration");
|
||||
return;
|
||||
}
|
||||
_listeners.addItem(listener);
|
||||
}
|
||||
|
||||
public function removeListener (listener :ChangeListener) :void
|
||||
{
|
||||
if (_listeners != null) {
|
||||
var dex :int = _listeners.getItemIndex(listener);
|
||||
if (dex != -1) {
|
||||
_listeners.removeItemAt(dex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function notifyListeners (event :DEvent) :void
|
||||
{
|
||||
if (_listeners == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var ii :int = 0; ii < _listeners.length; ii++) {
|
||||
var listener :* = _listeners.getItemAt(ii);
|
||||
try {
|
||||
event.notifyListener(listener);
|
||||
|
||||
if (listener is EventListener) {
|
||||
listener.eventReceived(event);
|
||||
}
|
||||
} catch (e :Error) {
|
||||
trace("Listener choked during notification");
|
||||
trace(e.getStackTrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function postMessage (name :String, args :Array) :void
|
||||
{
|
||||
postEvent(new MessageEvent(_oid, name, args));
|
||||
}
|
||||
|
||||
public function postEvent (DEvent event) :void
|
||||
{
|
||||
// TODO: transactons?
|
||||
if (_omgr != null) {
|
||||
_omgr.postEvent(event);
|
||||
|
||||
} else {
|
||||
trace("Unable to post event, object has no omgr");
|
||||
}
|
||||
}
|
||||
|
||||
public function isActive () :Boolean
|
||||
{
|
||||
return (_omgr != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't call this function! It initializes this distributed object
|
||||
* with the supplied distributed object manager. This is called by the
|
||||
* distributed object manager when an object is created and registered
|
||||
* with the system.
|
||||
*
|
||||
* @see DObjectManager#createObject
|
||||
*/
|
||||
public function setManager (omgr :DObjectManager) :void
|
||||
{
|
||||
_omgr = omgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by derived instances when an attribute setter method was
|
||||
* called.
|
||||
*/
|
||||
protected function requestAttributeChange (
|
||||
name :String, value :*, oldValue :*) :void
|
||||
{
|
||||
postEvent(new AttributeChangedEvent(_oid, name, value, oldValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by derived instances when an element updater method was
|
||||
* called.
|
||||
*/
|
||||
protected function requestElementUpdate (
|
||||
name :String, index :int, value :*, oldValue :*) :void
|
||||
{
|
||||
// dispatch an attribute changed event
|
||||
postEvent(new ElementUpdatedEvent(_oid, name, value, oldValue, index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls by derived instances when an oid adder method was called.
|
||||
*/
|
||||
protected function requestOidAdd (name :String, oid :int) :void
|
||||
{
|
||||
// dispatch an object added event
|
||||
postEvent(new ObjectAddedEvent(_oid, name, oid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls by derived instances when an oid remover method was called.
|
||||
*/
|
||||
protected function requestOidRemove (name :String, oid :int) :void
|
||||
{
|
||||
// dispatch an object removed event
|
||||
postEvent(new ObjectRemovedEvent(_oid, name, oid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls by derived instances when a set adder method was called.
|
||||
*/
|
||||
protected function requestEntryAdd (name :String, entry :DSet.Entry) :void
|
||||
{
|
||||
// dispatch an entry added event
|
||||
postEvent(new EntryAddedEvent(_oid, name, entry, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls by derived instances when a set remover method was called.
|
||||
*/
|
||||
protected function requestEntryRemove (name :String, key :Comparable) :void
|
||||
{
|
||||
// dispatch an entry removed event
|
||||
postEvent(new EntryRemovedEvent(_oid, name, key, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls by derived instances when a set updater method was called.
|
||||
*/
|
||||
protected function requestEntryUpdate (name :String, entry :DSet.Entry)
|
||||
{
|
||||
// dispatch an entry updated event
|
||||
postEvent(new EntryUpdatedEvent(_oid, name, entry, null));
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
@@ -32,5 +177,11 @@ public class DObject extends EventDispatcher
|
||||
}
|
||||
|
||||
protected var _oid :int;
|
||||
|
||||
/** A reference to our object manager. */
|
||||
protected var _omgr :DObjectManager;
|
||||
|
||||
/** Our event listeners. */
|
||||
protected var _listeners :ArrayCollection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
/**
|
||||
* The distributed object manager is responsible for managing the creation
|
||||
* and destruction of distributed objects and propagating dobj events to
|
||||
* the appropriate subscribers. On the client, objects are managed as
|
||||
* proxies to the real objects managed by the server, so attribute change
|
||||
* requests are forwarded to the server and events coming down from the
|
||||
* server are delivered to the local subscribers. On the server, the
|
||||
* objects are managed directly.
|
||||
*/
|
||||
public interface DObjectManager
|
||||
{
|
||||
/**
|
||||
* Returns true if this distributed object manager is the
|
||||
* authoritative manager for the specified distributed object, or fals
|
||||
* if we are only providing a proxy to the object.
|
||||
*/
|
||||
function isManager (obj :DObject) :Boolean;
|
||||
|
||||
/**
|
||||
* Creates a distributed object instance of the supplied class and
|
||||
* notifies the specified subscriber when it becomes available. This
|
||||
* is the proper mechanism for constructing a new distributed object
|
||||
* as it is the only way in which it can be properly registered with
|
||||
* the dobj system.
|
||||
*
|
||||
* @param dclass The class object of the derived class of
|
||||
* <code>DObject</code> (or <code>DObject.class</code> itself) that
|
||||
* should be instantiated.
|
||||
* @param target The subscriber to be notified when the object is
|
||||
* created and available, or if there was a problem creating the
|
||||
* object.
|
||||
*/
|
||||
function createObject (dclass :Class, target :Subscriber) :void;
|
||||
|
||||
/**
|
||||
* Requests that the specified subscriber be subscribed to the object
|
||||
* identified by the supplied object id. That subscriber will be
|
||||
* notified when the object is available or if the subscription
|
||||
* request failed.
|
||||
*
|
||||
* @param oid The object id of the distributed object to which
|
||||
* subscription is desired.
|
||||
* @param target The subscriber to be subscribed.
|
||||
*
|
||||
* @see Subscriber#objectAvailable
|
||||
* @see Subscriber#requestFailed
|
||||
*/
|
||||
function subscribeToObject (oid :int, target :Subscriber) :void;
|
||||
|
||||
/**
|
||||
* Requests that the specified subscriber be unsubscribed from the
|
||||
* object identified by the supplied object id.
|
||||
*
|
||||
* @param oid The object id of the distributed object from which
|
||||
* unsubscription is desired.
|
||||
* @param target The subscriber to be unsubscribed.
|
||||
*/
|
||||
function unsubscribeFromObject (oid :int, target :Subscriber) :void;
|
||||
|
||||
/**
|
||||
* Requests that the specified object be destroyed. Once destroyed an
|
||||
* object is removed from the runtime system and may no longer have
|
||||
* events dispatched on it.
|
||||
*
|
||||
* @param oid The object id of the distributed object to be destroyed.
|
||||
*/
|
||||
function destroyObject (oid :int) :void;
|
||||
|
||||
/**
|
||||
* Posts a distributed object event into the system. Instead of
|
||||
* requesting the modification of a distributed object attribute by
|
||||
* calling the setter for that attribute on the object itself, an
|
||||
* <code>AttributeChangedEvent</code> can be constructed and posted
|
||||
* directly. This is true for all event types and is useful for
|
||||
* situations where one doesn't have access to the object in question,
|
||||
* but needs to affect some event.
|
||||
*
|
||||
* <p> This event will be forwarded to the ultimate manager of the
|
||||
* object (on the client, this means it will be forwarded to the
|
||||
* server) where it will be checked for validity and then applied to
|
||||
* the object and dispatched to all its subscribers.
|
||||
*
|
||||
* @param event The event to be dispatched.
|
||||
*/
|
||||
function postEvent (event :DEvent) :void;
|
||||
|
||||
/**
|
||||
* When a distributed object removes its last subscriber, it will call
|
||||
* this function to let the object manager know. The manager might
|
||||
* then choose to flush this object from the system or unregister from
|
||||
* some upstream manager whose object it was proxying, for example.
|
||||
*/
|
||||
function removedLastSubscriber (obj :DObject, deathWish :Boolean) :void;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
|
||||
/**
|
||||
* The distributed set class provides a means by which an unordered set of
|
||||
* objects can be maintained as a distributed object field. Entries can be
|
||||
* added to and removed from the set, requests for which will generate
|
||||
* events much like other distributed object fields.
|
||||
*
|
||||
* <p> Classes that wish to act as set entries must implement the {@link
|
||||
* Entry} interface which extends {@link Streamable} and adds the
|
||||
* requirement that the object provide a key which will be used to
|
||||
* identify entry equality. Thus an entry is declared to be in a set of
|
||||
* the object returned by that entry's {@link Entry#getKey} method is
|
||||
* equal (using {@link Object#equals}) to the entry returned by the {@link
|
||||
* Entry#getKey} method of some other entry in the set. Additionally, in
|
||||
* the case of entry removal, only the key for the entry to be removed
|
||||
* will be transmitted with the removal event to save network
|
||||
* bandwidth. Lastly, the object returned by {@link Entry#getKey} must be
|
||||
* a {@link Streamable} type.
|
||||
*/
|
||||
public class DSet
|
||||
implements Streamable
|
||||
{
|
||||
/**
|
||||
* Returns the number of entries in this set.
|
||||
*/
|
||||
public function size () :int
|
||||
{
|
||||
return _entries.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the set contains an entry whose
|
||||
* <code>getKey()</code> method returns a key that
|
||||
* <code>equals()</code> the key returned by <code>getKey()</code> of
|
||||
* the supplied entry. Returns false otherwise.
|
||||
*/
|
||||
public function contains (elem :DSetEntry) :Boolean
|
||||
{
|
||||
return containsKey(elem.getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an entry in the set has a key that
|
||||
* <code>equals()</code> the supplied key. Returns false otherwise.
|
||||
*/
|
||||
public function containsKey (key :Comparable) :Boolean
|
||||
{
|
||||
return getByKey(key) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entry that matches (<code>getKey().equals(key)</code>)
|
||||
* the specified key or null if no entry could be found that matches
|
||||
* the key.
|
||||
*/
|
||||
public function getByKey (key :Comparable) :DSetEntry
|
||||
{
|
||||
// o(n) for now
|
||||
// TODO
|
||||
for (var ii :int = 0; ii < _entries.length; ii++) {
|
||||
var entry :DSetEntry = _entries[ii];
|
||||
if (key.compareTo(entry.getKey()) == 0) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the entries of this set. It does not
|
||||
* support modification (nor iteration while modifications are being
|
||||
* made to the set). It should not be kept around as it can quickly
|
||||
* become out of date.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
// public Iterator entries ()
|
||||
// {
|
||||
// return iterator();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns an iterator over the entries of this set. It does not
|
||||
* support modification (nor iteration while modifications are being
|
||||
* made to the set). It should not be kept around as it can quickly
|
||||
* become out of date.
|
||||
*/
|
||||
// public Iterator iterator ()
|
||||
// {
|
||||
// // the crazy sanity checks
|
||||
// if (_size < 0 ||_size > _entries.length ||
|
||||
// (_size > 0 && _entries[_size-1] == null)) {
|
||||
// Log.warning("DSet in a bad way [size=" + _size +
|
||||
// ", entries=" + StringUtil.toString(_entries) + "].");
|
||||
// Thread.dumpStack();
|
||||
// }
|
||||
//
|
||||
// return new Iterator() {
|
||||
// public boolean hasNext () {
|
||||
// checkComodification();
|
||||
// return (_index < _size);
|
||||
// }
|
||||
// public Object next () {
|
||||
// checkComodification();
|
||||
// return _entries[_index++];
|
||||
// }
|
||||
// public void remove () {
|
||||
// throw new UnsupportedOperationException();
|
||||
// }
|
||||
// protected void checkComodification () {
|
||||
// if (_modCount != _expectedModCount) {
|
||||
// throw new ConcurrentModificationException();
|
||||
// }
|
||||
// if (_ssize != _size) {
|
||||
// Log.warning("Size changed during iteration " +
|
||||
// "[ssize=" + _ssize + ", nsize=" + _size +
|
||||
// ", entsries=" + StringUtil.toString(_entries) +
|
||||
// "].");
|
||||
// Thread.dumpStack();
|
||||
// }
|
||||
// }
|
||||
// protected int _index = 0;
|
||||
// protected int _ssize = _size;
|
||||
// protected int _expectedModCount = _modCount;
|
||||
// };
|
||||
// }
|
||||
|
||||
/**
|
||||
* Copies the elements of this distributed set into the supplied
|
||||
* array. If the array is not large enough to hold all of the
|
||||
* elements, as many as fit into the array will be copied. If the
|
||||
* <code>array</code> argument is null, an object array of sufficient
|
||||
* size to contain all of the elements of this set will be created and
|
||||
* returned.
|
||||
*/
|
||||
public function toArray () :Array
|
||||
{
|
||||
return _entries.concat(null); // makes a copy
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified entry to the set. This should not be called
|
||||
* directly, instead the associated <code>addTo{Set}()</code> method
|
||||
* should be called on the distributed object that contains the set in
|
||||
* question.
|
||||
*
|
||||
* @return true if the entry was added, false if it was already in
|
||||
* the set.
|
||||
*/
|
||||
protected function add (elem :DSetEntry) :Boolean
|
||||
{
|
||||
if (contains(elem)) {
|
||||
trace("Refusing to add duplicate entry [set=" + this +
|
||||
", entry=" + elem + "].");
|
||||
return false;
|
||||
}
|
||||
|
||||
_entries.push(elem);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified entry from the set. This should not be called
|
||||
* directly, instead the associated <code>removeFrom{Set}()</code>
|
||||
* method should be called on the distributed object that contains the
|
||||
* set in question.
|
||||
*
|
||||
* @return true if the entry was removed, false if it was not in the
|
||||
* set.
|
||||
*/
|
||||
protected function remove (elem :DSetEntry) :Boolean
|
||||
{
|
||||
return (null != removeKey(elem.getKey()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from the set the entry whose key matches the supplied
|
||||
* key. This should not be called directly, instead the associated
|
||||
* <code>removeFrom{Set}()</code> method should be called on the
|
||||
* distributed object that contains the set in question.
|
||||
*
|
||||
* @return the old matching entry if found and removed, null if not
|
||||
* found.
|
||||
*/
|
||||
protected function removeKey (key :Comparable) :DSetEntry
|
||||
{
|
||||
// o(n) for now
|
||||
// TODO
|
||||
for (var ii :int = 0; ii < _entries.length; ii++) {
|
||||
var entry :DSetEntry = _entries[ii];
|
||||
if (key.compareTo(entry.getKey()) == 0) {
|
||||
_entries.splice(ii, 1);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified entry by locating an entry whose key matches
|
||||
* the key of the supplied entry and overwriting it. This should not
|
||||
* be called directly, instead the associated
|
||||
* <code>update{Set}()</code> method should be called on the
|
||||
* distributed object that contains the set in question.
|
||||
*
|
||||
* @return the old entry that was replaced, or null if it was not
|
||||
* found (in which case nothing is updated).
|
||||
*/
|
||||
protected function update (elem :DSetEntry) :DSetEntry
|
||||
{
|
||||
var key :Comparable = elem.getKey();
|
||||
for (var ii :int = 0; ii < _entries.length; ii++) {
|
||||
var entry :DSetEntry = _entries[ii];
|
||||
if (key.compareTo(entry.getKey()) == 0) {
|
||||
_entries[ii] = elem;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string representation of this set instance.
|
||||
*/
|
||||
public override function toString () :String
|
||||
{
|
||||
StringBuilder buf = new StringBuilder("(");
|
||||
buf.append(_entries.toString());
|
||||
buf.append(")");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
trace("TODO");
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
_entries = ins.readField(Array);
|
||||
_entries.length = ins.readInt(); // then read the length and limit it
|
||||
}
|
||||
|
||||
/** The entries of the set (in a sparse array). */
|
||||
protected var _entries :Array;
|
||||
|
||||
// /** Used to check for concurrent modification. */
|
||||
// protected transient int _modCount;
|
||||
//
|
||||
// /** The default capacity of a set instance. */
|
||||
// protected static final int INITIAL_CAPACITY = 2;
|
||||
//
|
||||
// /** Used for lookups and to keep the set contents sorted on
|
||||
// * insertions. */
|
||||
// protected static Comparator ENTRY_COMP = new Comparator() {
|
||||
// public int compare (Object o1, Object o2) {
|
||||
// Comparable c1 = (o1 instanceof Entry) ?
|
||||
// ((Entry)o1).getKey() : (Comparable)o1;
|
||||
// Comparable c2 = (o2 instanceof Entry) ?
|
||||
// ((Entry)o2).getKey() : (Comparable)o2;
|
||||
// return c1.compareTo(c2);
|
||||
// }
|
||||
// };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
public interface DSetEntry extends Streamable
|
||||
{
|
||||
function getKey () :Comparable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
|
||||
/**
|
||||
* An element updated event is dispatched when an element of an array
|
||||
* field in a distributed object is updated. It can also be constructed to
|
||||
* request the update of an entry and posted to the dobjmgr.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class ElementUpdatedEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new element updated event on the specified target
|
||||
* object with the supplied attribute name, element and index.
|
||||
*
|
||||
* @param targetOid the object id of the object whose attribute has
|
||||
* changed.
|
||||
* @param name the name of the attribute (data member) for which an
|
||||
* element has changed.
|
||||
* @param value the new value of the element (in the case of primitive
|
||||
* types, the reflection-defined object-alternative is used).
|
||||
* @param oldValue the previous value of the element (in the case of
|
||||
* primitive types, the reflection-defined object-alternative is
|
||||
* used).
|
||||
* @param index the index in the array of the updated element.
|
||||
*/
|
||||
public ElementUpdatedEvent (
|
||||
targetOid :int, name :String, value :*, oldValue :*, index :int)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_value = value;
|
||||
_oldValue = oldValue;
|
||||
_index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new value of the element.
|
||||
*/
|
||||
public function getValue () :*
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the element prior to the application of this
|
||||
* event.
|
||||
*/
|
||||
public function getOldValue () :*
|
||||
{
|
||||
return _oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the element.
|
||||
*/
|
||||
public function getIndex () :int
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this element update to the object.
|
||||
*/
|
||||
public override function applyToObject (target :DObject) :Boolean
|
||||
//throws ObjectAccessException
|
||||
{
|
||||
if (_oldValue === UNSET_OLD_ENTRY) {
|
||||
_oldValue = target[_name][_index];
|
||||
}
|
||||
target[_name][_index] = _value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public override function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeField(_value);
|
||||
out.writeInt(_index);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public override function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
_value = ins.readField(Object);
|
||||
_index = ins.readInt();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function notifyListener (listener :*) :void
|
||||
{
|
||||
if (listener is ElementUpdateListener) {
|
||||
listener.elementUpdated(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function toString (buf :StringBuilder) :void
|
||||
{
|
||||
buf.append("UPDATE:");
|
||||
super.toString(buf);
|
||||
buf.append(", value=", _value);
|
||||
buf.append(", index=", _index);
|
||||
}
|
||||
|
||||
protected var _value :*;
|
||||
protected var _index :int;
|
||||
protected var _oldValue :* = UNSET_OLD_ENTRY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* An entry added event is dispatched when an entry is added to a {@link
|
||||
* DSet} attribute of a distributed entry. It can also be constructed to
|
||||
* request the addition of an entry to a set and posted to the dobjmgr.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class EntryAddedEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new entry added event on the specified target object
|
||||
* with the supplied set attribute name and entry to add.
|
||||
*
|
||||
* @param targetOid the object id of the object to whose set we will
|
||||
* add an entry.
|
||||
* @param name the name of the attribute to which to add the specified
|
||||
* entry.
|
||||
* @param entry the entry to add to the set attribute.
|
||||
*/
|
||||
public function EntryAddedEvent (
|
||||
targetOid :int, name :String, entry :DSetEntry)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_entry = entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entry that has been added.
|
||||
*/
|
||||
public function getEntry () :DSetEntry
|
||||
{
|
||||
return _entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this event to the object.
|
||||
*/
|
||||
public override function applyToObject (target :DObject) :Boolean
|
||||
//throws ObjectAccessException
|
||||
{
|
||||
var added :Boolean = target[_name].add(_entry);
|
||||
if (!added) {
|
||||
trace("Duplicate entry found [event=" + this + "].");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function notifyListener (listener :*) :void
|
||||
{
|
||||
if (listener is SetListener) {
|
||||
listener.entryAdded(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function toString (StringBuilder buf)
|
||||
{
|
||||
buf.append("ELADD:");
|
||||
super.toString(buf);
|
||||
buf.append(", entry=", _entry);
|
||||
}
|
||||
|
||||
public override function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeField(_entry);
|
||||
}
|
||||
|
||||
public override function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
_entry = ins.readField(DSetEntry);
|
||||
}
|
||||
|
||||
protected var _entry :DSetEntry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
|
||||
/**
|
||||
* An entry removed event is dispatched when an entry is removed from a
|
||||
* {@link DSet} attribute of a distributed object. It can also be
|
||||
* constructed to request the removal of an entry from a set and posted to
|
||||
* the dobjmgr.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class EntryRemovedEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new entry removed event on the specified target object
|
||||
* with the supplied set attribute name and entry key to remove.
|
||||
*
|
||||
* @param targetOid the object id of the object from whose set we will
|
||||
* remove an entry.
|
||||
* @param name the name of the attribute from which to remove the
|
||||
* specified entry.
|
||||
* @param key the entry key that identifies the entry to remove.
|
||||
* @param oldEntry the previous value of the entry.
|
||||
*/
|
||||
public function EntryRemovedEvent (
|
||||
targetOid :int, name :String, key :Comparable, oldEntry :DSetEntry)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_key = key;
|
||||
_oldEntry = oldEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key that identifies the entry that has been removed.
|
||||
*/
|
||||
public function getKey () :Comparable
|
||||
{
|
||||
return _key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entry that was in the set prior to being updated.
|
||||
*/
|
||||
public function getOldEntry () :DSetEntry
|
||||
{
|
||||
return _oldEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this event to the object.
|
||||
*/
|
||||
public override function applyToObject (target :DObject) :Boolean
|
||||
//throws ObjectAccessException
|
||||
{
|
||||
if (_oldEntry == UNSET_OLD_ENTRY) {
|
||||
var dset :DSet = target[_name];
|
||||
// remove, fetch the previous value for interested callers
|
||||
_oldEntry = dset.removeKey(_key);
|
||||
if (_oldEntry == null) {
|
||||
// complain if there was actually nothing there
|
||||
trace("No matching entry to remove [key=" + _key +
|
||||
", set=" + set + "].");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function notifyListener (listener :*) :void
|
||||
{
|
||||
if (listener is SetListener) {
|
||||
listener.entryRemoved(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function toString (buf :StringBuilder)
|
||||
{
|
||||
buf.append("ELREM:");
|
||||
super.toString(buf);
|
||||
buf.append(", key=", _key);
|
||||
}
|
||||
|
||||
public override function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeField(_key);
|
||||
}
|
||||
|
||||
public override function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
_key = ins.readField(Comparable);
|
||||
}
|
||||
|
||||
protected var _key :Comparable;
|
||||
protected var _oldEntry :DSetEntry = UNSET_OLD_ENTRY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* An entry updated event is dispatched when an entry of a {@link DSet} is
|
||||
* updated. It can also be constructed to request the update of an entry
|
||||
* and posted to the dobjmgr.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class EntryUpdatedEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new entry updated event on the specified target object
|
||||
* for the specified set name and with the supplied updated entry.
|
||||
*
|
||||
* @param targetOid the object id of the object to whose set we will
|
||||
* add an entry.
|
||||
* @param name the name of the attribute in which to update the
|
||||
* specified entry.
|
||||
* @param entry the entry to update.
|
||||
* @param oldEntry the previous value of the entry.
|
||||
*/
|
||||
public function EntryUpdatedEvent (
|
||||
targetOid :int, name :String, entry :DSetEntry, oldEntry :DSetEntry)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_entry = entry;
|
||||
_oldEntry = oldEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entry that has been updated.
|
||||
*/
|
||||
public function getEntry () :DSetEntry
|
||||
{
|
||||
return _entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entry that was in the set prior to being updated.
|
||||
*/
|
||||
public function getOldEntry ():DSetEntry
|
||||
{
|
||||
return _oldEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this event to the object.
|
||||
*/
|
||||
public override function applyToObject (target :DObject) :Boolean
|
||||
//throws ObjectAccessException
|
||||
{
|
||||
// only apply the change if we haven't already
|
||||
if (_oldEntry == UNSET_OLD_ENTRY) {
|
||||
var dset :DSet = target[_name];
|
||||
// fetch the previous value for interested callers
|
||||
_oldEntry = dset.update(_entry);
|
||||
if (_oldEntry == null) {
|
||||
// complain if we didn't update anything
|
||||
trace("No matching entry to update [entry=" + this +
|
||||
", set=" + set + "].");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function notifyListener (listener :*) :void
|
||||
{
|
||||
if (listener is SetListener) {
|
||||
listener.entryUpdated(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function toString (buf :StringBuilder) :void
|
||||
{
|
||||
buf.append("ELUPD:");
|
||||
super.toString(buf);
|
||||
buf.append(", entry=", _entry);
|
||||
}
|
||||
|
||||
public override function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeField(_entry);
|
||||
}
|
||||
|
||||
public override function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
_entry = ins.readField(DSetEntry);
|
||||
}
|
||||
|
||||
protected var _entry :DSetEntry;
|
||||
protected var _oldEntry :DSetEntry = UNSET_OLD_ENTRY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* A message event is used to dispatch a message to all subscribers of a
|
||||
* distributed object without actually changing any of the fields of the
|
||||
* object. A message has a name, by which different subscribers of the
|
||||
* same object can distinguish their different messages, and an array of
|
||||
* arguments by which any contents of the message can be delivered.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class MessageEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new message event on the specified target object with
|
||||
* the supplied name and arguments.
|
||||
*
|
||||
* @param targetOid the object id of the object whose attribute has
|
||||
* changed.
|
||||
* @param name the name of the message event.
|
||||
* @param args the arguments for this message. This array should
|
||||
* contain only values of valid distributed object types.
|
||||
*/
|
||||
public function MessageEvent (targetOid :int, name :String, args :Array)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arguments to this message.
|
||||
*/
|
||||
public function getArgs () :Array
|
||||
{
|
||||
return _args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the arguments associated with this message event.
|
||||
* <em>Note:</em> this should only be called on events that have not
|
||||
* yet been dispatched into the distributed object system.
|
||||
*/
|
||||
public function setArgs (args :Array) :void
|
||||
{
|
||||
_args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this attribute change to the object.
|
||||
*/
|
||||
public override function applyToObject (target :DObject) :Boolean
|
||||
//throws ObjectAccessException
|
||||
{
|
||||
// nothing to do here
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function notifyListener (listener :*) :void
|
||||
{
|
||||
if (listener is MessageListener) {
|
||||
listener.messageReceived(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function toString (buf :StringBuilder) :void
|
||||
{
|
||||
buf.append("MSG:");
|
||||
super.toString(buf);
|
||||
buf.append(", args=", _args);
|
||||
}
|
||||
|
||||
protected var _args :Array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* A common parent class for all events that are associated with a name
|
||||
* (in some cases a field name, in other cases just an identifying name).
|
||||
*/
|
||||
public class NamedEvent extends DEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new named event for the specified target object with
|
||||
* the supplied attribute name.
|
||||
*
|
||||
* @param targetOid the object id of the object in question.
|
||||
* @param name the name associated with this event.
|
||||
*/
|
||||
public NamedEvent (targetOid :int, name :String)
|
||||
{
|
||||
super(targetOid);
|
||||
_name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the attribute to which this event pertains.
|
||||
*/
|
||||
public function getName () :String
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
protected override function toString (buf :StringBuilder) :void
|
||||
{
|
||||
super.toString(buf);
|
||||
buf.append(", name=", _name);
|
||||
}
|
||||
|
||||
public override function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeField(_name);
|
||||
}
|
||||
|
||||
public override function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
_name = ins.readField(String);
|
||||
}
|
||||
|
||||
/** The name of the event. */
|
||||
protected var _name :String;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import flash.util.StringBuilder;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* An object added event is dispatched when an object is added to an
|
||||
* <code>OidList</code> attribute of a distributed object. It can also be
|
||||
* constructed to request the addition of an oid to an
|
||||
* <code>OidList</code> attribute of an object and posted to the dobjmgr.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class ObjectAddedEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new object added event on the specified target object
|
||||
* with the supplied oid list attribute name and object id to add.
|
||||
*
|
||||
* @param targetOid the object id of the object to whose oid list we
|
||||
* will add an oid.
|
||||
* @param name the name of the attribute (data member) to which to add
|
||||
* the specified oid.
|
||||
* @param oid the oid to add to the oid list attribute.
|
||||
*/
|
||||
public function ObjectAddedEvent (targetOid :int, name :String, oid :int)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_oid = oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the oid that has been added.
|
||||
*/
|
||||
public function getOid () :int
|
||||
{
|
||||
return _oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this event to the object.
|
||||
*/
|
||||
public override function applyToObject (target :DObject) :void
|
||||
//throws ObjectAccessException
|
||||
{
|
||||
var list :OidList = target[_name];
|
||||
list.add(_oid);
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function notifyListener (listener :*) :void
|
||||
{
|
||||
if (listener is OidListListener) {
|
||||
listener.objectAdded(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeInt(_oid);
|
||||
}
|
||||
|
||||
public override function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
_oid = ins.readInt();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected override function toString (buf :StringBuilder) :void
|
||||
{
|
||||
buf.append("OBJADD:");
|
||||
super.toString(buf);
|
||||
buf.append(", oid=", _oid);
|
||||
}
|
||||
|
||||
protected var _oid :int;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// $Id: ObjectRemovedEvent.java 3099 2004-08-27 02:21:06Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.presents.dobj;
|
||||
|
||||
/**
|
||||
* An object removed event is dispatched when an object is removed from an
|
||||
* <code>OidList</code> attribute of a distributed object. It can also be
|
||||
* constructed to request the removal of an oid from an
|
||||
* <code>OidList</code> attribute of an object and posted to the dobjmgr.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class ObjectRemovedEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new object removed event on the specified target
|
||||
* object with the supplied oid list attribute name and object id to
|
||||
* remove.
|
||||
*
|
||||
* @param targetOid the object id of the object from whose oid list we
|
||||
* will remove an oid.
|
||||
* @param name the name of the attribute (data member) from which to
|
||||
* remove the specified oid.
|
||||
* @param oid the oid to remove from the oid list attribute.
|
||||
*/
|
||||
public ObjectRemovedEvent (int targetOid, String name, int oid)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_oid = oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a blank instance of this event in preparation for
|
||||
* unserialization from the network.
|
||||
*/
|
||||
public ObjectRemovedEvent ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the oid that has been removed.
|
||||
*/
|
||||
public int getOid ()
|
||||
{
|
||||
return _oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this event to the object.
|
||||
*/
|
||||
public boolean applyToObject (DObject target)
|
||||
throws ObjectAccessException
|
||||
{
|
||||
OidList list = (OidList)target.getAttribute(_name);
|
||||
list.remove(_oid);
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void notifyListener (Object listener)
|
||||
{
|
||||
if (listener is OidListListener) {
|
||||
((OidListListener)listener).objectRemoved(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void toString (StringBuffer buf)
|
||||
{
|
||||
buf.append("OBJREM:");
|
||||
super.toString(buf);
|
||||
buf.append(", oid=").append(_oid);
|
||||
}
|
||||
|
||||
protected int _oid;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.threerings.presents.dobj {
|
||||
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* An oid list is used to store lists of object ids. The list will not
|
||||
* allow duplicate ids. This class is not synchronized, with the
|
||||
* expectation that all modifications of instances will take place on the
|
||||
* dobjmgr thread.
|
||||
*/
|
||||
public class OidList
|
||||
implements Streamable
|
||||
{
|
||||
/**
|
||||
* Creates an empty oidlist.
|
||||
*/
|
||||
public function OidList ()
|
||||
{
|
||||
_oids = new Array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of object ids in the list.
|
||||
*/
|
||||
public function size () :int
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified object id to the list if it is not already
|
||||
* there.
|
||||
*
|
||||
* @return true if the object was added, false if it was already in
|
||||
* the list.
|
||||
*/
|
||||
public function add (oid :int) :Boolean
|
||||
{
|
||||
// check for existence
|
||||
for (int i = 0; i < _size; i++) {
|
||||
if (_oids[i] == oid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// make room if necessary
|
||||
if (_size+1 >= _oids.length) {
|
||||
expand();
|
||||
}
|
||||
|
||||
// add the oid
|
||||
_oids[_size++] = oid;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified oid from the list.
|
||||
*
|
||||
* @return true if the oid was in the list and was removed, false
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean remove (int oid)
|
||||
{
|
||||
// scan for the oid in question
|
||||
for (int i = 0; i < _size; i++) {
|
||||
if (_oids[i] == oid) {
|
||||
// shift the rest of the list back one
|
||||
System.arraycopy(_oids, i+1, _oids, i, --_size-i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified oid is in the list, false if not.
|
||||
*/
|
||||
public boolean contains (int oid)
|
||||
{
|
||||
for (int i = 0; i < _size; i++) {
|
||||
if (_oids[i] == oid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object id at the specified index. This does no boundary
|
||||
* checking.
|
||||
*/
|
||||
public function getAt (index :int) :int
|
||||
{
|
||||
return _oids[index];
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
trace("Not implemented");
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
// TODO: this is some custom shit. We need to work out array streaming
|
||||
_oids = new Array();
|
||||
var ecount :int = ins.readInt();
|
||||
for (var ii :int = 0; ii < ecount; ii++) {
|
||||
_oids.push(ins.readInt());
|
||||
}
|
||||
_oids.length = ins.readInt(); // then, limit the length to the size
|
||||
}
|
||||
|
||||
|
||||
public override function toString () :String
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("{");
|
||||
buf.append(_oids.toString());
|
||||
buf.append("}");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private var _oids :Array;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user