diff --git a/src/java/com/threerings/crowd/client/LocationDirector.java b/src/java/com/threerings/crowd/client/LocationDirector.java index 9e8669b20..5f32a5b83 100644 --- a/src/java/com/threerings/crowd/client/LocationDirector.java +++ b/src/java/com/threerings/crowd/client/LocationDirector.java @@ -1,5 +1,5 @@ // -// $Id: LocationDirector.java,v 1.9 2001/10/01 22:14:55 mdb Exp $ +// $Id: LocationDirector.java,v 1.10 2001/10/05 23:57:26 mdb Exp $ package com.threerings.cocktail.party.client; @@ -10,8 +10,7 @@ import com.threerings.cocktail.cher.client.*; import com.threerings.cocktail.cher.dobj.*; import com.threerings.cocktail.party.Log; -import com.threerings.cocktail.party.data.BodyObject; -import com.threerings.cocktail.party.data.PlaceObject; +import com.threerings.cocktail.party.data.*; import com.threerings.cocktail.party.util.PartyContext; /** @@ -127,13 +126,27 @@ public class LocationDirector * effected. * * @param placeId the place oid of our new location. + * @param config the configuration information for the new place. */ - protected void didMoveTo (int placeId) + protected void didMoveTo (int placeId, PlaceConfig config) { DObjectManager omgr = _ctx.getDObjectManager(); - // unsubscribe from our old place object + // do some cleaning up if we were previously in a place if (_plobj != null) { + // let the old controller know that things are going away + if (_controller != null) { + try { + _controller.didLeavePlace(_plobj); + } catch (Exception e) { + Log.warning("Place controller choked in " + + "didLeavePlace [plobj=" + _plobj + "]."); + Log.logStackTrace(e); + } + _controller = null; + } + + // unsubscribe from our old place object omgr.unsubscribeFromObject(_plobj.getOid(), this); _plobj = null; } @@ -142,6 +155,18 @@ public class LocationDirector _previousPlaceId = _placeId; _placeId = placeId; + try { + // start up a new place controller to manage the new place + Class cclass = config.getControllerClass(); + _controller = (PlaceController)cclass.newInstance(); + _controller.init(_ctx, config); + + } catch (Exception e) { + Log.warning("Error creating or initializing place controller " + + "[config=" + config + "]."); + Log.logStackTrace(e); + } + // subscribe to our new place object to complete the move omgr.subscribeToObject(_placeId, this); } @@ -217,10 +242,10 @@ public class LocationDirector /** * Called in response to a successful moveTo request. */ - public void handleMoveSucceeded (int invid) + public void handleMoveSucceeded (int invid, PlaceConfig config) { // handle the successful move - didMoveTo(_pendingPlaceId); + didMoveTo(_pendingPlaceId, config); // and clear out the tracked pending oid _pendingPlaceId = -1; @@ -244,6 +269,15 @@ public class LocationDirector // yay, we have our new place object _plobj = (PlaceObject)object; + // let the place controller know that we're ready to roll + try { + _controller.willEnterPlace(_plobj); + } catch (Exception e) { + Log.warning("Controller choked in willEnterPlace " + + "[place=" + _plobj + "]."); + Log.logStackTrace(e); + } + // let our observers know that all is well on the western front for (int i = 0; i < _observers.size(); i++) { LocationObserver obs = (LocationObserver)_observers.get(i); @@ -265,6 +299,11 @@ public class LocationDirector // let the kids know shit be fucked notifyFailure(placeId, "m.unable_to_fetch_place_object"); + // we need to sort out what to do about the half-initialized place + // controller. presently we punt and hope that calling + // didLeavePlace() without ever having called willEnterPlace() + // does whatever's necessary + // try to return to our previous location recoverFailedMove(placeId); } @@ -313,6 +352,9 @@ public class LocationDirector /** The place object that we currently occupy. */ protected PlaceObject _plobj; + /** The place controller in effect for our current place. */ + protected PlaceController _controller; + /** * The oid of the place for which we have an outstanding moveTo * request, or -1 if we have no outstanding request. diff --git a/src/java/com/threerings/crowd/client/PlaceController.java b/src/java/com/threerings/crowd/client/PlaceController.java new file mode 100644 index 000000000..7cc045a26 --- /dev/null +++ b/src/java/com/threerings/crowd/client/PlaceController.java @@ -0,0 +1,104 @@ +// +// $Id: PlaceController.java,v 1.1 2001/10/05 23:57:26 mdb Exp $ + +package com.threerings.cocktail.party.client; + +import java.awt.event.ActionEvent; +import com.samskivert.swing.Controller; + +import com.threerings.cocktail.party.data.PlaceConfig; +import com.threerings.cocktail.party.data.PlaceObject; +import com.threerings.cocktail.party.util.PartyContext; + +/** + * Controls the user interface that is used to display a place. When the + * client moves to a new place, the appropriate place controller is + * constructed and requested to create and display the appopriate user + * interface for that place. + */ +public abstract class PlaceController + extends Controller +{ + /** + * Initializes this place controller with a reference to the context + * that they can use to access client services and to the + * configuration record for this place. The controller should create + * as much of its user interface that it can without having access to + * the place object because this will be invoked in parallel with the + * fetching of the place object. When the place object is obtained, + * the controller will be notified and it can then finish the user + * interface configuration and put the user interface into operation. + * + * @param ctx the client context. + * @param config the place configuration for this place. + */ + public void init (PartyContext ctx, PlaceConfig config) + { + // keep these around + _ctx = ctx; + _config = config; + + // create our user interface + _view = createPlaceView(); + } + + /** + * Creates the user interface that will be used to display this place. + * The view instance returned will later be configured with the place + * object, once it becomes available. + */ + protected abstract PlaceView createPlaceView (); + + /** + * This is called by the location director once the place object has + * been fetched. The place controller will dispatch the place object + * to the user interface hierarchy via {@link + * PlaceViewUtil.dispatchWillEnterPlace}. Derived classes can override + * this and perform any other starting up that they need to do + */ + public void willEnterPlace (PlaceObject plobj) + { + // let the UI hierarchy know that we've got our place + if (_view != null ) { + PlaceViewUtil.dispatchWillEnterPlace(_view, plobj); + } + } + + /** + * This is called by the location director when we are leaving this + * place and need to clean up after ourselves and shutdown. Derived + * classes should override this method (being sure to call + * super.didLeavePlace) and perform any necessary + * cleanup. + */ + public void didLeavePlace (PlaceObject plobj) + { + // let the UI hierarchy know that we're outta here + if (_view != null ) { + PlaceViewUtil.dispatchDidLeavePlace(_view, plobj); + } + } + + /** + * Handles basic place controller action events. Derived classes + * should be sure to call super.handleAction for events + * they don't specifically handle. + */ + public boolean handleAction (ActionEvent action) + { + return false; + } + + /** A reference to the active client context. */ + protected PartyContext _ctx; + + /** A reference to our place configuration. */ + protected PlaceConfig _config; + + /** A reference to the place object for which we're controlling a user + * interface. */ + protected PlaceObject _plobj; + + /** A reference to the root user interface component. */ + protected PlaceView _view; +} diff --git a/src/java/com/threerings/crowd/client/PlaceDirector.java b/src/java/com/threerings/crowd/client/PlaceDirector.java deleted file mode 100644 index 3ac22755c..000000000 --- a/src/java/com/threerings/crowd/client/PlaceDirector.java +++ /dev/null @@ -1,8 +0,0 @@ -// -// $Id: PlaceDirector.java,v 1.1 2001/07/20 20:07:37 mdb Exp $ - -package com.threerings.cocktail.party.client; - -public class PlaceDirector -{ -} diff --git a/src/java/com/threerings/crowd/client/PlaceView.java b/src/java/com/threerings/crowd/client/PlaceView.java index 9a8994063..b1c3b4a91 100644 --- a/src/java/com/threerings/crowd/client/PlaceView.java +++ b/src/java/com/threerings/crowd/client/PlaceView.java @@ -1,7 +1,7 @@ // -// $Id: PlaceView.java,v 1.2 2001/10/04 22:47:48 mdb Exp $ +// $Id: PlaceView.java,v 1.3 2001/10/05 23:57:26 mdb Exp $ -package com.threerings.cocktail.party.util; +package com.threerings.cocktail.party.client; import com.threerings.cocktail.party.data.PlaceObject; diff --git a/src/java/com/threerings/crowd/client/PlaceViewUtil.java b/src/java/com/threerings/crowd/client/PlaceViewUtil.java index 2f87f0b82..c0786131e 100644 --- a/src/java/com/threerings/crowd/client/PlaceViewUtil.java +++ b/src/java/com/threerings/crowd/client/PlaceViewUtil.java @@ -1,9 +1,8 @@ // -// $Id: PlaceViewUtil.java,v 1.1 2001/10/04 20:02:49 mdb Exp $ +// $Id: PlaceViewUtil.java,v 1.2 2001/10/05 23:57:26 mdb Exp $ -package com.threerings.cocktail.party.util; +package com.threerings.cocktail.party.client; -import java.awt.Component; import java.awt.Container; import com.threerings.cocktail.party.Log; @@ -27,7 +26,7 @@ public class PlaceViewUtil * @param plobj the place object that is about to be entered. */ public static void dispatchWillEnterPlace ( - Component root, PlaceObject plobj) + Object root, PlaceObject plobj) { // dispatch the call on this component if it implements PlaceView if (root instanceof PlaceView) { @@ -60,7 +59,7 @@ public class PlaceViewUtil * @param plobj the place object that is about to be entered. */ public static void dispatchDidLeavePlace ( - Component root, PlaceObject plobj) + Object root, PlaceObject plobj) { // dispatch the call on this component if it implements PlaceView if (root instanceof PlaceView) { diff --git a/src/java/com/threerings/crowd/data/PlaceConfig.java b/src/java/com/threerings/crowd/data/PlaceConfig.java new file mode 100644 index 000000000..b46c4c483 --- /dev/null +++ b/src/java/com/threerings/crowd/data/PlaceConfig.java @@ -0,0 +1,94 @@ +// +// $Id: PlaceConfig.java,v 1.1 2001/10/05 23:57:26 mdb Exp $ + +package com.threerings.cocktail.party.data; + +import java.io.IOException; +import java.io.DataInputStream; +import java.io.DataOutputStream; + +import com.threerings.cocktail.cher.io.Streamable; + +/** + * The place config class encapsulates the configuration information for a + * particular type of place. The hierarchy of place config objects mimics + * the hierarchy of place managers and controllers. Both the place manager + * and place controller are provided with the place config object when the + * place is created. + * + *

The place config object is also the mechanism used to instantiate + * the appropriate place manager and controller. Every place must have an + * associated place config derived class that overrides {@link + * #getControllerClass} and {@link #getManagerClassName}, returning the + * appropriate place controller and manager class for that place. + * + *

A place that has specific configuration needs would extend this + * class (or an appropriate subclass) adding it's configuration + * information and overriding {@link #writeTo} and {@link #readFrom} to + * provide code to serialize and unserialize the additional fields. + */ +public abstract class PlaceConfig implements Streamable +{ + /** The oid of the place object for which we represent the + * configuration information. */ + public int placeOid; + + /** + * Returns the class that should be used to create a controller for + * this place. The controller class must derive from {@link + * com.threerings.cocktail.party.client.PlaceController}. + */ + public abstract Class getControllerClass (); + + /** + * Returns the name of the class that should be used to create a + * manager for this place. The manager class must derive from {@link + * com.threerings.cocktail.party.server.PlaceManager}. Note: + * this method differs from {@link #getControllerClass} because we + * want to avoid compile time linkage of the place config object + * (which is used on the client) to server code. This allows a code + * optimizer (DashO Pro, for example) to remove the server code from + * the client, knowing that it is never used. + */ + public abstract String getManagerClassName (); + + // documentation inherited + public void writeTo (DataOutputStream out) + throws IOException + { + out.writeInt(placeOid); + } + + // documentation inherited + public void readFrom (DataInputStream in) + throws IOException + { + placeOid = in.readInt(); + } + + /** + * Generates a string representation of this object by calling the + * overridable {@link #toString(StringBuffer)} which builds up the + * string in a manner friendly to derived classes. + */ + public String toString () + { + StringBuffer buf = new StringBuffer(); + buf.append("["); + toString(buf); + buf.append("]"); + return buf.toString(); + } + + /** + * An extensible mechanism for generating a string representation of + * this object. Derived classes should override this method, calling + * super and then appending their own data to the supplied string + * buffer. The regular {@link #toString} function will call this + * derived function to generate its string. + */ + protected void toString (StringBuffer buf) + { + buf.append("type=").append(getClass().getName()); + } +} diff --git a/src/java/com/threerings/crowd/server/LocationProvider.java b/src/java/com/threerings/crowd/server/LocationProvider.java index 54749b6ff..dc152b7f1 100644 --- a/src/java/com/threerings/crowd/server/LocationProvider.java +++ b/src/java/com/threerings/crowd/server/LocationProvider.java @@ -1,10 +1,11 @@ // -// $Id: LocationProvider.java,v 1.8 2001/10/01 22:14:55 mdb Exp $ +// $Id: LocationProvider.java,v 1.9 2001/10/05 23:57:26 mdb Exp $ package com.threerings.cocktail.party.server; import com.threerings.cocktail.cher.dobj.DObject; import com.threerings.cocktail.cher.server.InvocationProvider; +import com.threerings.cocktail.cher.server.ServiceFailedException; import com.threerings.cocktail.party.Log; import com.threerings.cocktail.party.client.LocationCodes; @@ -23,14 +24,15 @@ public class LocationProvider public void handleMoveToRequest (BodyObject source, int invid, int placeId) { - // try to do the actual move - String rcode = moveTo(source, placeId); + try { + // do the move + PlaceConfig config = moveTo(source, placeId); + // and send the response + sendResponse(source, invid, MOVE_SUCCEEDED_RESPONSE, config); - // send the response - if (rcode.equals(SUCCESS)) { - sendResponse(source, invid, MOVE_SUCCEEDED_RESPONSE); - } else { - sendResponse(source, invid, MOVE_FAILED_RESPONSE, rcode); + } catch (ServiceFailedException sfe) { + sendResponse(source, invid, MOVE_FAILED_RESPONSE, + sfe.getMessage()); } } @@ -38,10 +40,14 @@ public class LocationProvider * Moves the specified body from whatever location they currently * occupy to the location identified by the supplied place id. * - * @return the string SUCCESS if the move was successful - * or a reason code for failure if not. + * @return the config object for the new location. + * + * @exception ServiceFaildException thrown if the move was not + * successful for some reason (which will be communicated as an error + * code in the exception's message data). */ - public static String moveTo (BodyObject source, int placeId) + public static PlaceConfig moveTo (BodyObject source, int placeId) + throws ServiceFailedException { int bodoid = source.getOid(); @@ -50,7 +56,7 @@ public class LocationProvider if (pmgr == null) { Log.info("Requested to move to non-existent place " + "[source=" + source + ", place=" + placeId + "]."); - return NO_SUCH_PLACE; + throw new ServiceFailedException(NO_SUCH_PLACE); } // acquire a lock on the body object to ensure that rapid fire @@ -58,13 +64,13 @@ public class LocationProvider if (!source.acquireLock("moveToLock")) { // if we're still locked, a previous moveTo request hasn't // been fully processed - return MOVE_IN_PROGRESS; + throw new ServiceFailedException(MOVE_IN_PROGRESS); } // make sure they're not already in the location they're asking to // move to if (source.location == placeId) { - return ALREADY_THERE; + throw new ServiceFailedException(ALREADY_THERE); } // find out if they were previously in some other location @@ -112,6 +118,6 @@ public class LocationProvider // once all these events are processed source.releaseLock("moveToLock"); - return SUCCESS; + return pmgr.getConfig(); } } diff --git a/src/java/com/threerings/crowd/server/PlaceManager.java b/src/java/com/threerings/crowd/server/PlaceManager.java index f4d7ebacd..3fd02a2ce 100644 --- a/src/java/com/threerings/crowd/server/PlaceManager.java +++ b/src/java/com/threerings/crowd/server/PlaceManager.java @@ -1,5 +1,5 @@ // -// $Id: PlaceManager.java,v 1.13 2001/10/02 02:07:50 mdb Exp $ +// $Id: PlaceManager.java,v 1.14 2001/10/05 23:57:26 mdb Exp $ package com.threerings.cocktail.party.server; @@ -33,11 +33,19 @@ import com.threerings.cocktail.party.data.*; public class PlaceManager implements Subscriber { /** - * Called by the place registry after creating this place manager. + * Returns a reference to our place configuration object. */ - public void setPlaceRegistry (PlaceRegistry registry) + public PlaceConfig getConfig () { - _registry = registry; + return _config; + } + + /** + * Returns the place object managed by this place manager. + */ + public PlaceObject getPlaceObject () + { + return _plobj; } /** @@ -58,6 +66,29 @@ public class PlaceManager implements Subscriber return PlaceObject.class; } + /** + * Called by the place registry after creating this place manager. + */ + public void init (PlaceRegistry registry, PlaceConfig config) + { + _registry = registry; + _config = config; + + // let derived classes do initialization stuff + didInit(); + } + + /** + * Called after this place manager has been initialized with its + * configuration information but before it has been started up with + * its place object reference. Derived classes can override this + * function and perform any basic initialization that they desire. + * They should of course be sure to call super.didInit(). + */ + protected void didInit () + { + } + /** * Called by the place manager after the place object has been * successfully created. @@ -194,14 +225,6 @@ public class PlaceManager implements Subscriber _msghandlers.put(name, handler); } - /** - * Returns the place object managed by this place manager. - */ - public PlaceObject getPlaceObject () - { - return _plobj; - } - // nothing doing public void objectAvailable (DObject object) { @@ -269,6 +292,7 @@ public class PlaceManager implements Subscriber protected void toString (StringBuffer buf) { buf.append("place=").append(_plobj); + buf.append(", config=").append(_config); } /** @@ -291,6 +315,9 @@ public class PlaceManager implements Subscriber /** A reference to the place object that we manage. */ protected PlaceObject _plobj; + /** A reference to the configuration for our place. */ + protected PlaceConfig _config; + /** A reference to the place registry with which we're registered. */ protected PlaceRegistry _registry; diff --git a/src/java/com/threerings/crowd/server/PlaceRegistry.java b/src/java/com/threerings/crowd/server/PlaceRegistry.java index 165903755..dd49e04a5 100644 --- a/src/java/com/threerings/crowd/server/PlaceRegistry.java +++ b/src/java/com/threerings/crowd/server/PlaceRegistry.java @@ -1,5 +1,5 @@ // -// $Id: PlaceRegistry.java,v 1.9 2001/10/02 02:07:50 mdb Exp $ +// $Id: PlaceRegistry.java,v 1.10 2001/10/05 23:57:26 mdb Exp $ package com.threerings.cocktail.party.server; @@ -12,6 +12,7 @@ import com.samskivert.util.Queue; import com.threerings.cocktail.cher.dobj.*; import com.threerings.cocktail.party.Log; +import com.threerings.cocktail.party.data.PlaceConfig; import com.threerings.cocktail.party.data.PlaceObject; /** @@ -36,8 +37,10 @@ public class PlaceRegistry implements Subscriber * creation of the object and informing the manager when it is * created. * - * @param pmgrClass the {@link PlaceManager} derived class that should - * be instantiated to manage the place. + * @param config the configuration object for the place to be + * created. The {@link PlaceManager} derived class that should be + * instantiated to manage the place will be determined from the config + * object. * * @return a reference to the place manager that will manage the new * place object. @@ -45,14 +48,16 @@ public class PlaceRegistry implements Subscriber * @exception InstantiationException thrown if an error occurs trying * to instantiate and initialize the place manager. */ - public PlaceManager createPlace (Class pmgrClass) + public PlaceManager createPlace (PlaceConfig config) throws InstantiationException { try { + // load up the manager class + Class pmgrClass = Class.forName(config.getManagerClassName()); // create a place manager for this place PlaceManager pmgr = (PlaceManager)pmgrClass.newInstance(); - // let the pmgr know about us - pmgr.setPlaceRegistry(this); + // let the pmgr know about us and its configuration + pmgr.init(this, config); // stick the manager on the creation queue because we know // we'll get our calls to objectAvailable()/requestFailed() in @@ -65,9 +70,10 @@ public class PlaceRegistry implements Subscriber return pmgr; - } catch (IllegalAccessException iae) { + } catch (Exception e) { + Log.logStackTrace(e); throw new InstantiationException( - "Error instantiating place manager: " + iae); + "Error creating place manager [config=" + config + "]."); } }