Widened, modified traversePortal() so that the entire portal being traversed is

kept around and passed to mapEnteringBody() so that in Whirled instead of
having a portal map to a destination portal we can simply have the source
portal contain the location in the destination scene at which the arriver is to
be mapped.


git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@321 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
Michael Bayne
2007-06-22 21:59:04 +00:00
parent 114fc2f0e7
commit fe2fc21830
2 changed files with 164 additions and 227 deletions
@@ -21,10 +21,10 @@
package com.threerings.whirled.spot.server;
import java.util.HashMap;
import java.util.Iterator;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.IntIntMap;
import com.threerings.util.Name;
import com.threerings.presents.dobj.DObject;
@@ -50,9 +50,8 @@ import com.threerings.whirled.spot.data.SpotScene;
import com.threerings.whirled.spot.data.SpotSceneObject;
/**
* Handles the movement of bodies between locations in the scene and
* creates the necessary distributed objects to allow bodies in clusters
* to chat with one another.
* Handles the movement of bodies between locations in the scene and creates the necessary
* distributed objects to allow bodies in clusters to chat with one another.
*/
public class SpotSceneManager extends SceneManager
implements SpotCodes
@@ -62,8 +61,7 @@ public class SpotSceneManager extends SceneManager
*/
public static void moveBodyToDefaultPortal (BodyObject body)
{
SpotSceneManager mgr = (SpotSceneManager)
CrowdServer.plreg.getPlaceManager(body.location);
SpotSceneManager mgr = (SpotSceneManager)CrowdServer.plreg.getPlaceManager(body.location);
if (mgr != null) {
SpotScene scene = (SpotScene)mgr.getScene();
if (scene == null) {
@@ -84,24 +82,20 @@ public class SpotSceneManager extends SceneManager
}
/**
* Assigns a starting location for an entering body. This will happen
* before the body is made to "occupy" the scene (defined by their
* having an occupant info record). So when they do finally occupy the
* scene, the client will know where to render them.
* Assigns a starting location for an entering body. This will happen before the body is made
* to "occupy" the scene (defined by their having an occupant info record). So when they do
* finally occupy the scene, the client will know where to render them.
*/
public void mapEnteringBody (BodyObject body, int portalId)
public void mapEnteringBody (BodyObject body, Portal from)
{
// small optimization: don't save portalId -1, because it simply means
// "use the default entrance" and retrieving an unset value will
// return -1 anyway
if (portalId != -1) {
_enterers.put(body.getOid(), portalId);
// don't save a null from portal, because it simply means "use the default entrance"
if (from != null) {
_enterers.put(body.getOid(), from);
}
}
/**
* Called if a body failed to enter our scene after we assigned them
* an entering position.
* Called if a body failed to enter our scene after we assigned them an entering position.
*/
public void clearEnteringBody (BodyObject body)
{
@@ -109,10 +103,9 @@ public class SpotSceneManager extends SceneManager
}
/**
* This is called when a user requests to traverse a portal from this
* scene to another scene. The manager may return an error code string
* that will be reported back to the caller explaining the failure or
* <code>null</code> indicating that it is OK for the caller to
* This is called when a user requests to traverse a portal from this scene to another scene.
* The manager may return an error code string that will be reported back to the caller
* explaining the failure or <code>null</code> indicating that it is OK for the caller to
* traverse the portal.
*/
public String mayTraversePortal (BodyObject body, Portal portal)
@@ -121,11 +114,10 @@ public class SpotSceneManager extends SceneManager
}
/**
* This is called to let this scene manager know that the user is
* about to traverse the specified portal. The default implementation
* relocates the user to the location associated with the portal. It
* is still possible that the traversal will fail, so don't do
* anything too crazy.
* This is called to let this scene manager know that the user is about to traverse the
* specified portal. The default implementation relocates the user to the location associated
* with the portal. It is still possible that the traversal will fail, so don't do anything too
* crazy.
*/
public void willTraversePortal (BodyObject body, Portal portal)
{
@@ -135,10 +127,9 @@ public class SpotSceneManager extends SceneManager
// documentation inherited
protected void didStartup ()
{
// get a casted reference to our place object (we need to do this
// before calling super.didStartup() because that will call
// sceneManagerDidResolve() which may start letting people into
// the scene)
// get a casted reference to our place object (we need to do this before calling
// super.didStartup() because that will call sceneManagerDidResolve() which may start
// letting people into the scene)
_ssobj = (SpotSceneObject)_plobj;
super.didStartup();
@@ -165,17 +156,16 @@ public class SpotSceneManager extends SceneManager
removeFromCluster(bodyOid);
// let's make damned sure they're not in any cluster
Iterator cliter = _clusters.values().iterator();
Iterator<ClusterRecord> cliter = _clusters.values().iterator();
while (cliter.hasNext()) {
ClusterRecord clrec = (ClusterRecord)cliter.next();
ClusterRecord clrec = cliter.next();
if (clrec.containsKey(bodyOid)) {
Log.info("Pruning departed body from cluster [boid=" + bodyOid +
", cluster=" + clrec + "].");
clrec.removeBody(bodyOid);
if (clrec.size() == 0) {
// If we just removed the last body, destroy the cluster,
// need to use the iterator's removal so we don't
// hose ourselves.
// if we just removed the last body, destroy the cluster, need to use the
// iterator's removal so we don't hose ourselves
clrec.destroy(false);
cliter.remove();
}
@@ -188,25 +178,23 @@ public class SpotSceneManager extends SceneManager
{
super.insertOccupantInfo(info, body);
// we don't actually populate their occupant info, but instead assign
// them their starting location in the scene
// we don't actually populate their occupant info, but instead assign them their starting
// location in the scene
assignStartingLocation(body);
}
/**
* Give our new body a starting location.
* @param body The new body entering the scene.
*/
protected void assignStartingLocation (BodyObject body)
{
int portalId = _enterers.remove(body.getOid());
Portal from = _enterers.remove(body.getOid());
Portal entry;
if (portalId != -1) {
entry = _sscene.getPortal(portalId);
if (from != null && from.portalId != -1) {
entry = _sscene.getPortal(from.portalId);
if (entry == null) {
Log.warning("Body mapped at invalid portal [where=" + where() +
", who=" + body.who() +
", portalId=" + portalId + "].");
", who=" + body.who() + ", from=" + from + "].");
entry = _sscene.getDefaultEntrance();
}
} else {
@@ -216,40 +204,37 @@ public class SpotSceneManager extends SceneManager
// Log.debug("Positioning entering body [who=" + body.who() +
// ", where=" + entry.getOppLocation() + "].");
// create a scene location for them located on the entrance portal
// but facing the opposite direction
// create a scene location for them located on the entrance portal but facing the opposite
// direction
_ssobj.addToOccupantLocs(computeEnteringLocation(body, entry));
}
/**
* Called when the supplied body is entering our scene via the
* specified portal. The default location is the one associated with
* the portal, but derived classes may wish to adjust this.
* Called when the supplied body is entering our scene via the specified portal. The default
* location is the one associated with the portal, but derived classes may wish to adjust this.
*/
protected SceneLocation computeEnteringLocation (
BodyObject body, Portal entry)
protected SceneLocation computeEnteringLocation (BodyObject body, Portal entry)
{
return new SceneLocation(entry.getOppLocation(), body.getOid());
}
/**
* Called by the {@link SpotProvider} when we receive a request by a
* user to occupy a particular location.
* Called by the {@link SpotProvider} when we receive a request by a user to occupy a
* particular location.
*
* @param source the body to be moved.
* @param loc the location to which to move the body.
*
* @exception InvocationException thrown with a reason code explaining
* the failure if there is a problem processing the request.
* @exception InvocationException thrown with a reason code explaining the failure if there is
* a problem processing the request.
*/
protected void handleChangeLoc (BodyObject source, Location loc)
throws InvocationException
{
// make sure they are in our scene
if (!_ssobj.occupants.contains(source.getOid())) {
Log.warning("Refusing change loc from non-scene occupant " +
"[where=" + where() + ", who=" + source.who() +
", loc=" + loc + "].");
Log.warning("Refusing change loc from non-scene occupant [where=" + where() +
", who=" + source.who() + ", loc=" + loc + "].");
throw new InvocationException(INTERNAL_ERROR);
}
@@ -258,9 +243,8 @@ public class SpotSceneManager extends SceneManager
throw new InvocationException(INVALID_LOCATION);
}
// update the user's location information in the scene which will
// indicate to the client that their avatar should be moved from
// its current position to their new position
// update the user's location information in the scene which will indicate to the client
// that their avatar should be moved from its current position to their new position
updateLocation(source, loc);
// remove them from any cluster as they've departed
@@ -268,10 +252,9 @@ public class SpotSceneManager extends SceneManager
}
/**
* Derived classes can override this method and validate that the
* specified body can stand in the requested location. The default
* implementation returns <code>true</code> in all circumstances;
* stand where ye may!
* Derived classes can override this method and validate that the specified body can stand in
* the requested location. The default implementation returns <code>true</code> in all
* circumstances; stand where ye may!
*/
protected boolean validateLocation (BodyObject source, Location loc)
{
@@ -286,9 +269,8 @@ public class SpotSceneManager extends SceneManager
SceneLocation sloc = new SceneLocation(loc, source.getOid());
if (!_ssobj.occupantLocs.contains(sloc)) {
// complain if they don't already have a location configured
Log.warning("Changing loc for occupant without previous loc " +
"[where=" + where() + ", who=" + source.who() +
", nloc=" + loc + "].");
Log.warning("Changing loc for occupant without previous loc [where=" + where() +
", who=" + source.who() + ", nloc=" + loc + "].");
Thread.dumpStack();
_ssobj.addToOccupantLocs(sloc);
} else {
@@ -297,22 +279,21 @@ public class SpotSceneManager extends SceneManager
}
/**
* Called by the {@link SpotProvider} when we receive a request by a
* user to join a particular cluster.
* Called by the {@link SpotProvider} when we receive a request by a user to join a particular
* cluster.
*
* @param joiner the body to be moved.
* @param targetOid the bodyOid of another user or the oid of an
* existing cluster; the moving user will be made to join the other
* user's cluster.
* @param targetOid the bodyOid of another user or the oid of an existing cluster; the moving
* user will be made to join the other user's cluster.
*
* @exception InvocationException thrown with a reason code explaining
* the failure if there is a problem processing the request.
* @exception InvocationException thrown with a reason code explaining the failure if there is
* a problem processing the request.
*/
protected void handleJoinCluster (BodyObject joiner, int targetOid)
throws InvocationException
{
// if the cluster already exists, add this user and be done
ClusterRecord clrec = (ClusterRecord)_clusters.get(targetOid);
ClusterRecord clrec = _clusters.get(targetOid);
if (clrec != null) {
clrec.addBody(joiner);
return;
@@ -321,8 +302,7 @@ public class SpotSceneManager extends SceneManager
// otherwise see if they sent us the user's oid
DObject tobj = CrowdServer.omgr.getObject(targetOid);
if (!(tobj instanceof BodyObject)) {
Log.info("Can't join cluster, missing target " +
"[creator=" + joiner.who() +
Log.info("Can't join cluster, missing target [creator=" + joiner.who() +
", targetOid=" + targetOid + "].");
throw new InvocationException(NO_SUCH_CLUSTER);
}
@@ -330,9 +310,8 @@ public class SpotSceneManager extends SceneManager
// make sure we're in the same scene as said user
BodyObject friend = (BodyObject)tobj;
if (friend.location != joiner.location) {
Log.info("Refusing cluster join from non-proximate user " +
"[joiner=" + joiner.who() + ", jloc=" + joiner.location +
", target=" + friend.who() +
Log.info("Refusing cluster join from non-proximate user [joiner=" + joiner.who() +
", jloc=" + joiner.location + ", target=" + friend.who() +
", tloc=" + friend.location + "].");
throw new InvocationException(NO_SUCH_CLUSTER);
}
@@ -344,21 +323,18 @@ public class SpotSceneManager extends SceneManager
return;
}
// confirm that they can start a cluster with this unsuspecting
// other person
// confirm that they can start a cluster with this unsuspecting other person
checkCanCluster(joiner, friend);
// otherwise we create a new cluster and add our charter members!
// Log.debug("Creating cluster [starter=" + joiner.who() +
// ", target=" + friend.who() + "].");
// Log.debug("Creating cluster [starter=" + joiner.who() + ", tgt=" + friend.who() + "].");
clrec = createClusterRecord();
clrec.addBody(friend);
clrec.addBody(joiner);
}
/**
* Creates the cluster record instance that we'll use to manage our
* cluster.
* Creates the cluster record instance that we'll use to manage our cluster.
*/
protected ClusterRecord createClusterRecord ()
{
@@ -366,10 +342,9 @@ public class SpotSceneManager extends SceneManager
}
/**
* Gives derived classes an opportunity to veto a user's attempt to
* start a cluster with another user. If the attempt should be vetoed,
* this method should throw an {@link InvocationException} indicating
* the reason for veto.
* Gives derived classes an opportunity to veto a user's attempt to start a cluster with
* another user. If the attempt should be vetoed, this method should throw an {@link
* InvocationException} indicating the reason for veto.
*/
protected void checkCanCluster (BodyObject initiator, BodyObject target)
throws InvocationException
@@ -397,33 +372,27 @@ public class SpotSceneManager extends SceneManager
*/
protected ClusterRecord getCluster (int bodyOid)
{
ClusteredBodyObject bobj = (ClusteredBodyObject)
CrowdServer.omgr.getObject(bodyOid);
return (bobj == null) ? null :
(ClusterRecord)_clusters.get(bobj.getClusterOid());
ClusteredBodyObject bobj = (ClusteredBodyObject)CrowdServer.omgr.getObject(bodyOid);
return (bobj == null) ? null : _clusters.get(bobj.getClusterOid());
}
/**
* Called by the {@link SpotProvider} when we receive a cluster speak
* request.
* Called by the {@link SpotProvider} when we receive a cluster speak request.
*/
protected void handleClusterSpeakRequest (
int sourceOid, Name source, String bundle, String message, byte mode)
protected void handleClusterSpeakRequest (int sourceOid, Name source, String bundle,
String message, byte mode)
{
ClusterRecord clrec = getCluster(sourceOid);
if (clrec == null) {
Log.warning("Non-clustered user requested cluster speak " +
"[where=" + where() + ", chatter=" + source +
" (" + sourceOid + "), msg=" + message + "].");
Log.warning("Non-clustered user requested cluster speak [where=" + where() +
", chatter=" + source + " (" + sourceOid + "), msg=" + message + "].");
} else {
SpeakProvider.sendSpeak(clrec.getClusterObject(),
source, bundle, message, mode);
SpeakProvider.sendSpeak(clrec.getClusterObject(), source, bundle, message, mode);
}
}
/**
* Returns the location of the specified body or null if they have no
* location in this scene.
* Returns the location of the specified body or null if they have no location in this scene.
*/
protected SceneLocation locationForBody (int bodyOid)
{
@@ -431,8 +400,7 @@ public class SpotSceneManager extends SceneManager
}
/**
* Verifies that the specified cluster can be expanded to include
* another body.
* Verifies that the specified cluster can be expanded to include another body.
*/
protected boolean canAddBody (ClusterRecord clrec, BodyObject body)
{
@@ -440,26 +408,23 @@ public class SpotSceneManager extends SceneManager
}
/**
* Called when a user is added to a cluster. The scene manager
* implementation should take this opportunity to rearrange everyone
* in the cluster appropriately for the new size.
* Called when a user is added to a cluster. The scene manager implementation should take this
* opportunity to rearrange everyone in the cluster appropriately for the new size.
*/
protected void bodyAdded (ClusterRecord clrec, BodyObject body)
{
}
/**
* Called when a user is removed from a cluster. The scene manager
* implementation should take this opportunity to rearrange everyone
* in the cluster appropriately for the new size.
* Called when a user is removed from a cluster. The scene manager implementation should take
* this opportunity to rearrange everyone in the cluster appropriately for the new size.
*/
protected void bodyRemoved (ClusterRecord clrec, BodyObject body)
{
}
/**
* Used to manage clusters which are groups of users that can chat to
* one another.
* Used to manage clusters which are groups of users that can chat to one another.
*/
protected class ClusterRecord extends HashIntMap
{
@@ -485,9 +450,8 @@ public class SpotSceneManager extends SceneManager
throws InvocationException
{
if (!(body instanceof ClusteredBodyObject)) {
Log.warning("Refusing to add non-clustered body to cluster " +
"[cloid=" + _clobj.getOid() +
", size=" + size() + ", who=" + body.who() + "].");
Log.warning("Refusing to add non-clustered body to cluster [cloid=" +
_clobj.getOid() + ", size=" + size() + ", who=" + body.who() + "].");
throw new InvocationException(INTERNAL_ERROR);
}
@@ -511,10 +475,8 @@ public class SpotSceneManager extends SceneManager
body.startTransaction();
try {
bodyAdded(this, body); // do the hokey pokey
if (_clobj != null) {
((ClusteredBodyObject)body).setClusterOid(
_clobj.getOid());
((ClusteredBodyObject)body).setClusterOid(_clobj.getOid());
_clobj.addToOccupants(body.getOid());
_ssobj.updateClusters(_cluster);
}
@@ -534,9 +496,8 @@ public class SpotSceneManager extends SceneManager
{
BodyObject body = (BodyObject)remove(bodyOid);
if (body == null) {
Log.warning("Requested to remove unknown body from cluster " +
"[cloid=" + _clobj.getOid() +
", size=" + size() + ", who=" + bodyOid + "].");
Log.warning("Requested to remove unknown body from cluster [cloid=" +
_clobj.getOid() + ", size=" + size() + ", who=" + bodyOid + "].");
return;
}
@@ -548,7 +509,6 @@ public class SpotSceneManager extends SceneManager
try {
((ClusteredBodyObject)body).setClusterOid(-1);
bodyRemoved(this, body); // do the hokey pokey
if (_clobj != null) {
_clobj.removeFromOccupants(bodyOid);
_ssobj.updateClusters(_cluster);
@@ -557,6 +517,7 @@ public class SpotSceneManager extends SceneManager
} finally {
_ssobj.commitTransaction();
}
} finally {
if (body.isActive()) {
body.commitTransaction();
@@ -584,12 +545,10 @@ public class SpotSceneManager extends SceneManager
protected void destroy (boolean doRemoval)
{
// Log.debug("Cluster empty, going away " +
// "[cloid=" + _clobj.getOid() + "].");
// Log.debug("Cluster empty, going away [cloid=" + _clobj.getOid() + "].");
_ssobj.removeFromClusters(_cluster.getKey());
// If we've also been requested to remove ourself from the clusters
// list, do that.
// if we've also been requested to remove ourself from the clusters list, do that
if (doRemoval) {
_clusters.remove(_clobj.getOid());
}
@@ -607,8 +566,8 @@ public class SpotSceneManager extends SceneManager
protected SpotScene _sscene;
/** Records with information on all clusters in this scene. */
protected HashIntMap _clusters = new HashIntMap();
protected HashIntMap<ClusterRecord> _clusters = new HashIntMap<ClusterRecord>();
/** A mapping of entering bodies to portal ids. */
protected IntIntMap _enterers = new IntIntMap();
protected HashMap<Integer,Portal> _enterers = new HashMap<Integer,Portal>();
}