diff --git a/src/java/com/threerings/whirled/server/SceneProvider.java b/src/java/com/threerings/whirled/server/SceneProvider.java index f47c97b28..6922086dd 100644 --- a/src/java/com/threerings/whirled/server/SceneProvider.java +++ b/src/java/com/threerings/whirled/server/SceneProvider.java @@ -1,5 +1,5 @@ // -// $Id: SceneProvider.java,v 1.5 2001/11/12 20:56:56 mdb Exp $ +// $Id: SceneProvider.java,v 1.6 2001/12/14 23:12:38 mdb Exp $ package com.threerings.whirled.server; @@ -19,8 +19,8 @@ import com.threerings.whirled.data.SceneModel; * The scene provider handles the server side of the scene related * invocation services (e.g. moving from scene to scene). */ -public class SceneProvider - extends InvocationProvider implements SceneCodes +public class SceneProvider extends InvocationProvider + implements SceneCodes { /** * Processes a request from a client to move to a new scene. @@ -88,8 +88,7 @@ public class SceneProvider } } catch (ServiceFailedException sfe) { - sendResponse(source, invid, MOVE_FAILED_RESPONSE, - sfe.getMessage()); + sendResponse(source, invid, MOVE_FAILED_RESPONSE, sfe.getMessage()); } } } diff --git a/src/java/com/threerings/whirled/spot/client/SpotCodes.java b/src/java/com/threerings/whirled/spot/client/SpotCodes.java index f3835ab64..0c7ee6fa8 100644 --- a/src/java/com/threerings/whirled/spot/client/SpotCodes.java +++ b/src/java/com/threerings/whirled/spot/client/SpotCodes.java @@ -1,21 +1,27 @@ // -// $Id: SpotCodes.java,v 1.1 2001/12/14 00:12:32 mdb Exp $ +// $Id: SpotCodes.java,v 1.2 2001/12/14 23:12:39 mdb Exp $ package com.threerings.whirled.spot.client; import com.threerings.crowd.chat.ChatCodes; -import com.threerings.crowd.client.LocationCodes; +import com.threerings.whirled.client.SceneCodes; /** * Contains codes used by the Spot invocation services. */ -public interface SpotCodes extends ChatCodes, LocationCodes +public interface SpotCodes extends ChatCodes, SceneCodes { /** The module name for the Spot services. */ public static final String MODULE_NAME = "whirled!spot"; - /** The message identifier for a cluster speak request message. */ - public static final String CLUSTER_SPEAK_REQUEST = "cspkreq"; + /** The message identifier for a traversePortal request. Such a + * request generates a moveTo response rather than a specialized + * response. */ + public static final String TRAVERSE_PORTAL_REQUEST = "TraversePortal"; + + /** An error code indicating that the portal specified in a + * traversePortal request does not exist. */ + public static final String NO_SUCH_PORTAL = "m.no_such_portal"; /** The message identifier for a changeLoc request. */ public static final String CHANGE_LOC_REQUEST = "ChangeLoc"; @@ -34,4 +40,7 @@ public interface SpotCodes extends ChatCodes, LocationCodes /** An error code indicating that a location is occupied. Usually * generated by a failed changeLoc request. */ public static final String LOCATION_OCCUPIED = "m.location_occupied"; + + /** The message identifier for a cluster speak request message. */ + public static final String CLUSTER_SPEAK_REQUEST = "cspkreq"; } diff --git a/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java b/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java index 950963047..6266d6166 100644 --- a/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java +++ b/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java @@ -1,5 +1,5 @@ // -// $Id: SpotSceneDirector.java,v 1.2 2001/12/14 01:51:46 mdb Exp $ +// $Id: SpotSceneDirector.java,v 1.3 2001/12/14 23:12:39 mdb Exp $ package com.threerings.whirled.spot.client; @@ -14,6 +14,7 @@ import com.threerings.whirled.util.WhirledContext; import com.threerings.whirled.spot.Log; import com.threerings.whirled.spot.data.Location; +import com.threerings.whirled.spot.data.Portal; /** * Extends the standard scene director with facilities to move between @@ -55,6 +56,59 @@ public class SpotSceneDirector extends SceneDirector super(ctx, screp, dsfact); } + /** + * Requests that this client move to the location specified by the + * supplied portal id. A request will be made and when the response is + * received, the location observers will be notified of success or + * failure. + */ + public void traversePortal (int portalId) + { + // look up the destination scene and location + if (_scene == null) { + Log.warning("Requested to traverse portal when we have " + + "no scene [portalId=" + portalId + "]."); + return; + } + + // find the portal they're talking about + int targetSceneId = -1, targetLocId = -1; + DisplaySpotScene ds = (DisplaySpotScene)_scene; + Iterator portals = ds.getPortals().iterator(); + while (portals.hasNext()) { + Portal portal = (Portal)portals.next(); + if (portal.locationId == portalId) { + targetSceneId = portal.targetSceneId; + targetLocId = portal.targetLocId; + } + } + + // make sure we found the portal + if (targetSceneId == -1) { + Log.warning("Requested to traverse non-existent portal " + + "[portalId=" + portalId + + ", portals=" + + StringUtil.toString(ds.getPortals().iterator()) + "]."); + } + + // prepare to move to this scene (sets up pending data) + if (!prepareMoveTo(targetSceneId)) { + return; + } + + // check the version of our cached copy of the scene to which + // 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; + } + + // issue a traversePortal request + SpotService.traversePortal( + _ctx.getClient(), _sceneId, portalId, sceneVer, this); + } + /** * Issues a request to change our location within the scene to the * location identified by the specified id. Most client entities find diff --git a/src/java/com/threerings/whirled/spot/client/SpotService.java b/src/java/com/threerings/whirled/spot/client/SpotService.java index 19a56ff65..e2b1e96ff 100644 --- a/src/java/com/threerings/whirled/spot/client/SpotService.java +++ b/src/java/com/threerings/whirled/spot/client/SpotService.java @@ -1,5 +1,5 @@ // -// $Id: SpotService.java,v 1.1 2001/12/14 00:12:32 mdb Exp $ +// $Id: SpotService.java,v 1.2 2001/12/14 23:12:39 mdb Exp $ package com.threerings.whirled.spot.client; @@ -10,14 +10,31 @@ import com.threerings.whirled.spot.Log; /** * Provides a mechanism by which the client can request to move between - * locations within a scene. These services should not be used directly, + * locations within a scene and between scenes (taking exit and entry + * locations into account). These services should not be used directly, * but instead should be accessed via the {@link SpotSceneDirector}. */ public class SpotService implements SpotCodes { /** - * Requests that that this client's body be made to occupy the - * specified location. + * Requests to traverse the specified portal. + */ + public static void traversePortal ( + Client client, int sceneId, int portalId, int sceneVer, + SpotSceneDirector rsptarget) + { + InvocationDirector invdir = client.getInvocationDirector(); + Object[] args = new Object[] { + new Integer(sceneId), new Integer(portalId), + new Integer(sceneVer) }; + invdir.invoke(MODULE_NAME, TRAVERSE_PORTAL_REQUEST, args, rsptarget); + Log.info("Sent traversePortal request [sceneId=" + sceneId + + ", portalId=" + portalId + ", sceneVer=" + sceneVer + "]."); + } + + /** + * Requests that this client's body be made to occupy the specified + * location. */ public static void changeLoc (Client client, int sceneId, int locationId, SpotSceneDirector rsptarget) diff --git a/src/java/com/threerings/whirled/spot/server/SpotClient.java b/src/java/com/threerings/whirled/spot/server/SpotClient.java new file mode 100644 index 000000000..af47ffb2c --- /dev/null +++ b/src/java/com/threerings/whirled/spot/server/SpotClient.java @@ -0,0 +1,16 @@ +// +// $Id: SpotClient.java,v 1.1 2001/12/14 23:12:39 mdb Exp $ + +package com.threerings.whirled.spot.server; + +import com.threerings.whirled.server.WhirledClient; + +/** + * Extends the Whirled client and handles the necessary notifications that + * take place when a user logs off to let the clients displaying the scene + * they were occupying know that they didn't exit via a portal, but + * instead just disappeared. + */ +public class SpotClient extends WhirledClient +{ +} diff --git a/src/java/com/threerings/whirled/spot/server/SpotProvider.java b/src/java/com/threerings/whirled/spot/server/SpotProvider.java index f109fcbc7..4fcf6b54d 100644 --- a/src/java/com/threerings/whirled/spot/server/SpotProvider.java +++ b/src/java/com/threerings/whirled/spot/server/SpotProvider.java @@ -1,5 +1,5 @@ // -// $Id: SpotProvider.java,v 1.1 2001/12/14 00:12:32 mdb Exp $ +// $Id: SpotProvider.java,v 1.2 2001/12/14 23:12:39 mdb Exp $ package com.threerings.whirled.spot.server; @@ -8,10 +8,18 @@ import com.threerings.presents.server.InvocationProvider; import com.threerings.presents.server.ServiceFailedException; import com.threerings.crowd.data.BodyObject; +import com.threerings.crowd.data.PlaceConfig; +import com.threerings.crowd.data.PlaceObject; +import com.threerings.crowd.server.LocationProvider; + +import com.threerings.whirled.data.SceneModel; +import com.threerings.whirled.server.SceneManager; import com.threerings.whirled.server.SceneRegistry; +import com.threerings.whirled.server.WhirledServer; import com.threerings.whirled.spot.Log; import com.threerings.whirled.spot.client.SpotCodes; +import com.threerings.whirled.spot.data.SpotOccupantInfo; /** * This class provides the server side of the spot services. @@ -34,6 +42,141 @@ public class SpotProvider extends InvocationProvider invmgr.registerProvider(MODULE_NAME, new SpotProvider()); } + /** + * Processes a request from a client to traverse a portal. + * + * @param source the body object of the client making the request. + * @param invid the invocation service invocation id. + * @param sceneId the source scene id. + * @param portalId the portal in the source scene that is being + * traversed. + * @param sceneVer the version of the destination scene data that the + * client has cached. + */ + public void handleTraversePortalRequest ( + BodyObject source, int invid, int sceneId, int portalId, int sceneVer) + { + try { + // obtain the source scene + SpotSceneManager smgr = (SpotSceneManager) + _screg.getSceneManager(sceneId); + if (smgr == null) { + Log.warning("Traverse portal missing source scene " + + "[user=" + source.username + + ", sceneId=" + sceneId + + ", portalId=" + portalId + "]."); + throw new ServiceFailedException(INTERNAL_ERROR); + } + + // obtain the destination scene and location id + RuntimeSpotScene rss = (RuntimeSpotScene)smgr.getScene(); + int destSceneId = rss.getTargetSceneId(portalId); + final int destLocId = rss.getTargetLocationId(portalId); + + // make sure this portal has valid info + if (destSceneId == -1) { + Log.warning("Traverse portal provided with invalid portal " + + "[user=" + source.username + + ", sceneId=" + sceneId + + ", portalId=" + portalId + + ", destSceneId=" + destSceneId + "]."); + throw new ServiceFailedException(NO_SUCH_PORTAL); + } + + // avoid cluttering up the method declaration with final + // keywords + final BodyObject fsource = source; + final int finvid = invid; + final int fportalId = portalId; + final int fsceneVer = sceneVer; + + // create a callback object that will handle the resolution or + // failed resolution of the scene + SceneRegistry.ResolutionListener rl = + new SceneRegistry.ResolutionListener() { + public void sceneWasResolved (SceneManager scmgr) { + finishTraversePortalRequest( + fsource, finvid, scmgr, fsceneVer, + fportalId, destLocId); + } + + public void sceneFailedToResolve ( + int rsceneId, Exception reason) { + Log.warning("Unable to resolve target scene " + + "[sceneId=" + rsceneId + + ", reason=" + reason + "]."); + // pretend like the scene doesn't exist to the client + sendResponse(fsource, finvid, MOVE_FAILED_RESPONSE, + NO_SUCH_PLACE); + } + }; + + // make sure the scene they are headed to is actually loaded into + // the server + WhirledServer.screg.resolveScene(destSceneId, rl); + + } catch (ServiceFailedException sfe) { + sendResponse(source, invid, MOVE_FAILED_RESPONSE, sfe.getMessage()); + } + } + + /** + * This is called after the scene to which we are moving is guaranteed + * to have been loaded into the server. + */ + protected void finishTraversePortalRequest ( + BodyObject source, int invid, SceneManager scmgr, + int sceneVer, int exitPortalId, int destLocId) + { + // move to the place object associated with this scene + PlaceObject plobj = scmgr.getPlaceObject(); + int ploid = plobj.getOid(); + + // if they were in a scene (and at a location) prior to issuing + // this traverse portal request, we need to send a notification to + // that scene indicating that they are headed to the portal from + // which they depart. we unfortunately can't do it deep in the + // bowels of LocationProvider.moveTo() which is where we'd like to + // do it to ensure that nothing else ran amuck in the process. + // since we can't, we simply send another response putting the + // user back where they were in the event that anything fails + // during the moveTo process. it's a hack, but it's better than + // ripping apart moveTo and restructuring the code with this + // requirement in mind + int oldLocId = + updateLocation(source.location, source.getOid(), exitPortalId); + + try { + // try doing the actual move + PlaceConfig config = LocationProvider.moveTo(source, ploid); + + // now that the move succeeded, we need to update the occupant + // info in the new place object with this user's entry + // location + updateLocation(ploid, source.getOid(), destLocId); + + // check to see if they need a newer version of the scene data + SceneModel model = scmgr.getSceneModel(); + if (sceneVer < model.version) { + // then send the moveTo response + sendResponse(source, invid, MOVE_SUCCEEDED_PLUS_UPDATE_RESPONSE, + new Integer(ploid), config, model); + + } else { + // then send the moveTo response + sendResponse(source, invid, MOVE_SUCCEEDED_RESPONSE, + new Integer(ploid), config); + } + + } catch (ServiceFailedException sfe) { + sendResponse(source, invid, MOVE_FAILED_RESPONSE, sfe.getMessage()); + + // we need to undo the move to the exit portal location that + // we enacted earlier + updateLocation(source.location, source.getOid(), oldLocId); + } + } + /** * Processes a request from a client to move to a new location. */ @@ -61,6 +204,44 @@ public class SpotProvider extends InvocationProvider } } + /** + * Looks up the specified place object, obtains the occupant info for + * the specified body object and updates the location id in said + * occupant info. + * + * @return old location id or -1 if no update took place (because + * something was not in order, like the place object didn't exist, + * etc.). + */ + protected static int updateLocation ( + int placeOid, int bodyOid, int locationId) + { + PlaceObject place = null; + int oldLocId = -1; + + if (placeOid != -1 && locationId != -1) { + place = (PlaceObject)WhirledServer.omgr.getObject(placeOid); + } + + if (place != null) { + Integer key = new Integer(bodyOid); + SpotOccupantInfo info = (SpotOccupantInfo) + place.occupantInfo.get(key); + if (info != null) { + oldLocId = info.locationId; + // we need to clone the info because moveTo() down below + // is going to update the actual info record and post it + // in its own event, which would mess us up if we were + // using the real thing + info = (SpotOccupantInfo)info.clone(); + info.locationId = locationId; + place.updateOccupantInfo(info); + } + } + + return oldLocId; + } + /** The scene registry with which we interoperate. */ protected static SceneRegistry _screg; }