More progress.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3849 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
This document contains a couple of notes about some design decisions
|
||||
and some notes about flash that you may find useful.
|
||||
|
||||
|
||||
Design decisions
|
||||
----------------
|
||||
- I have kept accessors named like their Java counterparts, rather
|
||||
than embracing flash's property setter/getter methods (which are really
|
||||
cool), but I am starting to lean the other way and may rewrite some stuff.
|
||||
|
||||
- I am embracing flash's event distribution model because it saved me a bunch
|
||||
of work.
|
||||
|
||||
|
||||
|
||||
Notes
|
||||
-----
|
||||
- In actionscript, 'package' is simply a block command to sweep whatever is
|
||||
defined inside the block so that it's in that package. This means
|
||||
that in addition to classes being in a package, freestanding functions
|
||||
and I believe variables and constants can be in a package.
|
||||
|
||||
We are not putting freestanding functions anywhere. Make a util class with
|
||||
static methods.
|
||||
|
||||
- ActionScript does not have inner classes. Only one public class may be
|
||||
defined in a file, and the filename must match the public class.
|
||||
However, protected classes cannot be defined within the package block!
|
||||
So it seems like the model is:
|
||||
|
||||
package com.foo {
|
||||
public class FooBar {
|
||||
// stuff
|
||||
}
|
||||
} // end: package foo
|
||||
|
||||
class HelperClass {
|
||||
// helper stuff
|
||||
}
|
||||
|
||||
|
||||
To me, this makes it seem as if the helper class is now globally scoped,
|
||||
which of course is the exact opposite of what is desired. This may
|
||||
not be the case, I haven't played with it much yet.
|
||||
|
||||
- Similarly, I'm unclear about sandboxes. If a user-created .swf is playing
|
||||
inside ours, I don't know if it can interact with our classes, and if so,
|
||||
what happens if it proceeds to define a class like
|
||||
com.threerings.presents.client.Client?
|
||||
|
||||
- constructors do not defaultly call super()- be sure to do it explicitely.
|
||||
Maybe we should get in the habit of doing it in Java for consistency and
|
||||
explicitness.
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.threerings.io {
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
import flash.util.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,65 @@
|
||||
package com.threerings.io {
|
||||
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.ProgressEvent;
|
||||
|
||||
import flash.net.Socket;
|
||||
|
||||
import flash.util.ByteArray;
|
||||
import flash.util.Endian;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when our socket has data that we can read.
|
||||
*/
|
||||
protected function socketHasData (event :ProgressEvent) :void
|
||||
{
|
||||
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;
|
||||
}
|
||||
_length = _socket.readInt();
|
||||
_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);
|
||||
_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
|
||||
dispatchEvent(new FrameAvailableEvent(_curData));
|
||||
_curData = null; // clear, so we know we need to first read length
|
||||
|
||||
// there's a good chance there's more on the socket, recurse
|
||||
// now to read it
|
||||
socketHasData(event);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,20 @@ import com.threerings.util.SimpleMap;
|
||||
|
||||
public class ObjectInputStream
|
||||
{
|
||||
public function ObjectInputStream (targ:IDataInput)
|
||||
public function ObjectInputStream (source :IDataInput = null)
|
||||
{
|
||||
_targ = targ;
|
||||
if (source == null) {
|
||||
source = new ByteArray();
|
||||
}
|
||||
_source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new source from which to read our data.
|
||||
*/
|
||||
public function setSource (source :IDataInput)
|
||||
{
|
||||
_source = source;
|
||||
}
|
||||
|
||||
public function readObject () :*
|
||||
@@ -104,50 +115,50 @@ public class ObjectInputStream
|
||||
public function readBoolean () :Boolean
|
||||
//throws IOError
|
||||
{
|
||||
return _targ.readBoolean();
|
||||
return _source.readBoolean();
|
||||
}
|
||||
|
||||
public function readByte () :int
|
||||
//throws IOError
|
||||
{
|
||||
return _targ.readByte();
|
||||
return _source.readByte();
|
||||
}
|
||||
|
||||
public function readBytes (bytes :ByteArray, offset :uint = 0,
|
||||
length :uint = 0) :void
|
||||
//throws IOError
|
||||
{
|
||||
_targ.readBytes(bytes, offset, length);
|
||||
_source.readBytes(bytes, offset, length);
|
||||
}
|
||||
|
||||
public function readDouble () :Number
|
||||
//throws IOError
|
||||
{
|
||||
return _targ.readDouble();
|
||||
return _source.readDouble();
|
||||
}
|
||||
|
||||
public function readFloat () :Number
|
||||
//throws IOError
|
||||
{
|
||||
return _targ.readFloat();
|
||||
return _source.readFloat();
|
||||
}
|
||||
|
||||
public function readInt () :int
|
||||
//throws IOError
|
||||
{
|
||||
return _targ.readInt();
|
||||
return _source.readInt();
|
||||
}
|
||||
|
||||
public function readShort () :int
|
||||
//throws IOError
|
||||
{
|
||||
return _targ.readShort();
|
||||
return _source.readShort();
|
||||
}
|
||||
|
||||
public function readUTF () :String
|
||||
//throws IOError
|
||||
{
|
||||
return _targ.readUTF();
|
||||
return _source.readUTF();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +171,7 @@ public class ObjectInputStream
|
||||
}
|
||||
|
||||
/** The target DataInput that we route input from. */
|
||||
protected var _targ :IDataInput;
|
||||
protected var _source :IDataInput;
|
||||
|
||||
/** The object currently being read from the stream. */
|
||||
protected var _current :*;
|
||||
|
||||
@@ -30,16 +30,51 @@ public class Client extends EventDispatcher
|
||||
_port = port;
|
||||
}
|
||||
|
||||
public function getHostname () :String
|
||||
{
|
||||
return _hostname;
|
||||
}
|
||||
|
||||
public function getPort () :int
|
||||
{
|
||||
return _port;
|
||||
}
|
||||
|
||||
public function getCredentials () :Credentials
|
||||
{
|
||||
return _creds;
|
||||
}
|
||||
|
||||
public function setCredentials (creds :Credentials) :void
|
||||
{
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
public function getVersion () :String
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
public function setVersion (version :String)
|
||||
{
|
||||
_version = version;
|
||||
}
|
||||
|
||||
public function getAuthResponseData () :AuthResponseData
|
||||
{
|
||||
return _authData;
|
||||
}
|
||||
|
||||
public function getDObjectManager () :DObjectManager
|
||||
{
|
||||
return _omgr;
|
||||
}
|
||||
|
||||
public function getClientOid () :int
|
||||
{
|
||||
return _cloid;
|
||||
}
|
||||
|
||||
public function getClientObject () :ClientObject
|
||||
{
|
||||
return _clobj;
|
||||
@@ -50,6 +85,24 @@ public class Client extends EventDispatcher
|
||||
return _invdir;
|
||||
}
|
||||
|
||||
public function getService (clazz :Class) :InvocationService
|
||||
{
|
||||
if (_bstrap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
public function requireService (clazz :Class) :InvocationService
|
||||
{
|
||||
var isvc :InvocationService = getService(clazz);
|
||||
if (isvc == null) {
|
||||
throw new Error(clazz + " isn't available. I can't bear to go on.");
|
||||
}
|
||||
return isvc;
|
||||
}
|
||||
|
||||
public function getBootstrapData () :BootstrapData
|
||||
{
|
||||
return _bstrap;
|
||||
@@ -76,9 +129,11 @@ public class Client extends EventDispatcher
|
||||
_comm = new Communicator(this);
|
||||
_comm.logon();
|
||||
|
||||
_tickInterval = new Timer(5000);
|
||||
_tickInterval.addEventListener(TimerEvent.TIMER, tick);
|
||||
_tickInterval.start();
|
||||
if (_tickInterval == null) {
|
||||
_tickInterval = new Timer(5000);
|
||||
_tickInterval.addEventListener(TimerEvent.TIMER, tick);
|
||||
_tickInterval.start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -126,6 +181,22 @@ public class Client extends EventDispatcher
|
||||
_invdir.init(omgr, _cloid, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every five seconds; ensures that we ping the server if we
|
||||
* haven't communicated in a long while.
|
||||
*/
|
||||
protected function tick (event :TimerEvent) :void
|
||||
{
|
||||
if (_comm == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var now :Number = new Date().getTime();
|
||||
if (now - _comm.getLastWrite() > PingRequest.PING_INTERVAL) {
|
||||
_comm.postMessage(new PingRequest());
|
||||
}
|
||||
}
|
||||
|
||||
protected function gotClientObject (clobj :ClientObject) :void
|
||||
{
|
||||
_clobj = clobj;
|
||||
@@ -145,22 +216,33 @@ public class Client extends EventDispatcher
|
||||
notifyObservers(ClientEvent.CLIENT_OBJECT_CHANGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every five seconds; ensures that we ping the server if we
|
||||
* haven't communicated in a long while.
|
||||
*/
|
||||
protected function tick (event :TimerEvent) :void
|
||||
protected function cleanup (logonError :Error) :void
|
||||
{
|
||||
if (_comm == null) {
|
||||
return;
|
||||
}
|
||||
// clear out our references
|
||||
_comm = null;
|
||||
_omgr = null;
|
||||
_clobj = null;
|
||||
_cloid = -1;
|
||||
|
||||
var now :Number = new Date().getTime();
|
||||
if (now - _comm.getLastWrite() > PingRequest.PING_INTERVAL) {
|
||||
_comm.postMessage(new PingRequest());
|
||||
// and let our invocation director know we're logged off
|
||||
_invdir.cleanup();
|
||||
|
||||
// if this was due to a logon error, we can notify our listeners
|
||||
// now that we're cleaned up: they may want to retry logon on
|
||||
// another port, or something
|
||||
if (logonError != null) {
|
||||
notifyObservers(ClientEvent.CLIENT_FAILED_TO_LOGON, logonError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the omgr when we receive a pong packet.
|
||||
*/
|
||||
protected function gotPong (pong :PongResponse) :void
|
||||
{
|
||||
// TODO: compute time delta bowl-shit
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to dispatch a client event to any listeners
|
||||
* and return the result of dispatchEvent.
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
package com.threerings.presents.client {
|
||||
|
||||
import flash.net.Socket;
|
||||
|
||||
import flash.util.ByteArray;
|
||||
import flash.util.Endian;
|
||||
|
||||
import com.threerings.io.FrameAvailableEvent;
|
||||
import com.threerings.io.FrameReader;
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
public class Communicator
|
||||
{
|
||||
public function Communicator (client :Client)
|
||||
@@ -9,9 +19,156 @@ public class Communicator
|
||||
|
||||
public function logon () :void
|
||||
{
|
||||
// create the socket and set up listeners
|
||||
_socket = new Socket();
|
||||
_socket.addEventListener(Event.CONNECT, socketOpened);
|
||||
_socket.addEventListener(IOErrorEvent.IO_ERROR, socketError);
|
||||
_socket.addEventListener(Event.CLOSE, socketClosed);
|
||||
|
||||
// create our input/output business
|
||||
_outBuffer = new ByteArray();
|
||||
_outBuffer.endian = Endian.BIG_ENDIAN;
|
||||
_outStream = new ObjectOutputStream(_outBuffer);
|
||||
|
||||
_frameReader = new FrameReader(_socket);
|
||||
_frameReader.addEventListener(FrameAvailableEvent.FRAME_AVAILABLE,
|
||||
inputFrameReceived);
|
||||
_inStream = new ObjectInputStream();
|
||||
|
||||
_socket.connect(_client.getHostname(), _client.getPort());
|
||||
}
|
||||
|
||||
public function logoff () :void
|
||||
{
|
||||
if (_socket == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendMessage(new LogoffRequest());
|
||||
|
||||
shutdown(null);
|
||||
}
|
||||
|
||||
protected function shutdown (logonError :Error) :void
|
||||
{
|
||||
if (_socket != null) {
|
||||
try {
|
||||
_socket.close();
|
||||
} catch (err :Error) {
|
||||
trace("Error closing failed socket: " + err);
|
||||
}
|
||||
_socket = null;
|
||||
_outStream = null;
|
||||
_inStream = null;
|
||||
_frameReader = null;
|
||||
_outBuffer = null;
|
||||
}
|
||||
|
||||
_client.cleanup(logonError);
|
||||
}
|
||||
|
||||
protected function sendMessage (msg :UpstreamMessage) :void
|
||||
{
|
||||
// write the message (ends up in _outBuffer)
|
||||
_outStream.writeObject(msg);
|
||||
|
||||
// frame it by writing the length, then the bytes
|
||||
_socket.writeInt(_outBuffer.length);
|
||||
_socket.writeBytes(_outBuffer);
|
||||
_socket.flush();
|
||||
|
||||
// clean up the output buffer
|
||||
_outBuffer.length = 0;
|
||||
_outBuffer.position = 0;
|
||||
|
||||
// make a note of our most recent write time
|
||||
updateWriteStamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time at which we last sent a packet to the server.
|
||||
*/
|
||||
protected function getLastWrite () :Number
|
||||
{
|
||||
return _lastWrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a note of the time at which we last communicated with the server.
|
||||
*/
|
||||
protected function updateWriteStamp () :void
|
||||
{
|
||||
_lastWrite = new Date().getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a frame of data from the server is ready to be
|
||||
* decoded into a DownstreamMessage.
|
||||
*/
|
||||
protected function inputFrameReceived (event :FrameAvailableEvent) :void
|
||||
{
|
||||
// convert the frame data into a message from the server
|
||||
_inStream.setSource(event.getFrameData());
|
||||
var msg :DownstreamMessage = _inStream.readObject();
|
||||
|
||||
if (_omgr != null) {
|
||||
// if we're logged on, then just do the normal thing
|
||||
_omgr.processMessage(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, this would be the AuthResponse to our logon attempt.
|
||||
var rsp :AuthResponse = (msg as AuthResponse); // TODO: as correct?
|
||||
var data :AuthResponseData = rsp.getData();
|
||||
if (data.code !== AuthResponseData.SUCCESS) {
|
||||
shutdown(new Error(data.code));
|
||||
return;
|
||||
}
|
||||
|
||||
// logon success
|
||||
_omgr = new ClientDObjectMgr(this, _client);
|
||||
_client._authData = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the connection to the server was successfully opened.
|
||||
*/
|
||||
protected function socketOpened (event :Event) :void
|
||||
{
|
||||
// well that's great! let's logon
|
||||
var req :AuthRequest = new AuthRequest(_client.getCredentials(),
|
||||
_client.getVersion());
|
||||
sendMessage(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is an io error with the socket.
|
||||
*/
|
||||
protected function socketError (event :IOErrorEvent) :void
|
||||
{
|
||||
trace("socketError: " + event);
|
||||
shutdown(new Error("socket closed unexpectedly."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the connection to the server was closed.
|
||||
*/
|
||||
protected function socketClosed (event :Event) :void
|
||||
{
|
||||
_client.notifyObserver(ClientEvent.CLIENT_CONNECTION_FAILED);
|
||||
shutdown(null);
|
||||
}
|
||||
|
||||
protected var _client :Client;
|
||||
protected var _omgr :ClientDObjectManager;
|
||||
|
||||
protected var _outBuffer :ByteArray;
|
||||
protected var _outStream :ObjectOutputStream;
|
||||
|
||||
protected var _inStream :ObjectInputStream;
|
||||
|
||||
protected var _socket :Socket;
|
||||
|
||||
protected var _lastWrite :Number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,12 @@ package com.threerings.presents.dobj {
|
||||
|
||||
import flash.events.EventDispatcher;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
public class DObject extends EventDispatcher
|
||||
implements Streamable
|
||||
{
|
||||
public function getOid ():int
|
||||
{
|
||||
@@ -14,6 +19,18 @@ public class DObject extends EventDispatcher
|
||||
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
out.writeInt(_oid);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
_oid = ins.readInt();
|
||||
}
|
||||
|
||||
protected var _oid :int;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.threerings.presents.net {
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
public class AuthResponse extends DownstreamMessage
|
||||
{
|
||||
public function getData () :AuthResponseData
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
_data = ins.readField(AuthResponseData);
|
||||
}
|
||||
|
||||
protected var _data :AuthResponseData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.threerings.presents.net {
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
|
||||
public class AuthResponseData extends DObject
|
||||
{
|
||||
/** A constant used to indicate a successful authentication. */
|
||||
public static const SUCCESS :String = "success";
|
||||
|
||||
/** Either the SUCCESS constant or a reason code indicating
|
||||
* why the authentication failed. */
|
||||
public var code :String;
|
||||
|
||||
// documentation inherited
|
||||
public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeField(code);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
code = ins.readField(String);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.threerings.presents.net {
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
import com.threerings.util.StreamableArrayList;
|
||||
|
||||
/**
|
||||
* A BoostrapData object is communicated back to the client
|
||||
* after authentication has succeeded and after the server is fully
|
||||
* prepared to deal with the client. It contains information the client
|
||||
* will need to interact with the server.
|
||||
*/
|
||||
public class BootstrapData
|
||||
implements Streamable
|
||||
{
|
||||
/** The oid of this client's associated distributed object. */
|
||||
public var clientOid :int;
|
||||
|
||||
/** A list of handles to invocation services. */
|
||||
public var services :StreamableArrayList;
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
trace("This is client code: BootstrapData shouldn't be written");
|
||||
//out.writeShort(messageId);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
clientOid = ins.readInt();
|
||||
services = ins.readField(StreamableArrayList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.threerings.presents.net {
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
|
||||
public class BootstrapNotification extends DownstreamMessage
|
||||
{
|
||||
public function getData () :BootstrapData
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
super.readObject(ins);
|
||||
_data = ins.readField(BootstrapData);
|
||||
}
|
||||
|
||||
/** The data associated with this notification. */
|
||||
protected var _data :BootstrapData;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.threerings.presents.net {
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
public class DownstreamMessage
|
||||
@@ -13,7 +15,8 @@ public class DownstreamMessage
|
||||
// documentation inherited from interface Streamable
|
||||
public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
out.writeShort(messageId);
|
||||
trace("This is client code: Downstream messages shouldn't be written");
|
||||
//out.writeShort(messageId);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
|
||||
@@ -26,12 +26,6 @@ public class PingRequest extends UpstreamMessage
|
||||
super.writeObject(out);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public function readObject (ins :ObjectInputStream)
|
||||
{
|
||||
trace("read PingRequest on the client?");
|
||||
}
|
||||
|
||||
/** A time stamp obtained when we serialize this object. */
|
||||
protected var _packStamp :Number;
|
||||
}
|
||||
|
||||
@@ -28,11 +28,6 @@ public class PongResponse extends DownstreamMessage
|
||||
return _unpackStamp;
|
||||
}
|
||||
|
||||
public function writeObject (out :ObjectOutputStream)
|
||||
{
|
||||
trace("write a pong on the client??");
|
||||
}
|
||||
|
||||
public function readObject (ins :ObjectInputStream)
|
||||
{
|
||||
_unpackStamp = new Date().getTime();
|
||||
|
||||
@@ -29,7 +29,8 @@ public class UpstreamMessage
|
||||
// documentation inherited from interface Streamable
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
messageId = ins.readShort();
|
||||
trace("This is client code: Upstream messages shouldn't be read");
|
||||
//messageId = ins.readShort();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.threerings.util {
|
||||
|
||||
import mx.collections.ArrayCollection;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
public class StreamableArrayList extends ArrayCollection
|
||||
implements Streamable
|
||||
{
|
||||
// documentation inherited from interface Streamable
|
||||
public function writeObject (out :ObjectOutputStream) :void
|
||||
{
|
||||
out.writeInt(source.length);
|
||||
for (var ii :int = 0; ii < source.length; ii++) {
|
||||
out.writeObject(source[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface Streamable
|
||||
public function readObject (ins :ObjectInputStream) :void
|
||||
{
|
||||
var ecount :int = ins.readInt();
|
||||
for (var ii :int = 0; ii < ecount; ii++) {
|
||||
source[ii] = ins.readObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user