Small changes to the cluster services: cluster information is published in

the scene object; clusters are centered within their group of occupants; a
player must start a cluster with another player rather than by themselves.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2278 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2003-02-13 21:55:22 +00:00
parent 24b6d1fcec
commit 2d5d1c56a8
5 changed files with 204 additions and 34 deletions
@@ -1,5 +1,5 @@
// //
// $Id: SpotService.java,v 1.12 2003/02/12 07:23:31 mdb Exp $ // $Id: SpotService.java,v 1.13 2003/02/13 21:55:22 mdb Exp $
package com.threerings.whirled.spot.client; package com.threerings.whirled.spot.client;
@@ -44,11 +44,11 @@ public interface SpotService extends InvocationService
* location. * location.
* *
* @param loc the location to which to move. * @param loc the location to which to move.
* @param cluster if zero, a new cluster will be created and assigned * @param cluster if -1, the calling user will be removed from any
* to the calling user; if -1, the calling user will be removed from * cluster they currently occupy and not made to occupy a new cluster;
* any cluster they currently occupy and not made to occupy a new * if the bodyOid of another user, the calling user will be made to
* cluster; if the bodyOid of another user, the calling user will be * join the target user's cluster, or create a cluster with the target
* made to join the target user's cluster. * user if they are not already in one.
*/ */
public void changeLoc (Client client, Location loc, int cluster, public void changeLoc (Client client, Location loc, int cluster,
ChangeLocListener listener); ChangeLocListener listener);
@@ -0,0 +1,38 @@
//
// $Id: Cluster.java,v 1.1 2003/02/13 21:55:22 mdb Exp $
package com.threerings.whirled.spot.data;
import com.threerings.presents.dobj.DSet;
/**
* Contains information on clusters.
*/
public class Cluster
implements DSet.Entry
{
/** A unique identifier for this cluster (also the distributed object
* id of the cluster chat object). */
public int clusterOid;
/** The x-coordinate of the cluster in the scene. */
public int x;
/** The y-coordinate of the cluster in the scene. */
public int y;
/** The number of occupants in this cluster. */
public int occupants;
// documentation inherited
public Comparable getKey ()
{
if (_key == null) {
_key = new Integer(clusterOid);
}
return _key;
}
/** Used for {@link #geyKey}. */
protected transient Integer _key;
}
@@ -1,5 +1,5 @@
// //
// $Id: SpotSceneObject.dobj,v 1.1 2003/02/12 07:23:31 mdb Exp $ // $Id: SpotSceneObject.dobj,v 1.2 2003/02/13 21:55:22 mdb Exp $
package com.threerings.whirled.spot.data; package com.threerings.whirled.spot.data;
@@ -14,4 +14,7 @@ public class SpotSceneObject extends SceneObject
/** A distributed set containing {@link SceneLocation} records for all /** A distributed set containing {@link SceneLocation} records for all
* occupants of this scene. */ * occupants of this scene. */
public DSet occupantLocs = new DSet(); public DSet occupantLocs = new DSet();
/** Contains information on all {@link Cluster}s in this scene. */
public DSet clusters = new DSet();
} }
@@ -1,5 +1,5 @@
// //
// $Id: SpotSceneObject.java,v 1.1 2003/02/12 07:23:31 mdb Exp $ // $Id: SpotSceneObject.java,v 1.2 2003/02/13 21:55:22 mdb Exp $
package com.threerings.whirled.spot.data; package com.threerings.whirled.spot.data;
@@ -14,10 +14,16 @@ public class SpotSceneObject extends SceneObject
/** The field name of the <code>occupantLocs</code> field. */ /** The field name of the <code>occupantLocs</code> field. */
public static final String OCCUPANT_LOCS = "occupantLocs"; public static final String OCCUPANT_LOCS = "occupantLocs";
/** The field name of the <code>clusters</code> field. */
public static final String CLUSTERS = "clusters";
/** A distributed set containing {@link SceneLocation} records for all /** A distributed set containing {@link SceneLocation} records for all
* occupants of this scene. */ * occupants of this scene. */
public DSet occupantLocs = new DSet(); public DSet occupantLocs = new DSet();
/** Contains information on all {@link Cluster}s in this scene. */
public DSet clusters = new DSet();
/** /**
* Requests that the specified entry be added to the * Requests that the specified entry be added to the
* <code>occupantLocs</code> set. The set will not change until the event is * <code>occupantLocs</code> set. The set will not change until the event is
@@ -63,4 +69,50 @@ public class SpotSceneObject extends SceneObject
this.occupantLocs = occupantLocs; this.occupantLocs = occupantLocs;
requestAttributeChange(OCCUPANT_LOCS, occupantLocs); requestAttributeChange(OCCUPANT_LOCS, occupantLocs);
} }
/**
* Requests that the specified entry be added to the
* <code>clusters</code> set. The set will not change until the event is
* actually propagated through the system.
*/
public void addToClusters (DSet.Entry elem)
{
requestEntryAdd(CLUSTERS, elem);
}
/**
* Requests that the entry matching the supplied key be removed from
* the <code>clusters</code> set. The set will not change until the
* event is actually propagated through the system.
*/
public void removeFromClusters (Object key)
{
requestEntryRemove(CLUSTERS, key);
}
/**
* Requests that the specified entry be updated in the
* <code>clusters</code> set. The set will not change until the event is
* actually propagated through the system.
*/
public void updateClusters (DSet.Entry elem)
{
requestEntryUpdate(CLUSTERS, elem);
}
/**
* Requests that the <code>clusters</code> field be set to the
* specified value. Generally one only adds, updates and removes
* entries of a distributed set, but certain situations call for a
* complete replacement of the set value. The local value will be
* updated immediately and an event will be propagated through the
* system to notify all listeners that the attribute did
* change. Proxied copies of this object (on clients) will apply the
* value change when they received the attribute changed notification.
*/
public void setClusters (DSet clusters)
{
this.clusters = clusters;
requestAttributeChange(CLUSTERS, clusters);
}
} }
@@ -1,8 +1,9 @@
// //
// $Id: SpotSceneManager.java,v 1.25 2003/02/12 07:23:31 mdb Exp $ // $Id: SpotSceneManager.java,v 1.26 2003/02/13 21:55:22 mdb Exp $
package com.threerings.whirled.spot.server; package com.threerings.whirled.spot.server;
import java.awt.Point;
import java.util.Iterator; import java.util.Iterator;
import com.samskivert.util.ArrayIntSet; import com.samskivert.util.ArrayIntSet;
@@ -22,6 +23,7 @@ import com.threerings.crowd.server.CrowdServer;
import com.threerings.whirled.server.SceneManager; import com.threerings.whirled.server.SceneManager;
import com.threerings.whirled.spot.Log; import com.threerings.whirled.spot.Log;
import com.threerings.whirled.spot.data.Cluster;
import com.threerings.whirled.spot.data.ClusterObject; import com.threerings.whirled.spot.data.ClusterObject;
import com.threerings.whirled.spot.data.ClusteredBodyObject; import com.threerings.whirled.spot.data.ClusteredBodyObject;
import com.threerings.whirled.spot.data.Location; import com.threerings.whirled.spot.data.Location;
@@ -171,6 +173,8 @@ public class SpotSceneManager extends SceneManager
throw new InvocationException(INTERNAL_ERROR); throw new InvocationException(INTERNAL_ERROR);
} }
// TODO: make sure the location isn't too close to another user
// update the user's location information in the scene which will // update the user's location information in the scene which will
// indicate to the client that their avatar should be moved from // indicate to the client that their avatar should be moved from
// its current position to their new position // its current position to their new position
@@ -186,16 +190,10 @@ public class SpotSceneManager extends SceneManager
} }
// handle the cluster situation // handle the cluster situation
switch (cluster) { if (cluster == -1) {
case 0:
createNewCluster(source);
break;
case -1:
removeFromCluster(source.getOid()); removeFromCluster(source.getOid());
break; } else {
default: createOrJoinCluster(cluster, source);
addToCluster(cluster, source);
break;
} }
} }
@@ -219,25 +217,38 @@ public class SpotSceneManager extends SceneManager
/** /**
* Adds the specified user to the cluster occupied by the specified * Adds the specified user to the cluster occupied by the specified
* other user. * other user or creates a new cluster for the two users if neither is
* already clustered.
*/ */
protected void addToCluster (int memberOid, BodyObject joiner) protected void createOrJoinCluster (int memberOid, BodyObject joiner)
{ {
ClusterRecord clrec = (ClusterRecord)_clusters.get(memberOid); ClusterRecord clrec = (ClusterRecord)_clusters.get(memberOid);
if (clrec == null) { if (clrec != null) {
// this isn't completely impossible as the last user might // if the cluster already exists, add this user and be done
// leave a cluster just as another user is entering it, but if (clrec.addBody(joiner)) {
// before the entering user's client was informed that the _clusters.put(joiner.getOid(), clrec);
// cluster went away }
Log.info("Can't add user to non-existent cluster; " + return;
"creating a new one just for them " +
"[memberOid=" + memberOid +
", joiner=" + joiner.who() + "].");
clrec = new ClusterRecord();
} }
// otherwise we have to create a new cluster and add our two
// charter members!
clrec = new ClusterRecord();
BodyObject member = (BodyObject)CrowdServer.omgr.getObject(memberOid);
if (member == null) {
Log.warning("Can't create cluster, missing target " +
"[creator=" + joiner.who() +
", targetOid=" + memberOid + "].");
return;
}
// add our two lovely users to the newly created cluster
if (clrec.addBody(joiner)) { if (clrec.addBody(joiner)) {
_clusters.put(joiner.getOid(), clrec); _clusters.put(joiner.getOid(), clrec);
} }
if (clrec.addBody(member)) {
_clusters.put(member.getOid(), clrec);
}
} }
/** /**
@@ -269,11 +280,39 @@ public class SpotSceneManager extends SceneManager
} }
} }
/**
* Converts the x and y coordinates in the supplied location object to
* Cartesian coordinates that can be manipulated geometrically. The
* default implementation assumes the location coordinates are
* Cartesian, but systems that use different coordinate systems will
* want to override this method and perform the appropriate
* conversions.
*/
protected void locationToCoords (int lx, int ly, Point coords)
{
coords.x = lx;
coords.y = ly;
}
/**
* Converts the supplied x and y coordinates (obtained from a prior
* call to {@link #locationToCoords}) to location coordinates that can
* be sent back to the client. The default implementation assumes the
* location coordinates are Cartesian, but systems that use different
* coordinate systems will want to override this method and perform
* the appropriate conversions.
*/
protected void coordsToLocation (int cx, int cy, Point loc)
{
loc.x = cx;
loc.y = cy;
}
/** /**
* Used to manage clusters which are groups of users that can chat to * Used to manage clusters which are groups of users that can chat to
* one another. * one another.
*/ */
protected static class ClusterRecord extends HashIntMap protected class ClusterRecord extends HashIntMap
implements Subscriber implements Subscriber
{ {
public ClusterRecord () public ClusterRecord ()
@@ -284,11 +323,14 @@ public class SpotSceneManager extends SceneManager
public boolean addBody (BodyObject body) public boolean addBody (BodyObject body)
{ {
if (body instanceof ClusteredBodyObject) { if (body instanceof ClusteredBodyObject) {
put(body.getOid(), body);
_cluster.occupants++;
recomputeCenter();
if (_clobj != null) { if (_clobj != null) {
_ssobj.updateClusters(_cluster);
_clobj.addToOccupants(body.getOid());
((ClusteredBodyObject)body).setClusterOid(_clobj.getOid()); ((ClusteredBodyObject)body).setClusterOid(_clobj.getOid());
} }
put(body.getOid(), body);
_clobj.addToOccupants(body.getOid());
return true; return true;
} else { } else {
@@ -308,7 +350,12 @@ public class SpotSceneManager extends SceneManager
", size=" + size() + ", who=" + bodyOid + "]."); ", size=" + size() + ", who=" + bodyOid + "].");
} else { } else {
body.setClusterOid(-1); body.setClusterOid(-1);
_clobj.removeFromOccupants(bodyOid); _cluster.occupants--;
recomputeCenter();
if (_clobj != null) {
_clobj.removeFromOccupants(bodyOid);
_ssobj.updateClusters(_cluster);
}
} }
// if we've removed our last body; stick a fork in ourselves // if we've removed our last body; stick a fork in ourselves
@@ -327,11 +374,16 @@ public class SpotSceneManager extends SceneManager
// keep this feller around // keep this feller around
_clobj = (ClusterObject)object; _clobj = (ClusterObject)object;
// configure our cluster record and publish it
_cluster.clusterOid = _clobj.getOid();
_ssobj.addToClusters(_cluster);
// let any mapped users know about our cluster // let any mapped users know about our cluster
Iterator iter = values().iterator(); Iterator iter = values().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
ClusteredBodyObject body = (ClusteredBodyObject)iter.next(); ClusteredBodyObject body = (ClusteredBodyObject)iter.next();
body.setClusterOid(_clobj.getOid()); body.setClusterOid(_clobj.getOid());
_clobj.addToOccupants(((BodyObject)body).getOid());
} }
// if we didn't manage to add our creating user when we first // if we didn't manage to add our creating user when we first
@@ -354,6 +406,29 @@ public class SpotSceneManager extends SceneManager
} }
} }
protected void recomputeCenter ()
{
int tx = 0, ty = 0, count = 0;
// compute the average x and y position of all our cluster
// occupants
Iterator iter = _ssobj.occupantLocs.entries();
while (iter.hasNext()) {
SceneLocation sloc = (SceneLocation)iter.next();
if (containsKey(sloc.bodyOid)) {
locationToCoords(sloc.x, sloc.y, _scratch);
tx += _scratch.x;
ty += _scratch.x;
count++;
}
}
// convert the center back to "location" coordinates
coordsToLocation(tx/count, ty/count, _scratch);
_cluster.x = _scratch.x;
_cluster.y = _scratch.y;
}
protected void destroy () protected void destroy ()
{ {
Log.info("Cluster empty, going away " + Log.info("Cluster empty, going away " +
@@ -362,6 +437,8 @@ public class SpotSceneManager extends SceneManager
} }
protected ClusterObject _clobj; protected ClusterObject _clobj;
protected Cluster _cluster = new Cluster();
protected Point _scratch = new Point();
} }
/** A casted reference to our place object. */ /** A casted reference to our place object. */