Obtain our ActionScript dependencies from Maven as well, and publish our swc

artifacts thereto.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6289 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2010-11-18 20:50:09 +00:00
parent 0a04df1ce0
commit 34722f2ce9
222 changed files with 60 additions and 18 deletions
@@ -0,0 +1,61 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.client {
import com.threerings.bureau.data.AgentObject;
/**
* Represents an agent running within a bureau client.
*/
public class Agent
{
/**
* Initializes the Agent with the distributed agent object.
*/
public function init (agentObj :AgentObject) :void
{
_agentObj = agentObj;
}
/**
* Starts the code running in the agent.
*/
public function start () :void
{
throw new Error("Abstract method");
}
/**
* Stops the code running in the agent.
*/
public function stop () :void
{
throw new Error("Abstract method");
}
/**
* The shared agent object.
*/
protected var _agentObj :AgentObject;
}
}
@@ -0,0 +1,115 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.client {
import com.threerings.presents.client.Client;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.bureau.data.BureauAuthName;
import com.threerings.bureau.data.BureauCredentials;
import com.threerings.bureau.util.BureauContext;
/**
* Represents a client embedded in a bureau.
*/
public class BureauClient extends Client
{
// statically reference classes we require
BureauAuthName
/**
* Creates a new client.
* @param creds the credentials supplied during connection
* @param runQueue the place to post tasks required by clients
*/
public function BureauClient (token :String, bureauId :String)
{
super(null);
_bureauId = bureauId;
_creds = new BureauCredentials(_bureauId, token);
_ctx = createContext();
_director = createDirector();
}
public function getBureauDirector () :BureauDirector
{
return _director;
}
public function getBureauId () :String
{
return _bureauId;
}
protected function createDirector () :BureauDirector
{
throw new Error("Abstract method");
}
protected function createContext () :BureauContext
{
return new Context(this);
}
protected var _ctx :BureauContext;
protected var _bureauId :String;
protected var _director :BureauDirector;
}
}
import com.threerings.bureau.client.BureauClient;
import com.threerings.bureau.client.BureauDirector;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.client.Client;
import com.threerings.bureau.util.BureauContext;
class Context
implements BureauContext
{
function Context (client :BureauClient)
{
_client = client;
}
public function getBureauDirector () :BureauDirector
{
return _client.getBureauDirector();
}
public function getDObjectManager () :DObjectManager
{
return _client.getDObjectManager();
}
public function getClient () :Client
{
return _client;
}
public function getBureauId () :String
{
return _client.getBureauId();
}
protected var _client :BureauClient;
}
@@ -0,0 +1,80 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.client {
import com.threerings.bureau.client.BureauReceiver;
import com.threerings.presents.client.InvocationDecoder;
/**
* Dispatches calls to a {@link BureauReceiver} instance.
*/
public class BureauDecoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static const RECEIVER_CODE :String = "3e98f7a30deb5a8e25e05c71c6081bf4";
/** The method id used to dispatch {@link BureauReceiver#createAgent}
* notifications. */
public static const CREATE_AGENT :int = 1;
/** The method id used to dispatch {@link BureauReceiver#destroyAgent}
* notifications. */
public static const DESTROY_AGENT :int = 2;
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
*/
public function BureauDecoder (receiver :BureauReceiver)
{
this.receiver = receiver;
}
// documentation inherited
public override function getReceiverCode () :String
{
return RECEIVER_CODE;
}
// documentation inherited
public override function dispatchNotification (methodId :int, args :Array) :void
{
switch (methodId) {
case CREATE_AGENT:
BureauReceiver(receiver).createAgent(
args[0] as int
);
return;
case DESTROY_AGENT:
BureauReceiver(receiver).destroyAgent(
args[0] as int
);
return;
default:
super.dispatchNotification(methodId, args);
return;
}
}
}
}
@@ -0,0 +1,214 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.client {
import com.threerings.util.Map;
import com.threerings.util.Maps;
import com.threerings.util.Log;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.ClientEvent;
import com.threerings.presents.dobj.ObjectAccessError;
import com.threerings.presents.util.SafeSubscriber;
import com.threerings.bureau.data.AgentObject;
import com.threerings.bureau.data.BureauClientObject;
import com.threerings.bureau.data.BureauCodes;
import com.threerings.bureau.util.BureauContext;
/**
* Allows the server to create and destroy agents on a client.
* @see BureauRegistry
*/
public class BureauDirector extends BasicDirector
{
BureauClientObject;
/**
* Creates a new BureauDirector.
*/
public function BureauDirector (ctx :BureauContext)
{
super(ctx);
}
// from BasicDirector
public override function clientDidLogon (event :ClientEvent) :void
{
super.clientDidLogon(event);
var id :String = BureauContext(_ctx).getBureauId();
_bureauService.bureauInitialized(id);
}
/**
* Lets the server know that a fatal error has occurred and the bureau needs to be terminated.
*/
public function fatalError (message :String) :void
{
_bureauService.bureauError(message);
}
/**
* Creates a new agent when the server requests it.
*/
protected function createAgentFromId (agentId :int) :void
{
log.info("Subscribing to object", "agentId", agentId);
var subscriber :SafeSubscriber =
new SafeSubscriber(agentId, objectAvailable, requestFailed);
_subscribers.put(agentId, subscriber);
subscriber.subscribe(_ctx.getDObjectManager());
}
/**
* Destroys an agent at the server's request.
*/
protected function destroyAgent (agentId :int) :void
{
var agent :Agent = null;
agent = _agents.remove(agentId);
if (agent == null) {
log.warning("Lost an agent", "id", agentId);
}
else {
try {
agent.stop();
} catch (e :Error) {
log.warning("Stopping an agent caused an exception", e);
}
var subscriber :SafeSubscriber = _subscribers.remove(agentId);
if (subscriber == null) {
log.warning("Lost a subscriber for agent", "agent", agent);
}
else {
subscriber.unsubscribe(_ctx.getDObjectManager());
}
_bureauService.agentDestroyed(agentId);
}
}
/**
* Callback for when the a request to subscribe to an object finishes and the object is available.
*/
protected function objectAvailable (agentObject :AgentObject) :void
{
var oid :int = agentObject.getOid();
log.info("Object available", "oid", oid);
var agent :Agent;
try {
agent = createAgent(agentObject);
agent.init(agentObject);
agent.start();
}
catch (e :Error) {
log.warning("Could not create agent", "obj", agentObject, e);
_bureauService.agentCreationFailed(oid);
return;
}
_agents.put(oid, agent);
_bureauService.agentCreated(oid);
}
/**
* Callback for when the a request to subscribe to an object fails.
*/
protected function requestFailed (oid :int, cause :ObjectAccessError) :void
{
log.warning("Could not subscribe to agent", "oid", oid, cause);
}
// from BasicDirector
protected override function registerServices (client :Client) :void
{
super.registerServices(client);
// Require the bureau services
client.addServiceGroup(BureauCodes.BUREAU_GROUP);
// Set up our decoder so we can receive method calls
// from the server
var receiver :BureauReceiver =
new ReceiverDelegator(createAgentFromId, destroyAgent);
client.getInvocationDirector().
registerReceiver(new BureauDecoder(receiver));
}
// from BasicDirector
protected override function fetchServices (client :Client) :void
{
super.fetchServices(client);
_bureauService = client.getService(BureauService) as BureauService;
}
/**
* Called when it is time to create an Agent. Subclasses should read the
* <code>agentObject</code>'s type and/or properties to determine what kind of Agent to
* create.
* @param agentObj the distributed and object
* @return a new Agent that will govern the distributed object
*/
protected function createAgent (agentObj :AgentObject) :Agent
{
throw new Error("Abstract function");
}
/** Create a logger for the entire package.. */
protected var log :Log = Log.getLog("com.threerings.bureau");
protected var _bureauService :BureauService;
protected var _agents :Map = Maps.newMapOf(int);
protected var _subscribers :Map = Maps.newMapOf(int);
}
}
import com.threerings.bureau.client.BureauReceiver;
class ReceiverDelegator implements BureauReceiver
{
public function ReceiverDelegator (createFn :Function, destroyFn :Function)
{
_createFn = createFn;
_destroyFn = destroyFn;
}
public function createAgent (agentId :int) :void
{
_createFn(agentId);
}
public function destroyAgent (agentId :int) :void
{
_destroyFn(agentId);
}
protected var _createFn :Function;
protected var _destroyFn :Function;
}
@@ -0,0 +1,50 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.client {
import com.threerings.presents.client.InvocationReceiver;
/**
* Hooks for controlling a previously launched bureau client.
*/
public interface BureauReceiver extends InvocationReceiver
{
/**
* Creates a new agent. Implementors should create a new {@link Agent} and give it access to
* the {@link AgentObject} referred to by the <code>agentId</code> parameter and must notify
* the service that the agent has been created using {@link BureauService#agentCreated}.
* @param client the client receiving the request
* @param agentId the id of the <code>AgentObject</code> that needs an <code>Agent</code>
*/
function createAgent (agentId :int) :void;
/**
* Destroys a previously created agent. Implementors should destroy the agent that was created
* by the call to <code>createAgent</code> with the same agent id and must notify
* the service that the agent has been created using {@link BureauService#agentDestroyed}.
* @param client the client receiving the request
* @param agentId the id of the <code>AgentObject</code> whose <code>Agent</code>
* should be destroyed
*/
function destroyAgent (agentId :int) :void;
}
}
@@ -0,0 +1,47 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.client {
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
/**
* An ActionScript version of the Java BureauService interface.
*/
public interface BureauService extends InvocationService
{
// from Java interface BureauService
function agentCreated (arg1 :int) :void;
// from Java interface BureauService
function agentCreationFailed (arg1 :int) :void;
// from Java interface BureauService
function agentDestroyed (arg1 :int) :void;
// from Java interface BureauService
function bureauError (arg1 :String) :void;
// from Java interface BureauService
function bureauInitialized (arg1 :String) :void;
}
}
@@ -0,0 +1,160 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.data {
import com.threerings.io.ObjectInputStream;
import com.threerings.presents.dobj.DObject;
public class AgentObject extends DObject
{
// AUTO-GENERATED: FIELDS START
/** The field name of the <code>bureauId</code> field. */
public static const BUREAU_ID :String = "bureauId";
/** The field name of the <code>bureauType</code> field. */
public static const BUREAU_TYPE :String = "bureauType";
/** The field name of the <code>code</code> field. */
public static const CODE :String = "code";
/** The field name of the <code>className</code> field. */
public static const CLASS_NAME :String = "className";
/** The field name of the <code>clientOid</code> field. */
public static const CLIENT_OID :String = "clientOid";
// AUTO-GENERATED: FIELDS END
/** The id of the bureau the agent is running in. This is normally a unique id corresponding
* to the game or item that requires some server-side processing. */
public var bureauId :String;
/** The type of bureau that the agent is running in. This is normally derived from the kind
* of media that the game or item has specified for its code and determines the method of
* launching the bureau when the first agent is requested. */
public var bureauType :String;
/** The location of the code for the agent. This could be a URL to an action script file or
* some other description that the bureau can use to load and execute the agent's code. */
public var code :String;
/** The main class within the code to use when launching an agent. Whther this value is
* used depends on the type of bureau and will be resolve in the bureau client. */
public var className :String;
/** The id of the client running this agent (only set after the agent is assigned to a
* bureau and is running). */
public var clientOid :int;
// AUTO-GENERATED: METHODS START
// /**
// * Requests that the <code>bureauId</code> field be set to the
// * specified 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 setBureauId (value :String) :void
// {
// var ovalue :String = this.bureauId;
// requestAttributeChange(
// BUREAU_ID, value, ovalue);
// this.bureauId = value;
// }
// /**
// * Requests that the <code>bureauType</code> field be set to the
// * specified 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 setBureauType (value :String) :void
// {
// var ovalue :String = this.bureauType;
// requestAttributeChange(
// BUREAU_TYPE, value, ovalue);
// this.bureauType = value;
// }
// /**
// * Requests that the <code>code</code> field be set to the
// * specified 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 setCode (value :String) :void
// {
// var ovalue :String = this.code;
// requestAttributeChange(
// CODE, value, ovalue);
// this.code = value;
// }
// /**
// * Requests that the <code>className</code> field be set to the
// * specified 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 setClassName (value :String) :void
// {
// var ovalue :String = this.className;
// requestAttributeChange(
// CLASS_NAME, value, ovalue);
// this.className = value;
// }
// /**
// * Requests that the <code>clientOid</code> field be set to the
// * specified 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 setClientOid (value :int) :void
// {
// var ovalue :int = this.clientOid;
// requestAttributeChange(
// CLIENT_OID, Integer.valueOf(value), Integer.valueOf(ovalue));
// this.clientOid = value;
// }
// // AUTO-GENERATED: METHODS END
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
bureauId = ins.readField(String) as String;
bureauType = ins.readField(String) as String;
code = ins.readField(String) as String;
className = ins.readField(String) as String;
clientOid = ins.readInt();
}
}
}
@@ -0,0 +1,36 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.data {
import com.threerings.util.Name;
/**
* Represents an authenticated bureau client.
*/
public class BureauAuthName extends Name
{
public function BureauAuthName (bureauId :String = "")
{
super(bureauId);
}
}
}
@@ -0,0 +1,32 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.data {
import com.threerings.presents.data.ClientObject;
/**
* An object representing a Bureau connection. This is currently just a marker class.
*/
public class BureauClientObject extends ClientObject
{
}
}
@@ -0,0 +1,34 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.data {
import com.threerings.presents.data.InvocationCodes;
/**
* Codes and constants global to the Bureau services.
*/
public class BureauCodes extends InvocationCodes
{
/** Defines our invocation services group. */
public static const BUREAU_GROUP :String = "bureau";
}
}
@@ -0,0 +1,39 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.data {
import com.threerings.presents.net.ServiceCreds;
/**
* Extends the basic credentials to provide bureau-specific fields.
*/
public class BureauCredentials extends ServiceCreds
{
/**
* Creates new credentials for a specific bureau.
*/
public function BureauCredentials (bureauId :String = null, sharedSecret :String = null)
{
super(bureauId, sharedSecret);
}
}
}
@@ -0,0 +1,94 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.data {
import com.threerings.bureau.client.BureauService;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.util.Integer;
/**
* Provides the implementation of the <code>BureauService</code> 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 BureauMarshaller extends InvocationMarshaller
implements BureauService
{
/** The method id used to dispatch <code>agentCreated</code> requests. */
public static const AGENT_CREATED :int = 1;
// from interface BureauService
public function agentCreated (arg1 :int) :void
{
sendRequest(AGENT_CREATED, [
Integer.valueOf(arg1)
]);
}
/** The method id used to dispatch <code>agentCreationFailed</code> requests. */
public static const AGENT_CREATION_FAILED :int = 2;
// from interface BureauService
public function agentCreationFailed (arg1 :int) :void
{
sendRequest(AGENT_CREATION_FAILED, [
Integer.valueOf(arg1)
]);
}
/** The method id used to dispatch <code>agentDestroyed</code> requests. */
public static const AGENT_DESTROYED :int = 3;
// from interface BureauService
public function agentDestroyed (arg1 :int) :void
{
sendRequest(AGENT_DESTROYED, [
Integer.valueOf(arg1)
]);
}
/** The method id used to dispatch <code>bureauError</code> requests. */
public static const BUREAU_ERROR :int = 4;
// from interface BureauService
public function bureauError (arg1 :String) :void
{
sendRequest(BUREAU_ERROR, [
arg1
]);
}
/** The method id used to dispatch <code>bureauInitialized</code> requests. */
public static const BUREAU_INITIALIZED :int = 5;
// from interface BureauService
public function bureauInitialized (arg1 :String) :void
{
sendRequest(BUREAU_INITIALIZED, [
arg1
]);
}
}
}
@@ -0,0 +1,44 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.bureau.util {
import com.threerings.presents.util.PresentsContext;
import com.threerings.bureau.client.BureauDirector;
/**
* Defines the objects held on a bureau client. This includes usual set of objects found on a
* standard presents client.
*/
public interface BureauContext extends PresentsContext
{
/**
* Access the director object.
*/
function getBureauDirector () :BureauDirector;
/**
* Access the bureau id.
*/
function getBureauId () :String;
}
}
@@ -0,0 +1,73 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.MessageBundle;
import com.threerings.util.Name;
import com.threerings.util.StringUtil;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.crowd.chat.data.ChatCodes;
public class BroadcastHandler extends CommandHandler
{
override public function handleCommand (
ctx :CrowdContext, speakSvc :SpeakService,
cmd :String, args :String, history :Array) :String
{
if (StringUtil.isBlank(args)) {
return "m.usage_broadcast";
}
// mogrify and verify length
var chatdir :ChatDirector = ctx.getChatDirector();
args = chatdir.mogrifyChat(args);
args = chatdir.filter(args, null, true);
if (args == null) {
return MessageBundle.compose("m.broadcast_failed", "m.filtered");
}
var err :String = chatdir.checkLength(args);
if (err != null) {
return err;
}
doBroadcast(ctx, args);
history[0] = cmd + " ";
return ChatCodes.SUCCESS;
}
override public function checkAccess (user :BodyObject) :Boolean
{
return (null == user.checkAccess(ChatCodes.BROADCAST_ACCESS, null));
}
/**
* Actually do the broadcast.
*/
protected function doBroadcast (ctx :CrowdContext, msg :String) :void
{
ctx.getChatDirector().requestBroadcast(msg);
}
}
}
@@ -0,0 +1,36 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.crowd.chat.data.ChatChannel;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
/**
* An ActionScript version of the Java ChannelSpeakService interface.
*/
public interface ChannelSpeakService extends InvocationService
{
// from Java interface ChannelSpeakService
function speak (arg1 :ChatChannel, arg2 :String, arg3 :int) :void;
}
}
@@ -0,0 +1,33 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
/**
* A marker interface to be implemented by components such that
* the chat system will not try to steal focus from them when a chat-like
* character is entered.
*/
public interface ChatCantStealFocus
{
// nothing!
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,45 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.crowd.chat.data.ChatMessage;
/**
* A chat display provides a means by which chat messages can be
* displayed. The chat display will be notified when chat messages of
* various sorts have been received by the client.
*/
public interface ChatDisplay
{
/**
* Called to clear the chat display.
*/
function clear () :void;
/**
* Called to display a chat message.
*
* @see ChatMessage
*/
function displayMessage (msg :ChatMessage) :void;
}
}
@@ -0,0 +1,43 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.Name;
/**
* Filters messages chat messages to or from the server.
*/
public interface ChatFilter
{
/**
* Filter a chat message.
* @param msg the message text to be filtered.
* @param otherUser an optional argument that represents the target or the
* speaker, depending on 'outgoing', and can be considered in filtering if
* it is provided.
* @param outgoing true if the message is going out to the server.
*
* @return the filtered message, or null to block it completely.
*/
function filter (msg :String, otherUser :Name, outgoing :Boolean) :String;
}
}
@@ -0,0 +1,43 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.client.InvocationService_InvocationListener;
import com.threerings.util.Name;
/**
* An ActionScript version of the Java ChatService interface.
*/
public interface ChatService extends InvocationService
{
// from Java interface ChatService
function away (arg1 :String) :void;
// from Java interface ChatService
function broadcast (arg1 :String, arg2 :InvocationService_InvocationListener) :void;
// from Java interface ChatService
function tell (arg1 :Name, arg2 :String, arg3 :ChatService_TellListener) :void;
}
}
@@ -0,0 +1,36 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.presents.client.InvocationService_InvocationListener;
import com.threerings.util.Long;
/**
* An ActionScript version of the Java ChatService_TellListener interface.
*/
public interface ChatService_TellListener
extends InvocationService_InvocationListener
{
// from Java ChatService_TellListener
function tellSucceeded (arg1 :Long, arg2 :String) :void
}
}
@@ -0,0 +1,37 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.crowd.chat.data.ChatMessage;
/**
* An interface for listening to chat received from the server, prior
* to it being filtered/muted/whatever.
*/
public interface ChatSnooper
{
/**
* Handle the arrival of chat. Do not modify the chat message! That would be a filter!
*/
function snoopChat (msg :ChatMessage) :void;
}
}
@@ -0,0 +1,35 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
/**
* An interface to receive information about the most recent users
* that we've been chatting with.
*/
public interface ChatterObserver
{
/**
* Called when the list of chatters has been changed.
*/
function chattersUpdated (chatterNames :Array) :void;
}
}
@@ -0,0 +1,37 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.Name;
/**
* An interface used with the ChatDirector to validate which usernames
* may be added to the chatter list.
*/
public interface ChatterValidator
{
/**
* Arbitrates whether the username may be added to the chatters list.
*/
function isChatterValid (username :Name) :Boolean;
}
}
@@ -0,0 +1,38 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.crowd.util.CrowdContext;
import com.threerings.crowd.chat.data.ChatCodes;
public class ClearHandler extends CommandHandler
{
override public function handleCommand (
ctx :CrowdContext, speakSvc :SpeakService,
cmd :String, args :String, history :Array) :String
{
ctx.getChatDirector().clearDisplays();
return ChatCodes.SUCCESS;
}
}
}
@@ -0,0 +1,66 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.util.CrowdContext;
/**
* Used to implement a slash command (e.g. <code>/who</code>).
*/
public /* abstract */ class CommandHandler
{
/**
* Handles the specified chat command.
*
* @param speakSvc an optional SpeakService object representing
* the object to send the chat message on.
* @param command the slash command that was used to invoke this
* handler (e.g. <code>/tell</code>).
* @param args the arguments provided along with the command (e.g.
* <code>Bob hello</code>) or <code>null</code> if no arguments
* were supplied.
* @param history an in/out parameter that allows the command to
* modify the text that will be appended to the chat history. If
* this is set to null, nothing will be appended.
*
* @return an untranslated string that will be reported to the
* chat box to convey an error response to the user, or {@link
* ChatCodes#SUCCESS}.
*/
public function handleCommand (
ctx :CrowdContext, speakSvc :SpeakService,
cmd :String, args :String, history :Array) :String
{
throw new Error("abstract");
}
/**
* Returns true if this user should have access to this chat
* command.
*/
public function checkAccess (user :BodyObject) :Boolean
{
return true;
}
}
}
@@ -0,0 +1,237 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.Log;
import com.threerings.util.Name;
import com.threerings.util.StringUtil;
/**
* A chat filter that can filter out curse words from user chat.
*/
public /*abstract*/ class CurseFilter implements ChatFilter
{
/** Filtration constants. */
public static const DROP :int = 0;
public static const COMIC :int = 1;
public static const VERNACULAR :int = 2;
public static const UNFILTERED :int = 3;
/**
* Creates a curse filter. The curse words should be a string in the
* following format:
*
* <pre>
* *penis*=John_Thomas shit*=barnacle muff=britches
* </pre>
*
* The key/value pairs are separated by spaces, * matches word characters
* and the value after the = is the string into which to convert the text
* when converting to the vernacular. Underscores in the target string will
* be turned into spaces.
*
* <p> And stopWords should be in the following format:
*
* <pre>
* *faggot* rape rapes raped raping
* </pre>
*
* Words are separated by spaces and * matches any other word characters.
*/
public function CurseFilter (curseWords :String, stopWords :String)
{
configureCurseWords(curseWords);
configureStopWords(stopWords);
}
/**
* The client will need to provide a way to look up our current chat filter
* mode.
*/
public function getFilterMode () :int
{
throw new Error("abstract");
}
// from interface ChatFilter
public function filter (
msg :String, otherUser :Name, outgoing :Boolean) :String
{
// first, check against the drop-always list
if (_stopExp.test(msg)) {
return null;
}
// then see what kind of curse filtering the user has configured
var level :int = getFilterMode();
if (level == UNFILTERED) {
return msg;
}
var numStops :int = _curseExps.length;
for (var ii :int = 0; ii < numStops; ii++) {
var regexp :RegExp = (_curseExps[ii] as RegExp);
regexp.lastIndex = 0; // reset the matcher
var result :Object;
var replacement :String;
while (null != (result = regexp.exec(msg))) {
switch (level) {
case DROP:
return null;
case COMIC: default:
replacement = comicChars(int(_comicLength[ii]));
break;
case VERNACULAR:
replacement = String(_vernacular[ii]);
// preserve the case of the first letter of the text
// we're replacing
if (StringUtil.isUpperCase(String(result[2]))) {
replacement = replacement.charAt(0).toUpperCase() +
replacement.substring(1);
}
break;
}
// sub in the replacement text, carrying over the pre/post
// matching groups
replacement = StringUtil.substitute(String(_replacements[ii]),
String(result[1]), replacement, String(result[3]));
// do the replacement. Note the jimmying of lastIndex:
// abjectscript does not make this smooth!
var matchLength :int = String(result[0]).length;
var matchIndex :int = int(result.index);
msg = msg.substring(0, matchIndex) + replacement +
msg.substring(matchIndex + matchLength);
regexp.lastIndex += (replacement.length - matchLength);
}
}
return msg;
}
/**
* Configure the curse word portion of our filtering.
*/
protected function configureCurseWords (curseWords :String) :void
{
var tokens :Array = curseWords.split(" ");
for each (var token :String in tokens) {
token = StringUtil.trim(token);
if (token == "") {
continue;
}
var bits :Array = token.split("=");
if (bits.length != 2) {
Log.getLog(this).warning("Something looks wrong with " +
"your cursewords (" + token + "), skipping.");
continue;
}
var curse :String = String(bits[0]);
var sub :String = "";
var patPre :String = "()";
var patPost :String = "";
if (curse.charAt(0) == "*") {
curse = curse.substring(1);
patPre = "([a-zA-Z]*)";
sub += "{0}";
}
sub += "{1}";
if (curse.charAt(curse.length - 1) == "*") {
curse = curse.substring(0, curse.length - 1);
patPost = "([a-zA-Z]*)";
sub += "{2}";
}
var regexp :RegExp = new RegExp(
"\\b" + patPre + "(" + curse + ")" + patPost + "\\b", "gi");
_curseExps.push(regexp);
_replacements.push(sub);
var slang :String = StringUtil.trim(String(bits[1]));
slang = slang.replace(/_/g, " ");
_vernacular.push(slang);
_comicLength.push(curse.length);
}
}
/**
* Configure the words that will stop.
*/
protected function configureStopWords (stopWords :String) :void
{
var tokens :Array = stopWords.split(" ");
var pattern :String = "";
for each (var token :String in tokens) {
token = StringUtil.trim(token);
if (token == "") {
continue;
}
if ("" == pattern) {
pattern += "(";
} else {
pattern += "|";
}
pattern += "\\b" +
token.replace(/\*/g, "[A-Za-z]*") + "\\b";
}
pattern += ")";
_stopExp = new RegExp(pattern, "i");
}
/**
* Return a comicy replacement of the specified length;
*/
protected function comicChars (length :int) :String
{
var str :String = "";
while (length-- > 0) {
str += COMIC_CHARS.charAt(int(Math.random() * COMIC_CHARS.length));
}
return str;
}
/** A regexp that will always cause a message to be dropped if it
* matches. */
protected var _stopExp :RegExp;
/** Regexps for each curseword. */
protected var _curseExps :Array = [];
/** Length of comic-y replacements for each curseword. */
protected var _comicLength :Array = [];
/** Replacements. */
protected var _replacements :Array = [];
/** Replacements for each curseword "in the vernacular". */
protected var _vernacular :Array = [];
/** Comic replacement characters. */
protected const COMIC_CHARS :String = "!@#%&*";
}
}
@@ -0,0 +1,44 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.StringUtil;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.crowd.chat.data.ChatCodes;
public class EmoteHandler extends CommandHandler
{
override public function handleCommand (
ctx :CrowdContext, speakSvc :SpeakService,
cmd :String, args :String, history :Array) :String
{
if (StringUtil.isBlank(args)) {
return "m.usage_emote";
}
history[0] = cmd + " ";
return ctx.getChatDirector().deliverChat(
speakSvc, args, ChatCodes.EMOTE_MODE);
}
}
}
@@ -0,0 +1,79 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.Map;
import com.threerings.util.MessageBundle;
import com.threerings.util.StringUtil;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.crowd.chat.data.ChatCodes;
public class HelpHandler extends CommandHandler
{
override public function handleCommand (
ctx :CrowdContext, speakSvc :SpeakService,
cmd :String, args :String, history :Array) :String
{
var hcmd :String = "";
// grab the command they want help on
if (!StringUtil.isBlank(args)) {
hcmd = args;
var sidx :int = args.indexOf(" ");
if (sidx != -1) {
hcmd = args.substring(0, sidx);
}
}
// let the user give commands with or without the /
if (hcmd.charAt(0) == "/") {
hcmd = hcmd.substring(1);
}
var chatdir :ChatDirector = ctx.getChatDirector();
// handle "/help help" and "/help boguscmd"
var possibleCmds :Map = chatdir.getCommandHandlers(hcmd);
if ((hcmd === "help") || (possibleCmds.size() == 0)) {
possibleCmds = chatdir.getCommandHandlers("");
possibleCmds.remove("help"); // remove help from the list
}
switch (possibleCmds.size()) {
case 1:
chatdir.displayFeedback(null, "m.usage_" + possibleCmds.keys()[0]);
return ChatCodes.SUCCESS;
default:
var cmds :Array = possibleCmds.keys();
cmds.sort();
var cmdList :String = "";
for each (var skey :String in cmds) {
cmdList += " /" + skey;
}
return MessageBundle.tcompose("m.usage_help", cmdList);
}
}
}
}
@@ -0,0 +1,161 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.MessageBundle;
import com.threerings.util.Name;
import com.threerings.util.ObserverList;
import com.threerings.util.Set;
import com.threerings.util.Sets;
import com.threerings.presents.client.BasicDirector;
import com.threerings.crowd.util.CrowdContext;
/**
* Manages the mutelist.
*
* TODO: This class right now is pretty much just a placeholder.
*/
public class MuteDirector extends BasicDirector
implements ChatFilter
{
/**
* Should be instantiated after the ChatDirector.
*/
public function MuteDirector (ctx :CrowdContext)
{
super(ctx);
}
/**
* Called to shut down the mute director.
*/
public function shutdown () :void
{
if (_chatdir != null) {
_chatdir.removeChatFilter(this);
_chatdir = null;
}
}
/**
* Set the required ChatDirector.
*/
public function setChatDirector (chatdir :ChatDirector) :void
{
if (_chatdir == null) {
_chatdir = chatdir;
_chatdir.addChatFilter(this);
}
}
/**
* Add the specified mutelist observer.
*/
public function addMuteObserver (obs :MuteObserver) :void
{
_observers.add(obs);
}
/**
* Remove the specified mutelist observer.
*/
public function removeMuteObserver (obs :MuteObserver) :void
{
_observers.remove(obs);
}
/**
* Check to see if the specified user is muted.
*/
public function isMuted (username :Name) :Boolean
{
return _mutelist.contains(username);
}
/**
* Mute or unmute the specified user.
*/
public function setMuted (username :Name, mute :Boolean, giveFeedback :Boolean = true) :void
{
var changed :Boolean = mute ? _mutelist.add(username) : _mutelist.remove(username);
if (giveFeedback) {
var feedback :String = mute ? "m.muted"
: (changed ? "m.unmuted" : "m.notmuted");
_chatdir.displayFeedback(null, MessageBundle.tcompose(feedback, username));
}
// if the mutelist actually changed, notify observers
if (changed) {
notifyObservers(username, mute);
}
}
/**
* @return a list of the currently muted players.
*
* This list may be out of date immediately upon returning from this method.
*/
public function getMuted () :Array /* of Name */
{
return _mutelist.toArray();
}
// documentation inherited from interface ChatFilter
public function filter (msg :String, otherUser :Name, outgoing :Boolean) :String
{
// we are only concerned with filtering things going to or coming
// from muted users
if ((otherUser != null) && isMuted(otherUser)) {
// if it was outgoing, explain the dropped message, otherwise
// silently drop
if (outgoing) {
_chatdir.displayFeedback(null, "m.no_tell_mute");
}
return null;
}
return msg;
}
/**
* Notify our observers of a change in the mutelist.
*/
protected function notifyObservers (username :Name, muted :Boolean) :void
{
_observers.apply(function (observer :MuteObserver) :void {
observer.muteChanged(username, muted);
});
}
/** The chat director that we're working hard for. */
protected var _chatdir :ChatDirector;
/** The mutelist. */
protected var _mutelist :Set = Sets.newSetOf(Name);
/** List of mutelist observers. */
protected var _observers :ObserverList = new ObserverList(ObserverList.FAST_UNSAFE_NOTIFY);
}
}
@@ -0,0 +1,37 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.Name;
/**
* An interface that can be registered with the MuteDirector to receive
* notifications to the mutelist.
*/
public interface MuteObserver
{
/**
* The specified player was added or removed from the mutelist.
*/
function muteChanged (playername :Name, nowMuted :Boolean) :void;
}
}
@@ -0,0 +1,43 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.StringUtil;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.crowd.chat.data.ChatCodes;
public class SpeakHandler extends CommandHandler
{
override public function handleCommand (
ctx :CrowdContext, speakSvc :SpeakService,
cmd :String, args :String, history :Array) :String
{
if (StringUtil.isBlank(args)) {
return "m.usage_speak";
}
history[0] = cmd + " ";
return ctx.getChatDirector().requestChat(null, args, true);
}
}
}
@@ -0,0 +1,35 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
/**
* An ActionScript version of the Java SpeakService interface.
*/
public interface SpeakService extends InvocationService
{
// from Java interface SpeakService
function speak (arg1 :String, arg2 :int) :void;
}
}
@@ -0,0 +1,46 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.Long;
import com.threerings.presents.client.InvocationAdapter;
public class TellAdapter extends InvocationAdapter
implements ChatService_TellListener
{
public function TellAdapter (failedFunc :Function, successFunc :Function)
{
super(failedFunc);
_successFunc = successFunc;
}
// documentation inherited from interface TellListener
public function tellSucceeded (idleTime :Long, awayMsg :String) :void
{
_successFunc(idleTime, awayMsg);
}
/** The method to call on success. */
protected var _successFunc :Function;
}
}
@@ -0,0 +1,171 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.Name;
import com.threerings.util.ResultAdapter;
import com.threerings.util.ResultListener;
import com.threerings.util.StringUtil;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.crowd.chat.data.ChatCodes;
public class TellHandler extends CommandHandler
{
override public function handleCommand (
ctx :CrowdContext, speakSvc :SpeakService,
cmd :String, args :String, history :Array) :String
{
if (StringUtil.isBlank(args)) {
return "m.usage_tell";
}
var chatdir :ChatDirector = ctx.getChatDirector();
var useQuotes :Boolean = (args.charAt(0) == "\"");
var bits :Array = parseTell(args);
var handle :String = (bits[0] as String);
var message :String = (bits[1] as String);
// validate that we didn't eat all the tokens making the handle
if (StringUtil.isBlank(message)) {
return "m.usage_tell";
}
// make sure we're not trying to tell something to ourselves
var self :BodyObject =
(ctx.getClient().getClientObject() as BodyObject);
if (handle.toLowerCase() ===
self.getVisibleName().toString().toLowerCase()) {
return "m.talk_self";
}
// and lets just give things an opportunity to sanitize the name
var target :Name = normalizeAsName(handle);
// mogrify the chat
message = chatdir.mogrifyChat(message);
var err :String = chatdir.checkLength(message);
if (err != null) {
return err;
}
// clear out from the history any tells that are mistypes
var fullHist :Array = chatdir.accessHistory();
for (var ii :int = fullHist.length - 1; ii >= 0; ii--) {
var hist :String = (fullHist[ii] as String);
if (0 == hist.indexOf("/" + cmd)) {
var harg :String = StringUtil.trim(
hist.substring(cmd.length + 1));
// we blow away any historic tells that have msg content
if (!StringUtil.isBlank(parseTell(harg)[1] as String)) {
fullHist.splice(ii, 1);
}
}
}
// store the full command in the history, even if it was mistyped
var histEntry :String = cmd + " " +
(useQuotes ? ("\"" + target + "\"") : target.toString()) +
" " + message;
history[0] = histEntry;
var rl :ResultListener = new ResultAdapter(
function (result :Object) :void {
// replace the full one in the history with just:
// /tell "<handle>"
var newEntry :String = "/" + cmd + " " +
(useQuotes ? ("\"" + result + "\"")
: result.toString()) + " ";
var fullHist :Array = chatdir.accessHistory();
var dex :int = fullHist.indexOf(newEntry);
if (dex != -1) {
fullHist.splice(dex, 1);
}
var del :int = fullHist.lastIndexOf("/" + histEntry);
if (del >= 0) {
fullHist[del] = newEntry;
} else {
fullHist.push(newEntry);
}
}, null);
chatdir.requestTell(target, message, rl);
return ChatCodes.SUCCESS;
}
/**
* Parse the tell into two strings, handle and message. If either
* one is null then the parsing did not succeed.
*/
protected function parseTell (args :String) :Array
{
var handle :String;
var message :String;
if (args.charAt(0) == "\"") {
var nextQuote :int = args.indexOf("\"", 1);
if (nextQuote == -1 || nextQuote == 1) {
handle = message = null; // bogus parsing
} else {
handle = StringUtil.trim(args.substring(1, nextQuote));
message = StringUtil.trim(args.substring(nextQuote + 1));
}
} else {
var idx :int = args.search(/\s/);
if (idx == -1) {
handle = args;
message = "";
} else {
handle = StringUtil.trim(args.substring(0, idx));
message = StringUtil.trim(args.substring(idx));
}
}
return [ handle, message ];
}
/**
* Turn the user-entered string into a Name object, doing
* any particular normalization we want to do along the way
* so that "/tell Bob" and "/tell BoB" don't both show up in history.
*/
protected function normalizeAsName (handle :String) :Name
{
return new Name(handle);
}
/**
* Escape or otherwise do any final processing on the message
* prior to sending it.
*/
protected function escapeMessage (msg :String) :String
{
return msg;
}
}
}
@@ -0,0 +1,44 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client {
import com.threerings.util.StringUtil;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.crowd.chat.data.ChatCodes;
public class ThinkHandler extends CommandHandler
{
override public function handleCommand (
ctx :CrowdContext, speakSvc :SpeakService,
cmd :String, args :String, history :Array) :String
{
if (StringUtil.isBlank(args)) {
return "m.usage_think";
}
history[0] = cmd + " ";
return ctx.getChatDirector().deliverChat(
speakSvc, args, ChatCodes.THINK_MODE);
}
}
}
@@ -0,0 +1,50 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.crowd.chat.client.ChannelSpeakService;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.util.Byte;
/**
* Provides the implementation of the <code>ChannelSpeakService</code> 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 ChannelSpeakMarshaller extends InvocationMarshaller
implements ChannelSpeakService
{
/** The method id used to dispatch <code>speak</code> requests. */
public static const SPEAK :int = 1;
// from interface ChannelSpeakService
public function speak (arg1 :ChatChannel, arg2 :String, arg3 :int) :void
{
sendRequest(SPEAK, [
arg1, arg2, Byte.valueOf(arg3)
]);
}
}
}
@@ -0,0 +1,61 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.SimpleStreamableObject;
import com.threerings.util.Comparable;
import com.threerings.util.Equalable;
import com.threerings.util.Hashable;
import com.threerings.presents.dobj.DSet_Entry;
/**
* Represents a chat channel.
*/
public /*abstract*/ class ChatChannel extends SimpleStreamableObject
implements Comparable, Hashable, Equalable, DSet_Entry
{
// from interface Comparable
public function compareTo (other :Object) :int
{
throw new Error("abstract");
}
// from interface Hashable
public function hashCode () :int
{
throw new Error("abstract");
}
// from interface Equalable
public function equals (other :Object) :Boolean
{
return compareTo(other) == 0;
}
// from interface DSet_Entry
public function getKey () :Object
{
return this;
}
}
}
@@ -0,0 +1,94 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.presents.data.InvocationCodes;
import com.threerings.presents.data.Permission;
import com.threerings.crowd.data.BodyObject;
/**
* Contains codes used by the chat invocation services.
*/
public class ChatCodes extends InvocationCodes
{
/** A return value used by the ChatDirector and possibly other entities
* to indicate successful processing of chat. */
public static const SUCCESS :String = "success";
/** The message identifier for a chat notification message. */
public static const CHAT_NOTIFICATION :String = "crowd.chat";
/** The message identifier for a chat channel notification message. */
public static const CHAT_CHANNEL_NOTIFICATION :String = "crowd.chat.channel";
/** The access control identifier for normal chat privileges. */
public static const CHAT_ACCESS :Permission = new Permission();
/** The access control identifier for broadcast chat privileges. */
public static const BROADCAST_ACCESS :Permission = new Permission();
/** The configuration key for idle time. */
public static const IDLE_TIME_KEY :String = "narya.chat.idle_time";
/** The default time after which a player is assumed idle. */
public static const DEFAULT_IDLE_TIME :Number = 3 * 60 * 1000;
/** The chat localtype code for chat messages delivered on the place
* object currently occupied by the client. This is the only type of
* chat message that will be delivered unless the chat director is
* explicitly provided with other chat message sources via {@link
* ChatDirector#addAuxiliarySource}. */
public static const PLACE_CHAT_TYPE :String = "placeChat";
/** The chat localtype for messages received on the user object. */
public static const USER_CHAT_TYPE :String = "userChat";
/** The default mode used by {@link SpeakService#speak} requests. */
public static const DEFAULT_MODE :int = 0;
/** A {@link SpeakService#speak} mode to indicate that the user is
* thinking what they're saying, or is it that they're saying what
* they're thinking? */
public static const THINK_MODE :int = 1;
/** A {@link SpeakService#speak} mode to indicate that a speak is
* actually an emote. */
public static const EMOTE_MODE :int = 2;
/** A {@link SpeakService#speak} mode to indicate that a speak is
* actually a shout. */
public static const SHOUT_MODE :int = 3;
/** A {@link SpeakService#speak} mode to indicate that a speak is
* actually a server-wide broadcast. */
public static const BROADCAST_MODE :int = 4;
/** An error code delivered when the user targeted for a tell
* notification is not online. */
public static const USER_NOT_ONLINE :String = "m.user_not_online";
/** An error code delivered when the user targeted for a tell
* notification is disconnected. */
public static const USER_DISCONNECTED :String = "m.user_disconnected";
}
}
@@ -0,0 +1,79 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.crowd.chat.client.ChatService;
import com.threerings.crowd.chat.client.ChatService_TellListener;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService_InvocationListener;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller;
import com.threerings.util.Name;
/**
* Provides the implementation of the <code>ChatService</code> 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 ChatMarshaller extends InvocationMarshaller
implements ChatService
{
/** The method id used to dispatch <code>away</code> requests. */
public static const AWAY :int = 1;
// from interface ChatService
public function away (arg1 :String) :void
{
sendRequest(AWAY, [
arg1
]);
}
/** The method id used to dispatch <code>broadcast</code> requests. */
public static const BROADCAST :int = 2;
// from interface ChatService
public function broadcast (arg1 :String, arg2 :InvocationService_InvocationListener) :void
{
var listener2 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener2.listener = arg2;
sendRequest(BROADCAST, [
arg1, listener2
]);
}
/** The method id used to dispatch <code>tell</code> requests. */
public static const TELL :int = 3;
// from interface ChatService
public function tell (arg1 :Name, arg2 :String, arg3 :ChatService_TellListener) :void
{
var listener3 :ChatMarshaller_TellMarshaller = new ChatMarshaller_TellMarshaller();
listener3.listener = arg3;
sendRequest(TELL, [
arg1, arg2, listener3
]);
}
}
}
@@ -0,0 +1,52 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.crowd.chat.client.ChatService_TellListener;
import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller;
import com.threerings.util.Long;
/**
* Marshalls instances of the ChatService_TellMarshaller interface.
*/
public class ChatMarshaller_TellMarshaller
extends InvocationMarshaller_ListenerMarshaller
{
/** The method id used to dispatch <code>tellSucceeded</code> responses. */
public static const TELL_SUCCEEDED :int = 1;
// from InvocationMarshaller_ListenerMarshaller
override public function dispatchResponse (methodId :int, args :Array) :void
{
switch (methodId) {
case TELL_SUCCEEDED:
(listener as ChatService_TellListener).tellSucceeded(
(args[0] as Long), (args[1] as String));
return;
default:
super.dispatchResponse(methodId, args);
return;
}
}
}
}
@@ -0,0 +1,100 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 flash.utils.getTimer; // function import
import com.threerings.util.ClassUtil;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;
/**
* The abstract base class of all the client-side ChatMessage objects.
*/
public /*abstract*/ class ChatMessage
implements Streamable
{
/** The actual text of the message. */
public var message :String;
/** The bundle to use when translating this message. */
public var bundle :String;
/** The client side 'localtype' of this chat, set to the type registered with an auxiliary
* source in the ChatDirector. */
public var localtype :String;
/** The client time that this message was created. */
public var timestamp :int;
public function ChatMessage (msg :String = null, bundle :String = null)
{
this.message = msg;
this.bundle = bundle;
}
/**
* Once this message reaches the client, the information contained within
* is changed around a bit.
*/
public function setClientInfo (msg :String, localtype :String) :void
{
message = msg;
this.localtype = localtype;
bundle = null;
timestamp = getTimer();
}
/**
* Generates a string representation of this instance.
*/
public function toString () :String
{
return ClassUtil.shortClassName(this) +
" [message=" + message + ", bundle=" + bundle + "]";
}
/**
* Get the appropriate message format for this message.
*/
public function getFormat () :String
{
return null;
}
// from interface Streamable
public function readObject (ins :ObjectInputStream) :void
{
message = (ins.readField(String) as String);
bundle = (ins.readField(String) as String);
}
// from interface Streamable
public function writeObject (out :ObjectOutputStream) :void
{
out.writeField(message);
out.writeField(bundle);
}
}
}
@@ -0,0 +1,50 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.crowd.chat.client.SpeakService;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.util.Byte;
/**
* Provides the implementation of the <code>SpeakService</code> 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 <code>speak</code> requests. */
public static const SPEAK :int = 1;
// from interface SpeakService
public function speak (arg1 :String, arg2 :int) :void
{
sendRequest(SPEAK, [
arg1, Byte.valueOf(arg2)
]);
}
}
}
@@ -0,0 +1,67 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
/**
* A ChatMessage that represents a message that came from the server
* and did not result from direct user action.
*/
public class SystemMessage extends ChatMessage
{
/** Attention level constant to indicate that this message is merely providing the user with
* information. */
public static const INFO :int = 0;
/** Attention level constant to indicate that this message is the result of a user action. */
public static const FEEDBACK :int = 1;
/** Attention level constant to indicate that some action is required. */
public static const ATTENTION :int = 2;
/** The attention level of this message. */
public var attentionLevel :int;
public function SystemMessage (
msg :String = null, bundle :String = null, attLevel :int = 0)
{
super(msg, bundle);
this.attentionLevel = attLevel;
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
attentionLevel = ins.readByte();
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeByte(attentionLevel);
}
}
}
@@ -0,0 +1,71 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.util.Name;
/**
* A feedback message to indicate that a tell succeeded.
*/
public class TellFeedbackMessage extends UserMessage
{
/**
* A tell feedback message is only composed on the client.
*/
public function TellFeedbackMessage (target :Name, message :String, failed :Boolean = false)
{
super(target, null, message);
_failure = failed;
}
/**
* Returns true if this is a failure feedback, false if it is successful tell feedback.
*/
public function isFailure () :Boolean
{
return _failure;
}
override public function getFormat () :String
{
return _failure ? null : "m.told_format";
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
_failure = ins.readBoolean();
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeBoolean(_failure);
}
protected var _failure :Boolean;
}
}
@@ -0,0 +1,93 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.Name;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
/**
* A ChatMessage representing a message that came from another user.
*/
public class UserMessage extends ChatMessage
{
/** The user that the message came from. */
public var speaker :Name;
/** The mode of the message. @see ChatCodes.DEFAULT_MODE */
public var mode :int;
/**
* Constructs a user message for a player originated tell (which has no
* bundle and is in the default mode).
*/
public function UserMessage (speaker :Name = null, bundle :String = null,
message :String = null, mode :int = 0)
{
super(message, bundle);
this.speaker = speaker;
this.mode = mode;
}
/**
* Returns the name to display for the speaker. Some types of messages
* may wish to not use the canonical name for the speaker and should thus
* override this function.
*/
public function getSpeakerDisplayName () :Name
{
return speaker;
}
override public function getFormat () :String
{
switch (mode) {
case ChatCodes.THINK_MODE: return "m.think_format";
case ChatCodes.EMOTE_MODE: return "m.emote_format";
case ChatCodes.SHOUT_MODE: return "m.shout_format";
case ChatCodes.BROADCAST_MODE: return "m.broadcast_format";
}
if (ChatCodes.USER_CHAT_TYPE === localtype) {
return "m.tell_format";
}
return "m.speak_format";
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
speaker = Name(ins.readObject());
mode = ins.readByte();
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeObject(speaker);
out.writeByte(mode);
}
}
}
@@ -0,0 +1,64 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.Name;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
/**
* A system message triggered by the activity of another user.
* If the user is muted we can suppress this message, unlike a normal
* system message.
*/
public class UserSystemMessage extends SystemMessage
{
/** The "speaker" of this message, the user that triggered that this message be sent to us. */
public var speaker :Name;
/**
* Construct a UserSystemMessage.
*/
public function UserSystemMessage (
sender :Name = null, message :String = null, bundle :String = null,
attLevel :int = 0)
{
super(message, bundle, attLevel);
this.speaker = sender;
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
speaker = Name(ins.readObject());
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeObject(speaker);
}
}
}
@@ -0,0 +1,35 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.presents.client.Client;
import com.threerings.presents.client.InvocationService;
/**
* An ActionScript version of the Java BodyService interface.
*/
public interface BodyService extends InvocationService
{
// from Java interface BodyService
function setIdle (arg1 :Boolean) :void;
}
}
@@ -0,0 +1,46 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.presents.client.Client;
import com.threerings.presents.net.Credentials;
import com.threerings.crowd.data.BodyMarshaller;
import com.threerings.crowd.data.CrowdPermissionPolicy;
/**
* Users of the Crowd services should extend this client so that it can ensure that certain
* ActionScript classes that will arrive over the wire as a result of using basic Crowd services
* will be included in the client.
*/
public class CrowdClient extends Client
{
// statically reference classes we require
BodyMarshaller;
CrowdPermissionPolicy;
public function CrowdClient (creds :Credentials)
{
super(creds);
}
}
}
@@ -0,0 +1,64 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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;
public class LocationAdapter
implements LocationObserver
{
public function LocationAdapter (
mayChange :Function = null, didChange :Function = null,
changeFailed :Function = null)
{
_mayChange = mayChange;
_didChange = didChange;
_changeFailed = changeFailed;
}
// documentation inherited from interface LocationObserver
public function locationMayChange (placeId :int) :Boolean
{
return (_mayChange == null) || _mayChange(placeId);
}
// documentation inherited from interface LocationObserver
public function locationDidChange (place :PlaceObject) :void
{
if (_didChange != null) {
_didChange(place);
}
}
// documentation inherited from interface LocationObserver
public function locationChangeFailed (placeId :int, reason :String) :void
{
if (_changeFailed != null) {
_changeFailed(placeId, reason);
}
}
protected var _mayChange :Function;
protected var _didChange :Function;
protected var _changeFailed :Function;
}
}
@@ -0,0 +1,70 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client.LocationReceiver;
import com.threerings.presents.client.InvocationDecoder;
/**
* Dispatches calls to a {@link LocationReceiver} instance.
*/
public class LocationDecoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static const RECEIVER_CODE :String = "58f2830e027f4f3377e100ef12332497";
/** The method id used to dispatch {@link LocationReceiver#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 LocationDecoder (receiver :LocationReceiver)
{
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 LocationReceiver).forcedMove(
(args[0] as int)
);
return;
default:
super.dispatchNotification(methodId, args);
}
}
}
}
@@ -0,0 +1,616 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 flash.utils.getTimer; // function import
import com.threerings.util.ObserverList;
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.presents.dobj.DObject;
import com.threerings.presents.dobj.ObjectAccessError;
import com.threerings.presents.dobj.Subscriber;
import com.threerings.presents.dobj.SubscriberAdapter;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.CrowdCodes;
import com.threerings.crowd.data.LocationMarshaller;
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 Subscriber, LocationReceiver
{
// statically reference classes we require
LocationMarshaller;
/**
* 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 the controller for the location we currently occupy or null if we're not currently
* occupying any location.
*/
public function getPlaceController () :PlaceController
{
return _controller;
}
/**
* 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", 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;
// documentation inherited from interface MoveListener
var success :Function = function (config :PlaceConfig) :void {
// handle the successful move
didMoveTo(_pendingPlaceId, config);
// and clear out the tracked pending oid
_pendingPlaceId = -1;
handlePendingForcedMove();
};
// documentation inherited from interface MoveListener
var failure :Function = function (reason :String) :void {
// clear out our pending request oid
var placeId :int = _pendingPlaceId;
_pendingPlaceId = -1;
log.info("moveTo failed", "pid", placeId, "reason", reason);
// let our observers know that something has gone horribly awry
notifyFailure(placeId, reason);
handlePendingForcedMove();
};
// issue a moveTo request
log.info("Issuing moveTo", "placeId", placeId);
_lservice.moveTo(placeId, new MoveAdapter(success, failure));
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;
}
// if we're not actually in a place, then no need to do anything
if (_placeId > 0) {
_lservice.leavePlace();
didLeavePlace();
// let our observers know that we're no longer in a location
_observers.apply(didChangeOp);
}
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) :void {
var lobs :LocationObserver = (obs as LocationObserver);
vetoed = vetoed || !lobs.locationMayChange(placeId);
});
// 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 MoveVetoedError());
} else {
_moveListener = rl;
}
}
// and return the result
return !vetoed;
}
/**
* Called to inform our controller that we may be leaving the current place.
*/
protected function mayLeavePlace () :void
{
if (_controller != null) {
try {
_controller.mayLeavePlace(_plobj);
} catch (e :Error) {
log.warning("Place controller choked in mayLeavePlace", "plobj", _plobj, 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 function didMoveTo (placeId :int, config :PlaceConfig) :void
{
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
_controller = config.createController();
if (_controller == null) {
log.warning("Place config returned null controller", "config", config);
return;
}
_controller.init(_cctx, config);
// 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 function didLeavePlace () :void
{
if (_plobj != null) {
// let the old controller know that things are going away
if (_controller != null) {
try {
_controller.didLeavePlace(_plobj);
} catch (e :Error) {
log.warning("Place controller choked in didLeavePlace", "plobj", _plobj, e);
}
_controller = null;
}
// let the chat director know that we're leaving this place
_cctx.getChatDirector().leftLocation(_plobj);
// 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 function failedToMoveTo (placeId :int, reason :String) :void
{
if (_moveListener != null) {
_moveListener.requestFailed(new MoveFailedError(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 function checkRepeatMove () :Boolean
{
var now :Number = getTimer();
if (now - _lastRequestTime < STALE_REQUEST_DURATION) {
return true;
} else {
_lastRequestTime = now;
return false;
}
}
// documentation inherited from interface
override public function clientDidLogon (event :ClientEvent) :void
{
super.clientDidLogon(event);
var success :Function = function (object :DObject) :void {
gotBodyObject(object as BodyObject);
};
var failure :Function = function (oid :int, cause :ObjectAccessError) :void {
log.warning("Unable to fetch body object; all has gone horribly wrong", cause);
};
var client :Client = event.getClient();
client.getDObjectManager().subscribeToObject(
client.getClientOid(), new SubscriberAdapter(success, failure));
}
// documentation inherited
override public function clientDidLogoff (event :ClientEvent) :void
{
super.clientDidLogoff(event);
// 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;
_pendingForcedMoves = [];
_previousPlaceId = -1;
_lastRequestTime = 0;
_lservice = null;
}
// from BasicDirector
override protected function registerServices (client :Client) :void
{
client.addServiceGroup(CrowdCodes.CROWD_GROUP);
}
// from BasicDirector
override protected function fetchServices (client :Client) :void
{
// obtain our service handle
_lservice = (client.requireService(LocationService) as LocationService);
}
protected function gotBodyObject (clobj :BodyObject) :void
{
// 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 function forcedMove (placeId :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()) {
if (_pendingPlaceId == placeId) {
log.info("Dropping forced move because we have a move pending",
"pendId", _pendingPlaceId, "reqId", placeId);
} else {
log.info("Delaying forced move because we have a move pending",
"pendId", _pendingPlaceId, "reqId", placeId);
addPendingForcedMove(new function() :void {
forcedMove(placeId);
});
}
return;
}
log.info("Moving at request of server", "placeId", placeId);
// clear out our old place information
mayLeavePlace();
didLeavePlace();
// move to the new place
moveTo(placeId);
}
// documentation inherited from interface Subscriber
public function objectAvailable (object :DObject) :void
{
// yay, we have our new place object
_plobj = (object as PlaceObject);
// let the place controller know that we're ready to roll
if (_controller != null) {
try {
_controller.willEnterPlace(_plobj);
} catch (e :Error) {
log.warning("Controller choked in willEnterPlace", "place", _plobj, e);
}
}
// let the chat director know that we're entering this place
_cctx.getChatDirector().enteredLocation(_plobj);
// let our observers know that all is well on the western front
_observers.apply(didChangeOp);
}
// documentation inherited from interface Subscriber
public function requestFailed (oid :int, cause :ObjectAccessError) :void
{
// 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
var placeId :int = _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 function setFailureHandler (handler :LocationDirector_FailureHandler) :void
{
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;
}
}
/**
* The operation used to inform observers that the location changed.
*/
protected function didChangeOp (obs :Object) :void
{
(obs as LocationObserver).locationDidChange(_plobj);
};
protected function notifyFailure (placeId :int, reason :String) :void
{
_observers.apply(function (obs :Object) :void {
(obs as LocationObserver).locationChangeFailed(placeId, reason);
});
}
public function addPendingForcedMove (move :Function) :void
{
_pendingForcedMoves.push(move);
}
protected function handlePendingForcedMove () :void
{
if (!_pendingForcedMoves.length == 0) {
_ctx.getClient().callLater(_pendingForcedMoves.pop());
}
}
protected const log :Log = Log.getLog(this);
/** The context through which we access needed services. */
protected var _cctx :CrowdContext;
/** Provides access to location services. */
protected var _lservice :LocationService;
/** Our location observer list. */
protected var _observers :ObserverList = new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY);
/** The oid of the place we currently occupy. */
protected var _placeId :int = -1;
/** The place object that we currently occupy. */
protected var _plobj :PlaceObject;
/** The place controller in effect for our current place. */
protected var _controller :PlaceController;
/** The place oid to whihc we have an outstanding moveTo request, or -1 if we have none. */
protected var _pendingPlaceId :int = -1;
/** The oid of the place we previously occupied. */
protected var _previousPlaceId :int = -1;
/** The last time we requested a move to. */
protected var _lastRequestTime :Number;
/** The entity that deals when we fail to subscribe to a place object. */
protected var _failureHandler :LocationDirector_FailureHandler;
/** A listener that wants to know if we succeeded or how we failed to move. */
protected var _moveListener :ResultListener;
/** Forced move actions we should take once we complete the move we're in the middle of. */
protected var _pendingForcedMoves :Array = [];
/** Allow a moveTo request be outstanding for one minute before it is declared to be stale. */
protected static const STALE_REQUEST_DURATION :int = 60 * 1000;
}
}
@@ -0,0 +1,38 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 {
/**
* 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 interface LocationDirector_FailureHandler
{
/**
* Should instruct the client to move to the last known working
* location (as well as clean up after the failed moveTo request).
*/
function recoverFailedMove (placeId :int) :void;
}
}
@@ -0,0 +1,68 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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;
/**
* The location observer interface makes it possible for entities to be
* notified when the client moves to a new location. It also provides a
* means for an entity to participate in the ratification process of a new
* location. Observers may opt to reject a request to change to a new
* location, probably because something is going on in the previous
* location that should not be abandoned.
*
* <p> Note that these location callbacks occur on the main thread and
* should execute quickly and not block under any circumstance.
*/
public interface LocationObserver
{
/**
* Called when someone has requested that we switch to a new location.
* An observer may choose to veto the location change request for some
* reason or other.
*
* @return true if it's OK for the location to change, false if the
* change request should be aborted.
*/
function locationMayChange (placeId :int) :Boolean;
/**
* Called when we have switched to a new location.
*
* @param place the place object that represents the new location or
* null if we have switched to no location.
*/
function locationDidChange (place :PlaceObject) :void;
/**
* This is called on all location observers when a location change
* request is rejected by the server or fails for some other reason.
*
* @param placeId the place id to which we attempted to relocate, but
* failed.
* @param reason the reason code that explains why the location change
* request was rejected or otherwise failed.
*/
function locationChangeFailed (placeId :int, reason :String) :void;
}
}
@@ -0,0 +1,40 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.presents.client.InvocationReceiver;
/**
* Defines, for the location services, a set of notifications delivered
* asynchronously by the server to the client.
*/
public interface LocationReceiver extends InvocationReceiver
{
/**
* Used to communicate a required move notification to the client. The
* server will have removed the client from their existing location
* and the client is then responsible for generating a {@link
* LocationService#moveTo} request to move to the new location.
*/
function forcedMove (placeId :int) :void;
}
}
@@ -0,0 +1,38 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.presents.client.Client;
import com.threerings.presents.client.InvocationService;
/**
* An ActionScript version of the Java LocationService interface.
*/
public interface LocationService extends InvocationService
{
// from Java interface LocationService
function leavePlace () :void;
// from Java interface LocationService
function moveTo (arg1 :int, arg2 :LocationService_MoveListener) :void;
}
}
@@ -0,0 +1,36 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.PlaceConfig;
import com.threerings.presents.client.InvocationService_InvocationListener;
/**
* An ActionScript version of the Java LocationService_MoveListener interface.
*/
public interface LocationService_MoveListener
extends InvocationService_InvocationListener
{
// from Java LocationService_MoveListener
function moveSucceeded (arg1 :PlaceConfig) :void
}
}
@@ -0,0 +1,45 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.presents.client.InvocationAdapter;
import com.threerings.crowd.data.PlaceConfig;
public class MoveAdapter extends InvocationAdapter
implements LocationService_MoveListener
{
public function MoveAdapter (success :Function, failure :Function)
{
super(failure);
_success = success;
}
// documentation inherited from interface MoveListener
public function moveSucceeded (config :PlaceConfig) :void
{
_success(config);
}
protected var _success :Function;
}
}
@@ -0,0 +1,34 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 {
/**
* An exception that indicates that the server did not allow us to move.
*/
public class MoveFailedError extends Error
{
public function MoveFailedError (message :String)
{
super(message);
}
}
}
@@ -0,0 +1,30 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 {
/**
* An exception that indicates that a LocationObserver vetoed our move request.
*/
public class MoveVetoedError extends Error
{
}
}
@@ -0,0 +1,65 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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;
public class OccupantAdapter
implements OccupantObserver
{
public function OccupantAdapter (
entered :Function = null, left :Function = null, updated :Function = null)
{
_entered = entered;
_left = left;
_updated = updated;
}
// from interface OccupantObserver
public function occupantEntered (info :OccupantInfo) :void
{
if (_entered != null) {
_entered(info);
}
}
// from interface OccupantObserver
public function occupantLeft (info :OccupantInfo) :void
{
if (_left != null) {
_left(info);
}
}
// from interface OccupantObserver
public function occupantUpdated (oldinfo :OccupantInfo, newinfo :OccupantInfo) :void
{
if (_updated != null) {
_updated(oldinfo, newinfo);
}
}
protected var _entered :Function;
protected var _left :Function;
protected var _updated :Function;
}
}
@@ -0,0 +1,204 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.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.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
override public 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) :void {
(obj as OccupantObserver).occupantEntered(info);
});
}
// 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) :void {
(obj as OccupantObserver).occupantUpdated(oinfo, info);
});
}
// 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) :void {
(obj as OccupantObserver).occupantLeft(oinfo);
});
}
/** 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$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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,257 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.Controller;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext;
/**
* Controls the user interface that is used to display a place. When the
* client moves to a new place, the appropriate place controller is
* constructed and requested to create and display the appopriate user
* interface for that place.
*/
public /*abstract*/ class PlaceController extends Controller
{
/**
* Initializes this place controller with a reference to the context
* that they can use to access client services and to the
* configuration record for this place. The controller should create
* as much of its user interface that it can without having access to
* the place object because this will be invoked in parallel with the
* fetching of the place object. When the place object is obtained,
* the controller will be notified and it can then finish the user
* interface configuration and put the user interface into operation.
*
* @param ctx the client context.
* @param config the place configuration for this place.
*/
public function init (ctx :CrowdContext, config :PlaceConfig) :void
{
// keep these around
_ctx = ctx;
_config = config;
// create our user interface
_view = createPlaceView(_ctx);
// initialize our delegates
applyToDelegates(function (del :PlaceControllerDelegate) :void {
del.init(_ctx, _config);
});
// let the derived classes do any initialization stuff
didInit();
}
/**
* Derived classes can override this and perform any
* post-initialization processing they might need. They should of
* course be sure to call <code>super.didInit()</code>.
*/
protected function didInit () :void
{
}
/**
* Returns a reference to the place view associated with this
* controller. This is only valid after a call has been made to {@link
* #init}.
*/
public function getPlaceView () :PlaceView
{
return _view;
}
/**
* Returns the {@link PlaceConfig} associated with this place.
*/
public function getPlaceConfig () :PlaceConfig
{
return _config;
}
/**
* Creates the user interface that will be used to display this place.
* The view instance returned will later be configured with the place
* object, once it becomes available.
*
* @param ctx a reference to the {@link CrowdContext} associated with
* this controller.
*/
protected function createPlaceView (ctx :CrowdContext) :PlaceView
{
return null;
}
/**
* This is called by the location director once the place object has
* been fetched. The place controller will dispatch the place object
* to the user interface hierarchy via {@link
* PlaceViewUtil#dispatchWillEnterPlace}. Derived classes can override
* this and perform any other starting up that they need to do
*/
public function willEnterPlace (plobj :PlaceObject) :void
{
// keep a handle on our place object
_plobj = plobj;
if (_view != null ) {
// set it as our controlled panel
setControlledPanel(_view);
// let the UI hierarchy know that we've got our place
PlaceViewUtil.dispatchWillEnterPlace(_view, plobj);
// and display the user interface
setPlaceView();
}
// let our delegates know what's up
applyToDelegates(function (del :PlaceControllerDelegate) :void {
del.willEnterPlace(plobj);
});
}
/**
* Called before a request is submitted to the server to leave the
* current place. As such, this method may be called multiple times
* before {@link #didLeavePlace} is finally called. The request to
* leave may be rejected, but if a place controller needs to flush any
* information to the place manager before it leaves, it should so do
* here. This is the only place in which the controller is guaranteed
* to be able to communicate to the place manager, as by the time
* {@link #didLeavePlace} is called, the place manager may have
* already been destroyed.
*/
public function mayLeavePlace (plobj :PlaceObject) :void
{
// let our delegates know what's up
applyToDelegates(function (del :PlaceControllerDelegate) :void {
del.mayLeavePlace(plobj);
});
}
/**
* This is called by the location director when we are leaving this
* place and need to clean up after ourselves and shutdown. Derived
* classes should override this method (being sure to call
* <code>super.didLeavePlace</code>) and perform any necessary
* cleanup.
*/
public function didLeavePlace (plobj :PlaceObject) :void
{
// let our delegates know what's up
applyToDelegates(function (del :PlaceControllerDelegate) :void {
del.didLeavePlace(plobj);
});
setControlledPanel(null);
// let the UI hierarchy know that we're outta here
if (_view != null ) {
PlaceViewUtil.dispatchDidLeavePlace(_view, plobj);
clearPlaceView();
_view = null;
}
_plobj = null;
}
/**
* Handles basic place controller action events. Derived classes
* should be sure to call <code>super.handleAction</code> for events
* they don't specifically handle.
*/
override public function handleAction (cmd :String, arg :Object) :Boolean
{
var handled :Boolean = false;
// let our delegates have a crack at the action
applyToDelegates(function (del :PlaceControllerDelegate) :void {
// we take advantage of short-circuiting here
handled = handled || del.handleAction(cmd, arg);
});
// if they didn't handly it, pass it off to the super class
return handled || super.handleAction(cmd, arg);
}
/**
* Adds the supplied delegate to the list for this controller.
*/
protected function addDelegate (delegate :PlaceControllerDelegate) :void
{
if (_delegates == null) {
_delegates = new Array();
}
_delegates.push(delegate);
}
/**
* Applies the supplied operation to the registered delegates.
*/
protected function applyToDelegates (visitor :Function) :void
{
if (_delegates != null) {
for (var ii :int = 0; ii < _delegates.length; ii++) {
visitor.call(this, _delegates[ii]);
}
}
}
/**
* Sets the place view. By default, this simply calls {CrowdContext#setPlaceView},
* but subclasses can override the method to add the place view in some other way.
*/
protected function setPlaceView () :void
{
_ctx.setPlaceView(_view);
}
/**
* Clears out the place view. By default, this calls {CrowdContext#clearPlaceView}.
*/
protected function clearPlaceView () :void
{
_ctx.clearPlaceView(_view);
}
/** A reference to the active client context. */
protected var _ctx :CrowdContext;
/** A reference to our place configuration. */
protected var _config :PlaceConfig;
/** A reference to the place object for which we're controlling a user
* interface. */
protected var _plobj :PlaceObject;
/** A reference to the root user interface component. */
protected var _view :PlaceView;
/** A list of the delegates in use by this controller. */
protected var _delegates :Array;
}
}
@@ -0,0 +1,97 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext;
/**
* Provides an extensible mechanism for encapsulating delegated
* functionality that works with the place services.
*
* <p> Thanks to Java's lack of multiple inheritance, it will likely
* become necessary to factor certain services that might be used by a
* variety of {@link PlaceController} derived classes into delegate
* classes because they do not fit into the single inheritance hierarchy
* that makes sense for a particular application. To facilitate this
* process, this delegate class is provided which the standard place
* controller can be made to call out to for all of the standard methods.
*/
public class PlaceControllerDelegate
{
/**
* Constructs the delegate with the controller for which it is
* delegating.
*/
public function PlaceControllerDelegate (controller :PlaceController)
{
_controller = controller;
}
/**
* Called to initialize the delegate.
*/
public function init (ctx :CrowdContext, config :PlaceConfig) :void
{
}
/**
* Called to let the delegate know that we're entering a place.
*/
public function willEnterPlace (plobj :PlaceObject) :void
{
}
/**
* Called before a request is submitted to the server to leave the
* current place. The request to leave may be rejected, but if a place
* controller needs to make a final communication to the place manager
* before it leaves, it should so do here. This is the only place in
* which the controller is guaranteed to be able to communicate to the
* place manager, as by the time {@link #didLeavePlace} is called, the
* place manager may have already been destroyed.
*/
public function mayLeavePlace (plobj :PlaceObject) :void
{
}
/**
* Called to let the delegate know that we've left the place.
*/
public function didLeavePlace (plobj :PlaceObject) :void
{
}
/**
* Called to give the delegate a chance to handle controller actions
* that weren't handled by the main controller.
*/
public function handleAction (cmd :String, arg :Object) :Boolean
{
return false;
}
/** A reference to the controller for which we are delegating. */
protected var _controller :PlaceController;
}
}
@@ -0,0 +1,79 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 flash.events.IEventDispatcher;
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 extends IEventDispatcher
{
/**
* 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;
}
}
@@ -0,0 +1,109 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import mx.core.IRawChildrenContainer; // a simple interface from flex
import com.threerings.util.Log;
import com.threerings.crowd.data.PlaceObject;
/**
* Provides a mechanism for dispatching notifications to all user
* interface elements in a hierarchy that implement the {@link PlaceView}
* interface. Look at the documentation for {@link PlaceView} for more
* explanation.
*/
public class PlaceViewUtil
{
/**
* Dispatches a call to {@link PlaceView#willEnterPlace} to all UI
* elements in the hierarchy rooted at the component provided via the
* <code>root</code> parameter.
*
* @param root the component at which to start traversing the UI
* hierarchy.
* @param plobj the place object that is about to be entered.
*/
public static function dispatchWillEnterPlace (
root :Object, plobj :PlaceObject) :void
{
dispatch(root, plobj, "willEnterPlace");
}
/**
* Dispatches a call to {@link PlaceView#didLeavePlace} to all UI
* elements in the hierarchy rooted at the component provided via the
* <code>root</code> parameter.
*
* @param root the component at which to start traversing the UI
* hierarchy.
* @param plobj the place object that is about to be entered.
*/
public static function dispatchDidLeavePlace (
root :Object, plobj :PlaceObject) :void
{
dispatch(root, plobj, "didLeavePlace");
}
private static function dispatch (
root :Object, plobj :PlaceObject, funct :String) :void
{
// much of the code in here is adapted from
// com.threerings.flash.DisplayUtil, which we cannot access because
// it's in the nenya package. So sad, too bad.
if (root is PlaceView) {
try {
(root as PlaceView)[funct](plobj);
} catch (e :Error) {
var log :Log = Log.getLog(PlaceViewUtil);
log.warning("Component choked on " + funct + "() ", "component", root,
"plobj", plobj, e);
}
}
if (!(root is DisplayObject)) {
return;
}
if (root is DisplayObjectContainer) {
// a little type-unsafety so that we don't have to write two blocks
var o :Object= (root is IRawChildrenContainer) ?
IRawChildrenContainer(root).rawChildren : root;
var nn :int = int(o.numChildren);
for (var ii :int = 0; ii < nn; ii++) {
try {
root = o.getChildAt(ii);
} catch (err :SecurityError) {
// don't try dispatching to children we cannot access!
continue;
}
dispatch(root, plobj, funct);
}
}
}
}
}
@@ -0,0 +1,50 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.crowd.client.BodyService;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.util.langBoolean;
/**
* Provides the implementation of the <code>BodyService</code> 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 BodyMarshaller extends InvocationMarshaller
implements BodyService
{
/** The method id used to dispatch <code>setIdle</code> requests. */
public static const SET_IDLE :int = 1;
// from interface BodyService
public function setIdle (arg1 :Boolean) :void
{
sendRequest(SET_IDLE, [
langBoolean.valueOf(arg1)
]);
}
}
}
@@ -0,0 +1,197 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.util.Byte;
import com.threerings.util.Name;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationCodes;
import com.threerings.crowd.chat.data.ChatCodes;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
/**
* The basic user object class for Crowd users. Bodies have a location and a status.
*/
public class BodyObject extends ClientObject
{
// AUTO-GENERATED: FIELDS START
/** The field name of the <code>location</code> field. */
public static const LOCATION :String = "location";
/** The field name of the <code>status</code> field. */
public static const STATUS :String = "status";
/** The field name of the <code>awayMessage</code> field. */
public static const AWAY_MESSAGE :String = "awayMessage";
// AUTO-GENERATED: FIELDS END
/**
* The oid of the place currently occupied by this body or -1 if they currently occupy no
* place.
*/
public var location :Place;
/**
* The user's current status ({@link OccupantInfo#ACTIVE}, etc.).
*/
public var status :int;
/**
* If non-null, this contains a message to be auto-replied whenever another user delivers a
* tell message to this user.
*/
public var awayMessage :String;
/**
* Returns this user's access control tokens.
*/
public function getTokens () :TokenRing
{
return new TokenRing();
}
/**
* Returns the name that should be displayed to other users. The default is to use
* <code>username</code>.
* @see com.threerings.presents.data.ClientObject#username
*/
public function getVisibleName () :Name
{
return username;
}
/**
* Returns the oid of the place occupied by this body or -1 if we occupy no place.
*/
public function getPlaceOid () :int
{
return (location == null) ? -1 : location.placeOid;
}
// // AUTO-GENERATED: METHODS START
// /**
// * Requests that the <code>username</code> field be set to the
// * specified 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 setUsername (value :Name) :void
// {
// var ovalue :Name = this.username;
// requestAttributeChange(
// USERNAME, value, ovalue);
// this.username = value;
// }
//
// /**
// * Requests that the <code>location</code> field be set to the
// * specified 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 setLocation (value :int) :void
// {
// var ovalue :int = this.location;
// requestAttributeChange(
// LOCATION, value, ovalue);
// this.location = value;
// }
//
// /**
// * Requests that the <code>status</code> field be set to the
// * specified 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 setStatus (value :int) :void
// {
// var ovalue :int = this.status;
// requestAttributeChange(
// STATUS, Byte.valueOf(value), Byte.valueOf(ovalue));
// this.status = value;
// }
//
// /**
// * Requests that the <code>awayMessage</code> field be set to the
// * specified 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 setAwayMessage (value :String) :void
// {
// var ovalue :String = this.awayMessage;
// requestAttributeChange(
// AWAY_MESSAGE, value, ovalue);
// this.awayMessage = value;
// }
// // AUTO-GENERATED: METHODS END
//
// override public function writeObject (out :ObjectOutputStream) :void
// {
// super.writeObject(out);
//
// out.writeObject(username);
// out.writeInt(location);
// out.writeByte(status);
// out.writeField(awayMessage);
// }
override public function who () :String
{
var who :String = username.toString() + " (" + getOid();
if (status != OccupantInfo.ACTIVE) {
who += (" " + getStatusTranslation());
}
who += ")";
return who;
}
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
location = Place(ins.readObject());
status = ins.readByte();
awayMessage = (ins.readField(String) as String);
}
/**
* Get a translation suffix for this occupant's status.
* Can be overridden to translate nonstandard statuses.
*/
protected function getStatusTranslation () :String
{
return OccupantInfo.X_STATUS[status];
}
}
}
@@ -0,0 +1,34 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.presents.data.InvocationCodes;
/**
* Defines codes and constants global to the Crowd services.
*/
public class CrowdCodes extends InvocationCodes
{
/** Defines our invocation services group. */
public static const CROWD_GROUP :String = "crowd";
}
}
@@ -0,0 +1,54 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationCodes;
import com.threerings.presents.data.Permission;
import com.threerings.presents.data.PermissionPolicy;
import com.threerings.crowd.chat.data.ChatCodes;
/**
* Implements some Crowd permissions.
*/
public class CrowdPermissionPolicy extends PermissionPolicy
{
// from PermissionPolicy
override public function checkAccess (
clobj :ClientObject, perm :Permission, context :Object) :String
{
if (!(clobj is BodyObject)) {
return super.checkAccess(clobj, perm, context);
}
var body :BodyObject = (clobj as BodyObject);
if (perm == ChatCodes.BROADCAST_ACCESS) {
return body.getTokens().isAdmin() ? null : InvocationCodes.ACCESS_DENIED;
} else if (perm == ChatCodes.CHAT_ACCESS) {
return null;
} else {
return super.checkAccess(clobj, perm, context);
}
}
}
}
@@ -0,0 +1,44 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.presents.data.InvocationCodes;
/**
* Contains codes used by the location invocation services.
*/
public class LocationCodes extends InvocationCodes
{
/** An error code indicating that a place identified by a particular
* place id does not exist. Usually generated by a failed moveTo
* request. */
public static const NO_SUCH_PLACE :String = "m.no_such_place";
/** An error code sent when a user requests to move to a new place but
* they are in the middle of moving somewhere already. */
public static const MOVE_IN_PROGRESS :String = "m.move_in_progress";
/** An error code sent when a user requests to move to a place, but
* they are already in the requested place. */
public static const ALREADY_THERE :String = "m.already_there";
}
}
@@ -0,0 +1,64 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.crowd.client.LocationService;
import com.threerings.crowd.client.LocationService_MoveListener;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.util.Integer;
/**
* Provides the implementation of the <code>LocationService</code> 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 LocationMarshaller extends InvocationMarshaller
implements LocationService
{
/** The method id used to dispatch <code>leavePlace</code> requests. */
public static const LEAVE_PLACE :int = 1;
// from interface LocationService
public function leavePlace () :void
{
sendRequest(LEAVE_PLACE, [
]);
}
/** The method id used to dispatch <code>moveTo</code> requests. */
public static const MOVE_TO :int = 2;
// from interface LocationService
public function moveTo (arg1 :int, arg2 :LocationService_MoveListener) :void
{
var listener2 :LocationMarshaller_MoveMarshaller = new LocationMarshaller_MoveMarshaller();
listener2.listener = arg2;
sendRequest(MOVE_TO, [
Integer.valueOf(arg1), listener2
]);
}
}
}
@@ -0,0 +1,51 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.crowd.client.LocationService_MoveListener;
import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller;
/**
* Marshalls instances of the LocationService_MoveMarshaller interface.
*/
public class LocationMarshaller_MoveMarshaller
extends InvocationMarshaller_ListenerMarshaller
{
/** The method id used to dispatch <code>moveSucceeded</code> responses. */
public static const MOVE_SUCCEEDED :int = 1;
// from InvocationMarshaller_ListenerMarshaller
override public function dispatchResponse (methodId :int, args :Array) :void
{
switch (methodId) {
case MOVE_SUCCEEDED:
(listener as LocationService_MoveListener).moveSucceeded(
(args[0] as PlaceConfig));
return;
default:
super.dispatchResponse(methodId, args);
return;
}
}
}
}
@@ -0,0 +1,44 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.presents.dobj.ServerMessageEvent;
public class ManagerCaller
{
public function ManagerCaller (plobj :PlaceObject)
{
_plobj = plobj;
}
/**
* Called to call a method on the manager.
*/
public function invoke (method :String, ... args) :void
{
_plobj.postEvent(new ServerMessageEvent(_plobj.getOid(), method, args));
}
/** The place object we're thingy-ing for. */
protected var _plobj :PlaceObject;
}
}
@@ -0,0 +1,134 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import flash.system.ApplicationDomain;
import com.threerings.util.ClassUtil;
import com.threerings.util.Cloneable;
import com.threerings.util.Integer;
import com.threerings.util.Name;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.SimpleStreamableObject;
import com.threerings.presents.dobj.DSet_Entry;
import com.threerings.crowd.data.BodyObject;
/**
* The occupant info object contains all of the information about an
* occupant of a place that should be shared with other occupants of the
* place. These objects are stored in the place object itself and are
* updated when bodies enter and exit a place.
*
* <p> A system that builds upon the Crowd framework can extend this class to
* include extra information about their occupants. They will need to provide a
* derived {@link BodyObject} that creates and configures their occupant info
* in {@link BodyObject#createOccupantInfo}.
*
* <p> Note also that this class implements {@link Cloneable} which means
* that if derived classes add non-primitive attributes, they are
* responsible for adding the code to clone those attributes when a clone
* is requested.
*/
public class OccupantInfo extends SimpleStreamableObject
implements DSet_Entry, Cloneable
{
/** Constant value for {@link #status}. */
public static const ACTIVE :int = 0;
/** Constant value for {@link #status}. */
public static const IDLE :int = 1;
/** Constant value for {@link #status}. */
public static const DISCONNECTED :int = 2;
/** Maps status codes to human readable strings. */
public static const X_STATUS :Array = [ "active", "idle", "discon" ];
/** The body object id of this occupant (and our entry key). */
public var bodyOid :int;
/** The username of this occupant. */
public var username :Name;
/** The status of this occupant. */
public var status :int = ACTIVE;
/**
* Constructs an occupant info record, optionally obtaining data from the
* supplied BodyObject.
*/
public function OccupantInfo (body :BodyObject = null)
{
if (body != null) {
bodyOid = body.getOid();
username = body.getVisibleName();
status = body.status;
}
}
/** Access to the body object id as an int. */
public function getBodyOid () :int
{
return bodyOid;
}
/**
* Generates a cloned copy of this instance.
*/
public function clone () :Object
{
var that :OccupantInfo = ClassUtil.newInstance(this) as OccupantInfo;
that.bodyOid = this.bodyOid;
that.username = this.username;
that.status = this.status;
return that;
}
// documentation inherited from interface DSet_Entry
public function getKey () :Object
{
return bodyOid;
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
bodyOid = (ins.readField(Integer) as Integer).value;
username = Name(ins.readObject());
status = ins.readByte();
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeObject(new Integer(bodyOid));
out.writeObject(username);
out.writeByte(status);
}
}
}
@@ -0,0 +1,72 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.SimpleStreamableObject;
import com.threerings.util.Hashable;
/**
* Contains information on the current place occupied by a body.
*/
public class Place extends SimpleStreamableObject
implements Hashable
{
/** The oid of this place's {@link PlaceObject}. */
public var placeOid :int;
/**
* Creates a place with the supplied oid.
*/
public function Place (placeOid :int = 0)
{
this.placeOid = placeOid;
}
// from Object
public function hashCode () :int
{
return placeOid;
}
// from Object
public function equals (other :Object) :Boolean
{
return (other is Place) && ((other as Place).placeOid == placeOid);
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
placeOid = ins.readInt();
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeInt(placeOid);
}
}
}
@@ -0,0 +1,84 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.SimpleStreamableObject;
import com.threerings.crowd.client.PlaceController;
/**
* The place config class encapsulates the configuration information for a
* particular type of place. The hierarchy of place config objects mimics
* the hierarchy of place managers and controllers. Both the place manager
* and place controller are provided with the place config object when the
* place is created.
*
* <p> The place config object is also the mechanism used to instantiate
* the appropriate place manager and controller. Every place must have an
* associated place config derived class that overrides {@link
* #getControllerClass} and {@link #getManagerClassName}, returning the
* appropriate place controller and manager class for that place.
*/
public /*abstract*/ class PlaceConfig extends SimpleStreamableObject
{
public function PlaceConfig ()
{
// nothing needed
}
/**
* Returns the class that should be used to create a controller for this
* place. The controller class must derive from {@link PlaceController}.
*
* @deprecated Override {@link #createController} directly.
*/
public function getControllerClass () :Class
{
return null;
}
/**
* Create the controller that should be used for this place.
*/
public function createController () :PlaceController
{
return null;
}
/**
* Returns the name of the class that should be used to create a
* manager for this place. The manager class must derive from {@link
* com.threerings.crowd.server.PlaceManager}. <em>Note:</em> this
* method differs from {@link #getControllerClass} because we want to
* avoid compile time linkage of the place config object (which is
* used on the client) to server code. This allows a code optimizer
* (DashO Pro, for example) to remove the server code from the client,
* knowing that it is never used.
*/
public function getManagerClassName () :String
{
return null; // not used
}
}
}
@@ -0,0 +1,213 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.util.Iterator;
import com.threerings.util.Name;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet;
import com.threerings.presents.dobj.OidList;
import com.threerings.crowd.chat.data.SpeakMarshaller;
//import com.threerings.crowd.chat.data.SpeakObject;
/**
* A distributed object that contains information on a place that is
* occupied by bodies. This place might be a chat room, a game room, an
* island in a massively multiplayer piratical universe, anything that has
* occupants that might want to chat with one another.
*/
public class PlaceObject extends DObject
{
// AUTO-GENERATED: FIELDS START
/** The field name of the <code>occupants</code> field. */
public static const OCCUPANTS :String = "occupants";
/** The field name of the <code>occupantInfo</code> field. */
public static const OCCUPANT_INFO :String = "occupantInfo";
/** The field name of the <code>speakService</code> field. */
public static const SPEAK_SERVICE :String = "speakService";
// AUTO-GENERATED: FIELDS END
/**
* Allows the client to call methods on the manager.
*/
public var manager :ManagerCaller;
/**
* Tracks the oid of the body objects of all of the occupants of this
* place.
*/
public var occupants :OidList = new OidList();
/**
* Contains an info record (of type {@link OccupantInfo}) for each
* occupant that contains information about that occupant that needs
* to be known by everyone in the place. <em>Note:</em> Don't obtain
* occupant info records directly from this set when on the server,
* use <code>PlaceManager.getOccupantInfo()</code> instead (along with
* <code>PlaceManager.updateOccupantInfo()</code>) because it does
* some special processing to ensure that readers and updaters don't
* step on one another even if they make rapid fire changes to a
* user's occupant info.
*/
public var occupantInfo :DSet = new DSet();
/** Used to generate speak requests on this place object. */
public var speakService :SpeakMarshaller;
/**
* We need a constructor here in actionscript to set up the
* ManagerCaller.
*/
public function PlaceObject ()
{
super();
manager = new ManagerCaller(this);
}
/**
* Looks up a user's occupant info by name.
*
* @return the occupant info record for the named user or null if no
* user in the room has that username.
*/
public function getOccupantInfo (username :Name) :OccupantInfo
{
var itr :Iterator = occupantInfo.iterator();
while (itr.hasNext()) {
var info :OccupantInfo = (itr.next() as OccupantInfo);
if (info.username.equals(username)) {
return info;
}
}
return null;
}
// // AUTO-GENERATED: METHODS START
// /**
// * Requests that <code>oid</code> be added to the <code>occupants</code>
// * oid list. The list will not change until the event is actually
// * propagated through the system.
// */
// public function addToOccupants (oid :int) :void
// {
// requestOidAdd(OCCUPANTS, oid);
// }
//
// /**
// * Requests that <code>oid</code> be removed from the
// * <code>occupants</code> oid list. The list will not change until the
// * event is actually propagated through the system.
// */
// public function removeFromOccupants (oid :int) :void
// {
// requestOidRemove(OCCUPANTS, oid);
// }
//
// /**
// * Requests that the specified entry be added to the
// * <code>occupantInfo</code> set. The set will not change until the event is
// * actually propagated through the system.
// */
// public function addToOccupantInfo (elem :DSet_Entry) :void
// {
// requestEntryAdd(OCCUPANT_INFO, elem);
// }
//
// /**
// * Requests that the entry matching the supplied key be removed from
// * the <code>occupantInfo</code> set. The set will not change until the
// * event is actually propagated through the system.
// */
// public function removeFromOccupantInfo (key :Object) :void
// {
// requestEntryRemove(OCCUPANT_INFO, key);
// }
//
// /**
// * Requests that the specified entry be updated in the
// * <code>occupantInfo</code> set. The set will not change until the event is
// * actually propagated through the system.
// */
// public function updateOccupantInfo (elem :DSet_Entry) :void
// {
// requestEntryUpdate(OCCUPANT_INFO, elem);
// }
//
// /**
// * Requests that the <code>occupantInfo</code> field be set to the
// * specified value. Generally one only adds, updates and removes
// * entries of a distributed set, but certain situations call for a
// * complete replacement of the set value. The local value will be
// * updated immediately and an event will be propagated through the
// * system to notify all listeners that the attribute did
// * change. Proxied copies of this object (on clients) will apply the
// * value change when they received the attribute changed notification.
// */
// public function setOccupantInfo (value :DSet) :void
// {
// requestAttributeChange(OCCUPANT_INFO, value, this.occupantInfo);
// this.occupantInfo = (value == null) ? null : value;
// }
//
// /**
// * Requests that the <code>speakService</code> field be set to the
// * specified 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 setSpeakService (value :SpeakMarshaller) :void
// {
// var ovalue :SpeakMarshaller = this.speakService;
// requestAttributeChange(
// SPEAK_SERVICE, value, ovalue);
// this.speakService = value;
// }
// // AUTO-GENERATED: METHODS END
//
// // documentation inherited
// override public function writeObject (out :ObjectOutputStream) :void
// {
// super.writeObject(out);
// out.writeObject(occupants);
// out.writeObject(occupantInfo);
// out.writeObject(speakService);
// }
// documentation inherited
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
occupants = OidList(ins.readObject());
occupantInfo = DSet(ins.readObject());
speakService = SpeakMarshaller(ins.readObject());
}
}
}
@@ -0,0 +1,120 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data {
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.SimpleStreamableObject;
/**
* Defines access control tokens that convey certain privileges to users
* (see {@link BodyObject#checkAccess}).
*/
public class TokenRing extends SimpleStreamableObject
{
/** Indicates that this user is an administrator and can do things like broadcast, shutdown the
* server and whatnot. */
public static const ADMIN :int = (1 << 0);
/**
* Constructs a token ring with the supplied set of tokens.
*/
public function TokenRing (tokens :int = 0)
{
_tokens = tokens;
}
/**
* Adds the specified token to this ring.
*/
public function setToken (token :int, setOn :Boolean = true) :void
{
if (setOn) {
_tokens |= token;
} else {
clearToken(token);
}
}
/**
* Returns the bitmask that stores the various tokens.
*/
public function getTokens () :int
{
return _tokens;
}
/**
* Convenience function for checking whether this ring holds the
* {@link #ADMIN} token.
*/
public function isAdmin () :Boolean
{
return holdsToken(ADMIN);
}
/**
* Returns true if this token ring contains the specified token or tokens,
* exactly.
* For example, if you pass in the OR of two or more tokens,
* then the ring must contain all of those tokens.
*/
public function holdsToken (token :int) :Boolean
{
return (_tokens & token) == token;
}
/**
* Returns true if this token ring contains any one of the specified tokens.
*/
public function holdsAnyToken (tokens :int) :Boolean
{
return (_tokens & tokens) != 0;
}
/**
* Clears the specified token from this ring.
*/
public function clearToken (token :int) :void
{
_tokens &= ~token;
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
_tokens = ins.readInt();
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeInt(_tokens);
}
/** The tokens contained in this ring (composed together bitwise). */
protected var _tokens :int;
}
}
@@ -0,0 +1,64 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.peer.data {
import com.threerings.util.Name;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.presents.peer.data.ClientInfo;
/**
* Extends the standard {@link ClientInfo} with Crowd bits.
*/
public class CrowdClientInfo extends ClientInfo
{
/** The client's visible name, which is used for chatting. */
public var visibleName :Name;
public function CrowdClientInfo ()
{
// nothing needed
}
// documentation inherited
override public function getKey () :Object
{
return visibleName;
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
visibleName = Name(ins.readObject());
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeObject(visibleName);
}
}
}
@@ -0,0 +1,76 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.util {
import com.threerings.presents.util.PresentsContext;
import com.threerings.crowd.client.LocationDirector;
import com.threerings.crowd.client.OccupantDirector;
import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.chat.client.ChatDirector;
/**
* The crowd context provides access to the various managers, etc. that
* are needed by the crowd client code.
*/
public interface CrowdContext extends PresentsContext
{
/**
* Returns a reference to the location director.
*/
function getLocationDirector () :LocationDirector;
/**
* Returns a reference to the occupant director.
*/
function getOccupantDirector () :OccupantDirector;
/**
* Provides access to the chat director.
*/
function getChatDirector () :ChatDirector;
/**
* When the client enters a new place, the location director creates a
* place controller which then creates a place view to visualize the
* place for the user. The place view created by the place controller
* will be passed to this function to actually display it in whatever
* user interface is provided for the user. We don't require any
* particular user interface toolkit, so it is expected that the place
* view implementation will coordinate with the client implementation
* so that the client can display the view provided by the place
* controller.
*
* <p> Though the place view is created before we enter the place, it
* won't be displayed (via a call to this function) until we have
* fully entered the place and are ready for user interaction.
*/
function setPlaceView (view :PlaceView) :void;
/**
* When the client leaves a place, the place controller will remove
* any place view it set previously via {@link #setPlaceView} with a
* call to this method.
*/
function clearPlaceView (view :PlaceView) :void;
}
}
@@ -0,0 +1,73 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
import flash.utils.ByteArray;
public class ArrayMask
{
public function ArrayMask (length :int = 0)
{
var mlength :int = int(length / 8);
if (length % 8 != 0) {
mlength++;
}
_mask.length = mlength;
}
/**
* Set the specified index as containing a non-null element in the
* array we're representing.
*/
public function setBit (index :int) :void
{
_mask[int(index/8)] |= (1 << (index % 8));
}
/**
* Is the specified array element non-null?
*/
public function isSet (index :int) :Boolean
{
return (_mask[int(index/8)] & (1 << (index % 8))) != 0;
}
public function writeTo (out :ObjectOutputStream) :void
{
out.writeShort(_mask.length);
out.writeBytes(_mask);
}
// documentation inherited from interface Streamable
public function readFrom (ins :ObjectInputStream) :void
{
var len :int = ins.readShort();
_mask.length = len;
if (len > 0) {
ins.readBytes(_mask, 0, len);
}
}
/** The array mask. */
protected var _mask :ByteArray = new ByteArray();
}
}
@@ -0,0 +1,35 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
public class ClassMapping
{
public var code :int;
public var streamer :Streamer;
public function ClassMapping (code :int, streamer :Streamer)
{
this.code = code;
this.streamer = streamer;
}
}
}
@@ -0,0 +1,46 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
import flash.events.Event;
import flash.utils.ByteArray;
public class FrameAvailableEvent extends Event
{
/** The event code for a frame available. */
public static const FRAME_AVAILABLE :String = "frameAvail";
public function FrameAvailableEvent (frameData :ByteArray)
{
super(FRAME_AVAILABLE);
_frameData = frameData;
}
public function getFrameData () :ByteArray
{
return _frameData;
}
protected var _frameData :ByteArray;
}
}
@@ -0,0 +1,118 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
import flash.events.EventDispatcher;
import flash.events.ProgressEvent;
import flash.net.Socket;
import flash.utils.ByteArray;
import flash.utils.Endian;
import com.threerings.util.Log;
/**
* Reads socket data until a complete frame is available.
* This dispatches a FrameAvailableEvent.FRAME_AVAILABLE once a frame
* has been fully read off the socket and is ready for decoding.
*/
public class FrameReader extends EventDispatcher
{
public function FrameReader (socket :Socket)
{
_socket = socket;
_socket.addEventListener(ProgressEvent.SOCKET_DATA, socketHasData);
}
/**
* Stop listening on the socket.
*/
public function shutdown () :void
{
_socket.removeEventListener(ProgressEvent.SOCKET_DATA, socketHasData);
}
/**
* Called when our socket has data that we can read.
*/
protected function socketHasData (event :ProgressEvent) :void
{
try {
readAvailable();
} catch (e :Error) {
Log.getLog(this).warning("Error reading socket data", e);
}
}
protected function readAvailable () :void
{
if (ObjectInputStream.DEBUG) {
Log.getLog(this).debug("socketHasData(" + _socket.bytesAvailable + ")");
}
while (_socket.connected && _socket.bytesAvailable > 0) {
if (_curData == null) {
if (_socket.bytesAvailable < HEADER_SIZE) {
// if there are less bytes available than a header, let's
// just leave them on the socket until we can read the length
// all at once
return;
}
// the length specified is the length of the entire frame,
// including the length of the bytes used to encode the length.
// (I think this is pretty silly).
// So for our purposes we subtract 4 bytes so we know how much
// more data is in the frame.
_length = _socket.readInt() - HEADER_SIZE;
_curData = new ByteArray();
_curData.endian = Endian.BIG_ENDIAN;
}
// read bytes: either as much as possible or up to the end of the frame
var toRead :int = Math.min(_length - _curData.length, _socket.bytesAvailable);
// Just in case, if the amount needed is 0, don't do anything!
// Passing 0 causes it to read *all available bytes*.
if (toRead != 0) {
_socket.readBytes(_curData, _curData.length, toRead);
}
if (_length === _curData.length) {
// we have now read a complete frame, let us dispatch the data
_curData.position = 0; // move the read pointer to the beginning
if (ObjectInputStream.DEBUG) {
Log.getLog(this).debug("+ FrameAvailable");
}
dispatchEvent(new FrameAvailableEvent(_curData));
_curData = null; // clear, so we know we need to first read length
}
}
}
protected var _socket :Socket;
protected var _curData :ByteArray;
protected var _length :int;
/** The number of bytes in the frame header (a 32-bit integer). */
protected const HEADER_SIZE :int = 4;
}
}
@@ -0,0 +1,297 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
import flash.errors.IOError;
import flash.errors.MemoryError;
import flash.utils.ByteArray;
import flash.utils.IDataInput;
import com.threerings.util.ClassUtil;
import com.threerings.util.Log;
import com.threerings.util.Long;
public class ObjectInputStream
{
/** Enables verbose object I/O debugging. */
public static const DEBUG :Boolean = false;
public function ObjectInputStream (source :IDataInput = null, clientProps :Object = null)
{
_source = source || new ByteArray();
_cliProps = clientProps || {};
}
/**
* Set a new source from which to read our data.
*/
public function setSource (source :IDataInput) :void
{
_source = source;
}
/**
* Return a "client property" with the specified name.
* Actionscript only.
*/
public function getClientProperty (name :String) :*
{
return _cliProps[name];
}
/**
* Reads the next Object (or null) from the stream and returns it as * so that you may assign
* to any variable without casting. It is strongly recommended that you pass a 'checkType'
* parameter so that the type of the Object is verified to be what you believe it to be,
* thus helping you detect streaming errors as quickly as possible.
*
* @param checkType optional type to check the read object against
* @return the Object read, or null.
* @throws TypeError if the object is read successfully but is the wrong type
*
* @example This demonstrates how to type-check the objects read off the stream without
* having to cast them.
* <listing version="3.0">
*
* public function readObject (ins :ObjectInputStream) :void
* {
* _scoops = ins.readObject(Array);
* _coneType = ins.readObject(Cone);
* }
*
* protected var _scoops :Array;
* protected var _coneType :Cone;
* </listing>
*/
public function readObject (checkType :Class = null) :*
//throws IOError
{
var DEBUG_ID :String = "[" + (++_debugObjectCounter) + "] ";
try {
// read in the class code for this instance
var code :int = readShort();
// a zero code indicates a null value
if (code == 0) {
if (DEBUG) log.debug(DEBUG_ID + "Read null");
return null;
}
var cmap :ClassMapping;
// if the code is negative, that means we've never seen it
// before and class metadata follows
if (code < 0) {
// first swap the code into positive land
code *= -1;
// read in the class metadata
var jname :String = readUTF();
// log.debug("read jname: " + jname);
var streamer :Streamer = Streamer.getStreamerByJavaName(jname);
if (streamer == null) {
log.warning("OMG, cannot stream " + jname);
return null;
}
if (DEBUG) log.debug(DEBUG_ID + "Got streamer (" + streamer + ")");
cmap = new ClassMapping(code, streamer);
_classMap[code] = cmap;
if (DEBUG) log.debug(DEBUG_ID + "Created mapping: (" + code + "): " + jname);
} else {
cmap = (_classMap[code] as ClassMapping);
if (null == cmap) {
throw new IOError("Read object for which we have no " +
"registered class metadata [code=" + code + "].");
}
if (DEBUG) {
log.debug(DEBUG_ID + "Read known code: (" + code + ": " +
cmap.streamer.getJavaClassName() + ")");
}
}
// log.debug("Creating object sleeve...");
var target :Object = cmap.streamer.createObject(this);
//log.debug("Reading object...");
readBareObjectImpl(target, cmap.streamer);
if (DEBUG) log.debug(DEBUG_ID + "Read object: " + target);
if (checkType != null && !(target is checkType)) {
throw new TypeError(
"Cannot convert " + ClassUtil.getClass(target) + " to " + checkType);
}
return target;
} catch (me :MemoryError) {
throw new IOError("out of memory" + me.message);
}
return null; // not reached: compiler dumb
}
public function readBareObject (obj :Object) :void
//throws IOError
{
readBareObjectImpl(obj, Streamer.getStreamer(obj));
}
public function readBareObjectImpl (obj :Object, streamer :Streamer) :void
{
_current = obj;
_streamer = streamer;
try {
_streamer.readObject(obj, this);
} finally {
// clear out our current object references
_current = null;
_streamer = null;
}
}
/**
* Called to read an Object of a known final type into a Streamable object.
*
* @param type either a String representing the java type,
* a Class representing the actionscript type,
* or the Streamer to be used to read the field.
*/
public function readField (type :Object) :*
//throws IOError
{
if (!readBoolean()) {
return null;
}
var streamer :Streamer;
if (type is Streamer) {
streamer = Streamer(type);
} else {
var jname :String = type as String;
if (type is Class) {
jname = Translations.getToServer(ClassUtil.getClassName(type));
}
streamer= Streamer.getStreamerByJavaName(jname);
if (streamer == null) {
throw new Error("Cannot field stream " + type);
}
}
var obj :Object = streamer.createObject(this);
readBareObjectImpl(obj, streamer);
return obj;
}
public function defaultReadObject () :void
//throws IOError
{
_streamer.readObject(_current, this);
}
public function readBoolean () :Boolean
//throws IOError
{
return _source.readBoolean();
}
public function readByte () :int
//throws IOError
{
return _source.readByte();
}
/**
* Read bytes into the byte array. If length is not specified, then
* enough bytes to fill the array (from the offset) are read.
*/
public function readBytes (
bytes :ByteArray, offset :uint = 0, length :uint = uint.MAX_VALUE) :void
//throws IOError
{
// if no length specified then fill the ByteArray
if (length == uint.MAX_VALUE) {
length = bytes.length - offset;
}
// And, if we really want to read 0 bytes then just don't do anything, because an
// IDataInput will read *all available bytes* when the specified length is 0.
if (length > 0) {
_source.readBytes(bytes, offset, length);
}
}
public function readDouble () :Number
//throws IOError
{
return _source.readDouble();
}
public function readFloat () :Number
//throws IOError
{
return _source.readFloat();
}
public function readLong () :Long
{
const result :Long = new Long();
readBareObject(result);
return result;
}
public function readInt () :int
//throws IOError
{
return _source.readInt();
}
public function readShort () :int
//throws IOError
{
return _source.readShort();
}
public function readUTF () :String
//throws IOError
{
return _source.readUTF();
}
/** Named "client properties" that we can provide to deserialized objects. */
protected var _cliProps :Object;
/** The target DataInput that we route input from. */
protected var _source :IDataInput;
/** The object currently being read from the stream. */
protected var _current :Object;
/** The streamer being used currently. */
protected var _streamer :Streamer;
/** A map of short class code to ClassMapping info. */
protected var _classMap :Array = new Array();
private static var _debugObjectCounter :int = 0;
private static const log :Log = Log.getLog(ObjectInputStream);
}
}
@@ -0,0 +1,236 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
import flash.utils.ByteArray;
import flash.utils.IDataOutput;
import com.threerings.util.ClassUtil;
import com.threerings.util.Map;
import com.threerings.util.Maps;
import com.threerings.util.Log;
import com.threerings.util.Long;
import com.threerings.util.Short;
public class ObjectOutputStream
{
private static const log :Log = Log.getLog(ObjectOutputStream);
public function ObjectOutputStream (targ :IDataOutput)
{
_targ = targ;
}
public function writeObject (obj :Object) :void
//throws IOError
{
// if the object to be written is null (or undefined) write a zero
if (obj == null) {
writeShort(0);
return;
}
var cname :String;
if (obj is TypedArray) {
cname = (obj as TypedArray).getJavaType();
} else {
cname = ClassUtil.getClassName(obj);
}
// look up the class mapping record
var cmap :ClassMapping = (_classMap.get(cname) as ClassMapping);
// create a class mapping if we've not got one
if (cmap == null) {
var streamer :Streamer = Streamer.getStreamer(obj);
if (streamer == null) {
throw new Error("Unable to stream " + cname);
}
cmap = new ClassMapping(_nextCode++, streamer);
_classMap.put(cname, cmap);
if (_nextCode > Short.MAX_VALUE) {
throw new Error("Too many unique classes written to ObjectOutputStream");
}
if (ObjectInputStream.DEBUG) {
log.debug("Assigning class code", "code", cmap.code, "class", cname);
}
writeShort(-cmap.code);
writeUTF(streamer.getJavaClassName());
} else {
writeShort(cmap.code);
}
writeBareObjectImpl(obj, cmap.streamer);
}
public function writeBareObject (obj :Object, streamer :Streamer = null) :void
//throws IOError
{
if (streamer == null) {
streamer = Streamer.getStreamer(obj);
}
writeBareObjectImpl(obj, streamer);
}
public function writeBareObjectImpl (obj :Object, streamer :Streamer) :void
{
// otherwise, stream it!
_current = obj;
_streamer = streamer;
try {
_streamer.writeObject(obj, this);
} finally {
_current = null;
_streamer = null;
}
}
/**
* Called to write an object reference within a Streamable object when the type of the
* field is a final type.
*
* @example This is how you would do it, even if s and o referred to the same String object!
* <listing version="3.0">
* public class Foo
* implements Streamable
* {
* public var o :Object;
* public var s :String;
* ...
* public function writeObject (out :ObjectOutputStream) :void
* {
* out.writeObject(o);
* out.writeField(s);
* }
* }
* </listing>
*/
public function writeField (val :Object, streamer :Streamer = null) :void
//throws IOError
{
var b :Boolean = (val != null);
writeBoolean(b);
if (b) {
writeBareObject(val, streamer);
}
}
/**
* Uses the default streamable mechanism to write the contents of the object currently being
* streamed. This can only be called from within a <code>writeObject</code> implementation in a
* {@link Streamable} object.
*/
public function defaultWriteObject () :void
//throws IOError
{
// sanity check
if (_current == null) {
throw new Error("defaultWriteObject() called illegally.");
}
// write the instance data
_streamer.writeObject(_current, this);
}
public function writeBoolean (value :Boolean) :void
//throws IOError
{
_targ.writeBoolean(value);
}
public function writeByte (value :int) :void
//throws IOError
{
_targ.writeByte(value);
}
public function writeBytes (
bytes :ByteArray, offset :uint = 0, length :uint = uint.MAX_VALUE) :void
//throws IOError
{
if (length == uint.MAX_VALUE) {
length = bytes.length - offset;
}
if (length > 0) {
_targ.writeBytes(bytes, offset, length);
}
}
public function writeDouble (value :Number) :void
//throws IOError
{
_targ.writeDouble(value);
}
public function writeFloat (value :Number) :void
//throws IOError
{
_targ.writeFloat(value);
}
public function writeLong (value :Long) :void
{
writeBareObject(value);
}
public function writeInt (value :int) :void
//throws IOError
{
_targ.writeInt(value);
}
public function writeShort (value :int) :void
//throws IOError
{
_targ.writeShort(value);
}
public function writeUTF (value :String) :void
//throws IOError
{
_targ.writeUTF(value);
}
// these two are defined in IDataOutput, but have no java equivalent so we skip them.
//public function writeUnsignedInt (value :int) :void
//public function writeUTFBytes (value :int) :void
/** The target DataOutput that we route things to. */
protected var _targ :IDataOutput;
/** A counter used to assign codes to streamed classes. */
protected var _nextCode :int = 1;
/** The object currently being written out. */
protected var _current :Object;
/** The streamer being used currently. */
protected var _streamer :Streamer;
/** A map of classname to ClassMapping info. */
protected var _classMap :Map = Maps.newMapOf(String);
}
}
@@ -0,0 +1,64 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
import com.threerings.util.Joiner;
/**
* A simple serializable object implements the {@link Streamable}
* interface and provides a default {@link #toString} implementation which
* outputs all public members.
*/
public class SimpleStreamableObject implements Streamable
{
// from interface Streamable
public function readObject (ins :ObjectInputStream) :void
{
// nothing by default
}
// from interface Streamable
public function writeObject (out :ObjectOutputStream) :void
{
// nothing by default
}
/**
* Generates a string representation of this instance.
*/
public function toString () :String
{
var j :Joiner = Joiner.createFor(this);
toStringJoiner(j);
return j.toString();
}
/**
* Handles the toString-ification of all public members. Derived
* classes can override and include non-public members if desired.
*/
protected function toStringJoiner (j :Joiner): void
{
j.addFields(this);
}
}
}
@@ -0,0 +1,36 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
/**
* Note: all Streamable instances should have a constructor that copes
* with no arguments.
*/
public interface Streamable
{
function writeObject (out :ObjectOutputStream) :void;
//throws IOError;
function readObject (ins :ObjectInputStream) :void;
//throws IOError; /** ClassCastException equivalent. */
}
}
+165
View File
@@ -0,0 +1,165 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
import flash.utils.ByteArray;
import flash.utils.Dictionary;
import com.threerings.util.ByteEnum;
import com.threerings.util.ClassUtil;
import com.threerings.util.Enum;
import com.threerings.io.streamers.ArrayStreamer;
import com.threerings.io.streamers.ByteArrayStreamer;
import com.threerings.io.streamers.ByteEnumStreamer;
import com.threerings.io.streamers.EnumStreamer;
import com.threerings.io.streamers.MapStreamer;
import com.threerings.io.streamers.NumberStreamer;
import com.threerings.io.streamers.SetStreamer;
import com.threerings.io.streamers.StringStreamer;
public class Streamer
{
public static function getStreamer (obj :Object) :Streamer
{
var jname :String;
if (obj is TypedArray) {
jname = TypedArray(obj).getJavaType();
} else {
jname = Translations.getToServer(ClassUtil.getClassName(obj));
}
return getStreamerByJavaName(jname);
}
public static function getStreamerByJavaName (jname :String) :Streamer
{
initStreamers();
// see if we have a streamer for it
var streamer :Streamer = _byJName[jname] as Streamer;
if (streamer != null) {
return streamer;
}
// see if it's an array that we unstream using an ArrayStreamer
if (jname.charAt(0) === "[") {
streamer = new ArrayStreamer(jname);
} else {
// otherwise see if it represents a Streamable
// usually this is called from ObjectInputStream, but when it's called from
// ObjectOutputStream it's a bit annoying, because we started with a class/object.
// But: the code is smaller, so that wins
var clazz :Class = ClassUtil.getClassByName(Translations.getFromServer(jname));
if (ClassUtil.isAssignableAs(Enum, clazz)) {
streamer = ClassUtil.isAssignableAs(ByteEnum, clazz) ?
new ByteEnumStreamer(clazz, jname) : new EnumStreamer(clazz, jname);
} else if (ClassUtil.isAssignableAs(Streamable, clazz)) {
streamer = new Streamer(clazz, jname);
} else {
return null;
}
}
// add the good new streamer
registerStreamer(streamer);
return streamer;
}
/** This should be a protected constructor. */
public function Streamer (targ :Class, jname :String = null)
//throws IOError
{
_target = targ;
_jname = (jname != null) ? jname : Translations.getToServer(ClassUtil.getClassName(targ));
}
/**
* Return the String to use to identify the class that we're streaming.
*/
public function getJavaClassName () :String
{
return _jname;
}
public function writeObject (obj :Object, out :ObjectOutputStream) :void
//throws IOError
{
(obj as Streamable).writeObject(out);
}
public function createObject (ins :ObjectInputStream) :Object
//throws IOError
{
// actionscript is so fucked up
return new _target();
}
public function readObject (obj :Object, ins :ObjectInputStream) :void
//throws IOError
{
(obj as Streamable).readObject(ins);
}
public function toString () :String
{
return "[Streamer(" + _jname + ")]";
}
protected static function registerStreamer (st :Streamer, ... extraJavaNames) :void
{
_byJName[st.getJavaClassName()] = st;
for each (var name :String in extraJavaNames) {
_byJName[name] = st;
}
}
/**
* Initialize our streamers. This cannot simply be done statically
* because we cannot instantiate a subclass when this class is still
* being created. Fucking actionscript.
*/
private static function initStreamers () :void
{
if (_byJName != null) {
return;
}
_byJName = new Dictionary();
for each (var c :Class in [ StringStreamer, NumberStreamer, ByteArrayStreamer ]) {
registerStreamer(Streamer(new c()));
}
registerStreamer(ArrayStreamer.INSTANCE,
"java.util.List", "java.util.ArrayList", "java.util.Collection");
registerStreamer(SetStreamer.INSTANCE, "java.util.Set");
registerStreamer(MapStreamer.INSTANCE, "java.util.Map");
}
protected var _target :Class;
protected var _jname :String;
protected static var _byJName :Dictionary;
}
}
@@ -0,0 +1,68 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
import flash.utils.Dictionary;
/**
* Maintains a set of translations between actionscript class names
* and server (Java) class names.
*/
public class Translations
{
public static function getToServer (asName :String) :String
{
var javaName :String = (_toServer[asName] as String);
return (javaName == null) ? asName.replace("_", "$") : javaName;
}
public static function getFromServer (javaName :String) :String
{
var asName :String = (_fromServer[javaName] as String);
return (asName == null) ? javaName.replace("$", "_") : asName;
}
public static function addTranslation (asName :String, javaName :String) :void
{
_toServer[asName] = javaName;
_fromServer[javaName] = asName;
}
/** A mapping of actionscript names to java names. */
protected static var _toServer :Dictionary = new Dictionary();
/** A mapping of java names to actionscript names. */
protected static var _fromServer :Dictionary = new Dictionary();
// initialize some standard classes
addTranslation("Object", "java.lang.Object");
addTranslation("String", "java.lang.String");
addTranslation("Array", "[Ljava.lang.Object;");
addTranslation("flash.utils.ByteArray", "[B");
addTranslation("com.threerings.util.langBoolean", "java.lang.Boolean");
addTranslation("com.threerings.util.Byte", "java.lang.Byte");
addTranslation("com.threerings.util.Short", "java.lang.Short");
addTranslation("com.threerings.util.Integer", "java.lang.Integer");
addTranslation("com.threerings.util.Long", "java.lang.Long");
addTranslation("com.threerings.util.Float", "java.lang.Float");
}
}
+127
View File
@@ -0,0 +1,127 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io {
import flash.utils.ByteArray;
import com.threerings.util.ClassUtil;
import com.threerings.util.Cloneable;
public dynamic class TypedArray extends Array
implements Cloneable
{
/**
* Convenience method to get the java type of an array containing objects of the specified
* class.
*/
public static function getJavaType (of :Class) :String
{
if (of === Boolean) {
return "[Z";
} else if (of === int) { // Number will be int if something like 3.0
return "[I";
} else if (of === Number) {
return "[D";
} else if (of === ByteArray) {
return "[[B";
}
var cname :String = Translations.getToServer(ClassUtil.getClassName(of));
return "[L" + cname + ";";
}
/** Because we have no actionscript type to check, we'll just handle this one manually. */
public static function getJavaShortType () :String
{
return "[S";
}
/**
* A factory method to create a TypedArray for holding objects of the specified type.
*/
public static function create (of :Class, initialValues :Array = null) :TypedArray
{
var ta :TypedArray = new TypedArray(getJavaType(of));
if (initialValues != null) {
ta.addAll(initialValues);
}
return ta;
}
/**
* A factory method to create a TypedArray for holding data meant as java shorts.
*/
public static function createShort (initialValues :Array = null) :TypedArray
{
var ta :TypedArray = new TypedArray(getJavaShortType());
if (initialValues != null) {
ta.addAll(initialValues);
}
return ta;
}
/**
* Create a TypedArray
*
* @param jtype The java classname of this array, for example "[I" to represent an int[], or
* "[Ljava.lang.Object;" for Object[].
*/
public function TypedArray (jtype :String)
{
_jtype = jtype;
}
/**
* Adds all of the elements of the supplied array to this typed array. The types of the
* elements of the target array must, of course, be of the type specified for this array
* otherwise badness will ensue.
*
* @return this array instance for handy call chainability.
*/
public function addAll (other :Array) :TypedArray
{
push.apply(this, other);
return this;
}
public function getJavaType () :String
{
return _jtype;
}
// from Cloneable
public function clone () :Object
{
var clazz :Class = ClassUtil.getClass(this);
var copy :TypedArray = new clazz(_jtype);
copy.addAll(this);
return copy;
}
/** The 'type' of this array, which doesn't really mean anything except gives it a clue as to
* how to stream to our server. */
protected var _jtype :String;
}
}
@@ -0,0 +1,203 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.streamers {
import com.threerings.io.ArrayMask;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamer;
import com.threerings.io.Translations;
import com.threerings.io.TypedArray;
import com.threerings.util.ClassUtil;
import com.threerings.util.Enum;
import com.threerings.util.Byte;
import com.threerings.util.Float;
import com.threerings.util.Integer;
import com.threerings.util.Log;
import com.threerings.util.Long;
import com.threerings.util.Short;
import com.threerings.util.langBoolean;
/**
* A Streamer for Array objects.
*/
public class ArrayStreamer extends Streamer
{
public static const INSTANCE :ArrayStreamer = new ArrayStreamer();
public function ArrayStreamer (jname :String = "[Ljava.lang.Object;")
{
super(TypedArray, jname);
var secondChar :String = jname.charAt(1);
if (secondChar === "[") {
// if we're a multi-dimensional array then we need a delegate
_delegate = Streamer.getStreamerByJavaName(jname.substring(1));
_isFinal = true; // it just is
} else if (secondChar === "L") {
// form is "[L<class>;"
var baseJClass :String = jname.substring(2, jname.length - 1);
_delegate = Streamer.getStreamerByJavaName(baseJClass);
_elementType = ClassUtil.getClassByName(Translations.getFromServer(baseJClass));
_isFinal = isFinal(_elementType);
} else if (secondChar === "I") {
_elementType = int;
} else if (secondChar === "Z") {
_elementType = Boolean;
} else if (secondChar === "S") {
_elementType = PrimitiveShort;
} else {
Log.getLog(this).warning("Other array types are not yet handled", "jname", jname);
throw new Error("Don't know how to stream '" + jname + "' instances.");
}
}
override public function createObject (ins :ObjectInputStream) :Object
{
var ta :TypedArray = new TypedArray(_jname);
ta.length = ins.readInt();
return ta;
}
override public function writeObject (obj :Object, out :ObjectOutputStream) :void
{
var arr :Array = (obj as Array);
var ii :int;
out.writeInt(arr.length);
if (_elementType == int) {
for (ii = 0; ii < arr.length; ii++) {
out.writeInt(int(arr[ii]));
}
} else if (_elementType == PrimitiveShort) {
for (ii = 0; ii < arr.length; ii++) {
out.writeShort(int(arr[ii]));
}
} else if (_elementType == Boolean) {
for (ii = 0; ii < arr.length; ii++) {
out.writeBoolean(Boolean(arr[ii]));
}
} else if (_isFinal) {
var mask :ArrayMask = new ArrayMask(arr.length);
for (ii = 0; ii < arr.length; ii++) {
if (arr[ii] != null) {
mask.setBit(ii);
}
}
mask.writeTo(out);
// now write the populated elements
for (ii = 0; ii < arr.length; ii++) {
var element :Object = arr[ii];
if (element != null) {
out.writeBareObjectImpl(element, _delegate);
}
}
} else {
for (ii = 0; ii < arr.length; ii++) {
out.writeObject(arr[ii]);
}
}
}
override public function readObject (obj :Object, ins :ObjectInputStream) :void
{
var arr :Array = (obj as Array);
var ii :int;
if (_elementType == int) {
for (ii = 0; ii < arr.length; ii++) {
arr[ii] = ins.readInt();
}
} else if (_elementType == PrimitiveShort) {
for (ii = 0; ii < arr.length; ii++) {
arr[ii] = ins.readShort();
}
} else if (_elementType == Boolean) {
for (ii = 0; ii < arr.length; ii++) {
arr[ii] = ins.readBoolean();
}
} else if (_isFinal) {
var mask :ArrayMask = new ArrayMask();
mask.readFrom(ins);
for (ii = 0; ii < arr.length; ii++) {
if (mask.isSet(ii)) {
var target :Object;
if (_delegate == null) {
target = new _elementType();
} else {
target = _delegate.createObject(ins);
}
ins.readBareObjectImpl(target, _delegate);
arr[ii] = target;
}
}
} else {
for (ii = 0; ii < arr.length; ii++) {
arr[ii] = ins.readObject();
}
}
}
protected static function isFinal (type :Class) :Boolean
{
if (type === String || type == Byte || type == Short || type === Integer ||
type == Float || type == Long || type === langBoolean) {
return true;
}
// all enums are final, even if you forget to make your enum class final, you punk
if (ClassUtil.isAssignableAs(Enum, type)) {
return true;
}
// TODO: there's currently no way to determine final from the class
// I thought examining the prototype might do it, but no dice.
// Fuckers!
return false;
}
/** A streamer for our elements. */
protected var _delegate :Streamer;
/** If this is the final dimension of the array, the element type. */
protected var _elementType :Class;
/** Whether we're final or not: true if we're not the final dimension
* or if the element type is final. */
protected var _isFinal :Boolean;
}
}
/** A class to represent that the data in question should be handled as a java primitive short. */
class PrimitiveShort
{
}
@@ -0,0 +1,62 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.streamers {
import flash.utils.ByteArray;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamer;
/**
* A Streamer for ByteArray objects.
*/
public class ByteArrayStreamer extends Streamer
{
public function ByteArrayStreamer ()
{
super(ByteArray, "[B"); // yes, that's the Java class for a byte[].
}
override public function createObject (ins :ObjectInputStream) :Object
{
var bytes :ByteArray = new ByteArray();
bytes.length = ins.readInt();
return bytes;
}
override public function writeObject (obj :Object, out :ObjectOutputStream) :void
{
var bytes :ByteArray = (obj as ByteArray);
out.writeInt(bytes.length);
out.writeBytes(bytes);
}
override public function readObject (obj :Object, ins :ObjectInputStream) :void
{
var bytes :ByteArray = (obj as ByteArray);
if (bytes.length > 0) {
ins.readBytes(bytes, 0, bytes.length);
}
}
}
}
@@ -0,0 +1,52 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.streamers {
import com.threerings.util.ByteEnum;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamer;
public class ByteEnumStreamer extends Streamer
{
public function ByteEnumStreamer (enumClass :Class, jname :String = null)
{
super(enumClass, jname);
}
override public function createObject (ins :ObjectInputStream) :Object
{
return ByteEnum.fromByte(_target, ins.readByte());
}
override public function writeObject (obj :Object, out :ObjectOutputStream) :void
{
out.writeByte(ByteEnum(obj).toByte());
}
override public function readObject (obj :Object, ins :ObjectInputStream) :void
{
// unneeded, done in createObject
}
}
}
@@ -0,0 +1,52 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.streamers {
import com.threerings.util.Enum;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamer;
public class EnumStreamer extends Streamer
{
public function EnumStreamer (enumClass :Class, jname :String = null)
{
super(enumClass, jname);
}
override public function createObject (ins :ObjectInputStream) :Object
{
return Enum.valueOf(_target, ins.readUTF());
}
override public function writeObject (obj :Object, out :ObjectOutputStream) :void
{
out.writeUTF((obj as Enum).name());
}
override public function readObject (obj :Object, ins :ObjectInputStream) :void
{
// unneeded, done in createObject
}
}
}
@@ -0,0 +1,81 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.streamers {
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamer;
import com.threerings.util.ClassUtil;
import com.threerings.util.Map;
import com.threerings.util.Maps;
/**
* Streamer for Maps.
*/
public class MapStreamer extends Streamer
{
public static const INSTANCE :MapStreamer = new MapStreamer();
public static const DEFAULT_MAP :Map = Maps.newBuilder(Object).makeImmutable().build();
public function MapStreamer ()
{
super(Map, "java.util.HashMap");
}
override public function createObject (ins :ObjectInputStream) :Object
{
var size :int = ins.readInt();
var map :Map;
if (size > 0) {
// guess the type of map based on the first key
var key :Object = ins.readObject();
map = Maps.newMapOf(ClassUtil.getClass(key));
map.put(key, ins.readObject());
for (var ii :int = 1; ii < size; ii++) {
map.put(ins.readObject(), ins.readObject());
}
} else {
// oh crap
map = DEFAULT_MAP;
}
return map;
}
override public function readObject (obj :Object, ins :ObjectInputStream) :void
{
// nada
}
override public function writeObject (obj :Object, out :ObjectOutputStream) :void
{
var map :Map = Map(obj);
out.writeInt(map.size());
map.forEach(function (key :Object, value :Object) :void {
out.writeObject(key);
out.writeObject(value);
});
}
}
}
@@ -0,0 +1,54 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.streamers {
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamer;
/**
* A Streamer for Number objects.
*/
public class NumberStreamer extends Streamer
{
public function NumberStreamer ()
{
super(Number, "java.lang.Double");
}
override public function createObject (ins :ObjectInputStream) :Object
{
return ins.readDouble();
}
override public function writeObject (obj :Object, out :ObjectOutputStream) :void
{
var n :Number = (obj as Number);
out.writeDouble(n);
}
override public function readObject (obj :Object, ins :ObjectInputStream) :void
{
// nothing here, the Number is fully read in createObject()
}
}
}
@@ -0,0 +1,80 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.io.streamers {
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamer;
import com.threerings.util.ClassUtil;
import com.threerings.util.Set;
import com.threerings.util.Sets;
/**
* Streamer for Sets.
*/
public class SetStreamer extends Streamer
{
public static const INSTANCE :SetStreamer = new SetStreamer();
public static const DEFAULT_SET :Set = Sets.newBuilder(Object).makeImmutable().build();
public function SetStreamer ()
{
super(Set, "java.util.HashSet");
}
override public function createObject (ins :ObjectInputStream) :Object
{
var size :int = ins.readInt();
var set :Set;
if (size > 0) {
// guess the type of set based on the first value
var first :Object = ins.readObject();
set = Sets.newSetOf(ClassUtil.getClass(first));
set.add(first);
for (var ii :int = 1; ii < size; ii++) {
set.add(ins.readObject());
}
} else {
// oh crap
set = DEFAULT_SET;
}
return set;
}
override public function readObject (obj :Object, ins :ObjectInputStream) :void
{
// nada
}
override public function writeObject (obj :Object, out :ObjectOutputStream) :void
{
var set :Set = Set(obj);
out.writeInt(set.size());
set.forEach(function (value :Object) :void {
out.writeObject(value);
});
}
}
}

Some files were not shown because too many files have changed in this diff Show More