Finished up implementation of portal traversal back-end.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@781 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2001-12-14 23:12:39 +00:00
parent 17ad791035
commit efd1a69caa
6 changed files with 292 additions and 16 deletions
@@ -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());
}
}
}
@@ -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";
}
@@ -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
@@ -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)
@@ -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
{
}
@@ -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;
}