Added some magic on the road toward more elegant support for dynamic languages
like Groovy and JRuby. Created a DynamicEventDispatcher that automatically maps
attribute, element and set events to methods. Say you have a field:
public DSet occupantInfo;
you can create a method in any class:
public void occupantInfoAdded (BodyObject source, OccupantInfo entry);
and then bind that class as a listener using the dynamic event dispatcher:
_myobj.addListener(new DynamicEventDispatcher(object));
I also created a nicer replacement for the MessageHandler system which is
clunky but still way simpler than using a full InvocationService. Basically we
dispatch MessageEvent as if it were a method call.
For example, in AtlantiManager I define:
public void placeTile (BodyObject placer, AtlantiTile tile)
which receives a request by a player to place a tile on their turn. Then in
AtlantiController, I simply call:
_atlobj.manager.invoke("placeTile", tile);
Of course, in JRuby and Groovy, that's going to look like:
_atlobj.manager.placeTile(tile);
which is all part of the fun.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4344 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -21,26 +21,22 @@
|
|||||||
|
|
||||||
package com.threerings.crowd;
|
package com.threerings.crowd;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A placeholder class that contains a reference to the log object used by
|
* Contains a reference to the log object used by the Crowd services.
|
||||||
* the Crowd services.
|
|
||||||
*/
|
*/
|
||||||
public class Log
|
public class Log
|
||||||
{
|
{
|
||||||
public static com.samskivert.util.Log log =
|
/** We dispatch our log messages through this logger. */
|
||||||
new com.samskivert.util.Log("crowd");
|
public static Logger log =
|
||||||
|
Logger.getLogger("com.threerings.narya.crowd");
|
||||||
/** Convenience function. */
|
|
||||||
public static boolean debug ()
|
|
||||||
{
|
|
||||||
return (com.samskivert.util.Log.getLevel() ==
|
|
||||||
com.samskivert.util.Log.DEBUG);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
/** Convenience function. */
|
||||||
public static void debug (String message)
|
public static void debug (String message)
|
||||||
{
|
{
|
||||||
log.debug(message);
|
log.fine(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convenience function. */
|
/** Convenience function. */
|
||||||
@@ -58,6 +54,6 @@ public class Log
|
|||||||
/** Convenience function. */
|
/** Convenience function. */
|
||||||
public static void logStackTrace (Throwable t)
|
public static void logStackTrace (Throwable t)
|
||||||
{
|
{
|
||||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
log.log(Level.WARNING, t.getMessage(), t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,22 @@ public class PlaceObject extends DObject
|
|||||||
public static final String SPEAK_SERVICE = "speakService";
|
public static final String SPEAK_SERVICE = "speakService";
|
||||||
// AUTO-GENERATED: FIELDS END
|
// AUTO-GENERATED: FIELDS END
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exists solely to make calls into the manager look sensible:
|
||||||
|
* <pre>_plobj.manager.invoke("someMethod", args);</pre>
|
||||||
|
*/
|
||||||
|
public class ManagerCaller
|
||||||
|
{
|
||||||
|
public void invoke (String method, Object ... args) {
|
||||||
|
postMessage(method, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the client to call methods on the manager.
|
||||||
|
*/
|
||||||
|
public transient ManagerCaller manager = new ManagerCaller();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks the oid of the body objects of all of the occupants of this
|
* Tracks the oid of the body objects of all of the occupants of this
|
||||||
* place.
|
* place.
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ package com.threerings.crowd.server;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import com.samskivert.util.HashIntMap;
|
import com.samskivert.util.HashIntMap;
|
||||||
import com.samskivert.util.Interval;
|
import com.samskivert.util.Interval;
|
||||||
@@ -36,8 +37,7 @@ import com.threerings.presents.server.PresentsDObjectMgr;
|
|||||||
import com.threerings.presents.dobj.AccessController;
|
import com.threerings.presents.dobj.AccessController;
|
||||||
import com.threerings.presents.dobj.DObject;
|
import com.threerings.presents.dobj.DObject;
|
||||||
import com.threerings.presents.dobj.DObjectManager;
|
import com.threerings.presents.dobj.DObjectManager;
|
||||||
import com.threerings.presents.dobj.EntryAddedEvent;
|
import com.threerings.presents.dobj.DynamicEventDispatcher;
|
||||||
import com.threerings.presents.dobj.EntryRemovedEvent;
|
|
||||||
import com.threerings.presents.dobj.EntryUpdatedEvent;
|
import com.threerings.presents.dobj.EntryUpdatedEvent;
|
||||||
import com.threerings.presents.dobj.MessageEvent;
|
import com.threerings.presents.dobj.MessageEvent;
|
||||||
import com.threerings.presents.dobj.MessageListener;
|
import com.threerings.presents.dobj.MessageListener;
|
||||||
@@ -46,7 +46,7 @@ import com.threerings.presents.dobj.ObjectDeathListener;
|
|||||||
import com.threerings.presents.dobj.ObjectDestroyedEvent;
|
import com.threerings.presents.dobj.ObjectDestroyedEvent;
|
||||||
import com.threerings.presents.dobj.ObjectRemovedEvent;
|
import com.threerings.presents.dobj.ObjectRemovedEvent;
|
||||||
import com.threerings.presents.dobj.OidListListener;
|
import com.threerings.presents.dobj.OidListListener;
|
||||||
import com.threerings.presents.dobj.SetListener;
|
import com.threerings.presents.dobj.SetAdapter;
|
||||||
|
|
||||||
import com.threerings.crowd.Log;
|
import com.threerings.crowd.Log;
|
||||||
import com.threerings.crowd.data.BodyObject;
|
import com.threerings.crowd.data.BodyObject;
|
||||||
@@ -79,12 +79,15 @@ import com.threerings.crowd.chat.server.SpeakProvider;
|
|||||||
*/
|
*/
|
||||||
public class PlaceManager
|
public class PlaceManager
|
||||||
implements MessageListener, OidListListener, ObjectDeathListener,
|
implements MessageListener, OidListListener, ObjectDeathListener,
|
||||||
SetListener, SpeakProvider.SpeakerValidator
|
SpeakProvider.SpeakerValidator
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* An interface used to allow the registration of standard message
|
* An interface used to allow the registration of standard message handlers
|
||||||
* handlers to be invoked by the place manager when particular types
|
* to be invoked by the place manager when particular types of message
|
||||||
* of message events are received.
|
* events are received.
|
||||||
|
*
|
||||||
|
* @deprecated Use dynamically bound methods instead. See {@link
|
||||||
|
* DynamicEventDispatcher}.
|
||||||
*/
|
*/
|
||||||
public static interface MessageHandler
|
public static interface MessageHandler
|
||||||
{
|
{
|
||||||
@@ -160,27 +163,6 @@ public class PlaceManager
|
|||||||
_plobj.updateOccupantInfo((OccupantInfo)occInfo.clone());
|
_plobj.updateOccupantInfo((OccupantInfo)occInfo.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Derived classes will generally override this method to create a custom
|
|
||||||
* {@link PlaceObject} derivation that contains extra information.
|
|
||||||
*/
|
|
||||||
protected PlaceObject createPlaceObject ()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return getPlaceObjectClass().newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #createPlaceObject}.
|
|
||||||
*/
|
|
||||||
protected Class<? extends PlaceObject> getPlaceObjectClass ()
|
|
||||||
{
|
|
||||||
return PlaceObject.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the place registry after creating this place manager.
|
* Called by the place registry after creating this place manager.
|
||||||
*/
|
*/
|
||||||
@@ -241,15 +223,6 @@ public class PlaceManager
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if the permissions check failed, to give place managers a
|
|
||||||
* chance to do any cleanup that might be necessary due to their early
|
|
||||||
* initialization or permissions checking code.
|
|
||||||
*/
|
|
||||||
protected void permissionsFailed ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the place manager after the place object has been
|
* Called by the place manager after the place object has been
|
||||||
* successfully created.
|
* successfully created.
|
||||||
@@ -268,6 +241,7 @@ public class PlaceManager
|
|||||||
|
|
||||||
// we'll need to hear about place object events
|
// we'll need to hear about place object events
|
||||||
plobj.addListener(this);
|
plobj.addListener(this);
|
||||||
|
plobj.addListener(_bodyUpdater);
|
||||||
|
|
||||||
// configure this place's access controller
|
// configure this place's access controller
|
||||||
plobj.setAccessController(getAccessController());
|
plobj.setAccessController(getAccessController());
|
||||||
@@ -279,40 +253,6 @@ public class PlaceManager
|
|||||||
checkShutdownInterval();
|
checkShutdownInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if we should create a speaker service for our place object
|
|
||||||
* so that clients can use it to speak in this place.
|
|
||||||
*/
|
|
||||||
protected boolean shouldCreateSpeakService ()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an access controller for this place's distributed object, which
|
|
||||||
* by default is {@link CrowdObjectAccess#PLACE}.
|
|
||||||
*/
|
|
||||||
protected AccessController getAccessController ()
|
|
||||||
{
|
|
||||||
return CrowdObjectAccess.PLACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derived classes should override this (and be sure to call
|
|
||||||
* <code>super.didStartup()</code>) to perform any startup time
|
|
||||||
* initialization. The place object will be available by the time this
|
|
||||||
* method is executed.
|
|
||||||
*/
|
|
||||||
protected void didStartup ()
|
|
||||||
{
|
|
||||||
// let our delegates know that we've started up
|
|
||||||
applyToDelegates(new DelegateOp() {
|
|
||||||
public void apply (PlaceManagerDelegate delegate) {
|
|
||||||
delegate.didStartup(_plobj);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Causes the place object being managed by this place manager to be
|
* Causes the place object being managed by this place manager to be
|
||||||
* destroyed and the place manager to shut down.
|
* destroyed and the place manager to shut down.
|
||||||
@@ -331,22 +271,6 @@ public class PlaceManager
|
|||||||
cancelShutdowner();
|
cancelShutdowner();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when this place has been destroyed and the place manager has
|
|
||||||
* shut down (via a call to {@link #shutdown}). Derived classes can
|
|
||||||
* override this method and perform any necessary shutdown time
|
|
||||||
* processing.
|
|
||||||
*/
|
|
||||||
protected void didShutdown ()
|
|
||||||
{
|
|
||||||
// let our delegates know that we've shut down
|
|
||||||
applyToDelegates(new DelegateOp() {
|
|
||||||
public void apply (PlaceManagerDelegate delegate) {
|
|
||||||
delegate.didShutdown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an opportunity for the place manager to prevent bodies from
|
* Provides an opportunity for the place manager to prevent bodies from
|
||||||
* entering.
|
* entering.
|
||||||
@@ -391,6 +315,87 @@ public class PlaceManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a particular message handler instance to be used when
|
||||||
|
* processing message events with the specified name.
|
||||||
|
*
|
||||||
|
* @param name the message name of the message events that should be
|
||||||
|
* handled by this handler.
|
||||||
|
* @param handler the handler to be registered.
|
||||||
|
*
|
||||||
|
* @deprecated Use dynamically bound methods instead. See {@link
|
||||||
|
* DynamicEventDispatcher}.
|
||||||
|
*/
|
||||||
|
public void registerMessageHandler (String name, MessageHandler handler)
|
||||||
|
{
|
||||||
|
// create our handler map if necessary
|
||||||
|
if (_msghandlers == null) {
|
||||||
|
_msghandlers = new HashMap<String,MessageHandler>();
|
||||||
|
}
|
||||||
|
_msghandlers.put(name, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface MessageListener
|
||||||
|
public void messageReceived (MessageEvent event)
|
||||||
|
{
|
||||||
|
MessageHandler handler = null;
|
||||||
|
if (_msghandlers != null) {
|
||||||
|
handler = _msghandlers.get(event.getName());
|
||||||
|
}
|
||||||
|
if (handler != null) {
|
||||||
|
handler.handleEvent(event, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the first argument should be the client object of the caller or null
|
||||||
|
// if it is a server originated event
|
||||||
|
int srcoid = event.getSourceOid();
|
||||||
|
DObject source = (srcoid <= 0) ?
|
||||||
|
null : CrowdServer.omgr.getObject(srcoid);
|
||||||
|
Object[] args = event.getArgs(), nargs;
|
||||||
|
if (args == null) {
|
||||||
|
nargs = new Object[] { source };
|
||||||
|
} else {
|
||||||
|
nargs = new Object[args.length+1];
|
||||||
|
nargs[0] = source;
|
||||||
|
System.arraycopy(args, 0, nargs, 1, args.length);
|
||||||
|
}
|
||||||
|
_dispatcher.dispatchMethod(event.getName(), nargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface OidListListener
|
||||||
|
public void objectAdded (ObjectAddedEvent event)
|
||||||
|
{
|
||||||
|
if (event.getName().equals(PlaceObject.OCCUPANTS)) {
|
||||||
|
bodyEntered(event.getOid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface OidListListener
|
||||||
|
public void objectRemoved (ObjectRemovedEvent event)
|
||||||
|
{
|
||||||
|
if (event.getName().equals(PlaceObject.OCCUPANTS)) {
|
||||||
|
bodyLeft(event.getOid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface ObjectDeathListener
|
||||||
|
public void objectDestroyed (ObjectDestroyedEvent event)
|
||||||
|
{
|
||||||
|
// unregister ourselves
|
||||||
|
_registry.unmapPlaceManager(this);
|
||||||
|
|
||||||
|
// let our derived classes and delegates shut themselves down
|
||||||
|
didShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public boolean isValidSpeaker (
|
||||||
|
DObject speakObj, ClientObject speaker, byte mode)
|
||||||
|
{
|
||||||
|
// only allow people in the room to speak
|
||||||
|
return _plobj.occupants.contains(speaker.getOid());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string that can be used in log messages to identify the
|
* Returns a string that can be used in log messages to identify the
|
||||||
* place as sensibly as possible to the developer who has to puzzle
|
* place as sensibly as possible to the developer who has to puzzle
|
||||||
@@ -404,6 +409,102 @@ public class PlaceManager
|
|||||||
(_plobj != null) ? String.valueOf(_plobj.getOid()) : "-1");
|
(_plobj != null) ? String.valueOf(_plobj.getOid()) : "-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a string representation of this manager. Does so in a way
|
||||||
|
* that makes it easier for derived classes to add to the string
|
||||||
|
* representation.
|
||||||
|
*
|
||||||
|
* @see #toString(StringBuilder)
|
||||||
|
*/
|
||||||
|
public String toString ()
|
||||||
|
{
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append("[");
|
||||||
|
toString(buf);
|
||||||
|
buf.append("]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived classes will generally override this method to create a custom
|
||||||
|
* {@link PlaceObject} derivation that contains extra information.
|
||||||
|
*/
|
||||||
|
protected PlaceObject createPlaceObject ()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return getPlaceObjectClass().newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #createPlaceObject}.
|
||||||
|
*/
|
||||||
|
protected Class<? extends PlaceObject> getPlaceObjectClass ()
|
||||||
|
{
|
||||||
|
return PlaceObject.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called if the permissions check failed, to give place managers a
|
||||||
|
* chance to do any cleanup that might be necessary due to their early
|
||||||
|
* initialization or permissions checking code.
|
||||||
|
*/
|
||||||
|
protected void permissionsFailed ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if we should create a speaker service for our place object
|
||||||
|
* so that clients can use it to speak in this place.
|
||||||
|
*/
|
||||||
|
protected boolean shouldCreateSpeakService ()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an access controller for this place's distributed object, which
|
||||||
|
* by default is {@link CrowdObjectAccess#PLACE}.
|
||||||
|
*/
|
||||||
|
protected AccessController getAccessController ()
|
||||||
|
{
|
||||||
|
return CrowdObjectAccess.PLACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived classes should override this (and be sure to call
|
||||||
|
* <code>super.didStartup()</code>) to perform any startup time
|
||||||
|
* initialization. The place object will be available by the time this
|
||||||
|
* method is executed.
|
||||||
|
*/
|
||||||
|
protected void didStartup ()
|
||||||
|
{
|
||||||
|
// let our delegates know that we've started up
|
||||||
|
applyToDelegates(new DelegateOp() {
|
||||||
|
public void apply (PlaceManagerDelegate delegate) {
|
||||||
|
delegate.didStartup(_plobj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when this place has been destroyed and the place manager has
|
||||||
|
* shut down (via a call to {@link #shutdown}). Derived classes can
|
||||||
|
* override this method and perform any necessary shutdown time
|
||||||
|
* processing.
|
||||||
|
*/
|
||||||
|
protected void didShutdown ()
|
||||||
|
{
|
||||||
|
// let our delegates know that we've shut down
|
||||||
|
applyToDelegates(new DelegateOp() {
|
||||||
|
public void apply (PlaceManagerDelegate delegate) {
|
||||||
|
delegate.didShutdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an occupant is being added to this place. This will be
|
* Called when an occupant is being added to this place. This will be
|
||||||
* before the call to {@link #bodyEntered} and gives the derived class a
|
* before the call to {@link #bodyEntered} and gives the derived class a
|
||||||
@@ -420,7 +521,7 @@ public class PlaceManager
|
|||||||
*/
|
*/
|
||||||
protected void bodyEntered (final int bodyOid)
|
protected void bodyEntered (final int bodyOid)
|
||||||
{
|
{
|
||||||
if (Log.debug()) {
|
if (Log.log.getLevel() == Level.FINE) {
|
||||||
Log.debug("Body entered [where=" + where() +
|
Log.debug("Body entered [where=" + where() +
|
||||||
", oid=" + bodyOid + "].");
|
", oid=" + bodyOid + "].");
|
||||||
}
|
}
|
||||||
@@ -441,7 +542,7 @@ public class PlaceManager
|
|||||||
*/
|
*/
|
||||||
protected void bodyLeft (final int bodyOid)
|
protected void bodyLeft (final int bodyOid)
|
||||||
{
|
{
|
||||||
if (Log.debug()) {
|
if (Log.log.getLevel() == Level.FINE) {
|
||||||
Log.debug("Body left [where=" + where() +
|
Log.debug("Body left [where=" + where() +
|
||||||
", oid=" + bodyOid + "].");
|
", oid=" + bodyOid + "].");
|
||||||
}
|
}
|
||||||
@@ -551,122 +652,6 @@ public class PlaceManager
|
|||||||
return 5 * 60 * 1000L;
|
return 5 * 60 * 1000L;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a particular message handler instance to be used when
|
|
||||||
* processing message events with the specified name.
|
|
||||||
*
|
|
||||||
* @param name the message name of the message events that should be
|
|
||||||
* handled by this handler.
|
|
||||||
* @param handler the handler to be registered.
|
|
||||||
*/
|
|
||||||
public void registerMessageHandler (String name, MessageHandler handler)
|
|
||||||
{
|
|
||||||
// create our handler map if necessary
|
|
||||||
if (_msghandlers == null) {
|
|
||||||
_msghandlers = new HashMap<String,MessageHandler>();
|
|
||||||
}
|
|
||||||
_msghandlers.put(name, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches message events to registered message handlers. Derived
|
|
||||||
* classes should probably register message handlers rather than
|
|
||||||
* override this method directly.
|
|
||||||
*/
|
|
||||||
public void messageReceived (MessageEvent event)
|
|
||||||
{
|
|
||||||
MessageHandler handler = null;
|
|
||||||
if (_msghandlers != null) {
|
|
||||||
handler = _msghandlers.get(event.getName());
|
|
||||||
}
|
|
||||||
if (handler != null) {
|
|
||||||
handler.handleEvent(event, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles occupant arrival into the place. Derived classes may need
|
|
||||||
* to override this method to handle other oid lists in their derived
|
|
||||||
* place objects. They should be sure to call
|
|
||||||
* <code>super.objectAdded</code> if the event is one they don't
|
|
||||||
* explicitly handle.
|
|
||||||
*/
|
|
||||||
public void objectAdded (ObjectAddedEvent event)
|
|
||||||
{
|
|
||||||
if (event.getName().equals(PlaceObject.OCCUPANTS)) {
|
|
||||||
bodyEntered(event.getOid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles occupant departure from the place. Derived classes may need
|
|
||||||
* to override this method to handle other oid lists in their derived
|
|
||||||
* place objects. They should be sure to call
|
|
||||||
* <code>super.objectRemoved</code> if the event is one they don't
|
|
||||||
* explicitly handle.
|
|
||||||
*/
|
|
||||||
public void objectRemoved (ObjectRemovedEvent event)
|
|
||||||
{
|
|
||||||
if (event.getName().equals(PlaceObject.OCCUPANTS)) {
|
|
||||||
bodyLeft(event.getOid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles place destruction. We shut ourselves down and ask the place
|
|
||||||
* registry to unmap us.
|
|
||||||
*/
|
|
||||||
public void objectDestroyed (ObjectDestroyedEvent event)
|
|
||||||
{
|
|
||||||
// unregister ourselves
|
|
||||||
_registry.unmapPlaceManager(this);
|
|
||||||
|
|
||||||
// let our derived classes and delegates shut themselves down
|
|
||||||
didShutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public void entryAdded (EntryAddedEvent event)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public void entryUpdated (EntryUpdatedEvent event)
|
|
||||||
{
|
|
||||||
if (event.getName().equals(PlaceObject.OCCUPANT_INFO)) {
|
|
||||||
bodyUpdated((OccupantInfo)event.getEntry());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public void entryRemoved (EntryRemovedEvent event)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public boolean isValidSpeaker (
|
|
||||||
DObject speakObj, ClientObject speaker, byte mode)
|
|
||||||
{
|
|
||||||
// only allow people in the room to speak
|
|
||||||
return _plobj.occupants.contains(speaker.getOid());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a string representation of this manager. Does so in a way
|
|
||||||
* that makes it easier for derived classes to add to the string
|
|
||||||
* representation.
|
|
||||||
*
|
|
||||||
* @see #toString(StringBuilder)
|
|
||||||
*/
|
|
||||||
public String toString ()
|
|
||||||
{
|
|
||||||
StringBuilder buf = new StringBuilder();
|
|
||||||
buf.append("[");
|
|
||||||
toString(buf);
|
|
||||||
buf.append("]");
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An extensible way to add to the string representation of this
|
* An extensible way to add to the string representation of this
|
||||||
* class. Override this (being sure to call super) and append your
|
* class. Override this (being sure to call super) and append your
|
||||||
@@ -699,6 +684,15 @@ public class PlaceManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Listens for occupant updates. */
|
||||||
|
protected SetAdapter _bodyUpdater = new SetAdapter() {
|
||||||
|
public void entryUpdated (EntryUpdatedEvent event) {
|
||||||
|
if (event.getName().equals(PlaceObject.OCCUPANT_INFO)) {
|
||||||
|
bodyUpdated((OccupantInfo)event.getEntry());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** A reference to the place registry with which we're registered. */
|
/** A reference to the place registry with which we're registered. */
|
||||||
protected PlaceRegistry _registry;
|
protected PlaceRegistry _registry;
|
||||||
|
|
||||||
@@ -729,4 +723,9 @@ public class PlaceManager
|
|||||||
* down after a certain period of idility, or null if no
|
* down after a certain period of idility, or null if no
|
||||||
* interval is currently registered. */
|
* interval is currently registered. */
|
||||||
protected Interval _shutdownInterval;
|
protected Interval _shutdownInterval;
|
||||||
|
|
||||||
|
/** We use this to do our method lookup magic when we receive message
|
||||||
|
* events. */
|
||||||
|
protected DynamicEventDispatcher _dispatcher =
|
||||||
|
new DynamicEventDispatcher(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Narya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2006 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;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import com.samskivert.util.MethodFinder;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import static com.threerings.presents.Log.log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps distributed object events to methods using reflection.
|
||||||
|
*/
|
||||||
|
public class DynamicEventDispatcher
|
||||||
|
implements AttributeChangeListener, ElementUpdateListener, SetListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a dynamic event dispatcher on the supplied target.
|
||||||
|
*/
|
||||||
|
public DynamicEventDispatcher (Object target)
|
||||||
|
{
|
||||||
|
_target = target;
|
||||||
|
_finder = new MethodFinder(target.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface AttributeChangeListener
|
||||||
|
public void attributeChanged (AttributeChangedEvent event)
|
||||||
|
{
|
||||||
|
dispatchMethod(event.getName() + "Changed",
|
||||||
|
new Object[] { event.getValue() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface ElementUpdateListener
|
||||||
|
public void elementUpdated (ElementUpdatedEvent event)
|
||||||
|
{
|
||||||
|
dispatchMethod(event.getName() + "Updated",
|
||||||
|
new Object[] { event.getIndex(), event.getValue() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface SetListener
|
||||||
|
public void entryAdded (EntryAddedEvent event)
|
||||||
|
{
|
||||||
|
dispatchMethod(event.getName() + "Added",
|
||||||
|
new Object[] { event.getEntry() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface SetListener
|
||||||
|
public void entryUpdated (EntryUpdatedEvent event)
|
||||||
|
{
|
||||||
|
dispatchMethod(event.getName() + "Updated",
|
||||||
|
new Object[] { event.getEntry() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface SetListener
|
||||||
|
public void entryRemoved (EntryRemovedEvent event)
|
||||||
|
{
|
||||||
|
dispatchMethod(event.getName() + "Removed",
|
||||||
|
new Object[] { event.getKey() });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamically looks up the method in question on our target and dispatches
|
||||||
|
* an event if it does.
|
||||||
|
*/
|
||||||
|
public void dispatchMethod (String name, Object[] arguments)
|
||||||
|
{
|
||||||
|
// first check the cache
|
||||||
|
Method method = _mcache.get(name);
|
||||||
|
if (method == null) {
|
||||||
|
// if we haven't already determined this method doesn't exist, try
|
||||||
|
// to resolve it
|
||||||
|
if (!_mcache.containsKey(name)) {
|
||||||
|
_mcache.put(name, method = resolveMethod(name, arguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method != null) {
|
||||||
|
try {
|
||||||
|
method.invoke(_target, arguments);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.log(Level.WARNING, "Failed to dispatch event callback " +
|
||||||
|
name + "(" + StringUtil.toString(arguments) + ").", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for a method that matches the supplied signature.
|
||||||
|
*/
|
||||||
|
protected Method resolveMethod (String name, Object[] arguments)
|
||||||
|
{
|
||||||
|
Class clazz = _target.getClass();
|
||||||
|
Class[] ptypes = new Class[arguments.length];
|
||||||
|
for (int ii = 0; ii < arguments.length; ii++) {
|
||||||
|
ptypes[ii] = arguments[ii] == null ?
|
||||||
|
Object.class : arguments[ii].getClass();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return _finder.findMethod(name, ptypes);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The object on which we will dynamically dispatch events. */
|
||||||
|
protected Object _target;
|
||||||
|
|
||||||
|
/** Used to look up methods. */
|
||||||
|
protected MethodFinder _finder;
|
||||||
|
|
||||||
|
/** A cache of already resolved methods. */
|
||||||
|
protected HashMap<String,Method> _mcache = new HashMap<String,Method>();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user