Whirled lives in Vilya.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4210 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2006-06-23 18:35:30 +00:00
parent c0a244cb8d
commit c01703dc3f
77 changed files with 0 additions and 8269 deletions
-63
View File
@@ -1,63 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled;
/**
* A placeholder class that contains a reference to the log object used by
* the Whirled services.
*/
public class Log
{
public static com.samskivert.util.Log log =
new com.samskivert.util.Log("whirled");
/** Convenience function. */
public static boolean debug ()
{
return (com.samskivert.util.Log.getLevel() ==
com.samskivert.util.Log.DEBUG);
}
/** Convenience function. */
public static void debug (String message)
{
log.debug(message);
}
/** Convenience function. */
public static void info (String message)
{
log.info(message);
}
/** Convenience function. */
public static void warning (String message)
{
log.warning(message);
}
/** Convenience function. */
public static void logStackTrace (Throwable t)
{
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
}
}
@@ -1,91 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.client;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.presents.dobj.MessageListener;
import com.threerings.crowd.client.PlaceController;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.whirled.data.SceneCodes;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.util.WhirledContext;
/**
* The base scene controller class. It is expected that users of the
* Whirled services will extend this controller class when creating
* specialized controllers for their scenes.
*/
public abstract class SceneController extends PlaceController
{
// documentation inherited
public void init (CrowdContext ctx, PlaceConfig config)
{
super.init(ctx, config);
_wctx = (WhirledContext)ctx;
}
// documentation inherited
public void willEnterPlace (PlaceObject plobj)
{
super.willEnterPlace(plobj);
plobj.addListener(_updateListener);
}
// documentation inherited
public void didLeavePlace (PlaceObject plobj)
{
super.didLeavePlace(plobj);
plobj.removeListener(_updateListener);
}
/**
* This method is called if a scene update is recorded while we
* currently occupy a scene. The default implementation will update
* our local scene and scene model, but derived classes will likely
* want to ensure that the update is properly displayed.
*/
protected void sceneUpdated (SceneUpdate update)
{
// apply the update to the scene
_wctx.getSceneDirector().getScene().updateReceived(update);
// we don't persistify these updates in this circumstance, but
// next time we come to this scene we'll redownload the update and
// apply it to the repository; as the updates are meant to be very
// small, this shouldn't be horribly less efficient
}
/** Used to listen for scene updates. */
protected MessageListener _updateListener = new MessageListener() {
public void messageReceived (MessageEvent event) {
if (event.getName().equals(SceneCodes.SCENE_UPDATE)) {
sceneUpdated((SceneUpdate)event.getArgs()[0]);
}
}
};
protected WhirledContext _wctx;
}
@@ -1,69 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.client;
import com.threerings.presents.client.InvocationDecoder;
import com.threerings.whirled.client.SceneReceiver;
/**
* Dispatches calls to a {@link SceneReceiver} instance.
*/
public class SceneDecoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static final String RECEIVER_CODE = "c4d0cf66b81a6e83d119b2d607725651";
/** The method id used to dispatch {@link SceneReceiver#forcedMove}
* notifications. */
public static final int FORCED_MOVE = 1;
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
*/
public SceneDecoder (SceneReceiver receiver)
{
this.receiver = receiver;
}
// documentation inherited
public String getReceiverCode ()
{
return RECEIVER_CODE;
}
// documentation inherited
public void dispatchNotification (int methodId, Object[] args)
{
switch (methodId) {
case FORCED_MOVE:
((SceneReceiver)receiver).forcedMove(
((Integer)args[0]).intValue()
);
return;
default:
super.dispatchNotification(methodId, args);
return;
}
}
}
@@ -1,532 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.client;
import java.io.IOException;
import com.samskivert.util.LRUHashMap;
import com.samskivert.util.ResultListener;
import com.samskivert.util.StringUtil;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.crowd.client.LocationDirector;
import com.threerings.crowd.client.LocationObserver;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.whirled.Log;
import com.threerings.whirled.client.persist.SceneRepository;
import com.threerings.whirled.data.Scene;
import com.threerings.whirled.data.SceneCodes;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.util.NoSuchSceneException;
import com.threerings.whirled.util.SceneFactory;
import com.threerings.whirled.util.WhirledContext;
import com.threerings.whirled.data.SceneUpdate;
/**
* The scene director is the client's interface to all things scene
* related. It interfaces with the scene repository to ensure that scene
* objects are available when the client enters a particular scene. It
* handles moving from scene to scene (it coordinates with the {@link
* LocationDirector} in order to do this).
*
* <p> Note that when the scene director is in use instead of the location
* director, scene ids instead of place oids will be supplied to {@link
* LocationObserver#locationMayChange} and {@link
* LocationObserver#locationChangeFailed}.
*/
public class SceneDirector extends BasicDirector
implements SceneCodes, LocationDirector.FailureHandler,
SceneReceiver, SceneService.SceneMoveListener
{
/**
* Used to recover from a problem after a completed moveTo.
*/
public static interface MoveHandler
{
/**
* Should instruct the client to move the last known working
* location (as well as clean up after the failed moveTo request).
*/
public void recoverMoveTo (int sceneId);
}
/**
* Creates a new scene director with the specified context.
*
* @param ctx the active client context.
* @param locdir the location director in use on the client, with
* which the scene director will coordinate when changing location.
* @param screp the entity from which the scene director will load
* scene data from the local client scene storage. This may be null
* when the SceneDirector is constructed, but it should be
* supplied via {@link #setSceneRepository} prior to really using
* this director.
* @param fact the factory that knows which derivation of {@link
* Scene} to create for the current system.
*/
public SceneDirector (WhirledContext ctx, LocationDirector locdir,
SceneRepository screp, SceneFactory fact)
{
super(ctx);
// we'll need these for later
_ctx = ctx;
_locdir = locdir;
setSceneRepository(screp);
_fact = fact;
// set ourselves up as a failure handler with the location
// director because we need to do special processing
_locdir.setFailureHandler(this);
// register for scene notifications
_ctx.getClient().getInvocationDirector().registerReceiver(
new SceneDecoder(this));
}
/**
* Set the scene repository.
*/
public void setSceneRepository (SceneRepository screp)
{
_screp = screp;
_scache.clear();
}
/**
* Returns the display scene object associated with the scene we
* currently occupy or null if we currently occupy no scene.
*/
public Scene getScene ()
{
return _scene;
}
/**
* Requests that this client move the specified scene. A request will
* be made and when the response is received, the location observers
* will be notified of success or failure.
*
* @return true if the move to request was issued, false if it was
* rejected by a location observer or because we have another request
* outstanding.
*/
public boolean moveTo (int sceneId)
{
// make sure the sceneId is valid
if (sceneId < 0) {
Log.warning("Refusing moveTo(): invalid sceneId " + sceneId + ".");
return false;
}
// sanity-check the destination scene id
if (sceneId == _sceneId) {
Log.warning("Refusing request to move to the same scene " +
"[sceneId=" + sceneId + "].");
return false;
}
// prepare to move to this scene (sets up pending data)
if (!prepareMoveTo(sceneId, null)) {
return false;
}
// 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 sceneVers = 0;
if (_pendingModel != null) {
sceneVers = _pendingModel.version;
}
// issue a moveTo request
Log.info("Issuing moveTo(" + sceneId + ", " + sceneVers + ").");
_sservice.moveTo(_ctx.getClient(), sceneId, sceneVers, this);
return true;
}
/**
* Prepares to move to the requested scene. The location observers are
* asked to ratify the move and our pending scene mode is loaded from
* the scene repository. This can be called by cooperating directors
* that need to coopt the moveTo process.
*/
public boolean prepareMoveTo (int sceneId, ResultListener rl)
{
// first check to see if our observers are happy with this move
// request
if (!_locdir.mayMoveTo(sceneId, rl)) {
return false;
}
// we need to call this both to mark that we're issuing a move
// request and to check to see if the last issued request should
// be considered stale
boolean refuse = _locdir.checkRepeatMove();
// complain if we're over-writing a pending request
if (_pendingSceneId != -1) {
if (refuse) {
Log.warning("Refusing moveTo; We have a request outstanding " +
"[psid=" + _pendingSceneId +
", nsid=" + sceneId + "].");
return false;
} else {
Log.warning("Overriding stale moveTo request " +
"[psid=" + _pendingSceneId +
", nsid=" + sceneId + "].");
}
}
// load up the pending scene so that we can communicate it's most
// recent version to the server
_pendingModel = loadSceneModel(sceneId);
// make a note of our pending scene id
_pendingSceneId = sceneId;
// all systems go
return true;
}
/**
* Returns the model loaded in preparation for a scene
* transition. This is made available only for cooperating directors
* which may need to coopt the scene transition process. The pending
* model is only valid immediately following a call to {@link
* #prepareMoveTo}.
*/
public SceneModel getPendingModel ()
{
return _pendingModel;
}
// documentation inherited from interface
public void moveSucceeded (int placeId, PlaceConfig config)
{
// our move request was successful, deal with subscribing to our
// new place object
_locdir.didMoveTo(placeId, config);
// since we're committed to moving to the new scene, we'll
// parallelize and go ahead and load up the new scene now rather
// than wait until subscription to our place object succeeds
// keep track of our previous scene info
_previousSceneId = _sceneId;
// clear out the old info
clearScene();
// make the pending scene the active scene
_sceneId = _pendingSceneId;
_pendingSceneId = -1;
// load the new scene model
_model = loadSceneModel(_sceneId);
// complain if we didn't find a scene
if (_model == null) {
Log.warning("Aiya! Unable to load scene [sid=" + _sceneId +
", plid=" + placeId + "].");
return;
}
// and finally create a display scene instance with the model and
// the place config
_scene = _fact.createScene(_model, config);
}
// documentation inherited from interface
public void moveSucceededWithUpdates (
int placeId, PlaceConfig config, SceneUpdate[] updates)
{
Log.info("Got updates [placeId=" + placeId + ", config=" + config +
", updates=" + StringUtil.toString(updates) + "].");
// apply the updates to our cached scene
SceneModel model = loadSceneModel(_pendingSceneId);
boolean failure = false;
for (int ii = 0; ii < updates.length; ii++) {
try {
updates[ii].validate(model);
} catch (IllegalStateException ise) {
Log.warning("Scene update failed validation [model=" + model +
", update=" + updates[ii] +
", error=" + ise.getMessage() + "].");
failure = true;
break;
}
try {
updates[ii].apply(model);
} catch (Exception e) {
Log.warning("Failure applying scene update [model=" + model +
", update=" + updates[ii] + "].");
Log.logStackTrace(e);
failure = true;
break;
}
}
if (failure) {
// delete the now half-booched scene model from the repository
try {
_screp.deleteSceneModel(_pendingSceneId);
} catch (IOException ioe) {
Log.warning("Failure removing booched scene model " +
"[sceneId=" + _pendingSceneId + "].");
Log.logStackTrace(ioe);
}
// act as if the scene move failed, though we'll be in a funny
// state because the server thinks we've changed scenes, but
// the client can try again without its booched scene model
requestFailed(INTERNAL_ERROR);
return;
}
// store the updated scene in the repository
try {
_screp.storeSceneModel(model);
} catch (IOException ioe) {
Log.warning("Failed to update repository with updated scene " +
"[sceneId=" + model.sceneId + "].");
Log.logStackTrace(ioe);
}
// finally pass through to the normal success handler
moveSucceeded(placeId, config);
}
// documentation inherited from interface
public void moveSucceededWithScene (
int placeId, PlaceConfig config, SceneModel model)
{
Log.info("Got updated scene model [placeId=" + placeId +
", config=" + config + ", scene=" + model.sceneId + "/" +
model.name + "/" + model.version + "].");
// update the model in the repository
try {
_screp.storeSceneModel(model);
} catch (IOException ioe) {
Log.warning("Failed to update repository with new version " +
"[sceneId=" + model.sceneId +
", nvers=" + model.version + "].");
Log.logStackTrace(ioe);
}
// update our scene cache
_scache.put(Integer.valueOf(model.sceneId), model);
// and pass through to the normal move succeeded handler
moveSucceeded(placeId, config);
}
// documentation inherited from interface
public void requestFailed (String reason)
{
// clear out our pending request oid
int sceneId = _pendingSceneId;
_pendingSceneId = -1;
// let our observers know that something has gone horribly awry
_locdir.failedToMoveTo(sceneId, reason);
}
/**
* Called to clean up our place and scene state information when we
* leave a scene.
*/
public void didLeaveScene ()
{
// let the location director know what's up
_locdir.didLeavePlace();
// clear out our own scene state
clearScene();
}
// documentation inherited from interface
public void forcedMove (int sceneId)
{
Log.info("Moving at request of server [sceneId=" + sceneId + "].");
// clear out our old scene and place data
didLeaveScene();
// move to the new scene
moveTo(sceneId);
}
/**
* Sets the moveHandler for use in recoverFailedMove.
*/
public void setMoveHandler (MoveHandler handler)
{
if (_moveHandler != null) {
Log.warning("Requested to set move handler, but we've " +
"already got one. The conflicting entities will " +
"likely need to perform more sophisticated " +
"coordination to deal with failures. " +
"[old=" + _moveHandler + ", new=" + handler + "].");
} else {
_moveHandler = handler;
}
}
/**
* Called when something breaks down in the process of performing a
* <code>moveTo</code> request.
*/
public void recoverFailedMove (int placeId)
{
// we'll need this momentarily
int sceneId = _sceneId;
// clear out our now bogus scene tracking info
clearScene();
// if we were previously somewhere (and that somewhere isn't where
// we just tried to go), try going back to that happy place
if (_previousSceneId != -1 && _previousSceneId != sceneId) {
// if we have a move handler use that
if (_moveHandler != null) {
_moveHandler.recoverMoveTo(_previousSceneId);
} else {
moveTo(_previousSceneId);
}
}
}
/**
* Clears out our current scene information and releases the scene
* model for the loaded scene back to the cache.
*/
protected void clearScene ()
{
// clear out our scene id info
_sceneId = -1;
// clear out our references
_model = null;
_scene = null;
}
/**
* Loads a scene from the repository. If the scene is cached, it will
* be returned from the cache instead.
*/
protected SceneModel loadSceneModel (int sceneId)
{
// first look in the model cache
Integer key = Integer.valueOf(sceneId);
SceneModel model = (SceneModel)_scache.get(key);
// load from the repository if it's not cached
if (model == null) {
try {
model = _screp.loadSceneModel(sceneId);
_scache.put(key, model);
} catch (NoSuchSceneException nsse) {
// nothing special here, just fall through and return null
} catch (IOException ioe) {
// complain first, then return null
Log.warning("Error loading scene [scid=" + sceneId +
", error=" + ioe + "].");
}
}
return model;
}
// documentation inherited from interface
public void clientDidLogoff (Client client)
{
super.clientDidLogoff(client);
// clear out our business
clearScene();
_scache.clear();
_pendingSceneId = -1;
_pendingModel = null;
_previousSceneId = -1;
_sservice = null;
}
// documentation inherited from interface
protected void fetchServices (Client client)
{
// get a handle on our scene service
_sservice = (SceneService)client.requireService(SceneService.class);
}
/** Access to general client services. */
protected WhirledContext _ctx;
/** Access to our scene services. */
protected SceneService _sservice;
/** The client's active location director. */
protected LocationDirector _locdir;
/** The entity via which we load scene data. */
protected SceneRepository _screp;
/** The entity we use to create scenes from scene models. */
protected SceneFactory _fact;
/** A cache of scene model information. */
protected LRUHashMap _scache = new LRUHashMap(5);
/** The display scene object for the scene we currently occupy. */
protected Scene _scene;
/** The scene model for the scene we currently occupy. */
protected SceneModel _model;
/** The id of the scene we currently occupy. */
protected int _sceneId = -1;
/** Our most recent copy of the scene model for the scene we're about
* to enter. */
protected SceneModel _pendingModel;
/** The id of the scene for which we have an outstanding moveTo
* request, or -1 if we have no outstanding request. */
protected int _pendingSceneId = -1;
/** The id of the scene we previously occupied. */
protected int _previousSceneId = -1;
/** Reference to our move handler. */
protected MoveHandler _moveHandler = null;
}
@@ -1,39 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.client;
import com.threerings.presents.client.InvocationReceiver;
/**
* Defines, for the scene services, a set of notifications delivered
* asynchronously by the server to the client.
*/
public interface SceneReceiver extends InvocationReceiver
{
/**
* Used to communicate a required move notification to the client. The
* server will have removed the client from their existing scene
* and the client is then responsible for generating a {@link
* SceneService#moveTo} request to move to the new scene.
*/
public void forcedMove (int sceneId);
}
@@ -1,86 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
/**
* The scene service class provides the client interface to the scene
* related invocation services (e.g. moving from scene to scene).
*/
public interface SceneService extends InvocationService
{
/**
* Used to communicate the response to a {@link #moveTo} request.
*/
public static interface SceneMoveListener extends InvocationListener
{
/**
* Indicates that a move succeeded.
*
* @param placeId the place object id of the newly occupied scene.
* @param config metadata related to the newly occupied scene.
*/
public void moveSucceeded (int placeId, PlaceConfig config);
/**
* Indicates that a move succeeded and that the client's cached
* scene information should be updated with the supplied data.
*
* @param placeId the place object id of the newly occupied scene.
* @param config metadata related to the newly occupied scene.
* @param updates updates that must be applied to the client's
* copy of a scene model to bring it up to date.
*/
public void moveSucceededWithUpdates (int placeId, PlaceConfig config,
SceneUpdate[] updates);
/**
* Indicates that a move succeeded and that the client's cached
* scene information should be updated with the supplied data.
*
* @param placeId the place object id of the newly occupied scene.
* @param config metadata related to the newly occupied scene.
* @param model a fresh copy of the most recent scene data for the
* newly occupied scene.
*/
public void moveSucceededWithScene (int placeId, PlaceConfig config,
SceneModel model);
}
/**
* Requests that that this client's body be moved to the specified
* scene.
*
* @param sceneId the scene id to which we want to move.
* @param version the version number of the scene object that we have
* in our local repository.
*/
public void moveTo (Client client, int sceneId, int version,
SceneMoveListener listener);
}
@@ -1,65 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.client.persist;
import java.io.IOException;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.util.NoSuchSceneException;
/**
* The scene repository provides access to a persistent repository of
* scene information.
*
* @see SceneModel
*/
public interface SceneRepository
{
/**
* Fetches the model for the scene with the specified id.
*
* @exception IOException thrown if an error occurs attempting to load
* the scene data.
* @exception NoSuchSceneException thrown if no scene exists with the
* specified scene id.
*/
public SceneModel loadSceneModel (int sceneId)
throws IOException, NoSuchSceneException;
/**
* Updates or inserts this scene model as appropriate.
*
* @exception IOException thrown if an error occurs attempting to
* access the repository.
*/
public void storeSceneModel (SceneModel model)
throws IOException;
/**
* Deletes the specified scene model from the repository.
*
* @exception IOException thrown if an error occurs attempting to
* access the repository.
*/
public void deleteSceneModel (int sceneId)
throws IOException;
}
@@ -1,36 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
import com.threerings.io.Streamable;
/**
* An interface that must be implemented by auxiliary scene models.
*/
public interface AuxModel extends Streamable, Cloneable
{
/**
* Creates a clone of this auxiliary model.
*/
public Object clone ()
throws CloneNotSupportedException;
}
@@ -1,44 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.whirled.client.SceneController;
/**
* The default scene config simply causes the default scene manager and
* controller to be created. A user of the Whirled services would most
* likely extend the default scene config.
*
* <p> Note that this place config won't even work on the client side
* because it instantiates a {@link SceneController} which is an abstract
* class. It is used only for testing the server side and as a placeholder
* in case standard scene configuration information is one day needed.
*/
public class DefaultSceneConfig extends PlaceConfig
{
// documentation inherited
public String getManagerClassName ()
{
return "com.threerings.whirled.server.SceneManager";
}
}
@@ -1,83 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
import com.threerings.crowd.data.PlaceConfig;
/**
* This interface makes available basic scene information. At this basic
* level, not much information is available, but extensions to this
* interface begin to create a more comprehensive picture of a scene in a
* system built from the Whirled services.
*/
public interface Scene
{
/**
* Returns the unique identifier for this scene.
*/
public int getId ();
/**
* Returns the human readable name of this scene.
*/
public String getName ();
/**
* Returns the version number of this scene.
*/
public int getVersion ();
/**
* Returns the place config that can be used to determine which place
* controller instance should be used to display this scene as well as
* to obtain runtime configuration information.
*/
public PlaceConfig getPlaceConfig ();
/**
* Sets this scene's unique identifier.
*/
public void setId (int sceneId);
/**
* Sets the human readable name of this scene.
*/
public void setName (String name);
/**
* Sets this scene's version number.
*/
public void setVersion (int version);
/**
* Called to inform the scene that an update has been received while
* the scene was resolved and active. The update should be applied to
* the underlying scene model and any derivative data should be
* appropriately updated.
*/
public void updateReceived (SceneUpdate update);
/**
* Returns the scene model from which this scene was created.
*/
public SceneModel getSceneModel ();
}
@@ -1,33 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
import com.threerings.crowd.data.LocationCodes;
/**
* Contains codes used by the scene invocation services.
*/
public interface SceneCodes extends LocationCodes
{
/** The message identifier for scene update messages. */
public static final String SCENE_UPDATE = "scene_update";
}
@@ -1,127 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.whirled.Log;
/**
* An implementation of the {@link Scene} interface.
*/
public class SceneImpl implements Scene
{
/**
* Creates an instance that will obtain data from the supplied scene
* model and place config.
*/
public SceneImpl (SceneModel model, PlaceConfig config)
{
_model = model;
_config = config;
}
/**
* Instantiates a blank scene implementation. No place config will be
* associated with this scene.
*/
public SceneImpl ()
{
_model = SceneModel.blankSceneModel();
}
// documentation inherited
public int getId ()
{
return _model.sceneId;
}
// documentation inherited
public String getName ()
{
return _model.name;
}
// documentation inherited
public int getVersion ()
{
return _model.version;
}
// documentation inherited
public PlaceConfig getPlaceConfig ()
{
return _config;
}
// documentation inherited from interface
public void setId (int sceneId)
{
_model.sceneId = sceneId;
}
// documentation inherited from interface
public void setName (String name)
{
_model.name = name;
}
// documentation inherited from interface
public void setVersion (int version)
{
_model.version = version;
}
// documentation inherited from interface
public void updateReceived (SceneUpdate update)
{
try {
// validate and apply the update
update.validate(_model);
update.apply(_model);
} catch (Exception e) {
Log.warning("Error applying update [scene=" + this +
", update=" + update + "].");
Log.logStackTrace(e);
}
}
// documentation inherited from interface
public SceneModel getSceneModel ()
{
return _model;
}
/**
* Generates a string representation of this instance.
*/
public String toString ()
{
return "[model=" + _model + ", config=" + _config + "]";
}
/** A reference to our scene model. */
protected SceneModel _model;
/** A reference to our place configuration. */
protected PlaceConfig _config;
}
@@ -1,124 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
import com.threerings.whirled.client.SceneService;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
/**
* Provides the implementation of the {@link SceneService} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
public class SceneMarshaller extends InvocationMarshaller
implements SceneService
{
// documentation inherited
public static class SceneMoveMarshaller extends ListenerMarshaller
implements SceneMoveListener
{
/** The method id used to dispatch {@link #moveSucceeded}
* responses. */
public static final int MOVE_SUCCEEDED = 1;
// documentation inherited from interface
public void moveSucceeded (int arg1, PlaceConfig arg2)
{
_invId = null;
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, MOVE_SUCCEEDED,
new Object[] { Integer.valueOf(arg1), arg2 }));
}
/** The method id used to dispatch {@link #moveSucceededWithScene}
* responses. */
public static final int MOVE_SUCCEEDED_WITH_SCENE = 2;
// documentation inherited from interface
public void moveSucceededWithScene (int arg1, PlaceConfig arg2, SceneModel arg3)
{
_invId = null;
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, MOVE_SUCCEEDED_WITH_SCENE,
new Object[] { Integer.valueOf(arg1), arg2, arg3 }));
}
/** The method id used to dispatch {@link #moveSucceededWithUpdates}
* responses. */
public static final int MOVE_SUCCEEDED_WITH_UPDATES = 3;
// documentation inherited from interface
public void moveSucceededWithUpdates (int arg1, PlaceConfig arg2, SceneUpdate[] arg3)
{
_invId = null;
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, MOVE_SUCCEEDED_WITH_UPDATES,
new Object[] { Integer.valueOf(arg1), arg2, arg3 }));
}
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case MOVE_SUCCEEDED:
((SceneMoveListener)listener).moveSucceeded(
((Integer)args[0]).intValue(), (PlaceConfig)args[1]);
return;
case MOVE_SUCCEEDED_WITH_SCENE:
((SceneMoveListener)listener).moveSucceededWithScene(
((Integer)args[0]).intValue(), (PlaceConfig)args[1], (SceneModel)args[2]);
return;
case MOVE_SUCCEEDED_WITH_UPDATES:
((SceneMoveListener)listener).moveSucceededWithUpdates(
((Integer)args[0]).intValue(), (PlaceConfig)args[1], (SceneUpdate[])args[2]);
return;
default:
super.dispatchResponse(methodId, args);
return;
}
}
}
/** The method id used to dispatch {@link #moveTo} requests. */
public static final int MOVE_TO = 1;
// documentation inherited from interface
public void moveTo (Client arg1, int arg2, int arg3, SceneService.SceneMoveListener arg4)
{
SceneMarshaller.SceneMoveMarshaller listener4 = new SceneMarshaller.SceneMoveMarshaller();
listener4.listener = arg4;
sendRequest(arg1, MOVE_TO, new Object[] {
Integer.valueOf(arg2), Integer.valueOf(arg3), listener4
});
}
}
@@ -1,94 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
import com.samskivert.util.ArrayUtil;
import com.threerings.io.SimpleStreamableObject;
/**
* The scene model is the bare bones representation of the data for a
* scene in the Whirled system. From the scene model, one would create an
* instance of {@link Scene}.
*
* <p> The scene model is what is loaded from the scene repositories and
* what is transmitted over the wire when communicating scenes from the
* server to the client.
*/
public class SceneModel extends SimpleStreamableObject
implements Cloneable
{
/** This scene's unique identifier. */
public int sceneId;
/** The human readable name of this scene. */
public String name;
/** The version number of this scene. Versions are incremented
* whenever modifications are made to a scene so that clients can
* determine whether or not they have the latest version of a
* scene. */
public int version;
/** Auxiliary scene model information. */
public AuxModel[] auxModels = new AuxModel[0];
/**
* Adds the specified auxiliary model to this scene model.
*/
public void addAuxModel (AuxModel auxModel)
{
auxModels = (AuxModel[])ArrayUtil.append(auxModels, auxModel);
}
// documentation inherited
public Object clone ()
throws CloneNotSupportedException
{
SceneModel model = (SceneModel)super.clone();
model.auxModels = new AuxModel[auxModels.length];
for (int ii = 0; ii < auxModels.length; ii++) {
model.auxModels[ii] = (AuxModel)auxModels[ii].clone();
}
return model;
}
/**
* Creates and returns a blank scene model.
*/
public static SceneModel blankSceneModel ()
{
SceneModel model = new SceneModel();
populateBlankSceneModel(model);
return model;
}
/**
* Populates a blank scene model with blank values.
*/
protected static void populateBlankSceneModel (SceneModel model)
{
model.sceneId = -1;
model.name = "<blank>";
model.version = 0;
}
}
@@ -1,28 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
import com.threerings.crowd.data.PlaceObject;
public class SceneObject extends PlaceObject
{
}
@@ -1,201 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
import java.io.IOException;
import com.samskivert.util.StringUtil;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;
import com.threerings.whirled.Log;
/**
* Used to encapsulate updates to scenes in such a manner that updates can
* be stored persistently and sent to clients to update their own local
* copies of scenes.
*/
public class SceneUpdate
implements Streamable, Cloneable
{
/**
* Initializes this scene update such that it will operate on a scene
* with the specified target scene and version number.
*
* @param targetId the id of the scene on which we are to operate.
* @param targetVersion the version of the scene on which we are to
* operate.
*/
public void init (int targetId, int targetVersion)
{
_targetId = targetId;
_targetVersion = targetVersion;
}
/**
* Returns the scene id for which this update is appropriate.
*/
public int getSceneId ()
{
return _targetId;
}
/**
* Returns the scene version for which this update is appropriate.
*/
public int getSceneVersion ()
{
return _targetVersion;
}
/**
* Called to ensure that the scene is in the appropriate state prior
* to applying the update.
*
* @exception IllegalStateException thrown if the update cannot be
* applied to the scene because it is not in a valid state
* (appropriate previous updates were not applied, it's the wrong kind
* of scene, etc.).
*/
public void validate (SceneModel model)
throws IllegalStateException
{
if (model.sceneId != _targetId) {
String errmsg = "Wrong target scene, expected id " +
_targetId + " got id " + model.sceneId;
throw new IllegalStateException(errmsg);
}
if (model.version != _targetVersion) {
String errmsg = "Target scene not proper version, expected " +
_targetVersion + " got " + model.version;
throw new IllegalStateException(errmsg);
}
}
/**
* Applies this update to the specified scene model. Derived classes
* will want to override this method and apply updates of their own,
* being sure to call <code>super.apply</code>.
*/
public void apply (SceneModel model)
{
// increment the version; disallowing integer overflow
model.version = Math.max(_targetVersion + 1, model.version);
// sanity check for the amazing two billion updates
if (model.version == _targetVersion) {
Log.warning("Egads! This scene has been updated two billion " +
"times [model=" + model + ", update=" + this + "].");
}
}
/**
* Writes our custom streamable fields.
*/
public void writeObject (ObjectOutputStream out)
throws IOException
{
if (null == _dbSer.get()) {
out.writeInt(_targetId);
out.writeInt(_targetVersion);
}
out.defaultWriteObject();
}
/**
* Reads our custom streamable fields.
*/
public void readObject (ObjectInputStream in)
throws IOException, ClassNotFoundException
{
if (null == _dbSer.get()) {
_targetId = in.readInt();
_targetVersion = in.readInt();
}
in.defaultReadObject();
}
/**
* Serializes the bare representation of this instance without the scene id
* and version fields. Useful when storing updates in a database where the
* scene id and version fields are stored in separate columns and the rest
* if the representation is contained in an opaque blob.
*/
public void persistTo (ObjectOutputStream out)
throws IOException
{
_dbSer.set(Boolean.TRUE);
try {
out.writeBareObject(this);
} finally {
_dbSer.set(null);
}
}
/**
* Unserializes this instance from the bare representation created by
* {@link #persistTo}.
*/
public void unpersistFrom (ObjectInputStream in)
throws IOException, ClassNotFoundException
{
_dbSer.set(Boolean.TRUE);
try {
in.readBareObject(this);
} finally {
_dbSer.set(null);
}
}
/**
* Generates a string representation of this instance.
*/
public String toString ()
{
StringBuilder buf = new StringBuilder("[");
toString(buf);
return buf.append("]").toString();
}
/**
* An extensible mechanism for generating a string representation of
* this instance.
*/
protected void toString (StringBuilder buf)
{
buf.append("sceneId=").append(_targetId);
buf.append(", version=").append(_targetVersion);
buf.append(", ");
StringUtil.fieldsToString(buf, this);
}
/** The version number of the scene on which we operate. */
protected transient int _targetId;
/** The version number of the scene on which we operate. */
protected transient int _targetVersion;
/** Used when serializing this update for storage in the database. */
protected static ThreadLocal _dbSer = new ThreadLocal();
}
@@ -1,39 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.data;
/**
* A system that uses the whirled services must provide a body object
* extension that implements this interface.
*/
public interface ScenedBodyObject
{
/**
* Returns the scene id currently occupied by this body.
*/
public int getSceneId ();
/**
* Sets the scene id currently occupied by this body.
*/
public void setSceneId (int sceneId);
}
@@ -1,73 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.server;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.server.InvocationDispatcher;
import com.threerings.presents.server.InvocationException;
import com.threerings.whirled.client.SceneService;
import com.threerings.whirled.data.SceneMarshaller;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
/**
* Dispatches requests to the {@link SceneProvider}.
*/
public class SceneDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public SceneDispatcher (SceneProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new SceneMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case SceneMarshaller.MOVE_TO:
((SceneProvider)provider).moveTo(
source,
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), (SceneService.SceneMoveListener)args[2]
);
return;
default:
super.dispatchRequest(source, methodId, args);
return;
}
}
}
@@ -1,194 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.server;
import com.samskivert.io.PersistenceException;
import com.samskivert.util.Invoker;
import com.threerings.crowd.server.PlaceManager;
import com.threerings.presents.server.PresentsServer;
import com.threerings.whirled.Log;
import com.threerings.whirled.data.Scene;
import com.threerings.whirled.data.SceneCodes;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.server.WhirledServer;
import com.threerings.whirled.util.UpdateList;
/**
* The scene manager extends the place manager and takes care of basic
* scene services. Presently that is little more than registering the
* scene manager with the scene registry so that the manager can be looked
* up by scene id in addition to place object id.
*/
public class SceneManager extends PlaceManager
{
/**
* Returns the scene object (not the scene distributed object) being
* managed by this scene manager.
*/
public Scene getScene ()
{
return _scene;
}
/**
* Returns {@link UpdateList#getUpdates} for this scene's updates.
*/
public SceneUpdate[] getUpdates (int fromVersion)
{
return _updates.getUpdates(fromVersion);
}
/**
* Called by the scene registry once the scene manager has been
* created (and initialized), but before it is started up.
*/
protected void setSceneData (Scene scene, UpdateList updates,
SceneRegistry screg)
{
_scene = scene;
_screg = screg;
_updates = updates;
// make sure the list and our version of the scene are in
// accordance
if (!_updates.validate(scene.getVersion())) {
Log.warning("Provided with invalid updates; flushing " +
"[where=" + where() +
", sceneId=" + scene.getId() + "].");
// clear out the update list as it will not allow us to bring
// clients up to date with our current scene version; instead
// they'll have to download the whole thing
_updates = new UpdateList();
}
// let derived classes react to the receipt of scene data
gotSceneData();
}
/**
* A method that can be overridden by derived classes to perform
* initialization processing after we receive our scene information
* but before we're started up (and hence registered as an active
* place).
*/
protected void gotSceneData ()
{
}
/**
* We're fully ready to go, so now we register ourselves with the
* scene registry which will make us available to the clients and
* system at large.
*/
protected void didStartup ()
{
super.didStartup();
// Wait until us and all of our subclasses have completely finished
// running didStartup prior to registering the scene as being ready.
PresentsServer.omgr.postRunnable(new Runnable() {
public void run () {
_screg.sceneManagerDidStart(SceneManager.this);
}
});
}
/**
* Called when we have shutdown.
*/
protected void didShutdown ()
{
super.didShutdown();
// unregister ourselves with the scene registry
_screg.unmapSceneManager(this);
}
/**
* When a modification is made to a scene, the scene manager should
* create a SceneUpdate instance and pass it to this method which will
* update the in-memory scene, and apply and record the update in the
* scene repository.
*
* <p> This update will be stored persistently and provided (along
* with any other accumulated updates) to clients that later request
* to enter the scene with an old version of the scene data. Updates
* are not stored forever, but a sizable number of recent updates are
* stored so that moderately current clients can apply incremental
* patches to their scenes rather than redownloading entire scenes
* when they change.
*/
protected void recordUpdate (final SceneUpdate update)
{
// instruct our in-memory copy of the scene to apply the update
_scene.updateReceived(update);
// add it to our in memory update list
_updates.addUpdate(update);
// and apply and store it in the repository
WhirledServer.invoker.postUnit(new Invoker.Unit() {
public boolean invoke () {
try {
_screg.getSceneRepository().applyAndRecordUpdate(
_scene.getSceneModel(), update);
} catch (PersistenceException pe) {
Log.warning("Failed to apply scene update " + update + ".");
Log.logStackTrace(pe);
}
return false;
}
});
// broadcast the update to all occupants of the scene
_plobj.postMessage(SceneCodes.SCENE_UPDATE, new Object[] { update });
}
// documentation inherited
public String where ()
{
return _scene.getName() + " (" + super.where() + ":" +
_scene.getId() + ")";
}
// documentation inherited
protected void toString (StringBuilder buf)
{
super.toString(buf);
buf.append(", scene=").append(_scene);
}
/** A reference to our scene implementation which provides a
* meaningful interpretation of the data in the scene model. */
protected Scene _scene;
/** A list of the updates tracked for this scene. These will be used
* to attempt to bring clients up to date efficiently if they request
* to enter our scene with old scene model data. */
protected UpdateList _updates;
/** A reference to the scene registry so that we can call back to it
* when we're fully initialized. */
protected SceneRegistry _screg;
}
@@ -1,184 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.server.LocationProvider;
import com.threerings.whirled.Log;
import com.threerings.whirled.client.SceneService.SceneMoveListener;
import com.threerings.whirled.data.SceneCodes;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.data.ScenedBodyObject;
/**
* The scene provider handles the server side of the scene related
* invocation services (e.g. moving from scene to scene).
*/
public class SceneProvider
implements InvocationProvider, SceneCodes
{
/**
* Constructs a scene provider that will interact with the supplied
* scene registry.
*/
public SceneProvider (LocationProvider locprov, SceneRegistry screg)
{
_locprov = locprov;
_screg = screg;
}
/**
* Processes a request from a client to move to a new scene.
*/
public void moveTo (ClientObject caller, int sceneId,
final int sceneVer, final SceneMoveListener listener)
{
final BodyObject source = (BodyObject)caller;
// create a callback object that will handle the resolution or
// failed resolution of the scene
SceneRegistry.ResolutionListener rl = null;
rl = new SceneRegistry.ResolutionListener() {
public void sceneWasResolved (SceneManager scmgr) {
// make sure our caller is still around; under heavy load,
// clients might end their session while the scene is
// resolving
if (!source.isActive()) {
Log.info("Abandoning scene move, client gone " +
"[who=" + source.who() +
", dest=" + scmgr.where() + "].");
InvocationMarshaller.setNoResponse(listener);
return;
}
finishMoveToRequest(source, scmgr, sceneVer, listener);
}
public void sceneFailedToResolve (int rsceneId, Exception reason) {
Log.warning("Unable to resolve scene [sceneid=" + rsceneId +
", reason=" + reason + "].");
// pretend like the scene doesn't exist to the client
listener.requestFailed(NO_SUCH_PLACE);
}
};
// make sure the scene they are headed to is actually loaded into
// the server
_screg.resolveScene(sceneId, rl);
}
/**
* This is called after the scene to which we are moving is guaranteed
* to have been loaded into the server.
*/
protected void finishMoveToRequest (
BodyObject source, SceneManager scmgr, int sceneVersion,
SceneMoveListener listener)
{
try {
effectSceneMove(source, scmgr, sceneVersion, listener);
} catch (InvocationException sfe) {
listener.requestFailed(sfe.getMessage());
} catch (RuntimeException re) {
Log.logStackTrace(re);
listener.requestFailed(INTERNAL_ERROR);
}
}
/**
* Moves the supplied body into the supplied (already resolved) scene
* and informs the supplied listener if the move is successful.
*
* @exception InvocationException thrown if a failure occurs
* attempting to move the user into the place associated with the
* scene.
*/
public void effectSceneMove (BodyObject source, SceneManager scmgr,
int sceneVersion, SceneMoveListener listener)
throws InvocationException
{
// move to the place object associated with this scene
int ploid = scmgr.getPlaceObject().getOid();
PlaceConfig config = _locprov.moveTo(source, ploid);
// now that we've finally moved, we can update the user object
// with the new scene id
((ScenedBodyObject)source).setSceneId(scmgr.getScene().getId());
// check to see if they need a newer version of the scene data
SceneModel model = scmgr.getScene().getSceneModel();
if (sceneVersion < model.version) {
// try getting updates
SceneUpdate[] updates = scmgr.getUpdates(sceneVersion);
if (updates != null) {
listener.moveSucceededWithUpdates(ploid, config, updates);
} else {
listener.moveSucceededWithScene(ploid, config, model);
}
} else {
listener.moveSucceeded(ploid, config);
}
}
/**
* Ejects the specified body from their current scene and sends them a
* request to move to the specified new scene. This is the
* scene-equivalent to {@link LocationProvider#moveBody}.
*/
public void moveBody (BodyObject source, int sceneId)
{
// first remove them from their old place
_locprov.leaveOccupiedPlace(source);
// then send a forced move notification
SceneSender.forcedMove(source, sceneId);
}
/**
* Ejects the specified body from their current scene and zone. This
* is the zone equivalent to {@link
* LocationProvider#leaveOccupiedPlace}.
*/
public void leaveOccupiedScene (ScenedBodyObject source)
{
// remove them from their occupied place
_locprov.leaveOccupiedPlace((BodyObject)source);
// and clear out their scene information
source.setSceneId(-1);
}
/** The location provider we use to handle low-level location stuff. */
protected LocationProvider _locprov;
/** The scene registry with which we interact. */
protected SceneRegistry _screg;
}
@@ -1,354 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.server;
import java.util.ArrayList;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.Invoker;
import com.threerings.presents.server.InvocationManager;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.whirled.Log;
import com.threerings.whirled.data.Scene;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.server.persist.SceneRepository;
import com.threerings.whirled.util.SceneFactory;
import com.threerings.whirled.util.UpdateList;
/**
* The scene registry is responsible for the management of all scenes. It
* handles interaction with the scene repository and ensures that scenes
* are loaded into memory when needed and flushed from memory when not
* needed.
*
* <p> The scene repository also takes care of bridging from the blocking,
* synchronous world of the scene repository to the non-blocking
* asynchronous world of the distributed object event queue. Thus its
* interfaces for accessing scenes are structured so as to not block the
* dobjmgr thread while waiting for scenes to be read from or written to
* the repository.
*
* <p><em>Note:</em> All access to the scene registry should take place
* from the dobjmgr thread.
*/
public class SceneRegistry
{
/**
* Used to create {@link PlaceConfig} instances for scenes.
*/
public static interface ConfigFactory
{
/**
* Creates the place config instance appropriate to the specified
* scene.
*/
PlaceConfig createPlaceConfig (SceneModel model);
}
/** Used to provide scene-related server-side services. */
public SceneProvider sceneprov;
/**
* Constructs a scene registry, instructing it to load and store
* scenes using the supplied scene repository.
*/
public SceneRegistry (InvocationManager invmgr, SceneRepository screp,
SceneFactory scfact, ConfigFactory confact)
{
_screp = screp;
_scfact = scfact;
_confact = confact;
// create/register a scene provider with the invocation services
sceneprov = new SceneProvider(CrowdServer.plreg.locprov, this);
invmgr.registerDispatcher(new SceneDispatcher(sceneprov), true);
}
/**
* Fetches the scene manager assosciated with the specified scene.
*
* @return the scene manager for the specified scene or null if no
* scene manager is loaded for that scene.
*/
public SceneManager getSceneManager (int sceneId)
{
return (SceneManager)_scenemgrs.get(sceneId);
}
/**
* Returns a reference to the scene repository in use by this
* registry.
*/
public SceneRepository getSceneRepository ()
{
return _screp;
}
/**
* Returns {@link SceneManager#where} for the specified scene or
* <code>null:sceneId</code> if no scene manager exists for that
* scene.
*/
public String where (int sceneId)
{
SceneManager scmgr = getSceneManager(sceneId);
return (scmgr == null) ? ("null:" + sceneId) : scmgr.where();
}
/**
* Because scenes must be loaded from the scene repository and this
* must not be done on the dobjmgr thread, the interface for resolving
* scenes requires that the entity that wishes for a scene to be
* resolved implement this callback interface so that it can be
* notified when a scene has been loaded and initialized.
*/
public static interface ResolutionListener
{
/**
* Called when the scene has been successfully resolved. The scene
* manager instance provided can be used to obtain a reference to
* the scene, or the scene distributed object.
*/
public void sceneWasResolved (SceneManager scmgr);
/**
* Called if some failure occurred in the scene resolution
* process.
*/
public void sceneFailedToResolve (int sceneId, Exception reason);
}
/**
* Requests that the specified scene be resolved, which means loaded
* into the server and initialized if the scene is not currently
* active. The supplied callback instance will be notified, on the
* dobjmgr thread, when the scene has been resolved. If the scene is
* already active, it will be notified immediately (before the call to
* {@link #resolveScene} returns).
*
* @param sceneId the id of the scene to resolve.
* @param target a reference to a callback instance that will be
* notified when the scene has been resolved (which may be immediately
* if the scene is already active).
*/
public void resolveScene (int sceneId, ResolutionListener target)
{
SceneManager mgr = (SceneManager)_scenemgrs.get(sceneId);
if (mgr != null) {
// if the scene is already resolved, we're ready to roll
target.sceneWasResolved(mgr);
return;
}
if (Log.debug()) {
Log.debug("Resolving scene [id=" + sceneId + "].");
}
// otherwise we've got to resolve the scene and call them back
// later; we can manipulate the penders table with impunity here
// because we only do so on the dobjmgr thread
ArrayList penders = (ArrayList)_penders.get(sceneId);
// if we're already in the process of resolving this scene, just
// add these guys to the list to be notified when it finally is
// resolved
if (penders != null) {
penders.add(target);
} else {
// otherwise we've got to initiate the resolution process.
// first we create the penders list
_penders.put(sceneId, penders = new ArrayList());
penders.add(target);
// i don't like cluttering up method declarations with final
// keywords...
final int fsceneId = sceneId;
if (Log.debug()) {
Log.debug("Invoking scene lookup [id=" + sceneId + "].");
}
// then we queue up an execution unit that'll load the scene
// and initialize it and all that
WhirledServer.invoker.postUnit(new Invoker.Unit() {
// this is run on the invoker thread
public boolean invoke ()
{
try {
_model = _screp.loadSceneModel(fsceneId);
_updates = _screp.loadUpdates(fsceneId);
} catch (Exception e) {
_cause = e;
}
return true;
}
// this is run on the dobjmgr thread
public void handleResult ()
{
if (_model != null) {
processSuccessfulResolution(_model, _updates);
} else if (_cause != null) {
processFailedResolution(fsceneId, _cause);
} else {
Log.warning("Scene loading unit finished with " +
"neither a scene nor a reason for " +
"failure!?");
}
}
public String toString ()
{
return "SceneRegistry.SceneLoader " +
(_model == null ? "" : _model.name) +
"(" + fsceneId + ")";
}
protected SceneModel _model;
protected UpdateList _updates;
protected Exception _cause;
});
}
}
/**
* Called when the scene resolution has completed successfully.
*/
protected void processSuccessfulResolution (
SceneModel model, UpdateList updates)
{
// now that the scene is loaded, we can create a scene manager for
// it. that will be initialized by the place registry and when
// that is finally complete, then we can let our penders know
// what's up
try {
// first create our scene instance
Scene scene = _scfact.createScene(
model, _confact.createPlaceConfig(model));
// now create our scene manager
SceneManager scmgr = (SceneManager)
CrowdServer.plreg.createPlace(scene.getPlaceConfig(), null);
scmgr.setSceneData(scene, updates, this);
// when the scene manager completes its startup procedings, it
// will call back to the scene registry and let us know that
// we can turn the penders loose
} catch (Exception e) {
// so close, but no cigar
processFailedResolution(model.sceneId, e);
}
}
/**
* Called if resolving the scene fails for some reason.
*/
protected void processFailedResolution (int sceneId, Exception cause)
{
Log.info("Failed to resolve scene [sceneId=" + sceneId +
", cause=" + cause + "].");
Log.logStackTrace(cause);
// alas things didn't work out, notify our penders
ArrayList penders = (ArrayList)_penders.remove(sceneId);
if (penders != null) {
for (int i = 0; i < penders.size(); i++) {
ResolutionListener rl = (ResolutionListener)penders.get(i);
try {
rl.sceneFailedToResolve(sceneId, cause);
} catch (Exception e) {
Log.warning("Resolution listener choked.");
Log.logStackTrace(e);
}
}
}
}
/**
* Called by the scene manager once it has started up (meaning that it
* has its place object and is ready to roll).
*/
protected void sceneManagerDidStart (SceneManager scmgr)
{
// register this scene manager in our table
int sceneId = scmgr.getScene().getId();
_scenemgrs.put(sceneId, scmgr);
if (Log.debug()) {
Log.debug("Registering scene manager [scid=" + sceneId +
", scmgr=" + scmgr + "].");
}
// now notify any penders
ArrayList penders = (ArrayList)_penders.remove(sceneId);
if (penders != null) {
for (int i = 0; i < penders.size(); i++) {
ResolutionListener rl = (ResolutionListener)penders.get(i);
try {
rl.sceneWasResolved(scmgr);
} catch (Exception e) {
Log.warning("Resolution listener choked.");
Log.logStackTrace(e);
}
}
}
}
/**
* Called by the scene manager when it is shut down.
*/
protected void unmapSceneManager (SceneManager scmgr)
{
if (_scenemgrs.remove(scmgr.getScene().getId()) == null) {
Log.warning("Requested to unmap unmapped scene manager " +
"[scmgr=" + scmgr + "].");
return;
}
if (Log.debug()) {
Log.debug("Unmapped scene manager " + scmgr + ".");
}
}
/** The entity from which we load scene models. */
protected SceneRepository _screp;
/** Used to generate place configs for our scenes. */
protected ConfigFactory _confact;
/** The entity via which we create scene instances from scene
* models. */
protected SceneFactory _scfact;
/** A mapping from scene ids to scene managers. */
protected HashIntMap _scenemgrs = new HashIntMap();
/** The table of pending resolution listeners. */
protected HashIntMap _penders = new HashIntMap();
}
@@ -1,47 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationSender;
import com.threerings.whirled.client.SceneDecoder;
import com.threerings.whirled.client.SceneReceiver;
/**
* Used to issue notifications to a {@link SceneReceiver} instance on a
* client.
*/
public class SceneSender extends InvocationSender
{
/**
* Issues a notification that will result in a call to {@link
* SceneReceiver#forcedMove} on a client.
*/
public static void forcedMove (
ClientObject target, int arg1)
{
sendNotification(
target, SceneDecoder.RECEIVER_CODE, SceneDecoder.FORCED_MOVE,
new Object[] { Integer.valueOf(arg1) });
}
}
@@ -1,40 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.server;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.server.CrowdClient;
import com.threerings.whirled.data.ScenedBodyObject;
/**
* The client object used by client management on the Whirled server.
*/
public class WhirledClient extends CrowdClient
{
// documentation inherited from interface
protected void clearLocation (BodyObject bobj)
{
WhirledServer.screg.sceneprov.leaveOccupiedScene(
(ScenedBodyObject)bobj);
}
}
@@ -1,91 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.server;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.whirled.Log;
import com.threerings.whirled.server.persist.SceneRepository;
import com.threerings.whirled.util.SceneFactory;
/**
* The whirled server extends the {@link CrowdServer} and provides access
* to managers and the like that are needed by the Whirled serviecs.
*/
public abstract class WhirledServer extends CrowdServer
{
/** The scene registry. */
public static SceneRegistry screg;
/**
* Initializes all of the server services and prepares for operation.
*/
public void init ()
throws Exception
{
// do the base server initialization
super.init();
// configure the client to use our whirled client
clmgr.setClientClass(WhirledClient.class);
// create the scene repository
_screp = createSceneRepository();
// create our scene registry
screg = new SceneRegistry(invmgr, _screp, createSceneFactory(),
createConfigFactory());
Log.info("Whirled server initialized.");
}
/**
* Creates the scene repository that will be used by this server.
*
* @exception Exception thrown if any error occurs while instantiating
* or initializing the scene repository.
*/
protected abstract SceneRepository createSceneRepository ()
throws Exception;
/**
* Creates the scene factory that will be used by our scene registry.
*
* @exception Exception thrown if any error occurs while instantiating
* or initializing the scene repository.
*/
protected abstract SceneFactory createSceneFactory ()
throws Exception;
/**
* Creates the place config factory that will be used our scene
* registry.
*
* @exception Exception thrown if any error occurs while instantiating
* or initializing the scene repository.
*/
protected abstract SceneRegistry.ConfigFactory createConfigFactory ()
throws Exception;
/** The scene repository in use by this server. */
protected SceneRepository _screp;
}
@@ -1,60 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.server.persist;
import com.samskivert.io.PersistenceException;
import com.threerings.whirled.Log;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.util.NoSuchSceneException;
import com.threerings.whirled.util.UpdateList;
/**
* The dummy scene repository just pretends to load and store scenes, but
* in fact it just creates new blank scenes when requested to load a scene
* and does nothing when requested to save one.
*/
public class DummySceneRepository implements SceneRepository
{
// documentation inherited
public SceneModel loadSceneModel (int sceneId)
throws PersistenceException, NoSuchSceneException
{
Log.info("Creating dummy scene [id=" + sceneId + "].");
return SceneModel.blankSceneModel();
}
// documentation inherited from interface
public UpdateList loadUpdates (int sceneId)
throws PersistenceException, NoSuchSceneException
{
return new UpdateList();
}
// documentation inherited from interface
public void applyAndRecordUpdate (SceneModel model, SceneUpdate update)
throws PersistenceException
{
// nothing doing
}
}
@@ -1,73 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.server.persist;
import com.samskivert.io.PersistenceException;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.util.NoSuchSceneException;
import com.threerings.whirled.util.UpdateList;
/**
* The scene repository provides the basic interface for loading and
* updating scene data. It is used by the scene registry and though more
* scene related persistence services may be needed in a full-fledged
* application, the scene repository only encapsulates those needed by the
* scene registry and other services provided by the Whirled framework.
*/
public interface SceneRepository
{
/**
* Fetches the model for the scene with the specified scene id.
*
* @exception PersistenceException thrown if an error occurs
* attempting to load the scene data.
* @exception NoSuchSceneException thrown if no scene exists with the
* specified scene id.
*/
public SceneModel loadSceneModel (int sceneId)
throws PersistenceException, NoSuchSceneException;
/**
* Fetches the set of updates associated with the specified scene.
*
* @exception PersistenceException thrown if an error occurs
* attempting to load the scene updates.
* @exception NoSuchSceneException thrown if no scene exists with the
* specified scene id.
*/
public UpdateList loadUpdates (int sceneId)
throws PersistenceException, NoSuchSceneException;
/**
* Applise the supplied scene update to persistent representation of
* its associated scene, then stores the update persistently for
* future invocations of the server to load. <em>Note:</em> the scene
* update will have already been applied to the supplied scene model.
*
* @exception PersistenceException thrown if an error occurs
* attempting to apply the scene update.
*/
public void applyAndRecordUpdate (SceneModel model, SceneUpdate update)
throws PersistenceException;
}
@@ -1,56 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot;
/**
* A placeholder class that contains a reference to the log object used by
* the Whirled Spot services.
*/
public class Log
{
public static com.samskivert.util.Log log =
new com.samskivert.util.Log("whirled.spot");
/** Convenience function. */
public static void debug (String message)
{
log.debug(message);
}
/** Convenience function. */
public static void info (String message)
{
log.info(message);
}
/** Convenience function. */
public static void warning (String message)
{
log.warning(message);
}
/** Convenience function. */
public static void logStackTrace (Throwable t)
{
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
}
}
@@ -1,36 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.client;
import com.threerings.whirled.client.SceneController;
/**
* The base spot scene controller class. It is expected that users of the
* Whirled Spot services will extend this controller class when creating
* specialized controllers for their scenes. Presently there are no basic
* scene services provided by this controller, but its existence affords
* the addition of such services should they become necessary in the
* future.
*/
public abstract class SpotSceneController extends SceneController
{
}
@@ -1,492 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.client;
import com.samskivert.util.ResultListener;
import com.samskivert.util.StringUtil;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService.ConfirmListener;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.dobj.Subscriber;
import com.threerings.crowd.chat.client.ChatDirector;
import com.threerings.crowd.chat.data.ChatCodes;
import com.threerings.crowd.client.LocationAdapter;
import com.threerings.crowd.client.LocationDirector;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.whirled.client.SceneDirector;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.ScenedBodyObject;
import com.threerings.whirled.util.WhirledContext;
import com.threerings.whirled.spot.Log;
import com.threerings.whirled.spot.data.ClusteredBodyObject;
import com.threerings.whirled.spot.data.Location;
import com.threerings.whirled.spot.data.Portal;
import com.threerings.whirled.spot.data.SpotCodes;
import com.threerings.whirled.spot.data.SpotScene;
/**
* Extends the standard scene director with facilities to move between
* locations within a scene.
*/
public class SpotSceneDirector extends BasicDirector
implements SpotCodes, Subscriber, AttributeChangeListener
{
/**
* Creates a new spot scene director with the specified context and
* which will cooperate with the supplied scene director.
*
* @param ctx the active client context.
* @param locdir the location director with which we will be
* cooperating.
* @param scdir the scene director with which we will be cooperating.
*/
public SpotSceneDirector (WhirledContext ctx, LocationDirector locdir,
SceneDirector scdir)
{
super(ctx);
_ctx = ctx;
_scdir = scdir;
// wire ourselves up to hear about leave place notifications
locdir.addLocationObserver(new LocationAdapter() {
public void locationDidChange (PlaceObject place) {
// we need to clear some things out when we leave a place
handleDeparture();
}
});
}
/**
* Configures this spot scene director with a chat director, with
* which it will coordinate to implement cluster chatting.
*/
public void setChatDirector (ChatDirector chatdir)
{
_chatdir = chatdir;
}
/**
* Returns our current location unless we have a location change
* pending, in which case our pending location is returned.
*/
public Location getIntendedLocation ()
{
return (_pendingLoc != null) ? _pendingLoc : _location;
}
/**
* 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.
*
* @return true if the request was issued, false if it was rejected by
* a location observer or because we have another request outstanding.
*/
public boolean traversePortal (int portalId)
{
return traversePortal(portalId, null);
}
/**
* 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 boolean traversePortal (int portalId, ResultListener rl)
{
// look up the destination scene and location
SpotScene scene = (SpotScene)_scdir.getScene();
if (scene == null) {
Log.warning("Requested to traverse portal when we have " +
"no scene [portalId=" + portalId + "].");
return false;
}
// sanity check the server's notion of what scene we're in with
// our notion of it
int sceneId = _scdir.getScene().getId();
ScenedBodyObject sbobj = (ScenedBodyObject)
_ctx.getClient().getClientObject();
if (sceneId != sbobj.getSceneId()) {
Log.warning("Client and server differ in opinion of what scene " +
"we're in [sSceneId=" + sbobj.getSceneId() +
", cSceneId=" + sceneId + "].");
return false;
}
// find the portal they're talking about
Portal dest = scene.getPortal(portalId);
if (dest == null) {
Log.warning("Requested to traverse non-existent portal " +
"[portalId=" + portalId + ", portals=" +
StringUtil.toString(scene.getPortals()) + "].");
return false;
}
// prepare to move to this scene (sets up pending data)
if (!_scdir.prepareMoveTo(dest.targetSceneId, rl)) {
Log.info("Portal traversal vetoed by scene director " +
"[portalId=" + portalId + "].");
return false;
}
// 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;
SceneModel pendingModel = _scdir.getPendingModel();
if (pendingModel != null) {
sceneVer = pendingModel.version;
}
// issue a traversePortal request
Log.info("Issuing traversePortal(" +
sceneId + ", " + dest + ", " + sceneVer + ").");
_sservice.traversePortal(
_ctx.getClient(), sceneId, portalId, sceneVer, _scdir);
return true;
}
/**
* Issues a request to change our location within the scene to the
* specified location.
*
* @param loc the new location to which to move.
* @param listener will be notified of success or failure. Most client
* entities find out about location changes via changes to the
* occupant info data, but the initiator of a location change request
* can be notified of its success or failure, primarily so that it can
* act in anticipation of a successful location change (like by
* starting a sprite moving toward the new location), but backtrack if
* it finds out that the location change failed.
*/
public void changeLocation (Location loc, final ResultListener listener)
{
// refuse if there's a pending location change or if we're already
// at the specified location
if (loc.equivalent(_location)) {
Log.info("Not going to " + loc + "; we're at " + _location +
" and we're headed to " + _pendingLoc + ".");
if (listener != null) {
// This isn't really a failure, it's just a no-op.
listener.requestCompleted(_location);
}
return;
}
if (_pendingLoc != null) {
Log.info("Not going to " + loc + "; we're at " + _location +
" and we're headed to " + _pendingLoc + ".");
if (listener != null) {
// Already moving, best thing to do is ignore it.
listener.requestCompleted(_pendingLoc);
}
return;
}
SpotScene scene = (SpotScene)_scdir.getScene();
if (scene == null) {
Log.warning("Requested to change locations, but we're not " +
"currently in any scene [loc=" + loc + "].");
if (listener != null) {
listener.requestFailed(new Exception("m.cant_get_there"));
}
return;
}
int sceneId = _scdir.getScene().getId();
Log.info("Sending changeLocation request [scid=" + sceneId +
", loc=" + loc + "].");
_pendingLoc = (Location)loc.clone();
ConfirmListener clist = new ConfirmListener() {
public void requestProcessed () {
_location = _pendingLoc;
_pendingLoc = null;
if (listener != null) {
listener.requestCompleted(_location);
}
}
public void requestFailed (String reason) {
_pendingLoc = null;
if (listener != null) {
listener.requestFailed(new Exception(reason));
}
}
};
_sservice.changeLocation(_ctx.getClient(), sceneId, loc, clist);
}
/**
* Issues a request to join the cluster associated with the specified
* user (starting one if necessary).
*
* @param froid the bodyOid of another user; the calling user will
* be made to join the target user's cluster.
* @param listener will be notified of success or failure.
*/
public void joinCluster (int froid, final ResultListener listener)
{
SpotScene scene = (SpotScene)_scdir.getScene();
if (scene == null) {
Log.warning("Requested to join cluster, but we're not " +
"currently in any scene [froid=" + froid + "].");
if (listener != null) {
listener.requestFailed(new Exception("m.cant_get_there"));
}
return;
}
Log.info("Joining cluster [friend=" + froid + "].");
_sservice.joinCluster(_ctx.getClient(), froid, new ConfirmListener() {
public void requestProcessed () {
if (listener != null) {
listener.requestCompleted(null);
}
}
public void requestFailed (String reason) {
if (listener != null) {
listener.requestFailed(new Exception(reason));
}
}
});
}
/**
* Sends a chat message to the other users in the cluster to which the
* location that we currently occupy belongs.
*
* @return true if a cluster speak message was delivered, false if we
* are not in a valid cluster and refused to deliver the request.
*/
public boolean requestClusterSpeak (String message)
{
return requestClusterSpeak(message, ChatCodes.DEFAULT_MODE);
}
/**
* Sends a chat message to the other users in the cluster to which the
* location that we currently occupy belongs.
*
* @return true if a cluster speak message was delivered, false if we
* are not in a valid cluster and refused to deliver the request.
*/
public boolean requestClusterSpeak (String message, byte mode)
{
// make sure we're currently in a scene
SpotScene scene = (SpotScene)_scdir.getScene();
if (scene == null) {
Log.warning("Requested to speak to cluster, but we're not " +
"currently in any scene [message=" + message + "].");
return false;
}
// make sure we're part of a cluster
if (_self.getClusterOid() <= 0) {
Log.info("Ignoring cluster speak as we're not in a cluster " +
"[cloid=" + _self.getClusterOid() + "].");
return false;
}
message = _chatdir.filter(message, null, true);
if (message != null) {
_sservice.clusterSpeak(_ctx.getClient(), message, mode);
}
return true;
}
// documentation inherited from interface
public void objectAvailable (DObject object)
{
clearCluster(false);
int oid = object.getOid();
if (oid != _self.getClusterOid()) {
// we got it too late, just unsubscribe
DObjectManager omgr = _ctx.getDObjectManager();
omgr.unsubscribeFromObject(oid, this);
} else {
// it's our new cluster!
_clobj = object;
if (_chatdir != null) {
_chatdir.addAuxiliarySource(object, CLUSTER_CHAT_TYPE);
}
}
}
// documentation inherited from interface
public void requestFailed (int oid, ObjectAccessException cause)
{
Log.warning("Unable to subscribe to cluster chat object " +
"[oid=" + oid + ", cause=" + cause + "].");
}
// documentation inherited from interface
public void attributeChanged (AttributeChangedEvent event)
{
if (event.getName().equals(_self.getClusterField()) &&
!event.getValue().equals(event.getOldValue())) {
maybeUpdateCluster();
}
}
// documentation inherited
public void clientDidLogon (Client client)
{
super.clientDidLogon(client);
ClientObject clientObj = client.getClientObject();
if (clientObj instanceof ClusteredBodyObject) {
// listen to the client object
clientObj.addListener(this);
_self = (ClusteredBodyObject) clientObj;
// we may need to subscribe to a cluster due to session resumption
maybeUpdateCluster();
}
}
// documentation inherited
public void clientObjectDidChange (Client client)
{
super.clientObjectDidChange(client);
// listen to the client object
ClientObject clientObj = client.getClientObject();
clientObj.addListener(this);
_self = (ClusteredBodyObject) clientObj;
}
// documentation inherited
public void clientDidLogoff (Client client)
{
super.clientDidLogoff(client);
// clear out our business
_location = null;
_pendingLoc = null;
_sservice = null;
clearCluster(true);
// stop listening to the client object
client.getClientObject().removeListener(this);
_self = null;
}
// documentation inherited
protected void fetchServices (Client client)
{
_sservice = (SpotService)client.requireService(SpotService.class);
}
/**
* Clean up after a few things when we depart from a scene.
*/
protected void handleDeparture ()
{
// clear out our last known location id
_location = null;
}
/**
* Checks to see if our cluster has changed and does the necessary
* subscription machinations if necessary.
*/
protected void maybeUpdateCluster ()
{
int cloid = _self.getClusterOid();
if ((_clobj == null && cloid <= 0) ||
(_clobj != null && cloid == _clobj.getOid())) {
// our cluster didn't change, we can stop now
return;
}
// clear out any old cluster object
clearCluster(false);
// if there's a new cluster object, subscribe to it
if (_chatdir != null && cloid > 0) {
DObjectManager omgr = _ctx.getDObjectManager();
// we'll wire up to the chat director when this completes
omgr.subscribeToObject(cloid, this);
}
}
/**
* Convenience routine to unwire chat for and unsubscribe from our
* current cluster, if any.
*
* @param force clear the cluster even if we're still apparently in it.
*/
protected void clearCluster (boolean force)
{
if (_clobj != null &&
(force || (_clobj.getOid() != _self.getClusterOid()))) {
if (_chatdir != null) {
_chatdir.removeAuxiliarySource(_clobj);
}
DObjectManager omgr = _ctx.getDObjectManager();
omgr.unsubscribeFromObject(_clobj.getOid(), this);
_clobj = null;
}
}
/** The active client context. */
protected WhirledContext _ctx;
/** Access to spot scene services. */
protected SpotService _sservice;
/** The scene director with which we are cooperating. */
protected SceneDirector _scdir;
/** A casted reference to our clustered body object. */
protected ClusteredBodyObject _self;
/** A reference to the chat director with which we coordinate. */
protected ChatDirector _chatdir;
/** The location we currently occupy. */
protected Location _location;
/** The location to which we have an outstanding change location
* request. */
protected Location _pendingLoc;
/** The cluster chat object for the cluster we currently occupy. */
protected DObject _clobj;
}
@@ -1,83 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.whirled.client.SceneService.SceneMoveListener;
import com.threerings.whirled.spot.data.Location;
/**
* Defines the mechanism by which the client can request to move around
* 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 interface SpotService extends InvocationService
{
/**
* Requests to traverse the specified portal.
*
* @param sceneId the player's current scene which is used to sanity
* check things when the request actually arrives.
* @param portalId the portal to be traversed.
* @param destSceneVer the version of the destination scene data that
* the client has in its local repository.
*/
public void traversePortal (
Client client, int sceneId, int portalId, int destSceneVer,
SceneMoveListener listener);
/**
* Requests that this client's body be made to move to the specified
* location. The user will be removed from any cluster from which they
* are an occupant.
*
* @param sceneId the id of the scene in which to change location.
* @param loc the location to which to move.
*/
public void changeLocation (Client client, int sceneId, Location loc,
ConfirmListener listener);
/**
* Requests that this client start or join the specified cluster. They
* will be relocated appropriately by the scene manager.
*
* @param friendOid the bodyOid of another user or the oid of an
* existing cluster; the calling user will be made to join the cluster
* or target user's cluster, or create a cluster with the target user
* if they are not already in one.
*/
public void joinCluster (Client client, int friendOid,
ConfirmListener listener);
/**
* Requests that the supplied message be delivered to listeners in the
* cluster to which the specified location belongs.
*
* @param message the text of the message to be spoken.
* @param mode an associated mode constant that can be used to
* identify different kinds of "speech" (emote, thought bubble, etc.).
*/
public void clusterSpeak (Client client, String message, byte mode);
}
@@ -1,77 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import java.awt.Rectangle;
import com.samskivert.util.StringUtil;
import com.threerings.io.Streamable;
import com.threerings.presents.dobj.DSet;
/**
* Contains information on clusters.
*/
public class Cluster extends Rectangle
implements DSet.Entry, Streamable
{
/** A unique identifier for this cluster (also the distributed object
* id of the cluster chat object). */
public int clusterOid;
// documentation inherited
public Comparable getKey ()
{
if (_key == null) {
_key = Integer.valueOf(clusterOid);
}
return _key;
}
// documentation inherited
public boolean equals (Object other)
{
if (other instanceof Cluster) {
return ((Cluster)other).clusterOid == clusterOid;
} else {
return false;
}
}
// documentation inherited
public int hashCode ()
{
return clusterOid;
}
/**
* Generates a string representation of this instance.
*/
public String toString ()
{
return StringUtil.fieldsToString(this);
}
/** Used for {@link #getKey}. */
protected transient Integer _key;
}
@@ -1,74 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.OidList;
import com.threerings.crowd.chat.data.SpeakObject;
/**
* Used to dispatch chat in clusters.
*/
public class ClusterObject extends DObject
implements SpeakObject
{
// AUTO-GENERATED: FIELDS START
/** The field name of the <code>occupants</code> field. */
public static final String OCCUPANTS = "occupants";
// AUTO-GENERATED: FIELDS END
/**
* Tracks the oid of the body objects that occupy this cluster.
*/
public OidList occupants = new OidList();
// documentation inherited
public void applyToListeners (ListenerOp op)
{
for (int ii = 0, ll = occupants.size(); ii < ll; ii++) {
op.apply(occupants.get(ii));
}
}
// AUTO-GENERATED: METHODS START
/**
* Requests that <code>oid</code> be added to the <code>occupants</code>
* oid list. The list will not change until the event is actually
* propagated through the system.
*/
public void addToOccupants (int oid)
{
requestOidAdd(OCCUPANTS, oid);
}
/**
* Requests that <code>oid</code> be removed from the
* <code>occupants</code> oid list. The list will not change until the
* event is actually propagated through the system.
*/
public void removeFromOccupants (int oid)
{
requestOidRemove(OCCUPANTS, oid);
}
// AUTO-GENERATED: METHODS END
}
@@ -1,47 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import com.threerings.whirled.data.ScenedBodyObject;
/**
* Defines some required methods for a {@link ScenedBodyObject} that is to
* participate in the Whirled Spot system.
*/
public interface ClusteredBodyObject extends ScenedBodyObject
{
/**
* Returns the field name of the cluster oid distributed object field.
*/
public String getClusterField ();
/**
* Returns the oid of the cluster to which this user currently
* belongs.
*/
public int getClusterOid ();
/**
* Sets the oid of the cluster to which this user currently belongs.
*/
public void setClusterOid (int clusterOid);
}
@@ -1,34 +0,0 @@
package com.threerings.whirled.spot.data;
import com.threerings.io.Streamable;
public interface Location extends Streamable, Cloneable
{
/**
* Get a new Location instance that is equals() to this one but that
* has an orientation facing the opposite direction.
*/
public Location getOpposite ();
/**
* Two locations are equivalent if they specify the same location
* and orientation.
*/
public boolean equivalent (Location other);
/**
* Two locations are equals if they specify the same coordinates, but
* the orientation may be different.
*/
public boolean equals (Object other);
/**
* The hashcode of a Location should be based only on its coordinates.
*/
public int hashCode ();
/**
* Locations are cloneable.
*/
public Object clone ();
}
@@ -1,112 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import com.threerings.io.SimpleStreamableObject;
/**
* Represents an exit to another scene. A body sprite would walk over to a
* portal's coordinates and then either proceed off of the edge of the
* display, or open a door and walk through it, or fizzle away in a Star
* Trekkian transporter style or whatever is appropriate for the game in
* question. It contains information on the scene to which the body exits
* when using this portal and the location at which the body sprite should
* appear in that target scene.
*/
public class Portal extends SimpleStreamableObject
implements Cloneable
{
/** This portal's unique identifier. */
public short portalId;
/** The location of the portal.
* This field is present on client and server, it is streamed specially. */
public Location loc;
/** The scene identifier of the scene to which a body will exit when
* they "use" this portal. */
public int targetSceneId;
/** The portal identifier of the portal at which a body will enter
* the target scene when they "use" this portal. */
public short targetPortalId;
/**
* Returns a location instance configured with the location and
* orientation of this portal.
*/
public Location getLocation ()
{
return (Location) loc.clone();
}
/**
* Returns a location instance configured with the location and
* opposite orientation of this portal. This is useful for when a body
* is entering a scene at a portal and we want them to face the
* opposite direction (as they are entering via the portal rather than
* leaving, which is the natural "orientation" of a portal).
*/
public Location getOppLocation ()
{
return loc.getOpposite();
}
/**
* Returns true if the portal has a potentially valid target scene and
* portal id (they are not guaranteed to exist, but they are at least
* potentially valid values rather than -1 or 0).
*/
public boolean isValid ()
{
return (targetSceneId > 0) && (targetPortalId > 0);
}
/**
* Creates a clone of this instance.
*/
public Object clone ()
{
try {
return (Portal)super.clone();
} catch (CloneNotSupportedException cnse) {
throw new RuntimeException("Portal.clone() failed " + cnse);
}
}
/**
* Portal equality is determined by portal id.
*/
public boolean equals (Object other)
{
return (other instanceof Portal) &&
((Portal) other).portalId == portalId;
}
/**
* Computes a reasonable hashcode for portal instances.
*/
public int hashCode ()
{
return portalId;
}
}
@@ -1,90 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import com.threerings.io.SimpleStreamableObject;
import com.threerings.presents.dobj.DSet;
/**
* Extends {@link Location} with the data and functionality needed to
* represent a particular user's location in a scene.
*/
public class SceneLocation extends SimpleStreamableObject
implements DSet.Entry
{
/** The oid of the body that occupies this location. */
public int bodyOid;
/** The actual location, which is interpreted by the display system. */
public Location loc;
/**
* Creates a scene location with the specified information.
*/
public SceneLocation (Location loc, int bodyOid)
{
this.loc = loc;
this.bodyOid = bodyOid;
}
/**
* Creates a blank instance suitable for unserialization.
*/
public SceneLocation ()
{
}
// documentation inherited
public Comparable getKey ()
{
if (_key == null) {
_key = Integer.valueOf(bodyOid);
}
return _key;
}
// documentation inherited
public boolean equals (Object other)
{
// TEMP
if (other instanceof Location) {
com.threerings.whirled.spot.Log.warning(
"Illegal compare of SceneLocation and Location!!!");
Thread.dumpStack(); // this will help us find logic errors,
// as a SceneLocation and a Location shouldn't be compared
}
// END: temp
return (other instanceof SceneLocation) &&
this.loc.equals(((SceneLocation) other).loc);
}
// documentation inherited
public int hashCode ()
{
return loc.hashCode();
}
/** Used for {@link #getKey}. */
protected transient Integer _key;
}
@@ -1,57 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import com.threerings.crowd.chat.data.ChatCodes;
import com.threerings.whirled.data.SceneCodes;
/**
* Contains codes used by the Spot invocation services.
*/
public interface SpotCodes extends ChatCodes, SceneCodes
{
/** 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";
/** 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";
/** An error code indicating that a location is not valid. Usually
* generated by a failed changeLoc request. */
public static final String INVALID_LOCATION = "m.invalid_location";
/** An error code indicating that a cluster is not valid. Usually
* generated by a failed joinCluster request. */
public static final String NO_SUCH_CLUSTER = "m.no_such_cluster";
/** An error code indicating that a cluster is full. Usually generated
* by a failed joinCluster request. */
public static final String CLUSTER_FULL = "m.cluster_full";
/** The chat type code with which we register our cluster auxiliary
* chat objects. Chat display implementations should interpret chat
* messages with this type accordingly. */
public static final String CLUSTER_CHAT_TYPE = "clusterChat";
}
@@ -1,93 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
import com.threerings.whirled.client.SceneService;
import com.threerings.whirled.data.SceneMarshaller;
import com.threerings.whirled.spot.client.SpotService;
import com.threerings.whirled.spot.data.Location;
/**
* Provides the implementation of the {@link SpotService} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
public class SpotMarshaller extends InvocationMarshaller
implements SpotService
{
/** The method id used to dispatch {@link #changeLocation} requests. */
public static final int CHANGE_LOCATION = 1;
// documentation inherited from interface
public void changeLocation (Client arg1, int arg2, Location arg3, InvocationService.ConfirmListener arg4)
{
InvocationMarshaller.ConfirmMarshaller listener4 = new InvocationMarshaller.ConfirmMarshaller();
listener4.listener = arg4;
sendRequest(arg1, CHANGE_LOCATION, new Object[] {
Integer.valueOf(arg2), arg3, listener4
});
}
/** The method id used to dispatch {@link #clusterSpeak} requests. */
public static final int CLUSTER_SPEAK = 2;
// documentation inherited from interface
public void clusterSpeak (Client arg1, String arg2, byte arg3)
{
sendRequest(arg1, CLUSTER_SPEAK, new Object[] {
arg2, Byte.valueOf(arg3)
});
}
/** The method id used to dispatch {@link #joinCluster} requests. */
public static final int JOIN_CLUSTER = 3;
// documentation inherited from interface
public void joinCluster (Client arg1, int arg2, InvocationService.ConfirmListener arg3)
{
InvocationMarshaller.ConfirmMarshaller listener3 = new InvocationMarshaller.ConfirmMarshaller();
listener3.listener = arg3;
sendRequest(arg1, JOIN_CLUSTER, new Object[] {
Integer.valueOf(arg2), listener3
});
}
/** The method id used to dispatch {@link #traversePortal} requests. */
public static final int TRAVERSE_PORTAL = 4;
// documentation inherited from interface
public void traversePortal (Client arg1, int arg2, int arg3, int arg4, SceneService.SceneMoveListener arg5)
{
SceneMarshaller.SceneMoveMarshaller listener5 = new SceneMarshaller.SceneMoveMarshaller();
listener5.listener = arg5;
sendRequest(arg1, TRAVERSE_PORTAL, new Object[] {
Integer.valueOf(arg2), Integer.valueOf(arg3), Integer.valueOf(arg4), listener5
});
}
}
@@ -1,80 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import java.util.Iterator;
/**
* Makes available the spot scene information that the server needs to do
* its business.
*/
public interface SpotScene
{
/**
* Returns a {@link Portal} object for the portal with the specified
* id or null if no portal exists with that id.
*/
public Portal getPortal (int portalId);
/**
* Returns the number of portals in this scene.
*/
public int getPortalCount ();
/**
* Returns an iterator over the portals in this scene.
*/
public Iterator getPortals ();
/**
* Returns the portal id that should be assigned to the next portal
* added to this scene.
*/
public short getNextPortalId ();
/**
* Returns the portal that represents the default entrance to this
* scene. If a body enters the scene at logon time rather than
* entering from some other scene, this is the portal at which they
* would appear.
*/
public Portal getDefaultEntrance ();
/**
* Adds a portal to this scene, immediately making the requisite
* modifications to the underlying scene model. The portal id should
* have already been assigned using the value obtained from {@link
* #getNextPortalId}.
*/
public void addPortal (Portal portal);
/**
* Removes the specified portal from the scene.
*/
public void removePortal (Portal portal);
/**
* Sets the default entrance in this scene, immediately making the
* requisite modifications to the underlying scene model.
*/
public void setDefaultEntrance (Portal portal);
}
@@ -1,157 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import java.util.Iterator;
import com.samskivert.util.HashIntMap;
import com.threerings.whirled.spot.Log;
/**
* An implementation of the {@link SpotScene} interface.
*/
public class SpotSceneImpl
implements SpotScene
{
/**
* Creates an instance that will obtain data from the supplied spot
* scene model.
*/
public SpotSceneImpl (SpotSceneModel smodel)
{
_smodel = smodel;
readPortals();
}
protected void readPortals ()
{
_portals.clear();
for (int ii = 0, ll = _smodel.portals.length; ii < ll; ii++) {
Portal port = _smodel.portals[ii];
_portals.put(port.portalId, port);
}
}
/**
* Instantiates a blank scene implementation.
*/
public SpotSceneImpl ()
{
_smodel = new SpotSceneModel();
}
// documentation inherited from interface
public Portal getPortal (int portalId)
{
return (Portal)_portals.get(portalId);
}
// documentation inherited from interface
public int getPortalCount ()
{
return _portals.size();
}
// documentation inherited from interface
public Iterator getPortals ()
{
return _portals.values().iterator();
}
// documentation inherited from interface
public short getNextPortalId ()
{
// compute a new portal id for our friend the portal
for (short ii = 1; ii < MAX_PORTAL_ID; ii++) {
if (!_portals.containsKey(ii)) {
return ii;
}
}
return (short)-1;
}
// documentation inherited from interface
public Portal getDefaultEntrance ()
{
return getPortal(_smodel.defaultEntranceId);
}
// documentation inherited from interface
public void addPortal (Portal portal)
{
if (portal.portalId <= 0) {
Log.warning("Refusing to add zero-id portal " +
"[scene=" + this + ", portal=" + portal + "].");
return;
}
// add it to our model
_smodel.addPortal(portal);
// and slap it into our table
_portals.put(portal.portalId, portal);
}
// documentation inherited from interface
public void removePortal (Portal portal)
{
// remove the portal from our mapping
_portals.remove(portal.portalId);
// remove it from the model
_smodel.removePortal(portal);
}
/**
* Used when we're being parsed from an XML scene model.
*/
public void setDefaultEntranceId (int defaultEntranceId)
{
_smodel.defaultEntranceId = defaultEntranceId;
}
// documentation inherited from interface
public void setDefaultEntrance (Portal portal)
{
_smodel.defaultEntranceId = (portal == null) ? -1 : portal.portalId;
}
/**
* This should be called if a scene update was received that caused
* our underlying scene model to change.
*/
public void updateReceived ()
{
readPortals();
}
/** A casted reference to our scene model. */
protected SpotSceneModel _smodel;
/** A mapping from portal id to portal. */
protected HashIntMap _portals = new HashIntMap();
/** We don't allow more than ~32k portals in a scene. Things would
* slow down *way* before we got there. */
protected static final int MAX_PORTAL_ID = Short.MAX_VALUE;
}
@@ -1,96 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import com.samskivert.util.ArrayUtil;
import com.samskivert.util.ListUtil;
import com.threerings.io.SimpleStreamableObject;
import com.threerings.whirled.data.AuxModel;
import com.threerings.whirled.data.SceneModel;
/**
* The spot scene model extends the standard scene model with information
* on portals. Portals are referenced by an identifier, unique within the
* scene and unchanging, so that portals can stably reference the target
* portal in the scene to which they connect.
*/
public class SpotSceneModel extends SimpleStreamableObject
implements AuxModel
{
/** An array containing all portals in this scene. */
public Portal[] portals = new Portal[0];
/** The portal id of the default entrance to this scene. If a body
* enters the scene without coming from another scene, this is the
* portal at which they would appear. */
public int defaultEntranceId = -1;
/**
* Adds a portal to this scene model.
*/
public void addPortal (Portal portal)
{
portals = (Portal[])ArrayUtil.append(portals, portal);
}
/**
* Removes a portal from this model.
*/
public void removePortal (Portal portal)
{
int pidx = ListUtil.indexOf(portals, portal);
if (pidx != -1) {
portals = (Portal[])ArrayUtil.splice(portals, pidx, 1);
}
}
// documentation inherited
public Object clone ()
throws CloneNotSupportedException
{
SpotSceneModel model = (SpotSceneModel)super.clone();
// clone our portals individually
model.portals = new Portal[portals.length];
for (int ii = 0, ll = portals.length; ii < ll; ii++) {
model.portals[ii] = (Portal)portals[ii].clone();
}
return model;
}
/**
* Locates and returns the {@link SpotSceneModel} among the auxiliary
* scene models associated with the supplied scene
* model. <code>null</code> is returned if no spot scene model could
* be found.
*/
public static SpotSceneModel getSceneModel (SceneModel model)
{
for (int ii = 0; ii < model.auxModels.length; ii++) {
if (model.auxModels[ii] instanceof SpotSceneModel) {
return (SpotSceneModel)model.auxModels[ii];
}
}
return null;
}
}
@@ -1,140 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.data;
import com.threerings.presents.dobj.DSet;
import com.threerings.whirled.data.SceneObject;
/**
* Extends the {@link SceneObject} with information specific to spots.
*/
public class SpotSceneObject extends SceneObject
{
// AUTO-GENERATED: FIELDS START
/** The field name of the <code>occupantLocs</code> field. */
public static final String OCCUPANT_LOCS = "occupantLocs";
/** The field name of the <code>clusters</code> field. */
public static final String CLUSTERS = "clusters";
// AUTO-GENERATED: FIELDS END
/** A distributed set containing {@link SceneLocation} records for all
* occupants of this scene. */
public DSet occupantLocs = new DSet();
/** Contains information on all {@link Cluster}s in this scene. */
public DSet clusters = new DSet();
// AUTO-GENERATED: METHODS START
/**
* Requests that the specified entry be added to the
* <code>occupantLocs</code> set. The set will not change until the event is
* actually propagated through the system.
*/
public void addToOccupantLocs (DSet.Entry elem)
{
requestEntryAdd(OCCUPANT_LOCS, occupantLocs, elem);
}
/**
* Requests that the entry matching the supplied key be removed from
* the <code>occupantLocs</code> set. The set will not change until the
* event is actually propagated through the system.
*/
public void removeFromOccupantLocs (Comparable key)
{
requestEntryRemove(OCCUPANT_LOCS, occupantLocs, key);
}
/**
* Requests that the specified entry be updated in the
* <code>occupantLocs</code> set. The set will not change until the event is
* actually propagated through the system.
*/
public void updateOccupantLocs (DSet.Entry elem)
{
requestEntryUpdate(OCCUPANT_LOCS, occupantLocs, elem);
}
/**
* Requests that the <code>occupantLocs</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 setOccupantLocs (DSet value)
{
requestAttributeChange(OCCUPANT_LOCS, value, this.occupantLocs);
this.occupantLocs = (value == null) ? null : value.typedClone();
}
/**
* 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, 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 (Comparable key)
{
requestEntryRemove(CLUSTERS, 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, 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 value)
{
requestAttributeChange(CLUSTERS, value, this.clusters);
this.clusters = (value == null) ? null : value.typedClone();
}
// AUTO-GENERATED: METHODS END
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

@@ -1,108 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<!-- $Id: package.html,v 1.1 2001/11/13 00:12:20 mdb Exp $ -->
</head>
<body bgcolor="white">
Extends the Whirled services with the notion of locations, clusters,
coordinates and orientation.
<p> The basic Whirled services facilitate a game that provides a
connected set of scenes and the ability for the bodies to move from
a scene to its neighboring scenes. The Spot package extends those
services by providing the notion of locations within scenes which a
body can occupy and which, while occupied, cannot be occupied by
other bodies.
</p>
<p> The philosophy is that a game that uses these services wants to
create a constrained universe where a body cannot simply stand
anywhere on the screen, but can only stand in a set of predefined
locations. This is a simplified mechanism for providing finer
grained locality management, such that expensive distance monitoring
algorithms need not be used to determine who can interact (speak)
with others in a particular scene. For many games, the overhead of
tracking each users' position in near-realtime coupled with a
complex message broadcasting system that dispatches messages to
users in geometric proximity to one another is serious overkill and
does not add sufficient value to the game to merit inclusion.
</p>
<p><b>Locations and clusters</b><br>
The Spot package provides a functional approximation in the form of
clusters of locations, with predefined coordinates, that are
sufficiently proximal that bodies in that cluster of locations can
all speak to one another. The locations for a particular scene are
specified in advance which serves the additional purpose of putting
a limit on the number of bodies that can be standing (and
participating) in a particular scene. This puts a natural upper
bound on the amount of network traffic that will be generated by the
need to broadcast scene updates to all occupants of a scene.
</p>
<p> Locations also serve as entry and exit points for a scene,
providing a means both for the graphical user interface to identify
the location at which bodies enter and exit a scene as well as
providing a spatial context for the connections between scenes
(i.e. the exit to the scene to the north can be located in the
northward part of a scene display).
</p>
<p><b>Orientation</b><br>
Locations define a notion of orientation which is assigned a priori,
along with the coordinates of the location and which can be
interpreted by the graphical display as the direction that body
sprites should face when standing on that location. Clusters of
locations can be constructed such that all of the bodies standing in
that cluster appear to be facing one another which provides visible
reinforcement that members of the cluster can "hear" the
conversation, while members of other clusters cannot. The
orientation of entry and exit locations are also useful in
establishing an initial orientation for a body sprite that enters a
scene based on the visualization of the entrance mechanism (i.e. if
they appear to enter through the door, the body sprite should be
oriented as if it just walked in the proper direction through the
door).
</p>
<p> Because the Spot services redefine the scope at which speak
messages are normally distributed (the Crowd services would, by
default, have speak messages dispatched to everyone in the entire
scene because the scene is a place and the default chat mechanisms
dispatch speak messages to all occupants of a place), it defines an
additional communication mode known as shouting, which conveys the
message to everyone in the entire scene.
</p>
<p><b>Portals</b><br>
Portals are a combination of locations and the standard Whirled
notion of neighboring scenes. They contain the coordinates to which
a body sprite would traverse prior to exiting to a neighboring scene
(generally so that they appear to walk to whatever visual
representation of the portal is provided by the scene, like a door
or perhaps simply the edge of the scene). Conversely, they represent
the coordinates at which a body arriving from same neighboring scene
would appear in the displayed scene.
</p>
<p><b>Example display</b><br>
This is an example of how these concepts might look in a game
providing top-down 2D display of its scenes. <br>
</p>
<center>
<img align="center" src="images/scene.png" alt="Example scene image">
</center>
<p> The arrows would, of course, not be rendered but are shown to
indicate the presence of locations (and portals) and their
associated orientation information.
</p>
</body>
</html>
@@ -1,34 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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,95 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.server;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.server.InvocationDispatcher;
import com.threerings.presents.server.InvocationException;
import com.threerings.whirled.client.SceneService;
import com.threerings.whirled.data.SceneMarshaller;
import com.threerings.whirled.spot.client.SpotService;
import com.threerings.whirled.spot.data.Location;
import com.threerings.whirled.spot.data.SpotMarshaller;
/**
* Dispatches requests to the {@link SpotProvider}.
*/
public class SpotDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public SpotDispatcher (SpotProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new SpotMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case SpotMarshaller.CHANGE_LOCATION:
((SpotProvider)provider).changeLocation(
source,
((Integer)args[0]).intValue(), (Location)args[1], (InvocationService.ConfirmListener)args[2]
);
return;
case SpotMarshaller.CLUSTER_SPEAK:
((SpotProvider)provider).clusterSpeak(
source,
(String)args[0], ((Byte)args[1]).byteValue()
);
return;
case SpotMarshaller.JOIN_CLUSTER:
((SpotProvider)provider).joinCluster(
source,
((Integer)args[0]).intValue(), (InvocationService.ConfirmListener)args[1]
);
return;
case SpotMarshaller.TRAVERSE_PORTAL:
((SpotProvider)provider).traversePortal(
source,
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), ((Integer)args[2]).intValue(), (SceneService.SceneMoveListener)args[3]
);
return;
default:
super.dispatchRequest(source, methodId, args);
return;
}
}
}
@@ -1,324 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.server;
import com.samskivert.util.StringUtil;
import com.threerings.util.MessageManager;
import com.threerings.util.Name;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.RootDObjectManager;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.crowd.chat.data.ChatCodes;
import com.threerings.crowd.chat.server.SpeakProvider;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.server.PlaceRegistry;
import com.threerings.whirled.client.SceneService.SceneMoveListener;
import com.threerings.whirled.data.ScenedBodyObject;
import com.threerings.whirled.server.SceneManager;
import com.threerings.whirled.server.SceneRegistry;
import com.threerings.whirled.spot.Log;
import com.threerings.whirled.spot.client.SpotService;
import com.threerings.whirled.spot.data.Location;
import com.threerings.whirled.spot.data.Portal;
import com.threerings.whirled.spot.data.SpotCodes;
import com.threerings.whirled.spot.data.SpotScene;
/**
* Provides the server-side implementation of the spot services.
*/
public class SpotProvider
implements SpotCodes, InvocationProvider
{
/**
* Creates a spot provider that can be registered with the invocation
* manager to handle spot services.
*/
public SpotProvider (RootDObjectManager omgr, PlaceRegistry plreg,
SceneRegistry screg)
{
// we'll need these later
_plreg = plreg;
_screg = screg;
_omgr = omgr;
}
/**
* Processes a {@link SpotService#traversePortal} request.
*/
public void traversePortal (ClientObject caller, int sceneId, int portalId,
int destSceneVer, SceneMoveListener listener)
throws InvocationException
{
// le sanity check
int cSceneId = getCallerSceneId(caller);
if (cSceneId != sceneId) {
Log.info("Ignoring stale traverse portal request " +
"[caller=" + caller.who() + ", oSceneId=" + sceneId +
", portalId=" + portalId + ", cSceneId=" + cSceneId + "].");
InvocationMarshaller.setNoResponse(listener);
return;
}
// avoid cluttering up the method declaration with final keywords
final BodyObject fsource = (BodyObject)caller;
final int fportalId = portalId;
final int fsceneVer = destSceneVer;
final SceneMoveListener flistener = listener;
// obtain the source scene
final SpotSceneManager srcmgr = (SpotSceneManager)
_screg.getSceneManager(sceneId);
if (srcmgr == null) {
Log.warning("Traverse portal missing source scene " +
"[user=" + fsource.who() + ", sceneId=" + sceneId +
", portalId=" + portalId + "].");
throw new InvocationException(INTERNAL_ERROR);
}
// obtain the destination scene and location id
SpotScene rss = (SpotScene)srcmgr.getScene();
final Portal fdest = rss.getPortal(portalId);
// give the source scene manager a chance to do access control
String errmsg = srcmgr.mayTraversePortal(fsource, fdest);
if (errmsg != null) {
throw new InvocationException(errmsg);
}
// make sure this portal has valid info
if (fdest == null || !fdest.isValid()) {
Log.warning("Traverse portal with invalid portal " +
"[user=" + fsource.who() + ", scene=" + srcmgr.where() +
", pid=" + portalId + ", portal=" + fdest +
", portals=" + StringUtil.toString(rss.getPortals()) +
"].");
throw new InvocationException(NO_SUCH_PORTAL);
}
// resolve their destination scene
SceneRegistry.ResolutionListener rl =
new SceneRegistry.ResolutionListener() {
public void sceneWasResolved (SceneManager scmgr) {
// make sure our caller is still around; under heavy
// load, clients might end their session while the
// scene is resolving
if (!fsource.isActive()) {
Log.info("Abandoning portal traversal, client gone " +
"[who=" + fsource.who() +
", dest=" + scmgr.where() + "].");
InvocationMarshaller.setNoResponse(flistener);
return;
}
// let the source manager know that this guy is
// departing via the specified portal
srcmgr.willTraversePortal(fsource, fdest);
SpotSceneManager sscmgr = (SpotSceneManager)scmgr;
finishTraversePortalRequest(
fsource, sscmgr, fsceneVer, fdest, flistener);
}
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
flistener.requestFailed(NO_SUCH_PLACE);
}
};
_screg.resolveScene(fdest.targetSceneId, rl);
}
/**
* 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, SpotSceneManager destmgr, int sceneVer,
Portal dest, SceneMoveListener listener)
{
// let the destination scene manager know that we're coming in
destmgr.mapEnteringBody(source, dest.targetPortalId);
try {
// move to the place object associated with this scene
_screg.sceneprov.effectSceneMove(
source, destmgr, sceneVer, listener);
} catch (InvocationException sfe) {
listener.requestFailed(sfe.getMessage());
// and let the destination scene manager know that we're no
// longer coming in
destmgr.clearEnteringBody(source);
}
}
/**
* Processes a {@link SpotService#changeLocation} request.
*/
public void changeLocation (ClientObject caller, int sceneId, Location loc,
SpotService.ConfirmListener listener)
throws InvocationException
{
BodyObject source = (BodyObject)caller;
int cSceneId = getCallerSceneId(caller);
if (cSceneId != sceneId) {
Log.info("Rejecting changeLocation for invalid scene " +
"[user=" + source.who() + ", insid=" + cSceneId +
", wantsid=" + sceneId + ", loc=" + loc + "].");
throw new InvocationException(INVALID_LOCATION);
}
// 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 from " +
"non-existent scene [user=" + source.who() +
", sceneId=" + sceneId + ", loc=" + loc +"].");
throw new InvocationException(INTERNAL_ERROR);
}
// pass the buck to yon scene manager
smgr.handleChangeLoc(source, loc);
// if that method finished, we're good to go
listener.requestProcessed();
}
/**
* Processes a {@link SpotService#joinCluster} request.
*/
public void joinCluster (ClientObject caller, int friendOid,
SpotService.ConfirmListener listener)
throws InvocationException
{
int sceneId = getCallerSceneId(caller);
BodyObject source = (BodyObject)caller;
// look up the scene manager for the specified scene
SpotSceneManager smgr = (SpotSceneManager)
_screg.getSceneManager(sceneId);
if (smgr == null) {
Log.warning("User requested to join cluster from " +
"non-existent scene [user=" + source.who() +
", sceneId=" + sceneId + ", foid=" + friendOid +"].");
throw new InvocationException(INTERNAL_ERROR);
}
// pass the buck to yon scene manager
smgr.handleJoinCluster(source, friendOid);
// if that method finished, we're good to go
listener.requestProcessed();
}
/**
* Handles request to generate a speak message in the specified cluster.
*/
public void clusterSpeak (ClientObject caller, String message, byte mode)
throws InvocationException
{
// ensure the caller has normal chat access
BodyObject source = (BodyObject)caller;
String errmsg = source.checkAccess(ChatCodes.CHAT_ACCESS, null);
if (errmsg != null) {
SpeakProvider.sendFeedback(source,
MessageManager.GLOBAL_BUNDLE, errmsg);
return;
}
sendClusterChatMessage(getCallerSceneId(caller), source.getOid(),
source.getVisibleName(), null, message, mode);
}
/**
* Sends a cluster chat notification to the specified location in the
* specified place object originating with the specified speaker (the
* speaker can be a server entity that wishes to fake a "speak"
* message, in which case the bundle argument should be non-null and
* should contain the id of the bundle to be used to translate the
* message text) and with the supplied message content.
*
* @param sceneId the scene id in which to deliver the chat message.
* @param speakerOid the body object id of the speaker (used to verify
* that they are in the cluster in question).
* @param speaker the username of the user that generated the message
* (or some special speaker name for server messages).
* @param bundle the bundle identifier that will be used by the client
* to translate the message text (or null if the message originated
* from a real live human who wrote it in their native tongue).
* @param message the text of the chat message.
*/
public void sendClusterChatMessage (
int sceneId, int speakerOid, Name speaker,
String bundle, String message, byte mode)
{
// 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=" + speaker + ", sceneId=" + sceneId +
", message=" + message + "].");
return;
}
// pass this request on to the spot scene manager
smgr.handleClusterSpeakRequest(
speakerOid, speaker, bundle, message, mode);
}
/**
* Obtains the scene id occupied by the supplied caller.
*
* @exception InvocationException thrown if the caller does not
* implement {@link ScenedBodyObject}.
*/
protected static int getCallerSceneId (ClientObject caller)
throws InvocationException
{
if (caller instanceof ScenedBodyObject) {
return ((ScenedBodyObject)caller).getSceneId();
} else {
Log.warning("Can't get scene from non-scened caller " +
caller.who() + ".");
throw new InvocationException(INTERNAL_ERROR);
}
}
/** The place registry with which we interoperate. */
protected PlaceRegistry _plreg;
/** The scene registry with which we interoperate. */
protected SceneRegistry _screg;
/** The object manager we use to do dobject stuff. */
protected RootDObjectManager _omgr;
}
@@ -1,626 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.server;
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;
import com.threerings.presents.dobj.Subscriber;
import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.server.InvocationException;
import com.threerings.crowd.chat.server.SpeakProvider;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.OccupantInfo;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.whirled.server.SceneManager;
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.ClusteredBodyObject;
import com.threerings.whirled.spot.data.Location;
import com.threerings.whirled.spot.data.Portal;
import com.threerings.whirled.spot.data.SceneLocation;
import com.threerings.whirled.spot.data.SpotCodes;
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.
*/
public class SpotSceneManager extends SceneManager
implements SpotCodes
{
/**
* Move the specified body to the default portal, if possible.
*/
public static void moveBodyToDefaultPortal (BodyObject body)
{
SpotSceneManager mgr = (SpotSceneManager)
CrowdServer.plreg.getPlaceManager(body.location);
if (mgr != null) {
SpotScene scene = (SpotScene)mgr.getScene();
if (scene == null) {
Log.warning("No scene in moveBodyToDefaultPortal()? " +
"[who=" + body.who() +
", where=" + mgr.where() + "].");
return;
}
try {
Location eloc = scene.getDefaultEntrance().getLocation();
mgr.handleChangeLoc(body, eloc);
} catch (InvocationException ie) {
Log.warning("Could not move user to default portal " +
"[where=" + mgr.where() + ", who=" + body.who() +
", error=" + ie + "].");
}
}
}
/**
* 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)
{
_enterers.put(body.getOid(), portalId);
}
/**
* Called if a body failed to enter our scene after we assigned them
* an entering position.
*/
public void clearEnteringBody (BodyObject body)
{
_enterers.remove(body.getOid());
}
/**
* 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)
{
return null;
}
/**
* 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)
{
updateLocation(body, portal.getLocation());
}
// 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)
_ssobj = (SpotSceneObject)_plobj;
super.didStartup();
}
// documentation inherited
protected void gotSceneData ()
{
super.gotSceneData();
// keep a casted reference around to our scene
_sscene = (SpotScene)_scene;
}
// documentation inherited
protected void bodyLeft (int bodyOid)
{
super.bodyLeft(bodyOid);
// clear out their location information
_ssobj.removeFromOccupantLocs(Integer.valueOf(bodyOid));
// clear any cluster they may occupy
removeFromCluster(bodyOid);
// let's make damned sure they're not in any cluster
Iterator cliter = _clusters.values().iterator();
while (cliter.hasNext()) {
ClusterRecord clrec = (ClusterRecord)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.
clrec.destroy(false);
cliter.remove();
}
}
}
}
// documentation inherited
protected void insertOccupantInfo (OccupantInfo info, BodyObject body)
{
super.insertOccupantInfo(info, body);
// we don't actually populate their occupant info, but instead assign
// them their starting location in the scene
int portalId = _enterers.remove(body.getOid());
Portal entry;
if (portalId != -1) {
entry = _sscene.getPortal(portalId);
if (entry == null) {
Log.warning("Body mapped at invalid portal [where=" + where() +
", who=" + body.who() +
", portalId=" + portalId + "].");
entry = _sscene.getDefaultEntrance();
}
} else {
entry = _sscene.getDefaultEntrance();
}
// 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
_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.
*/
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.
*
* @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.
*/
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 + "].");
throw new InvocationException(INTERNAL_ERROR);
}
// let our derived classes decide if this is an OK place to stand
if (!validateLocation(source, loc)) {
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
updateLocation(source, loc);
// remove them from any cluster as they've departed
removeFromCluster(source.getOid());
}
/**
* 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)
{
return true;
}
/**
* Updates the location of the specified body.
*/
protected void updateLocation (BodyObject source, Location loc)
{
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 + "].");
Thread.dumpStack();
_ssobj.addToOccupantLocs(sloc);
} else {
_ssobj.updateOccupantLocs(sloc);
}
}
/**
* 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.
*
* @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);
if (clrec != null) {
clrec.addBody(joiner);
return;
}
// 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() +
", targetOid=" + targetOid + "].");
throw new InvocationException(NO_SUCH_CLUSTER);
}
// 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() +
", tloc=" + friend.location + "].");
throw new InvocationException(NO_SUCH_CLUSTER);
}
// see if the friend is already in a cluster
clrec = getCluster(friend.getOid());
if (clrec != null) {
clrec.addBody(joiner);
return;
}
// 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() + "].");
clrec = createClusterRecord();
clrec.addBody(friend);
clrec.addBody(joiner);
}
/**
* Creates the cluster record instance that we'll use to manage our
* cluster.
*/
protected ClusterRecord createClusterRecord ()
{
return new ClusterRecord();
}
/**
* 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
{
// nothing to do by default
}
/**
* Removes the specified user from any cluster they occupy.
*/
protected void removeFromCluster (int bodyOid)
{
ClusterRecord clrec = getCluster(bodyOid);
if (clrec != null) {
clrec.removeBody(bodyOid);
// If that was the last person, destroy the cluster
if (clrec.size() == 0) {
clrec.destroy(true);
}
}
}
/**
* Fetches the cluster record for the specified body.
*/
protected ClusterRecord getCluster (int bodyOid)
{
ClusteredBodyObject bobj = (ClusteredBodyObject)
CrowdServer.omgr.getObject(bodyOid);
return (bobj == null) ? null :
(ClusterRecord)_clusters.get(bobj.getClusterOid());
}
/**
* 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)
{
ClusterRecord clrec = getCluster(sourceOid);
if (clrec == null) {
Log.warning("Non-clustered user requested cluster speak " +
"[where=" + where() + ", chatter=" + source +
" (" + sourceOid + "), msg=" + message + "].");
} else {
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.
*/
protected SceneLocation locationForBody (int bodyOid)
{
return (SceneLocation)_ssobj.occupantLocs.get(Integer.valueOf(bodyOid));
}
/**
* Verifies that the specified cluster can be expanded to include
* another body.
*/
protected boolean canAddBody (ClusterRecord clrec, BodyObject body)
{
return true;
}
/**
* 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.
*/
protected void bodyRemoved (ClusterRecord clrec, BodyObject body)
{
}
/**
* Used to manage clusters which are groups of users that can chat to
* one another.
*/
protected class ClusterRecord extends HashIntMap
implements Subscriber
{
public ClusterRecord ()
{
CrowdServer.omgr.createObject(ClusterObject.class, this);
}
public boolean addBody (BodyObject body)
throws InvocationException
{
if (!(body instanceof ClusteredBodyObject)) {
Log.warning("Refusing to add non-clustered body to cluster " +
"[cloid=" + _clobj.getOid() +
", size=" + size() + ", who=" + body.who() + "].");
throw new InvocationException(INTERNAL_ERROR);
}
// if they're already in the cluster, do nothing
if (containsKey(body.getOid())) {
return false;
}
// make sure we can add this body
if (!canAddBody(this, body)) {
// Log.debug("Cluster full, refusing growth " + this + ".");
throw new InvocationException(CLUSTER_FULL);
}
// make sure our intrepid joiner is not in any another cluster
removeFromCluster(body.getOid());
put(body.getOid(), body);
_ssobj.startTransaction();
try {
body.startTransaction();
try {
bodyAdded(this, body); // do the hokey pokey
if (_clobj != null) {
((ClusteredBodyObject)body).setClusterOid(
_clobj.getOid());
_clobj.addToOccupants(body.getOid());
_ssobj.updateClusters(_cluster);
}
} finally {
body.commitTransaction();
}
} finally {
_ssobj.commitTransaction();
}
// Log.debug("Added " + body.who() + " to "+ this + ".");
return true;
}
public void removeBody (int bodyOid)
{
BodyObject body = (BodyObject)remove(bodyOid);
if (body == null) {
Log.warning("Requested to remove unknown body from cluster " +
"[cloid=" + _clobj.getOid() +
", size=" + size() + ", who=" + bodyOid + "].");
return;
}
if (body.isActive()) {
body.startTransaction();
}
try {
_ssobj.startTransaction();
try {
((ClusteredBodyObject)body).setClusterOid(-1);
bodyRemoved(this, body); // do the hokey pokey
if (_clobj != null) {
_clobj.removeFromOccupants(bodyOid);
_ssobj.updateClusters(_cluster);
}
} finally {
_ssobj.commitTransaction();
}
} finally {
if (body.isActive()) {
body.commitTransaction();
}
}
// Log.debug("Removed " + bodyOid + " from "+ this + ".");
}
public ClusterObject getClusterObject ()
{
return _clobj;
}
public Cluster getCluster ()
{
return _cluster;
}
public void objectAvailable (DObject object)
{
// keep this feller around
_clobj = (ClusterObject)object;
_clusters.put(_clobj.getOid(), this);
// let any mapped users know about our cluster
Iterator iter = values().iterator();
while (iter.hasNext()) {
ClusteredBodyObject body = (ClusteredBodyObject)iter.next();
body.setClusterOid(_clobj.getOid());
_clobj.addToOccupants(((BodyObject)body).getOid());
}
// configure our cluster record and publish it
_cluster.clusterOid = _clobj.getOid();
_ssobj.addToClusters(_cluster);
// if we didn't manage to add our creating user when we first
// started up, there's no point in our sticking around
if (size() == 0) {
destroy(true);
}
}
public void requestFailed (int oid, ObjectAccessException cause)
{
Log.warning("Aiya! Failed to create cluster object " +
"[cause=" + cause + ", penders=" + size() + "].");
// let any mapped users know that we're hosed
Iterator iter = values().iterator();
while (iter.hasNext()) {
ClusteredBodyObject body = (ClusteredBodyObject)iter.next();
body.setClusterOid(-1);
}
}
public String toString ()
{
return "[cluster=" + _cluster + ", size=" + size() + "]";
}
protected void destroy (boolean doRemoval)
{
// 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 (doRemoval) {
_clusters.remove(_clobj.getOid());
}
CrowdServer.omgr.destroyObject(_clobj.getOid());
}
protected ClusterObject _clobj;
protected Cluster _cluster = new Cluster();
}
/** A casted reference to our place object. */
protected SpotSceneObject _ssobj;
/** A casted reference to our scene instance. */
protected SpotScene _sscene;
/** Records with information on all clusters in this scene. */
protected HashIntMap _clusters = new HashIntMap();
/** A mapping of entering bodies to portal ids. */
protected IntIntMap _enterers = new IntIntMap();
}
@@ -1,42 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.tools;
import com.threerings.whirled.spot.data.Portal;
/**
* An editable portal extends the standard portal with information needed
* by the loader and editor.
*/
public class EditablePortal extends Portal
{
/** The human-readable name of this portal. */
public String name;
/** The human-readable name of the scene to which this portal
* links. */
public String targetSceneName;
/** The human-readable name of the portal to which this portal links
* in its target scene. */
public String targetPortalName;
}
@@ -1,164 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.tools.xml;
import java.lang.reflect.Field;
import com.samskivert.xml.SetPropertyFieldsRule;
import com.samskivert.util.StringUtil;
import com.samskivert.util.ValueMarshaller;
import com.threerings.tools.xml.NestableRuleSet;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.ObjectCreateRule;
import org.apache.commons.digester.Rule;
import org.xml.sax.Attributes;
import com.threerings.whirled.spot.data.Location;
import com.threerings.whirled.spot.data.Portal;
import com.threerings.whirled.spot.data.SpotSceneModel;
import com.threerings.whirled.spot.tools.EditablePortal;
/**
* Used to parse a {@link SpotSceneModel} from XML.
*/
public abstract class SpotSceneRuleSet implements NestableRuleSet
{
// documentation inherited from interface
public String getOuterElement ()
{
return SpotSceneWriter.OUTER_ELEMENT;
}
// documentation inherited from interface
public void addRuleInstances (String prefix, Digester digester)
{
digester.addObjectCreate(prefix, SpotSceneModel.class.getName());
// set up rules to parse and set our fields
digester.addRule(prefix, new SetPropertyFieldsRule());
// create EditablePortal instances when we see <portal>
digester.addRule(prefix + "/portal", new PortalCreateRule(this));
digester.addRule(prefix + "/portal", new PortalFieldsRule());
digester.addSetNext(prefix + "/portal", "addPortal",
Portal.class.getName());
}
/**
* Create a new instance of the Location class that should be used
* with Portals.
*/
protected abstract Location createLocation ();
/**
* A rule used to create the portal but also initialize the Location
* property within it.
*/
protected static class PortalCreateRule extends ObjectCreateRule
{
public PortalCreateRule (SpotSceneRuleSet ruleset)
{
super(EditablePortal.class.getName());
_ruleset = ruleset;
}
// documentation inherited
public void begin (String namespace, String name, Attributes attributes)
throws Exception
{
super.begin(namespace, name, attributes);
// create the empty Location in the Portal
Portal p = (Portal) digester.peek();
p.loc = _ruleset.createLocation();
}
protected SpotSceneRuleSet _ruleset;
}
/**
* Set fields in the Portal, or in the Location object
* contained therein. If there are ambiguous attribute names then..
* well. yeah.
*/
protected static class PortalFieldsRule extends Rule
{
// documentation inherited
public void begin (String namespace, String name, Attributes attrs)
throws Exception
{
Portal portal = (Portal) digester.peek();
Class portalClass = portal.getClass();
Location loc = portal.loc;
Class locClass = loc.getClass();
// iterate over the attributes, setting public fields where
// applicable
for (int i = 0; i < attrs.getLength(); i++) {
String lname = attrs.getLocalName(i);
if (StringUtil.isBlank(lname)) {
lname = attrs.getQName(i);
}
// look for a public field with this lname
Field field;
Object container;
try {
field = portalClass.getField(lname);
container = portal;
} catch (NoSuchFieldException nsfe) {
// if we didn't find the field in the Portal, maybe it's
// in the Location
try {
field = locClass.getField(lname);
container = loc;
} catch (NoSuchFieldException nsfe2) {
digester.getLogger().warn(
"Skipping property '" + lname +
"' for which there is no field.");
continue;
}
}
// convert the value into the appropriate object type
String valstr = attrs.getValue(i);
// use the value marshaller to parse the
// property based on the type of the target object field
Object value = ValueMarshaller.unmarshal(
field.getType(), valstr);
if (digester.getLogger().isDebugEnabled()) {
digester.getLogger().debug(" Setting property '" + lname +
"' to '" + valstr + "'");
}
// and finally set the field
field.set(container, value);
}
}
}
}
@@ -1,113 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.spot.tools.xml;
import java.lang.reflect.Field;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import com.megginson.sax.DataWriter;
import com.threerings.tools.xml.NestableWriter;
import com.threerings.whirled.spot.Log;
import com.threerings.whirled.spot.data.Location;
import com.threerings.whirled.spot.data.SpotSceneModel;
import com.threerings.whirled.spot.tools.EditablePortal;
/**
* Generates an XML representation of a {@link SpotSceneModel}.
*/
public class SpotSceneWriter
implements NestableWriter
{
/** The outer element used to enclose our spot scene definition. */
public static final String OUTER_ELEMENT = "spot";
// documentation inherited from interface
public void write (Object object, DataWriter writer)
throws SAXException
{
SpotSceneModel model = (SpotSceneModel)object;
AttributesImpl attrs = new AttributesImpl();
addSceneAttributes(model, attrs);
writer.startElement("", OUTER_ELEMENT, "", attrs);
writeSceneData(model, writer);
writer.endElement(OUTER_ELEMENT);
}
protected void addSceneAttributes (SpotSceneModel model,
AttributesImpl attrs)
{
if (model.defaultEntranceId != -1) {
attrs.addAttribute("", "defaultEntranceId", "", "",
String.valueOf(model.defaultEntranceId));
}
}
protected void writeSceneData (SpotSceneModel model, DataWriter writer)
throws SAXException
{
// write out the portal info
for (int ii = 0; ii < model.portals.length; ii++) {
EditablePortal port = (EditablePortal)model.portals[ii];
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "portalId", "", "",
String.valueOf(port.portalId));
addPortalLocationAttributes(port.loc, attrs);
maybeAddAttr(attrs, "name", port.name);
maybeAddAttr(attrs, "targetSceneName", port.targetSceneName);
maybeAddAttr(attrs, "targetPortalName", port.targetPortalName);
writer.emptyElement("", "portal", "", attrs);
}
}
protected void addPortalLocationAttributes (
Location portalLoc, AttributesImpl attrs)
{
// we just add all the visible fields of the location, but something
// more sophisticated could be done
Class clazz = portalLoc.getClass();
Field[] fields = clazz.getFields();
for (int ii=0; ii < fields.length; ii++) {
try {
attrs.addAttribute("", fields[ii].getName(), "", "",
String.valueOf(fields[ii].get(portalLoc)));
} catch (IllegalAccessException iae) {
Log.warning("Unable to write portal field, skipping " +
"[field=" + fields[ii].getName() + ", e=" + iae + "].");
}
}
}
/**
* Adds the supplied attribute to the attributes object iff the value
* is non-null.
*/
protected void maybeAddAttr (
AttributesImpl attrs, String name, String value)
{
if (value != null) {
attrs.addAttribute("", name, "", "", value);
}
}
}
@@ -1,113 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.tools.xml;
import java.io.IOException;
import java.io.FileInputStream;
import org.xml.sax.SAXException;
import org.apache.commons.digester.Digester;
import com.samskivert.util.StringUtil;
import com.threerings.tools.xml.NestableRuleSet;
import com.threerings.whirled.data.AuxModel;
import com.threerings.whirled.data.SceneModel;
/**
* A simple class for parsing an editable scene instance.
*/
public class SceneParser
{
/**
* Constructs a scene parser that parses scenes with the specified XML
* path prefix.
*/
public SceneParser (String prefix)
{
// create and configure our digester
_digester = new Digester();
// create our scene rule set
SceneRuleSet set = createSceneRuleSet();
// configure our top-level path prefix
if (StringUtil.isBlank(prefix)) {
_prefix = set.getOuterElement();
} else {
_prefix = prefix + "/" + set.getOuterElement();
}
// add the scene rules
set.addRuleInstances(_prefix, _digester);
// add a rule to grab the finished scene model
_digester.addSetNext(_prefix, "setScene", SceneModel.class.getName());
}
/**
* Creates the rule set used to parse our scene.
*/
protected SceneRuleSet createSceneRuleSet ()
{
return new SceneRuleSet();
}
/**
* Adds a {@link NestableRuleSet} for parsing auxiliary scene models.
*/
public void registerAuxRuleSet (NestableRuleSet set)
{
// add their outer element to the prefix
String prefix = _prefix + "/" + set.getOuterElement();
// add the rules to generate the aux scene model
set.addRuleInstances(prefix, _digester);
// and add a rule to grab it
_digester.addSetNext(prefix, "addAuxModel", AuxModel.class.getName());
}
/**
* Parses the XML file at the specified path into a scene model
* instance.
*/
public SceneModel parseScene (String path)
throws IOException, SAXException
{
_model = null;
_digester.push(this);
_digester.parse(new FileInputStream(path));
return _model;
}
/**
* Called by the parser once the scene is parsed.
*/
public void setScene (SceneModel model)
{
_model = model;
}
protected String _prefix;
protected Digester _digester;
protected SceneModel _model;
}
@@ -1,60 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.tools.xml;
import org.apache.commons.digester.Digester;
import com.samskivert.xml.SetPropertyFieldsRule;
import com.threerings.tools.xml.NestableRuleSet;
import com.threerings.whirled.data.SceneModel;
/**
* Used to parse a {@link SceneModel} from XML.
*/
public class SceneRuleSet implements NestableRuleSet
{
// documentation inherited from interface
public String getOuterElement ()
{
return SceneWriter.OUTER_ELEMENT;
}
// documentation inherited from interface
public void addRuleInstances (String prefix, Digester digester)
{
// this creates the appropriate instance when we encounter our tag
digester.addObjectCreate(prefix, getSceneClass().getName());
// set up rules to parse and set our fields
digester.addRule(prefix, new SetPropertyFieldsRule());
}
/**
* This indicates the class (which should extend {@link SceneModel})
* to be instantiated during the parsing process.
*/
protected Class getSceneClass ()
{
return SceneModel.class;
}
}
@@ -1,121 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.tools.xml;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import com.megginson.sax.DataWriter;
import com.threerings.tools.xml.NestableWriter;
import com.threerings.whirled.Log;
import com.threerings.whirled.data.AuxModel;
import com.threerings.whirled.data.SceneModel;
/**
* Generates an XML representation of an {@link SceneModel}.
*/
public class SceneWriter
{
/** The outer element used to enclose our scene definition. */
public static final String OUTER_ELEMENT = "scene";
/**
* Registers a writer for writing auxiliary scene models of the
* supplied class.
*/
public void registerAuxWriter (Class aclass, NestableWriter writer)
{
_auxers.put(aclass, writer);
}
/**
* Writes the supplied scene out to the specified file.
*/
public void writeScene (File out, SceneModel model)
throws IOException, SAXException
{
FileWriter fout = new FileWriter(out);
DataWriter dout = new DataWriter(fout);
dout.setIndentStep(2);
dout.startDocument();
writeSceneModel(model, dout);
dout.endDocument();
fout.close();
}
/**
* Writes the data for the supplied {@link SceneModel} to the XML data
* writer supplied. The writer should already be configured with the
* appropriate indentation level so that this writer can simply output
* its elements and allow the calling code to determine where in the
* greater scene description file the scene data should live.
*/
public void writeSceneModel (SceneModel model, DataWriter writer)
throws SAXException
{
AttributesImpl attrs = new AttributesImpl();
addSceneAttributes(model, attrs);
writer.startElement("", OUTER_ELEMENT, "", attrs);
writeSceneData(model, writer);
writer.endElement(OUTER_ELEMENT);
}
/**
* Adds attributes to the top-level element before it gets written.
*/
protected void addSceneAttributes (
SceneModel model, AttributesImpl attrs)
{
attrs.addAttribute("", "name", "", "", model.name);
attrs.addAttribute("", "version", "", "",
Integer.toString(model.version));
}
/**
* Writes just the scene data which is handy for derived classes which
* may wish to add their own scene data to the scene output.
*/
protected void writeSceneData (SceneModel model, DataWriter writer)
throws SAXException
{
// write out our auxiliary scene models
for (int ii = 0; ii < model.auxModels.length; ii++) {
AuxModel amodel = model.auxModels[ii];
NestableWriter awriter = (NestableWriter)
_auxers.get(amodel.getClass());
if (awriter != null) {
awriter.write(amodel, writer);
} else {
Log.warning("No writer registered for auxiliary scene model " +
"[mclass=" + amodel.getClass() + "].");
}
}
}
protected HashMap _auxers = new HashMap();
}
@@ -1,42 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.util;
/**
* Thrown when an attempt to load a non-existent scene is made on the
* repository.
*/
public class NoSuchSceneException extends Exception
{
public NoSuchSceneException (int sceneid)
{
super("No such scene [sceneid=" + sceneid + "]");
_sceneid = sceneid;
}
public int getSceneId ()
{
return _sceneid;
}
protected int _sceneid;
}
@@ -1,39 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.util;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.whirled.data.Scene;
import com.threerings.whirled.data.SceneModel;
/**
* This is used by the Whirled services to obtain a {@link Scene}
* implementation given a scene model and associated data.
*/
public interface SceneFactory
{
/**
* Creates a {@link Scene} implementation given the supplied scene
* model and place config.
*/
public Scene createScene (SceneModel model, PlaceConfig config);
}
@@ -1,107 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.util;
import java.util.ArrayList;
import com.threerings.whirled.Log;
import com.threerings.whirled.data.SceneUpdate;
/**
* A list specialized for storing {@link SceneUpdate} objects.
*/
public class UpdateList
{
public UpdateList ()
{
_updates = new ArrayList();
}
/**
* Adds an update to this list. The update must follow appropriately
* the chain of updates established by the updates already in the list
* (meaning it must operate on one version higher than the most recent
* update already in the list).
*/
public void addUpdate (SceneUpdate update)
{
if (_minVersion == -1) {
// this is our first update, so we initialize our min version
_minVersion = update.getSceneVersion();
} else {
int gotVersion = update.getSceneVersion();
int expVersion = _minVersion + _updates.size();
if (gotVersion > expVersion) {
Log.warning("Update continuity broken, flushing list " +
"[got=" + update + ", expect=" + expVersion +
", ucount=" + _updates.size() + "].");
// flush out our old updates and start anew from here
_updates.clear();
_minVersion = expVersion;
} else if (gotVersion < expVersion) {
// we somehow got an update that's older than updates we
// already have? wick wick wack
String errmsg = "Invalid update version " +
"[want=" + expVersion + ", got=" + update + "]";
throw new IllegalArgumentException(errmsg);
}
}
_updates.add(update);
}
/**
* Returns all of the updates that should be applied to a scene with
* the specified version to bring it up to date. <code>null</code> is
* returned if the scene's version is older than the oldest update in
* our list, in which case it cannot be brought up to date by applying
* updates from this list.
*/
public SceneUpdate[] getUpdates (int fromVersion)
{
if (_minVersion == -1 || fromVersion < _minVersion) {
return null;
}
int offset = fromVersion - _minVersion;
int ucount = _updates.size() - offset;
SceneUpdate[] updates = new SceneUpdate[ucount];
for (int ii = 0; ii < ucount; ii++) {
updates[ii] = (SceneUpdate)_updates.get(ii+offset);
}
return updates;
}
/**
* Returns true if the supplied actual scene version is in accordance
* with the updates contained in this list.
*/
public boolean validate (int sceneVersion)
{
return ((_minVersion == -1) || // we have no updates
(_minVersion + _updates.size() == sceneVersion));
}
protected ArrayList _updates;
protected int _minVersion = -1;
}
@@ -1,37 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.util;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.whirled.client.SceneDirector;
/**
* The whirled context provides access to the various managers, etc. that
* are needed by the whirled client code.
*/
public interface WhirledContext extends CrowdContext
{
/**
* Returns a reference to the scene director.
*/
public SceneDirector getSceneDirector ();
}
@@ -1,56 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone;
/**
* A placeholder class that contains a reference to the log object used by
* the Whirled Zone services.
*/
public class Log
{
public static com.samskivert.util.Log log =
new com.samskivert.util.Log("whirled.zone");
/** Convenience function. */
public static void debug (String message)
{
log.debug(message);
}
/** Convenience function. */
public static void info (String message)
{
log.info(message);
}
/** Convenience function. */
public static void warning (String message)
{
log.warning(message);
}
/** Convenience function. */
public static void logStackTrace (Throwable t)
{
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
}
}
@@ -1,51 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.client;
import com.threerings.whirled.zone.data.ZoneSummary;
/**
* The zone adapter makes life easier for a class that really only cares
* about one or two of the zone observer callbacks and doesn't want to
* provide empty implementations of the others. One can either extend zone
* adapter, or create an anonymous instance that overrides the desired
* callback(s).
*
* @see ZoneObserver
*/
public class ZoneAdapter implements ZoneObserver
{
// documentation inherited from interface
public void zoneWillChange (int zoneId)
{
}
// documentation inherited from interface
public void zoneDidChange (ZoneSummary summary)
{
}
// documentation inherited from interface
public void zoneChangeFailed (String reason)
{
}
}
@@ -1,69 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.client;
import com.threerings.presents.client.InvocationDecoder;
import com.threerings.whirled.zone.client.ZoneReceiver;
/**
* Dispatches calls to a {@link ZoneReceiver} instance.
*/
public class ZoneDecoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static final String RECEIVER_CODE = "2d900cf54355111b4bb4befcdff42b82";
/** The method id used to dispatch {@link ZoneReceiver#forcedMove}
* notifications. */
public static final int FORCED_MOVE = 1;
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
*/
public ZoneDecoder (ZoneReceiver receiver)
{
this.receiver = receiver;
}
// documentation inherited
public String getReceiverCode ()
{
return RECEIVER_CODE;
}
// documentation inherited
public void dispatchNotification (int methodId, Object[] args)
{
switch (methodId) {
case FORCED_MOVE:
((ZoneReceiver)receiver).forcedMove(
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue()
);
return;
default:
super.dispatchNotification(methodId, args);
return;
}
}
}
@@ -1,315 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.client;
import java.util.ArrayList;
import com.samskivert.util.ResultListener;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.whirled.client.SceneDirector;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.util.WhirledContext;
import com.threerings.whirled.zone.Log;
import com.threerings.whirled.zone.data.ZoneSummary;
import com.threerings.whirled.zone.util.ZoneUtil;
/**
* The zone director augments the scene services with the notion of zones.
* Zones are self-contained, connected groups of scenes. The normal scene
* director services can be used to move from scene to scene, but moving
* to a new zone requires a special move request which can be accomplished
* via the zone director. The zone director also makes available the zone
* summary which provides information on the zone which can be used to
* generate an overview map or similar.
*/
public class ZoneDirector extends BasicDirector
implements ZoneReceiver, ZoneService.ZoneMoveListener,
SceneDirector.MoveHandler
{
/**
* Constructs a zone director with the supplied context, and delegate
* scene director (which the zone director will coordinate with when
* moving from scene to scene). A zone director is required on the
* client side for systems that wish to use the zone services.
*/
public ZoneDirector (WhirledContext ctx, SceneDirector scdir)
{
super(ctx);
_ctx = ctx;
_scdir = scdir;
_scdir.setMoveHandler(this);
// register for zone notifications
_ctx.getClient().getInvocationDirector().registerReceiver(
new ZoneDecoder(this));
}
/**
* Returns the summary for the zone currently occupied by the client
* or null if the client does not currently occupy a zone (not a
* normal situation).
*/
public ZoneSummary getZoneSummary ()
{
return _summary;
}
/**
* Adds a zone observer to the list. This observer will subsequently
* be notified of effected and failed zone changes.
*/
public void addZoneObserver (ZoneObserver observer)
{
_observers.add(observer);
}
/**
* Removes a zone observer from the list.
*/
public void removeZoneObserver (ZoneObserver observer)
{
_observers.remove(observer);
}
/**
* Requests that this client move the specified scene in the specified
* zone. A request will be made and when the response is received, the
* location observers will be notified of success or failure.
*/
public boolean moveTo (int zoneId, int sceneId)
{
return moveTo(zoneId, sceneId, null);
}
/**
* Requests that this client move the specified scene in the specified
* zone. A request will be made and when the response is received, the
* location observers will be notified of success or failure.
*/
public boolean moveTo (int zoneId, int sceneId, ResultListener rl)
{
// make sure the zoneId and sceneId are valid
if (zoneId < 0 || sceneId < 0) {
Log.warning("Refusing moveTo(): invalid sceneId or zoneId " +
"[zoneId=" + zoneId + ", sceneId=" + sceneId + "].");
return false;
}
// if the requested zone is the same as our current zone, we just
// want a regular old moveTo request
if (_summary != null && zoneId == _summary.zoneId) {
return _scdir.moveTo(sceneId);
}
// otherwise, we make a zoned moveTo request; prepare to move to
// this scene (sets up pending data)
if (!_scdir.prepareMoveTo(sceneId, rl)) {
return false;
}
// let our zone observers know that we're attempting to switch
// zones
notifyObservers(Integer.valueOf(zoneId));
// 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 sceneVers = 0;
SceneModel pendingModel = _scdir.getPendingModel();
if (pendingModel != null) {
sceneVers = pendingModel.version;
}
// issue a moveTo request
Log.info("Issuing zoned moveTo(" + ZoneUtil.toString(zoneId) +
", " + sceneId + ", " + sceneVers + ").");
_zservice.moveTo(_ctx.getClient(), zoneId, sceneId, sceneVers, this);
return true;
}
// documentation inherited
protected void fetchServices (Client client)
{
_zservice = (ZoneService)client.requireService(ZoneService.class);
}
// documentation inherited
public void clientDidLogoff (Client client)
{
super.clientDidLogoff(client);
// clear out our business
_zservice = null;
_summary = null;
_previousZoneId = -1;
}
/**
* Called in response to a successful {@link ZoneService#moveTo}
* request.
*/
public void moveSucceeded (
int placeId, PlaceConfig config, ZoneSummary summary)
{
if (_summary != null) {
// keep track of our previous zone info
_previousZoneId = _summary.zoneId;
}
// keep track of the summary
_summary = summary;
// pass the rest off to the standard scene transition code
_scdir.moveSucceeded(placeId, config);
// and let the zone observers know what's up
notifyObservers(summary);
}
/**
* Called in response to a successful {@link ZoneService#moveTo}
* request when our cached scene was out of date and the server
* determined that we needed some updates.
*/
public void moveSucceededWithUpdates (
int placeId, PlaceConfig config, ZoneSummary summary,
SceneUpdate[] updates)
{
// keep track of the summary
_summary = summary;
// pass the rest off to the standard scene transition code
_scdir.moveSucceededWithUpdates(placeId, config, updates);
// and let the zone observers know what's up
notifyObservers(summary);
}
/**
* Called in response to a successful {@link ZoneService#moveTo}
* request when our cached scene was out of date and the server
* determined that we needed an updated copy.
*/
public void moveSucceededWithScene (
int placeId, PlaceConfig config, ZoneSummary summary, SceneModel model)
{
// keep track of the summary
_summary = summary;
// pass the rest off to the standard scene transition code
_scdir.moveSucceededWithScene(placeId, config, model);
// and let the zone observers know what's up
notifyObservers(summary);
}
/**
* Called in response to a failed zoned <code>moveTo</code> request.
*/
public void requestFailed (String reason)
{
// let the scene director cope
_scdir.requestFailed(reason);
// and let the observers know what's up
notifyObservers(reason);
}
// documentation inherited from interface
public void forcedMove (int zoneId, int sceneId)
{
Log.info("Moving at request of server [zoneId=" + zoneId +
", sceneId=" + sceneId + "].");
// clear out our old scene and place data
_scdir.didLeaveScene();
// move to the new zone and scene
moveTo(zoneId, sceneId, null);
}
/**
* Called when something breaks down after successfully completely a
* <code>moveTo</code> request.
*/
public void recoverMoveTo (int sceneId)
{
if (_previousZoneId != -1) {
moveTo(_previousZoneId, sceneId);
} else {
_scdir.moveTo(sceneId);
}
}
/**
* Notifies observers of success or failure, depending on the type of
* object provided as data.
*/
protected void notifyObservers (Object data)
{
// let our observers know that all is well on the western front
for (int i = 0; i < _observers.size(); i++) {
ZoneObserver obs = (ZoneObserver)_observers.get(i);
try {
if (data instanceof Integer) {
obs.zoneWillChange(((Integer)data).intValue());
} else if (data instanceof ZoneSummary) {
obs.zoneDidChange((ZoneSummary)data);
} else {
obs.zoneChangeFailed((String)data);
}
} catch (Throwable t) {
Log.warning("Zone observer choked during notification " +
"[data=" + data + ", obs=" + obs + "].");
Log.logStackTrace(t);
}
}
}
/** A reference to the active client context. */
protected WhirledContext _ctx;
/** A reference to the scene director with which we coordinate. */
protected SceneDirector _scdir;
/** Provides access to zone services. */
protected ZoneService _zservice;
/** A reference to the zone summary for the currently occupied
* zone. */
protected ZoneSummary _summary;
/** Our zone observer list. */
protected ArrayList _observers = new ArrayList();
/** Our previous zone id. */
protected int _previousZoneId = -1;
}
@@ -1,58 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.client;
import com.threerings.whirled.zone.data.ZoneSummary;
/**
* The zone observer interface makes it possible for entities to be
* notified when the client moves to a new zone.
*/
public interface ZoneObserver
{
/**
* Called when we begin the process of switching to a new zone. This
* will be followed by a call to {@link #zoneDidChange} to indicate
* that the change was successful or {@link #zoneChangeFailed} if the
* change fails.
*
* @param zoneId the zone id of the zone to which we are changing.
*/
public void zoneWillChange (int zoneId);
/**
* Called when we have switched to a new zone.
*
* @param summary the summary information for the new zone or null if
* we have switched to no zone.
*/
public void zoneDidChange (ZoneSummary summary);
/**
* This is called on all zone observers when a zone change request is
* rejected by the server or fails for some other reason.
*
* @param reason the reason code that explains why the zone change
* request was rejected or otherwise failed.
*/
public void zoneChangeFailed (String reason);
}
@@ -1,40 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.client;
import com.threerings.presents.client.InvocationReceiver;
/**
* Defines, for the zone services, a set of notifications delivered
* asynchronously by the server to the client.
*/
public interface ZoneReceiver extends InvocationReceiver
{
/**
* Used to communicate a required move notification to the client. The
* server will have removed the client from their existing scene and
* the client is then responsible for generating a {@link
* ZoneService#moveTo} request to move to the new scene in the
* specified zone.
*/
public void forcedMove (int zoneId, int sceneId);
}
@@ -1,67 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.zone.data.ZoneSummary;
/**
* Defines the client interface to the zone related invocation services
* (e.g. moving between zones).
*/
public interface ZoneService extends InvocationService
{
/** Used to deliver responses to {@link #moveTo} requests. */
public static interface ZoneMoveListener extends InvocationListener
{
public void moveSucceeded (
int placeId, PlaceConfig config, ZoneSummary summary);
public void moveSucceededWithUpdates (
int placeId, PlaceConfig config, ZoneSummary summary,
SceneUpdate[] updates);
public void moveSucceededWithScene (
int placeId, PlaceConfig config, ZoneSummary summary,
SceneModel model);
}
/**
* Requests that that this client's body be moved to the specified
* scene in the specified zone.
*
* @param zoneId the zone id to which we want to move.
* @param sceneId the scene id to which we want to move.
* @param version the version number of the scene object that we have
* in our local repository.
* @param listener the object that will receive the callback when the
* request succeeds or fails.
*/
public void moveTo (Client client, int zoneId, int sceneId,
int version, ZoneMoveListener listener);
}
@@ -1,58 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.data;
import com.samskivert.util.StringUtil;
import com.threerings.io.Streamable;
/**
* The scene summary class is used to provide info about the connected
* group of scenes that make up a zone. The group of scenes that make up a
* zone is a self-contained set of scenes, connected with one another (by
* portals) but not to any scenes outside the group.
*/
public class SceneSummary implements Streamable
{
/** The id of this scene. */
public int sceneId;
/** The name of this scene. */
public String name;
/** The ids of the scenes to which this scene is connected via
* portals. */
public int[] neighbors;
/** The directions in which each of the neighbors lay. */
public int[] neighborDirs;
/**
* Generates a string representation of this instance.
*/
public String toString ()
{
return "[sceneId=" + sceneId + ", name=" + name +
", neighbors=" + StringUtil.toString(neighbors) +
", neighborDirs=" + StringUtil.toString(neighborDirs) + "]";
}
}
@@ -1,35 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.data;
import com.threerings.whirled.data.SceneCodes;
/**
* Contains codes used by the zone services.
*/
public interface ZoneCodes extends SceneCodes
{
/** An error code indicating that a zone identified by a particular
* zone id does not exist. Usually generated by a failed moveTo
* request. */
public static final String NO_SUCH_ZONE = "m.no_such_zone";
}
@@ -1,125 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.data;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.zone.client.ZoneService;
import com.threerings.whirled.zone.data.ZoneSummary;
/**
* Provides the implementation of the {@link ZoneService} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
public class ZoneMarshaller extends InvocationMarshaller
implements ZoneService
{
// documentation inherited
public static class ZoneMoveMarshaller extends ListenerMarshaller
implements ZoneMoveListener
{
/** The method id used to dispatch {@link #moveSucceeded}
* responses. */
public static final int MOVE_SUCCEEDED = 1;
// documentation inherited from interface
public void moveSucceeded (int arg1, PlaceConfig arg2, ZoneSummary arg3)
{
_invId = null;
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, MOVE_SUCCEEDED,
new Object[] { Integer.valueOf(arg1), arg2, arg3 }));
}
/** The method id used to dispatch {@link #moveSucceededWithScene}
* responses. */
public static final int MOVE_SUCCEEDED_WITH_SCENE = 2;
// documentation inherited from interface
public void moveSucceededWithScene (int arg1, PlaceConfig arg2, ZoneSummary arg3, SceneModel arg4)
{
_invId = null;
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, MOVE_SUCCEEDED_WITH_SCENE,
new Object[] { Integer.valueOf(arg1), arg2, arg3, arg4 }));
}
/** The method id used to dispatch {@link #moveSucceededWithUpdates}
* responses. */
public static final int MOVE_SUCCEEDED_WITH_UPDATES = 3;
// documentation inherited from interface
public void moveSucceededWithUpdates (int arg1, PlaceConfig arg2, ZoneSummary arg3, SceneUpdate[] arg4)
{
_invId = null;
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, MOVE_SUCCEEDED_WITH_UPDATES,
new Object[] { Integer.valueOf(arg1), arg2, arg3, arg4 }));
}
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case MOVE_SUCCEEDED:
((ZoneMoveListener)listener).moveSucceeded(
((Integer)args[0]).intValue(), (PlaceConfig)args[1], (ZoneSummary)args[2]);
return;
case MOVE_SUCCEEDED_WITH_SCENE:
((ZoneMoveListener)listener).moveSucceededWithScene(
((Integer)args[0]).intValue(), (PlaceConfig)args[1], (ZoneSummary)args[2], (SceneModel)args[3]);
return;
case MOVE_SUCCEEDED_WITH_UPDATES:
((ZoneMoveListener)listener).moveSucceededWithUpdates(
((Integer)args[0]).intValue(), (PlaceConfig)args[1], (ZoneSummary)args[2], (SceneUpdate[])args[3]);
return;
default:
super.dispatchResponse(methodId, args);
return;
}
}
}
/** The method id used to dispatch {@link #moveTo} requests. */
public static final int MOVE_TO = 1;
// documentation inherited from interface
public void moveTo (Client arg1, int arg2, int arg3, int arg4, ZoneService.ZoneMoveListener arg5)
{
ZoneMarshaller.ZoneMoveMarshaller listener5 = new ZoneMarshaller.ZoneMoveMarshaller();
listener5.listener = arg5;
sendRequest(arg1, MOVE_TO, new Object[] {
Integer.valueOf(arg2), Integer.valueOf(arg3), Integer.valueOf(arg4), listener5
});
}
}
@@ -1,53 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.data;
import com.samskivert.util.StringUtil;
import com.threerings.io.SimpleStreamableObject;
import com.threerings.util.Name;
/**
* The zone summary contains information on a zone, including its name and
* summary info on all of the scenes in this zone (which can be used to
* generate a map of the zone on the client).
*/
public class ZoneSummary extends SimpleStreamableObject
{
/** The zone's fully qualified unique identifier. */
public int zoneId;
/** The name of the zone. */
public Name name;
/** The summary information for all of the scenes in the zone. */
public SceneSummary[] scenes;
/**
* Generates a string representation of this instance.
*/
public String toString ()
{
return "[zoneId=" + zoneId + ", name=" + name +
", scenes=" + StringUtil.toString(scenes) + "]";
}
}
@@ -1,41 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.data;
import com.threerings.whirled.data.ScenedBodyObject;
/**
* A system that uses the zone services must provide a body object
* extension that implements this interface.
*/
public interface ZonedBodyObject extends ScenedBodyObject
{
/**
* Returns the zone id currently occupied by this body.
*/
public int getZoneId ();
/**
* Sets the zone id currently occupied by this body.
*/
public void setZoneId (int zoneId);
}
@@ -1,74 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.server;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.server.InvocationDispatcher;
import com.threerings.presents.server.InvocationException;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.zone.client.ZoneService;
import com.threerings.whirled.zone.data.ZoneMarshaller;
import com.threerings.whirled.zone.data.ZoneSummary;
/**
* Dispatches requests to the {@link ZoneProvider}.
*/
public class ZoneDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public ZoneDispatcher (ZoneProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new ZoneMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case ZoneMarshaller.MOVE_TO:
((ZoneProvider)provider).moveTo(
source,
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), ((Integer)args[2]).intValue(), (ZoneService.ZoneMoveListener)args[3]
);
return;
default:
super.dispatchRequest(source, methodId, args);
return;
}
}
}
@@ -1,104 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.server;
import com.threerings.crowd.data.BodyObject;
import com.threerings.whirled.zone.data.ZoneSummary;
import com.threerings.whirled.zone.data.ZonedBodyObject;
/**
* A zone is a collection of scenes organized into a connected group. A
* user can wander around within a zone, moving from scene to scene via
* the standard mechanisms. To move between zones, they must use a special
* mechanism (like at the dock, they can move from an island zone into
* their ship zone; or they can move from an island zone into their house
* zone). A zone provides scene summary information that can be used to
* display a map of the zone to the client.
*/
public interface ZoneManager
{
/**
* Used to notify requesters when an asynchronous zone load has
* completed (successfully or not).
*/
public static interface ResolutionListener
{
/**
* Called when a zone was successfully resolved.
*/
public void zoneWasResolved (ZoneSummary summary);
/**
* Called when a zone failed to resolve.
*/
public void zoneFailedToResolve (int zoneId, Exception reason);
}
/**
* Resolves and delivers the scene summary information for the
* requested zone. Zone resolution is an asynchronous process, which
* necessitates this callback-style interface.
*
* @param zoneId the qualified zone id of the zone to resolve.
* @param listener the listener that should be notified when the zone
* is successfully resolved or is known to have failed to resolve.
*/
public void resolveZone (int zoneId, ResolutionListener listener);
/**
* Called when a body has requested to leave a zone. The zone manager
* may return null to indicate that the body is allowed to leave the
* current zone or a string error code indicating the reason for
* denial of access (which will be propagated back to the requesting
* client).
*
* @param body the body object of the user that desires to depart
* their current zone (which can be obtained by casting the {@link
* BodyObject} to a {@link ZonedBodyObject}).
*/
public String ratifyBodyExit (BodyObject body);
/**
* Called when a body has requested to enter a zone. The zone manager
* may return null to indicate that the body is allowed access to the
* zone or a string error code indicating the reason for denial of
* access (which will be propagated back to the requesting client).
* This method is called <em>after</em> the zone is resolved so that
* the zone manager may complete the ratification process without
* blocking (which it must do).
*
* @param body the body object of the user that desires access to the
* specified zone.
* @param zoneId the id of the zone to which the user desires access.
*/
public String ratifyBodyEntry (BodyObject body, int zoneId);
/**
* Called when a body has been granted access to a zone. This method
* must not block.
*
* @param body the body object of the user that was just granted
* access to a zone.
* @param zoneId the id of the zone to which they were granted access.
*/
public void bodyDidEnterZone (BodyObject body, int zoneId);
}
@@ -1,303 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationProvider;
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.data.SceneUpdate;
import com.threerings.whirled.data.ScenedBodyObject;
import com.threerings.whirled.server.SceneManager;
import com.threerings.whirled.server.SceneRegistry;
import com.threerings.whirled.zone.Log;
import com.threerings.whirled.zone.client.ZoneService.ZoneMoveListener;
import com.threerings.whirled.zone.data.ZoneCodes;
import com.threerings.whirled.zone.data.ZoneSummary;
import com.threerings.whirled.zone.data.ZonedBodyObject;
/**
* Provides zone related services which are presently the ability to move
* from zone to zone.
*/
public class ZoneProvider
implements ZoneCodes, InvocationProvider
{
/**
* Constructs a zone provider that will interoperate with the supplied
* zone and scene registries. The zone provider will automatically be
* constructed and registered by the {@link ZoneRegistry}, which a
* zone-using system must create and initialize in their server.
*/
public ZoneProvider (LocationProvider locprov, ZoneRegistry zonereg,
SceneRegistry screg)
{
_locprov = locprov;
_zonereg = zonereg;
_screg = screg;
}
/**
* Processes a request from a client to move to a scene in a new zone.
*
* @param caller the user requesting the move.
* @param zoneId the qualified zone id of the new zone.
* @param sceneId the identifier of the new scene.
* @param sceneVer the version of the scene model currently held by
* the client.
* @param listener the entity to inform of success or failure.
*/
public void moveTo (ClientObject caller, int zoneId, int sceneId,
int sceneVer, ZoneMoveListener listener)
throws InvocationException
{
// avoid cluttering up the method declaration with final keywords
final BodyObject fsource = (BodyObject)caller;
final int fsceneId = sceneId;
final int fsceneVer = sceneVer;
final ZoneMoveListener flistener = listener;
// look up the caller's current zone id and make sure it is happy
// about their departure from the current zone
if (!(caller instanceof ZonedBodyObject)) {
Log.warning("Request to switch zones by non-ZonedBodyObject!? " +
"[clobj=" + caller.getClass() + "].");
throw new InvocationException(INTERNAL_ERROR);
}
ZonedBodyObject zcaller = (ZonedBodyObject)caller;
ZoneManager ozmgr = _zonereg.getZoneManager(zcaller.getZoneId());
if (ozmgr != null) {
String msg = ozmgr.ratifyBodyExit(fsource);
if (msg != null) {
throw new InvocationException(msg);
}
}
// look up the zone manager for the zone
ZoneManager zmgr = _zonereg.getZoneManager(zoneId);
if (zmgr == null) {
Log.warning("Requested to enter a zone for which we have no " +
"manager [user=" + fsource.who() +
", zoneId=" + zoneId + "].");
throw new InvocationException(NO_SUCH_ZONE);
}
// resolve the zone!
ZoneManager.ResolutionListener zl = new ZoneManager.ResolutionListener()
{
public void zoneWasResolved (ZoneSummary summary) {
continueMoveTo(
fsource, summary, fsceneId, fsceneVer, flistener);
}
public void zoneFailedToResolve (int zoneId, Exception reason) {
Log.warning("Unable to resolve zone [zoneId=" + zoneId +
", reason=" + reason + "].");
flistener.requestFailed(NO_SUCH_ZONE);
}
};
zmgr.resolveZone(zoneId, zl);
}
/**
* This is called after we have resolved our zone.
*/
protected void continueMoveTo (
BodyObject source, ZoneSummary summary, int sceneId, int sceneVer,
ZoneMoveListener listener)
{
// avoid cluttering up the method declaration with final keywords
final BodyObject fsource = source;
final ZoneSummary fsum = summary;
final int fsceneVer = sceneVer;
final ZoneMoveListener flistener = listener;
// give the zone manager a chance to veto the request
ZoneManager zmgr = _zonereg.getZoneManager(summary.zoneId);
String errmsg = zmgr.ratifyBodyEntry(source, summary.zoneId);
if (errmsg != null) {
listener.requestFailed(errmsg);
return;
}
// create a callback object that will handle the resolution or
// failed resolution of the scene
SceneRegistry.ResolutionListener rl = null;
rl = new SceneRegistry.ResolutionListener() {
public void sceneWasResolved (SceneManager scmgr) {
// make sure our caller is still around; under heavy load,
// clients might end their session while the scene is
// resolving
if (!fsource.isActive()) {
Log.info("Abandoning zone move, client gone " +
"[who=" + fsource.who() +
", dest=" + scmgr.where() + "].");
// No one to respond to, just mark that we're okay with it.
InvocationMarshaller.setNoResponse(flistener);
return;
}
finishMoveTo(fsource, fsum, scmgr, fsceneVer, flistener);
}
public void sceneFailedToResolve (int sceneId, Exception reason) {
Log.warning("Unable to resolve scene [sceneid=" + sceneId +
", reason=" + reason + "].");
// pretend like the scene doesn't exist to the client
flistener.requestFailed(NO_SUCH_PLACE);
}
};
// make sure the scene they are headed to is actually loaded into
// the server
_screg.resolveScene(sceneId, rl);
}
/**
* This is called after the scene to which we are moving is guaranteed
* to have been loaded into the server.
*/
protected void finishMoveTo (
BodyObject source, ZoneSummary summary, SceneManager scmgr,
int sceneVersion, ZoneMoveListener listener)
{
// move to the place object associated with this scene
PlaceObject plobj = scmgr.getPlaceObject();
int ploid = plobj.getOid();
try {
// try doing the actual move
PlaceConfig config = _locprov.moveTo(source, ploid);
// now that we've finally moved, we can update the user object
// with the new scene and zone ids
source.startTransaction();
try {
((ScenedBodyObject)source).setSceneId(scmgr.getScene().getId());
((ZonedBodyObject)source).setZoneId(summary.zoneId);
} finally {
source.commitTransaction();
}
// check to see if they need a newer version of the scene data
SceneModel model = scmgr.getScene().getSceneModel();
if (sceneVersion < model.version) {
SceneUpdate[] updates = scmgr.getUpdates(sceneVersion);
if (updates != null) {
listener.moveSucceededWithUpdates(
ploid, config, summary, updates);
} else {
listener.moveSucceededWithScene(
ploid, config, summary, model);
}
} else {
// then send the moveTo response
listener.moveSucceeded(ploid, config, summary);
}
// let the zone manager know that someone just came on in
ZoneManager zmgr = _zonereg.getZoneManager(summary.zoneId);
zmgr.bodyDidEnterZone(source, summary.zoneId);
} catch (InvocationException ie) {
listener.requestFailed(ie.getMessage());
} catch (RuntimeException re) {
Log.logStackTrace(re);
listener.requestFailed(INTERNAL_ERROR);
}
}
/**
* Ejects the specified body from their current scene and sends them a
* request to move to the specified new zone and scene. This is the
* zone-equivalent to {@link LocationProvider#moveBody}.
*
* @return null if the user was forcibly moved, or a string indicating
* the reason for denial of departure of their current zone (from
* {@link ZoneManager#ratifyBodyExit}).
*/
public String moveBody (ZonedBodyObject source, int zoneId, int sceneId)
{
if (source.getZoneId() == zoneId) {
// handle the case of moving somewhere in the same zone
_screg.sceneprov.moveBody((BodyObject) source, sceneId);
} else {
// first remove them from their old location
String reason = leaveOccupiedZone(source);
if (reason != null) {
return reason;
}
// then send a forced move notification
ZoneSender.forcedMove((BodyObject)source, zoneId, sceneId);
}
return null;
}
/**
* Ejects the specified body from their current scene and zone. This
* is the zone equivalent to {@link
* LocationProvider#leaveOccupiedPlace}.
*
* @return null if the user was forcibly moved, or a string indicating
* the reason for denial of departure of their current zone (from
* {@link ZoneManager#ratifyBodyExit}).
*/
public String leaveOccupiedZone (ZonedBodyObject source)
{
// look up the caller's current zone id and make sure it is happy
// about their departure from the current zone
ZoneManager zmgr = _zonereg.getZoneManager(source.getZoneId());
String msg;
if (zmgr != null &&
(msg = zmgr.ratifyBodyExit((BodyObject)source)) != null) {
return msg;
}
// remove them from their occupied scene
_screg.sceneprov.leaveOccupiedScene(source);
// and clear out their zone information
source.setZoneId(-1);
return null;
}
/** The entity that handles basic location changes. */
protected LocationProvider _locprov;
/** The zone registry with which we communicate. */
protected ZoneRegistry _zonereg;
/** The scene registry with which we communicate. */
protected SceneRegistry _screg;
}
@@ -1,88 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.server;
import com.samskivert.util.HashIntMap;
import com.threerings.presents.server.InvocationManager;
import com.threerings.crowd.server.PlaceRegistry;
import com.threerings.whirled.server.SceneRegistry;
import com.threerings.whirled.zone.Log;
import com.threerings.whirled.zone.util.ZoneUtil;
/**
* The zone registry takes care of mapping zone requests to the
* appropriate registered zone manager.
*/
public class ZoneRegistry
{
/** Implements the server-side of the zone-related services. */
public ZoneProvider zoneprov;
/**
* Creates a zone manager with the supplied configuration.
*/
public ZoneRegistry (InvocationManager invmgr, PlaceRegistry plreg,
SceneRegistry screg)
{
// create a zone provider and register it with the invocation
// services
zoneprov = new ZoneProvider(plreg.locprov, this, screg);
invmgr.registerDispatcher(new ZoneDispatcher(zoneprov), true);
}
/**
* Registers the supplied zone manager as the manager for the
* specified zone type. Zone types are 7 bits and managers are
* responsible for making sure they don't use a zone type that
* collides with another manager (given that we have only three zone
* types at present, this doesn't seem unreasonable).
*/
public void registerZoneManager (byte zoneType, ZoneManager manager)
{
ZoneManager old = (ZoneManager)_managers.get(zoneType);
if (old != null) {
Log.warning("Zone manager already registered with requested " +
"type [type=" + zoneType + ", old=" + old +
", new=" + manager + "].");
} else {
_managers.put(zoneType, manager);
}
}
/**
* Returns the zone manager that handles the specified zone id.
*
* @param qualifiedZoneId the qualified zone id for which the manager
* should be looked up.
*/
public ZoneManager getZoneManager (int qualifiedZoneId)
{
int zoneType = ZoneUtil.zoneType(qualifiedZoneId);
return (ZoneManager)_managers.get(zoneType);
}
/** A table of zone managers. */
protected HashIntMap _managers = new HashIntMap();
}
@@ -1,47 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationSender;
import com.threerings.whirled.zone.client.ZoneDecoder;
import com.threerings.whirled.zone.client.ZoneReceiver;
/**
* Used to issue notifications to a {@link ZoneReceiver} instance on a
* client.
*/
public class ZoneSender extends InvocationSender
{
/**
* Issues a notification that will result in a call to {@link
* ZoneReceiver#forcedMove} on a client.
*/
public static void forcedMove (
ClientObject target, int arg1, int arg2)
{
sendNotification(
target, ZoneDecoder.RECEIVER_CODE, ZoneDecoder.FORCED_MOVE,
new Object[] { Integer.valueOf(arg1), Integer.valueOf(arg2) });
}
}
@@ -1,66 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.whirled.zone.util;
/**
* Server-specific, zone-related utility functions.
*/
public class ZoneUtil
{
/**
* Composes the zone type and zone id into a qualified zone id. A
* qualified zone id is what should be passed around so that the
* server can determine the zone type from the zone id when necessary.
*/
public static int qualifyZoneId (byte zoneType, int zoneId)
{
int qualifiedZoneId = zoneType;
qualifiedZoneId <<= 24;
qualifiedZoneId |= zoneId;
return qualifiedZoneId;
}
/**
* Extracts the zone type from a qualified zone id.
*/
public static int zoneType (int qualifiedZoneId)
{
return (0xFF000000 & qualifiedZoneId) >> 24;
}
/**
* Extracts the zone id from a qualified zone id.
*/
public static int zoneId (int qualifiedZoneId)
{
return (0x00FFFFFF & qualifiedZoneId);
}
/**
* Returns an easier to read representation of the supplied qualified
* zone id: <code>type:id</code>.
*/
public static String toString (int qualifiedZoneId)
{
return zoneType(qualifiedZoneId) + ":" + zoneId(qualifiedZoneId);
}
}