More.
Made ObserverList work with a passed-in function, actionscript style. I might reconsider some earlier decisions to go away from ObserverList. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3952 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -24,6 +24,7 @@ package com.threerings.crowd.chat.client {
|
|||||||
import mx.collections.ArrayCollection;
|
import mx.collections.ArrayCollection;
|
||||||
|
|
||||||
import com.threerings.util.ArrayUtil;
|
import com.threerings.util.ArrayUtil;
|
||||||
|
import com.threerings.util.ResultListener;
|
||||||
import com.threerings.util.SimpleMap;
|
import com.threerings.util.SimpleMap;
|
||||||
|
|
||||||
import com.threerings.presents.client.BasicDirector;
|
import com.threerings.presents.client.BasicDirector;
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// $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.crowd.chat.data {
|
||||||
|
|
||||||
|
import com.threerings.util.Byte;
|
||||||
|
|
||||||
|
import com.threerings.crowd.chat.client.SpeakService;
|
||||||
|
import com.threerings.presents.client.Client;
|
||||||
|
import com.threerings.presents.data.InvocationMarshaller;
|
||||||
|
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the implementation of the {@link SpeakService} 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 SpeakMarshaller extends InvocationMarshaller
|
||||||
|
implements SpeakService
|
||||||
|
{
|
||||||
|
/** The method id used to dispatch {@link #speak} requests. */
|
||||||
|
public static const SPEAK :int = 1;
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public function speak (arg1 :Client, arg2 :String, arg3 :int) :void
|
||||||
|
{
|
||||||
|
sendRequest(arg1, SPEAK, [ arg2, new Byte(arg3) ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,642 @@
|
|||||||
|
//
|
||||||
|
// $Id: LocationDirector.java 3638 2005-06-30 00:02:13Z 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.crowd.client {
|
||||||
|
|
||||||
|
// TODO: class is in progress of conversion
|
||||||
|
|
||||||
|
import com.threerings.util.ObserverList;
|
||||||
|
import com.threerings.util.ResultListener;
|
||||||
|
|
||||||
|
import com.threerings.presents.client.BasicDirector;
|
||||||
|
import com.threerings.presents.client.Client;
|
||||||
|
|
||||||
|
import com.threerings.presents.dobj.DObject;
|
||||||
|
import com.threerings.presents.dobj.ObjectAccessError;
|
||||||
|
import com.threerings.presents.dobj.Subscriber;
|
||||||
|
|
||||||
|
import com.threerings.crowd.Log;
|
||||||
|
import com.threerings.crowd.data.BodyObject;
|
||||||
|
import com.threerings.crowd.data.LocationCodes;
|
||||||
|
import com.threerings.crowd.data.PlaceConfig;
|
||||||
|
import com.threerings.crowd.data.PlaceObject;
|
||||||
|
import com.threerings.crowd.util.CrowdContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location director provides a means by which entities on the client
|
||||||
|
* can request to move from place to place and can be notified if other
|
||||||
|
* entities have caused the client to move to a new place. It also
|
||||||
|
* provides a mechanism for ratifying a request to move to a new place
|
||||||
|
* before actually issuing the request.
|
||||||
|
*/
|
||||||
|
public class LocationDirector extends BasicDirector
|
||||||
|
implements LocationCodes, Subscriber, LocationReceiver,
|
||||||
|
LocationService.MoveListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Used to recover from a moveTo request that was accepted but
|
||||||
|
* resulted in a failed attempt to fetch the place object to which we
|
||||||
|
* were moving.
|
||||||
|
*/
|
||||||
|
// public static interface FailureHandler
|
||||||
|
// {
|
||||||
|
// /**
|
||||||
|
// * Should instruct the client to move to the last known working
|
||||||
|
// * location (as well as clean up after the failed moveTo request).
|
||||||
|
// */
|
||||||
|
// public void recoverFailedMove (int placeId);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a location director which will configure itself for
|
||||||
|
* operation using the supplied context.
|
||||||
|
*/
|
||||||
|
public function LocationDirector (ctx :CrowdContext)
|
||||||
|
{
|
||||||
|
super(ctx);
|
||||||
|
|
||||||
|
// keep this around for later
|
||||||
|
_cctx = ctx;
|
||||||
|
|
||||||
|
// register for location notifications
|
||||||
|
_cctx.getClient().getInvocationDirector().registerReceiver(
|
||||||
|
new LocationDecoder(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a location observer to the list. This observer will
|
||||||
|
* subsequently be notified of potential, effected and failed location
|
||||||
|
* changes.
|
||||||
|
*/
|
||||||
|
public function addLocationObserver (observer :LocationObserver) :void
|
||||||
|
{
|
||||||
|
_observers.add(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a location observer from the list.
|
||||||
|
*/
|
||||||
|
public function removeLocationObserver (observer :LocationObserver) :void
|
||||||
|
{
|
||||||
|
_observers.remove(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the place object for the location we currently occupy or
|
||||||
|
* null if we're not currently occupying any location.
|
||||||
|
*/
|
||||||
|
public function getPlaceObject () :PlaceObject
|
||||||
|
{
|
||||||
|
return _plobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there is a pending move request.
|
||||||
|
*/
|
||||||
|
public function movePending () :Boolean
|
||||||
|
{
|
||||||
|
return (_pendingPlaceId > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that this client be moved to the specified place. 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 (placeId :int) :Boolean
|
||||||
|
{
|
||||||
|
// make sure the placeId is valid
|
||||||
|
if (placeId < 0) {
|
||||||
|
Log.warning("Refusing moveTo(): invalid placeId " + placeId + ".");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first check to see if our observers are happy with this move
|
||||||
|
// request
|
||||||
|
if (!mayMoveTo(placeId, null)) {
|
||||||
|
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 = checkRepeatMove();
|
||||||
|
|
||||||
|
// complain if we're over-writing a pending request
|
||||||
|
if (_pendingPlaceId != -1) {
|
||||||
|
// if the pending request has been outstanding more than a
|
||||||
|
// minute, go ahead and let this new one through in an attempt
|
||||||
|
// to recover from dropped moveTo requests
|
||||||
|
if (refuse) {
|
||||||
|
Log.warning("Refusing moveTo; We have a request outstanding " +
|
||||||
|
"[ppid=" + _pendingPlaceId +
|
||||||
|
", npid=" + placeId + "].");
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.warning("Overriding stale moveTo request " +
|
||||||
|
"[ppid=" + _pendingPlaceId +
|
||||||
|
", npid=" + placeId + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a note of our pending place id
|
||||||
|
_pendingPlaceId = placeId;
|
||||||
|
|
||||||
|
// issue a moveTo request
|
||||||
|
Log.info("Issuing moveTo(" + placeId + ").");
|
||||||
|
_lservice.moveTo(_cctx.getClient(), placeId, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to move to the room that we last occupied, if such a room
|
||||||
|
* exists.
|
||||||
|
*
|
||||||
|
* @return true if we had a previous room and we requested to move to
|
||||||
|
* it, false if we had no previous room.
|
||||||
|
*/
|
||||||
|
public function moveBack () :Boolean
|
||||||
|
{
|
||||||
|
if (_previousPlaceId == -1) {
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
moveTo(_previousPlaceId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues a request to leave our current location.
|
||||||
|
*
|
||||||
|
* @return true if we were able to leave, false if we are in the
|
||||||
|
* middle of moving somewhere and can't yet leave.
|
||||||
|
*/
|
||||||
|
public function leavePlace () :Boolean
|
||||||
|
{
|
||||||
|
if (_pendingPlaceId != -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lservice.leavePlace(_cctx.getClient());
|
||||||
|
didLeavePlace();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This can be called by cooperating directors that need to coopt the
|
||||||
|
* moving process to extend it in some way or other. In such
|
||||||
|
* situations, they should call this method before moving to a new
|
||||||
|
* location to check to be sure that all of the registered location
|
||||||
|
* observers are amenable to a location change.
|
||||||
|
*
|
||||||
|
* @param placeId the place oid of our tentative new location.
|
||||||
|
*
|
||||||
|
* @return true if everyone is happy with the move, false if it was
|
||||||
|
* vetoed by one of the location observers.
|
||||||
|
*/
|
||||||
|
public function mayMoveTo (placeId :int, rl :ResultListener) :Boolean
|
||||||
|
{
|
||||||
|
var vetoed :Boolean = false;
|
||||||
|
_observers.apply(function (obs :Object) :Boolean {
|
||||||
|
var lobs :LocationObserver = (obs as LocationObserver);
|
||||||
|
vetoed = vetoed || !lobs.locationMayChange(placeId);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// if we're actually going somewhere, let the controller know that
|
||||||
|
// we might be leaving
|
||||||
|
mayLeavePlace();
|
||||||
|
|
||||||
|
// if we have a result listener, let it know if we failed
|
||||||
|
// or keep it for later if we're still going
|
||||||
|
if (rl != null) {
|
||||||
|
if (vetoed) {
|
||||||
|
rl.requestFailed(new MoveVetoedException());
|
||||||
|
} else {
|
||||||
|
_moveListener = rl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// and return the result
|
||||||
|
return !vetoed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to inform our controller that we may be leaving the current
|
||||||
|
* place.
|
||||||
|
*/
|
||||||
|
protected void mayLeavePlace ()
|
||||||
|
{
|
||||||
|
if (_controller != null) {
|
||||||
|
try {
|
||||||
|
_controller.mayLeavePlace(_plobj);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.warning("Place controller choked in " +
|
||||||
|
"mayLeavePlace [plobj=" + _plobj + "].");
|
||||||
|
Log.logStackTrace(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This can be called by cooperating directors that need to coopt the
|
||||||
|
* moving process to extend it in some way or other. In such
|
||||||
|
* situations, they will be responsible for receiving the successful
|
||||||
|
* move response and they should let the location director know that
|
||||||
|
* the move has been effected.
|
||||||
|
*
|
||||||
|
* @param placeId the place oid of our new location.
|
||||||
|
* @param config the configuration information for the new place.
|
||||||
|
*/
|
||||||
|
public void didMoveTo (int placeId, PlaceConfig config)
|
||||||
|
{
|
||||||
|
if (_moveListener != null) {
|
||||||
|
_moveListener.requestCompleted(config);
|
||||||
|
_moveListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of our previous place id
|
||||||
|
_previousPlaceId = _placeId;
|
||||||
|
|
||||||
|
// clear out our last request time
|
||||||
|
_lastRequestTime = 0;
|
||||||
|
|
||||||
|
// do some cleaning up in case we were previously in a place
|
||||||
|
didLeavePlace();
|
||||||
|
|
||||||
|
// make a note that we're now mostly in the new location
|
||||||
|
_placeId = placeId;
|
||||||
|
|
||||||
|
// check whether we should use a custom class loader
|
||||||
|
ClassLoader loader = getClassLoader(config);
|
||||||
|
if (loader != null) {
|
||||||
|
config.setClassLoader(loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
Class cclass = config.getControllerClass();
|
||||||
|
try {
|
||||||
|
// start up a new place controller to manage the new place
|
||||||
|
_controller = (PlaceController)cclass.newInstance();
|
||||||
|
_controller.init(_cctx, config);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.warning("Error creating or initializing place controller " +
|
||||||
|
"[cclass=" + cclass.getName() +
|
||||||
|
", config=" + config + "].");
|
||||||
|
Log.logStackTrace(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscribe to our new place object to complete the move
|
||||||
|
_cctx.getDObjectManager().subscribeToObject(_placeId, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we're leaving our current location. Informs the
|
||||||
|
* location's controller that we're departing, unsubscribes from the
|
||||||
|
* location's place object, and clears out our internal place
|
||||||
|
* information.
|
||||||
|
*/
|
||||||
|
public void didLeavePlace ()
|
||||||
|
{
|
||||||
|
if (_plobj != null) {
|
||||||
|
// let the old controller know that things are going away
|
||||||
|
if (_controller != null) {
|
||||||
|
try {
|
||||||
|
_controller.didLeavePlace(_plobj);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.warning("Place controller choked in " +
|
||||||
|
"didLeavePlace [plobj=" + _plobj + "].");
|
||||||
|
Log.logStackTrace(e);
|
||||||
|
}
|
||||||
|
_controller = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsubscribe from our old place object
|
||||||
|
_cctx.getDObjectManager().unsubscribeFromObject(
|
||||||
|
_plobj.getOid(), this);
|
||||||
|
_plobj = null;
|
||||||
|
|
||||||
|
// and clear out the associated place id
|
||||||
|
_placeId = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This can be called by cooperating directors that need to coopt the
|
||||||
|
* moving process to extend it in some way or other. If the coopted
|
||||||
|
* move request fails, this failure can be propagated to the location
|
||||||
|
* observers if appropriate.
|
||||||
|
*
|
||||||
|
* @param placeId the place oid to which we failed to move.
|
||||||
|
* @param reason the reason code given for failure.
|
||||||
|
*/
|
||||||
|
public void failedToMoveTo (int placeId, String reason)
|
||||||
|
{
|
||||||
|
if (_moveListener != null) {
|
||||||
|
_moveListener.requestFailed(new MoveFailedException(reason));
|
||||||
|
_moveListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear out our last request time
|
||||||
|
_lastRequestTime = 0;
|
||||||
|
|
||||||
|
// let our observers know what's up
|
||||||
|
notifyFailure(placeId, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to test and set a time stamp that we use to determine if a
|
||||||
|
* pending moveTo request is stale.
|
||||||
|
*/
|
||||||
|
public boolean checkRepeatMove ()
|
||||||
|
{
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - _lastRequestTime < STALE_REQUEST_DURATION) {
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_lastRequestTime = now;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void clientDidLogon (Client client)
|
||||||
|
{
|
||||||
|
super.clientDidLogon(client);
|
||||||
|
|
||||||
|
// subscribe to our body object
|
||||||
|
Subscriber sub = new Subscriber() {
|
||||||
|
public void objectAvailable (DObject object)
|
||||||
|
{
|
||||||
|
gotBodyObject((BodyObject)object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestFailed (int oid, ObjectAccessException cause)
|
||||||
|
{
|
||||||
|
Log.warning("Location director unable to fetch body " +
|
||||||
|
"object; all has gone horribly wrong" +
|
||||||
|
"[cause=" + cause + "].");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
int cloid = client.getClientOid();
|
||||||
|
client.getDObjectManager().subscribeToObject(cloid, sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void clientDidLogoff (Client client)
|
||||||
|
{
|
||||||
|
super.clientDidLogoff(client);
|
||||||
|
|
||||||
|
// clear ourselves out and inform observers of our departure
|
||||||
|
mayLeavePlace();
|
||||||
|
didLeavePlace();
|
||||||
|
|
||||||
|
// let our observers know that we're no longer in a location
|
||||||
|
_observers.apply(_didChangeOp);
|
||||||
|
|
||||||
|
// clear out everything else (it's possible that we were logged
|
||||||
|
// off in the middle of a change location request)
|
||||||
|
_pendingPlaceId = -1;
|
||||||
|
_previousPlaceId = -1;
|
||||||
|
_lastRequestTime = 0L;
|
||||||
|
_lservice = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
protected void fetchServices (Client client)
|
||||||
|
{
|
||||||
|
// obtain our service handle
|
||||||
|
_lservice = (LocationService)
|
||||||
|
client.requireService(LocationService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void gotBodyObject (BodyObject clobj)
|
||||||
|
{
|
||||||
|
// check to see if we are already in a location, in which case
|
||||||
|
// we'll want to be going there straight away
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void moveSucceeded (PlaceConfig config)
|
||||||
|
{
|
||||||
|
// handle the successful move
|
||||||
|
didMoveTo(_pendingPlaceId, config);
|
||||||
|
|
||||||
|
// and clear out the tracked pending oid
|
||||||
|
_pendingPlaceId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void requestFailed (String reason)
|
||||||
|
{
|
||||||
|
// clear out our pending request oid
|
||||||
|
int placeId = _pendingPlaceId;
|
||||||
|
_pendingPlaceId = -1;
|
||||||
|
|
||||||
|
Log.info("moveTo failed [pid=" + placeId +
|
||||||
|
", reason=" + reason + "].");
|
||||||
|
|
||||||
|
// let our observers know that something has gone horribly awry
|
||||||
|
notifyFailure(placeId, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void forcedMove (final int placeId)
|
||||||
|
{
|
||||||
|
Log.info("Moving at request of server [placeId=" + placeId + "].");
|
||||||
|
|
||||||
|
if (movePending()) {
|
||||||
|
// clear out our old place information
|
||||||
|
mayLeavePlace();
|
||||||
|
didLeavePlace();
|
||||||
|
|
||||||
|
// move to the new place
|
||||||
|
moveTo(placeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we receive the place object to which we subscribed
|
||||||
|
* after a successful moveTo request.
|
||||||
|
*/
|
||||||
|
public void objectAvailable (DObject object)
|
||||||
|
{
|
||||||
|
// yay, we have our new place object
|
||||||
|
_plobj = (PlaceObject)object;
|
||||||
|
|
||||||
|
// let the place controller know that we're ready to roll
|
||||||
|
if (_controller != null) {
|
||||||
|
try {
|
||||||
|
_controller.willEnterPlace(_plobj);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.warning("Controller choked in willEnterPlace " +
|
||||||
|
"[place=" + _plobj + "].");
|
||||||
|
Log.logStackTrace(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// let our observers know that all is well on the western front
|
||||||
|
_observers.apply(_didChangeOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called if we are unable to subscribe to the place object that was
|
||||||
|
* provided to us with our successful moveTo request. This is
|
||||||
|
* generally a bad scene and we do our best to recover by going back
|
||||||
|
* to the previously known location.
|
||||||
|
*/
|
||||||
|
public void requestFailed (int oid, ObjectAccessException cause)
|
||||||
|
{
|
||||||
|
// aiya! we were unable to fetch our new place object; something
|
||||||
|
// is badly wrong
|
||||||
|
Log.warning("Aiya! Unable to fetch place object for new location " +
|
||||||
|
"[plid=" + oid + ", reason=" + cause + "].");
|
||||||
|
|
||||||
|
// clear out our half initialized place info
|
||||||
|
int placeId = _placeId;
|
||||||
|
_placeId = -1;
|
||||||
|
|
||||||
|
// let the kids know shit be fucked
|
||||||
|
notifyFailure(placeId, "m.unable_to_fetch_place_object");
|
||||||
|
|
||||||
|
// we need to sort out what to do about the half-initialized place
|
||||||
|
// controller. presently we punt and hope that calling
|
||||||
|
// didLeavePlace() without ever having called willEnterPlace()
|
||||||
|
// does whatever's necessary
|
||||||
|
|
||||||
|
// try to return to our previous location
|
||||||
|
if (_failureHandler != null) {
|
||||||
|
_failureHandler.recoverFailedMove(placeId);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// if we were previously somewhere (and that somewhere isn't
|
||||||
|
// where we just tried to go), try going back to that happy
|
||||||
|
// place
|
||||||
|
if (_previousPlaceId != -1 && _previousPlaceId != placeId) {
|
||||||
|
moveTo(_previousPlaceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the failure handler which will recover from place object
|
||||||
|
* fetching failures. In the event that we are unable to fetch our
|
||||||
|
* place object after making a successful moveTo request, we attempt
|
||||||
|
* to rectify the failure by moving back to the last known working
|
||||||
|
* location. Because entites that cooperate with the location director
|
||||||
|
* may need to become involved in this failure recovery, we provide
|
||||||
|
* this interface whereby they can interject themseves into the
|
||||||
|
* failure recovery process and do their own failure recovery.
|
||||||
|
*/
|
||||||
|
// public void setFailureHandler (FailureHandler handler)
|
||||||
|
// {
|
||||||
|
// if (_failureHandler != null) {
|
||||||
|
// Log.warning("Requested to set failure handler, but we've " +
|
||||||
|
// "already got one. The conflicting entities will " +
|
||||||
|
// "likely need to perform more sophisticated " +
|
||||||
|
// "coordination to deal with failures. " +
|
||||||
|
// "[old=" + _failureHandler + ", new=" + handler + "].");
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// _failureHandler = handler;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
protected void notifyFailure (final int placeId, final String reason)
|
||||||
|
{
|
||||||
|
_observers.apply(new ObserverOp() {
|
||||||
|
public boolean apply (Object obs) {
|
||||||
|
((LocationObserver)obs).locationChangeFailed(placeId, reason);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By overriding this method, it is possible to customize the location
|
||||||
|
* director to cause it to load the classes associated with a
|
||||||
|
* particular place via a custom class loader. That loader may enforce
|
||||||
|
* restricted privileges or obtain the classes from some special
|
||||||
|
* source.
|
||||||
|
*/
|
||||||
|
protected ClassLoader getClassLoader (PlaceConfig config)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The context through which we access needed services. */
|
||||||
|
protected CrowdContext _cctx;
|
||||||
|
|
||||||
|
/** Provides access to location services. */
|
||||||
|
protected LocationService _lservice;
|
||||||
|
|
||||||
|
/** Our location observer list. */
|
||||||
|
protected ObserverList _observers =
|
||||||
|
new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||||
|
|
||||||
|
/** The oid of the place we currently occupy. */
|
||||||
|
protected int _placeId = -1;
|
||||||
|
|
||||||
|
/** The place object that we currently occupy. */
|
||||||
|
protected PlaceObject _plobj;
|
||||||
|
|
||||||
|
/** The place controller in effect for our current place. */
|
||||||
|
protected PlaceController _controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The oid of the place for which we have an outstanding moveTo
|
||||||
|
* request, or -1 if we have no outstanding request.
|
||||||
|
*/
|
||||||
|
protected int _pendingPlaceId = -1;
|
||||||
|
|
||||||
|
/** The oid of the place we previously occupied. */
|
||||||
|
protected int _previousPlaceId = -1;
|
||||||
|
|
||||||
|
/** The last time we requested a move to. */
|
||||||
|
protected long _lastRequestTime;
|
||||||
|
|
||||||
|
/** The entity that deals when we fail to subscribe to a place
|
||||||
|
* object. */
|
||||||
|
// protected FailureHandler _failureHandler;
|
||||||
|
|
||||||
|
/** A listener that wants to know if we succeeded or
|
||||||
|
* how we failed to move. */
|
||||||
|
protected ResultListener _moveListener;
|
||||||
|
|
||||||
|
/** The operation used to inform observers that the location changed. */
|
||||||
|
protected ObserverOp _didChangeOp = new ObserverOp() {
|
||||||
|
public boolean apply (Object obs) {
|
||||||
|
((LocationObserver)obs).locationDidChange(_plobj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** We require that a moveTo request be outstanding for one minute
|
||||||
|
* before it is declared to be stale. */
|
||||||
|
protected static final long STALE_REQUEST_DURATION = 60L * 1000L;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
//
|
||||||
|
// $Id: OccupantDirector.java 3406 2005-03-15 02:12:03Z 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.crowd.client {
|
||||||
|
|
||||||
|
import com.threerings.util.Iterator;
|
||||||
|
import com.threerings.util.ObserverList;
|
||||||
|
import com.threerings.util.Name;
|
||||||
|
|
||||||
|
import com.threerings.presents.client.BasicDirector;
|
||||||
|
import com.threerings.presents.client.Client;
|
||||||
|
import com.threerings.presents.client.ClientEvent;
|
||||||
|
import com.threerings.presents.dobj.EntryAddedEvent;
|
||||||
|
import com.threerings.presents.dobj.EntryRemovedEvent;
|
||||||
|
import com.threerings.presents.dobj.EntryUpdatedEvent;
|
||||||
|
import com.threerings.presents.dobj.SetListener;
|
||||||
|
|
||||||
|
import com.threerings.crowd.Log;
|
||||||
|
import com.threerings.crowd.data.OccupantInfo;
|
||||||
|
import com.threerings.crowd.data.PlaceObject;
|
||||||
|
import com.threerings.crowd.util.CrowdContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The occupant director listens for occupants of places to enter and
|
||||||
|
* exit, and dispatches notices to interested parties about these events.
|
||||||
|
*
|
||||||
|
* <p> It will eventually provide a framework for keeping track of
|
||||||
|
* occupant information in a network efficient manner. The idea being that
|
||||||
|
* we want to store as little information about occupants as possible in
|
||||||
|
* the place object (probably just body oid and username), but upon
|
||||||
|
* entering a place, this will be all we know about the occupants. We then
|
||||||
|
* dispatch a request to get information about all of the occupants in the
|
||||||
|
* room (things like avatar information for a graphical display or perhaps
|
||||||
|
* their ratings in the game that is associated with a place for a gaming
|
||||||
|
* site) which we then pass on to the occupant observers when it becomes
|
||||||
|
* available.
|
||||||
|
*
|
||||||
|
* <p> This information would be cached and we could return cached
|
||||||
|
* information for occupants for which we have cached info. We will
|
||||||
|
* probably want to still make a request for the occupant info so that we
|
||||||
|
* can update non-static occupant data rather than permanently using
|
||||||
|
* what's in the cache.
|
||||||
|
*/
|
||||||
|
public class OccupantDirector extends BasicDirector
|
||||||
|
implements LocationObserver, SetListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructs a new occupant director with the supplied context.
|
||||||
|
*/
|
||||||
|
public function OccupantDirector (ctx :CrowdContext)
|
||||||
|
{
|
||||||
|
super(ctx);
|
||||||
|
|
||||||
|
// register ourselves as a location observer
|
||||||
|
ctx.getLocationDirector().addLocationObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified occupant observer to the list.
|
||||||
|
*/
|
||||||
|
public function addOccupantObserver (obs :OccupantObserver) :void
|
||||||
|
{
|
||||||
|
_observers.add(obs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified occupant observer from the list.
|
||||||
|
*/
|
||||||
|
public function removeOccupantObserver (obs :OccupantObserver) :void
|
||||||
|
{
|
||||||
|
_observers.remove(obs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the occupant info for the user in question if it exists in
|
||||||
|
* the currently occupied place. Returns null if no occupant info
|
||||||
|
* exists for the specified body.
|
||||||
|
*/
|
||||||
|
public function getOccupantInfo (bodyOid :int) :OccupantInfo
|
||||||
|
{
|
||||||
|
// make sure we're somewhere
|
||||||
|
return (_place == null) ? null :
|
||||||
|
(_place.occupantInfo.get(bodyOid) as OccupantInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the occupant info for the user in question if it exists in
|
||||||
|
* the currently occupied place. Returns null if no occupant info
|
||||||
|
* exists with the specified username.
|
||||||
|
*/
|
||||||
|
public function getOccupantInfoByName (username :Name) :OccupantInfo
|
||||||
|
{
|
||||||
|
return (_place == null) ? null : _place.getOccupantInfo(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public override function clientDidLogoff (event :ClientEvent) :void
|
||||||
|
{
|
||||||
|
super.clientDidLogoff(event);
|
||||||
|
// clear things out
|
||||||
|
if (_place != null) {
|
||||||
|
_place.removeListener(this);
|
||||||
|
_place = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface LocationObserver
|
||||||
|
public function locationMayChange (placeId :int) :Boolean
|
||||||
|
{
|
||||||
|
// we've got no opinion
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface LocationObserver
|
||||||
|
public function locationDidChange (place :PlaceObject) :void
|
||||||
|
{
|
||||||
|
// unlisten to the old place object if there was one
|
||||||
|
if (_place != null) {
|
||||||
|
_place.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// listen to the new one
|
||||||
|
_place = place;
|
||||||
|
if (_place != null) {
|
||||||
|
_place.addListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface LocationObserver
|
||||||
|
public function locationChangeFailed (placeId :int, reason :String) :void
|
||||||
|
{
|
||||||
|
// nothing to do here either
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface SetListener
|
||||||
|
public function entryAdded (event :EntryAddedEvent) :void
|
||||||
|
{
|
||||||
|
// bail if this isn't for the OCCUPANT_INFO field
|
||||||
|
if (event.getName() != PlaceObject.OCCUPANT_INFO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now let the occupant observers know what's up
|
||||||
|
var info :OccupantInfo = (event.getEntry() as OccupantInfo);
|
||||||
|
_observers.apply(function (obj :Object) :Boolean {
|
||||||
|
(obj as OccupantObserver).occupantEntered(info);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface SetListener
|
||||||
|
public function entryUpdated (event :EntryUpdatedEvent) :void
|
||||||
|
{
|
||||||
|
// bail if this isn't for the OCCUPANT_INFO field
|
||||||
|
if (event.getName() != PlaceObject.OCCUPANT_INFO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now let the occupant observers know what's up
|
||||||
|
var info :OccupantInfo = (event.getEntry() as OccupantInfo);
|
||||||
|
var oinfo :OccupantInfo = (event.getOldEntry() as OccupantInfo);
|
||||||
|
_observers.apply(function (obj :Object) :Boolean {
|
||||||
|
(obj as OccupantObserver).occupantUpdated(oinfo, info);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface SetListener
|
||||||
|
public function entryRemoved (event :EntryRemovedEvent) :void
|
||||||
|
{
|
||||||
|
// bail if this isn't for the OCCUPANT_INFO field
|
||||||
|
if (event.getName() != PlaceObject.OCCUPANT_INFO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// let the occupant observers know what's up
|
||||||
|
var oinfo :OccupantInfo = (event.getOldEntry() as OccupantInfo);
|
||||||
|
_observers.apply(function (obj :Object) :Boolean {
|
||||||
|
(obj as OccupantObserver).occupantLeft(oinfo);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The occupant observers to keep abreast of occupant antics. */
|
||||||
|
protected var _observers :ObserverList =
|
||||||
|
new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||||
|
|
||||||
|
/** The user's current location. */
|
||||||
|
protected var _place :PlaceObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// $Id: OccupantObserver.java 3098 2004-08-27 02:12: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.crowd.client {
|
||||||
|
|
||||||
|
import com.threerings.crowd.data.OccupantInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An entity that is interested in hearing about bodies that enter and
|
||||||
|
* leave a location (as well as disconnect and reconnect) can implement
|
||||||
|
* this interface and register itself with the {@link OccupantDirector}.
|
||||||
|
*/
|
||||||
|
public interface OccupantObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when a body enters the place.
|
||||||
|
*/
|
||||||
|
function occupantEntered (info :OccupantInfo) :void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a body leaves the place.
|
||||||
|
*/
|
||||||
|
function occupantLeft (info :OccupantInfo) :void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an occupant is updated.
|
||||||
|
*
|
||||||
|
* @param oldinfo the occupant info prior to the update.
|
||||||
|
* @param newinfo the newly update info record.
|
||||||
|
*/
|
||||||
|
function occupantUpdated (
|
||||||
|
oldinfo :OccupantInfo, newinfo :OccupantInfo) :void;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// $Id: PlaceView.java 3098 2004-08-27 02:12: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.crowd.client {
|
||||||
|
|
||||||
|
import com.threerings.crowd.data.PlaceObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface provides a convenient means for decoupling user
|
||||||
|
* interface components that interact with a place object and that need to
|
||||||
|
* keep themselves up to date when the client moves from place to place.
|
||||||
|
*
|
||||||
|
* <p> In general, such components need to know when the client is about
|
||||||
|
* to enter a place so that they can subscribe if necessary or at least
|
||||||
|
* extract information about the place. They also need to know when a
|
||||||
|
* client has left a place so that they can unsubscribe and clean up after
|
||||||
|
* themselves. This is the information that the place view interface makes
|
||||||
|
* available to them in a decoupled way.
|
||||||
|
*
|
||||||
|
* <p> The part of the client implementation that is responsible for the
|
||||||
|
* main user interface can act as a location observer, and it can make use
|
||||||
|
* of {@link PlaceViewUtil} to dispatch notification of place changes to
|
||||||
|
* every <code>PlaceView</code> implementing user interface element in the
|
||||||
|
* user interface hierarchy with calls to {@link
|
||||||
|
* PlaceViewUtil#dispatchWillEnterPlace} and {@link
|
||||||
|
* PlaceViewUtil#dispatchDidLeavePlace}. These functions traverse the UI
|
||||||
|
* hierarchy (starting with the element provided which would generally be
|
||||||
|
* the top-level UI element, and dispatch calls to {@link #willEnterPlace}
|
||||||
|
* and {@link #didLeavePlace} respectively on any UI element they find
|
||||||
|
* that implements <code>PlaceView</code>.
|
||||||
|
*
|
||||||
|
* <p> By doing this, the client code can simply create place-sensitive
|
||||||
|
* user interface elements and stick them in the user interface and
|
||||||
|
* essentially forget about them, knowing that they will all be notified
|
||||||
|
* of place entering and exiting by virtue of the single dispatching
|
||||||
|
* calls. It is useful to note that place-sensitive user interface
|
||||||
|
* elements will also generally need a reference to the {@link
|
||||||
|
* com.threerings.crowd.util.CrowdContext} derivative in use by
|
||||||
|
* the client, but those are best supplied at construct time.
|
||||||
|
*/
|
||||||
|
public interface PlaceView
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when the client has entered a place and is about to display
|
||||||
|
* the user interface for that place.
|
||||||
|
*
|
||||||
|
* @param plobj the place object that was just entered.
|
||||||
|
*/
|
||||||
|
function willEnterPlace (plobj :PlaceObject) :void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after the client has left a place and needs to clean up
|
||||||
|
* after the user interface that was displaying that place.
|
||||||
|
*
|
||||||
|
* @param plobj the place object that was just left.
|
||||||
|
*/
|
||||||
|
function didLeavePlace (plobj :PlaceObject) :void;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -146,6 +146,11 @@ public class TestClient extends Client
|
|||||||
": " + arguments);
|
": " + arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function crazyArgs (num :int = 0, str :String = "lol") :void
|
||||||
|
{
|
||||||
|
Log.debug("crazyArgs " + num + ", " + str);
|
||||||
|
}
|
||||||
|
|
||||||
prototype var _foo :String;
|
prototype var _foo :String;
|
||||||
|
|
||||||
protected var _savedFunc :Function;
|
protected var _savedFunc :Function;
|
||||||
|
|||||||
@@ -5,6 +5,19 @@ package com.threerings.util {
|
|||||||
*/
|
*/
|
||||||
public class ArrayUtil
|
public class ArrayUtil
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Return the first index of the specified element, or -1 if not present.
|
||||||
|
*/
|
||||||
|
public static function indexOf (arr :Array, element :Object) :int
|
||||||
|
{
|
||||||
|
for (var ii :int = 0; ii < arr.length; ii++) {
|
||||||
|
if (arr[ii] === element) {
|
||||||
|
return ii;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the first instance of the specified element from the array.
|
* Remove the first instance of the specified element from the array.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -384,3 +384,8 @@ public class MessageBundle
|
|||||||
protected static const QUAL_SEP :String = ":";
|
protected static const QUAL_SEP :String = ":";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: figure out how this is all actually going to even work
|
||||||
|
class ResourceBundle
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.threerings.util {
|
||||||
|
|
||||||
|
public class ObserverList
|
||||||
|
{
|
||||||
|
/** A notification ordering policy indicating that the observers
|
||||||
|
* should be notified in the order they were added and that the
|
||||||
|
* notification should be done on a snapshot of the array. */
|
||||||
|
public static const SAFE_IN_ORDER_NOTIFY :int = 1;
|
||||||
|
|
||||||
|
/** A notification ordering policy wherein the observers are notified
|
||||||
|
* last to first so that they can be removed during the notification
|
||||||
|
* process and new observers added will not inadvertently be notified
|
||||||
|
* as well, but no copy of the observer list need be made. This will
|
||||||
|
* not work if observers are added or removed from arbitrary positions
|
||||||
|
* in the list during a notification call. */
|
||||||
|
public static const FAST_UNSAFE_NOTIFY :int = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function ObserverList (notifyPolicy :int = 2)
|
||||||
|
{
|
||||||
|
_notifyPolicy = notifyPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an observer to this list.
|
||||||
|
*/
|
||||||
|
public function add (observer :Object) :void
|
||||||
|
{
|
||||||
|
if (ArrayUtil.indexOf(_list, observer) == -1) {
|
||||||
|
_list.push(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an observer from this list.
|
||||||
|
*/
|
||||||
|
public function remove (observer :Object) :void
|
||||||
|
{
|
||||||
|
ArrayUtil.removeFirst(_list, observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply some operation to all observers.
|
||||||
|
* The function to be passed in should expect one argument and return a
|
||||||
|
* Boolean.
|
||||||
|
* function (observer :Object) :Boolean.
|
||||||
|
* The function should return false if the observer should be removed
|
||||||
|
* from the list.
|
||||||
|
*/
|
||||||
|
public function apply (func :Function) :void
|
||||||
|
{
|
||||||
|
var list :Array = _list;
|
||||||
|
if (_notifyPolicy == SAFE_IN_ORDER_NOTIFY) {
|
||||||
|
list = list.concat(); // make a duplicate
|
||||||
|
list.reverse(); // reverse it so that we start with earlier obs
|
||||||
|
}
|
||||||
|
for (var ii :int = list.length-1; ii >= 0; ii--) {
|
||||||
|
try {
|
||||||
|
var result :Boolean = func(list[ii]);
|
||||||
|
if (!result) {
|
||||||
|
// remove it if directed to do so
|
||||||
|
remove(list[ii]);
|
||||||
|
}
|
||||||
|
} catch (err :Error) {
|
||||||
|
Log.warning("ObserverOp choked during notification.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Our notification policy. */
|
||||||
|
protected var _notifyPolicy :int;
|
||||||
|
|
||||||
|
/** The actual list of observers. */
|
||||||
|
protected var _list :Array = new Array();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.threerings.util {
|
||||||
|
|
||||||
|
public interface ResultListener
|
||||||
|
{
|
||||||
|
function requestCompleted (obj :Object) :void;
|
||||||
|
|
||||||
|
function requestFailed (cause :Error) :void;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user