// // $Id: LocationProvider.java,v 1.21 2003/08/09 04:58:25 mdb Exp $ package com.threerings.crowd.server; import com.threerings.presents.dobj.RootDObjectManager; import com.threerings.presents.data.ClientObject; import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationManager; import com.threerings.presents.server.InvocationProvider; import com.threerings.crowd.Log; import com.threerings.crowd.client.LocationService; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.LocationCodes; import com.threerings.crowd.data.OccupantInfo; import com.threerings.crowd.data.PlaceConfig; import com.threerings.crowd.data.PlaceObject; /** * This class provides the server end of the location services. */ public class LocationProvider implements LocationCodes, InvocationProvider { /** * Creates a location provider and prepares it for operation. */ public LocationProvider (InvocationManager invmgr, RootDObjectManager omgr, PlaceRegistry plreg) { // we'll need these later _invmgr = invmgr; _omgr = omgr; _plreg = plreg; } /** * Requests that this client's body be moved to the specified * location. * * @param caller the client object of the client that invoked this * remotely callable method. * @param placeId the object id of the place object to which the body * should be moved. * @param listener the listener that will be informed of success or * failure. */ public void moveTo (ClientObject caller, int placeId, LocationService.MoveListener listener) throws InvocationException { // do the move and send the response listener.moveSucceeded(moveTo((BodyObject)caller, placeId)); } /** * Moves the specified body from whatever location they currently * occupy to the location identified by the supplied place id. * * @return the config object for the new location. * * @exception ServiceFaildException thrown if the move was not * successful for some reason (which will be communicated as an error * code in the exception's message data). */ public PlaceConfig moveTo (BodyObject source, int placeId) throws InvocationException { int bodoid = source.getOid(); // make sure the place in question actually exists PlaceManager pmgr = _plreg.getPlaceManager(placeId); if (pmgr == null) { Log.info("Requested to move to non-existent place " + "[source=" + source + ", place=" + placeId + "]."); throw new InvocationException(NO_SUCH_PLACE); } // if they're already in the location they're asking to move to, // just give them the config because we don't need to update // anything in distributed object world if (source.location == placeId) { Log.debug("Going along with client request to move to where " + "they already are [source=" + source.who() + ", placeId=" + placeId + "]."); return pmgr.getConfig(); } // acquire a lock on the body object to ensure that rapid fire // moveTo requests don't break things if (!source.acquireLock("moveToLock")) { // if we're still locked, a previous moveTo request hasn't // been fully processed throw new InvocationException(MOVE_IN_PROGRESS); } try { PlaceObject place = pmgr.getPlaceObject(); // the doubly nested try catch is to prevent failure if one or // the other of the transactions fails to start place.startTransaction(); try { source.startTransaction(); try { // remove them from any previous location leaveOccupiedPlace(source); // generate a new occupant info record (which will add // it to the target location) pmgr.buildOccupantInfo(source); // set the body's new location source.setLocation(place.getOid()); // add the body oid to the place object's occupant list place.addToOccupants(bodoid); } finally { source.commitTransaction(); } } finally { place.commitTransaction(); } } finally { // and finally queue up a lock release event to release the // lock once all these events are processed source.releaseLock("moveToLock"); } return pmgr.getConfig(); } /** * Removes the specified body from the place object they currently * occupy. Does nothing if the body is not currently in a place. */ public void leaveOccupiedPlace (BodyObject source) { int oldloc = source.location; int bodoid = source.getOid(); // nothing to do if they weren't previously in some location if (oldloc == -1) { return; } // remove them from the occupant list try { PlaceObject pold = (PlaceObject)_omgr.getObject(oldloc); if (pold != null) { Integer key = new Integer(bodoid); pold.startTransaction(); try { // remove their occupant info (which is keyed on oid) pold.removeFromOccupantInfo(key); // and remove them from the occupant list pold.removeFromOccupants(bodoid); } finally { pold.commitTransaction(); } } else { Log.info("Body's prior location no longer around? " + "[boid=" + bodoid + ", poid=" + oldloc + "]."); } } catch (ClassCastException cce) { Log.warning("Body claims to occupy non-PlaceObject!? " + "[boid=" + bodoid + ", poid=" + oldloc + ", error=" + cce + "]."); } // clear out their location oid source.setLocation(-1); } /** * Forcibly moves the specified body object to the new place. This is * accomplished by first removing the client from their old location * and then sending the client a notification, instructing it to move * to the new location (which it does using the normal moveTo * service). This has the benefit that the client is removed from * their old place regardless of whether or not they are cooperating. * If they choose to ignore the forced move request, they will remain * in limbo, unable to do much of anything. */ public void moveBody (BodyObject source, int placeId) { // first remove them from their old place leaveOccupiedPlace(source); // then send a forced move notification LocationSender.forcedMove(source, placeId); } /** The invocation manager with which we interoperate. */ protected InvocationManager _invmgr; /** The distributed object manager with which we interoperate. */ protected RootDObjectManager _omgr; /** The place registry with which we interoperate. */ protected PlaceRegistry _plreg; }