From 1f010f0a4e6a9c9e2192455198e9263e0e2b8bda Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Sat, 13 Nov 2010 21:58:45 +0000 Subject: [PATCH] Add a C++ client for presents. It provides code generators for streamables, services and receivers, but not DObject. It uses Apple's CFNetwork for its sockets and builds with XCode, so it's limited to OS X and iOS. It should be straightforward to replace both to make it cross-platform. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6264 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- build.xml | 35 +- presents.xcodeproj/project.pbxproj | 537 ++++++++++++++++++ src/cpp/presents/ArrayMask.cpp | 39 ++ src/cpp/presents/ArrayMask.h | 18 + src/cpp/presents/ClassMapping.h | 12 + src/cpp/presents/DisconnectReason.h | 13 + src/cpp/presents/InvocationDecoder.cpp | 9 + src/cpp/presents/InvocationDecoder.h | 18 + src/cpp/presents/ObjectInputStream.cpp | 100 ++++ src/cpp/presents/ObjectInputStream.h | 49 ++ src/cpp/presents/ObjectOutputStream.cpp | 81 +++ src/cpp/presents/ObjectOutputStream.h | 89 +++ src/cpp/presents/PresentsClient.cpp | 252 ++++++++ src/cpp/presents/PresentsClient.h | 116 ++++ src/cpp/presents/PresentsError.h | 14 + src/cpp/presents/SocketStream.h | 18 + src/cpp/presents/Streamable.h | 15 + src/cpp/presents/Streamer.cpp | 98 ++++ src/cpp/presents/Streamer.h | 103 ++++ src/cpp/presents/Util.cpp | 18 + src/cpp/presents/Util.h | 37 ++ src/cpp/presents/box/BoxedBoolean.cpp | 19 + src/cpp/presents/box/BoxedBoolean.h | 24 + src/cpp/presents/box/BoxedByte.cpp | 19 + src/cpp/presents/box/BoxedByte.h | 24 + src/cpp/presents/box/BoxedDouble.cpp | 19 + src/cpp/presents/box/BoxedDouble.h | 24 + src/cpp/presents/box/BoxedFloat.cpp | 19 + src/cpp/presents/box/BoxedFloat.h | 24 + src/cpp/presents/box/BoxedInt.cpp | 19 + src/cpp/presents/box/BoxedInt.h | 24 + src/cpp/presents/box/BoxedShort.cpp | 19 + src/cpp/presents/box/BoxedShort.h | 24 + src/cpp/presents/box/boxer.py | 55 ++ src/cpp/presents/client/Registration.cpp | 18 + src/cpp/presents/client/Registration.h | 23 + src/cpp/presents/data/ClientObject.cpp | 22 + src/cpp/presents/data/ClientObject.h | 27 + .../presents/data/InvocationMarshaller.cpp | 18 + src/cpp/presents/data/InvocationMarshaller.h | 27 + src/cpp/presents/data/PermissionPolicy.cpp | 14 + src/cpp/presents/data/PermissionPolicy.h | 21 + .../presents/data/RegistrationMarshaller.cpp | 21 + .../presents/data/RegistrationMarshaller.h | 24 + src/cpp/presents/dobj/DEvent.cpp | 16 + src/cpp/presents/dobj/DEvent.h | 22 + src/cpp/presents/dobj/DObject.cpp | 16 + src/cpp/presents/dobj/DObject.h | 22 + src/cpp/presents/dobj/DSet.cpp | 21 + src/cpp/presents/dobj/DSet.h | 21 + src/cpp/presents/dobj/EntryAddedEvent.cpp | 18 + src/cpp/presents/dobj/EntryAddedEvent.h | 23 + src/cpp/presents/dobj/EntryRemovedEvent.cpp | 18 + src/cpp/presents/dobj/EntryRemovedEvent.h | 23 + .../dobj/InvocationNotificationEvent.cpp | 22 + .../dobj/InvocationNotificationEvent.h | 24 + .../presents/dobj/InvocationRequestEvent.cpp | 22 + .../presents/dobj/InvocationRequestEvent.h | 24 + src/cpp/presents/dobj/NamedEvent.cpp | 18 + src/cpp/presents/dobj/NamedEvent.h | 22 + src/cpp/presents/io/FramedInputStream.cpp | 63 ++ src/cpp/presents/io/FramedInputStream.h | 19 + src/cpp/presents/io/FramingOutputStream.cpp | 30 + src/cpp/presents/io/FramingOutputStream.h | 19 + src/cpp/presents/io/InputStream.h | 9 + src/cpp/presents/io/OutputStream.h | 9 + src/cpp/presents/net/AuthRequest.cpp | 24 + src/cpp/presents/net/AuthRequest.h | 26 + src/cpp/presents/net/AuthResponse.cpp | 18 + src/cpp/presents/net/AuthResponse.h | 23 + src/cpp/presents/net/AuthResponseData.cpp | 18 + src/cpp/presents/net/AuthResponseData.h | 22 + src/cpp/presents/net/BootstrapData.cpp | 21 + src/cpp/presents/net/BootstrapData.h | 25 + .../presents/net/BootstrapNotification.cpp | 18 + src/cpp/presents/net/BootstrapNotification.h | 23 + src/cpp/presents/net/Credentials.cpp | 14 + src/cpp/presents/net/Credentials.h | 21 + src/cpp/presents/net/DownstreamMessage.cpp | 16 + src/cpp/presents/net/DownstreamMessage.h | 22 + src/cpp/presents/net/EventNotification.cpp | 18 + src/cpp/presents/net/EventNotification.h | 23 + src/cpp/presents/net/ForwardEventRequest.cpp | 18 + src/cpp/presents/net/ForwardEventRequest.h | 23 + src/cpp/presents/net/ObjectResponse.cpp | 18 + src/cpp/presents/net/ObjectResponse.h | 23 + src/cpp/presents/net/PingRequest.cpp | 16 + src/cpp/presents/net/PingRequest.h | 21 + src/cpp/presents/net/PongResponse.cpp | 20 + src/cpp/presents/net/PongResponse.h | 23 + src/cpp/presents/net/UpstreamMessage.cpp | 16 + src/cpp/presents/net/UpstreamMessage.h | 22 + .../presents/net/UsernamePasswordCreds.cpp | 20 + src/cpp/presents/net/UsernamePasswordCreds.h | 24 + src/cpp/presents/platform/CFSocketStream.cpp | 187 ++++++ src/cpp/presents/platform/NSLog.mm | 9 + src/cpp/presents/stable.h | 144 +++++ .../presents/streamers/StreamableStreamer.h | 34 ++ src/cpp/presents/streamers/StringStreamer.cpp | 17 + src/cpp/presents/streamers/StringStreamer.h | 10 + src/cpp/presents/streamers/VectorStreamer.h | 223 ++++++++ src/cpp/util/Name.cpp | 16 + src/cpp/util/Name.h | 22 + .../presents/tools/cpp/CPPArgBuilder.java | 71 +++ .../presents/tools/cpp/CPPField.java | 28 + .../presents/tools/cpp/CPPType.java | 149 +++++ .../presents/tools/cpp/CPPUtil.java | 131 +++++ .../tools/cpp/GenCPPReceiverTask.java | 82 +++ .../presents/tools/cpp/GenCPPServiceTask.java | 73 +++ .../tools/cpp/GenCPPStreamableTask.java | 161 ++++++ .../presents/tools/cpp/MethodDescriptor.java | 32 ++ .../com/threerings/presents/tools.properties | 3 + .../presents/tools/cpp/decoder_cpp.mustache | 28 + .../presents/tools/cpp/decoder_h.mustache | 20 + .../tools/cpp/marshaller_cpp.mustache | 28 + .../presents/tools/cpp/marshaller_h.mustache | 30 + .../presents/tools/cpp/receiver_h.mustache | 21 + .../tools/cpp/streamable_cpp.mustache | 29 + .../presents/tools/cpp/streamable_h.mustache | 29 + 119 files changed, 4830 insertions(+), 3 deletions(-) create mode 100644 presents.xcodeproj/project.pbxproj create mode 100644 src/cpp/presents/ArrayMask.cpp create mode 100644 src/cpp/presents/ArrayMask.h create mode 100644 src/cpp/presents/ClassMapping.h create mode 100644 src/cpp/presents/DisconnectReason.h create mode 100644 src/cpp/presents/InvocationDecoder.cpp create mode 100644 src/cpp/presents/InvocationDecoder.h create mode 100644 src/cpp/presents/ObjectInputStream.cpp create mode 100644 src/cpp/presents/ObjectInputStream.h create mode 100644 src/cpp/presents/ObjectOutputStream.cpp create mode 100644 src/cpp/presents/ObjectOutputStream.h create mode 100644 src/cpp/presents/PresentsClient.cpp create mode 100644 src/cpp/presents/PresentsClient.h create mode 100644 src/cpp/presents/PresentsError.h create mode 100644 src/cpp/presents/SocketStream.h create mode 100644 src/cpp/presents/Streamable.h create mode 100644 src/cpp/presents/Streamer.cpp create mode 100644 src/cpp/presents/Streamer.h create mode 100644 src/cpp/presents/Util.cpp create mode 100644 src/cpp/presents/Util.h create mode 100644 src/cpp/presents/box/BoxedBoolean.cpp create mode 100644 src/cpp/presents/box/BoxedBoolean.h create mode 100644 src/cpp/presents/box/BoxedByte.cpp create mode 100644 src/cpp/presents/box/BoxedByte.h create mode 100644 src/cpp/presents/box/BoxedDouble.cpp create mode 100644 src/cpp/presents/box/BoxedDouble.h create mode 100644 src/cpp/presents/box/BoxedFloat.cpp create mode 100644 src/cpp/presents/box/BoxedFloat.h create mode 100644 src/cpp/presents/box/BoxedInt.cpp create mode 100644 src/cpp/presents/box/BoxedInt.h create mode 100644 src/cpp/presents/box/BoxedShort.cpp create mode 100644 src/cpp/presents/box/BoxedShort.h create mode 100644 src/cpp/presents/box/boxer.py create mode 100644 src/cpp/presents/client/Registration.cpp create mode 100644 src/cpp/presents/client/Registration.h create mode 100644 src/cpp/presents/data/ClientObject.cpp create mode 100644 src/cpp/presents/data/ClientObject.h create mode 100644 src/cpp/presents/data/InvocationMarshaller.cpp create mode 100644 src/cpp/presents/data/InvocationMarshaller.h create mode 100644 src/cpp/presents/data/PermissionPolicy.cpp create mode 100644 src/cpp/presents/data/PermissionPolicy.h create mode 100644 src/cpp/presents/data/RegistrationMarshaller.cpp create mode 100644 src/cpp/presents/data/RegistrationMarshaller.h create mode 100644 src/cpp/presents/dobj/DEvent.cpp create mode 100644 src/cpp/presents/dobj/DEvent.h create mode 100644 src/cpp/presents/dobj/DObject.cpp create mode 100644 src/cpp/presents/dobj/DObject.h create mode 100644 src/cpp/presents/dobj/DSet.cpp create mode 100644 src/cpp/presents/dobj/DSet.h create mode 100644 src/cpp/presents/dobj/EntryAddedEvent.cpp create mode 100644 src/cpp/presents/dobj/EntryAddedEvent.h create mode 100644 src/cpp/presents/dobj/EntryRemovedEvent.cpp create mode 100644 src/cpp/presents/dobj/EntryRemovedEvent.h create mode 100644 src/cpp/presents/dobj/InvocationNotificationEvent.cpp create mode 100644 src/cpp/presents/dobj/InvocationNotificationEvent.h create mode 100644 src/cpp/presents/dobj/InvocationRequestEvent.cpp create mode 100644 src/cpp/presents/dobj/InvocationRequestEvent.h create mode 100644 src/cpp/presents/dobj/NamedEvent.cpp create mode 100644 src/cpp/presents/dobj/NamedEvent.h create mode 100644 src/cpp/presents/io/FramedInputStream.cpp create mode 100644 src/cpp/presents/io/FramedInputStream.h create mode 100644 src/cpp/presents/io/FramingOutputStream.cpp create mode 100644 src/cpp/presents/io/FramingOutputStream.h create mode 100644 src/cpp/presents/io/InputStream.h create mode 100644 src/cpp/presents/io/OutputStream.h create mode 100644 src/cpp/presents/net/AuthRequest.cpp create mode 100644 src/cpp/presents/net/AuthRequest.h create mode 100644 src/cpp/presents/net/AuthResponse.cpp create mode 100644 src/cpp/presents/net/AuthResponse.h create mode 100644 src/cpp/presents/net/AuthResponseData.cpp create mode 100644 src/cpp/presents/net/AuthResponseData.h create mode 100644 src/cpp/presents/net/BootstrapData.cpp create mode 100644 src/cpp/presents/net/BootstrapData.h create mode 100644 src/cpp/presents/net/BootstrapNotification.cpp create mode 100644 src/cpp/presents/net/BootstrapNotification.h create mode 100644 src/cpp/presents/net/Credentials.cpp create mode 100644 src/cpp/presents/net/Credentials.h create mode 100644 src/cpp/presents/net/DownstreamMessage.cpp create mode 100644 src/cpp/presents/net/DownstreamMessage.h create mode 100644 src/cpp/presents/net/EventNotification.cpp create mode 100644 src/cpp/presents/net/EventNotification.h create mode 100644 src/cpp/presents/net/ForwardEventRequest.cpp create mode 100644 src/cpp/presents/net/ForwardEventRequest.h create mode 100644 src/cpp/presents/net/ObjectResponse.cpp create mode 100644 src/cpp/presents/net/ObjectResponse.h create mode 100644 src/cpp/presents/net/PingRequest.cpp create mode 100644 src/cpp/presents/net/PingRequest.h create mode 100644 src/cpp/presents/net/PongResponse.cpp create mode 100644 src/cpp/presents/net/PongResponse.h create mode 100644 src/cpp/presents/net/UpstreamMessage.cpp create mode 100644 src/cpp/presents/net/UpstreamMessage.h create mode 100644 src/cpp/presents/net/UsernamePasswordCreds.cpp create mode 100644 src/cpp/presents/net/UsernamePasswordCreds.h create mode 100644 src/cpp/presents/platform/CFSocketStream.cpp create mode 100644 src/cpp/presents/platform/NSLog.mm create mode 100644 src/cpp/presents/stable.h create mode 100644 src/cpp/presents/streamers/StreamableStreamer.h create mode 100644 src/cpp/presents/streamers/StringStreamer.cpp create mode 100644 src/cpp/presents/streamers/StringStreamer.h create mode 100644 src/cpp/presents/streamers/VectorStreamer.h create mode 100644 src/cpp/util/Name.cpp create mode 100644 src/cpp/util/Name.h create mode 100644 src/main/java/com/threerings/presents/tools/cpp/CPPArgBuilder.java create mode 100644 src/main/java/com/threerings/presents/tools/cpp/CPPField.java create mode 100644 src/main/java/com/threerings/presents/tools/cpp/CPPType.java create mode 100644 src/main/java/com/threerings/presents/tools/cpp/CPPUtil.java create mode 100644 src/main/java/com/threerings/presents/tools/cpp/GenCPPReceiverTask.java create mode 100644 src/main/java/com/threerings/presents/tools/cpp/GenCPPServiceTask.java create mode 100644 src/main/java/com/threerings/presents/tools/cpp/GenCPPStreamableTask.java create mode 100644 src/main/java/com/threerings/presents/tools/cpp/MethodDescriptor.java create mode 100644 src/main/resources/com/threerings/presents/tools/cpp/decoder_cpp.mustache create mode 100644 src/main/resources/com/threerings/presents/tools/cpp/decoder_h.mustache create mode 100644 src/main/resources/com/threerings/presents/tools/cpp/marshaller_cpp.mustache create mode 100644 src/main/resources/com/threerings/presents/tools/cpp/marshaller_h.mustache create mode 100644 src/main/resources/com/threerings/presents/tools/cpp/receiver_h.mustache create mode 100644 src/main/resources/com/threerings/presents/tools/cpp/streamable_cpp.mustache create mode 100644 src/main/resources/com/threerings/presents/tools/cpp/streamable_h.mustache diff --git a/build.xml b/build.xml index 741b669ff..5118b5b50 100644 --- a/build.xml +++ b/build.xml @@ -18,7 +18,7 @@ - @@ -36,6 +36,7 @@ + @@ -163,7 +164,7 @@ - @@ -241,7 +242,7 @@ - + @@ -252,6 +253,7 @@ + @@ -283,6 +285,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + DisconnectEvent; +} \ No newline at end of file diff --git a/src/cpp/presents/InvocationDecoder.cpp b/src/cpp/presents/InvocationDecoder.cpp new file mode 100644 index 000000000..1249b4005 --- /dev/null +++ b/src/cpp/presents/InvocationDecoder.cpp @@ -0,0 +1,9 @@ +#include "stable.h" +#include "InvocationDecoder.h" + +using namespace presents; + +InvocationDecoder::InvocationDecoder(utf8 rcode) : + receiverCode(rcode) +{ +} \ No newline at end of file diff --git a/src/cpp/presents/InvocationDecoder.h b/src/cpp/presents/InvocationDecoder.h new file mode 100644 index 000000000..0cea2122f --- /dev/null +++ b/src/cpp/presents/InvocationDecoder.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Streamable.h" + +namespace presents +{ + class InvocationDecoder { + public: + const utf8 receiverCode; + InvocationDecoder(utf8 receiverCode); + virtual void dispatchNotification(int8 methodId, const std::vector< Shared >& args) = 0; + + private: + // not implemented + InvocationDecoder (const InvocationDecoder&); + InvocationDecoder& operator= (const InvocationDecoder&); + }; +} \ No newline at end of file diff --git a/src/cpp/presents/ObjectInputStream.cpp b/src/cpp/presents/ObjectInputStream.cpp new file mode 100644 index 000000000..ed429bde9 --- /dev/null +++ b/src/cpp/presents/ObjectInputStream.cpp @@ -0,0 +1,100 @@ +#include "stable.h" + +#include + +#include "ObjectInputStream.h" +#include "PresentsError.h" +#include "presents/io/InputStream.h" + +#include "Streamer.h" + +#include "Util.h" +bool ObjectInputStream::readBoolean () +{ + return readByte() != 0; +} + +namespace +{ + template + T readInteger(presents::InputStream* in) + { + T value; + assert(in->read(&value, sizeof(T)) == sizeof(T)); + return presents::presentsToHost(value); + } +} + +int8 ObjectInputStream::readByte () +{ + return readInteger(_in); +} + +int16 ObjectInputStream::readShort () +{ + return readInteger(_in); +} + +int32 ObjectInputStream::readInt () +{ + return readInteger(_in); +} + +int64 ObjectInputStream::readLong () +{ + return readInteger(_in); +} + +void ObjectInputStream::readBytes (uint8* bytes, size_t length) +{ + _in->read(bytes, length); +} + +float ObjectInputStream::readFloat () +{ + float value; + readBytes((uint8*) &value, 4); + return value; +} + +double ObjectInputStream::readDouble () +{ + double value; + readBytes((uint8*) &value, 8); + return value; +} + +utf8 ObjectInputStream::readUTF () +{ + uint16 dataLength = readInteger(_in); + boost::scoped_array data(new uint8[dataLength]); + readBytes((uint8*)&data[0], dataLength); + return std::string((char*)&data[0], dataLength); +} + +Shared ObjectInputStream::readObject () +{ + int16 code = readShort(); + if (code == 0) { + return Shared(); + } + + Streamer* streamer; + if (code < 0) { + code *= -1; + + utf8 name = readUTF(); + + streamer = getStreamer(name); + ClassMapping cmap = { code, streamer }; + _classMap.insert(_classMap.begin() + code, cmap); + + } else { + streamer = (&_classMap[code])->streamer; + if (streamer == NULL) { + throw PresentsError((boost::format("Unknown streamer code '%1%'") % code).str()); + } + } + + return streamer->createObject(*this); +} \ No newline at end of file diff --git a/src/cpp/presents/ObjectInputStream.h b/src/cpp/presents/ObjectInputStream.h new file mode 100644 index 000000000..322507004 --- /dev/null +++ b/src/cpp/presents/ObjectInputStream.h @@ -0,0 +1,49 @@ +// +// $Id$ + +#pragma once + +#include "ClassMapping.h" +#include "Streamer.h" + +namespace presents { class InputStream; } + +using namespace presents; + +class ObjectInputStream +{ +public: + ObjectInputStream (presents::InputStream* stream) : _in(stream), _classMap(1) { } + + bool readBoolean (); + int8 readByte (); + int16 readShort (); + int32 readInt (); + int64 readLong (); + void readBytes (uint8* bytes, size_t length); + float readFloat (); + double readDouble (); + utf8 readUTF (); + + Shared readObject (); + + template + Shared readField () + { + return boost::static_pointer_cast(readField(getJavaName((const T*)NULL))); + } + + Shared readField (utf8 javaname) + { + if (readBoolean()) { + return getStreamer(javaname)->createObject(*this); + } else { + return Shared(); + } + } + +protected: + presents::InputStream* _in; + typedef std::vector ClassMap; + ClassMap _classMap; +}; diff --git a/src/cpp/presents/ObjectOutputStream.cpp b/src/cpp/presents/ObjectOutputStream.cpp new file mode 100644 index 000000000..10f58fae4 --- /dev/null +++ b/src/cpp/presents/ObjectOutputStream.cpp @@ -0,0 +1,81 @@ +#include "stable.h" + +#include "presents/io/OutputStream.h" +#include "ObjectOutputStream.h" + +#include "Streamable.h" +#include "Streamer.h" +#include "Util.h" + +#pragma warning (disable: 4100) // unreferenced parameter + +using namespace presents; + +namespace { +template +static void writeInteger(OutputStream* out, T value) +{ + value = hostToPresents(value); + out->write((uint8*)&value, sizeof(value)); +} +} + +void ObjectOutputStream::writeBoolean (bool value) +{ + writeInteger(_out, (uint8)(value ? 1 : 0)); +} + +void ObjectOutputStream::writeByte (int8 value) +{ + writeInteger(_out, value); +} + +void ObjectOutputStream::writeLong (int64 value) +{ + writeInteger(_out, value); +} + +void ObjectOutputStream::writeInt (int32 value) +{ + writeInteger(_out, value); +} + +void ObjectOutputStream::writeShort (int16 value) +{ + writeInteger(_out, value); +} + +void ObjectOutputStream::writeBytes (const uint8* bytes, size_t length) +{ + _out->write(bytes, length); +} + +void ObjectOutputStream::writeDouble (double value) +{ + _out->write((uint8*) &value, 8); +} + +void ObjectOutputStream::writeFloat (float value) +{ + _out->write((uint8*) &value, 4); +} + +void ObjectOutputStream::writeUTF (const utf8& value) +{ + size_t bytesToWrite = value.size() * sizeof(uint8); + writeShort(bytesToWrite); + _out->write((uint8*)value.data(), value.size()); +} + +void ObjectOutputStream::writeBareObject (const Shared& object, Streamer* streamer) +{ + streamer->writeObject(object, *this); +} + +void ObjectOutputStream::writeField (const Shared& object, const utf8& javaName) +{ + writeBoolean(object != NULL); + if (object != NULL) { + writeBareObject(object, getStreamer(javaName)); + } +} diff --git a/src/cpp/presents/ObjectOutputStream.h b/src/cpp/presents/ObjectOutputStream.h new file mode 100644 index 000000000..50d2da487 --- /dev/null +++ b/src/cpp/presents/ObjectOutputStream.h @@ -0,0 +1,89 @@ +// +// $Id$ + +#pragma once + +#include +#include +#include +#include + +#include "ClassMapping.h" +#include "PresentsError.h" +#include "Streamable.h" +#include "Streamer.h" + +//interface Streamable; +//interface Streamer; + +namespace presents { class OutputStream; } + +using namespace presents; + +class ObjectOutputStream +{ +public: + ObjectOutputStream (presents::OutputStream* stream) : _out(stream), _nextClassCode(1) { } + + void writeBoolean (bool value); + void writeByte (int8 value); + void writeShort (int16 value); + void writeInt (int32 value); + void writeLong (int64 value); + void writeBytes (const uint8* bytes, size_t length); + void writeDouble (double value); + void writeFloat (float value); + void writeUTF (const utf8& value); + + template + void writeObject (const Shared& object) + { + if (object.get() == NULL) { + writeShort(0); + return; + } + + const utf8 className = getObjectName(object.get()); + ClassMap::iterator it = _classMap.find(className); + Streamer* streamer; + + if (it == _classMap.end()) { + streamer = getStreamer(className); + if (streamer == NULL) { + throw PresentsError((boost::format("No streamer for '%1%'") % className).str()); + } + + ClassMapping cmap = { (int16)_nextClassCode++, streamer }; + assert(_nextClassCode < 32767); // Can't be more than Short.MAX_VALUE + _classMap[className] = cmap; + + writeShort(-cmap.code); + writeUTF(className); + + } else { + ClassMapping* cmap = &(it->second); + streamer = cmap->streamer; + writeShort(cmap->code); + } + + writeBareObject((Shared)object, streamer); + } + + template + void writeField (const Shared& object) + { + writeField(object, getJavaName((T*)NULL)); + } + + void writeField (const Shared& object, const utf8& javaName); + +protected: + void writeBareObject (const Shared& object, Streamer* streamer); + + presents::OutputStream* _out; + + typedef std::map ClassMap; + ClassMap _classMap; + + int _nextClassCode; +}; diff --git a/src/cpp/presents/PresentsClient.cpp b/src/cpp/presents/PresentsClient.cpp new file mode 100644 index 000000000..4c00e7bc6 --- /dev/null +++ b/src/cpp/presents/PresentsClient.cpp @@ -0,0 +1,252 @@ +// +// $Id$ + +#include "presents/stable.h" +#include "PresentsClient.h" + +#include "SocketStream.h" +#include "ObjectInputStream.h" +#include "ObjectOutputStream.h" + +#include "presents/box/BoxedBoolean.h" +#include "presents/box/BoxedByte.h" +#include "presents/box/BoxedDouble.h" +#include "presents/box/BoxedFloat.h" +#include "presents/box/BoxedInt.h" +#include "presents/box/BoxedShort.h" + +#include "presents/client/Registration.h" + +#include "presents/data/ClientObject.h" +#include "presents/data/InvocationMarshaller.h" +#include "presents/data/PermissionPolicy.h" +#include "presents/data/RegistrationMarshaller.h" + +#include "presents/dobj/DEvent.h" +#include "presents/dobj/DObject.h" +#include "presents/dobj/DSet.h" +#include "presents/dobj/EntryAddedEvent.h" +#include "presents/dobj/EntryRemovedEvent.h" +#include "presents/dobj/InvocationNotificationEvent.h" +#include "presents/dobj/InvocationRequestEvent.h" +#include "presents/dobj/NamedEvent.h" + +#include "presents/io/FramedInputStream.h" +#include "presents/io/FramingOutputStream.h" + +#include "presents/net/AuthRequest.h" +#include "presents/net/AuthResponse.h" +#include "presents/net/AuthResponseData.h" +#include "presents/net/BootstrapData.h" +#include "presents/net/BootstrapNotification.h" +#include "presents/net/Credentials.h" +#include "presents/net/DownstreamMessage.h" +#include "presents/net/EventNotification.h" +#include "presents/net/ForwardEventRequest.h" +#include "presents/net/ObjectResponse.h" +#include "presents/net/PingRequest.h" +#include "presents/net/PongResponse.h" +#include "presents/net/UpstreamMessage.h" +#include "presents/net/UsernamePasswordCreds.h" + +#include "util/Name.h" + +#include "presents/Util.h" + +using namespace presents; + +PresentsClient::PresentsClient () + : _outMessageId(0) + , _nextReceiverId(1) + , _oid(-1) + , _socket(NULL) + , _idleTime(0) + , _connected(false) + , _active(false) +{ + // ensure all Streamables are not stripped out of the library by clients + // (Some of these classes aren't directly referenced by PresentsClient) + box::BoxedBoolean::registerWithPresents(); + box::BoxedByte::registerWithPresents(); + box::BoxedDouble::registerWithPresents(); + box::BoxedFloat::registerWithPresents(); + box::BoxedInt::registerWithPresents(); + box::BoxedShort::registerWithPresents(); + + client::Registration::registerWithPresents(); + + data::ClientObject::registerWithPresents(); + data::InvocationMarshaller::registerWithPresents(); + data::PermissionPolicy::registerWithPresents(); + data::RegistrationMarshaller::registerWithPresents(); + + dobj::DEvent::registerWithPresents(); + dobj::DObject::registerWithPresents(); + dobj::DSet::registerWithPresents(); + dobj::EntryAddedEvent::registerWithPresents(); + dobj::EntryRemovedEvent::registerWithPresents(); + dobj::InvocationNotificationEvent::registerWithPresents(); + dobj::InvocationRequestEvent::registerWithPresents(); + dobj::NamedEvent::registerWithPresents(); + + net::AuthRequest::registerWithPresents(); + net::AuthResponse::registerWithPresents(); + net::AuthResponseData::registerWithPresents(); + net::BootstrapData::registerWithPresents(); + net::BootstrapNotification::registerWithPresents(); + net::Credentials::registerWithPresents(); + net::DownstreamMessage::registerWithPresents(); + net::EventNotification::registerWithPresents(); + net::ForwardEventRequest::registerWithPresents(); + net::ObjectResponse::registerWithPresents(); + net::PingRequest::registerWithPresents(); + net::PongResponse::registerWithPresents(); + net::UpstreamMessage::registerWithPresents(); + net::UsernamePasswordCreds::registerWithPresents(); +} + +PresentsClient::~PresentsClient () +{ +} + +bool PresentsClient::isConnected () const +{ + return _connected; +} + +void PresentsClient::connect (const std::string& host, int port, Shared creds, Shared version) +{ + assert(!_active); + assert(!_onSocketDisconnect.connected()); + _active = true; + _socket.reset(createSocketStream(host.c_str(), port)); + _onSocketDisconnect = _socket->onDisconnect(boost::bind(&PresentsClient::handleDisconnect, this, _1)); + _framedIn.reset(new FramedInputStream(_socket.get())); + _framingOut.reset(new FramingOutputStream(_socket.get())); + _in.reset(new ObjectInputStream(_framedIn.get())); + _out.reset(new ObjectOutputStream(_framingOut.get())); + + Shared req(new net::AuthRequest); + req->version = version; + req->zone = Shared(new utf8("")); + + req->creds = creds; + req->bootGroups = Shared< std::vector< Shared > >(new std::vector< Shared >); + req->bootGroups->push_back(Shared(new utf8("client"))); + PLOG("Connecting to %s:%d", host.c_str(), port); + postMessage(req); +} + +void PresentsClient::disconnect () +{ + if (_active) { + _socket->close(); + } +} + +void PresentsClient::update (float dt) +{ + if (!_active) { + return; + } + _socket->update(dt); + if (!_active) { + // Update can inform of uf a disconnect through onDisconnect, which shuts us down. + return; + } + while (_framedIn->readFrame()) { + Shared obj = boost::static_pointer_cast(_in->readObject()); + if (obj == NULL) { + PLOG("Got null message? What the dickens?"); + continue; + } + const utf8& msgName = obj->getJavaClassName(); + if (msgName == net::AuthResponse::javaName()) { + Shared resp = boost::static_pointer_cast(obj); + PLOG("Got auth response from corpseserver: %s", resp->data->code->c_str()); + _onAuth(*resp->data); + } else if (msgName == net::BootstrapNotification::javaName()) { + PLOG("Got bootstrap"); + Shared strap = boost::static_pointer_cast(obj); + _oid = strap->data->clientOid; + typedef std::vector< Shared > MarshVec; + Shared services(strap->data->services); + for (MarshVec::iterator iter = services->begin(); iter != services->end(); ++iter) { + _services.insert(MarshallerMap::value_type((*iter)->getJavaClassName(), *iter)); + } + _connected = true; + _onConnected(this); + } else if (msgName == net::EventNotification::javaName()) { + Shared event = boost::static_pointer_cast(obj)->event; + if (event->getJavaClassName() == dobj::InvocationNotificationEvent::javaName()) { + Shared notification = boost::static_pointer_cast(event); + DecoderMap::iterator it = _decoders.find(notification->receiverId); + it->second->dispatchNotification(notification->methodId, *notification->args); + } else { + PLOG("Ignoring event %s", event->getJavaClassName().c_str()); + } + } else { + PLOG("Ignoring message %s", msgName.c_str()); + } + if (!_active) { + // The client may decide to disconnect as a result of reading the message, in which case we need to stop reading. + return; + } + } + _idleTime += dt; + if (!_connected) { + if (_idleTime > 30) { + handleDisconnect(SERVER_UNREACHABLE); + } + } else if (_idleTime > 60) { + Shared ping(new net::PingRequest()); + postMessage(ping); + } +} + +void PresentsClient::postMessage (Shared message) +{ + if (!_active) { + PLOG("PresentsClient::postMessage called while not connected!"); + } else { + message->messageId = _outMessageId++; + _out->writeObject(message); + _framingOut->writeFrame(); + _idleTime = 0; + } +} + +void PresentsClient::sendRequest (int32 invOid, int32 invCode, int8 methId, const Shared< std::vector< Shared > >& args) +{ + Shared req(new dobj::InvocationRequestEvent); + req->toid = invOid; + req->invCode = invCode; + req->methodId = methId; + req->args = args; + Shared forward(new net::ForwardEventRequest); + forward->event = req; + postMessage(forward); +} + + +void PresentsClient::handleDisconnect (DisconnectReason reason) +{ + PLOG("PresentsClient::handleDisconnect"); + _active = false; + _socket.reset(); + _in.reset(); + _out.reset(); + _framedIn.reset(); + _framingOut.reset(); + _oid = -1; + _decoders.clear(); + _services.clear(); + _onSocketDisconnect.disconnect(); + _connected = false; + _onDisconnected(reason); +} + +Shared PresentsClient::getSharedThis() +{ + return shared_from_this(); +} diff --git a/src/cpp/presents/PresentsClient.h b/src/cpp/presents/PresentsClient.h new file mode 100644 index 000000000..421b8a591 --- /dev/null +++ b/src/cpp/presents/PresentsClient.h @@ -0,0 +1,116 @@ +// +// $Id$ + +#pragma once + +#include + +#include "presents/InvocationDecoder.h" +#include "presents/DisconnectReason.h" +#include "presents/PresentsError.h" +#include "presents/client/Registration.h" +#include "presents/data/RegistrationMarshaller.h" + +class ObjectInputStream; +class ObjectOutputStream; + +namespace presents +{ +namespace data { class InvocationMarshaller; } +namespace net { + class AuthResponseData; + class Credentials; + class UpstreamMessage; +} +class FramedInputStream; +class FramingOutputStream; +class SocketStream; + +class PresentsClient : public boost::enable_shared_from_this +{ +protected: + typedef std::map > MarshallerMap; + typedef std::map > DecoderMap; + +public: + typedef boost::signal ClientEvent; + typedef boost::signal AuthFailureEvent; + + PresentsClient (); + virtual ~PresentsClient (); + + void update (float dt); + + template + Shared requireService () + { + MarshallerMap::iterator it = _services.find(T::javaName()); + if (it->second == NULL) { + throw PresentsError((boost::format("No marshaller for %1%") % T::javaName()).str()); + } + return boost::static_pointer_cast(it->second); + } + + template + void registerReceiver (Shared receiver) + { + assert(_socket != NULL); + Shared reg(new presents::client::Registration); + reg->receiverId = _nextReceiverId++; + Shared dec(new typename T::Decoder(receiver)); + reg->receiverCode = Shared(new utf8(dec->receiverCode)); + _decoders.insert(DecoderMap::value_type(reg->receiverId, dec)); + requireService()->registerReceiver(getSharedThis(), reg); + } + + bool isConnected () const; + void connect (const std::string& hostname, int port, Shared creds, Shared version); + void disconnect (); + + void postMessage (Shared msg); + void sendRequest (int32 invOid, int32 invCode, int8 methodId, const Shared< std::vector< Shared > >& args); + int getOid() const { assert(_oid != -1); return _oid; } + + connection_t onConnected (const ClientEvent::slot_type& slot) { + return _onConnected.connect(slot); + } + + connection_t onDisconnected (const DisconnectEvent::slot_type& slot) { + return _onDisconnected.connect(slot); + } + + connection_t onAuth (const AuthFailureEvent::slot_type& slot) { + return _onAuth.connect(slot); + } + +protected: + void handleDisconnect (DisconnectReason); + Shared getSharedThis(); + + boost::scoped_ptr _socket; + boost::scoped_ptr _framingOut; + boost::scoped_ptr _framedIn; + boost::scoped_ptr _in; + boost::scoped_ptr _out; + int16 _outMessageId; + int16 _nextReceiverId; + int32 _oid; + DecoderMap _decoders; + MarshallerMap _services; + float _idleTime; + + // If the connection to the presents server is authenticated and ready to go. _onConnected is + // signaled when this becomes true + bool _connected; + + // If we're connected or are trying to connect. This is always true when _connected is true, + // but also before the connection is authed and before the bootstrap data arrives. + bool _active; + + ClientEvent _onConnected; + DisconnectEvent _onDisconnected; + AuthFailureEvent _onAuth; + + connection_t _onSocketDisconnect; +}; +} diff --git a/src/cpp/presents/PresentsError.h b/src/cpp/presents/PresentsError.h new file mode 100644 index 000000000..5fd8ec952 --- /dev/null +++ b/src/cpp/presents/PresentsError.h @@ -0,0 +1,14 @@ +#pragma once + +namespace presents +{ + class PresentsError : public std::runtime_error + { + public: + PresentsError(const std::string& message) : std::runtime_error(message) + {} + + virtual ~PresentsError() throw() + {} + }; +} \ No newline at end of file diff --git a/src/cpp/presents/SocketStream.h b/src/cpp/presents/SocketStream.h new file mode 100644 index 000000000..af6033143 --- /dev/null +++ b/src/cpp/presents/SocketStream.h @@ -0,0 +1,18 @@ +#pragma once + +#include "DisconnectReason.h" +#include "io/InputStream.h" +#include "io/OutputStream.h" + +namespace presents { +class SocketStream : public InputStream, public OutputStream +{ +public: + virtual ~SocketStream() {}; + virtual void close () = 0; + virtual void update (float dt) = 0; + virtual connection_t onDisconnect (const DisconnectEvent::slot_type& slot) = 0; +}; + +SocketStream* createSocketStream (const char* hostname, int port); +} \ No newline at end of file diff --git a/src/cpp/presents/Streamable.h b/src/cpp/presents/Streamable.h new file mode 100644 index 000000000..d0f69e0d2 --- /dev/null +++ b/src/cpp/presents/Streamable.h @@ -0,0 +1,15 @@ +// +// $Id$ + +#pragma once + +class ObjectOutputStream; +class ObjectInputStream; + +struct Streamable +{ + virtual void writeObject (ObjectOutputStream& out) const = 0; + virtual void readObject (ObjectInputStream& in) = 0; + virtual const utf8& getJavaClassName () const = 0; +}; + diff --git a/src/cpp/presents/Streamer.cpp b/src/cpp/presents/Streamer.cpp new file mode 100644 index 000000000..48d2e3241 --- /dev/null +++ b/src/cpp/presents/Streamer.cpp @@ -0,0 +1,98 @@ +#include "stable.h" +#include "Streamer.h" + +#include +#include +#include + +#include "ObjectInputStream.h" +#include "ObjectOutputStream.h" +#include "PresentsError.h" + +#include "streamers/StringStreamer.h" +#include "streamers/StreamableStreamer.h" +#include "streamers/VectorStreamer.h" + +#include "Util.h" + +class StreamerMap +{ +private: + typedef std::map Map; + +public: + void insert (const utf8& javaName, Streamer* streamer) + { + _map[javaName] = streamer; + } + + Streamer* get (const utf8& javaName) + { + Map::iterator it = _map.find(javaName); + if (it == _map.end()) { + throw PresentsError((boost::format("Unable to find Streamer for %1%") % javaName).str()); + } + return it->second; + } + + ~StreamerMap () + { + for (Map::iterator iter = _map.begin(); iter != _map.end(); ++iter) { + delete iter->second; + } + } + +private: + Map _map; +}; + +namespace +{ + template + void registerPrimitiveArrayStreamer (StreamerMap& map) + { + map.insert(getJavaName((const std::vector*)NULL), new VectorStreamer()); + } + + template + void registerObjectArrayStreamer (StreamerMap& map) + { + map.insert(getJavaName<>((std::vector< Shared >*)NULL), new VectorStreamer()); + } +} + +StreamerMap* getMap () +{ + static bool gInited = false; + static StreamerMap gMap; + + if (!gInited) { + gInited = true; + + gMap.insert(getJavaName((utf8*)NULL), new StringStreamer()); + gMap.insert(JAVA_LIST_NAME(), new VectorStreamer< Streamable >()); + + registerObjectArrayStreamer(gMap); + registerObjectArrayStreamer(gMap); + + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + } + + return &gMap; +} + +void registerStreamer (const utf8& javaName, Streamer* streamer) +{ + getMap()->insert(javaName, streamer); +} + +Streamer* getStreamer (const utf8& javaName) +{ + return getMap()->get(javaName); +} diff --git a/src/cpp/presents/Streamer.h b/src/cpp/presents/Streamer.h new file mode 100644 index 000000000..5eb5a3fa0 --- /dev/null +++ b/src/cpp/presents/Streamer.h @@ -0,0 +1,103 @@ +// +// $Id$ + +#pragma once + +#include +#include "Streamable.h" + +static inline utf8 JAVA_LIST_NAME () { return "java.util.ArrayList"; } + +class ObjectOutputStream; +class ObjectInputStream; + +struct Streamer +{ + virtual Shared createObject (ObjectInputStream& in) = 0; + virtual void writeObject (const Shared& object, ObjectOutputStream& out) = 0; +}; + +Streamer* getStreamer (const utf8& javaName); +void registerStreamer (const utf8& javaName, Streamer* streamer); + +inline const utf8 getJavaName (const utf8*) +{ + return "java.lang.String"; +} + +inline const utf8 getJavaName (const int8*) +{ + return "B"; +} + +inline const utf8 getJavaName (const int16*) +{ + return "S"; +} + +inline const utf8 getJavaName (const int32*) +{ + return "I"; +} + +inline const utf8 getJavaName (const int64*) +{ + return "J"; +} + +inline const utf8 getJavaName (const bool*) +{ + return "Z"; +} + +inline const utf8 getJavaName (const float*) +{ + return "F"; +} + +inline const utf8 getJavaName (const double*) +{ + return "D"; +} + +template +inline const utf8 getJavaName (const Shared*) +{ + return getJavaName((const T*)NULL); +} + +template +inline const utf8 getJavaName (const std::vector*) +{ + return "[" + getJavaName((const T*)NULL); +} + +template +inline const utf8 getJavaName (const std::vector< Shared >*) +{ + return "[L" + getJavaName((const T*)NULL) + ";"; +} + +template +inline const utf8 getJavaName (const T*) +{ + return T::javaName(); +} + +template <> +inline const utf8 getJavaName (const Streamable*) +{ + return "java.lang.Object"; +} + +template +inline const utf8 getObjectName (T* object) +{ + if (boost::is_base_of::value) { + // Look up the object's class virtually + return ((Streamable*)object)->getJavaClassName(); + } else { + // Everything else is assumed to be a final class + return getJavaName(object); + } +} diff --git a/src/cpp/presents/Util.cpp b/src/cpp/presents/Util.cpp new file mode 100644 index 000000000..185a5fd36 --- /dev/null +++ b/src/cpp/presents/Util.cpp @@ -0,0 +1,18 @@ +#include "Util.h" + +void presents::internal::log(const char* format, ...) +{ + va_list argumentList; + va_start(argumentList, format); + + char buffer[1024]; +#ifdef PRESENTS_COMPILER_MSVC + if (_vsnprintf(buffer, 1024, format, argumentList) > 0) { +#else + if (vsprintf(buffer, format, argumentList) > 0) { +#endif + presents::log(buffer); + } + + va_end(argumentList); +} \ No newline at end of file diff --git a/src/cpp/presents/Util.h b/src/cpp/presents/Util.h new file mode 100644 index 000000000..1883da2a1 --- /dev/null +++ b/src/cpp/presents/Util.h @@ -0,0 +1,37 @@ +#pragma once + +namespace presents { + void log (const char* message); + + namespace internal { + void log (const char* format, ...); + } + + template + inline T reverseBytes (T source) + { + uint8* pData = reinterpret_cast(&source); + std::reverse(pData, pData + sizeof(T)); + return source; + } + + template + T presentsToHost (T source) + { +#ifdef PRESENTS_HOST_LITTLE_ENDIAN + return reverseBytes(source); +#else + return source; +#endif + } + + template + T hostToPresents (T source) + { +#ifdef PRESENTS_HOST_LITTLE_ENDIAN + return reverseBytes(source); +#else + return source; +#endif + } +} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedBoolean.cpp b/src/cpp/presents/box/BoxedBoolean.cpp new file mode 100644 index 000000000..a1b72e926 --- /dev/null +++ b/src/cpp/presents/box/BoxedBoolean.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedBoolean.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Boolean", BoxedBoolean); + +void BoxedBoolean::readObject (ObjectInputStream& in) +{ + value = in.readBoolean(); +} + +void BoxedBoolean::writeObject (ObjectOutputStream& out) const +{ + out.writeBoolean(value); +} diff --git a/src/cpp/presents/box/BoxedBoolean.h b/src/cpp/presents/box/BoxedBoolean.h new file mode 100644 index 000000000..edc689f8a --- /dev/null +++ b/src/cpp/presents/box/BoxedBoolean.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedBoolean : public Streamable{ + public: + DECLARE_STREAMABLE(); + + bool value; + + static Shared createShared (bool value) + { + Shared shared(new BoxedBoolean); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedByte.cpp b/src/cpp/presents/box/BoxedByte.cpp new file mode 100644 index 000000000..ffd43060d --- /dev/null +++ b/src/cpp/presents/box/BoxedByte.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedByte.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Byte", BoxedByte); + +void BoxedByte::readObject (ObjectInputStream& in) +{ + value = in.readByte(); +} + +void BoxedByte::writeObject (ObjectOutputStream& out) const +{ + out.writeByte(value); +} diff --git a/src/cpp/presents/box/BoxedByte.h b/src/cpp/presents/box/BoxedByte.h new file mode 100644 index 000000000..53546a2f6 --- /dev/null +++ b/src/cpp/presents/box/BoxedByte.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedByte : public Streamable{ + public: + DECLARE_STREAMABLE(); + + int8 value; + + static Shared createShared (int8 value) + { + Shared shared(new BoxedByte); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedDouble.cpp b/src/cpp/presents/box/BoxedDouble.cpp new file mode 100644 index 000000000..0fa214ee4 --- /dev/null +++ b/src/cpp/presents/box/BoxedDouble.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedDouble.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Double", BoxedDouble); + +void BoxedDouble::readObject (ObjectInputStream& in) +{ + value = in.readDouble(); +} + +void BoxedDouble::writeObject (ObjectOutputStream& out) const +{ + out.writeDouble(value); +} diff --git a/src/cpp/presents/box/BoxedDouble.h b/src/cpp/presents/box/BoxedDouble.h new file mode 100644 index 000000000..98224a564 --- /dev/null +++ b/src/cpp/presents/box/BoxedDouble.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedDouble : public Streamable{ + public: + DECLARE_STREAMABLE(); + + double value; + + static Shared createShared (double value) + { + Shared shared(new BoxedDouble); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedFloat.cpp b/src/cpp/presents/box/BoxedFloat.cpp new file mode 100644 index 000000000..41e45979e --- /dev/null +++ b/src/cpp/presents/box/BoxedFloat.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedFloat.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Float", BoxedFloat); + +void BoxedFloat::readObject (ObjectInputStream& in) +{ + value = in.readFloat(); +} + +void BoxedFloat::writeObject (ObjectOutputStream& out) const +{ + out.writeFloat(value); +} diff --git a/src/cpp/presents/box/BoxedFloat.h b/src/cpp/presents/box/BoxedFloat.h new file mode 100644 index 000000000..c0aaa6222 --- /dev/null +++ b/src/cpp/presents/box/BoxedFloat.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedFloat : public Streamable{ + public: + DECLARE_STREAMABLE(); + + float value; + + static Shared createShared (float value) + { + Shared shared(new BoxedFloat); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedInt.cpp b/src/cpp/presents/box/BoxedInt.cpp new file mode 100644 index 000000000..8e9707a34 --- /dev/null +++ b/src/cpp/presents/box/BoxedInt.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedInt.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Integer", BoxedInt); + +void BoxedInt::readObject (ObjectInputStream& in) +{ + value = in.readInt(); +} + +void BoxedInt::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(value); +} diff --git a/src/cpp/presents/box/BoxedInt.h b/src/cpp/presents/box/BoxedInt.h new file mode 100644 index 000000000..7bbdd6933 --- /dev/null +++ b/src/cpp/presents/box/BoxedInt.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedInt : public Streamable{ + public: + DECLARE_STREAMABLE(); + + int32 value; + + static Shared createShared (int32 value) + { + Shared shared(new BoxedInt); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedShort.cpp b/src/cpp/presents/box/BoxedShort.cpp new file mode 100644 index 000000000..a63a44164 --- /dev/null +++ b/src/cpp/presents/box/BoxedShort.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedShort.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Short", BoxedShort); + +void BoxedShort::readObject (ObjectInputStream& in) +{ + value = in.readShort(); +} + +void BoxedShort::writeObject (ObjectOutputStream& out) const +{ + out.writeShort(value); +} diff --git a/src/cpp/presents/box/BoxedShort.h b/src/cpp/presents/box/BoxedShort.h new file mode 100644 index 000000000..b18c44c30 --- /dev/null +++ b/src/cpp/presents/box/BoxedShort.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedShort : public Streamable{ + public: + DECLARE_STREAMABLE(); + + int16 value; + + static Shared createShared (int16 value) + { + Shared shared(new BoxedShort); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/boxer.py b/src/cpp/presents/box/boxer.py new file mode 100644 index 000000000..0604dfd8e --- /dev/null +++ b/src/cpp/presents/box/boxer.py @@ -0,0 +1,55 @@ +primitives = [("Int", "int32", "Integer"), + ("Short", "int16", "Short"), + ("Byte", "int8", "Byte"), + ("Boolean", "bool", "Boolean"), + ("Double", "double", "Double"), + ("Float", "float", "Float")] +for interpreter, cpptype, javatype in primitives: + header = open("Boxed" + interpreter + ".h", 'w') + header.write('''#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class Boxed%(interpreter)s : public Streamable{ + public: + DECLARE_STREAMABLE(); + + %(cpptype)s value; + + static Shared createShared (%(cpptype)s value) + { + Shared shared(new Boxed%(interpreter)s); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}}''' % locals()) + header.close() + impl = open("Boxed" + interpreter + ".cpp", "w") + impl.write('''#include "presents/stable.h" +#include "Boxed%(interpreter)s.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.%(javatype)s", Boxed%(interpreter)s); + +void Boxed%(interpreter)s::readObject (ObjectInputStream& in) +{ + value = in.read%(interpreter)s(); +} + +void Boxed%(interpreter)s::writeObject (ObjectOutputStream& out) const +{ + out.write%(interpreter)s(value); +} +''' % locals()) + impl.close() diff --git a/src/cpp/presents/client/Registration.cpp b/src/cpp/presents/client/Registration.cpp new file mode 100644 index 000000000..072f9d214 --- /dev/null +++ b/src/cpp/presents/client/Registration.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "Registration.h" + +using namespace presents::client; + +DEFINE_STREAMABLE("com.threerings.presents.client.InvocationReceiver$Registration", Registration); + +void Registration::readObject (ObjectInputStream& in) +{ + receiverCode = in.readField< utf8 >(); + receiverId = int16(in.readShort()); +} + +void Registration::writeObject (ObjectOutputStream& out) const +{ + out.writeField(receiverCode); + out.writeShort(receiverId); +} diff --git a/src/cpp/presents/client/Registration.h b/src/cpp/presents/client/Registration.h new file mode 100644 index 000000000..b60c47a89 --- /dev/null +++ b/src/cpp/presents/client/Registration.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace client { + +class Registration : public Streamable { +public: + DECLARE_STREAMABLE(); + + Shared receiverCode; + int16 receiverId; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/data/ClientObject.cpp b/src/cpp/presents/data/ClientObject.cpp new file mode 100644 index 000000000..f083e5585 --- /dev/null +++ b/src/cpp/presents/data/ClientObject.cpp @@ -0,0 +1,22 @@ +#include "presents/stable.h" +#include "ClientObject.h" + +using namespace presents::data; + +DEFINE_STREAMABLE("com.threerings.presents.data.ClientObject", ClientObject); + +void ClientObject::readObject (ObjectInputStream& in) +{ + presents::dobj::DObject::readObject(in); + username = boost::static_pointer_cast(in.readObject()); + receivers = boost::static_pointer_cast(in.readObject()); + permPolicy = boost::static_pointer_cast(in.readObject()); +} + +void ClientObject::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DObject::writeObject(out); + out.writeObject(username); + out.writeObject(receivers); + out.writeObject(permPolicy); +} diff --git a/src/cpp/presents/data/ClientObject.h b/src/cpp/presents/data/ClientObject.h new file mode 100644 index 000000000..8448b193b --- /dev/null +++ b/src/cpp/presents/data/ClientObject.h @@ -0,0 +1,27 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DObject.h" +#include "util/Name.h" +#include "presents/data/PermissionPolicy.h" +#include "presents/dobj/DSet.h" + +namespace presents { namespace data { + +class ClientObject : public presents::dobj::DObject { +public: + DECLARE_STREAMABLE(); + + Shared username; + Shared receivers; + Shared permPolicy; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/data/InvocationMarshaller.cpp b/src/cpp/presents/data/InvocationMarshaller.cpp new file mode 100644 index 000000000..78a2342b6 --- /dev/null +++ b/src/cpp/presents/data/InvocationMarshaller.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "InvocationMarshaller.h" + +using namespace presents::data; + +DEFINE_STREAMABLE("com.threerings.presents.data.InvocationMarshaller", InvocationMarshaller); + +void InvocationMarshaller::readObject (ObjectInputStream& in) +{ + invOid = int32(in.readInt()); + invCode = int32(in.readInt()); +} + +void InvocationMarshaller::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(invOid); + out.writeInt(invCode); +} diff --git a/src/cpp/presents/data/InvocationMarshaller.h b/src/cpp/presents/data/InvocationMarshaller.h new file mode 100644 index 000000000..4c5c071f9 --- /dev/null +++ b/src/cpp/presents/data/InvocationMarshaller.h @@ -0,0 +1,27 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { + +class PresentsClient; + +namespace data { + +class InvocationMarshaller : public Streamable { +public: + DECLARE_STREAMABLE(); + + int32 invOid; + int32 invCode; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/data/PermissionPolicy.cpp b/src/cpp/presents/data/PermissionPolicy.cpp new file mode 100644 index 000000000..9583db76f --- /dev/null +++ b/src/cpp/presents/data/PermissionPolicy.cpp @@ -0,0 +1,14 @@ +#include "presents/stable.h" +#include "PermissionPolicy.h" + +using namespace presents::data; + +DEFINE_STREAMABLE("com.threerings.presents.data.PermissionPolicy", PermissionPolicy); + +void PermissionPolicy::readObject (ObjectInputStream& /*in*/) +{ +} + +void PermissionPolicy::writeObject (ObjectOutputStream& /*out*/) const +{ +} diff --git a/src/cpp/presents/data/PermissionPolicy.h b/src/cpp/presents/data/PermissionPolicy.h new file mode 100644 index 000000000..cf29ee0be --- /dev/null +++ b/src/cpp/presents/data/PermissionPolicy.h @@ -0,0 +1,21 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace data { + +class PermissionPolicy : public Streamable { +public: + DECLARE_STREAMABLE(); + + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/data/RegistrationMarshaller.cpp b/src/cpp/presents/data/RegistrationMarshaller.cpp new file mode 100644 index 000000000..21262e3e7 --- /dev/null +++ b/src/cpp/presents/data/RegistrationMarshaller.cpp @@ -0,0 +1,21 @@ +#include "presents/stable.h" +#include "RegistrationMarshaller.h" +#include "presents/PresentsClient.h" + +using namespace presents::data; + +DEFINE_STREAMABLE("com.threerings.presents.data.RegistrationMarshaller", RegistrationMarshaller); + +void RegistrationMarshaller::registerReceiver (Shared client, Shared arg1) +{ + typedef std::vector< Shared > StreamableList; + Shared args(new StreamableList); + args->push_back(arg1); + args->push_back(getSharedThis()); + client->sendRequest(invOid, invCode, 1, args); +} + +Shared RegistrationMarshaller::getSharedThis() +{ + return shared_from_this(); +} diff --git a/src/cpp/presents/data/RegistrationMarshaller.h b/src/cpp/presents/data/RegistrationMarshaller.h new file mode 100644 index 000000000..ae7cf856c --- /dev/null +++ b/src/cpp/presents/data/RegistrationMarshaller.h @@ -0,0 +1,24 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/data/InvocationMarshaller.h" +#include "presents/streamers/StreamableStreamer.h" +#include +#include "presents/client/Registration.h" + +namespace presents { namespace data { + +class RegistrationMarshaller : public presents::data::InvocationMarshaller, public boost::enable_shared_from_this { +public: + DECLARE_STREAMABLE(); + + virtual ~RegistrationMarshaller () {} + + void registerReceiver (Shared client, Shared arg1); +protected: + Shared getSharedThis(); +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/DEvent.cpp b/src/cpp/presents/dobj/DEvent.cpp new file mode 100644 index 000000000..7960e0f13 --- /dev/null +++ b/src/cpp/presents/dobj/DEvent.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "DEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.DEvent", DEvent); + +void DEvent::readObject (ObjectInputStream& in) +{ + toid = int32(in.readInt()); +} + +void DEvent::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(toid); +} diff --git a/src/cpp/presents/dobj/DEvent.h b/src/cpp/presents/dobj/DEvent.h new file mode 100644 index 000000000..465fd6246 --- /dev/null +++ b/src/cpp/presents/dobj/DEvent.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace dobj { + +class DEvent : public Streamable { +public: + DECLARE_STREAMABLE(); + + int32 toid; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/DObject.cpp b/src/cpp/presents/dobj/DObject.cpp new file mode 100644 index 000000000..3d77f7540 --- /dev/null +++ b/src/cpp/presents/dobj/DObject.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "DObject.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.DObject", DObject); + +void DObject::readObject (ObjectInputStream& in) +{ + oid = int32(in.readInt()); +} + +void DObject::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(oid); +} diff --git a/src/cpp/presents/dobj/DObject.h b/src/cpp/presents/dobj/DObject.h new file mode 100644 index 000000000..85b8500d9 --- /dev/null +++ b/src/cpp/presents/dobj/DObject.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace dobj { + +class DObject : public Streamable { +public: + DECLARE_STREAMABLE(); + + int32 oid; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/DSet.cpp b/src/cpp/presents/dobj/DSet.cpp new file mode 100644 index 000000000..417bfc6e6 --- /dev/null +++ b/src/cpp/presents/dobj/DSet.cpp @@ -0,0 +1,21 @@ +#include "presents/stable.h" +#include "DSet.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.DSet", DSet); + +void DSet::readObject (ObjectInputStream& in) +{ + size = in.readInt(); + entries = Shared > >(new std::vector< Shared >(size)); + for (int ii = 0; ii < size; ii++) { + (*entries)[ii] = boost::static_pointer_cast(in.readObject()); + } +} + +void DSet::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(size); + out.writeField(entries); +} diff --git a/src/cpp/presents/dobj/DSet.h b/src/cpp/presents/dobj/DSet.h new file mode 100644 index 000000000..7dd592d74 --- /dev/null +++ b/src/cpp/presents/dobj/DSet.h @@ -0,0 +1,21 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace dobj { + +class DSet : public Streamable { +public: + DECLARE_STREAMABLE(); + + Shared< std::vector< Shared > > entries; + int32 size; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/EntryAddedEvent.cpp b/src/cpp/presents/dobj/EntryAddedEvent.cpp new file mode 100644 index 000000000..738e9e659 --- /dev/null +++ b/src/cpp/presents/dobj/EntryAddedEvent.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "EntryAddedEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.EntryAddedEvent", EntryAddedEvent); + +void EntryAddedEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::NamedEvent::readObject(in); + entry = boost::static_pointer_cast(in.readObject()); +} + +void EntryAddedEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::NamedEvent::writeObject(out); + out.writeObject(entry); +} diff --git a/src/cpp/presents/dobj/EntryAddedEvent.h b/src/cpp/presents/dobj/EntryAddedEvent.h new file mode 100644 index 000000000..1442887f0 --- /dev/null +++ b/src/cpp/presents/dobj/EntryAddedEvent.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/NamedEvent.h" +#include "presents/Streamable.h" + +namespace presents { namespace dobj { + +class EntryAddedEvent : public presents::dobj::NamedEvent { +public: + DECLARE_STREAMABLE(); + + Shared entry; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/EntryRemovedEvent.cpp b/src/cpp/presents/dobj/EntryRemovedEvent.cpp new file mode 100644 index 000000000..f3f80527d --- /dev/null +++ b/src/cpp/presents/dobj/EntryRemovedEvent.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "EntryRemovedEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.EntryRemovedEvent", EntryRemovedEvent); + +void EntryRemovedEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::NamedEvent::readObject(in); + key = boost::static_pointer_cast(in.readObject()); +} + +void EntryRemovedEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::NamedEvent::writeObject(out); + out.writeObject(key); +} diff --git a/src/cpp/presents/dobj/EntryRemovedEvent.h b/src/cpp/presents/dobj/EntryRemovedEvent.h new file mode 100644 index 000000000..ec1795960 --- /dev/null +++ b/src/cpp/presents/dobj/EntryRemovedEvent.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/NamedEvent.h" +#include "presents/Streamable.h" + +namespace presents { namespace dobj { + +class EntryRemovedEvent : public presents::dobj::NamedEvent { +public: + DECLARE_STREAMABLE(); + + Shared key; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/InvocationNotificationEvent.cpp b/src/cpp/presents/dobj/InvocationNotificationEvent.cpp new file mode 100644 index 000000000..d50bd6c10 --- /dev/null +++ b/src/cpp/presents/dobj/InvocationNotificationEvent.cpp @@ -0,0 +1,22 @@ +#include "presents/stable.h" +#include "InvocationNotificationEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.InvocationNotificationEvent", InvocationNotificationEvent); + +void InvocationNotificationEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::DEvent::readObject(in); + receiverId = int16(in.readShort()); + methodId = int8(in.readByte()); + args = in.readField< std::vector< Shared > >(); +} + +void InvocationNotificationEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DEvent::writeObject(out); + out.writeShort(receiverId); + out.writeByte(methodId); + out.writeField(args); +} diff --git a/src/cpp/presents/dobj/InvocationNotificationEvent.h b/src/cpp/presents/dobj/InvocationNotificationEvent.h new file mode 100644 index 000000000..3018ba0e7 --- /dev/null +++ b/src/cpp/presents/dobj/InvocationNotificationEvent.h @@ -0,0 +1,24 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DEvent.h" + +namespace presents { namespace dobj { + +class InvocationNotificationEvent : public presents::dobj::DEvent { +public: + DECLARE_STREAMABLE(); + + int16 receiverId; + int8 methodId; + Shared< std::vector< Shared > > args; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/InvocationRequestEvent.cpp b/src/cpp/presents/dobj/InvocationRequestEvent.cpp new file mode 100644 index 000000000..e6fe2eaa5 --- /dev/null +++ b/src/cpp/presents/dobj/InvocationRequestEvent.cpp @@ -0,0 +1,22 @@ +#include "presents/stable.h" +#include "InvocationRequestEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.InvocationRequestEvent", InvocationRequestEvent); + +void InvocationRequestEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::DEvent::readObject(in); + invCode = int32(in.readInt()); + methodId = int8(in.readByte()); + args = in.readField< std::vector< Shared > >(); +} + +void InvocationRequestEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DEvent::writeObject(out); + out.writeInt(invCode); + out.writeByte(methodId); + out.writeField(args); +} diff --git a/src/cpp/presents/dobj/InvocationRequestEvent.h b/src/cpp/presents/dobj/InvocationRequestEvent.h new file mode 100644 index 000000000..4eee6c5ac --- /dev/null +++ b/src/cpp/presents/dobj/InvocationRequestEvent.h @@ -0,0 +1,24 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DEvent.h" + +namespace presents { namespace dobj { + +class InvocationRequestEvent : public presents::dobj::DEvent { +public: + DECLARE_STREAMABLE(); + + int32 invCode; + int8 methodId; + Shared< std::vector< Shared > > args; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/NamedEvent.cpp b/src/cpp/presents/dobj/NamedEvent.cpp new file mode 100644 index 000000000..e2c0f2d7f --- /dev/null +++ b/src/cpp/presents/dobj/NamedEvent.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "NamedEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.NamedEvent", NamedEvent); + +void NamedEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::DEvent::readObject(in); + name = in.readField< utf8 >(); +} + +void NamedEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DEvent::writeObject(out); + out.writeField(name); +} diff --git a/src/cpp/presents/dobj/NamedEvent.h b/src/cpp/presents/dobj/NamedEvent.h new file mode 100644 index 000000000..cb1018e65 --- /dev/null +++ b/src/cpp/presents/dobj/NamedEvent.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DEvent.h" + +namespace presents { namespace dobj { + +class NamedEvent : public presents::dobj::DEvent { +public: + DECLARE_STREAMABLE(); + + Shared name; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/io/FramedInputStream.cpp b/src/cpp/presents/io/FramedInputStream.cpp new file mode 100644 index 000000000..ee3e36d85 --- /dev/null +++ b/src/cpp/presents/io/FramedInputStream.cpp @@ -0,0 +1,63 @@ +#include "FramedInputStream.h" + +#include "presents/stable.h" + +#include "presents/PresentsError.h" +#include "presents/Util.h" +#include "FramedInputStream.h" +#include "InputStream.h" + +using namespace presents; + +static const int READ_BUFFER_LENGTH = 4096; + +FramedInputStream::FramedInputStream (InputStream* base) +: _base(base) +, _length(-1) +, _fillPosition(0) +, _readPosition(0) +, _readBufferLength(READ_BUFFER_LENGTH) +, _readBuffer(new uint8[READ_BUFFER_LENGTH]) +{ +} + +// Returns bytes read +size_t FramedInputStream::read (void* pData, size_t bytesToRead) +{ + if (_fillPosition != _length) { + throw PresentsError("Attempted to read with no frame!"); + } + if ((int32) (_readPosition + bytesToRead) > _length) { + throw PresentsError("Attempted to read past frame!"); + } + memcpy(pData, &_readBuffer[_readPosition], bytesToRead); + _readPosition += bytesToRead; + return bytesToRead; +} + +bool FramedInputStream::readFrame () +{ + if (_length == _fillPosition) { + if (_fillPosition != _readPosition) { + throw PresentsError("Attempted to read new frame without consuming old one"); + } + _length = -1; + _fillPosition = _readPosition = 0; + } + if (_length == -1) { + _fillPosition += _base->read(&_readBuffer[_fillPosition], 4 - _fillPosition); + if (_fillPosition == 4) { + _length = ((_readBuffer[0] << 24) | (_readBuffer[1] << 16) | (_readBuffer[2] << 8) | _readBuffer[3]) - 4; + if (_length > _readBufferLength) { + _readBufferLength *= 2; + _readBuffer.reset(new uint8[_readBufferLength]); + PLOG("Thas a mighty big frame. Musta been feedin' him corn. Growing buffer to %d", _readBufferLength); + } + _fillPosition = 0; + } else { + return false; + } + } + _fillPosition += _base->read(&_readBuffer[_fillPosition], _length - _fillPosition); + return _fillPosition == _length; +} diff --git a/src/cpp/presents/io/FramedInputStream.h b/src/cpp/presents/io/FramedInputStream.h new file mode 100644 index 000000000..0a18c33fa --- /dev/null +++ b/src/cpp/presents/io/FramedInputStream.h @@ -0,0 +1,19 @@ +#pragma once + +#include "InputStream.h" + +namespace presents { + +class FramedInputStream : public InputStream +{ +public: + FramedInputStream (InputStream* base); + virtual size_t read(void* pData, size_t bytesToRead); + bool readFrame (); +protected: + int32 _length; + InputStream* _base; + boost::scoped_array _readBuffer; + int32 _fillPosition, _readPosition, _readBufferLength; +}; +} \ No newline at end of file diff --git a/src/cpp/presents/io/FramingOutputStream.cpp b/src/cpp/presents/io/FramingOutputStream.cpp new file mode 100644 index 000000000..540436c1a --- /dev/null +++ b/src/cpp/presents/io/FramingOutputStream.cpp @@ -0,0 +1,30 @@ +#include "presents/stable.h" +#include "FramingOutputStream.h" + +#include "presents/Util.h" + +using namespace presents; + +FramingOutputStream::FramingOutputStream (OutputStream* base) + : _base(base) +{} + +// Returns bytes written +size_t FramingOutputStream::write(const uint8* pData, size_t bytesToWrite) +{ + uint8* bytes = (uint8*)pData; + for (int ii = 0; ii < bytesToWrite; ii++) { + _frameBuffer.push_back(bytes[ii]); + } + return bytesToWrite; +} + + +void FramingOutputStream::writeFrame() +{ + int32 length = hostToPresents((int32)(_frameBuffer.size() + sizeof(int32))); + _base->write((uint8*)&length, sizeof(int32)); + _base->write(&_frameBuffer[0], _frameBuffer.size()); + + _frameBuffer.clear(); +} \ No newline at end of file diff --git a/src/cpp/presents/io/FramingOutputStream.h b/src/cpp/presents/io/FramingOutputStream.h new file mode 100644 index 000000000..23de81887 --- /dev/null +++ b/src/cpp/presents/io/FramingOutputStream.h @@ -0,0 +1,19 @@ +#pragma once + +#include "OutputStream.h" + +namespace presents { + +class FramingOutputStream : public OutputStream { +public: + FramingOutputStream (OutputStream* base); + + // Returns bytes written + virtual size_t write (const uint8* pData, size_t bytesToWrite); + void writeFrame (); + +protected: + OutputStream* _base; + std::vector _frameBuffer; +}; +} \ No newline at end of file diff --git a/src/cpp/presents/io/InputStream.h b/src/cpp/presents/io/InputStream.h new file mode 100644 index 000000000..304812b8d --- /dev/null +++ b/src/cpp/presents/io/InputStream.h @@ -0,0 +1,9 @@ +#pragma once + +namespace presents { +class InputStream { +public: + virtual ~InputStream() {}; + virtual size_t read(void* pData, size_t bytesToRead) = 0; +}; +} diff --git a/src/cpp/presents/io/OutputStream.h b/src/cpp/presents/io/OutputStream.h new file mode 100644 index 000000000..9b8610f64 --- /dev/null +++ b/src/cpp/presents/io/OutputStream.h @@ -0,0 +1,9 @@ +#pragma once + +namespace presents { +class OutputStream { +public: + virtual ~OutputStream() {}; + virtual size_t write(const uint8* pData, size_t bytesToWrite) = 0; +}; +} diff --git a/src/cpp/presents/net/AuthRequest.cpp b/src/cpp/presents/net/AuthRequest.cpp new file mode 100644 index 000000000..ca15a4d7f --- /dev/null +++ b/src/cpp/presents/net/AuthRequest.cpp @@ -0,0 +1,24 @@ +#include "presents/stable.h" +#include "AuthRequest.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.AuthRequest", AuthRequest); + +void AuthRequest::readObject (ObjectInputStream& in) +{ + presents::net::UpstreamMessage::readObject(in); + creds = boost::static_pointer_cast(in.readObject()); + version = in.readField< utf8 >(); + zone = in.readField< utf8 >(); + bootGroups = boost::static_pointer_cast< std::vector< Shared > >(in.readObject()); +} + +void AuthRequest::writeObject (ObjectOutputStream& out) const +{ + presents::net::UpstreamMessage::writeObject(out); + out.writeObject(creds); + out.writeField(version); + out.writeField(zone); + out.writeObject(bootGroups); +} diff --git a/src/cpp/presents/net/AuthRequest.h b/src/cpp/presents/net/AuthRequest.h new file mode 100644 index 000000000..9d736f5d3 --- /dev/null +++ b/src/cpp/presents/net/AuthRequest.h @@ -0,0 +1,26 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/UpstreamMessage.h" +#include "presents/net/Credentials.h" + +namespace presents { namespace net { + +class AuthRequest : public presents::net::UpstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared creds; + Shared version; + Shared zone; + Shared< std::vector< Shared > > bootGroups; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/AuthResponse.cpp b/src/cpp/presents/net/AuthResponse.cpp new file mode 100644 index 000000000..c14d71e7a --- /dev/null +++ b/src/cpp/presents/net/AuthResponse.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "AuthResponse.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.AuthResponse", AuthResponse); + +void AuthResponse::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + data = boost::static_pointer_cast(in.readObject()); +} + +void AuthResponse::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeObject(data); +} diff --git a/src/cpp/presents/net/AuthResponse.h b/src/cpp/presents/net/AuthResponse.h new file mode 100644 index 000000000..a9d2301fd --- /dev/null +++ b/src/cpp/presents/net/AuthResponse.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/AuthResponseData.h" +#include "presents/net/DownstreamMessage.h" + +namespace presents { namespace net { + +class AuthResponse : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared data; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/AuthResponseData.cpp b/src/cpp/presents/net/AuthResponseData.cpp new file mode 100644 index 000000000..156395f79 --- /dev/null +++ b/src/cpp/presents/net/AuthResponseData.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "AuthResponseData.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.AuthResponseData", AuthResponseData); + +void AuthResponseData::readObject (ObjectInputStream& in) +{ + presents::dobj::DObject::readObject(in); + code = in.readField< utf8 >(); +} + +void AuthResponseData::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DObject::writeObject(out); + out.writeField(code); +} diff --git a/src/cpp/presents/net/AuthResponseData.h b/src/cpp/presents/net/AuthResponseData.h new file mode 100644 index 000000000..fdadbb80a --- /dev/null +++ b/src/cpp/presents/net/AuthResponseData.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DObject.h" + +namespace presents { namespace net { + +class AuthResponseData : public presents::dobj::DObject { +public: + DECLARE_STREAMABLE(); + + Shared code; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/BootstrapData.cpp b/src/cpp/presents/net/BootstrapData.cpp new file mode 100644 index 000000000..9b6d5e4ad --- /dev/null +++ b/src/cpp/presents/net/BootstrapData.cpp @@ -0,0 +1,21 @@ +#include "presents/stable.h" +#include "BootstrapData.h" +#include "presents/Streamer.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.BootstrapData", BootstrapData); + +void BootstrapData::readObject (ObjectInputStream& in) +{ + connectionId = int32(in.readInt()); + clientOid = int32(in.readInt()); + services = boost::static_pointer_cast< std::vector< Shared > >(in.readField(JAVA_LIST_NAME())); +} + +void BootstrapData::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(connectionId); + out.writeInt(clientOid); + out.writeField(services, JAVA_LIST_NAME()); +} diff --git a/src/cpp/presents/net/BootstrapData.h b/src/cpp/presents/net/BootstrapData.h new file mode 100644 index 000000000..c98b2e6e0 --- /dev/null +++ b/src/cpp/presents/net/BootstrapData.h @@ -0,0 +1,25 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/data/InvocationMarshaller.h" +#include "presents/Streamable.h" + +namespace presents { namespace net { + +class BootstrapData : public Streamable { +public: + DECLARE_STREAMABLE(); + + int32 connectionId; + int32 clientOid; + Shared< std::vector< Shared > > services; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/BootstrapNotification.cpp b/src/cpp/presents/net/BootstrapNotification.cpp new file mode 100644 index 000000000..3c7c09a3f --- /dev/null +++ b/src/cpp/presents/net/BootstrapNotification.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "BootstrapNotification.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.BootstrapNotification", BootstrapNotification); + +void BootstrapNotification::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + data = boost::static_pointer_cast(in.readObject()); +} + +void BootstrapNotification::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeObject(data); +} diff --git a/src/cpp/presents/net/BootstrapNotification.h b/src/cpp/presents/net/BootstrapNotification.h new file mode 100644 index 000000000..55c58695a --- /dev/null +++ b/src/cpp/presents/net/BootstrapNotification.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/BootstrapData.h" +#include "presents/net/DownstreamMessage.h" + +namespace presents { namespace net { + +class BootstrapNotification : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared data; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/Credentials.cpp b/src/cpp/presents/net/Credentials.cpp new file mode 100644 index 000000000..7d02a2357 --- /dev/null +++ b/src/cpp/presents/net/Credentials.cpp @@ -0,0 +1,14 @@ +#include "presents/stable.h" +#include "Credentials.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.Credentials", Credentials); + +void Credentials::readObject (ObjectInputStream& /*in*/) +{ +} + +void Credentials::writeObject (ObjectOutputStream& /*out*/) const +{ +} diff --git a/src/cpp/presents/net/Credentials.h b/src/cpp/presents/net/Credentials.h new file mode 100644 index 000000000..73ce50e39 --- /dev/null +++ b/src/cpp/presents/net/Credentials.h @@ -0,0 +1,21 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace net { + +class Credentials : public Streamable { +public: + DECLARE_STREAMABLE(); + + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/DownstreamMessage.cpp b/src/cpp/presents/net/DownstreamMessage.cpp new file mode 100644 index 000000000..08782e9ff --- /dev/null +++ b/src/cpp/presents/net/DownstreamMessage.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "DownstreamMessage.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.DownstreamMessage", DownstreamMessage); + +void DownstreamMessage::readObject (ObjectInputStream& in) +{ + messageId = int16(in.readShort()); +} + +void DownstreamMessage::writeObject (ObjectOutputStream& out) const +{ + out.writeShort(messageId); +} diff --git a/src/cpp/presents/net/DownstreamMessage.h b/src/cpp/presents/net/DownstreamMessage.h new file mode 100644 index 000000000..32cdea2fc --- /dev/null +++ b/src/cpp/presents/net/DownstreamMessage.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace net { + +class DownstreamMessage : public Streamable { +public: + DECLARE_STREAMABLE(); + + int16 messageId; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/EventNotification.cpp b/src/cpp/presents/net/EventNotification.cpp new file mode 100644 index 000000000..4458a0be1 --- /dev/null +++ b/src/cpp/presents/net/EventNotification.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "EventNotification.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.EventNotification", EventNotification); + +void EventNotification::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + event = boost::static_pointer_cast(in.readObject()); +} + +void EventNotification::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeObject(event); +} diff --git a/src/cpp/presents/net/EventNotification.h b/src/cpp/presents/net/EventNotification.h new file mode 100644 index 000000000..faddeb045 --- /dev/null +++ b/src/cpp/presents/net/EventNotification.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/DownstreamMessage.h" +#include "presents/dobj/DEvent.h" + +namespace presents { namespace net { + +class EventNotification : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared event; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/ForwardEventRequest.cpp b/src/cpp/presents/net/ForwardEventRequest.cpp new file mode 100644 index 000000000..5c76acfd2 --- /dev/null +++ b/src/cpp/presents/net/ForwardEventRequest.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "ForwardEventRequest.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.ForwardEventRequest", ForwardEventRequest); + +void ForwardEventRequest::readObject (ObjectInputStream& in) +{ + presents::net::UpstreamMessage::readObject(in); + event = boost::static_pointer_cast(in.readObject()); +} + +void ForwardEventRequest::writeObject (ObjectOutputStream& out) const +{ + presents::net::UpstreamMessage::writeObject(out); + out.writeObject(event); +} diff --git a/src/cpp/presents/net/ForwardEventRequest.h b/src/cpp/presents/net/ForwardEventRequest.h new file mode 100644 index 000000000..13d38b60d --- /dev/null +++ b/src/cpp/presents/net/ForwardEventRequest.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/UpstreamMessage.h" +#include "presents/dobj/DEvent.h" + +namespace presents { namespace net { + +class ForwardEventRequest : public presents::net::UpstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared event; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/ObjectResponse.cpp b/src/cpp/presents/net/ObjectResponse.cpp new file mode 100644 index 000000000..387b10911 --- /dev/null +++ b/src/cpp/presents/net/ObjectResponse.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "ObjectResponse.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.ObjectResponse", ObjectResponse); + +void ObjectResponse::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + dobj = boost::static_pointer_cast(in.readObject()); +} + +void ObjectResponse::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeObject(dobj); +} diff --git a/src/cpp/presents/net/ObjectResponse.h b/src/cpp/presents/net/ObjectResponse.h new file mode 100644 index 000000000..f2d611bea --- /dev/null +++ b/src/cpp/presents/net/ObjectResponse.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" +#include "presents/net/DownstreamMessage.h" + +namespace presents { namespace net { + +class ObjectResponse : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared dobj; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/PingRequest.cpp b/src/cpp/presents/net/PingRequest.cpp new file mode 100644 index 000000000..9a555b27c --- /dev/null +++ b/src/cpp/presents/net/PingRequest.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "PingRequest.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.PingRequest", PingRequest); + +void PingRequest::readObject (ObjectInputStream& in) +{ + presents::net::UpstreamMessage::readObject(in); +} + +void PingRequest::writeObject (ObjectOutputStream& out) const +{ + presents::net::UpstreamMessage::writeObject(out); +} diff --git a/src/cpp/presents/net/PingRequest.h b/src/cpp/presents/net/PingRequest.h new file mode 100644 index 000000000..89e4cc6eb --- /dev/null +++ b/src/cpp/presents/net/PingRequest.h @@ -0,0 +1,21 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/UpstreamMessage.h" + +namespace presents { namespace net { + +class PingRequest : public presents::net::UpstreamMessage { +public: + DECLARE_STREAMABLE(); + + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/PongResponse.cpp b/src/cpp/presents/net/PongResponse.cpp new file mode 100644 index 000000000..77b2da8d7 --- /dev/null +++ b/src/cpp/presents/net/PongResponse.cpp @@ -0,0 +1,20 @@ +#include "presents/stable.h" +#include "PongResponse.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.PongResponse", PongResponse); + +void PongResponse::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + packStamp = int64(in.readLong()); + processDelay = int32(in.readInt()); +} + +void PongResponse::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeLong(packStamp); + out.writeInt(processDelay); +} diff --git a/src/cpp/presents/net/PongResponse.h b/src/cpp/presents/net/PongResponse.h new file mode 100644 index 000000000..c06e7539e --- /dev/null +++ b/src/cpp/presents/net/PongResponse.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/DownstreamMessage.h" + +namespace presents { namespace net { + +class PongResponse : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + int64 packStamp; + int32 processDelay; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/UpstreamMessage.cpp b/src/cpp/presents/net/UpstreamMessage.cpp new file mode 100644 index 000000000..bb2d784ad --- /dev/null +++ b/src/cpp/presents/net/UpstreamMessage.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "UpstreamMessage.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.UpstreamMessage", UpstreamMessage); + +void UpstreamMessage::readObject (ObjectInputStream& in) +{ + messageId = int16(in.readShort()); +} + +void UpstreamMessage::writeObject (ObjectOutputStream& out) const +{ + out.writeShort(messageId); +} diff --git a/src/cpp/presents/net/UpstreamMessage.h b/src/cpp/presents/net/UpstreamMessage.h new file mode 100644 index 000000000..91eadf3b2 --- /dev/null +++ b/src/cpp/presents/net/UpstreamMessage.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace net { + +class UpstreamMessage : public Streamable { +public: + DECLARE_STREAMABLE(); + + int16 messageId; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/UsernamePasswordCreds.cpp b/src/cpp/presents/net/UsernamePasswordCreds.cpp new file mode 100644 index 000000000..8c31727b0 --- /dev/null +++ b/src/cpp/presents/net/UsernamePasswordCreds.cpp @@ -0,0 +1,20 @@ +#include "presents/stable.h" +#include "UsernamePasswordCreds.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.UsernamePasswordCreds", UsernamePasswordCreds); + +void UsernamePasswordCreds::readObject (ObjectInputStream& in) +{ + presents::net::Credentials::readObject(in); + username = boost::static_pointer_cast(in.readObject()); + password = in.readField< utf8 >(); +} + +void UsernamePasswordCreds::writeObject (ObjectOutputStream& out) const +{ + presents::net::Credentials::writeObject(out); + out.writeObject(username); + out.writeField(password); +} diff --git a/src/cpp/presents/net/UsernamePasswordCreds.h b/src/cpp/presents/net/UsernamePasswordCreds.h new file mode 100644 index 000000000..60898aa3a --- /dev/null +++ b/src/cpp/presents/net/UsernamePasswordCreds.h @@ -0,0 +1,24 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "util/Name.h" +#include "presents/net/Credentials.h" + +namespace presents { namespace net { + +class UsernamePasswordCreds : public presents::net::Credentials { +public: + DECLARE_STREAMABLE(); + + Shared username; + Shared password; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/platform/CFSocketStream.cpp b/src/cpp/presents/platform/CFSocketStream.cpp new file mode 100644 index 000000000..ddc501d80 --- /dev/null +++ b/src/cpp/presents/platform/CFSocketStream.cpp @@ -0,0 +1,187 @@ +// +// $Id$ + +#include "presents/stable.h" +#include "presents/SocketStream.h" +#include "presents/Util.h" +#include +#include + +namespace presents +{ + +class CFSocketStream : public SocketStream +{ +public: + CFSocketStream (const CFStringRef hostname, int port) + : _reader(NULL) + , _writer(NULL) + { + CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, hostname, port, &_reader, &_writer); + CFReadStreamOpen(_reader); + CFWriteStreamOpen(_writer); + } + + virtual ~CFSocketStream () + { + CloseWithReason(CLIENT_CLOSED, "Destroyed!"); + } + + // Returns bytes read + virtual size_t read (void* pData, size_t bytesToRead) + { + if (_reader == NULL) { + PLOG("Attempted to read from closed stream!"); + return 0; + } + switch (CFReadStreamGetStatus(_reader)) { + case kCFStreamStatusClosed: + CloseWithReason(SERVER_DISCONNECT, "Server closed our read connection"); + return 0; + case kCFStreamStatusError: + HandleStreamError(CFReadStreamCopyError(_reader)); + return 0; + } + if (CFReadStreamHasBytesAvailable(_reader)) { + int read = CFReadStreamRead(_reader, (UInt8*)pData, bytesToRead); + if (read < 0) { + PLOG("Got error while reading!"); + HandleStreamError(CFReadStreamCopyError(_reader)); + return 0; + } else if (read == 0 && CFReadStreamGetStatus(_reader) == kCFStreamStatusAtEnd) { + // Only disconnect due to end-of-stream if the stream thinks there should be data + CloseWithReason(SERVER_DISCONNECT, "Hit read stream end"); + } + return read; + } + return 0; + } + + // Returns bytes written + virtual size_t write (const uint8* pData, size_t bytesToWrite) + { + if (_writer == NULL) { + PLOG("Attempted to write to closed stream!"); + return 0; + } + uint8* bytes = (uint8*)pData; + for (int ii = 0; ii < bytesToWrite; ii++) { + _writerBuffer.push_back(bytes[ii]); + } + return bytesToWrite; + } + + virtual void update (float dt) + { + if (_writer == NULL) { + PLOG("Attempted to write to closed stream!"); + return; + } + switch (CFWriteStreamGetStatus(_writer)) { + case kCFStreamStatusClosed: + CloseWithReason(SERVER_DISCONNECT, "Server closed our write connection"); + return; + case kCFStreamStatusAtEnd: + CloseWithReason(SERVER_DISCONNECT, "Hit write stream end"); + return; + case kCFStreamStatusError: + HandleStreamError(CFWriteStreamCopyError(_writer)); + return; + } + while (!_writerBuffer.empty() && CFWriteStreamCanAcceptBytes(_writer)) { + int written = CFWriteStreamWrite(_writer, (UInt8*)&_writerBuffer[0], _writerBuffer.size()); + if (written < 0) { + PLOG("Hit error in write!"); + HandleStreamError(CFWriteStreamCopyError(_writer)); + } else { + _writerBuffer.erase(_writerBuffer.begin(), _writerBuffer.begin() + written); + } + } + } + + virtual void close () + { + CloseWithReason(CLIENT_CLOSED, "Client requested close"); + } + + virtual connection_t onDisconnect (const DisconnectEvent::slot_type& slot) + { + return _onDisconnected.connect(slot); + } + +protected: + void CloseWithReason (presents::DisconnectReason why, const std::string& detail) + { + if (_reader == NULL) { + return;// Multiple closes don't hurt nobody + } + PLOG("Closing stream: %s", detail.c_str()); + CFReadStreamClose(_reader); + CFRelease(_reader); + _reader = NULL; + CFWriteStreamClose(_writer); + CFRelease(_writer); + _writer = NULL; + PLOG("Signaling disconnected"); + _onDisconnected(why); + } + + void HandleStreamError (CFErrorRef error) + { + if (CFErrorGetDomain(error) == kCFErrorDomainPOSIX) { + switch (CFErrorGetCode(error)) { + case ENETDOWN: + CloseWithReason(NETWORK_DISCONNECT, "Net down!"); + break; + case ENETUNREACH: + CloseWithReason(NETWORK_DISCONNECT, "Net unreachable"); + break; + case ENETRESET: + CloseWithReason(NETWORK_DISCONNECT, "Net reset"); + break; + case ECONNRESET: + CloseWithReason(NETWORK_DISCONNECT, "Net connection reset"); + break; + case EHOSTDOWN: + CloseWithReason(SERVER_UNREACHABLE, "host down!"); + break; + case EHOSTUNREACH: + CloseWithReason(SERVER_UNREACHABLE, "host unreachable!"); + break; + case ECONNREFUSED: + CloseWithReason(SERVER_UNREACHABLE, "connection refused"); + break; + case EPIPE: + CloseWithReason(SERVER_DISCONNECT, "broken pipe"); + break; + case ENOTCONN: + CloseWithReason(SERVER_DISCONNECT, "not connected"); + break; + default: + PLOG("Unknown posix net error: %d", CFErrorGetCode(error)); + CloseWithReason(NETWORK_DISCONNECT, "Unknown failure!"); + break; + } + } else { + PLOG("Unknown CFStreamError domain: domain: %s error: %d", CFErrorGetDomain(error), CFErrorGetCode(error)); + CloseWithReason(UNKNOWN, "CFStream error"); + } + CFRelease(error); + } + + CFWriteStreamRef _writer; + std::vector _writerBuffer; + CFReadStreamRef _reader; + DisconnectEvent _onDisconnected; +}; + +SocketStream* createSocketStream (const char* hostname, int port) +{ + CFStringRef cfHostname = CFStringCreateWithCString(kCFAllocatorDefault, hostname, + kCFStringEncodingMacRoman); + CFSocketStream* stream = new CFSocketStream(cfHostname, port); + CFRelease(cfHostname); + return stream; +} + +} diff --git a/src/cpp/presents/platform/NSLog.mm b/src/cpp/presents/platform/NSLog.mm new file mode 100644 index 000000000..33820c365 --- /dev/null +++ b/src/cpp/presents/platform/NSLog.mm @@ -0,0 +1,9 @@ +#include "presents/Util.h" +#undef interface + +#include + +void presents::log(const char* msg) +{ + NSLog(@"%s", msg); +} diff --git a/src/cpp/presents/stable.h b/src/cpp/presents/stable.h new file mode 100644 index 000000000..f09456c06 --- /dev/null +++ b/src/cpp/presents/stable.h @@ -0,0 +1,144 @@ +// +// C Library Headers +// +#include +#include +#include +#include + +// +// Standard C++ Headers +// +#include +#include +#include +#include + +// +// Boost Headers +// +#pragma warning(push, 3) + +#define BOOST_ALL_NO_LIB +#define BOOST_REGEX_NO_LIB +#define BOOST_DATE_TIME_NO_LIB + +#include +#include +#include + +// +// boost::signals +#pragma warning(push) +#pragma warning(disable: 4512) // assignment operator could not be generated +#include +#pragma warning(pop) + +// typedefs +typedef boost::signals::connection connection_t; +typedef boost::signals::scoped_connection scoped_connection_t; + +// GCC +#if defined(__GNUC__) +# define PRESENTS_COMPILER_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + +// Visual Studio +#elif defined(_MSC_VER) +# define PRESENTS_COMPILER_MSVC _MSC_VER + +// Unknown Compiler +#else +# error The current compiler is not supported. +#endif + +#if defined(_WIN32) +# define PRESENTS_HOST_LITTLE_ENDIAN +typedef signed __int8 int8; +typedef unsigned __int8 uint8; +typedef signed __int16 int16; +typedef unsigned __int16 uint16; +typedef signed __int32 int32; +typedef unsigned __int32 uint32; +typedef signed __int64 int64; +typedef unsigned __int64 uint64; +typedef unsigned int uint; +typedef unsigned long ulong; + +#elif defined(__IPHONE__) +# define PRESENTS_IOS +#include +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +#define _INT32 + +typedef unsigned int uint; +typedef unsigned long ulong; + +#elif defined(__APPLE__) +# define PRESENTS_MACOSX +#include + +#ifndef __i386__ +typedef SInt64 int64; +typedef UInt64 uint64; +typedef SInt32 int32; +typedef SInt16 int16; +typedef SInt8 int8; +typedef UInt32 uint32; +typedef UInt16 uint16; +typedef UInt8 uint8; +#else +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +#define _INT32 +#endif + +typedef unsigned int uint; +typedef unsigned long ulong; + + +#elif defined(__linux__) +# define PRESENTS_HOST_LITTLE_ENDIAN +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; +typedef signed long long int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +typedef unsigned int uint; +typedef unsigned long ulong; + +// Unknown Platform +#else +# error The current platform is not supported. +#endif + +#if defined(PRESENTS_IOS) || defined(PRESENTS_MACOSX) +// Apple defines __BIG_ENDIAN__ and __LITTLE_ENDIAN__ +# if defined(__BIG_ENDIAN__) +# define PRESENTS_HOST_BIG_ENDIAN +# else +# define PRESENTS_HOST_LITTLE_ENDIAN +# endif +#endif + +#define PLOG presents::internal::log + +#define Shared boost::shared_ptr + +#define utf8 std::string \ No newline at end of file diff --git a/src/cpp/presents/streamers/StreamableStreamer.h b/src/cpp/presents/streamers/StreamableStreamer.h new file mode 100644 index 000000000..ed2664e94 --- /dev/null +++ b/src/cpp/presents/streamers/StreamableStreamer.h @@ -0,0 +1,34 @@ +#pragma once + +#include "presents/Streamer.h" +#include "presents/Streamable.h" + +template +class StreamableStreamer : public Streamer +{ +public: + Shared createObject (ObjectInputStream& in) + { + Shared streamable(new T()); + streamable->readObject(in); + return streamable; + } + + void writeObject (const Shared& object, ObjectOutputStream& out) + { + ((Shared&)object)->writeObject(out); + } +}; + +#define DECLARE_STREAMABLE() \ + static const utf8& javaName (); \ + static void registerWithPresents () { javaName(); } \ + virtual const utf8& getJavaClassName () const { return javaName(); } + +#define DEFINE_STREAMABLE(nameString, StreamableClass) \ + static const utf8& gStreamableClassRegistration = StreamableClass::javaName(); \ + const utf8& StreamableClass::javaName () {\ + static const utf8 JAVA_NAME = nameString; \ + static bool gRegistered = false; \ + if (!gRegistered) { registerStreamer(JAVA_NAME, new StreamableStreamer()); gRegistered = true; } \ + return JAVA_NAME; } diff --git a/src/cpp/presents/streamers/StringStreamer.cpp b/src/cpp/presents/streamers/StringStreamer.cpp new file mode 100644 index 000000000..15f08c247 --- /dev/null +++ b/src/cpp/presents/streamers/StringStreamer.cpp @@ -0,0 +1,17 @@ +#include "presents/stable.h" +#include "StringStreamer.h" + +#include + +#include "../ObjectInputStream.h" +#include "../ObjectOutputStream.h" + +Shared StringStreamer::createObject (ObjectInputStream& in) +{ + return Shared(new utf8(in.readUTF())); +} + +void StringStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + out.writeUTF(*(boost::static_pointer_cast(object))); +} diff --git a/src/cpp/presents/streamers/StringStreamer.h b/src/cpp/presents/streamers/StringStreamer.h new file mode 100644 index 000000000..abb07e429 --- /dev/null +++ b/src/cpp/presents/streamers/StringStreamer.h @@ -0,0 +1,10 @@ +#pragma once + +#include "../Streamer.h" + +class StringStreamer : public Streamer +{ +public: + Shared createObject (ObjectInputStream& in); + void writeObject (const Shared& object, ObjectOutputStream& out); +}; diff --git a/src/cpp/presents/streamers/VectorStreamer.h b/src/cpp/presents/streamers/VectorStreamer.h new file mode 100644 index 000000000..5b0720575 --- /dev/null +++ b/src/cpp/presents/streamers/VectorStreamer.h @@ -0,0 +1,223 @@ +#pragma once + +#include "../Streamer.h" +#include "presents/ArrayMask.h" + +template +class VectorStreamer : public Streamer +{ +public: + Shared createObject (ObjectInputStream& in); + void writeObject (const Shared& object, ObjectOutputStream& out); +}; + +template +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int length = in.readInt(); + Shared< std::vector< Shared > > v(new std::vector< Shared >(length)); + for (int ii = 0; ii < length; ++ii) { + Shared read = in.readObject(); + (*v)[ii] = boost::static_pointer_cast(read); + } + return v; +} + +template +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + Shared< std::vector< Shared > >& v = (Shared< std::vector< Shared > >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeObject((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readByte(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeByte((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readShort(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeShort((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readInt(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeInt((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readLong(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeLong((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readFloat(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeFloat((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readDouble(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeDouble((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readBoolean(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeBoolean((*v)[ii]); + } +} + +template <> +Shared VectorStreamer< utf8 >::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector< Shared > > v(new std::vector< Shared >(length)); + ArrayMask mask(in); + for (int ii = 0; ii < length; ++ii) { + if (mask.isSet(ii)) { + (*v)[ii] = Shared(new utf8(in.readUTF())); + } else { + (*v)[ii] = Shared(); + } + } + return v; +} + +template <> +void VectorStreamer< utf8 >::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector< Shared > >& v = (Shared< std::vector< Shared > >&)(object); + int length = v->size(); + out.writeInt(length); + ArrayMask mask(length); + for (int ii = 0; ii < length; ++ii) { + if ((*v)[ii] != NULL) { + mask.set(ii); + } + } + mask.writeTo(out); + for (int ii = 0; ii < length; ++ii) { + out.writeUTF(*((*v)[ii])); + } +} diff --git a/src/cpp/util/Name.cpp b/src/cpp/util/Name.cpp new file mode 100644 index 000000000..e19183ab8 --- /dev/null +++ b/src/cpp/util/Name.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "Name.h" + +using namespace util; + +DEFINE_STREAMABLE("com.threerings.util.Name", Name); + +void Name::readObject (ObjectInputStream& in) +{ + name = in.readField< utf8 >(); +} + +void Name::writeObject (ObjectOutputStream& out) const +{ + out.writeField(name); +} diff --git a/src/cpp/util/Name.h b/src/cpp/util/Name.h new file mode 100644 index 000000000..0eb55c179 --- /dev/null +++ b/src/cpp/util/Name.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace util { + +class Name : public Streamable { +public: + DECLARE_STREAMABLE(); + + Shared name; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +} \ No newline at end of file diff --git a/src/main/java/com/threerings/presents/tools/cpp/CPPArgBuilder.java b/src/main/java/com/threerings/presents/tools/cpp/CPPArgBuilder.java new file mode 100644 index 000000000..62f2ebaf9 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/CPPArgBuilder.java @@ -0,0 +1,71 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Type; + +import java.util.List; + +import com.google.common.collect.Lists; + +import com.threerings.presents.tools.InvocationTask.ServiceMethod; + +public class CPPArgBuilder +{ + public CPPArgBuilder(boolean ignoreFirst) + { + _ignoreFirst = ignoreFirst; + } + + public String getArguments (ServiceMethod meth) + { + return getArguments(meth, ""); + } + + public String getArguments (ServiceMethod meth, String prefix) + { + StringBuilder buf = new StringBuilder(prefix); + Type[] ptypes = meth.method.getGenericParameterTypes(); + for (int ii = _ignoreFirst ? 1 : 0; ii < ptypes.length; ii++) { + if (buf.length() > 0) { + buf.append(", "); + } + buf.append(CPPUtil.getCPPType(ptypes[ii])).append(" arg").append(ii); + } + return buf.toString(); + } + + public List getArgumentNames (ServiceMethod meth) + { + Type[] ptypes = meth.method.getGenericParameterTypes(); + List args = Lists.newArrayListWithCapacity(ptypes.length - 1); + for (int ii = 1; ii < ptypes.length; ii++) { + args.add("arg" + ii); + } + return args; + } + + public String getArgumentsFromVector (ServiceMethod meth) + { + StringBuilder buf = new StringBuilder(); + Type[] ptypes = meth.method.getGenericParameterTypes(); + for (int ii = _ignoreFirst ? 1 : 0; ii < ptypes.length; ii++) { + if (buf.length() > 0) { + buf.append(", "); + } + CPPType type = new CPPType(ptypes[ii]); + buf.append(type.getCastFromStreamable("args[" + ii + "]")); + } + return buf.toString(); + } + + public List getServiceArguments (ServiceMethod meth) + { + Type[] ptypes = meth.method.getGenericParameterTypes(); + List args = Lists.newArrayListWithCapacity(ptypes.length); + for (int ii = _ignoreFirst ? 1 : 0; ii < ptypes.length; ii++) { + args.add(new CPPType(ptypes[ii]).getAsStreamable("arg" + ii)); + } + return args; + } + + protected final boolean _ignoreFirst; +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/CPPField.java b/src/main/java/com/threerings/presents/tools/cpp/CPPField.java new file mode 100644 index 000000000..8975f00b1 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/CPPField.java @@ -0,0 +1,28 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Field; +import java.io.IOException; + +public class CPPField +{ + public final String name; + public final CPPType type; + public final String reader; + public final String writer; + + public CPPField (Field f) + throws IOException + { + type = new CPPType(f.getGenericType()); + name = f.getName().startsWith("_") ? f.getName().substring(1) : f.getName(); + if (type.fixed != null) { + reader = type.cast + "(in.read" + type.interpreter + "(" + type.fixed + "))"; + } else if (type.interpreter.equals("Field")) { + reader = "in.readField< " + type.getWithoutShared() + " >()"; + } else { + reader = type.cast + "(in.read" + type.interpreter + "())"; + } + writer = "out.write" + type.interpreter + "(" + name + + (type.fixed == null ? "" : ", " + type.fixed) + ")"; + } +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/CPPType.java b/src/main/java/com/threerings/presents/tools/cpp/CPPType.java new file mode 100644 index 000000000..2196b1682 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/CPPType.java @@ -0,0 +1,149 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.threerings.io.Streamable; +import com.threerings.presents.dobj.DSet; + +public class CPPType +{ + public static final String JAVA_LIST_FIXED = "JAVA_LIST_NAME()"; + + /** The full cpp type. */ + public final String type; + + /** The method on ObjectInputStream and ObjectOutputStream to interpret this type. */ + public final String interpreter; + + /** CPP code to cast from this object to its reader type. */ + public final String cast; + + /** If this is a fixed type and thereby doesn't need a type encoding on the wire. */ + public final String fixed; + + /** Another type embedded in this type, or null if it doesn't have an embedded type. */ + public final CPPType dependent; + + public final String representationImport; + + public final Class rawType; + + public final boolean primitive; + + public CPPType (Type javaType) + { + if (javaType instanceof ParameterizedType) { + rawType = (Class)((ParameterizedType)javaType).getRawType(); + } else if (javaType instanceof TypeVariable) { + rawType = (Class)((TypeVariable)javaType).getBounds()[0]; + } else { + rawType = (Class)javaType; + } + + if (rawType.equals(List.class)) { + fixed = JAVA_LIST_FIXED; + } else { + fixed = null; + } + + if (rawType.isArray()) { + Class componentType = rawType.getComponentType(); + dependent = new CPPType(componentType); + type = "Shared< std::vector< " + dependent.type + " > >"; + interpreter = componentType.equals(Object.class) || + componentType.isPrimitive() ? "Field" : "Object"; + representationImport = null; + + } else { + type = CPPUtil.getCPPType(javaType); + interpreter = getCPPInterpreter(rawType); + if (javaType instanceof ParameterizedType) { + Type[] typeArguments = ((ParameterizedType)javaType).getActualTypeArguments(); + if (rawType.equals(Comparable.class)) { + dependent = new CPPType(Streamable.class); + representationImport = "presents/Streamable.h"; + } else if (rawType == List.class) { + if (!Streamable.class.isAssignableFrom((Class)typeArguments[0])) { + throw new IllegalArgumentException( + "Lists may only contain Streamables in C++, not '" + typeArguments[0] + + "'"); + } + dependent = new CPPType(typeArguments[0]); + representationImport = null; + } else if (rawType.equals(DSet.class)) { + dependent = null; + representationImport = CPPUtil.makePath(rawType, ".h"); + } else { + throw new IllegalArgumentException("Don't know how to handle " + rawType); + } + } else if (javaType instanceof TypeVariable + || javaType.equals(com.threerings.presents.dobj.DSet.Entry.class)) { + dependent = new CPPType(Streamable.class); + representationImport = "presents/Streamable.h"; + } else { + if (!Streamable.class.equals(rawType) && Streamable.class.isAssignableFrom(rawType)) { + representationImport = CPPUtil.makePath(rawType, ".h"); + } else { + representationImport = null; + } + dependent = null; + } + } + + primitive = rawType.isPrimitive(); + Matcher m = TYPE_EXTRACT.matcher(type); + cast = m.matches() ? "boost::static_pointer_cast<" + m.group(1) + ">" : type; + } + + protected String getCPPInterpreter (Class ftype) + { + if (ftype.equals(String.class) || ftype.equals(List.class)) { + return "Field"; + } else if (ftype.equals(Boolean.TYPE)) { + return "Boolean"; + } else if (ftype.equals(Byte.TYPE)) { + return "Byte"; + } else if (ftype.equals(Short.TYPE)) { + return "Short"; + } else if (ftype.equals(Integer.TYPE)) { + return "Int"; + } else if (ftype.equals(Long.TYPE)) { + return "Long"; + } else if (ftype.equals(Float.TYPE)) { + return "Float"; + } else if (ftype.equals(Double.TYPE)) { + return "Double"; + } else { + return "Object"; + } + } + + public String getCastFromStreamable (String name) + { + if (primitive) { + return "boost::static_pointer_cast(" + name + ")->value"; + } + return cast + "(" + name + ")"; + } + + public String getAsStreamable (String name) + { + if (primitive) { + return "presents::box::Boxed" + interpreter + "::createShared(" + name + ")"; + } + return name; + } + + public String getWithoutShared () + { + Matcher m = TYPE_EXTRACT.matcher(type); + return m.matches() ? m.group(1) : type; + } + + protected static final Pattern TYPE_EXTRACT = Pattern.compile("Shared<(.*)>"); +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/CPPUtil.java b/src/main/java/com/threerings/presents/tools/cpp/CPPUtil.java new file mode 100644 index 000000000..83f8bbd06 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/CPPUtil.java @@ -0,0 +1,131 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import com.google.common.base.Charsets; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import com.google.common.io.Resources; +import com.samskivert.mustache.Mustache; + +import com.threerings.presents.dobj.DSet; + +public class CPPUtil +{ + public static String getCPPType (Type ftype) + { + if (ftype.equals(com.threerings.presents.dobj.DSet.Entry.class)) { + return "Shared"; + } + if (ftype instanceof ParameterizedType) { + Type[] typeArguments = ((ParameterizedType)ftype).getActualTypeArguments(); + Type raw = ((ParameterizedType)ftype).getRawType(); + if (raw.equals(Comparable.class)) { + return "Shared"; + } else if (raw.equals(List.class)) { + return "Shared< std::vector< " + getCPPType(typeArguments[0]) + " > >"; + } else if (raw.equals(DSet.class)) { + ftype = raw; + } else { + throw new IllegalArgumentException("Don't know how to handle " + raw); + } + } + + if (ftype.equals(Boolean.class) || ftype.equals(Byte.class) || ftype.equals(Short.class) + || ftype.equals(Integer.class) || ftype.equals(Long.class) + || ftype.equals(Float.class) || ftype.equals(Double.class)) { + throw new IllegalArgumentException("Presents can't handle boxed types in C++"); + } + if (ftype.equals(String.class)) { + return "Shared"; + } else if (ftype.equals(Boolean.TYPE)) { + return "bool"; + } else if (ftype.equals(Byte.TYPE)) { + return "int8"; + } else if (ftype.equals(Short.TYPE)) { + return "int16"; + } else if (ftype.equals(Integer.TYPE)) { + return "int32"; + } else if (ftype.equals(Long.TYPE)) { + return "int64"; + } else if (ftype.equals(Float.TYPE)) { + return "float"; + } else if (ftype.equals(Double.TYPE)) { + return "double"; + } else if (ftype.equals(Object.class) || ftype instanceof TypeVariable) { + return "Shared"; + } else { + return "Shared<" + makeCPPName((Class)ftype) + ">"; + } + } + + public static void writeTemplate (String tmplPath, File root, String path, + Map ctx) + throws IOException + { + String tmpl = + Resources.toString(Resources.getResource(tmplPath), Charsets.UTF_8); + File file = new File(root, path); + file.getParentFile().mkdirs(); + BufferedWriter out = new BufferedWriter(new FileWriter(file)); + Mustache.compiler().escapeHTML(false).compile(tmpl).execute(ctx, out); + out.close(); + } + + public static String makeCPPName (Class sclass) + { + return makeCPPName(makeNamespaces(sclass), sclass.getSimpleName()); + } + + public static String makeCPPName (List namespaces, String className) + { + return Joiner.on("::").join(namespaces) + "::" + className; + } + + public static String makePath (Class klass, String ext) + { + return makePath(makeNamespaces(klass), klass.getSimpleName(), ext); + } + + public static String makePath (List namespaces, String className, String ext) + { + return Joiner.on(File.separator).join(namespaces) + File.separator + className+ ext; + } + + public static List makeNamespaces (String pack) + { + Iterable split = Splitter.on(".").split(pack); + List segs = Lists.newArrayList(split); + if (segs.size() > 1 && segs.get(0).equals("com") && segs.get(1).equals("threerings")) { + segs.remove(0); + segs.remove(0); + } + return segs; + + } + + public static List makeNamespaces (Class sclass) + { + if (sclass.getPackage() == null) { + return Collections.emptyList(); + } else { + return makeNamespaces(sclass.getPackage().getName()); + } + } + + public static String makeNamespace (Class sclass) + { + return Joiner.on("::").join(makeNamespaces(sclass)); + } +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/GenCPPReceiverTask.java b/src/main/java/com/threerings/presents/tools/cpp/GenCPPReceiverTask.java new file mode 100644 index 000000000..c2b3e06f6 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/GenCPPReceiverTask.java @@ -0,0 +1,82 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Type; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import java.io.File; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import com.threerings.presents.tools.GenReceiverTask; + +public class GenCPPReceiverTask extends GenReceiverTask +{ + public void setCpproot (File asroot) + { + _cpproot = asroot; + } + + @Override + protected void generateDecoder (Class receiver, File source, String rname, String rpackage, + List methods, Iterator imports, String rcode) + throws Exception + { + String dname = rname.replace("Receiver", "Decoder"); + + Map ctx = Maps.newHashMap(); + List namespaces = CPPUtil.makeNamespaces(receiver); + ctx.put("receiverName", rname); + ctx.put("decoderName", dname); + ctx.put("namespaces", namespaces); + ctx.put("namespace", CPPUtil.makeNamespace(receiver)); + ctx.put("package", rpackage); + ctx.put("methods", MethodDescriptor.from(methods)); + ctx.put("receiverCode", rcode); + ctx.put("argbuilder", new CPPArgBuilder(false)); + + CPPUtil.writeTemplate(DECODER_HEADER_TMPL, _cpproot, + CPPUtil.makePath(namespaces, dname, ".h"), ctx); + + Set receiverHeaderIncludes = Sets.newHashSet(); + Set decoderImplIncludes = Sets.newHashSet(); + for (ServiceMethod meth : methods) { + for (Type type : meth.method.getGenericParameterTypes()) { + CPPType cppType = new CPPType(type); + if (cppType.primitive) { + decoderImplIncludes.add("presents/box/Boxed" + cppType.interpreter + ".h"); + } + while (cppType != null) { + if (cppType.representationImport != null) { + receiverHeaderIncludes.add(cppType.representationImport); + } + cppType = cppType.dependent; + } + } + } + + ctx.put("includes", decoderImplIncludes); + CPPUtil.writeTemplate(DECODER_CPP_TMPL, _cpproot, + CPPUtil.makePath(namespaces, dname, ".cpp"), ctx); + + + ctx.put("includes", receiverHeaderIncludes); + CPPUtil.writeTemplate(RECEIVER_HEADER_TMPL, _cpproot, + CPPUtil.makePath(namespaces, rname, ".h"), ctx); + + super.generateDecoder(receiver, source, rname, rpackage, methods, imports, rcode); + } + + protected File _cpproot; + + protected static final String RECEIVER_HEADER_TMPL = + "com/threerings/presents/tools/cpp/receiver_h.mustache"; + protected static final String DECODER_HEADER_TMPL = + "com/threerings/corpse/tools/decoder_h.mustache"; + protected static final String DECODER_CPP_TMPL = + "com/threerings/presents/tools/cpp/decoder_cpp.mustache"; +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/GenCPPServiceTask.java b/src/main/java/com/threerings/presents/tools/cpp/GenCPPServiceTask.java new file mode 100644 index 000000000..3f51701f0 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/GenCPPServiceTask.java @@ -0,0 +1,73 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Type; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.base.Joiner; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import com.threerings.presents.tools.GenServiceTask; + +import static com.threerings.presents.tools.cpp.CPPUtil.makeNamespaces; +import static com.threerings.presents.tools.cpp.CPPUtil.makePath; +import static com.threerings.presents.tools.cpp.CPPUtil.writeTemplate; + +public class GenCPPServiceTask extends GenServiceTask +{ + public void setCpproot (File asroot) + { + _cpproot = asroot; + } + + @Override + protected void generateMarshaller (File source, ServiceDescription sdesc) + throws Exception + { + String name = sdesc.sname.replace("Service", "Marshaller"); + String mpackage = sdesc.spackage.replace(".client", ".data"); + + List namespaces = makeNamespaces(mpackage); + Map ctx = Maps.newHashMap(); + ctx.put("name", name); + ctx.put("javaName", mpackage + "." + name); + ctx.put("namespaces", namespaces); + ctx.put("namespace", Joiner.on("::").join(namespaces)); + ctx.put("methods", MethodDescriptor.from(sdesc.methods)); + ctx.put("listeners", sdesc.listeners); + ctx.put("argbuilder", new CPPArgBuilder(true)); + + Set includes = Sets.newHashSet(); + Set implIncludes = Sets.newHashSet(); + for (ServiceMethod meth : sdesc.methods) { + for (Type type : meth.method.getGenericParameterTypes()) { + CPPType cppType = new CPPType(type); + if (cppType.primitive) { + implIncludes.add("presents/box/Boxed" + cppType.interpreter + ".h"); + } + while (cppType != null) { + if (cppType.representationImport != null) { + includes.add(cppType.representationImport); + } + cppType = cppType.dependent; + } + } + } + ctx.put("includes", implIncludes); + writeTemplate(CPP_TMPL, _cpproot, makePath(namespaces, name, ".cpp"), ctx); + ctx.put("includes", includes); + writeTemplate(HEADER_TMPL, _cpproot, makePath(namespaces, name, ".h"), ctx); + super.generateMarshaller(source, sdesc); + } + + protected File _cpproot; + + protected static final String HEADER_TMPL = + "com/threerings/presents/tools/cpp/marshaller_h.mustache"; + protected static final String CPP_TMPL = + "com/threerings/presents/tools/cpp/marshaller_cpp.mustache"; +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/GenCPPStreamableTask.java b/src/main/java/com/threerings/presents/tools/cpp/GenCPPStreamableTask.java new file mode 100644 index 000000000..5d67e6696 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/GenCPPStreamableTask.java @@ -0,0 +1,161 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import java.io.File; +import java.io.IOException; + +import org.apache.tools.ant.BuildException; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.threerings.io.SimpleStreamableObject; +import com.threerings.io.Streamable; + +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.dobj.DSet; +import com.threerings.presents.net.Message; +import com.threerings.presents.tools.GenTask; + +import static com.threerings.presents.tools.cpp.CPPUtil.makeCPPName; +import static com.threerings.presents.tools.cpp.CPPUtil.makeNamespaces; +import static com.threerings.presents.tools.cpp.CPPUtil.makePath; + +public class GenCPPStreamableTask extends GenTask +{ + public class Generate { + public void setClass (String name) + { + _toProcess.add(loadClass(name)); + } + } + + public Generate createGenerate () + { + return new Generate(); + } + + public void setCpproot (File asroot) + { + _cpproot = asroot; + } + + @Override + public void execute () + { + super.execute(); + for (Class klass : _toProcess) { + processClass(null, klass); + } + } + + @Override + protected void processClass (File fn, Class sclass) + { + try { + processClass(sclass); + } catch (IOException e) { + throw new BuildException(e); + } + } + + /** + * Processes a resolved Streamable class instance. + */ + protected void processClass (Class sclass) + throws IOException + { + if (!Streamable.class.isAssignableFrom(sclass) || + ((sclass.getModifiers() & Modifier.INTERFACE) != 0) || + DSet.class.equals(sclass) || + (InvocationMarshaller.class.isAssignableFrom(sclass) && !InvocationMarshaller.class.equals(sclass))) { +// System.err.println("Skipping " + sclass.getName() + "..."); + return; + } + + System.err.println("Generating " + sclass.getName()); + + // see if our parent also implements Streamable + boolean needSuper = Streamable.class.isAssignableFrom(sclass.getSuperclass()) && + !NONSUPER.contains(sclass.getSuperclass()); + + Map ctx = Maps.newHashMap(); + ctx.put("superclassStreamable", needSuper); + ctx.put("name", sclass.getSimpleName()); + ctx.put("namespaces", makeNamespaces(sclass)); + ctx.put("javaName", sclass.getName()); + ctx.put("namespace", CPPUtil.makeNamespace(sclass)); + + Set headerIncludes = Sets.newHashSet(); + Set implIncludes = Sets.newHashSet(); + if (needSuper) { + ctx.put("super", makeCPPName(sclass.getSuperclass())); + addInclude(sclass.getSuperclass(), headerIncludes); + } else { + ctx.put("super", "Streamable"); + headerIncludes.add("presents/Streamable.h"); + } + + List fields = Lists.newArrayList(); + ctx.put("fields", fields); + for (Field field : sclass.getDeclaredFields()) { + int mods = field.getModifiers(); + if (!Modifier.isStatic(mods) && !Modifier.isTransient(mods)) { + CPPField cppField = new CPPField(field); + fields.add(cppField); + CPPType type = cppField.type; + while (type != null) { + if (CPPType.JAVA_LIST_FIXED.equals(type.fixed)) { + implIncludes.add("presents/Streamer.h"); + } + if (type.representationImport != null) { + headerIncludes.add(type.representationImport); + } + type = type.dependent; + } + } + } + String inStream, outStream; + if (fields.isEmpty() && !needSuper) { + inStream = "/*in*/"; + outStream = "/*out*/"; + } else { + inStream = "in"; + outStream = "out"; + + } + ctx.put("inStreamArg", inStream); + ctx.put("outStreamArg", outStream); + + // now write all that out to the target source file + ctx.put("includes", headerIncludes); + CPPUtil.writeTemplate(HEADER_TMPL, _cpproot, makePath(sclass, ".h"), ctx); + + ctx.put("includes", implIncludes); + CPPUtil.writeTemplate(CPP_TMPL, _cpproot, makePath(sclass, ".cpp"), ctx); + } + + protected static void addInclude (Class ftype, Set includes) + { + includes.add(makePath(ftype, ".h")); + } + + protected File _cpproot; + + protected Set> _toProcess = Sets.newHashSet(); + + protected static final Set> NONSUPER = Sets.newHashSet(); + static { + NONSUPER.add(Message.class); + NONSUPER.add(SimpleStreamableObject.class); + } + protected static final String HEADER_TMPL = + "com/threerings/presents/tools/cpp/streamable_h.mustache"; + protected static final String CPP_TMPL = + "com/threerings/presents/tools/cpp/streamable_cpp.mustache"; +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/MethodDescriptor.java b/src/main/java/com/threerings/presents/tools/cpp/MethodDescriptor.java new file mode 100644 index 000000000..be83ee6bc --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/MethodDescriptor.java @@ -0,0 +1,32 @@ +package com.threerings.presents.tools.cpp; + +import java.util.List; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; + +import com.threerings.presents.tools.InvocationTask.ServiceMethod; + +public class MethodDescriptor +{ + public static List from(List methods) { + return Lists.transform(methods, new Function() { + @Override public MethodDescriptor apply (ServiceMethod from) { + return new MethodDescriptor(from); + }}); + } + + public final String methodName; + public final String vectorArguments; + public final String arguments; + public final String clientArguments; + public final List serviceArguments; + + public MethodDescriptor(ServiceMethod methodSource) { + methodName = methodSource.method.getName(); + vectorArguments = new CPPArgBuilder(false).getArgumentsFromVector(methodSource); + arguments = new CPPArgBuilder(false).getArguments(methodSource); + clientArguments = new CPPArgBuilder(true).getArguments(methodSource, "Shared client"); + serviceArguments = new CPPArgBuilder(true).getServiceArguments(methodSource); + } +} \ No newline at end of file diff --git a/src/main/resources/com/threerings/presents/tools.properties b/src/main/resources/com/threerings/presents/tools.properties index 6c63c9a68..acb1864ce 100644 --- a/src/main/resources/com/threerings/presents/tools.properties +++ b/src/main/resources/com/threerings/presents/tools.properties @@ -3,3 +3,6 @@ genservice=com.threerings.presents.tools.GenServiceTask genreceiver=com.threerings.presents.tools.GenReceiverTask instream=com.threerings.presents.tools.InstrumentStreamableTask genascript=com.threerings.presents.tools.GenActionScriptTask +gencppservice=com.threerings.presents.tools.cpp.GenCPPServiceTask +gencppstreamable=com.threerings.presents.tools.cpp.GenCPPStreamableTask +gencppreceiver=com.threerings.presents.tools.cpp.GenCPPReceiverTask diff --git a/src/main/resources/com/threerings/presents/tools/cpp/decoder_cpp.mustache b/src/main/resources/com/threerings/presents/tools/cpp/decoder_cpp.mustache new file mode 100644 index 000000000..534bdd7b1 --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/decoder_cpp.mustache @@ -0,0 +1,28 @@ +#include "presents/stable.h" +#include "{{decoderName}}.h" +#include "{{receiverName}}.h" +{{#includes}} +#include "{{this}}" +{{/includes}} + +using namespace {{namespace}}; + +{{decoderName}}::{{decoderName}} (Shared<{{receiverName}}> receiver) : + InvocationDecoder("{{receiverCode}}"), + _receiver(receiver) +{ +} + +void {{decoderName}}::dispatchNotification(int8 methodId, const std::vector< Shared >& args) +{ + switch(methodId) { +{{#methods}} + case {{-index}}: + _receiver->{{methodName}} ( {{vectorArguments}} ); + return; +{{/methods}} + default: + LOG_INFO("{{decoderName}} got unknown method: %d", methodId); + return; + } +} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/decoder_h.mustache b/src/main/resources/com/threerings/presents/tools/cpp/decoder_h.mustache new file mode 100644 index 000000000..b11b07f9b --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/decoder_h.mustache @@ -0,0 +1,20 @@ +#pragma once + +#include "presents/InvocationDecoder.h" + +{{#namespaces}} +namespace {{this}} { {{/namespaces}} + +class {{receiverName}}; + +class {{decoderName}} : public presents::InvocationDecoder { +public: + {{decoderName}}(Shared<{{receiverName}}> receiver); + void dispatchNotification(int8 methodId, const std::vector< Shared >& args); + +protected: + Shared<{{receiverName}}> _receiver; +}; + +{{#namespaces}} +}{{/namespaces}} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/marshaller_cpp.mustache b/src/main/resources/com/threerings/presents/tools/cpp/marshaller_cpp.mustache new file mode 100644 index 000000000..3cf2ef08e --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/marshaller_cpp.mustache @@ -0,0 +1,28 @@ +#include "presents/stable.h" +#include "{{name}}.h" +#include "presents/PresentsClient.h" +{{#includes}} +#include "{{this}}" +{{/includes}} + +using namespace {{namespace}}; + +DEFINE_STREAMABLE("{{javaName}}", {{name}}); + +{{#methods}} +void {{name}}::{{methodName}} ({{clientArguments}}) +{ + typedef std::vector< Shared > StreamableList; + Shared args(new StreamableList); +{{#serviceArguments}} + args->push_back({{this}}); +{{/serviceArguments}} + args->push_back(getSharedThis()); + client->sendRequest(invOid, invCode, {{-index}}, args); +} + +{{/methods}} +Shared<{{name}}> {{name}}::getSharedThis() +{ + return shared_from_this(); +} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/marshaller_h.mustache b/src/main/resources/com/threerings/presents/tools/cpp/marshaller_h.mustache new file mode 100644 index 000000000..453452661 --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/marshaller_h.mustache @@ -0,0 +1,30 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/data/InvocationMarshaller.h" +#include "presents/streamers/StreamableStreamer.h" +#include +{{#includes}} +#include "{{this}}" +{{/includes}} + +{{#namespaces}}namespace {{this}} { {{/namespaces}} + + +class {{name}} : public presents::data::InvocationMarshaller, public boost::enable_shared_from_this<{{name}}> { +public: + DECLARE_STREAMABLE(); + + virtual ~{{name}} () {} + +{{#methods}} + void {{methodName}} ({{clientArguments}}); +{{/methods}} +protected: + Shared<{{name}}> getSharedThis(); +}; + +{{#namespaces}} +}{{/namespaces}} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/receiver_h.mustache b/src/main/resources/com/threerings/presents/tools/cpp/receiver_h.mustache new file mode 100644 index 000000000..4f5d3f4e0 --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/receiver_h.mustache @@ -0,0 +1,21 @@ +#pragma once + +#include "{{decoderName}}.h" +{{#includes}} +#include "{{this}}" +{{/includes}} + +{{#namespaces}}namespace {{this}} { {{/namespaces}} + + +class {{receiverName}} { +public: + typedef {{decoderName}} Decoder; + +{{#methods}} + virtual void {{methodName}} ({{arguments}}) = 0; +{{/methods}} +}; + +{{#namespaces}} +}{{/namespaces}} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/streamable_cpp.mustache b/src/main/resources/com/threerings/presents/tools/cpp/streamable_cpp.mustache new file mode 100644 index 000000000..fed50e4ae --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/streamable_cpp.mustache @@ -0,0 +1,29 @@ +#include "presents/stable.h" +#include "{{name}}.h" +{{#includes}} +#include "{{this}}" +{{/includes}} + +using namespace {{namespace}}; + +DEFINE_STREAMABLE("{{javaName}}", {{name}}); + +void {{name}}::readObject (ObjectInputStream& {{inStreamArg}}) +{ +{{#superclassStreamable}} + {{super}}::readObject(in); +{{/superclassStreamable}} +{{#fields}} + {{name}} = {{reader}}; +{{/fields}} +} + +void {{name}}::writeObject (ObjectOutputStream& {{outStreamArg}}) const +{ +{{#superclassStreamable}} + {{super}}::writeObject(out); +{{/superclassStreamable}} +{{#fields}} + {{writer}}; +{{/fields}} +} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/streamable_h.mustache b/src/main/resources/com/threerings/presents/tools/cpp/streamable_h.mustache new file mode 100644 index 000000000..3ddac4ad5 --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/streamable_h.mustache @@ -0,0 +1,29 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +{{#includes}} +#include "{{this}}" +{{/includes}} + +{{#namespaces}} +namespace {{this}} { {{/namespaces}} + + +class {{name}} : public {{super}} { +public: + DECLARE_STREAMABLE(); + +{{#fields}} + {{type.type}} {{name}}; +{{/fields}} + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +{{#namespaces}} +}{{/namespaces}}