diff --git a/src/java/com/threerings/crowd/client/LocationDirector.java b/src/java/com/threerings/crowd/client/LocationDirector.java index ad5c6a823..bcee89f7c 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.15 2001/12/13 06:34:40 mdb Exp $ +// $Id: LocationDirector.java,v 1.16 2001/12/16 05:18:20 mdb Exp $ package com.threerings.crowd.client; @@ -23,6 +23,24 @@ import com.threerings.crowd.util.CrowdContext; public class LocationDirector implements ClientObserver, Subscriber { + /** + * Used to recover from a moveTo request that was accepted but + * resulted in a failed attempt to fetch the place object to which we + * were moving. + */ + public static interface FailureHandler + { + /** + * Should instruct the client to move to the last known working + * location (as well as clean up after the failed moveTo request). + */ + public void recoverFailedMove (int placeId); + } + + /** + * Constructs a location director which will configure itself for + * operation using the supplied context. + */ public LocationDirector (CrowdContext ctx) { // keep this around for later @@ -111,18 +129,18 @@ public class LocationDirector } /** - * This can be called by derived classes that need to coopt the moving - * process to extend it in some way or other. In such situations, they - * should call this method before moving to a new location to check to - * be sure that all of the registered location observers are amenable - * to a location change. + * This can be called by cooperating directors that need to coopt the + * moving process to extend it in some way or other. In such + * situations, they should call this method before moving to a new + * location to check to be sure that all of the registered location + * observers are amenable to a location change. * * @param placeId the place oid of our tentative new location. * * @return true if everyone is happy with the move, false if it was * vetoed by one of the location observers. */ - protected boolean mayMoveTo (int placeId) + public boolean mayMoveTo (int placeId) { for (int i = 0; i < _observers.size(); i++) { LocationObserver obs = (LocationObserver)_observers.get(i); @@ -137,16 +155,16 @@ public class LocationDirector } /** - * This can be called by derived classes that need to coopt the moving - * process to extend it in some way or other. In such situations, they - * will be responsible for receiving the successful move response and - * they should let the location director know that the move has been - * effected. + * This can be called by cooperating directors that need to coopt the + * moving process to extend it in some way or other. In such + * situations, they will be responsible for receiving the successful + * move response and they should let the location director know that + * the move has been effected. * * @param placeId the place oid of our new location. * @param config the configuration information for the new place. */ - protected void didMoveTo (int placeId, PlaceConfig config) + public void didMoveTo (int placeId, PlaceConfig config) { DObjectManager omgr = _ctx.getDObjectManager(); @@ -191,15 +209,15 @@ public class LocationDirector } /** - * This can be called by derived classes that need to coopt the moving - * process to extend it in some way or other. If the coopted move - * request fails, this failure can be propagated to the location + * This can be called by cooperating directors that need to coopt the + * moving process to extend it in some way or other. If the coopted + * move request fails, this failure can be propagated to the location * observers if appropriate. * * @param placeId the place oid to which we failed to move. * @param reason the reason code given for failure. */ - protected void failedToMoveTo (int placeId, String reason) + public void failedToMoveTo (int placeId, String reason) { // let our observers know what's up notifyFailure(placeId, reason); @@ -281,6 +299,10 @@ public class LocationDirector notifyFailure(placeId, reason); } + /** + * Called when we receive the place object to which we subscribed + * after a successful moveTo request. + */ public void objectAvailable (DObject object) { // yay, we have our new place object @@ -304,6 +326,12 @@ public class LocationDirector } } + /** + * Called if we are unable to subscribe to the place object that was + * provided to us with our successful moveTo request. This is + * generally a bad scene and we do our best to recover by going back + * to the previously known location. + */ public void requestFailed (int oid, ObjectAccessException cause) { // aiya! we were unable to fetch our new place object; something @@ -324,24 +352,40 @@ public class LocationDirector // does whatever's necessary // try to return to our previous location - recoverFailedMove(placeId); + if (_failureHandler != null) { + _failureHandler.recoverFailedMove(placeId); + + } else { + // if we were previously somewhere (and that somewhere isn't + // where we just tried to go), try going back to that happy + // place + if (_previousPlaceId != -1 && _previousPlaceId != placeId) { + moveTo(_previousPlaceId); + } + } } /** - * If a moveTo request fails because we are unable to - * fetch our new place object, we need to do something to recover. By - * default that means attempting to return to the last location we - * occupied, but derived classes may need to do things differently. - * - * @param placeId the place id that we tried to move to but that - * failed. + * Sets the failure handler which will recover from place object + * fetching failures. In the event that we are unable to fetch our + * place object after making a successful moveTo request, we attempt + * to rectify the failure by moving back to the last known working + * location. Because entites that cooperate with the location director + * may need to become involved in this failure recovery, we provide + * this interface whereby they can interject themseves into the + * failure recovery process and do their own failure recovery. */ - protected void recoverFailedMove (int placeId) + public void setFailureHandler (FailureHandler handler) { - // if we were previously somewhere (and that somewhere isn't where - // we just tried to go), try going back to that happy place - if (_previousPlaceId != -1 && _previousPlaceId != placeId) { - moveTo(_previousPlaceId); + if (_failureHandler != null) { + Log.warning("Requested to set failure handler, but we've " + + "already got one. The conflicting entities will " + + "likely need to perform more sophisticated " + + "coordination to deal with failures. " + + "[old=" + _failureHandler + ", new=" + handler + "]."); + + } else { + _failureHandler = handler; } } @@ -376,4 +420,8 @@ public class LocationDirector /** The oid of the place we previously occupied. */ protected int _previousPlaceId = -1; + + /** The entity that deals when we fail to subscribe to a place + * object. */ + protected FailureHandler _failureHandler; } diff --git a/src/java/com/threerings/whirled/client/SceneDirector.java b/src/java/com/threerings/whirled/client/SceneDirector.java index d6b5c742e..549e4bed5 100644 --- a/src/java/com/threerings/whirled/client/SceneDirector.java +++ b/src/java/com/threerings/whirled/client/SceneDirector.java @@ -1,5 +1,5 @@ // -// $Id: SceneDirector.java,v 1.9 2001/12/14 01:51:45 mdb Exp $ +// $Id: SceneDirector.java,v 1.10 2001/12/16 05:18:20 mdb Exp $ package com.threerings.whirled.client; @@ -24,7 +24,7 @@ import com.threerings.whirled.util.WhirledContext; * The scene director is the client's interface to all things scene * related. It interfaces with the scene repository to ensure that scene * objects are available when the client enters a particular scene. It - * handles moving from scene to scene (it extends and replaces the {@link + * handles moving from scene to scene (it coordinates with the {@link * LocationDirector} in order to do this). * *

Note that when the scene director is in use instead of the location @@ -33,26 +33,31 @@ import com.threerings.whirled.util.WhirledContext; * LocationObserver#locationChangeFailed}. */ public class SceneDirector - extends LocationDirector implements SceneCodes + implements SceneCodes, LocationDirector.FailureHandler { /** * Creates a new scene director with the specified context. * * @param ctx the active client context. + * @param locdir the location director in use on the client, with + * which the scene director will coordinate when changing location. * @param screp the entity from which the scene director will load * scene data from the local client scene storage. * @param dsfact the factory that knows which derivation of {@link * DisplayScene} to create for the current system. */ - public SceneDirector (WhirledContext ctx, SceneRepository screp, - DisplaySceneFactory dsfact) + public SceneDirector (WhirledContext ctx, LocationDirector locdir, + SceneRepository screp, DisplaySceneFactory dsfact) { - super(ctx); - // we'll need these for later _ctx = ctx; + _locdir = locdir; _screp = screp; _dsfact = dsfact; + + // set ourselves up as a failure handler with the location + // director because we need to do special processing + _locdir.setFailureHandler(this); } /** @@ -91,13 +96,14 @@ public class SceneDirector /** * Prepares to move to the requested scene. The location observers are * asked to ratify the move and our pending scene mode is loaded from - * the scene repository. + * the scene repository. This can be called by cooperating directors + * that need to coopt the moveTo process. */ - protected boolean prepareMoveTo (int sceneId) + public boolean prepareMoveTo (int sceneId) { // first check to see if our observers are happy with this move // request - if (!mayMoveTo(sceneId)) { + if (!_locdir.mayMoveTo(sceneId)) { return false; } @@ -125,6 +131,18 @@ public class SceneDirector return true; } + /** + * Returns the model loaded in preparation for a scene + * transition. This is made available only for cooperating directors + * which may need to coopt the scene transition process. The pending + * model is only valid immediately following a call to {@link + * #prepareMoveTo}. + */ + public SceneModel getPendingModel () + { + return _pendingModel; + } + /** * Called in response to a successful moveTo request. */ @@ -133,7 +151,7 @@ public class SceneDirector { // our move request was successful, deal with subscribing to our // new place object - didMoveTo(placeId, config); + _locdir.didMoveTo(placeId, config); // since we're committed to moving to the new scene, we'll // parallelize and go ahead and load up the new scene now rather @@ -199,14 +217,14 @@ public class SceneDirector _pendingSceneId = -1; // let our observers know that something has gone horribly awry - notifyFailure(sceneId, reason); + _locdir.failedToMoveTo(sceneId, reason); } /** * Called when something breaks down in the process of performing a * moveTo request. */ - protected void recoverFailedMove (int placeId) + public void recoverFailedMove (int placeId) { // we'll need this momentarily int sceneId = _sceneId; @@ -282,6 +300,9 @@ public class SceneDirector /** Access to general client services. */ protected WhirledContext _ctx; + /** The client's active location director. */ + protected LocationDirector _locdir; + /** The entity via which we load scene data. */ protected SceneRepository _screp; diff --git a/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java b/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java index 6266d6166..4aeba856b 100644 --- a/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java +++ b/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java @@ -1,15 +1,13 @@ // -// $Id: SpotSceneDirector.java,v 1.3 2001/12/14 23:12:39 mdb Exp $ +// $Id: SpotSceneDirector.java,v 1.4 2001/12/16 05:18:20 mdb Exp $ package com.threerings.whirled.spot.client; import java.util.Iterator; import com.samskivert.util.StringUtil; -import com.threerings.whirled.client.DisplayScene; -import com.threerings.whirled.client.DisplaySceneFactory; import com.threerings.whirled.client.SceneDirector; -import com.threerings.whirled.client.persist.SceneRepository; +import com.threerings.whirled.data.SceneModel; import com.threerings.whirled.util.WhirledContext; import com.threerings.whirled.spot.Log; @@ -20,7 +18,7 @@ import com.threerings.whirled.spot.data.Portal; * Extends the standard scene director with facilities to move between * locations within a scene. */ -public class SpotSceneDirector extends SceneDirector +public class SpotSceneDirector implements SpotCodes { /** @@ -42,18 +40,16 @@ public class SpotSceneDirector extends SceneDirector } /** - * Creates a new spot scene director with the specified context. + * Creates a new spot scene director with the specified context and + * which will cooperate with the supplied scene director. * * @param ctx the active client context. - * @param screp the entity from which the scene director will load - * scene data from the local client scene storage. - * @param dsfact the factory that knows which derivation of {@link - * DisplayScene} to create for the current system. + * @param scdir the scene director with which we will be cooperating. */ - public SpotSceneDirector ( - WhirledContext ctx, SceneRepository screp, DisplaySceneFactory dsfact) + public SpotSceneDirector (WhirledContext ctx, SceneDirector scdir) { - super(ctx, screp, dsfact); + _ctx = ctx; + _scdir = scdir; } /** @@ -65,7 +61,8 @@ public class SpotSceneDirector extends SceneDirector public void traversePortal (int portalId) { // look up the destination scene and location - if (_scene == null) { + DisplaySpotScene scene = (DisplaySpotScene)_scdir.getScene(); + if (scene == null) { Log.warning("Requested to traverse portal when we have " + "no scene [portalId=" + portalId + "]."); return; @@ -73,8 +70,7 @@ public class SpotSceneDirector extends SceneDirector // find the portal they're talking about int targetSceneId = -1, targetLocId = -1; - DisplaySpotScene ds = (DisplaySpotScene)_scene; - Iterator portals = ds.getPortals().iterator(); + Iterator portals = scene.getPortals().iterator(); while (portals.hasNext()) { Portal portal = (Portal)portals.next(); if (portal.locationId == portalId) { @@ -85,14 +81,14 @@ public class SpotSceneDirector extends SceneDirector // make sure we found the portal if (targetSceneId == -1) { + portals = scene.getPortals().iterator(); Log.warning("Requested to traverse non-existent portal " + "[portalId=" + portalId + - ", portals=" + - StringUtil.toString(ds.getPortals().iterator()) + "]."); + ", portals=" + StringUtil.toString(portals) + "]."); } // prepare to move to this scene (sets up pending data) - if (!prepareMoveTo(targetSceneId)) { + if (!_scdir.prepareMoveTo(targetSceneId)) { return; } @@ -100,13 +96,14 @@ public class SpotSceneDirector extends SceneDirector // we're requesting to move; if we were unable to load it, assume // a cached version of zero int sceneVer = 0; - if (_pendingModel != null) { - sceneVer = _pendingModel.version; + SceneModel pendingModel = _scdir.getPendingModel(); + if (pendingModel != null) { + sceneVer = pendingModel.version; } // issue a traversePortal request SpotService.traversePortal( - _ctx.getClient(), _sceneId, portalId, sceneVer, this); + _ctx.getClient(), scene.getId(), portalId, sceneVer, _scdir); } /** @@ -127,7 +124,8 @@ public class SpotSceneDirector extends SceneDirector } // make sure we're currently in a scene - if (_sceneId == -1) { + DisplaySpotScene scene = (DisplaySpotScene)_scdir.getScene(); + if (scene == null) { Log.warning("Requested to change locations, but we're not " + "currently in any scene [locId=" + locationId + "]."); return; @@ -135,8 +133,7 @@ public class SpotSceneDirector extends SceneDirector // make sure the specified location is in the current scene int locidx = -1; - DisplaySpotScene sscene = (DisplaySpotScene)_scene; - Iterator locs = sscene.getLocations().iterator(); + Iterator locs = scene.getLocations().iterator(); for (int i = 0; locs.hasNext(); i++) { Location loc = (Location)locs.next(); if (loc.locationId == locationId) { @@ -147,7 +144,7 @@ public class SpotSceneDirector extends SceneDirector if (locidx == -1) { Log.warning("Requested to change to a location that's not " + "in the current scene [locs=" + StringUtil.toString( - sscene.getLocations().iterator()) + + scene.getLocations().iterator()) + ", locId=" + locationId + "]."); return; } @@ -156,7 +153,8 @@ public class SpotSceneDirector extends SceneDirector _pendingLocId = locationId; _changeObserver = obs; // and send the location change request - SpotService.changeLoc(_ctx.getClient(), _sceneId, locationId, this); + SpotService.changeLoc(_ctx.getClient(), scene.getId(), + locationId, this); } /** @@ -195,6 +193,12 @@ public class SpotSceneDirector extends SceneDirector } } + /** The active client context. */ + protected WhirledContext _ctx; + + /** The scene director with which we are cooperating. */ + protected SceneDirector _scdir; + /** The location id on which we have an outstanding change location * request. */ protected int _pendingLocId = -1; diff --git a/src/java/com/threerings/whirled/spot/client/SpotService.java b/src/java/com/threerings/whirled/spot/client/SpotService.java index e2b1e96ff..8206c05e2 100644 --- a/src/java/com/threerings/whirled/spot/client/SpotService.java +++ b/src/java/com/threerings/whirled/spot/client/SpotService.java @@ -1,11 +1,13 @@ // -// $Id: SpotService.java,v 1.2 2001/12/14 23:12:39 mdb Exp $ +// $Id: SpotService.java,v 1.3 2001/12/16 05:18:20 mdb Exp $ package com.threerings.whirled.spot.client; import com.threerings.presents.client.Client; import com.threerings.presents.client.InvocationDirector; +import com.threerings.whirled.client.SceneDirector; + import com.threerings.whirled.spot.Log; /** @@ -21,7 +23,7 @@ public class SpotService implements SpotCodes */ public static void traversePortal ( Client client, int sceneId, int portalId, int sceneVer, - SpotSceneDirector rsptarget) + SceneDirector rsptarget) { InvocationDirector invdir = client.getInvocationDirector(); Object[] args = new Object[] { diff --git a/src/java/com/threerings/whirled/zone/client/ZoneDirector.java b/src/java/com/threerings/whirled/zone/client/ZoneDirector.java index bd2080112..4a13595fc 100644 --- a/src/java/com/threerings/whirled/zone/client/ZoneDirector.java +++ b/src/java/com/threerings/whirled/zone/client/ZoneDirector.java @@ -1,21 +1,18 @@ // -// $Id: ZoneDirector.java,v 1.1 2001/12/04 00:31:58 mdb Exp $ +// $Id: ZoneDirector.java,v 1.2 2001/12/16 05:18:20 mdb Exp $ package com.threerings.whirled.zone.client; import com.threerings.crowd.data.PlaceConfig; -import com.threerings.whirled.client.DisplaySceneFactory; import com.threerings.whirled.client.SceneDirector; -import com.threerings.whirled.client.persist.SceneRepository; - import com.threerings.whirled.data.SceneModel; import com.threerings.whirled.util.WhirledContext; import com.threerings.whirled.zone.data.ZoneSummary; /** - * The zone director extends the scene director with the notion of zones. + * The zone director augments the scene services with the notion of zones. * Zones are self-contained, connected groups of scenes. The normal scene * director services can be used to move from scene to scene, but moving * to a new zone requires a special move request which can be accomplished @@ -23,17 +20,18 @@ import com.threerings.whirled.zone.data.ZoneSummary; * summary which provides information on the zone which can be used to * generate an overview map or similar. */ -public class ZoneDirector extends SceneDirector +public class ZoneDirector { /** - * Constructs a zone director with the supplied context, repository - * and scene factory. A zone director is required on the client side - * for systems that wish to use the zone services. + * Constructs a zone director with the supplied context, and delegate + * scene director (which the zone director will coordinate with when + * moving from scene to scene). A zone director is required on the + * client side for systems that wish to use the zone services. */ - public ZoneDirector (WhirledContext ctx, SceneRepository screp, - DisplaySceneFactory dsfact) + public ZoneDirector (WhirledContext ctx, SceneDirector scdir) { - super(ctx, screp, dsfact); + _ctx = ctx; + _scdir = scdir; } /** @@ -56,11 +54,11 @@ public class ZoneDirector extends SceneDirector // if the requested zone is the same as our current zone, we just // want a regular old moveTo request if (_summary != null && zoneId == _summary.zoneId) { - moveTo(sceneId); + _scdir.moveTo(sceneId); } else { // otherwise, we make a zoned moveTo request // prepare to move to this scene (sets up pending data) - if (!prepareMoveTo(sceneId)) { + if (!_scdir.prepareMoveTo(sceneId)) { return; } @@ -68,8 +66,9 @@ public class ZoneDirector extends SceneDirector // we're requesting to move; if we were unable to load it, assume // a cached version of zero int sceneVers = 0; - if (_pendingModel != null) { - sceneVers = _pendingModel.version; + SceneModel pendingModel = _scdir.getPendingModel(); + if (pendingModel != null) { + sceneVers = pendingModel.version; } // issue a moveTo request @@ -89,7 +88,7 @@ public class ZoneDirector extends SceneDirector _summary = summary; // and pass the rest off to the standard scene transition code - handleMoveSucceeded(invid, placeId, config); + _scdir.handleMoveSucceeded(invid, placeId, config); } /** @@ -105,9 +104,23 @@ public class ZoneDirector extends SceneDirector _summary = summary; // and pass the rest off to the standard scene transition code - handleMoveSucceededPlusUpdate(invid, placeId, config, model); + _scdir.handleMoveSucceededPlusUpdate(invid, placeId, config, model); } + /** + * Called in response to a failed zoned moveTo request. + */ + public void handleMoveFailed (int invid, String reason) + { + _scdir.handleMoveFailed(invid, reason); + } + + /** A reference to the active client context. */ + protected WhirledContext _ctx; + + /** A reference to the scene director with which we coordinate. */ + protected SceneDirector _scdir; + /** A reference to the zone summary for the currently occupied * zone. */ protected ZoneSummary _summary;