Finally got around to making compound events actually stick together until
they arrive at the client. Mmm... network efficiency++. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2396 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: ClientDObjectMgr.java,v 1.22 2003/03/11 04:43:14 mdb Exp $
|
||||
// $Id: ClientDObjectMgr.java,v 1.23 2003/04/10 17:48:42 mdb Exp $
|
||||
|
||||
package com.threerings.presents.client;
|
||||
|
||||
@@ -166,6 +166,7 @@ public class ClientDObjectMgr
|
||||
|
||||
} else if (obj instanceof EventNotification) {
|
||||
DEvent evt = ((EventNotification)obj).getEvent();
|
||||
// Log.info("Dispatch event: " + evt);
|
||||
dispatchEvent(evt);
|
||||
|
||||
} else if (obj instanceof ObjectResponse) {
|
||||
@@ -202,8 +203,6 @@ public class ClientDObjectMgr
|
||||
*/
|
||||
protected void dispatchEvent (DEvent event)
|
||||
{
|
||||
// Log.info("Dispatch event: " + event);
|
||||
|
||||
// if this is a compound event, we need to process its contained
|
||||
// events in order
|
||||
if (event instanceof CompoundEvent) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: CompoundEvent.java,v 1.8 2002/12/20 23:41:26 mdb Exp $
|
||||
// $Id: CompoundEvent.java,v 1.9 2003/04/10 17:48:42 mdb Exp $
|
||||
|
||||
package com.threerings.presents.dobj;
|
||||
|
||||
@@ -27,9 +27,9 @@ public class CompoundEvent extends DEvent
|
||||
/**
|
||||
* Constructs a compound event and prepares it for operation.
|
||||
*/
|
||||
public CompoundEvent (DObjectManager omgr)
|
||||
public CompoundEvent (DObject target, DObjectManager omgr)
|
||||
{
|
||||
super(0); // we don't have a single target object oid
|
||||
super(target.getOid());
|
||||
|
||||
// sanity check
|
||||
if (omgr == null) {
|
||||
@@ -38,22 +38,10 @@ public class CompoundEvent extends DEvent
|
||||
}
|
||||
|
||||
_omgr = omgr;
|
||||
_target = target;
|
||||
_events = new StreamableArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets the event know that this dobject is participating in their
|
||||
* transaction. The supplied dobject will have their transaction
|
||||
* cleared when this event is committed or cancelled.
|
||||
*/
|
||||
public void addObject (DObject object)
|
||||
{
|
||||
if (_participants == null) {
|
||||
_participants = new ArrayList();
|
||||
}
|
||||
_participants.add(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts an event to this transaction. The event will be delivered as
|
||||
* part of the entire transaction if it is committed or discarded if
|
||||
@@ -66,6 +54,7 @@ public class CompoundEvent extends DEvent
|
||||
|
||||
/**
|
||||
* Returns the list of events contained within this compound event.
|
||||
* Don't mess with it.
|
||||
*/
|
||||
public List getEvents ()
|
||||
{
|
||||
@@ -80,13 +69,20 @@ public class CompoundEvent extends DEvent
|
||||
*/
|
||||
public void commit ()
|
||||
{
|
||||
// first clear our participants
|
||||
clearParticipants();
|
||||
// first clear our target
|
||||
clearTarget();
|
||||
|
||||
// then post this event onto the queue (but only if we actually
|
||||
// accumulated some events)
|
||||
if (_events.size() > 0) {
|
||||
switch (_events.size()) {
|
||||
case 0: // nothing doing
|
||||
break;
|
||||
case 1: // no point in being compound
|
||||
_omgr.postEvent((DEvent)_events.get(0));
|
||||
break;
|
||||
default: // now we're talking
|
||||
_omgr.postEvent(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +92,8 @@ public class CompoundEvent extends DEvent
|
||||
*/
|
||||
public void cancel ()
|
||||
{
|
||||
// clear our participants
|
||||
clearParticipants();
|
||||
// clear our target
|
||||
clearTarget();
|
||||
// clear our event queue in case someone holds onto us
|
||||
_events.clear();
|
||||
}
|
||||
@@ -125,18 +121,13 @@ public class CompoundEvent extends DEvent
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls out to all of the participating dobjects, clearing their
|
||||
* transaction reference.
|
||||
* Calls out to our target object, clearing its transaction reference.
|
||||
*/
|
||||
protected void clearParticipants ()
|
||||
protected void clearTarget ()
|
||||
{
|
||||
if (_participants != null) {
|
||||
int psize = _participants.size();
|
||||
for (int i = 0; i < psize; i++) {
|
||||
DObject obj = (DObject)_participants.get(i);
|
||||
obj.clearTransaction();
|
||||
}
|
||||
_participants = null;
|
||||
if (_target != null) {
|
||||
_target.clearTransaction();
|
||||
_target = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,10 +145,8 @@ public class CompoundEvent extends DEvent
|
||||
* committed. */
|
||||
protected transient DObjectManager _omgr;
|
||||
|
||||
/** A list of the dobject participants in this transaction. They will
|
||||
* be notified when we are committed or cancelled so that they can
|
||||
* stop posting their events to us. */
|
||||
protected transient ArrayList _participants;
|
||||
/** The object for which we're managing a transaction. */
|
||||
protected transient DObject _target;
|
||||
|
||||
/** A list of the events associated with this compound event. */
|
||||
protected StreamableArrayList _events;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: DObject.java,v 1.61 2003/03/30 19:38:56 mdb Exp $
|
||||
// $Id: DObject.java,v 1.62 2003/04/10 17:48:42 mdb Exp $
|
||||
|
||||
package com.threerings.presents.dobj;
|
||||
|
||||
@@ -16,18 +16,26 @@ import com.threerings.presents.Log;
|
||||
/**
|
||||
* The distributed object forms the foundation of the Presents system. All
|
||||
* information shared among users of the system is done via distributed
|
||||
* objects. A distributed object has a set of subscribers. These
|
||||
* subscribers have access to the object or a proxy of the object and
|
||||
* therefore have access to the data stored in the object's members at all
|
||||
* times.
|
||||
* objects. A distributed object has a set of listeners. These listeners
|
||||
* have access to the object or a proxy of the object and therefore have
|
||||
* access to the data stored in the object's members at all times.
|
||||
*
|
||||
* <p> When there is any change to that data, initiated by one of the
|
||||
* subscribers, an event is generated which is dispatched to all
|
||||
* subscribers of the object, notifying them of that change and affecting
|
||||
* that change to the copy of the object maintained at each client. In
|
||||
* this way, both a respository of shared information and a mechanism for
|
||||
* asynchronous notification are made available as a fundamental
|
||||
* application building blocks.
|
||||
* <p> Additionally, an object as a set of subscribers. Subscribers manage
|
||||
* the lifespan of the object; while a subscriber is subscribed, the
|
||||
* listeners registered with an object will be notified of events. When
|
||||
* the subscriber unsubscribes, the object becomes non-live and the
|
||||
* listeners are no longer notified. <em>Note:</em> on the server, object
|
||||
* subscription is merely a formality as all objects remain live all the
|
||||
* time, so <em>do not</em> depend on event notifications ceasing when a
|
||||
* subscriber has relinquished its subscription. Always unregister all
|
||||
* listeners when they no longer need to hear from an object.
|
||||
*
|
||||
* <p> When there is any change to the the object's fields data, an event
|
||||
* is generated which is dispatched to all listeners of the object,
|
||||
* notifying them of that change and effecting that change to the copy of
|
||||
* the object maintained at each client. In this way, both a respository
|
||||
* of shared information and a mechanism for asynchronous notification are
|
||||
* made available as a fundamental application building blocks.
|
||||
*
|
||||
* <p> To define what information is shared, an application creates a
|
||||
* distributed object declaration which is much like a class declaration
|
||||
@@ -78,14 +86,16 @@ import com.threerings.presents.Log;
|
||||
* These method calls on the actual distributed object will result in the
|
||||
* proper attribute change events being generated and dispatched.
|
||||
*
|
||||
* <p> Note that distributed object fields can only be of a limited set of
|
||||
* supported types. These types are:
|
||||
* <p> Note that distributed object fields can be any of the following set
|
||||
* of primitive types:
|
||||
*
|
||||
* <code><pre>
|
||||
* byte, short, int, long, float, double
|
||||
* Byte, Short, Integer, Long, Float, Double, String
|
||||
* byte[], short[], int[], long[], float[], double[], String[]
|
||||
* boolean, byte, short, int, long, float, double
|
||||
* Boolean, Byte, Short, Integer, Long, Float, Double, String
|
||||
* boolean[], byte[], short[], int[], long[], float[], double[], String[]
|
||||
* </pre></code>
|
||||
*
|
||||
* Fields of type {@link Streamable} can also be used.
|
||||
*/
|
||||
public class DObject implements Streamable
|
||||
{
|
||||
@@ -173,11 +183,9 @@ public class DObject implements Streamable
|
||||
Object[] els = ListUtil.testAndAdd(_listeners, listener);
|
||||
if (els != null) {
|
||||
_listeners = els;
|
||||
|
||||
} else {
|
||||
// complain if an object requests to add itself more than once
|
||||
Log.warning("Refusing listener that's already in the list " +
|
||||
"[dobj=" + which() + ", listener=" + listener + "]");
|
||||
Log.warning("Refusing repeat listener registration " +
|
||||
"[dobj=" + which() + ", list=" + listener + "]");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
}
|
||||
@@ -341,24 +349,19 @@ public class DObject implements Streamable
|
||||
/**
|
||||
* Called by the distributed object manager after it has applied an
|
||||
* event to this object. This dispatches an event notification to all
|
||||
* of the subscribers of this object.
|
||||
* of the listeners registered with this object.
|
||||
*
|
||||
* @param event the event that was just applied.
|
||||
*/
|
||||
public void notifyListeners (DEvent event)
|
||||
{
|
||||
// if we have no listeners, we're home free
|
||||
if (_listeners == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate over the listener list, performing the necessary
|
||||
// notifications
|
||||
int llength = _listeners.length;
|
||||
for (int i = 0; i < llength; i++) {
|
||||
Object listener = _listeners[i];
|
||||
|
||||
// skip empty slots
|
||||
if (listener == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -374,8 +377,34 @@ public class DObject implements Streamable
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Listener choked during notification " +
|
||||
"[listener=" + listener +
|
||||
", event=" + event + "].");
|
||||
"[list=" + listener + ", event=" + event + "].");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the distributed object manager after it has applied an
|
||||
* event to this object. This dispatches an event notification to all
|
||||
* of the proxy listeners registered with this object.
|
||||
*
|
||||
* @param event the event that was just applied.
|
||||
*/
|
||||
public void notifyProxies (DEvent event)
|
||||
{
|
||||
if (_subs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int ii = 0, ll = _subs.length; ii < ll; ii++) {
|
||||
Object sub = _subs[ii];
|
||||
try {
|
||||
if (sub != null && sub instanceof ProxySubscriber) {
|
||||
((ProxySubscriber)sub).eventReceived(event);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.warning("Proxy choked during notification " +
|
||||
"[sub=" + sub + ", event=" + event + "].");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
@@ -565,19 +594,18 @@ public class DObject implements Streamable
|
||||
*
|
||||
* <p> When the transaction is complete, the caller must call {@link
|
||||
* #commitTransaction} or {@link CompoundEvent#commit} to commit the
|
||||
* transaction and release all involved objects back to their normal
|
||||
* transaction and release the object back to its normal
|
||||
* non-transacting state. If the caller decides not to commit their
|
||||
* transaction, they must call {@link #cancelTransaction} or {@link
|
||||
* CompoundEvent#cancel} to cancel the transaction and release all
|
||||
* involved objects. Failure to do so will cause the pooch to be
|
||||
* totally screwed.
|
||||
* CompoundEvent#cancel} to cancel the transaction. Failure to do so
|
||||
* will cause the pooch to be totally screwed.
|
||||
*
|
||||
* <p> Note: like all other distributed object operations,
|
||||
* transactions are not thread safe. It is expected that a single
|
||||
* thread will handle all distributed object operations and that
|
||||
* thread will begin and complete a transaction before giving up
|
||||
* control to unknown code which might try to operate on the
|
||||
* transacting distributed object (or objects).
|
||||
* transacting distributed object.
|
||||
*
|
||||
* <p> Note also: if the object is already engaged in a transaction, a
|
||||
* transaction participant count will be incremented to note that an
|
||||
@@ -589,19 +617,8 @@ public class DObject implements Streamable
|
||||
* cancels the transaction, the entire transaction is cancelled for
|
||||
* all participants, regardless of whether the other participants
|
||||
* attempted to commit the transaction.
|
||||
*
|
||||
* <p> Note also: nested transactions are not allowed when an object
|
||||
* is joined to another object's transaction via {@link
|
||||
* #joinTransaction}.
|
||||
*
|
||||
* @return the compound event that encapsulates the transaction. This
|
||||
* can be ignored if the transaction will be limited to this object,
|
||||
* but if it is desired that other objects be involved in the
|
||||
* transaction, the caller will need to pass the {@link CompoundEvent}
|
||||
* returned by this method to a call to {@link #joinTransaction} on
|
||||
* the other objects that are involved.
|
||||
*/
|
||||
public CompoundEvent startTransaction ()
|
||||
public void startTransaction ()
|
||||
{
|
||||
// sanity check
|
||||
if (!isActive()) {
|
||||
@@ -611,40 +628,10 @@ public class DObject implements Streamable
|
||||
}
|
||||
|
||||
if (_tevent != null) {
|
||||
// a transaction count of -1 indicates that we're joined to
|
||||
// another objects transaction that should not allow
|
||||
// transaction nesting
|
||||
if (_tcount == -1) {
|
||||
String errmsg = "Object involved in shared transaction, " +
|
||||
"nesting not allowed [dobj=" + this + "]";
|
||||
throw new IllegalStateException(errmsg);
|
||||
} else {
|
||||
_tcount++;
|
||||
}
|
||||
|
||||
_tcount++;
|
||||
} else {
|
||||
_tevent = new CompoundEvent(_omgr);
|
||||
_tevent.addObject(this);
|
||||
_tevent = new CompoundEvent(this, _omgr);
|
||||
}
|
||||
return _tevent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes this object to join the supplied transaction. See {@link
|
||||
* #startTransaction} for more information on transactions. The
|
||||
* transaction can be committed by a call to {@link
|
||||
* #commitTransaction} on any participanting object.
|
||||
*/
|
||||
public void joinTransaction (CompoundEvent event)
|
||||
{
|
||||
if (_tevent != null) {
|
||||
String errmsg = "Cannot join transaction while already " +
|
||||
"transacting [dobj=" + this + "]";
|
||||
throw new IllegalStateException(errmsg);
|
||||
}
|
||||
_tevent = event;
|
||||
_tevent.addObject(this);
|
||||
_tcount = -1; // make a note that nesting is not allowed
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// $Id: ProxySubscriber.java,v 1.1 2003/04/10 17:48:42 mdb Exp $
|
||||
|
||||
package com.threerings.presents.dobj;
|
||||
|
||||
/**
|
||||
* Defines a special kind of subscriber that proxies events for a
|
||||
* subordinate distributed object manager. All events dispatched on
|
||||
* objects with which this subscriber is registered are passed along to
|
||||
* the subscriber for delivery to its subordinate manager.
|
||||
*
|
||||
* @see DObject#addListener
|
||||
*/
|
||||
public interface ProxySubscriber extends Subscriber
|
||||
{
|
||||
/**
|
||||
* Called when any event has been dispatched on an object.
|
||||
*
|
||||
* @param event The event that was dispatched on the object.
|
||||
*/
|
||||
public void eventReceived (DEvent event);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: PresentsClient.java,v 1.53 2003/03/30 21:04:18 mdb Exp $
|
||||
// $Id: PresentsClient.java,v 1.54 2003/04/10 17:48:42 mdb Exp $
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
@@ -15,9 +15,8 @@ import com.threerings.presents.data.ClientObject;
|
||||
|
||||
import com.threerings.presents.dobj.DEvent;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.EventListener;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.Subscriber;
|
||||
import com.threerings.presents.dobj.ProxySubscriber;
|
||||
|
||||
import com.threerings.presents.net.BootstrapData;
|
||||
import com.threerings.presents.net.BootstrapNotification;
|
||||
@@ -54,8 +53,7 @@ import com.threerings.presents.server.net.MessageHandler;
|
||||
* conmgr thread and therefore also need not be synchronized.
|
||||
*/
|
||||
public class PresentsClient
|
||||
implements Subscriber, EventListener, MessageHandler,
|
||||
ClientResolutionListener
|
||||
implements ProxySubscriber, MessageHandler, ClientResolutionListener
|
||||
{
|
||||
/** Used by {@link #setUsername} to report success or failure. */
|
||||
public static interface UserChangeListener
|
||||
@@ -416,7 +414,6 @@ public class PresentsClient
|
||||
{
|
||||
DObject object = (DObject)_subscrips.remove(oid);
|
||||
if (object != null) {
|
||||
object.removeListener(this);
|
||||
object.removeSubscriber(this);
|
||||
} else {
|
||||
Log.warning("Requested to unmap non-existent subscription " +
|
||||
@@ -435,7 +432,6 @@ public class PresentsClient
|
||||
DObject object = (DObject)enum.next();
|
||||
// Log.info("Clearing subscription [client=" + this +
|
||||
// ", obj=" + object.getOid() + "].");
|
||||
object.removeListener(this);
|
||||
object.removeSubscriber(this);
|
||||
}
|
||||
}
|
||||
@@ -649,8 +645,6 @@ public class PresentsClient
|
||||
// queue up an object response
|
||||
Connection conn = getConnection();
|
||||
if (conn != null) {
|
||||
// add ourselves as an event listener
|
||||
object.addListener(this);
|
||||
// pass the successful subscrip on to the client
|
||||
conn.postMessage(new ObjectResponse(object));
|
||||
// make a note of this new subscription
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: PresentsDObjectMgr.java,v 1.30 2003/03/31 04:11:24 mdb Exp $
|
||||
// $Id: PresentsDObjectMgr.java,v 1.31 2003/04/10 17:48:42 mdb Exp $
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.threerings.presents.dobj.*;
|
||||
* server. By virtue of running on the server, it manages its objects
|
||||
* directly rather than managing proxies of objects which is what is done
|
||||
* on the client. Thus it simply queues up events and dispatches them to
|
||||
* subscribers.
|
||||
* listeners.
|
||||
*
|
||||
* <p> The server object manager is meant to run on the main thread of the
|
||||
* server application and thus provides a method to be invoked by the
|
||||
@@ -180,18 +180,9 @@ public class PresentsDObjectMgr
|
||||
}
|
||||
|
||||
} else if (unit instanceof CompoundEvent) {
|
||||
// if this is a compound event, we need to apply each
|
||||
// event indivdually
|
||||
CompoundEvent event = (CompoundEvent)unit;
|
||||
List events = event.getEvents();
|
||||
int ecount = events.size();
|
||||
for (int i = 0; i < ecount; i++) {
|
||||
processEvent((DEvent)events.get(i));
|
||||
}
|
||||
processCompoundEvent((CompoundEvent)unit);
|
||||
|
||||
} else {
|
||||
// otherwise it's a regular event, so do the standard
|
||||
// processing
|
||||
processEvent((DEvent)unit);
|
||||
}
|
||||
|
||||
@@ -217,9 +208,45 @@ public class PresentsDObjectMgr
|
||||
Log.info("DOMGR exited.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the processing associated with a compound event, notifying
|
||||
* listeners and the like.
|
||||
*/
|
||||
protected void processCompoundEvent (CompoundEvent event)
|
||||
{
|
||||
List events = event.getEvents();
|
||||
int ecount = events.size();
|
||||
|
||||
// look up the target object
|
||||
DObject target = (DObject)_objects.get(event.getTargetOid());
|
||||
if (target == null) {
|
||||
Log.debug("Compound event target no longer exists " +
|
||||
"[event=" + event + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// check the permissions on all of the events
|
||||
for (int ii = 0; ii < ecount; ii++) {
|
||||
DEvent sevent = (DEvent)events.get(ii);
|
||||
if (!target.checkPermissions(sevent)) {
|
||||
Log.warning("Event failed permissions check " +
|
||||
"[event=" + sevent + ", target=" + target + "].");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// dispatch the events
|
||||
for (int ii = 0; ii < ecount; ii++) {
|
||||
dispatchEvent((DEvent)events.get(ii), target);
|
||||
}
|
||||
|
||||
// always notify proxies of compound events
|
||||
target.notifyProxies(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the processing associated with an event, notifying
|
||||
* subscribers and the like.
|
||||
* listeners and the like.
|
||||
*/
|
||||
protected void processEvent (DEvent event)
|
||||
{
|
||||
@@ -244,22 +271,36 @@ public class PresentsDObjectMgr
|
||||
return;
|
||||
}
|
||||
|
||||
if (dispatchEvent(event, target)) {
|
||||
// unless requested not to, notify any proxies
|
||||
target.notifyProxies(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event after the target object has been resolved and
|
||||
* the permissions have been checked. This is used by {@link
|
||||
* #processEvent} and {@link #processCompoundEvent}.
|
||||
*
|
||||
* @return the value returned by {@link DEvent#applyToObject}.
|
||||
*/
|
||||
protected boolean dispatchEvent (DEvent event, DObject target)
|
||||
{
|
||||
boolean notify = true; // assume always notify
|
||||
try {
|
||||
// do any internal management necessary based on this
|
||||
// event
|
||||
// do any internal management necessary based on this event
|
||||
Method helper = (Method)_helpers.get(event.getClass());
|
||||
if (helper != null) {
|
||||
// invoke the helper method
|
||||
Object rv =
|
||||
helper.invoke(this, new Object[] { event, target });
|
||||
Object rv = helper.invoke(this, new Object[] { event, target });
|
||||
// if helper returns false, we abort event processing
|
||||
if (!((Boolean)rv).booleanValue()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// everything's good so far, apply the event to the object
|
||||
boolean notify = event.applyToObject(target);
|
||||
notify = event.applyToObject(target);
|
||||
|
||||
// if the event returns false from applyToObject, this
|
||||
// means it's a silent event and we shouldn't notify the
|
||||
@@ -276,6 +317,7 @@ public class PresentsDObjectMgr
|
||||
|
||||
// track the number of events dispatched
|
||||
++_eventCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user