Add Zone handling to vilya's actionscript side to go with scene handling. Also, handle SceneDirector's forcedMove in the same way the java side does in the face of already pending moves.
git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@910 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
@@ -173,7 +173,7 @@ public class SceneDirector extends BasicDirector
|
|||||||
var refuse :Boolean = _locdir.checkRepeatMove();
|
var refuse :Boolean = _locdir.checkRepeatMove();
|
||||||
|
|
||||||
// complain if we're over-writing a pending request
|
// complain if we're over-writing a pending request
|
||||||
if (_pendingData != null) {
|
if (movePending()) {
|
||||||
if (refuse) {
|
if (refuse) {
|
||||||
log.warning("Refusing moveTo; We have a request outstanding",
|
log.warning("Refusing moveTo; We have a request outstanding",
|
||||||
"psid", _pendingData.sceneId, "nsid", sceneId);
|
"psid", _pendingData.sceneId, "nsid", sceneId);
|
||||||
@@ -383,9 +383,26 @@ public class SceneDirector extends BasicDirector
|
|||||||
clearScene();
|
clearScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there is a pending move request.
|
||||||
|
*/
|
||||||
|
public function movePending () :Boolean
|
||||||
|
{
|
||||||
|
return (_pendingData != null);
|
||||||
|
}
|
||||||
|
|
||||||
// documentation inherited from interface
|
// documentation inherited from interface
|
||||||
public function forcedMove (sceneId :int) :void
|
public function forcedMove (sceneId :int) :void
|
||||||
{
|
{
|
||||||
|
// if we're in the middle of a move, we can't abort it or we will screw everything up, so
|
||||||
|
// just finish up what we're doing and assume that the repeated move request was the
|
||||||
|
// spurious one as it would be in the case of lag causing rapid-fire repeat requests
|
||||||
|
if (movePending()) {
|
||||||
|
log.info("Dropping forced move because we have a move pending",
|
||||||
|
"pendId", _pendingData.sceneId, "reqId", sceneId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log.info("Moving at request of server", "sceneId", sceneId);
|
log.info("Moving at request of server", "sceneId", sceneId);
|
||||||
|
|
||||||
// clear out our old scene and place data
|
// clear out our old scene and place data
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
//
|
||||||
|
// $Id: SceneDecoder.as 887 2010-01-05 22:12:02Z dhoover $
|
||||||
|
//
|
||||||
|
// Vilya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/vilya/
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.whirled.zone.client {
|
||||||
|
|
||||||
|
import com.threerings.presents.client.InvocationDecoder;
|
||||||
|
import com.threerings.whirled.zone.client.ZoneReceiver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches calls to a {@link ZoneReceiver} instance.
|
||||||
|
*/
|
||||||
|
public class ZoneDecoder extends InvocationDecoder
|
||||||
|
{
|
||||||
|
/** The generated hash code used to identify this receiver class. */
|
||||||
|
public static const RECEIVER_CODE :String =
|
||||||
|
"2d900cf54355111b4bb4befcdff42b82";
|
||||||
|
|
||||||
|
/** The method id used to dispatch {@link ZoneReceiver#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 ZoneDecoder (receiver :ZoneReceiver)
|
||||||
|
{
|
||||||
|
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 ZoneReceiver).forcedMove(args[0] as int, args[1] as int);
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
super.dispatchNotification(methodId, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
package com.threerings.whirled.zone.client {
|
||||||
|
|
||||||
|
import com.threerings.io.TypedArray;
|
||||||
|
import com.threerings.util.Log;
|
||||||
|
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.crowd.data.PlaceConfig;
|
||||||
|
|
||||||
|
import com.threerings.whirled.client.SceneDirector;
|
||||||
|
import com.threerings.whirled.client.SceneDirector_MoveHandler;
|
||||||
|
import com.threerings.whirled.data.SceneModel;
|
||||||
|
import com.threerings.whirled.data.SceneUpdate;
|
||||||
|
import com.threerings.whirled.util.WhirledContext;
|
||||||
|
|
||||||
|
import com.threerings.whirled.zone.client.ZoneService_ZoneMoveListener;
|
||||||
|
import com.threerings.whirled.zone.data.ZoneSummary;
|
||||||
|
import com.threerings.whirled.zone.util.ZoneUtil;
|
||||||
|
|
||||||
|
|
||||||
|
public class ZoneDirector extends BasicDirector
|
||||||
|
implements ZoneReceiver, ZoneService_ZoneMoveListener, SceneDirector_MoveHandler
|
||||||
|
{
|
||||||
|
private static const log :Log = Log.getLog(ZoneDirector);
|
||||||
|
|
||||||
|
// We could be streamed one of these...
|
||||||
|
SceneUpdate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a zone director with the supplied context, and delegate scene director (which the
|
||||||
|
* zone director will coordinate with when moving from scene to scene). A zone director is
|
||||||
|
* required on the client side for systems that wish to use the zone services.
|
||||||
|
*/
|
||||||
|
public function ZoneDirector (ctx :WhirledContext, scdir :SceneDirector)
|
||||||
|
{
|
||||||
|
super(ctx);
|
||||||
|
_wCtx = ctx;
|
||||||
|
_scdir = scdir;
|
||||||
|
_scdir.setMoveHandler(this);
|
||||||
|
|
||||||
|
// register for zone notifications
|
||||||
|
_wCtx.getClient().getInvocationDirector().registerReceiver(new ZoneDecoder(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the summary for the zone currently occupied by the client or null if the client does
|
||||||
|
* not currently occupy a zone (not a normal situation).
|
||||||
|
*/
|
||||||
|
public function getZoneSummary () :ZoneSummary
|
||||||
|
{
|
||||||
|
return _summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a zone observer to the list. This observer will subsequently be notified of effected
|
||||||
|
* and failed zone changes.
|
||||||
|
*/
|
||||||
|
public function addZoneObserver (observer :ZoneObserver) :void
|
||||||
|
{
|
||||||
|
_observers.add(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a zone observer from the list.
|
||||||
|
*/
|
||||||
|
public function removeZoneObserver (observer :ZoneObserver) :void
|
||||||
|
{
|
||||||
|
_observers.remove(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that this client move the specified scene in the specified zone. A request will be
|
||||||
|
* made and when the response is received, the location observers will be notified of success
|
||||||
|
* or failure.
|
||||||
|
*/
|
||||||
|
public function moveTo (zoneId :int, sceneId :int, rl :ResultListener = null) :Boolean
|
||||||
|
{
|
||||||
|
// make sure the zoneId and sceneId are valid
|
||||||
|
if (zoneId < 0 || sceneId < 0) {
|
||||||
|
log.warning("Refusing moveTo(): invalid sceneId or zoneId",
|
||||||
|
"zoneId", zoneId, "sceneId", sceneId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the requested zone is the same as our current zone, we just want a regular old moveTo
|
||||||
|
// request
|
||||||
|
if (_summary != null && zoneId == _summary.zoneId) {
|
||||||
|
return _scdir.moveTo(sceneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, we make a zoned moveTo request; prepare to move to this scene (sets up
|
||||||
|
// pending data)
|
||||||
|
if (!_scdir.prepareMoveTo(sceneId, rl)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// let our zone observers know that we're attempting to switch zones
|
||||||
|
notifyObservers(zoneId);
|
||||||
|
|
||||||
|
// check the version of our cached copy of the scene to which we're requesting to move; if
|
||||||
|
// we were unable to load it, assume a cached version of zero
|
||||||
|
var sceneVers :int = 0;
|
||||||
|
var pendingModel :SceneModel = _scdir.getPendingModel();
|
||||||
|
if (pendingModel != null) {
|
||||||
|
sceneVers = pendingModel.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
// issue a moveTo request
|
||||||
|
log.info("Issuing zoned moveTo(" + ZoneUtil.toString(zoneId) +
|
||||||
|
", " + sceneId + ", " + sceneVers + ").");
|
||||||
|
_zservice.moveTo(zoneId, sceneId, sceneVers, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected function fetchServices (client :Client) :void
|
||||||
|
{
|
||||||
|
_zservice = (ZoneService)(client.requireService(ZoneService));
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function clientDidLogoff (event :ClientEvent) :void
|
||||||
|
{
|
||||||
|
super.clientDidLogoff(event);
|
||||||
|
|
||||||
|
// clear out our business
|
||||||
|
_zservice = null;
|
||||||
|
_summary = null;
|
||||||
|
_previousZoneId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface ZoneService.ZoneMoveListener
|
||||||
|
public function moveSucceeded (placeId :int, config :PlaceConfig, summary :ZoneSummary) :void
|
||||||
|
{
|
||||||
|
if (_summary != null) {
|
||||||
|
// keep track of our previous zone info
|
||||||
|
_previousZoneId = _summary.zoneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of the summary
|
||||||
|
_summary = summary;
|
||||||
|
|
||||||
|
// pass the rest off to the standard scene transition code
|
||||||
|
_scdir.moveSucceeded(placeId, config);
|
||||||
|
|
||||||
|
// and let the zone observers know what's up
|
||||||
|
notifyObservers(summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface ZoneService.ZoneMoveListener
|
||||||
|
public function moveSucceededWithUpdates (
|
||||||
|
placeId :int, config :PlaceConfig, summary :ZoneSummary,
|
||||||
|
updates :TypedArray /* of SceneUpdate */) :void
|
||||||
|
{
|
||||||
|
// keep track of the summary
|
||||||
|
_summary = summary;
|
||||||
|
|
||||||
|
// pass the rest off to the standard scene transition code
|
||||||
|
_scdir.moveSucceededWithUpdates(placeId, config, updates);
|
||||||
|
|
||||||
|
// and let the zone observers know what's up
|
||||||
|
notifyObservers(summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface ZoneService.ZoneMoveListener
|
||||||
|
public function moveSucceededWithScene (
|
||||||
|
placeId :int, config :PlaceConfig, summary :ZoneSummary, model :SceneModel) :void
|
||||||
|
{
|
||||||
|
// keep track of the summary
|
||||||
|
_summary = summary;
|
||||||
|
|
||||||
|
// pass the rest off to the standard scene transition code
|
||||||
|
_scdir.moveSucceededWithScene(placeId, config, model);
|
||||||
|
|
||||||
|
// and let the zone observers know what's up
|
||||||
|
notifyObservers(summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface ZoneService.ZoneMoveListener
|
||||||
|
public function requestFailed (reason :String) :void
|
||||||
|
{
|
||||||
|
// let the scene director cope
|
||||||
|
_scdir.requestFailed(reason);
|
||||||
|
|
||||||
|
// and let the observers know what's up
|
||||||
|
notifyObservers(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public function forcedMove (zoneId :int, sceneId :int) :void
|
||||||
|
{
|
||||||
|
// if we're in the middle of a move, we can't abort it or we will screw everything up, so
|
||||||
|
// just finish up what we're doing and assume that the repeated move request was the
|
||||||
|
// spurious one as it would be in the case of lag causing rapid-fire repeat requests
|
||||||
|
if (_scdir.movePending()) {
|
||||||
|
log.info("Dropping forced move because we have a move pending",
|
||||||
|
"pend", _scdir.getPendingModel(), "rzId", zoneId, "rsId", sceneId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Moving at request of server",
|
||||||
|
"zoneId", zoneId, "sceneId", sceneId);
|
||||||
|
// clear out our old scene and place data
|
||||||
|
_scdir.didLeaveScene();
|
||||||
|
// move to the new zone and scene
|
||||||
|
moveTo(zoneId, sceneId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from SceneDirector.MoveHandler
|
||||||
|
public function recoverMoveTo (previousSceneId :int) :void
|
||||||
|
{
|
||||||
|
if (_summary != null) {
|
||||||
|
return; // if we're currently somewhere, just stay there
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise if we were previously in a zone/scene, try going back there
|
||||||
|
if (_previousZoneId != -1) {
|
||||||
|
moveTo(_previousZoneId, previousSceneId);
|
||||||
|
} else {
|
||||||
|
_scdir.moveTo(previousSceneId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies observers of success or failure, depending on the type of object provided as data.
|
||||||
|
*/
|
||||||
|
protected function notifyObservers (data :Object) :void
|
||||||
|
{
|
||||||
|
// let our observers know that all is well on the western front
|
||||||
|
for each (var obs :ZoneObserver in _observers) {
|
||||||
|
try {
|
||||||
|
if (data is Number) {
|
||||||
|
obs.zoneWillChange(data as Number);
|
||||||
|
} else if (data is ZoneSummary) {
|
||||||
|
obs.zoneDidChange(data as ZoneSummary);
|
||||||
|
} else {
|
||||||
|
obs.zoneChangeFailed(data as String);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e :Error) {
|
||||||
|
log.warning("Zone observer choked during notification",
|
||||||
|
"data", data, "obs", obs, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A reference to the active client context. */
|
||||||
|
protected var _wCtx :WhirledContext;
|
||||||
|
|
||||||
|
/** A reference to the scene director with which we coordinate. */
|
||||||
|
protected var _scdir :SceneDirector;
|
||||||
|
|
||||||
|
/** Provides access to zone services. */
|
||||||
|
protected var _zservice :ZoneService;
|
||||||
|
|
||||||
|
/** A reference to the zone summary for the currently occupied zone. */
|
||||||
|
protected var _summary :ZoneSummary;
|
||||||
|
|
||||||
|
/** Our zone observer list. */
|
||||||
|
protected var _observers :Array = [];
|
||||||
|
|
||||||
|
/** Our previous zone id. */
|
||||||
|
protected var _previousZoneId :int = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.threerings.whirled.zone.client {
|
||||||
|
|
||||||
|
import com.threerings.whirled.zone.data.ZoneSummary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The zone observer interface makes it possible for entities to be
|
||||||
|
* notified when the client moves to a new zone.
|
||||||
|
*/
|
||||||
|
public interface ZoneObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when we begin the process of switching to a new zone. This
|
||||||
|
* will be followed by a call to {@link #zoneDidChange} to indicate
|
||||||
|
* that the change was successful or {@link #zoneChangeFailed} if the
|
||||||
|
* change fails.
|
||||||
|
*
|
||||||
|
* @param zoneId the zone id of the zone to which we are changing.
|
||||||
|
*/
|
||||||
|
function zoneWillChange (zoneId :int) :void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we have switched to a new zone.
|
||||||
|
*
|
||||||
|
* @param summary the summary information for the new zone or null if
|
||||||
|
* we have switched to no zone.
|
||||||
|
*/
|
||||||
|
function zoneDidChange (summary :ZoneSummary) :void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called on all zone observers when a zone change request is
|
||||||
|
* rejected by the server or fails for some other reason.
|
||||||
|
*
|
||||||
|
* @param reason the reason code that explains why the zone change
|
||||||
|
* request was rejected or otherwise failed.
|
||||||
|
*/
|
||||||
|
function zoneChangeFailed (reason :String) :void;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.threerings.whirled.zone.client {
|
||||||
|
|
||||||
|
import com.threerings.presents.client.InvocationReceiver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines, for the zone services, a set of notifications delivered asynchronously by the server
|
||||||
|
* to the client.
|
||||||
|
*/
|
||||||
|
public interface ZoneReceiver extends InvocationReceiver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Used to communicate a required move notification to the client. The server will have
|
||||||
|
* removed the client from their existing scene and the client is then responsible for
|
||||||
|
* generating a {@link ZoneService#moveTo} request to move to the new scene in the specified
|
||||||
|
* zone.
|
||||||
|
*/
|
||||||
|
function forcedMove (zoneId :int, sceneId :int) :void;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,8 +66,8 @@ public class SceneSummary
|
|||||||
{
|
{
|
||||||
sceneId = ins.readInt();
|
sceneId = ins.readInt();
|
||||||
name = (ins.readField(String) as String);
|
name = (ins.readField(String) as String);
|
||||||
neighbors = TypedArray(ins.readObject());
|
neighbors = TypedArray(ins.readField(TypedArray.getJavaType(int)));
|
||||||
neighborDirs = TypedArray(ins.readObject());
|
neighborDirs = TypedArray(ins.readField(TypedArray.getJavaType(int)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// from interface Streamable
|
// from interface Streamable
|
||||||
@@ -75,8 +75,8 @@ public class SceneSummary
|
|||||||
{
|
{
|
||||||
out.writeInt(sceneId);
|
out.writeInt(sceneId);
|
||||||
out.writeField(name);
|
out.writeField(name);
|
||||||
out.writeObject(neighbors);
|
out.writeField(neighbors);
|
||||||
out.writeObject(neighborDirs);
|
out.writeField(neighborDirs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.threerings.whirled.zone.util {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server-specific, zone-related utility functions.
|
||||||
|
*/
|
||||||
|
public class ZoneUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Composes the zone type and zone id into a qualified zone id. A
|
||||||
|
* qualified zone id is what should be passed around so that the
|
||||||
|
* server can determine the zone type from the zone id when necessary.
|
||||||
|
*/
|
||||||
|
public static function qualifyZoneId (zoneType :int, zoneId :int) :int
|
||||||
|
{
|
||||||
|
var qualifiedZoneId :int = zoneType;
|
||||||
|
qualifiedZoneId <<= 24;
|
||||||
|
qualifiedZoneId |= zoneId;
|
||||||
|
return qualifiedZoneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the zone type from a qualified zone id.
|
||||||
|
*/
|
||||||
|
public static function zoneType (qualifiedZoneId :int) :int
|
||||||
|
{
|
||||||
|
return (0xFF000000 & qualifiedZoneId) >> 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the zone id from a qualified zone id.
|
||||||
|
*/
|
||||||
|
public static function zoneId (qualifiedZoneId :int) :int
|
||||||
|
{
|
||||||
|
return (0x00FFFFFF & qualifiedZoneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an easier to read representation of the supplied qualified
|
||||||
|
* zone id: <code>type:id</code>.
|
||||||
|
*/
|
||||||
|
public static function toString (qualifiedZoneId :int) :String
|
||||||
|
{
|
||||||
|
return zoneType(qualifiedZoneId) + ":" + zoneId(qualifiedZoneId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user