From 01866933ebede3ca4ddc455da077a127abab5230 Mon Sep 17 00:00:00 2001 From: Ray Greenwell Date: Fri, 23 Jun 2006 19:21:10 +0000 Subject: [PATCH] Whirled actionscript code, transferred from narya. git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@5 c613c5cb-e716-0410-b11b-feb51c14d237 --- .../whirled/client/SceneController.as | 92 +++ .../threerings/whirled/client/SceneDecoder.as | 68 +++ .../whirled/client/SceneDirector.as | 527 ++++++++++++++++++ .../client/SceneDirector_MoveHandler.as | 14 + .../whirled/client/SceneReceiver.as | 40 ++ .../threerings/whirled/client/SceneService.as | 50 ++ .../client/SceneService_SceneMoveListener.as | 50 ++ .../whirled/client/persist/SceneRepository.as | 66 +++ .../com/threerings/whirled/data/AuxModel.as | 35 ++ .../whirled/data/DefaultSceneConfig.as | 45 ++ src/as/com/threerings/whirled/data/Scene.as | 84 +++ .../com/threerings/whirled/data/SceneCodes.as | 34 ++ .../com/threerings/whirled/data/SceneImpl.as | 124 +++++ .../whirled/data/SceneMarshaller.as | 53 ++ .../SceneMarshaller_SceneMoveMarshaller.as | 81 +++ .../com/threerings/whirled/data/SceneModel.as | 118 ++++ .../threerings/whirled/data/SceneObject.as | 29 + .../threerings/whirled/data/SceneUpdate.as | 153 +++++ .../whirled/data/ScenedBodyObject.as | 40 ++ .../spot/client/SpotSceneController.as | 37 ++ .../whirled/spot/client/SpotSceneDirector.as | 472 ++++++++++++++++ .../whirled/spot/client/SpotService.as | 85 +++ .../threerings/whirled/spot/data/Cluster.as | 76 +++ .../whirled/spot/data/ClusterObject.as | 66 +++ .../whirled/spot/data/ClusteredBodyObject.as | 48 ++ .../threerings/whirled/spot/data/Location.as | 52 ++ .../threerings/whirled/spot/data/Portal.as | 141 +++++ .../whirled/spot/data/SceneLocation.as | 86 +++ .../threerings/whirled/spot/data/SpotCodes.as | 58 ++ .../whirled/spot/data/SpotMarshaller.as | 100 ++++ .../threerings/whirled/spot/data/SpotScene.as | 81 +++ .../whirled/spot/data/SpotSceneImpl.as | 152 +++++ .../whirled/spot/data/SpotSceneModel.as | 115 ++++ .../whirled/spot/data/SpotSceneObject.as | 162 ++++++ .../whirled/util/NoSuchSceneError.as | 43 ++ .../threerings/whirled/util/SceneFactory.as | 40 ++ .../com/threerings/whirled/util/UpdateList.as | 99 ++++ .../threerings/whirled/util/WhirledContext.as | 38 ++ 38 files changed, 3654 insertions(+) create mode 100644 src/as/com/threerings/whirled/client/SceneController.as create mode 100644 src/as/com/threerings/whirled/client/SceneDecoder.as create mode 100644 src/as/com/threerings/whirled/client/SceneDirector.as create mode 100644 src/as/com/threerings/whirled/client/SceneDirector_MoveHandler.as create mode 100644 src/as/com/threerings/whirled/client/SceneReceiver.as create mode 100644 src/as/com/threerings/whirled/client/SceneService.as create mode 100644 src/as/com/threerings/whirled/client/SceneService_SceneMoveListener.as create mode 100644 src/as/com/threerings/whirled/client/persist/SceneRepository.as create mode 100644 src/as/com/threerings/whirled/data/AuxModel.as create mode 100644 src/as/com/threerings/whirled/data/DefaultSceneConfig.as create mode 100644 src/as/com/threerings/whirled/data/Scene.as create mode 100644 src/as/com/threerings/whirled/data/SceneCodes.as create mode 100644 src/as/com/threerings/whirled/data/SceneImpl.as create mode 100644 src/as/com/threerings/whirled/data/SceneMarshaller.as create mode 100644 src/as/com/threerings/whirled/data/SceneMarshaller_SceneMoveMarshaller.as create mode 100644 src/as/com/threerings/whirled/data/SceneModel.as create mode 100644 src/as/com/threerings/whirled/data/SceneObject.as create mode 100644 src/as/com/threerings/whirled/data/SceneUpdate.as create mode 100644 src/as/com/threerings/whirled/data/ScenedBodyObject.as create mode 100644 src/as/com/threerings/whirled/spot/client/SpotSceneController.as create mode 100644 src/as/com/threerings/whirled/spot/client/SpotSceneDirector.as create mode 100644 src/as/com/threerings/whirled/spot/client/SpotService.as create mode 100644 src/as/com/threerings/whirled/spot/data/Cluster.as create mode 100644 src/as/com/threerings/whirled/spot/data/ClusterObject.as create mode 100644 src/as/com/threerings/whirled/spot/data/ClusteredBodyObject.as create mode 100644 src/as/com/threerings/whirled/spot/data/Location.as create mode 100644 src/as/com/threerings/whirled/spot/data/Portal.as create mode 100644 src/as/com/threerings/whirled/spot/data/SceneLocation.as create mode 100644 src/as/com/threerings/whirled/spot/data/SpotCodes.as create mode 100644 src/as/com/threerings/whirled/spot/data/SpotMarshaller.as create mode 100644 src/as/com/threerings/whirled/spot/data/SpotScene.as create mode 100644 src/as/com/threerings/whirled/spot/data/SpotSceneImpl.as create mode 100644 src/as/com/threerings/whirled/spot/data/SpotSceneModel.as create mode 100644 src/as/com/threerings/whirled/spot/data/SpotSceneObject.as create mode 100644 src/as/com/threerings/whirled/util/NoSuchSceneError.as create mode 100644 src/as/com/threerings/whirled/util/SceneFactory.as create mode 100644 src/as/com/threerings/whirled/util/UpdateList.as create mode 100644 src/as/com/threerings/whirled/util/WhirledContext.as diff --git a/src/as/com/threerings/whirled/client/SceneController.as b/src/as/com/threerings/whirled/client/SceneController.as new file mode 100644 index 00000000..b337aaca --- /dev/null +++ b/src/as/com/threerings/whirled/client/SceneController.as @@ -0,0 +1,92 @@ +// +// $Id: SceneController.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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 + override public function init (ctx :CrowdContext, config :PlaceConfig) :void + { + super.init(ctx, config); + _wctx = WhirledContext(ctx); + } + + // documentation inherited + override public function willEnterPlace (plobj :PlaceObject) :void + { + super.willEnterPlace(plobj); + plobj.addListener(_updateListener); + } + + // documentation inherited + override public function didLeavePlace (plobj :PlaceObject) :void + { + 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 function sceneUpdated (update :SceneUpdate) :void + { + // 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 var _updateListener :MessageListener = new MessageAdapter( + function (event :MessageEvent) :void { + if (event.getName() == SceneCodes.SCENE_UPDATE) { + sceneUpdated(event.getArgs()[0] as SceneUpdate); + } + } + ); + + protected var _wctx :WhirledContext; +} +} diff --git a/src/as/com/threerings/whirled/client/SceneDecoder.as b/src/as/com/threerings/whirled/client/SceneDecoder.as new file mode 100644 index 00000000..aad2769f --- /dev/null +++ b/src/as/com/threerings/whirled/client/SceneDecoder.as @@ -0,0 +1,68 @@ +// +// $Id: SceneDecoder.java 3914 2006-03-06 21:51:59Z mdb $ +// +// 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 const RECEIVER_CODE :String = + "c4d0cf66b81a6e83d119b2d607725651"; + + /** The method id used to dispatch {@link SceneReceiver#forcedMove} + * notifications. */ + public static const FORCED_MOVE :int = 1; + + /** + * Creates a decoder that may be registered to dispatch invocation + * service notifications to the specified receiver. + */ + public function SceneDecoder (receiver :SceneReceiver) + { + this.receiver = receiver; + } + + // documentation inherited + override public function getReceiverCode () :String + { + return RECEIVER_CODE; + } + + // documentation inherited + override public function dispatchNotification (methodId :int, args :Array) :void + { + switch (methodId) { + case FORCED_MOVE: + (receiver as SceneReceiver).forcedMove(args[0] as int); + return; + + default: + super.dispatchNotification(methodId, args); + } + } +} +} diff --git a/src/as/com/threerings/whirled/client/SceneDirector.as b/src/as/com/threerings/whirled/client/SceneDirector.as new file mode 100644 index 00000000..7343cf96 --- /dev/null +++ b/src/as/com/threerings/whirled/client/SceneDirector.as @@ -0,0 +1,527 @@ +// +// $Id: SceneDirector.java 4088 2006-05-04 00:39:46Z mjohnson $ +// +// 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 flash.errors.IOError; +import flash.errors.IllegalOperationError; + +import com.threerings.util.HashMap; +import com.threerings.util.ResultListener; + +import com.threerings.io.TypedArray; + +import com.threerings.presents.client.BasicDirector; +import com.threerings.presents.client.Client; +import com.threerings.presents.client.ClientEvent; +import com.threerings.presents.data.InvocationCodes; + +import com.threerings.crowd.client.LocationDirector; +import com.threerings.crowd.client.LocationDirector_FailureHandler; +import com.threerings.crowd.client.LocationObserver; +import com.threerings.crowd.data.PlaceConfig; + +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.NoSuchSceneError; +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). + * + *

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 LocationDirector_FailureHandler, + SceneReceiver, SceneService_SceneMoveListener +{ + private static const log :Log = Log.getLog(SceneDirector); + + /** + * 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 function SceneDirector ( + ctx :WhirledContext, locdir :LocationDirector, + screp :SceneRepository, fact :SceneFactory) + { + super(ctx); + + // we'll need these for later + _wctx = 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 + _wctx.getClient().getInvocationDirector().registerReceiver( + new SceneDecoder(this)); + } + + /** + * Set the scene repository. + */ + public function setSceneRepository (screp :SceneRepository) :void + { + _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 function getScene () :Scene + { + 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 function moveTo (sceneId :int) :Boolean + { + // 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 + var sceneVers :int = 0; + if (_pendingModel != null) { + sceneVers = _pendingModel.version; + } + + // issue a moveTo request + log.info("Issuing moveTo(" + sceneId + ", " + sceneVers + ")."); + _sservice.moveTo(_wctx.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 function prepareMoveTo (sceneId :int, rl :ResultListener) :Boolean + { + // 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 + var refuse :Boolean = _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 function getPendingModel () :SceneModel + { + return _pendingModel; + } + + // documentation inherited from interface SceneService_SceneMoveListener + public function moveSucceeded (placeId :int, config :PlaceConfig) :void + { + // 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 SceneService_SceneMoveListener + public function moveSucceededWithUpdates ( + placeId :int, config :PlaceConfig, updates :TypedArray) :void + { + log.info("Got updates [placeId=" + placeId + ", config=" + config + + ", updates=" + updates + "]."); + + // apply the updates to our cached scene + var model :SceneModel = loadSceneModel(_pendingSceneId); + var failure :Boolean = false; + for each (var update :SceneUpdate in updates) { + try { + update.validate(model); + } catch (ise :IllegalOperationError) { + log.warning("Scene update failed validation [model=" + model + + ", update=" + update + + ", error=" + ise.getMessage() + "]."); + failure = true; + break; + } + + try { + update.apply(model); + } catch (e :Error) { + log.warning("Failure applying scene update [model=" + model + + ", update=" + update + "]."); + log.logStackTrace(e); + failure = true; + break; + } + } + + if (failure) { + // delete the now half-booched scene model from the repository + try { + _screp.deleteSceneModel(_pendingSceneId); + } catch (ioe :IOError) { + 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(InvocationCodes.INTERNAL_ERROR); + return; + } + + // store the updated scene in the repository + try { + _screp.storeSceneModel(model); + } catch (ioe :IOError) { + 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 SceneService-SceneMoveListener + public function moveSucceededWithScene ( + placeId :int, config :PlaceConfig, model :SceneModel) :void + { + 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 (ioe :IOError) { + log.warning("Failed to update repository with new version " + + "[sceneId=" + model.sceneId + + ", nvers=" + model.version + "]."); + log.logStackTrace(ioe); + } + + // update our scene cache + _scache.put(model.sceneId, model); + + // and pass through to the normal move succeeded handler + moveSucceeded(placeId, config); + } + + // documentation inherited from interface + public function requestFailed (reason :String) :void + { + // clear out our pending request oid + var sceneId :int = _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 function didLeaveScene () :void + { + // let the location director know what's up + _locdir.didLeavePlace(); + + // clear out our own scene state + clearScene(); + } + + // documentation inherited from interface + public function forcedMove (sceneId :int) :void + { + 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 function setMoveHandler (handler :SceneDirector_MoveHandler) :void + { + 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 + * moveTo request. + */ + public function recoverFailedMove (placeId :int) :void + { + // we'll need this momentarily + var sceneId :int = _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 function clearScene () :void + { + // 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 function loadSceneModel (sceneId :int) :SceneModel + { + // first look in the model cache + var model :SceneModel = (_scache.get(sceneId) as SceneModel); + + // load from the repository if it's not cached + if (model == null) { + try { + model = _screp.loadSceneModel(sceneId); + _scache.put(sceneId, model); + + } catch (nsse :NoSuchSceneError) { + // nothing special here, just fall through and return null + + } catch (ioe :IOError) { + // complain first, then return null + log.warning("Error loading scene [scid=" + sceneId + + ", error=" + ioe + "]."); + } + } + + return model; + } + + // documentation inherited + override public function clientDidLogoff (event :ClientEvent) :void + { + super.clientDidLogoff(event); + + // clear out our business + clearScene(); + _scache.clear(); + _pendingSceneId = -1; + _pendingModel = null; + _previousSceneId = -1; + _sservice = null; + } + + // documentation inherited + override protected function fetchServices (client :Client) :void + { + // get a handle on our scene service + _sservice = (client.requireService(SceneService) as SceneService); + } + + /** Access to general client services. */ + protected var _wctx :WhirledContext; + + /** Access to our scene services. */ + protected var _sservice :SceneService; + + /** The client's active location director. */ + protected var _locdir :LocationDirector; + + /** The entity via which we load scene data. */ + protected var _screp :SceneRepository; + + /** The entity we use to create scenes from scene models. */ + protected var _fact :SceneFactory; + + /** A cache of scene model information. */ + protected var _scache :HashMap = new HashMap(); // TODO: LRUHashMap(5) + + /** The display scene object for the scene we currently occupy. */ + protected var _scene :Scene; + + /** The scene model for the scene we currently occupy. */ + protected var _model :SceneModel; + + /** The id of the scene we currently occupy. */ + protected var _sceneId :int = -1; + + /** Our most recent copy of the scene model for the scene we're about + * to enter. */ + protected var _pendingModel :SceneModel; + + /** The id of the scene for which we have an outstanding moveTo + * request, or -1 if we have no outstanding request. */ + protected var _pendingSceneId :int = -1; + + /** The id of the scene we previously occupied. */ + protected var _previousSceneId :int = -1; + + /** Reference to our move handler. */ + protected var _moveHandler :SceneDirector_MoveHandler = null; +} +} diff --git a/src/as/com/threerings/whirled/client/SceneDirector_MoveHandler.as b/src/as/com/threerings/whirled/client/SceneDirector_MoveHandler.as new file mode 100644 index 00000000..35eb712e --- /dev/null +++ b/src/as/com/threerings/whirled/client/SceneDirector_MoveHandler.as @@ -0,0 +1,14 @@ +package com.threerings.whirled.client { + +/** + * Used to recover from a problem after a completed moveTo. + */ +public interface SceneDirector_MoveHandler +{ + /** + * Should instruct the client to move the last known working + * location (as well as clean up after the failed moveTo request). + */ + function recoverMoveTo (sceneId :int) :void; +} +} diff --git a/src/as/com/threerings/whirled/client/SceneReceiver.as b/src/as/com/threerings/whirled/client/SceneReceiver.as new file mode 100644 index 00000000..3941b91e --- /dev/null +++ b/src/as/com/threerings/whirled/client/SceneReceiver.as @@ -0,0 +1,40 @@ +// +// $Id: SceneReceiver.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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. + */ + function forcedMove (sceneId :int) :void +} +} diff --git a/src/as/com/threerings/whirled/client/SceneService.as b/src/as/com/threerings/whirled/client/SceneService.as new file mode 100644 index 00000000..1e8294bc --- /dev/null +++ b/src/as/com/threerings/whirled/client/SceneService.as @@ -0,0 +1,50 @@ +// +// $Id: SceneService.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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 +{ + /** + * 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. + */ + function moveTo ( + client :Client, sceneId :int, version :int, + listener :SceneService_SceneMoveListener) :void; +} +} diff --git a/src/as/com/threerings/whirled/client/SceneService_SceneMoveListener.as b/src/as/com/threerings/whirled/client/SceneService_SceneMoveListener.as new file mode 100644 index 00000000..2809b5f5 --- /dev/null +++ b/src/as/com/threerings/whirled/client/SceneService_SceneMoveListener.as @@ -0,0 +1,50 @@ +package com.threerings.whirled.client { + +import com.threerings.presents.client.InvocationListener; + +import com.threerings.io.TypedArray; + +import com.threerings.crowd.data.PlaceConfig; + +import com.threerings.whirled.data.SceneModel; +import com.threerings.whirled.data.SceneUpdate; + +/** + * Used to communicate the response to a {@link #moveTo} request. + */ +public interface SceneService_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. + */ + function moveSucceeded (placeId :int, config :PlaceConfig) :void; + + /** + * 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. + */ + function moveSucceededWithUpdates ( + placeId :int, config :PlaceConfig, + updates :TypedArray /*of SceneUpdate*/) :void; + + /** + * 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. + */ + function moveSucceededWithScene ( + placeId :int, config :PlaceConfig, model :SceneModel) :void; +} +} diff --git a/src/as/com/threerings/whirled/client/persist/SceneRepository.as b/src/as/com/threerings/whirled/client/persist/SceneRepository.as new file mode 100644 index 00000000..2d5aa949 --- /dev/null +++ b/src/as/com/threerings/whirled/client/persist/SceneRepository.as @@ -0,0 +1,66 @@ +// +// $Id: SceneRepository.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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 flash.errors.IOError; + +import com.threerings.whirled.data.SceneModel; +import com.threerings.whirled.util.NoSuchSceneError; + +/** + * 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. + */ + function loadSceneModel (sceneId :int) :SceneModel; + //throws IOException, NoSuchSceneException; + + /** + * Updates or inserts this scene model as appropriate. + * + * @exception IOException thrown if an error occurs attempting to + * access the repository. + */ + function storeSceneModel (model :SceneModel) :void; + //throws IOException; + + /** + * Deletes the specified scene model from the repository. + * + * @exception IOException thrown if an error occurs attempting to + * access the repository. + */ + function deleteSceneModel (sceneId :int) :void; + //throws IOException; +} +} diff --git a/src/as/com/threerings/whirled/data/AuxModel.as b/src/as/com/threerings/whirled/data/AuxModel.as new file mode 100644 index 00000000..e7adf1f5 --- /dev/null +++ b/src/as/com/threerings/whirled/data/AuxModel.as @@ -0,0 +1,35 @@ +// +// $Id: AuxModel.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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.util.Cloneable; + +import com.threerings.io.Streamable; + +/** + * An interface that must be implemented by auxiliary scene models. + */ +public interface AuxModel extends Streamable, Cloneable +{ + // no new methods +} +} diff --git a/src/as/com/threerings/whirled/data/DefaultSceneConfig.as b/src/as/com/threerings/whirled/data/DefaultSceneConfig.as new file mode 100644 index 00000000..89b07839 --- /dev/null +++ b/src/as/com/threerings/whirled/data/DefaultSceneConfig.as @@ -0,0 +1,45 @@ +// +// $Id: DefaultSceneConfig.java 4026 2006-04-18 01:32:41Z mdb $ +// +// 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. + * + *

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 function getManagerClassName () :String + { + return "com.threerings.whirled.server.SceneManager"; + } +} +} diff --git a/src/as/com/threerings/whirled/data/Scene.as b/src/as/com/threerings/whirled/data/Scene.as new file mode 100644 index 00000000..5301da69 --- /dev/null +++ b/src/as/com/threerings/whirled/data/Scene.as @@ -0,0 +1,84 @@ +// +// $Id: Scene.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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. + */ + function getId () :int; + + /** + * Returns the human readable name of this scene. + */ + function getName () :String; + + /** + * Returns the version number of this scene. + */ + function getVersion () :int; + + /** + * 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. + */ + function getPlaceConfig () :PlaceConfig; + + /** + * Sets this scene's unique identifier. + */ + function setId (sceneId :int) :void; + + /** + * Sets the human readable name of this scene. + */ + function setName (name :String) :void; + + /** + * Sets this scene's version number. + */ + function setVersion (version :int) :void; + + /** + * 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. + */ + function updateReceived (update :SceneUpdate) :void; + + /** + * Returns the scene model from which this scene was created. + */ + function getSceneModel () :SceneModel; +} +} diff --git a/src/as/com/threerings/whirled/data/SceneCodes.as b/src/as/com/threerings/whirled/data/SceneCodes.as new file mode 100644 index 00000000..b28ec338 --- /dev/null +++ b/src/as/com/threerings/whirled/data/SceneCodes.as @@ -0,0 +1,34 @@ +// +// $Id: SceneCodes.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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 class SceneCodes extends LocationCodes +{ + /** The message identifier for scene update messages. */ + public static const SCENE_UPDATE :String = "scene_update"; +} +} diff --git a/src/as/com/threerings/whirled/data/SceneImpl.as b/src/as/com/threerings/whirled/data/SceneImpl.as new file mode 100644 index 00000000..c2d1f402 --- /dev/null +++ b/src/as/com/threerings/whirled/data/SceneImpl.as @@ -0,0 +1,124 @@ +// +// $Id: SceneImpl.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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; + +/** + * 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 function SceneImpl ( + model :SceneModel = null, config :PlaceConfig = null) + { + if (model != null) { + _model = model; + _config = config; + + } else { + _model = SceneModel.blankSceneModel(); + } + } + + // documentation inherited + public function getId () :int + { + return _model.sceneId; + } + + // documentation inherited + public function getName () :String + { + return _model.name; + } + + // documentation inherited + public function getVersion () :int + { + return _model.version; + } + + // documentation inherited + public function getPlaceConfig () :PlaceConfig + { + return _config; + } + + // documentation inherited from interface + public function setId (sceneId :int) :void + { + _model.sceneId = sceneId; + } + + // documentation inherited from interface + public function setName (name :String) :void + { + _model.name = name; + } + + // documentation inherited from interface + public function setVersion (version :int) :void + { + _model.version = version; + } + + // documentation inherited from interface + public function updateReceived (update :SceneUpdate) :void + { + try { + // validate and apply the update + update.validate(_model); + update.apply(_model); + } catch (e :Error) { + var log :Log = Log.getLog(this); + log.warning("Error applying update [scene=" + this + + ", update=" + update + "]."); + log.logStackTrace(e); + } + } + + // documentation inherited from interface + public function getSceneModel () :SceneModel + { + return _model; + } + + /** + * Generates a string representation of this instance. + */ + public function toString () :String + { + return "[model=" + _model + ", config=" + _config + "]"; + } + + /** A reference to our scene model. */ + protected var _model :SceneModel; + + /** A reference to our place configuration. */ + protected var _config :PlaceConfig; +} +} diff --git a/src/as/com/threerings/whirled/data/SceneMarshaller.as b/src/as/com/threerings/whirled/data/SceneMarshaller.as new file mode 100644 index 00000000..f98098c6 --- /dev/null +++ b/src/as/com/threerings/whirled/data/SceneMarshaller.as @@ -0,0 +1,53 @@ +// +// $Id: SceneMarshaller.java 3793 2005-12-21 02:12:57Z ray $ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2005 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.util.Integer; + +import com.threerings.presents.client.Client; +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.whirled.client.SceneService; +import com.threerings.whirled.client.SceneService_SceneMoveListener; + +/** + * 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 +{ + /** The method id used to dispatch {@link #moveTo} requests. */ + public static const MOVE_TO :int = 1; + + // documentation inherited from interface + public function moveTo (arg1 :Client, arg2 :int, arg3 :int, arg4 :SceneService_SceneMoveListener) :void + { + var listener4 :SceneMarshaller_SceneMoveMarshaller = new SceneMarshaller_SceneMoveMarshaller(); + listener4.listener = arg4; + sendRequest(arg1, MOVE_TO, [ new Integer(arg2), new Integer(arg3), listener4 ]); + } + +} +} diff --git a/src/as/com/threerings/whirled/data/SceneMarshaller_SceneMoveMarshaller.as b/src/as/com/threerings/whirled/data/SceneMarshaller_SceneMoveMarshaller.as new file mode 100644 index 00000000..23e48546 --- /dev/null +++ b/src/as/com/threerings/whirled/data/SceneMarshaller_SceneMoveMarshaller.as @@ -0,0 +1,81 @@ +package com.threerings.whirled.data { + +import com.threerings.util.Integer; + +import com.threerings.io.TypedArray; + +import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller; + +import com.threerings.crowd.data.PlaceConfig; + +import com.threerings.presents.dobj.InvocationResponseEvent; + +import com.threerings.whirled.client.SceneService_SceneMoveListener; +import com.threerings.whirled.data.SceneModel; +import com.threerings.whirled.data.SceneUpdate; + +public class SceneMarshaller_SceneMoveMarshaller extends InvocationMarshaller_ListenerMarshaller + implements SceneService_SceneMoveListener +{ + /** The method id used to dispatch {@link #moveSucceeded} + * responses. */ + public static const MOVE_SUCCEEDED :int = 1; + + // documentation inherited from interface SceneService_SceneMoveListener + public function moveSucceeded (arg1 :int, arg2 :PlaceConfig) :void + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, MOVE_SUCCEEDED, + [ new Integer(arg1), arg2 ])); + } + + /** The method id used to dispatch {@link #moveSucceededWithScene} + * responses. */ + public static const MOVE_SUCCEEDED_WITH_SCENE :int = 2; + + // documentation inherited from interface SceneService_SceneMoveListener + public function moveSucceededWithScene (arg1 :int, arg2 :PlaceConfig, arg3 :SceneModel) :void + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, MOVE_SUCCEEDED_WITH_SCENE, + [ new Integer(arg1), arg2, arg3 ])); + } + + /** The method id used to dispatch {@link #moveSucceededWithUpdates} + * responses. */ + public static const MOVE_SUCCEEDED_WITH_UPDATES :int = 3; + + // documentation inherited from interface SceneService_SceneMoveListener + public function moveSucceededWithUpdates (arg1 :int, arg2 :PlaceConfig, arg3 :TypedArray /* of SceneUpdate */) :void + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, MOVE_SUCCEEDED_WITH_UPDATES, + [ new Integer(arg1), arg2, arg3 ])); + } + + // documentation inherited + override public function dispatchResponse (methodId :int, args :Array) :void + { + switch (methodId) { + case MOVE_SUCCEEDED: + (listener as SceneService_SceneMoveListener).moveSucceeded( + (args[0] as Integer).value, args[1] as PlaceConfig); + return; + + case MOVE_SUCCEEDED_WITH_SCENE: + (listener as SceneService_SceneMoveListener).moveSucceededWithScene( + (args[0] as Integer).value, args[1] as PlaceConfig, args[2] as SceneModel); + return; + + case MOVE_SUCCEEDED_WITH_UPDATES: + (listener as SceneService_SceneMoveListener).moveSucceededWithUpdates( + (args[0] as Integer).value, args[1] as PlaceConfig, args[2] as TypedArray); + return; + + default: + super.dispatchResponse(methodId, args); + } + } +} + +} diff --git a/src/as/com/threerings/whirled/data/SceneModel.as b/src/as/com/threerings/whirled/data/SceneModel.as new file mode 100644 index 00000000..8a843dfc --- /dev/null +++ b/src/as/com/threerings/whirled/data/SceneModel.as @@ -0,0 +1,118 @@ +// +// $Id: SceneModel.java 3726 2005-10-11 19:17:43Z ray $ +// +// 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.util.ClassUtil; +import com.threerings.util.Cloneable; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; +import com.threerings.io.Streamable; +import com.threerings.io.TypedArray; + +/** + * 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}. + * + *

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 + implements Streamable, Cloneable +{ + /** This scene's unique identifier. */ + public var sceneId :int; + + /** The human readable name of this scene. */ + public var name :String; + + /** 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 var version :int; + + /** Auxiliary scene model information. */ + public var auxModels :TypedArray = + new TypedArray("[Lcom.threerings.whirled.data.AuxModel;"); + + /** + * Adds the specified auxiliary model to this scene model. + */ + public function addAuxModel (auxModel :AuxModel) :void + { + auxModels.push(auxModel); + } + + // documentation inherited from interface Cloneable + public function clone () :Object + { + var clazz :Class = ClassUtil.getClass(this); + var model :SceneModel = new clazz(); + + for each (var aux :AuxModel in auxModels) { + model.addAuxModel(aux.clone() as AuxModel); + } + return model; + } + + // documentation inherited from interface Streamable + public function writeObject (out :ObjectOutputStream) :void + { + out.writeInt(sceneId); + out.writeField(name); + out.writeInt(version); + out.writeObject(auxModels); + } + + // documentation inherited from interface Streamable + public function readObject (ins :ObjectInputStream) :void + { + sceneId = ins.readInt(); + name = (ins.readField(String) as String); + version = ins.readInt(); + auxModels = (ins.readObject() as TypedArray); + } + + /** + * Creates and returns a blank scene model. + */ + public static function blankSceneModel () :SceneModel + { + var model :SceneModel = new SceneModel(); + populateBlankSceneModel(model); + return model; + } + + /** + * Populates a blank scene model with blank values. + */ + protected static function populateBlankSceneModel (model :SceneModel) :void + { + model.sceneId = -1; + model.name = ""; + model.version = 0; + } +} +} diff --git a/src/as/com/threerings/whirled/data/SceneObject.as b/src/as/com/threerings/whirled/data/SceneObject.as new file mode 100644 index 00000000..38db6249 --- /dev/null +++ b/src/as/com/threerings/whirled/data/SceneObject.as @@ -0,0 +1,29 @@ +// +// $Id: SceneObject.java 3288 2004-12-28 03:51:29Z mdb $ +// +// 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 +{ +} +} diff --git a/src/as/com/threerings/whirled/data/SceneUpdate.as b/src/as/com/threerings/whirled/data/SceneUpdate.as new file mode 100644 index 00000000..e59f01f6 --- /dev/null +++ b/src/as/com/threerings/whirled/data/SceneUpdate.as @@ -0,0 +1,153 @@ +// +// $Id: SceneUpdate.java 3760 2005-11-15 03:08:23Z ray $ +// +// 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 flash.errors.IllegalOperationError; + +import com.threerings.util.StringBuilder; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; +import com.threerings.io.Streamable; + +/** + * 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 function init (targetId :int, targetVersion :int) :void + { + _targetId = targetId; + _targetVersion = targetVersion; + } + + /** + * Returns the scene id for which this update is appropriate. + */ + public function getSceneId () :int + { + return _targetId; + } + + /** + * Returns the scene version for which this update is appropriate. + */ + public function getSceneVersion () :int + { + 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 function validate (model :SceneModel) :void + //throws IllegalStateException + { + if (model.sceneId != _targetId) { + var errmsg :String = "Wrong target scene, expected id " + + _targetId + " got id " + model.sceneId; + throw new IllegalOperationError(errmsg); + } + if (model.version != _targetVersion) { + var errmsg :String = "Target scene not proper version, expected " + + _targetVersion + " got " + model.version; + throw new IllegalOperationError(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 super.apply. + */ + public function apply (model :SceneModel) :void + { + // 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.getLog(this).warning("Egads! This scene has been updated two" + + " billion times [model=" + model + ", update=" + this + "]."); + } + } + + // documentation inherited from interface Streamable + public function writeObject (out :ObjectOutputStream) :void + //throws IOException + { + out.writeInt(_targetId); + out.writeInt(_targetVersion); + } + + // documentation inherited from interface Streamable + public function readObject (ins :ObjectInputStream) :void + //throws IOException, ClassNotFoundException + { + _targetId = ins.readInt(); + _targetVersion = ins.readInt(); + } + + /** + * Generates a string representation of this instance. + */ + public function toString () :String + { + var buf :StringBuilder = new StringBuilder("["); + toStringBuf(buf); + return buf.append("]").toString(); + } + + /** + * An extensible mechanism for generating a string representation of + * this instance. + */ + protected function toStringBuf (buf :StringBuilder) :void + { + buf.append("sceneId=").append(_targetId); + buf.append(", version=").append(_targetVersion); + } + + /** The version number of the scene on which we operate. */ + protected var _targetId :int; + + /** The version number of the scene on which we operate. */ + protected var _targetVersion :int; +} +} diff --git a/src/as/com/threerings/whirled/data/ScenedBodyObject.as b/src/as/com/threerings/whirled/data/ScenedBodyObject.as new file mode 100644 index 00000000..edd4747c --- /dev/null +++ b/src/as/com/threerings/whirled/data/ScenedBodyObject.as @@ -0,0 +1,40 @@ +// +// $Id: ScenedBodyObject.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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. + */ + function getSceneId () :int; + + /** + * Sets the scene id currently occupied by this body. + */ + function setSceneId (sceneId :int) :void; +} +} diff --git a/src/as/com/threerings/whirled/spot/client/SpotSceneController.as b/src/as/com/threerings/whirled/spot/client/SpotSceneController.as new file mode 100644 index 00000000..39c969e4 --- /dev/null +++ b/src/as/com/threerings/whirled/spot/client/SpotSceneController.as @@ -0,0 +1,37 @@ +// +// $Id: SpotSceneController.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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 +{ +} +} diff --git a/src/as/com/threerings/whirled/spot/client/SpotSceneDirector.as b/src/as/com/threerings/whirled/spot/client/SpotSceneDirector.as new file mode 100644 index 00000000..4e6dff13 --- /dev/null +++ b/src/as/com/threerings/whirled/spot/client/SpotSceneDirector.as @@ -0,0 +1,472 @@ +// +// $Id: SpotSceneDirector.java 3890 2006-02-24 19:51:11Z mthomas $ +// +// 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.util.ResultListener; + +import com.threerings.presents.client.BasicDirector; +import com.threerings.presents.client.Client; +import com.threerings.presents.client.ClientEvent; +import com.threerings.presents.client.ConfirmAdapter; +import com.threerings.presents.client.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.ObjectAccessError; +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.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 Subscriber, AttributeChangeListener +{ + private static const log :Log = Log.getLog(SpotSceneDirector); + + /** + * 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 function SpotSceneDirector ( + ctx :WhirledContext, locdir :LocationDirector, scdir :SceneDirector) + { + super(ctx); + + _wctx = ctx; + _scdir = scdir; + + // wire ourselves up to hear about leave place notifications + locdir.addLocationObserver(new LocationAdapter(null, + function (place :PlaceObject) :void { + handleDeparture(); + })); + } + + /** + * Configures this spot scene director with a chat director, with + * which it will coordinate to implement cluster chatting. + */ + public function setChatDirector (chatdir :ChatDirector) :void + { + _chatdir = chatdir; + } + + /** + * Returns our current location unless we have a location change + * pending, in which case our pending location is returned. + */ + public function getIntendedLocation () :Location + { + 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 function traversePortal ( + portalId :int, + rl :com.threerings.util.ResultListener = null) :Boolean + { + // look up the destination scene and location + var scene :SpotScene = (_scdir.getScene() as SpotScene); + 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 + var sceneId :int = _scdir.getScene().getId(); + var sbobj :ScenedBodyObject = + (_wctx.getClient().getClientObject() as ScenedBodyObject); + 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 + var dest :Portal = scene.getPortal(portalId); + if (dest == null) { + log.warning("Requested to traverse non-existent portal " + + "[portalId=" + portalId + ", portals=" + 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 + var sceneVer :int = 0; + var pendingModel :SceneModel = _scdir.getPendingModel(); + if (pendingModel != null) { + sceneVer = pendingModel.version; + } + + // issue a traversePortal request + log.info("Issuing traversePortal(" + + sceneId + ", " + dest + ", " + sceneVer + ")."); + _sservice.traversePortal( + _wctx.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 function changeLocation ( + loc :Location, listener :com.threerings.util.ResultListener) :void + { + // 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; + } + + var scene :SpotScene = (_scdir.getScene() as SpotScene); + 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 Error("m.cant_get_there")); + } + return; + } + + var sceneId :int = _scdir.getScene().getId(); + log.info("Sending changeLocation request [scid=" + sceneId + + ", loc=" + loc + "]."); + + _pendingLoc = (loc.clone() as Location); + var clist :ConfirmAdapter = new ConfirmAdapter( + function () :void { + _location = _pendingLoc; + _pendingLoc = null; + if (listener != null) { + listener.requestCompleted(_location); + } + }, + function (reason :String) :void { + _pendingLoc = null; + if (listener != null) { + listener.requestFailed(new Error(reason)); + } + }); + _sservice.changeLocation(_wctx.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 function joinCluster ( + froid :int, listener :com.threerings.util.ResultListener) :void + { + var scene :SpotScene = (_scdir.getScene() as SpotScene); + 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 Error("m.cant_get_there")); + } + return; + } + + log.info("Joining cluster [friend=" + froid + "]."); + + _sservice.joinCluster(_wctx.getClient(), froid, new ConfirmAdapter( + function () :void { + if (listener != null) { + listener.requestCompleted(null); + } + }, + function (reason :String) :void { + if (listener != null) { + listener.requestFailed(new Error(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 function requestClusterSpeak ( + message :String, mode :int = ChatCodes.DEFAULT_MODE) :Boolean + { + // make sure we're currently in a scene + var scene :SpotScene = (_scdir.getScene() as SpotScene); + 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(_wctx.getClient(), message, mode); + } + return true; + } + + // documentation inherited from interface + public function objectAvailable (object :DObject) :void + { + clearCluster(false); + var oid :int = object.getOid(); + if (oid != _self.getClusterOid()) { + // we got it too late, just unsubscribe + var omgr :DObjectManager = _wctx.getDObjectManager(); + omgr.unsubscribeFromObject(oid, this); + } else { + // it's our new cluster! + _clobj = object; + if (_chatdir != null) { + _chatdir.addAuxiliarySource(object, + SpotCodes.CLUSTER_CHAT_TYPE); + } + } + } + + // documentation inherited from interface + public function requestFailed (oid :int, cause :ObjectAccessError) :void + { + log.warning("Unable to subscribe to cluster chat object " + + "[oid=" + oid + ", cause=" + cause + "]."); + } + + // documentation inherited from interface + public function attributeChanged (event :AttributeChangedEvent) :void + { + if (event.getName() == _self.getClusterField() && + event.getValue() != event.getOldValue()) { + maybeUpdateCluster(); + } + } + + // documentation inherited + override public function clientDidLogon (event :ClientEvent) :void + { + super.clientDidLogon(event); + + var clientObj :ClientObject = event.getClient().getClientObject(); + if (clientObj is ClusteredBodyObject) { + // listen to the client object + clientObj.addListener(this); + _self = (clientObj as ClusteredBodyObject); + + // we may need to subscribe to a cluster due to session resumption + maybeUpdateCluster(); + } + } + + // documentation inherited + override public function clientObjectDidChange (event :ClientEvent) :void + { + super.clientObjectDidChange(event); + + // listen to the client object + var clientObj :ClientObject = event.getClient().getClientObject(); + clientObj.addListener(this); + _self = (clientObj as ClusteredBodyObject); + } + + // documentation inherited + override public function clientDidLogoff (event :ClientEvent) :void + { + super.clientDidLogoff(event); + + // clear out our business + _location = null; + _pendingLoc = null; + _sservice = null; + clearCluster(true); + + // stop listening to the client object + event.getClient().getClientObject().removeListener(this); + _self = null; + } + + // documentation inherited + override protected function fetchServices (client :Client) :void + { + _sservice = (client.requireService(SpotService) as SpotService); + } + + /** + * Clean up after a few things when we depart from a scene. + */ + protected function handleDeparture () :void + { + // 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 function maybeUpdateCluster () :void + { + var cloid :int = _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) { + var omgr :DObjectManager = _wctx.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 function clearCluster (force :Boolean) :void + { + if (_clobj != null && + (force || (_clobj.getOid() != _self.getClusterOid()))) { + if (_chatdir != null) { + _chatdir.removeAuxiliarySource(_clobj); + } + var omgr :DObjectManager = _wctx.getDObjectManager(); + omgr.unsubscribeFromObject(_clobj.getOid(), this); + _clobj = null; + } + } + + /** The active client context. */ + protected var _wctx :WhirledContext; + + /** Access to spot scene services. */ + protected var _sservice :SpotService; + + /** The scene director with which we are cooperating. */ + protected var _scdir :SceneDirector; + + /** A casted reference to our clustered body object. */ + protected var _self :ClusteredBodyObject; + + /** A reference to the chat director with which we coordinate. */ + protected var _chatdir :ChatDirector; + + /** The location we currently occupy. */ + protected var _location :Location; + + /** The location to which we have an outstanding change location + * request. */ + protected var _pendingLoc :Location; + + /** The cluster chat object for the cluster we currently occupy. */ + protected var _clobj :DObject; +} +} diff --git a/src/as/com/threerings/whirled/spot/client/SpotService.as b/src/as/com/threerings/whirled/spot/client/SpotService.as new file mode 100644 index 00000000..7308f81a --- /dev/null +++ b/src/as/com/threerings/whirled/spot/client/SpotService.as @@ -0,0 +1,85 @@ +// +// $Id: SpotService.java 3363 2005-02-22 18:54:48Z mdb $ +// +// 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. + */ + function traversePortal ( + client :Client, sceneId :int, portalId :int, destSceneVer :int, + listener :SceneService_SceneMoveListener) :void; + + /** + * 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. + */ + function changeLocation ( + client :Client, sceneId :int, loc :Location, + listener :ConfirmListener) :void; + + /** + * 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. + */ + function joinCluster ( + client :Client, friendOid :int, listener :ConfirmListener) :void; + + /** + * 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.). + */ + function clusterSpeak (client :Client, message :String, mode :int) :void; +} +} diff --git a/src/as/com/threerings/whirled/spot/data/Cluster.as b/src/as/com/threerings/whirled/spot/data/Cluster.as new file mode 100644 index 00000000..795476d1 --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/Cluster.as @@ -0,0 +1,76 @@ +// +// $Id: Cluster.java 3310 2005-01-24 23:08:21Z mdb $ +// +// 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 flash.geom.Rectangle; + +import com.threerings.io.Streamable; +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; + +import com.threerings.presents.dobj.DSet_Entry; + +/** + * 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 var clusterOid :int; + + // documentation inherited from interface DSet_Entry + public function getKey () :Object + { + return clusterOid; + } + + // documentation inherited from interface Streamable + public function writeObject (out :ObjectOutputStream) :void + { + out.writeInt(x); + out.writeInt(y); + out.writeInt(width); + out.writeInt(height); + out.writeInt(clusterOid); + } + + // documentation inherited from interface Streamable + public function readObject (ins :ObjectInputStream) :void + { + x = ins.readInt(); + y = ins.readInt(); + width = ins.readInt(); + height = ins.readInt(); + clusterOid = ins.readInt(); + } + + /** + * Generates a string representation of this instance. + */ + public function toString () :String + { + return super.toString() + ", clusterOid=" + clusterOid; + } +} +} diff --git a/src/as/com/threerings/whirled/spot/data/ClusterObject.as b/src/as/com/threerings/whirled/spot/data/ClusterObject.as new file mode 100644 index 00000000..b99b30fe --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/ClusterObject.as @@ -0,0 +1,66 @@ +// +// $Id: ClusterObject.java 3288 2004-12-28 03:51:29Z mdb $ +// +// 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 +{ + // AUTO-GENERATED: FIELDS START + /** The field name of the occupants field. */ + public static const OCCUPANTS :String = "occupants"; + // AUTO-GENERATED: FIELDS END + + /** + * Tracks the oid of the body objects that occupy this cluster. + */ + public var occupants :OidList = new OidList(); + + // AUTO-GENERATED: METHODS START + /** + * Requests that oid be added to the occupants + * oid list. The list will not change until the event is actually + * propagated through the system. + */ + public function addToOccupants (oid :int) :void + { + requestOidAdd(OCCUPANTS, oid); + } + + /** + * Requests that oid be removed from the + * occupants oid list. The list will not change until the + * event is actually propagated through the system. + */ + public function removeFromOccupants (oid :int) :void + { + requestOidRemove(OCCUPANTS, oid); + } + // AUTO-GENERATED: METHODS END +} +} diff --git a/src/as/com/threerings/whirled/spot/data/ClusteredBodyObject.as b/src/as/com/threerings/whirled/spot/data/ClusteredBodyObject.as new file mode 100644 index 00000000..2a1958c4 --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/ClusteredBodyObject.as @@ -0,0 +1,48 @@ +// +// $Id: ClusteredBodyObject.java 3310 2005-01-24 23:08:21Z mdb $ +// +// 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. + */ + function getClusterField () :String; + + /** + * Returns the oid of the cluster to which this user currently + * belongs. + */ + function getClusterOid () :int; + + /** + * Sets the oid of the cluster to which this user currently belongs. + */ + function setClusterOid (clusterOid :int) :void; +} +} diff --git a/src/as/com/threerings/whirled/spot/data/Location.as b/src/as/com/threerings/whirled/spot/data/Location.as new file mode 100644 index 00000000..5a5c0dc9 --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/Location.as @@ -0,0 +1,52 @@ +// +// $Id: Location.java 3726 2005-10-11 19:17:43Z ray $ +// +// 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.Streamable; + +import com.threerings.util.Cloneable; +import com.threerings.util.Hashable; + +/** + * Contains information on a scene occupant's position and orientation. + */ +public interface Location extends Cloneable, Streamable, Hashable +{ + /** + * Get a new Location instance that is equals() to this one but that + * has an orientation facing the opposite direction. + */ + function getOpposite () :Location; + + /** + * Two locations are equivalent if they specify the same location + * and orientation. + */ + function equivalent (other :Location) :Boolean; + + /** Two locations are equals by coordinates only. */ + //function equals (other :Object) :Boolean; + + /** The hashcode should be based on coordinates only. */ + //function hashCode () :int; +} +} diff --git a/src/as/com/threerings/whirled/spot/data/Portal.as b/src/as/com/threerings/whirled/spot/data/Portal.as new file mode 100644 index 00000000..5805661f --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/Portal.as @@ -0,0 +1,141 @@ +// +// $Id: Portal.java 4072 2006-04-28 01:34:02Z ray $ +// +// 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.util.ClassUtil; +import com.threerings.util.Cloneable; + +import com.threerings.io.Streamable; +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; + +import com.threerings.util.Hashable; + +/** + * 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 + implements Streamable, Hashable, Cloneable +{ + /** This portal's unique identifier. */ + public var portalId :int; + + /** The location of the portal. Typically this is a base Location (2d) + * class, but different games could use a different subclass of + * Location. */ + public var loc :Location; + + /** The scene identifier of the scene to which a body will exit when + * they "use" this portal. */ + public var targetSceneId :int; + + /** The portal identifier of the portal at which a body will enter + * the target scene when they "use" this portal. */ + public var targetPortalId :int; + + /** + * Returns a location instance configured with the location and + * orientation of this portal. + */ + public function getLocation () :Location + { + return (loc.clone() as Location); + } + + /** + * 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 function getOppLocation () :Location + { + 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 function isValid () :Boolean + { + return (targetSceneId > 0) && (targetPortalId > 0); + } + + // documentation inherited from interface Streamable + public function writeObject (out :ObjectOutputStream) :void + { + out.writeShort(portalId); + out.writeObject(loc); + out.writeInt(targetSceneId); + out.writeShort(targetPortalId); + } + + // documentation inherited from interface Streamable + public function readObject (ins :ObjectInputStream) :void + { + portalId = ins.readShort(); + loc = (ins.readObject() as Location); + targetSceneId = ins.readInt(); + targetPortalId = ins.readShort(); + } + + // documentation inherited from interface Cloneable + public function clone () :Object + { + var clazz :Class = ClassUtil.getClass(this); + var p :Portal = new clazz(); + p.portalId = portalId; + p.loc = loc; + p.targetSceneId = targetSceneId; + p.targetPortalId = targetPortalId; + return p; + } + + // documentation inherited from interface Hashable + public function equals (other :Object) :Boolean + { + return (other is Portal) && + ((other as Portal).portalId == portalId); + } + + // documentation inherited from interface Hashable + public function hashCode () :int + { + return portalId; + } + + public function toString () :String + { + return "Portal[id=" + portalId + ", destScene=" + targetSceneId + + ", loc=" + loc + "]."; + } +} +} diff --git a/src/as/com/threerings/whirled/spot/data/SceneLocation.as b/src/as/com/threerings/whirled/spot/data/SceneLocation.as new file mode 100644 index 00000000..0469cb2e --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/SceneLocation.as @@ -0,0 +1,86 @@ +// +// $Id: SceneLocation.java 3310 2005-01-24 23:08:21Z mdb $ +// +// 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.util.Hashable; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; + +import com.threerings.presents.dobj.DSet_Entry; + +/** + * Extends {@link Location} with the data and functionality needed to + * represent a particular user's location in a scene. + */ +public class SceneLocation + implements DSet_Entry, Hashable +{ + /** The oid of the body that occupies this location. */ + public var bodyOid :int; + + /** The actual location, which is interpreted by the display system. */ + public var loc :Location; + + /** + * Creates a scene location with the specified information. + */ + public function SceneLocation (loc :Location = null, bodyOid :int = 0) + { + this.loc = loc; + this.bodyOid = bodyOid; + } + + // documentation inherited from interface DSet_Entry + public function getKey () :Object + { + return bodyOid; + } + + // documentation inherited from interface Hashable + public function equals (other :Object) :Boolean + { + return (other is SceneLocation) && + this.loc.equals((other as SceneLocation).loc); + } + + // documentation inherited from interface Hashable + public function hashCode () :int + { + return loc.hashCode(); + } + + // documentation inherited from superinterface Streamable + public function writeObject (out :ObjectOutputStream) :void + { + out.writeInt(bodyOid); + out.writeObject(loc); + } + + // documentation inherited from superinterface Streamable + public function readObject (ins :ObjectInputStream) :void + { + bodyOid = ins.readInt(); + loc = (ins.readObject() as Location); + } +} +} diff --git a/src/as/com/threerings/whirled/spot/data/SpotCodes.as b/src/as/com/threerings/whirled/spot/data/SpotCodes.as new file mode 100644 index 00000000..c5ad11f2 --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/SpotCodes.as @@ -0,0 +1,58 @@ +// +// $Id: SpotCodes.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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 class SpotCodes extends SceneCodes /*, ChatCodes */ +{ + /** An error code indicating that the portal specified in a + * traversePortal request does not exist. */ + public static const NO_SUCH_PORTAL :String = "m.no_such_portal"; + + /** An error code indicating that a location is occupied. Usually + * generated by a failed changeLoc request. */ + public static const LOCATION_OCCUPIED :String = "m.location_occupied"; + + /** An error code indicating that a location is not valid. Usually + * generated by a failed changeLoc request. */ + public static const INVALID_LOCATION :String = "m.invalid_location"; + + /** An error code indicating that a cluster is not valid. Usually + * generated by a failed joinCluster request. */ + public static const NO_SUCH_CLUSTER :String = "m.no_such_cluster"; + + /** An error code indicating that a cluster is full. Usually generated + * by a failed joinCluster request. */ + public static const CLUSTER_FULL :String = "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 const CLUSTER_CHAT_TYPE :String = "clusterChat"; +} +} diff --git a/src/as/com/threerings/whirled/spot/data/SpotMarshaller.as b/src/as/com/threerings/whirled/spot/data/SpotMarshaller.as new file mode 100644 index 00000000..2ae4762f --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/SpotMarshaller.as @@ -0,0 +1,100 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2005 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.util.Byte; +import com.threerings.util.Integer; + +import com.threerings.presents.client.Client; +import com.threerings.presents.client.InvocationService; +import com.threerings.presents.client.ConfirmListener; +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.data.InvocationMarshaller_ConfirmMarshaller; +import com.threerings.presents.dobj.InvocationResponseEvent; +import com.threerings.whirled.client.SceneService; +import com.threerings.whirled.client.SceneService_SceneMoveListener; +import com.threerings.whirled.data.SceneMarshaller_SceneMoveMarshaller; +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 const CHANGE_LOCATION :int = 1; + + // documentation inherited from interface + public function changeLocation (arg1 :Client, arg2 :int, arg3 :Location, arg4 :ConfirmListener) :void + { + var listener4 :InvocationMarshaller_ConfirmMarshaller = new InvocationMarshaller_ConfirmMarshaller(); + listener4.listener = arg4; + sendRequest(arg1, CHANGE_LOCATION, [ + new Integer(arg2), arg3, listener4 + ]); + } + + /** The method id used to dispatch {@link #clusterSpeak} requests. */ + public static const CLUSTER_SPEAK :int = 2; + + // documentation inherited from interface + public function clusterSpeak (arg1 :Client, arg2 :String, arg3 :int) :void + { + sendRequest(arg1, CLUSTER_SPEAK, [ + arg2, new Byte(arg3) + ]); + } + + /** The method id used to dispatch {@link #joinCluster} requests. */ + public static const JOIN_CLUSTER :int = 3; + + // documentation inherited from interface + public function joinCluster (arg1 :Client, arg2 :int, arg3 :ConfirmListener) :void + { + var listener3 :InvocationMarshaller_ConfirmMarshaller = new InvocationMarshaller_ConfirmMarshaller(); + listener3.listener = arg3; + sendRequest(arg1, JOIN_CLUSTER, [ + new Integer(arg2), listener3 + ]); + } + + /** The method id used to dispatch {@link #traversePortal} requests. */ + public static const TRAVERSE_PORTAL :int = 4; + + // documentation inherited from interface + public function traversePortal (arg1 :Client, arg2 :int, arg3 :int, arg4 :int, arg5 :SceneService_SceneMoveListener) :void + { + var listener5 :SceneMarshaller_SceneMoveMarshaller = new SceneMarshaller_SceneMoveMarshaller(); + listener5.listener = arg5; + sendRequest(arg1, TRAVERSE_PORTAL, [ + new Integer(arg2), new Integer(arg3), new Integer(arg4), listener5 + ]); + } + +} +} diff --git a/src/as/com/threerings/whirled/spot/data/SpotScene.as b/src/as/com/threerings/whirled/spot/data/SpotScene.as new file mode 100644 index 00000000..33feb162 --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/SpotScene.as @@ -0,0 +1,81 @@ +// +// $Id: SpotScene.java 3451 2005-03-31 19:40:55Z mdb $ +// +// 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.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. + */ + function getPortal (portalId :int) :Portal; + + /** + * Returns the number of portals in this scene. + */ + function getPortalCount () :int; + + /** + * Returns an iterator over the portals in this scene. + */ + function getPortals () :Iterator; + + /** + * Returns the portal id that should be assigned to the next portal + * added to this scene. + */ + function getNextPortalId () :int; + + /** + * 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. + */ + function getDefaultEntrance () :Portal; + + /** + * 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}. + */ + function addPortal (portal :Portal) :void; + + /** + * Removes the specified portal from the scene. + */ + function removePortal (portal :Portal) :void; + + /** + * Sets the default entrance in this scene, immediately making the + * requisite modifications to the underlying scene model. + */ + function setDefaultEntrance (portal :Portal) :void; +} +} diff --git a/src/as/com/threerings/whirled/spot/data/SpotSceneImpl.as b/src/as/com/threerings/whirled/spot/data/SpotSceneImpl.as new file mode 100644 index 00000000..1cb2e87b --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/SpotSceneImpl.as @@ -0,0 +1,152 @@ +// +// $Id: SpotSceneImpl.java 3451 2005-03-31 19:40:55Z mdb $ +// +// 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.util.ArrayIterator; +import com.threerings.util.Iterator; +import com.threerings.util.HashMap; + +/** + * 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 function SpotSceneImpl (model :SpotSceneModel = null) + { + if (model != null) { + _smodel = model; + readPortals(); + + } else { + _smodel = new SpotSceneModel(); + } + } + + protected function readPortals () :void + { + _portals.clear(); + for each (var port :Portal in _smodel.portals) { + _portals.put(port.portalId, port); + } + } + + // documentation inherited from interface + public function getPortal (portalId :int) :Portal + { + return (_portals.get(portalId) as Portal); + } + + // documentation inherited from interface + public function getPortalCount () :int + { + return _portals.size(); + } + + // documentation inherited from interface + public function getPortals () :Iterator + { + return new ArrayIterator(_portals.values()); + } + + // documentation inherited from interface + public function getNextPortalId () :int + { + // compute a new portal id for our friend the portal + for (var ii :int = 1; ii < MAX_PORTAL_ID; ii++) { + if (!_portals.containsKey(ii)) { + return ii; + } + } + return -1; + } + + // documentation inherited from interface + public function getDefaultEntrance () :Portal + { + return getPortal(_smodel.defaultEntranceId); + } + + // documentation inherited from interface + public function addPortal (portal :Portal) :void + { + if (portal.portalId <= 0) { + Log.getLog(this).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 function removePortal (portal :Portal) :void + { + // 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 function setDefaultEntranceId (defaultEntranceId :int) :void + { + _smodel.defaultEntranceId = defaultEntranceId; + } + + // documentation inherited from interface + public function setDefaultEntrance (portal :Portal) :void + { + _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 function updateReceived () :void + { + readPortals(); + } + + /** A casted reference to our scene model. */ + protected var _smodel :SpotSceneModel; + + /** A mapping from portal id to portal. */ + protected var _portals :HashMap = new HashMap(); + + /** We don't allow more than ~32k portals in a scene. Things would + * slow down *way* before we got there. */ + protected static const MAX_PORTAL_ID :int = int(Math.pow(2, 15) - 1); +} +} diff --git a/src/as/com/threerings/whirled/spot/data/SpotSceneModel.as b/src/as/com/threerings/whirled/spot/data/SpotSceneModel.as new file mode 100644 index 00000000..c793e342 --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/SpotSceneModel.as @@ -0,0 +1,115 @@ +// +// $Id: SpotSceneModel.java 3726 2005-10-11 19:17:43Z ray $ +// +// 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.util.ClassUtil; + +import com.threerings.io.Streamable; +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; +import com.threerings.io.TypedArray; + +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 + implements Streamable, AuxModel +{ + /** An array containing all portals in this scene. */ + public var portals :TypedArray = + new TypedArray(TypedArray.getJavaType(Portal)); + + /** 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 var defaultEntranceId :int = -1; + + /** + * Adds a portal to this scene model. + */ + public function addPortal (portal :Portal) :void + { + portals.push(portal); + } + + /** + * Removes a portal from this model. + */ + public function removePortal (portal :Portal) :void + { + for (var ii :int = 0; ii < portals.length; ii++) { + if (portal.equals(portals[ii])) { + portals.splice(ii, 1); + return; + } + } + } + + // documentation inherited from superinterface Cloneable + public function clone () :Object + { + var clazz :Class = ClassUtil.getClass(this); + var model :SpotSceneModel = new clazz(); + + for each (var portal :Portal in portals) { + model.portals.push(portal.clone()); + } + return model; + } + + // documentation inherited from interface Streamable + public function writeObject (out :ObjectOutputStream) :void + { + out.writeObject(portals); + out.writeInt(defaultEntranceId); + } + + // documentation inherited from interface Streamable + public function readObject (ins :ObjectInputStream) :void + { + portals = (ins.readObject() as TypedArray); + defaultEntranceId = ins.readInt(); + } + + /** + * Locates and returns the {@link SpotSceneModel} among the auxiliary + * scene models associated with the supplied scene + * model. null is returned if no spot scene model could + * be found. + */ + public static function getSceneModel (model :SceneModel) :SpotSceneModel + { + for each (var aux :AuxModel in model.auxModels) { + if (aux is SpotSceneModel) { + return (aux as SpotSceneModel); + } + } + return null; + } +} +} diff --git a/src/as/com/threerings/whirled/spot/data/SpotSceneObject.as b/src/as/com/threerings/whirled/spot/data/SpotSceneObject.as new file mode 100644 index 00000000..25d30c09 --- /dev/null +++ b/src/as/com/threerings/whirled/spot/data/SpotSceneObject.as @@ -0,0 +1,162 @@ +// +// $Id: SpotSceneObject.java 3300 2005-01-08 22:05:00Z ray $ +// +// 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.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; + +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 occupantLocs field. */ + public static const OCCUPANT_LOCS :String = "occupantLocs"; + + /** The field name of the clusters field. */ + public static const CLUSTERS :String = "clusters"; + // AUTO-GENERATED: FIELDS END + + /** A distributed set containing {@link SceneLocation} records for all + * occupants of this scene. */ + public var occupantLocs :DSet = new DSet(); + + /** Contains information on all {@link Cluster}s in this scene. */ + public var clusters :DSet = new DSet(); + + // AUTO-GENERATED: METHODS START + /** + * Requests that the specified entry be added to the + * occupantLocs set. The set will not change until the event is + * actually propagated through the system. + */ + public function addToOccupantLocs (elem :DSet_Entry) :void + { + requestEntryAdd(OCCUPANT_LOCS, elem); + } + + /** + * Requests that the entry matching the supplied key be removed from + * the occupantLocs set. The set will not change until the + * event is actually propagated through the system. + */ + public function removeFromOccupantLocs (key :Object) :void + { + requestEntryRemove(OCCUPANT_LOCS, key); + } + + /** + * Requests that the specified entry be updated in the + * occupantLocs set. The set will not change until the event is + * actually propagated through the system. + */ + public function updateOccupantLocs (elem :DSet_Entry) :void + { + requestEntryUpdate(OCCUPANT_LOCS, elem); + } + + /** + * Requests that the occupantLocs 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 function setOccupantLocs (value :DSet) :void + { + requestAttributeChange(OCCUPANT_LOCS, value, this.occupantLocs); + this.occupantLocs = value; + } + + /** + * Requests that the specified entry be added to the + * clusters set. The set will not change until the event is + * actually propagated through the system. + */ + public function addToClusters (elem :DSet_Entry) :void + { + requestEntryAdd(CLUSTERS, elem); + } + + /** + * Requests that the entry matching the supplied key be removed from + * the clusters set. The set will not change until the + * event is actually propagated through the system. + */ + public function removeFromClusters (key :Object) :void + { + requestEntryRemove(CLUSTERS, key); + } + + /** + * Requests that the specified entry be updated in the + * clusters set. The set will not change until the event is + * actually propagated through the system. + */ + public function updateClusters (elem :DSet_Entry) :void + { + requestEntryUpdate(CLUSTERS, elem); + } + + /** + * Requests that the clusters 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 function setClusters (value :DSet) :void + { + requestAttributeChange(CLUSTERS, value, this.clusters); + this.clusters = value; + } + // AUTO-GENERATED: METHODS END + + // documentation inherited + override public function writeObject (out :ObjectOutputStream) :void + { + super.writeObject(out); + + out.writeObject(occupantLocs); + out.writeObject(clusters); + } + + // documentation inherited + override public function readObject (ins :ObjectInputStream) :void + { + super.readObject(ins); + + occupantLocs = (ins.readObject() as DSet); + clusters = (ins.readObject() as DSet); + } +} +} diff --git a/src/as/com/threerings/whirled/util/NoSuchSceneError.as b/src/as/com/threerings/whirled/util/NoSuchSceneError.as new file mode 100644 index 00000000..1ddb9fb9 --- /dev/null +++ b/src/as/com/threerings/whirled/util/NoSuchSceneError.as @@ -0,0 +1,43 @@ +// +// $Id: NoSuchSceneError.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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 NoSuchSceneError extends Error +{ + public function NoSuchSceneError (sceneid :int) + { + super("No such scene [sceneid=" + sceneid + "]"); + _sceneid = sceneid; + } + + public function getSceneId () :int + { + return _sceneid; + } + + protected var _sceneid :int; +} +} diff --git a/src/as/com/threerings/whirled/util/SceneFactory.as b/src/as/com/threerings/whirled/util/SceneFactory.as new file mode 100644 index 00000000..8c7da365 --- /dev/null +++ b/src/as/com/threerings/whirled/util/SceneFactory.as @@ -0,0 +1,40 @@ +// +// $Id: SceneFactory.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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. + */ + function createScene (model :SceneModel, config :PlaceConfig) :Scene; +} +} diff --git a/src/as/com/threerings/whirled/util/UpdateList.as b/src/as/com/threerings/whirled/util/UpdateList.as new file mode 100644 index 00000000..4b8ad5a6 --- /dev/null +++ b/src/as/com/threerings/whirled/util/UpdateList.as @@ -0,0 +1,99 @@ +// +// $Id: UpdateList.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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.whirled.data.SceneUpdate; + +/** + * A list specialized for storing {@link SceneUpdate} objects. + */ +public class UpdateList +{ + public function UpdateList () + { + _updates = new Array(); + } + + /** + * 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 function addUpdate (update :SceneUpdate) :void + { + if (_minVersion == -1) { + // this is our first update, so we initialize our min version + _minVersion = update.getSceneVersion(); + + } else { + var gotVersion :int = update.getSceneVersion(); + var expVersion :int = _minVersion + _updates.length; + if (gotVersion > expVersion) { + Log.getLog(this).warning("Update continuity broken, " + + "flushing list [got=" + update + ", expect=" + expVersion + + ", ucount=" + _updates.length + "]."); + // flush out our old updates and start anew from here + _updates.length = 0; + _minVersion = expVersion; + + } else if (gotVersion < expVersion) { + // we somehow got an update that's older than updates we + // already have? wick wick wack + throw new IllegalArgumentError("Invalid update version " + + "[want=" + expVersion + ", got=" + update + "]"); + } + } + _updates.push(update); + } + + /** + * Returns all of the updates that should be applied to a scene with + * the specified version to bring it up to date. null 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 function getUpdates (fromVersion :int) :Array /*of SceneUpdate*/ + { + if (_minVersion == -1 || fromVersion < _minVersion) { + return null; + } + + var offset :int = fromVersion - _minVersion; + return _updates.slice(offset); + } + + /** + * Returns true if the supplied actual scene version is in accordance + * with the updates contained in this list. + */ + public function validate (sceneVersion :int) :Boolean + { + return ((_minVersion == -1) || // we have no updates + (_minVersion + _updates.length == sceneVersion)); + } + + protected var _updates :Array; + protected var _minVersion :int = -1; +} +} diff --git a/src/as/com/threerings/whirled/util/WhirledContext.as b/src/as/com/threerings/whirled/util/WhirledContext.as new file mode 100644 index 00000000..dc63de53 --- /dev/null +++ b/src/as/com/threerings/whirled/util/WhirledContext.as @@ -0,0 +1,38 @@ +// +// $Id: WhirledContext.java 3099 2004-08-27 02:21:06Z mdb $ +// +// 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. + */ + function getSceneDirector () :SceneDirector; +} +}