1c02c2d6f4
than a place service (which may be changed back when place services improve or I may change the main chat stuff to be an invocation service). git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@803 542714f4-19e9-0310-aa3c-eee0fc999fb1
280 lines
11 KiB
Java
280 lines
11 KiB
Java
//
|
|
// $Id: SpotProvider.java,v 1.5 2001/12/16 21:02:18 mdb Exp $
|
|
|
|
package com.threerings.whirled.spot.server;
|
|
|
|
import com.threerings.presents.dobj.RootDObjectManager;
|
|
import com.threerings.presents.server.InvocationManager;
|
|
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.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.
|
|
*/
|
|
public class SpotProvider extends InvocationProvider
|
|
implements SpotCodes
|
|
{
|
|
/**
|
|
* Constructs a spot provider and registers it with the invocation
|
|
* manager to handle spot services. This need be done by a server that
|
|
* wishes to make use of the Spot services.
|
|
*/
|
|
public static void init (
|
|
InvocationManager invmgr, SceneRegistry screg, RootDObjectManager omgr)
|
|
{
|
|
// we'll need these later
|
|
_screg = screg;
|
|
_omgr = omgr;
|
|
|
|
// register a spot provider instance
|
|
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) {
|
|
SpotSceneManager sscmgr = (SpotSceneManager)scmgr;
|
|
finishTraversePortalRequest(
|
|
fsource, finvid, sscmgr, 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
|
|
_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, SpotSceneManager scmgr,
|
|
int sceneVer, int exitPortalId, int destLocId)
|
|
{
|
|
// move to the place object associated with this scene
|
|
PlaceObject plobj = scmgr.getPlaceObject();
|
|
int ploid = plobj.getOid();
|
|
int bodyOid = source.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, bodyOid, exitPortalId);
|
|
|
|
// let the destination scene manager know that we're coming in
|
|
scmgr.mapEnteringBody(bodyOid, destLocId);
|
|
|
|
try {
|
|
// try doing the actual move
|
|
PlaceConfig config = LocationProvider.moveTo(source, ploid);
|
|
|
|
// 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, bodyOid, oldLocId);
|
|
|
|
// and let the destination scene manager know that we're no
|
|
// longer coming in
|
|
scmgr.clearEnteringBody(bodyOid);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes a request from a client to move to a new location.
|
|
*/
|
|
public void handleChangeLocRequest (
|
|
BodyObject source, int invid, int sceneId, int locationId)
|
|
{
|
|
try {
|
|
// look up the scene manager for the specified scene
|
|
SpotSceneManager smgr = (SpotSceneManager)
|
|
_screg.getSceneManager(sceneId);
|
|
if (smgr == null) {
|
|
Log.warning("User requested to change location in " +
|
|
"non-existent scene [user=" + source.username +
|
|
", sceneId=" + sceneId +
|
|
", locId=" + locationId + "].");
|
|
throw new ServiceFailedException(INTERNAL_ERROR);
|
|
}
|
|
|
|
int locOid = smgr.handleChangeLocRequest(source, locationId);
|
|
sendResponse(source, invid, CHANGE_LOC_SUCCEEDED_RESPONSE,
|
|
new Integer(locOid));
|
|
|
|
} catch (ServiceFailedException sfe) {
|
|
sendResponse(source, invid, CHANGE_LOC_FAILED_RESPONSE,
|
|
sfe.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles {@link SpotCodes#CLUSTER_SPEAK_REQUEST} messages.
|
|
*/
|
|
public void handleClusterSpeakRequest (
|
|
BodyObject source, int invid, int sceneId, int locId, String message)
|
|
{
|
|
// look up the scene manager for the specified scene
|
|
SpotSceneManager smgr = (SpotSceneManager)
|
|
_screg.getSceneManager(sceneId);
|
|
if (smgr == null) {
|
|
Log.warning("User requested cluster chat in " +
|
|
"non-existent scene [user=" + source.username +
|
|
", sceneId=" + sceneId + ", locId=" + locId +
|
|
", message=" + message + "].");
|
|
|
|
} else {
|
|
// pass this request on to the spot scene manager as it will
|
|
// need to check that the location exists and that the
|
|
// requester occupies it and so on
|
|
smgr.handleClusterSpeakRequest(source, locId, message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)_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;
|
|
|
|
/** The object manager we use to do dobject stuff. */
|
|
protected static RootDObjectManager _omgr;
|
|
}
|