Files
watrbx-game-engine/Network/Streaming.cpp
T
2025-09-18 17:55:52 -04:00

1615 lines
39 KiB
C++

/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */
#include "streaming.h"
#include "stringcompressor.h"
#include "Util/BinaryString.h"
#include "Util/BrickColor.h"
#include "Util/UDim.h"
#include "Util/Faces.h"
#include "Util/Axes.h"
#include "Util/Quaternion.h"
#include "Util/SystemAddress.h"
#include "GuidRegistryService.h"
#include "Util/Math.h"
#include "Util/NormalId.h"
#include "Reflection/Event.h"
#include "Reflection/EnumConverter.h"
#include "util/StreamRegion.h"
#include <boost/algorithm/string.hpp>
#include "Replicator.h"
#include "util/VarInt.h"
#include "util/PhysicalProperties.h"
#include "v8datamodel/NumberSequence.h"
#include "v8datamodel/NumberRange.h"
#include "v8datamodel/ColorSequence.h"
//#define LOSSY_QUAT
// with this defined we lose precision when compressing quaternions for streaming.
// This makes places load incorrectly in multiplayer.
DYNAMIC_FASTINTVARIABLE(PhysicsCompressionSizeFilter, 50)
SYNCHRONIZED_FASTFLAGVARIABLE(NetworkAlignBinaryString, true) // 223
SYNCHRONIZED_FASTFLAGVARIABLE(NetworkDisableStringCompression, false)
#define MAX_STRING_SIZE 200000
namespace RBX {
using namespace Reflection;
namespace Network {
void serializeEnumIndex(const Reflection::EnumDescriptor* enumDesc, const size_t& index, RakNet::BitStream &bitStream, size_t enumSizeMSB/*default to 0*/)
{
RBXASSERT(index < enumDesc->getEnumCount());
if (enumSizeMSB == 0)
{
enumSizeMSB = enumDesc->getEnumCountMSB();
}
bitStream.WriteBits((const unsigned char*) &index, enumSizeMSB+1);
}
void deserializeEnumIndex(const Reflection::EnumDescriptor* enumDesc, size_t& index, RakNet::BitStream &bitStream, size_t enumSizeMSB/*default to 0*/)
{
if (enumSizeMSB == 0)
{
enumSizeMSB = enumDesc->getEnumCountMSB();
}
readFastN( bitStream, index, enumSizeMSB+1 );
if (index >= enumDesc->getEnumCount())
{
// overflowed value, set to default
// This could happen on an outdated client connecting to the latest server where some new values are added to an enum
StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "Enum value overflow on %s, size %d, index %d. Set to 0. (Are you using an outdated client?)", enumDesc->name.c_str(), (int)enumDesc->getEnumCount(), (int)index);
index = 0;
}
}
void serializeEnum(const Reflection::EnumDescriptor* enumDesc , const Reflection::Variant& value, RakNet::BitStream &bitStream, size_t enumSizeMSB/*default to 0*/)
{
const EnumDescriptor::Item* item = enumDesc->lookup(value);
RBXASSERT(item);
const size_t valueIndex = item->index;
serializeEnumIndex(enumDesc, valueIndex, bitStream, enumSizeMSB);
}
void deserializeEnum(const Reflection::EnumDescriptor* enumDesc, Reflection::Variant& result, RakNet::BitStream &bitStream, size_t enumSizeMSB/*default to 0*/)
{
size_t index = 0;
deserializeEnumIndex(enumDesc, index, bitStream, enumSizeMSB);
if(!enumDesc->convertToValue(index,result))
throw RBX::network_stream_exception("deserializeEnum conversion failed");
}
void serializeEnumProperty(const ConstProperty& property, RakNet::BitStream &bitStream, size_t enumSizeMSB/*default to 0*/)
{
const EnumPropertyDescriptor& enumDesc = static_cast<const EnumPropertyDescriptor&>(property.getDescriptor());
const size_t value = enumDesc.getIndexValue(property.getInstance());
serializeEnumIndex(&enumDesc.enumDescriptor, value, bitStream, enumSizeMSB);
}
void deserializeEnumProperty(Property& property, RakNet::BitStream &bitStream, size_t enumSizeMSB/*default to 0*/)
{
const EnumPropertyDescriptor& enumDesc = static_cast<const EnumPropertyDescriptor&>(property.getDescriptor());
size_t index = 0;
deserializeEnumIndex(&enumDesc.enumDescriptor, index, bitStream, enumSizeMSB);
if (property.getInstance())
enumDesc.setIndexValue(property.getInstance(), index);
}
void serializeStringCompressed(const std::string& value, RakNet::BitStream& stream)
{
uint32_t size = static_cast<uint32_t>(value.size());
if (size > MAX_STRING_SIZE)
throw RBX::network_stream_exception(RBX::format("BitStream string write: String too long: %u", size));
stream.Write(size);
RakNet::StringCompressor::Instance()->EncodeString(value.c_str(), static_cast<int>(size+1), &stream);
}
void deserializeStringCompressed(std::string& value, RakNet::BitStream& stream)
{
uint32_t size;
Network::readFastT( stream, size );
if (size>MAX_STRING_SIZE)
throw RBX::network_stream_exception(RBX::format("BitStream >> std::string: Bad string length: %d, bit pos: %d", (int)size, stream.GetReadOffset()));
char* buffer = (char*)alloca(size+1);
RakNet::StringCompressor::Instance()->DecodeString(buffer, static_cast<int>(size+1), &stream);
value = buffer;
}
} // namespace Network
RakNet::BitStream& operator << (RakNet::BitStream& stream, const RBX::Guid::Scope& value)
{
Network::serializeGuidScope( stream, value, false );
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, RBX::Guid::Scope& value)
{
Network::deserializeGuidScope( stream, value, false );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, int value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, int& value)
{
Network::readFastT( stream, value );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, unsigned int value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, unsigned int& value)
{
Network::readFastT( stream, value );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, unsigned long long value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, unsigned long long& value)
{
Network::readFastT( stream, value );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, char value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, char& value)
{
Network::readFastT( stream, value );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, signed char value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, signed char& value)
{
Network::readFastT( stream, value );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, unsigned char value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, unsigned char& value)
{
Network::readFastT( stream, value );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, short value)
{
stream.Write(value);
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, unsigned short value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, short& value)
{
Network::readFastT( stream, value );
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, unsigned short& value)
{
Network::readFastT( stream, value );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, bool value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, bool& value)
{
Network::readFastT( stream, value );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, float value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, float& value)
{
Network::readFastT( stream, value );
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, double value)
{
stream.Write(value);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, double& value)
{
Network::readFastT( stream, value );
return stream;
}
// This one is expensive in terms of CPU
// It uses huffman coding based on empirical alphabet frequency
// So please avoid using it unless the string worth the compression
// TODO: refactor to explicit function to avoid abusing
RakNet::BitStream& operator << (RakNet::BitStream& stream, const std::string& value)
{
uint32_t size = static_cast<uint32_t>(value.size());
if (size > MAX_STRING_SIZE)
throw RBX::network_stream_exception(RBX::format("BitStream string write: String too long: %u", size));
stream.Write(size);
if (SFFlag::getNetworkDisableStringCompression())
{
stream.Write(value.c_str(), value.size());
}
else
{
RakNet::StringCompressor::Instance()->EncodeString(value.c_str(), static_cast<int>(size+1), &stream);
}
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, std::string& value)
{
uint32_t size;
Network::readFastT( stream, size );
if (size>MAX_STRING_SIZE)
throw RBX::network_stream_exception(RBX::format("BitStream >> std::string: Bad string length: %d, bit pos: %d", (int)size, stream.GetReadOffset()));
if (SFFlag::getNetworkDisableStringCompression())
{
value.resize(size);
if (size)
stream.Read(&value[0], size);
}
else
{
char* buffer = (char*)alloca(size+1);
RakNet::StringCompressor::Instance()->DecodeString(buffer, static_cast<int>(size+1), &stream);
value = buffer;
}
return stream;
}
#define MAX_BINARY_STRING_SIZE 512000
RakNet::BitStream& operator << (RakNet::BitStream& stream, const BinaryString& value)
{
uint32_t size = static_cast<uint32_t>(value.value().size());
if (size > MAX_BINARY_STRING_SIZE)
throw RBX::network_stream_exception(RBX::format("BitStream string write: BinaryString too long: %u", size));
stream.AlignWriteToByteBoundary();
stream.Write(size);
stream.Write(value.value().c_str(), static_cast<int>(size));
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, BinaryString& value)
{
stream.AlignReadToByteBoundary();
uint32_t size;
if (!stream.Read(size))
throw RBX::network_stream_exception("BitStream >> BinaryString: failed to read length");
if (size > MAX_BINARY_STRING_SIZE)
throw RBX::network_stream_exception("BitStream >> BinaryString: Bad string length");
char* buffer = (char*)alloca(size+1);
stream.Read(buffer, size);
value.set(buffer, size);
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const RBX::ContentId& value)
{
stream << value.toString();
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, RBX::ContentId& value)
{
std::string text;
stream >> text;
value = ContentId(text);
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const BrickColor& value)
{
// NOTE: This is technically a "lossy" write
size_t i = value.getClosestPaletteIndex();
stream.WriteBits((const unsigned char*) &i, BrickColor::paletteSizeMSB);
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const UDim& value)
{
stream.Write(value.scale);
stream.Write((int)value.offset);
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const UDim2& value)
{
stream << value.x;
stream << value.y;
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const RBX::RbxRay& value)
{
stream << value.origin();
stream << value.direction();
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const Faces& value)
{
stream.Write(value.normalIdMask);
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const Axes& value)
{
stream.Write(value.axisMask);
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const G3D::Color3& value)
{
RBXASSERT_VERY_FAST(G3D::isFinite(value.r));
RBXASSERT_VERY_FAST(G3D::isFinite(value.g));
RBXASSERT_VERY_FAST(G3D::isFinite(value.b));
stream.Write(value.r);
stream.Write(value.g);
stream.Write(value.b);
return stream;
}
namespace Network {
const float brickEpsilon = 0.0005f;
inline bool brickEq(float a, float b) {
return (a == b) || (fabs(a - b) <= brickEpsilon);
}
// Bricks tend to snap to 0.5, 0.1, 0.5 increments. Special-case these values
// by sending them as 11-bit integers
static bool isBrickLocation(const G3D::Vector3& v, short& x, unsigned short& y, short& z)
{
// Limit range to a 11x11x11 bit box around the origin, with y>=0
if (v.x>=512.0f)
return false;
if (v.x<=-512.0f)
return false;
if (v.z>=512.0f)
return false;
if (v.z<=-512.0f)
return false;
if (v.y>=204.8f)
return false;
if (v.y<0)
return false;
// Now convert the components to integers, checking each time to confirm it conforms
float dx(2*v.x);
x = short(dx);
if (float(x)!=dx) // exact compare is OK
return false;
float dz(2*v.z);
z = short(dz);
if (float(z)!=dz) // exact compare is OK
return false;
float dy(10*v.y);
y = (unsigned short)dy;
if (!brickEq(y, dy)) // fuzzy compare because of round-off error
return false;
return true;
}
void writeBrickVector(RakNet::BitStream& stream, const G3D::Vector3& value)
{
RBXASSERT_VERY_FAST(G3D::isFinite(value.x));
RBXASSERT_VERY_FAST(G3D::isFinite(value.y));
RBXASSERT_VERY_FAST(G3D::isFinite(value.z));
short x;
unsigned short y;
short z;
if (isBrickLocation(value, x, y, z))
{
stream << true;
stream.WriteBits((const unsigned char*)&x, 11);
stream.WriteBits((const unsigned char*)&y, 11);
stream.WriteBits((const unsigned char*)&z, 11);
}
else
{
stream << false;
stream << value.x;
stream << value.y;
stream << value.z;
}
}
void readBrickVector(RakNet::BitStream& stream, G3D::Vector3& value)
{
bool isBrickLocation;
stream >> isBrickLocation;
if (isBrickLocation)
{
short x = 0;
unsigned short y = 0;
short z = 0;
Network::readFastN<11>(stream, x);
Network::readFastN<11>(stream, y);
Network::readFastN<11>(stream, z);
// Fill in the sign bits:
if (x & 0x0400) x |= 0xFC00;
if (z & 0x0400) z |= 0xFC00;
value.x = float(x) / 2;
value.y = float(y) / 10;
value.z = float(z) / 2;
}
else
{
stream >> value.x;
stream >> value.y;
stream >> value.z;
}
RBXASSERT_VERY_FAST(G3D::isFinite(value.x));
RBXASSERT_VERY_FAST(G3D::isFinite(value.y));
RBXASSERT_VERY_FAST(G3D::isFinite(value.z));
}
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const G3D::Vector2& value)
{
RBXASSERT_FISHING(G3D::isFinite(value.x));
RBXASSERT_FISHING(G3D::isFinite(value.y));
stream << value.x;
stream << value.y;
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, G3D::Vector2& value)
{
stream >> value.x;
stream >> value.y;
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const StreamRegion::Id& value)
{
const Vector3int32 &vv = value.value();
if (vv.x <= 127 && vv.y <= 127 && vv.z <= 127 &&
vv.x >= -128 && vv.y >= -128 && vv.z >= -128)
{
stream << false; // small int, use 3 bytes
stream << (char)vv.x;
stream << (char)vv.y;
stream << (char)vv.z;
}
else
{
stream << true; // large int, use 12 bytes
stream << vv.x;
stream << vv.y;
stream << vv.z;
}
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, StreamRegion::Id& value)
{
Vector3int32 vv;
bool largeInt;
stream >> largeInt;
if (largeInt)
{
stream >> vv.x;
stream >> vv.y;
stream >> vv.z;
}
else
{
char x, y, z;
stream >> x;
stream >> y;
stream >> z;
vv.x = x;
vv.y = y;
vv.z = z;
}
value = StreamRegion::Id(vv);
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const G3D::Vector3& value)
{
RBXASSERT_FISHING(G3D::isFinite(value.x));
RBXASSERT_FISHING(G3D::isFinite(value.y));
RBXASSERT_FISHING(G3D::isFinite(value.z));
stream << value.x;
stream << value.y;
stream << value.z;
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, G3D::Vector3& value)
{
stream >> value.x;
stream >> value.y;
stream >> value.z;
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const G3D::Vector3int16& value)
{
stream << value.x;
stream << value.y;
stream << value.z;
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, G3D::Vector3int16& value)
{
stream >> value.x;
stream >> value.y;
stream >> value.z;
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const G3D::Vector2int16& value)
{
stream << value.x;
stream << value.y;
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, G3D::Vector2int16& value)
{
stream >> value.x;
stream >> value.y;
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const RBX::Velocity& value)
{
stream << value.linear;
stream << value.rotational;
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, RBX::Velocity& value)
{
stream >> value.linear;
stream >> value.rotational;
return stream;
}
namespace Network {
void rationalize(G3D::CoordinateFrame& value)
{
if (!value.translation.isFinite())
value.translation = G3D::Vector3(0, -1e6, 0);
else
value.translation = G3D::clamp(G3D::Vector3(-1e6, -1e6, -1e6), value.translation, G3D::Vector3(1e6, 1e6, 1e6));
}
const int orientationBits = 6;
BOOST_STATIC_ASSERT((2 << (orientationBits-1)) > Math::maxOrientationId);
BOOST_STATIC_ASSERT(0 <= Math::minOrientationId);
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, const G3D::CoordinateFrame& cf)
{
// TODO: Get rid of this hack when we figure out why the values are bad:
G3D::CoordinateFrame value = cf;
Network::rationalize(value);
Network::writeBrickVector(stream, value.translation);
const bool isAxisAligned = Math::isAxisAligned(value.rotation);
stream << isAxisAligned;
if (isAxisAligned) {
const int orientId = Math::getOrientId(value.rotation);
stream.WriteBits((const unsigned char*)&orientId, Network::orientationBits);
}
else
{
Quaternion q(value.rotation);
RBXASSERT_VERY_FAST(G3D::isFinite(q.w));
RBXASSERT_VERY_FAST(G3D::isFinite(q.x));
RBXASSERT_VERY_FAST(G3D::isFinite(q.y));
RBXASSERT_VERY_FAST(G3D::isFinite(q.z));
#ifdef LOSSY_QUAT
stream.WriteNormQuat(q.w, q.x, q.y, q.z);
#else
// Orientation quaternions are unit quaternions, so max and min are 1 and -1.
// WriteNormQuat (if using LOSSY_QUAT) uses 6 bytes + 4 bits
// Straight streaming of 4 floats uses 16 bytes
// Handled here with 4 WriteFloat16 calls which uses 8 bytes
stream.WriteFloat16(q.w, -1.0f, 1.0f);
stream.WriteFloat16(q.x, -1.0f, 1.0f);
stream.WriteFloat16(q.y, -1.0f, 1.0f);
stream.WriteFloat16(q.z, -1.0f, 1.0f);
#endif
}
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, G3D::CoordinateFrame& value)
{
Network::readBrickVector(stream, value.translation);
bool isAxisAligned;
stream >> isAxisAligned;
if (isAxisAligned)
{
int orientId = 0;
Network::readFastN<Network::orientationBits>( stream, orientId );
Math::idToMatrix3(orientId, value.rotation);
}
else
{
Quaternion q;
#ifdef LOSSY_QUAT
if (!stream.ReadNormQuat(q.w, q.x, q.y, q.z))
throw RBX::network_stream_exception("BitStream >> CoordinateFrame ReadNormQuat failed");
#else
// Orientation quaternions are unit quaternions, so max and min are 1 and -1.
// WriteNormQuat (if using LOSSY_QUAT) uses 6 bytes + 4 bits
// Straight streaming of 4 floats uses 16 bytes
// Handled here with 4 WriteFloat16 calls which uses 8 bytes
stream.ReadFloat16(q.w, -1.0f, 1.0f);
stream.ReadFloat16(q.x, -1.0f, 1.0f);
stream.ReadFloat16(q.y, -1.0f, 1.0f);
stream.ReadFloat16(q.z, -1.0f, 1.0f);
#endif
RBXASSERT_VERY_FAST(G3D::isFinite(q.w));
RBXASSERT_VERY_FAST(G3D::isFinite(q.x));
RBXASSERT_VERY_FAST(G3D::isFinite(q.y));
RBXASSERT_VERY_FAST(G3D::isFinite(q.z));
q.toRotationMatrix(value.rotation);
Math::orthonormalizeIfNecessary(value.rotation);
}
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, UDim& value)
{
int offset;
stream >> value.scale;
stream >> offset;
value.offset = offset;
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, UDim2& value)
{
stream >> value.x;
stream >> value.y;
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, RbxRay& value)
{
stream >> value.origin();
stream >> value.direction();
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, Faces& value)
{
stream >> value.normalIdMask;
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, Axes& value)
{
stream >> value.axisMask;
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, BrickColor& value)
{
size_t i = 0;
Network::readFastN<BrickColor::paletteSizeMSB>( stream, i );
value = BrickColor::colorPalette()[i];
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, G3D::Color3& value)
{
stream >> value.r;
stream >> value.g;
stream >> value.b;
RBXASSERT_VERY_FAST(G3D::isFinite(value.r));
RBXASSERT_VERY_FAST(G3D::isFinite(value.g));
RBXASSERT_VERY_FAST(G3D::isFinite(value.b));
return stream;
}
RakNet::BitStream& operator << (RakNet::BitStream& stream, RBX::SystemAddress value)
{
stream << value.binaryAddress;
stream.Write(value.port);
return stream;
}
template<>
RakNet::BitStream& operator >> (RakNet::BitStream& stream, RBX::SystemAddress& value)
{
stream >> value.binaryAddress;
stream >> value.port;
return stream;
}
//////////////////////////////////////////////////////////////////////////
RakNet::BitStream& operator<<( RakNet::BitStream& stream, const NumberSequenceKeypoint& p )
{
return stream << p.time << p.value << p.envelope;
}
template<>
RakNet::BitStream& operator>>( RakNet::BitStream& stream, NumberSequenceKeypoint& p )
{
return stream >> p.time >> p.value >> p.envelope;
}
RakNet::BitStream& operator<<( RakNet::BitStream& stream, const ColorSequenceKeypoint& p )
{
return stream << p.time << p.value << p.envelope;
}
template<>
RakNet::BitStream& operator>>( RakNet::BitStream& stream, ColorSequenceKeypoint& p )
{
return stream >> p.time >> p.value >> p.envelope;
}
RakNet::BitStream& operator<<( RakNet::BitStream& stream, const NumberSequence& ns )
{
const std::vector<NumberSequence::Key>& keys = ns.getPoints();
stream<<uint32_t(keys.size());
for (int j=0, e=keys.size(); j<e; ++j )
{
stream<<keys[j];
}
return stream;
}
template<>
RakNet::BitStream& operator>>( RakNet::BitStream& stream, NumberSequence& ns )
{
uint32_t size;
stream>>size;
if( size > NumberSequence::kMaxSize )
throw network_stream_exception("Number sequence is too big");
std::vector<NumberSequence::Key> keys(size);
for (unsigned j=0; j<size; ++j)
{
stream>>keys[j];
}
ns = keys;
return stream;
}
RakNet::BitStream& operator<<( RakNet::BitStream& stream, const ColorSequence& ns )
{
const std::vector<ColorSequence::Key>& keys = ns.getPoints();
stream<<uint32_t(keys.size());
for (int j=0, e=keys.size(); j<e; ++j )
{
stream<<keys[j];
}
return stream;
}
template<>
RakNet::BitStream& operator>>( RakNet::BitStream& stream, ColorSequence& ns )
{
uint32_t size;
stream>>size;
if( size > ColorSequence::kMaxSize )
throw network_stream_exception("Number sequence is too big");
std::vector<ColorSequence::Key> keys(size);
for (unsigned j=0; j<size; ++j)
{
stream>>keys[j];
}
ns = keys;
return stream;
}
RakNet::BitStream& operator<<( RakNet::BitStream& stream, const NumberRange& r )
{
return stream << r.min << r.max;
}
template<>
RakNet::BitStream& operator>>( RakNet::BitStream& stream, NumberRange& r )
{
return stream >> r.min >> r.max;
}
RakNet::BitStream& operator<<( RakNet::BitStream& stream, const Rect2D& r )
{
return stream << r.x0y0() << r.x1y1();
}
template<>
RakNet::BitStream& operator>>( RakNet::BitStream& stream, Rect2D& r )
{
Vector2 x0y0;
Vector2 x1y1;
stream >> x0y0;
stream >> x1y1;
r = Rect2D::xyxy(x0y0,x1y1);
return stream;
}
RakNet::BitStream& operator<<( RakNet::BitStream& stream, const PhysicalProperties& p)
{
bool customEnabled = p.getCustomEnabled();
stream << customEnabled;
if (customEnabled)
stream << p.getDensity() << p.getFriction() << p.getElasticity() << p.getFrictionWeight() << p.getElasticityWeight();
return stream;
}
template<>
RakNet::BitStream& operator>>( RakNet::BitStream& stream, PhysicalProperties& p)
{
bool customEnabled;
float density;
float friction;
float elasticity;
float frictionWeight;
float elasticityWeight;
stream >> customEnabled;
if (customEnabled)
{
stream >> density;
stream >> friction;
stream >> elasticity;
stream >> frictionWeight;
stream >> elasticityWeight;
p = PhysicalProperties(density, friction, elasticity, frictionWeight, elasticityWeight);
}
else
{
p = PhysicalProperties();
}
return stream;
}
//////////////////////////////////////////////////////////////////////////
namespace Network {
template<>
void serialize<RBX::ContentId>(const ConstProperty& property, RakNet::BitStream &bitStream)
{
bitStream << property.getStringValue();
}
template<>
void serialize<UDim>(const ConstProperty& property, RakNet::BitStream &bitStream)
{
bitStream << property.getValue<UDim>();
}
template<>
void deserialize<UDim>(Property& property, RakNet::BitStream &bitStream)
{
UDim c;
bitStream >> c;
if (property.getInstance())
property.setValue(c);
}
template<>
void serialize<UDim2>(const ConstProperty& property, RakNet::BitStream &bitStream)
{
bitStream << property.getValue<UDim2>();
}
template<>
void deserialize<UDim2>(Property& property, RakNet::BitStream &bitStream)
{
UDim2 c;
bitStream >> c;
if (property.getInstance())
property.setValue(c);
}
template<>
void serialize<RBX::RbxRay>(const ConstProperty& property, RakNet::BitStream &bitStream)
{
bitStream << property.getValue<RBX::RbxRay>();
}
template<>
void deserialize<RBX::RbxRay>(Property& property, RakNet::BitStream &bitStream)
{
RbxRay c;
bitStream >> c;
if (property.getInstance())
property.setValue(c);
}
template<>
void serialize<Faces>(const ConstProperty& property, RakNet::BitStream &bitStream)
{
bitStream << property.getValue<Faces>();
}
template<>
void deserialize<Faces>(Property& property, RakNet::BitStream &bitStream)
{
Faces c;
bitStream >> c;
if (property.getInstance())
property.setValue(c);
}
template<>
void serialize<Axes>(const ConstProperty& property, RakNet::BitStream &bitStream)
{
bitStream << property.getValue<Axes>();
}
template<>
void deserialize<Axes>(Property& property, RakNet::BitStream &bitStream)
{
Axes c;
bitStream >> c;
if (property.getInstance())
property.setValue(c);
}
template<>
void serialize<BrickColor>(const ConstProperty& property, RakNet::BitStream &bitStream)
{
bitStream << property.getValue<BrickColor>();
}
template<>
void deserialize<BrickColor>(Property& property, RakNet::BitStream &bitStream)
{
BrickColor c;
bitStream >> c;
if (property.getInstance())
property.setValue(c);
}
template<>
void deserialize<RBX::ContentId>(Property& property, RakNet::BitStream &bitStream)
{
std::string value;
bitStream >> value;
if (property.getInstance())
property.setStringValue(value);
}
void serializeStringProperty(const Reflection::ConstProperty& property, RakNet::BitStream &bitStream)
{
bitStream << property.getDescriptor().getStringValue(property.getInstance());
}
void deserializeStringProperty(Reflection::Property& property, RakNet::BitStream &bitStream)
{
std::string value;
bitStream >> value;
if (property.getInstance())
{
const Reflection::PropertyDescriptor& desc = property.getDescriptor();
desc.setStringValue(property.getInstance(), value);
}
}
void serializeGuidScope(RakNet::BitStream& stream, const RBX::Guid::Scope& value, bool canDisableCompression)
{
if (canDisableCompression) {
RakNet::RakString scope = value.getName()->c_str();
stream.Write(scope);
} else {
stream << value.getName()->toString();
}
}
void deserializeGuidScope(RakNet::BitStream& stream, RBX::Guid::Scope& value, bool canDisableCompression)
{
std::string str;
if (canDisableCompression) {
RakNet::RakString scope;
stream.Read(scope);
str = scope.C_String();
} else {
stream >> str;
}
value.set(str);
}
IdSerializer::IdSerializer()
{
}
bool IdSerializer::trySerializeId(RakNet::BitStream& stream, const Instance* instance)
{
if (instance)
{
guidRegistry->registerGuid(instance);
RBX::Guid::Data id;
instance->getGuid().extract(id);
if (!scopeNames.trySend(stream, id.scope))
return false;
stream.WriteBits((const unsigned char*) &id.index, 32);
return true;
}
else
{
serializeId(stream, NULL);
return true;
}
}
bool IdSerializer::canSerializeId(const Instance* instance)
{
if (instance)
{
// check if value is in dictionary
guidRegistry->registerGuid(instance);
RBX::Guid::Data id;
instance->getGuid().extract(id);
return scopeNames.canSend(id.scope);
}
return false;
}
void IdSerializer::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
guidRegistry.reset();
Super::onServiceProvider(oldProvider, newProvider);
if (newProvider)
guidRegistry = ServiceProvider::create<GuidRegistryService>(newProvider)->registry;
}
IdSerializer::Id IdSerializer::extractId(const Instance* instance)
{
IdSerializer::Id result;
if (instance)
{
guidRegistry->registerGuid(instance);
instance->getGuid().extract(result.id);
result.valid = true;
}
else{
result.valid = false;
}
return result;
}
void IdSerializer::sendId(RakNet::BitStream& stream, const Id& id)
{
if(id.valid){
scopeNames.send(stream, id.id.scope);
stream.WriteBits((const unsigned char*) &id.id.index, 32);
}
else{
scopeNames.sendEmptyItem(stream);
}
}
void IdSerializer::serializeId(RakNet::BitStream& stream, const Instance* instance)
{
if (instance)
{
guidRegistry->registerGuid(instance);
RBX::Guid::Data id;
instance->getGuid().extract(id);
serializeId(stream, id);
}
else
{
scopeNames.sendEmptyItem(stream);
}
}
void IdSerializer::serializeId(RakNet::BitStream& stream, const RBX::Guid::Data& id) {
scopeNames.send(stream, id.scope);
stream.WriteBits((const unsigned char*) &id.index, 32);
}
void IdSerializer::serializeIdWithoutDictionary(RakNet::BitStream& stream, const Instance* instance)
{
RBX::Guid::Data id;
if (instance)
{
guidRegistry->registerGuid(instance);
instance->getGuid().extract(id);
}
serializeIdWithoutDictionary(stream, id);
}
void IdSerializer::serializeIdWithoutDictionary(RakNet::BitStream& stream, const RBX::Guid::Data& id)
{
if (id.scope.isNull())
{
unsigned char code = 0;
stream << code;
}
else
{
if (id.scope == serverScope)
{
unsigned char code = 255;
stream << code;
}
else
{
const std::string& scope = id.scope.getName()->toString();
RBXASSERT(scope.size() < 255);
unsigned char code = scope.size();
stream << code;
stream.WriteBits(reinterpret_cast<const unsigned char*>(scope.c_str()), code * 8);
}
stream.WriteBits((const unsigned char*) &id.index, 32);
}
}
void IdSerializer::deserializeId(RakNet::BitStream& stream, RBX::Guid::Data& id)
{
scopeNames.receive(stream, id.scope);
if (!id.scope.isNull())
{
id.index = 0;
// This version does endian swapping.
Network::readFastN<32>( stream, id.index );
}
else
id.index = 0;
}
void IdSerializer::deserializeIdWithoutDictionary(RakNet::BitStream& stream, RBX::Guid::Data& id)
{
unsigned char code = 0;
Network::readFastT(stream, code);
if (code == 0)
{
id.scope.setNull();
id.index = 0;
}
else
{
if (code == 255)
{
RBXASSERT(!serverScope.isNull());
id.scope = serverScope;
}
else
{
char buffer[256];
stream.ReadBits(reinterpret_cast<unsigned char*>(buffer), code * 8);
buffer[code] = 0;
id.scope.set(buffer);
}
Network::readFastN<32>(stream, id.index);
}
}
void IdSerializer::setRefValue(WaitItem& wi, Instance* instance)
{
wi.desc->setRefValue(wi.instance.get(), instance);
}
void IdSerializer::resolvePendingReferences(Instance* instance, RBX::Guid::Data id)
{
boost::mutex::scoped_lock lock(waitItemsMutex);
WaitItemMap::iterator iter = waitItems.find(id);
if (iter!=waitItems.end())
{
std::for_each(
iter->second.begin(),
iter->second.end(),
boost::bind(&IdSerializer::setRefValue, this, _1, instance)
);
waitItems.erase(iter);
}
}
void IdSerializer::serializeInstanceRef(const Instance* instance, RakNet::BitStream& bitStream)
{
serializeId(bitStream, instance);
}
//Debuggable -
// Parent == NULL, or Parent::Debugable
bool IdSerializer::deserializeInstanceRef(RakNet::BitStream& stream, shared_ptr<Instance>& instance, RBX::Guid::Data& id)
{
deserializeId(stream, id);
bool answer = guidRegistry->lookupByGuid(id, instance);
RBXASSERT( !instance
|| !ServiceProvider::findServiceProvider(instance.get())
|| (ServiceProvider::findServiceProvider(instance.get()) == ServiceProvider::findServiceProvider(this))
);
return answer;
}
void IdSerializer::addPendingRef(const Reflection::RefPropertyDescriptor* desc,
boost::shared_ptr<Instance> instance, RBX::Guid::Data id) {
boost::mutex::scoped_lock lock(waitItemsMutex);
WaitItem item = { desc, instance };
waitItems[id].push_back(item);
}
template<class T>
void DescriptorSender<T>::visit(const T* desc)
{
const unsigned int id = descToId.size();
IdContainer idContainer;
idContainer.id = id;
idContainer.outdated = false;
descToId[desc] = idContainer;
idBits = Math::computeMSB(descToId.size())+1;
}
template<>
std::string DescriptorSender<ClassDescriptor>::teachName(const ClassDescriptor* t) const
{
return t->name.toString();
}
template<>
void DescriptorReceiver<ClassDescriptor>::learnName(std::string s, int i, uint32_t checksum)
{
const RBX::Name& n = RBX::Name::lookup(s);
ClassDescriptor::ClassDescriptors::const_iterator iter = ClassDescriptor::all_begin();
while (iter!=ClassDescriptor::all_end())
{
if ((*iter)->name == n)
{
idToDesc[i].desc = *iter;
idToDesc[i].outdated = !verifyChecksum((*iter), checksum);
*((*iter)->isOutdated) = idToDesc[i].outdated;
*((*iter)->isReplicable) = true;
return;
}
++iter;
}
StandardOut::singleton()->printf(MESSAGE_WARNING, "ClassDescriptor failed to learn %s", s.c_str());
idToDesc[i].desc = NULL;
idToDesc[i].outdated = false;
}
template<>
std::string DescriptorSender<EventDescriptor>::teachName(const EventDescriptor* t) const
{
return t->owner.name.toString() + ":" + t->name.toString();
}
template<>
void DescriptorReceiver<EventDescriptor>::learnName(std::string s, int i, uint32_t checksum)
{
std::vector<std::string> words;
boost::split(words, s, boost::is_any_of(":"));
// First get the class name
const RBX::Name& n = RBX::Name::lookup(words[0]);
ClassDescriptor::ClassDescriptors::const_iterator iter = ClassDescriptor::all_begin();
while (iter!=ClassDescriptor::all_end())
{
const ClassDescriptor* c = *iter;
if (c->name == n)
{
if (EventDescriptor* desc = c->findEventDescriptor(words[1].c_str()))
{
idToDesc[i].desc = desc;
idToDesc[i].outdated = !verifyChecksum(desc, checksum);
if (idToDesc[i].outdated)
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "EventDescriptor %s is out of date, replication will be ignored", s.c_str());
}
*(desc->isOutdated) = idToDesc[i].outdated;
*(desc->isReplicable) = true;
return;
}
else
break;
}
++iter;
}
StandardOut::singleton()->printf(MESSAGE_WARNING, "EventDescriptor failed to learn %s", s.c_str());
idToDesc[i].desc = NULL;
idToDesc[i].outdated = false;
}
template<>
std::string DescriptorSender<PropertyDescriptor>::teachName(const PropertyDescriptor* t) const
{
return t->owner.name.toString() + ":" + t->name.toString();
}
template<>
void DescriptorReceiver<PropertyDescriptor>::learnName(std::string s, int i, uint32_t checksum)
{
std::vector<std::string> words;
boost::split(words, s, boost::is_any_of(":"));
// First get the class name
const RBX::Name& n = RBX::Name::lookup(words[0]);
ClassDescriptor::ClassDescriptors::const_iterator iter = ClassDescriptor::all_begin();
while (iter!=ClassDescriptor::all_end())
{
const ClassDescriptor* c = *iter;
if (c->name == n)
{
if (PropertyDescriptor* desc = c->findPropertyDescriptor(words[1].c_str()))
{
idToDesc[i].desc = desc;
idToDesc[i].outdated = !verifyChecksum(desc, checksum);
*(desc->isOutdated) = idToDesc[i].outdated;
*(desc->isReplicable) = true;
return;
}
else
break;
}
++iter;
}
idToDesc[i].desc = NULL;
idToDesc[i].outdated = false;
}
template<>
std::string DescriptorSender<Type>::teachName(const Type* t) const
{
return t->name.toString();
}
template<>
void DescriptorReceiver<Type>::learnName(std::string s, int i, uint32_t checksum)
{
const RBX::Name& n = RBX::Name::lookup(s);
const std::vector<const Type*>& types = Type::getAllTypes();
for (size_t ti = 0; ti < types.size(); ++ti)
{
if (types[ti]->name == n)
{
idToDesc[i].desc = types[ti];
idToDesc[i].outdated = !verifyChecksum(types[ti], checksum);
return;
}
}
StandardOut::singleton()->printf(MESSAGE_WARNING, "Type failed to learn %s", s.c_str());
idToDesc[i].desc = NULL;
idToDesc[i].outdated = false;
}
template<>
DescriptorSender<ClassDescriptor>::DescriptorSender()
{
ClassDescriptor::ClassDescriptors::const_iterator iter = ClassDescriptor::all_begin();
ClassDescriptor::ClassDescriptors::const_iterator end = ClassDescriptor::all_end();
while (iter!=end)
{
// TODO: Skip classes that can't be constructed??? (Abstract classes)
visit(*iter);
++iter;
}
}
template<>
DescriptorSender<PropertyDescriptor>::DescriptorSender()
{
MemberDescriptorContainer<PropertyDescriptor>::Collection::const_iterator iter = MemberDescriptorContainer<PropertyDescriptor>::all_begin();
MemberDescriptorContainer<PropertyDescriptor>::Collection::const_iterator end = MemberDescriptorContainer<PropertyDescriptor>::all_end();
while (iter!=end)
{
visit(*iter);
++iter;
}
}
template<>
DescriptorSender<EventDescriptor>::DescriptorSender()
{
MemberDescriptorContainer<EventDescriptor>::Collection::const_iterator iter = MemberDescriptorContainer<EventDescriptor>::all_begin();
MemberDescriptorContainer<EventDescriptor>::Collection::const_iterator end = MemberDescriptorContainer<EventDescriptor>::all_end();
while (iter!=end)
{
visit(*iter);
++iter;
}
}
template<>
DescriptorSender<Type>::DescriptorSender()
{
const std::vector<const Type*>& types = Type::getAllTypes();
for (size_t i = 0; i < types.size(); ++i)
{
visit(types[i]);
}
}
}
}