Finished the job of splitting Narya into submodules.

The ActionScript code is now under aslib and is built via Maven (and Ant). The
CPP code is under cpplib and is ignored by everything (other than service
generation), but now at least it's not crufting up the Java submodule with its
XCode project.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6783 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2012-02-14 19:11:33 +00:00
parent 20074741b0
commit 1c311431ad
336 changed files with 113 additions and 51 deletions
+100
View File
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.threerings</groupId>
<artifactId>narya-parent</artifactId>
<version>1.12-SNAPSHOT</version>
</parent>
<artifactId>naryalib</artifactId>
<packaging>swc</packaging>
<name>Narya ActionScript</name>
<repositories>
<repository>
<id>ooo-public</id>
<url>http://ooo-maven.googlecode.com/hg/repository</url>
</repository>
<repository>
<id>flexmojos</id>
<url>http://repository.sonatype.org/content/groups/flexgroup/</url>
</repository>
</repositories>
<properties>
<flex.version>4.1.0.16076</flex.version>
</properties>
<dependencies>
<dependency>
<groupId>com.threerings</groupId>
<artifactId>aspirin</artifactId>
<version>1.11</version>
<type>swc</type>
<scope>external</scope>
</dependency>
<dependency>
<groupId>com.adobe</groupId>
<artifactId>as3corelib</artifactId>
<version>0.93</version>
<type>swc</type>
<scope>external</scope>
</dependency>
<dependency>
<groupId>org.osflash</groupId>
<artifactId>signals</artifactId>
<version>0.8</version>
<type>swc</type>
<scope>external</scope>
</dependency>
<dependency>
<groupId>com.adobe.flex.framework</groupId>
<artifactId>flex-framework</artifactId>
<version>${flex.version}</version>
<type>pom</type>
<optional>true</optional>
</dependency>
</dependencies>
<pluginRepositories>
<pluginRepository>
<id>flexmojos</id>
<url>http://repository.sonatype.org/content/groups/flexgroup/</url>
</pluginRepository>
</pluginRepositories>
<build>
<sourceDirectory>src/main/as</sourceDirectory>
<plugins>
<plugin>
<groupId>org.sonatype.flexmojos</groupId>
<artifactId>flexmojos-maven-plugin</artifactId>
<extensions>true</extensions>
<version>3.9</version>
<configuration>
<omitTraceStatements>false</omitTraceStatements>
<debug>true</debug>
<incremental>false</incremental>
<useNetwork>false</useNetwork>
<verboseStacktraces>true</verboseStacktraces>
</configuration>
<dependencies>
<dependency>
<groupId>com.adobe.flex</groupId>
<artifactId>compiler</artifactId>
<version>${flex.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.adobe.flex.compiler</groupId>
<artifactId>asdoc</artifactId>
<version>${flex.version}</version>
<classifier>template</classifier>
<type>zip</type>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,61 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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-2012 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.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.presents.client.Client;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.bureau.client.BureauClient;
import com.threerings.bureau.client.BureauDirector;
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,76 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.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;
}
public override function getReceiverCode () :String
{
return RECEIVER_CODE;
}
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);
}
}
}
}
@@ -0,0 +1,214 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.Log;
import com.threerings.util.Map;
import com.threerings.util.Maps;
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,34 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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;
public interface BureauReceiver extends InvocationReceiver
{
// from Java interface BureauReceiver
function createAgent (arg1 :int) :void;
// from Java interface BureauReceiver
function destroyAgent (arg1 :int) :void;
}
}
@@ -0,0 +1,46 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.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,161 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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-2012 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-2012 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-2012 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-2012 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,95 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.Integer;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.bureau.client.BureauService;
/**
* 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,45 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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,72 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.StringUtil;
import com.threerings.crowd.chat.data.ChatCodes;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.util.CrowdContext;
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-2012 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;
import com.threerings.crowd.chat.data.ChatChannel;
/**
* 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-2012 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-2012 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-2012 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-2012 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.presents.client.InvocationService;
import com.threerings.presents.client.InvocationService_InvocationListener;
/**
* 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,37 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.InvocationService_InvocationListener;
/**
* 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-2012 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-2012 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-2012 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,37 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.ChatCodes;
import com.threerings.crowd.util.CrowdContext;
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-2012 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-2012 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,43 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.chat.data.ChatCodes;
import com.threerings.crowd.util.CrowdContext;
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,78 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.chat.data.ChatCodes;
import com.threerings.crowd.util.CrowdContext;
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-2012 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-2012 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,41 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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;
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,34 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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;
/**
* 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-2012 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,170 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.chat.data.ChatCodes;
import com.threerings.crowd.client.CrowdClient;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.util.CrowdContext;
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() as CrowdClient).bodyOf();
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,43 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.chat.data.ChatCodes;
import com.threerings.crowd.util.CrowdContext;
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,51 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.Byte;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.crowd.chat.client.ChannelSpeakService;
/**
* 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,62 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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,92 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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;
/**
* 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,80 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.presents.client.InvocationService_InvocationListener;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller;
import com.threerings.crowd.chat.client.ChatService;
import com.threerings.crowd.chat.client.ChatService_TellListener;
/**
* 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,54 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.Long;
import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller;
import com.threerings.crowd.chat.client.ChatService_TellListener;
/**
* 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-2012 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;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;
import com.threerings.util.ClassUtil;
/**
* 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,51 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.Byte;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.crowd.chat.client.SpeakService;
/**
* 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-2012 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,72 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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-2012 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 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-2012 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 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,34 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.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,57 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.BodyObject;
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 = null)
{
super(creds);
}
/**
* Returns the body this client is (currently) attached to. If you subclass this, its result
* should probably match whatever {@link BodyLocator#forClient} would return for this client.
* The default implements the historical assumption that the client object is also the body.
*/
public function bodyOf () :BodyObject
{
return (getClientObject() as BodyObject);
}
}
}
@@ -0,0 +1,64 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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,66 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.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;
}
public override function getReceiverCode () :String
{
return RECEIVER_CODE;
}
public override function dispatchNotification (methodId :int, args :Array) :void
{
switch (methodId) {
case FORCED_MOVE:
LocationReceiver(receiver).forcedMove(
(args[0] as int)
);
return;
default:
super.dispatchNotification(methodId, args);
}
}
}
}
@@ -0,0 +1,615 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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;
import com.threerings.util.Log;
import com.threerings.util.ObserverList;
import com.threerings.util.ResultListener;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.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-2012 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-2012 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,31 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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;
public interface LocationReceiver extends InvocationReceiver
{
// from Java interface LocationReceiver
function forcedMove (arg1 :int) :void;
}
}
@@ -0,0 +1,37 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.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,37 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.InvocationService_InvocationListener;
import com.threerings.crowd.data.PlaceConfig;
/**
* 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-2012 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-2012 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-2012 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-2012 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-2012 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.Name;
import com.threerings.util.ObserverList;
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-2012 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-2012 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-2012 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-2012 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-2012 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;
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,51 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.langBoolean;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.crowd.client.BodyService;
/**
* 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,200 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.util.Byte;
import com.threerings.util.Name;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.PermissionPolicy;
/**
* 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];
}
override protected function createPermissionPolicy () :PermissionPolicy
{
return new CrowdPermissionPolicy();
}
}
}
@@ -0,0 +1,34 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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-2012 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-2012 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-2012 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.Integer;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.crowd.client.LocationService;
import com.threerings.crowd.client.LocationService_MoveListener;
/**
* 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,52 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.InvocationMarshaller_ListenerMarshaller;
import com.threerings.crowd.client.LocationService_MoveListener;
/**
* 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-2012 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,132 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.ClassUtil;
import com.threerings.util.Cloneable;
import com.threerings.util.Integer;
import com.threerings.util.Name;
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,73 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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,82 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.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,212 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.util.Iterator;
import com.threerings.util.Name;
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;
/**
* 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-2012 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-2012 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.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.util.Name;
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-2012 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.chat.client.ChatDirector;
import com.threerings.crowd.client.LocationDirector;
import com.threerings.crowd.client.OccupantDirector;
import com.threerings.crowd.client.PlaceView;
/**
* 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-2012 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-2012 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,45 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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,116 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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,296 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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-2012 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.Log;
import com.threerings.util.Long;
import com.threerings.util.Map;
import com.threerings.util.Maps;
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.getUpstreamJavaClassName());
} 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-2012 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-2012 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. */
}
}
@@ -0,0 +1,199 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.io.streamers.ArrayStreamer;
import com.threerings.io.streamers.ByteArrayStreamer;
import com.threerings.io.streamers.ByteEnumStreamer;
import com.threerings.io.streamers.DelegatingStreamer;
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;
import com.threerings.util.ByteEnum;
import com.threerings.util.ClassUtil;
import com.threerings.util.Enum;
import com.threerings.util.Log;
public class Streamer
{
public static function registerStreamer (st :Streamer, ... extraJavaNames) :void
{
initStreamers();
extraJavaNames.unshift(st.getJavaClassName());
for each (var name :String in extraJavaNames) {
if (name in _byJName) {
log.warning("Existing Streamer replaced", "name", name,
"cur", _byJName[name], "replacement", st);
}
_byJName[name] = st;
}
}
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 {
// if we already support streaming a superclass of this object, use it instead
// (e.g. a DictionaryMap gets deserialized on the server as a HashMap)
for each (var upcast :Streamer in _byJName) {
if (ClassUtil.isAssignableAs(upcast._target, clazz)) {
streamer = new DelegatingStreamer(upcast, clazz, jname);
break;
}
}
if (streamer == null) {
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));
}
/**
* Returns the canonical class name used to identify this streamer. This is known as the java
* class name and is typically the class sent to the server, but may in some cases be different.
* @see com.threerings.io.streamers.DelegatingStreamer
*/
public function getJavaClassName () :String
{
return _jname;
}
/**
* Returns the class name used to deserialize the class represented by this streamer on the
* server. This is typically the same as the java class name, but may in some cases be
* different.
* @see com.threerings.io.streamers.DelegatingStreamer
*/
public function getUpstreamJavaClassName () :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 + ")]";
}
/**
* 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;
protected static const log :Log = Log.getLog(Streamer);
}
}
@@ -0,0 +1,68 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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");
}
}
@@ -0,0 +1,127 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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,204 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.Byte;
import com.threerings.util.ClassUtil;
import com.threerings.util.Enum;
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-2012 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-2012 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.ByteEnum;
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,70 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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 that allows subclasses or implementations of the classes or interfaces supported by
* other streamers to be included in upstream messages. All serialization support is impelemented
* using the parent streamer, but the java class name is different. This is so that the streamer
* caching mechanism and the upstream class mappings both work.
*/
public class DelegatingStreamer extends Streamer
{
public function DelegatingStreamer (parent :Streamer, targ:Class, jname:String=null)
{
super(targ, jname);
_parent = parent;
}
/** @inheritDoc */
override public function getUpstreamJavaClassName () :String
{
return _parent.getUpstreamJavaClassName();
}
/** @inheritDoc */
override public function writeObject (obj :Object, out :ObjectOutputStream) :void
//throws IOError
{
_parent.writeObject(obj, out);
}
/** @inheritDoc */
override public function createObject (ins :ObjectInputStream) :Object
//throws IOError
{
return _parent.createObject(ins);
}
/** @inheritDoc */
override public function readObject (obj :Object, ins :ObjectInputStream) :void
//throws IOError
{
_parent.readObject(obj, ins);
}
protected var _parent :Streamer;
}
}
@@ -0,0 +1,52 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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.Enum;
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,80 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 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);
});
}
}
}

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