mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
Merge pull request #2999 from ey6es/metavoxels
Provide a way to stream enums that aren't object properties, added tests for delta streaming, fixed Windows initialization bug.
This commit is contained in:
commit
5ed1dfc4b0
4 changed files with 272 additions and 107 deletions
|
@ -79,10 +79,6 @@ IDStreamer& IDStreamer::operator>>(int& value) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
static QByteArray getEnumName(const QMetaEnum& metaEnum) {
|
||||
return QByteArray(metaEnum.scope()) + "::" + metaEnum.name();
|
||||
}
|
||||
|
||||
int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) {
|
||||
getMetaObjects().insert(className, metaObject);
|
||||
|
||||
|
@ -90,17 +86,6 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta
|
|||
for (const QMetaObject* superClass = metaObject; superClass; superClass = superClass->superClass()) {
|
||||
getMetaObjectSubClasses().insert(superClass, metaObject);
|
||||
}
|
||||
|
||||
// register the streamers for all enumerators
|
||||
// temporarily disabled: crashes on Windows
|
||||
//for (int i = 0; i < metaObject->enumeratorCount(); i++) {
|
||||
// QMetaEnum metaEnum = metaObject->enumerator(i);
|
||||
// const TypeStreamer*& streamer = getEnumStreamers()[QPair<QByteArray, QByteArray>(metaEnum.scope(), metaEnum.name())];
|
||||
// if (!streamer) {
|
||||
// getEnumStreamersByName().insert(getEnumName(metaEnum), streamer = new EnumTypeStreamer(metaEnum));
|
||||
// }
|
||||
//}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -225,7 +210,8 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) {
|
|||
}
|
||||
connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*)));
|
||||
QPointer<SharedObject>& reference = _sharedObjectReferences[it.key()->getOriginID()];
|
||||
if (reference) {
|
||||
if (reference && reference != it.key()) {
|
||||
// the object has been replaced by a successor, so we can forget about the original
|
||||
_sharedObjectStreamer.removePersistentID(reference);
|
||||
reference->disconnect(this);
|
||||
}
|
||||
|
@ -259,7 +245,8 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) {
|
|||
continue;
|
||||
}
|
||||
QPointer<SharedObject>& reference = _sharedObjectReferences[it.value()->getRemoteOriginID()];
|
||||
if (reference) {
|
||||
if (reference && reference != it.value()) {
|
||||
// the object has been replaced by a successor, so we can forget about the original
|
||||
_sharedObjectStreamer.removePersistentValue(reference.data());
|
||||
}
|
||||
reference = it.value();
|
||||
|
@ -311,7 +298,7 @@ void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) {
|
|||
}
|
||||
const QMetaObject* metaObject = value->metaObject();
|
||||
_metaObjectStreamer << metaObject;
|
||||
foreach (const PropertyWriter& propertyWriter, getPropertyWriters(metaObject)) {
|
||||
foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) {
|
||||
propertyWriter.writeDelta(*this, value, reference);
|
||||
}
|
||||
}
|
||||
|
@ -489,7 +476,7 @@ Bitstream& Bitstream::operator<<(const QObject* object) {
|
|||
}
|
||||
const QMetaObject* metaObject = object->metaObject();
|
||||
_metaObjectStreamer << metaObject;
|
||||
foreach (const PropertyWriter& propertyWriter, getPropertyWriters(metaObject)) {
|
||||
foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) {
|
||||
propertyWriter.write(*this, object);
|
||||
}
|
||||
return *this;
|
||||
|
@ -574,7 +561,7 @@ Bitstream& Bitstream::operator<(const QMetaObject* metaObject) {
|
|||
if (_metadataType == NO_METADATA) {
|
||||
return *this;
|
||||
}
|
||||
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters(metaObject);
|
||||
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters().value(metaObject);
|
||||
*this << propertyWriters.size();
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
foreach (const PropertyWriter& propertyWriter, propertyWriters) {
|
||||
|
@ -608,7 +595,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
|||
qWarning() << "Unknown class name: " << className << "\n";
|
||||
}
|
||||
if (_metadataType == NO_METADATA) {
|
||||
objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject));
|
||||
objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject));
|
||||
return *this;
|
||||
}
|
||||
int storedPropertyCount;
|
||||
|
@ -632,7 +619,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
|||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
bool matches = true;
|
||||
if (metaObject) {
|
||||
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters(metaObject);
|
||||
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters().value(metaObject);
|
||||
if (propertyWriters.size() == properties.size()) {
|
||||
for (int i = 0; i < propertyWriters.size(); i++) {
|
||||
const PropertyWriter& propertyWriter = propertyWriters.at(i);
|
||||
|
@ -651,7 +638,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
|||
QByteArray remoteHashResult(localHashResult.size(), 0);
|
||||
read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE);
|
||||
if (metaObject && matches && localHashResult == remoteHashResult) {
|
||||
objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject));
|
||||
objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject));
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
@ -988,31 +975,6 @@ void Bitstream::clearSharedObject(QObject* object) {
|
|||
}
|
||||
}
|
||||
|
||||
const QVector<PropertyWriter>& Bitstream::getPropertyWriters(const QMetaObject* metaObject) {
|
||||
QVector<PropertyWriter>& propertyWriters = _propertyWriters[metaObject];
|
||||
if (propertyWriters.isEmpty()) {
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (!property.isStored()) {
|
||||
continue;
|
||||
}
|
||||
const TypeStreamer* streamer;
|
||||
if (property.isEnumType()) {
|
||||
QMetaEnum metaEnum = property.enumerator();
|
||||
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
|
||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||
} else {
|
||||
streamer = getTypeStreamers().value(property.userType());
|
||||
}
|
||||
if (streamer) {
|
||||
propertyWriters.append(PropertyWriter(property, streamer));
|
||||
}
|
||||
}
|
||||
}
|
||||
return propertyWriters;
|
||||
}
|
||||
|
||||
QHash<QByteArray, const QMetaObject*>& Bitstream::getMetaObjects() {
|
||||
static QHash<QByteArray, const QMetaObject*> metaObjects;
|
||||
return metaObjects;
|
||||
|
@ -1028,42 +990,100 @@ QHash<int, const TypeStreamer*>& Bitstream::getTypeStreamers() {
|
|||
return typeStreamers;
|
||||
}
|
||||
|
||||
QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& Bitstream::getEnumStreamers() {
|
||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> enumStreamers;
|
||||
const QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& Bitstream::getEnumStreamers() {
|
||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> enumStreamers = createEnumStreamers();
|
||||
return enumStreamers;
|
||||
}
|
||||
|
||||
QHash<QByteArray, const TypeStreamer*>& Bitstream::getEnumStreamersByName() {
|
||||
static QHash<QByteArray, const TypeStreamer*> enumStreamersByName;
|
||||
QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> Bitstream::createEnumStreamers() {
|
||||
QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> enumStreamers;
|
||||
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
||||
for (int i = 0; i < metaObject->enumeratorCount(); i++) {
|
||||
QMetaEnum metaEnum = metaObject->enumerator(i);
|
||||
const TypeStreamer*& streamer = enumStreamers[QPair<QByteArray, QByteArray>(metaEnum.scope(), metaEnum.name())];
|
||||
if (!streamer) {
|
||||
streamer = new EnumTypeStreamer(metaEnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
return enumStreamers;
|
||||
}
|
||||
|
||||
const QHash<QByteArray, const TypeStreamer*>& Bitstream::getEnumStreamersByName() {
|
||||
static QHash<QByteArray, const TypeStreamer*> enumStreamersByName = createEnumStreamersByName();
|
||||
return enumStreamersByName;
|
||||
}
|
||||
|
||||
QVector<PropertyReader> Bitstream::getPropertyReaders(const QMetaObject* metaObject) {
|
||||
QVector<PropertyReader> propertyReaders;
|
||||
if (!metaObject) {
|
||||
return propertyReaders;
|
||||
QHash<QByteArray, const TypeStreamer*> Bitstream::createEnumStreamersByName() {
|
||||
QHash<QByteArray, const TypeStreamer*> enumStreamersByName;
|
||||
foreach (const TypeStreamer* streamer, getEnumStreamers()) {
|
||||
enumStreamersByName.insert(streamer->getName(), streamer);
|
||||
}
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (!property.isStored()) {
|
||||
continue;
|
||||
}
|
||||
const TypeStreamer* streamer;
|
||||
if (property.isEnumType()) {
|
||||
QMetaEnum metaEnum = property.enumerator();
|
||||
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
|
||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||
} else {
|
||||
streamer = getTypeStreamers().value(property.userType());
|
||||
}
|
||||
if (streamer) {
|
||||
propertyReaders.append(PropertyReader(TypeReader(QByteArray(), streamer), property));
|
||||
return enumStreamersByName;
|
||||
}
|
||||
|
||||
const QHash<const QMetaObject*, QVector<PropertyReader> >& Bitstream::getPropertyReaders() {
|
||||
static QHash<const QMetaObject*, QVector<PropertyReader> > propertyReaders = createPropertyReaders();
|
||||
return propertyReaders;
|
||||
}
|
||||
|
||||
QHash<const QMetaObject*, QVector<PropertyReader> > Bitstream::createPropertyReaders() {
|
||||
QHash<const QMetaObject*, QVector<PropertyReader> > propertyReaders;
|
||||
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
||||
QVector<PropertyReader>& readers = propertyReaders[metaObject];
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (!property.isStored()) {
|
||||
continue;
|
||||
}
|
||||
const TypeStreamer* streamer;
|
||||
if (property.isEnumType()) {
|
||||
QMetaEnum metaEnum = property.enumerator();
|
||||
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
|
||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||
} else {
|
||||
streamer = getTypeStreamers().value(property.userType());
|
||||
}
|
||||
if (streamer) {
|
||||
readers.append(PropertyReader(TypeReader(QByteArray(), streamer), property));
|
||||
}
|
||||
}
|
||||
}
|
||||
return propertyReaders;
|
||||
}
|
||||
|
||||
const QHash<const QMetaObject*, QVector<PropertyWriter> >& Bitstream::getPropertyWriters() {
|
||||
static QHash<const QMetaObject*, QVector<PropertyWriter> > propertyWriters = createPropertyWriters();
|
||||
return propertyWriters;
|
||||
}
|
||||
|
||||
QHash<const QMetaObject*, QVector<PropertyWriter> > Bitstream::createPropertyWriters() {
|
||||
QHash<const QMetaObject*, QVector<PropertyWriter> > propertyWriters;
|
||||
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
||||
QVector<PropertyWriter>& writers = propertyWriters[metaObject];
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (!property.isStored()) {
|
||||
continue;
|
||||
}
|
||||
const TypeStreamer* streamer;
|
||||
if (property.isEnumType()) {
|
||||
QMetaEnum metaEnum = property.enumerator();
|
||||
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
|
||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||
} else {
|
||||
streamer = getTypeStreamers().value(property.userType());
|
||||
}
|
||||
if (streamer) {
|
||||
writers.append(PropertyWriter(property, streamer));
|
||||
}
|
||||
}
|
||||
}
|
||||
return propertyWriters;
|
||||
}
|
||||
|
||||
TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer) :
|
||||
_typeName(typeName),
|
||||
_streamer(streamer),
|
||||
|
@ -1461,17 +1481,21 @@ QDebug& operator<<(QDebug& debug, const QMetaObject* metaObject) {
|
|||
return debug << (metaObject ? metaObject->className() : "null");
|
||||
}
|
||||
|
||||
EnumTypeStreamer::EnumTypeStreamer(const QMetaObject* metaObject, const char* name) :
|
||||
_metaObject(metaObject),
|
||||
_enumName(name),
|
||||
_name(QByteArray(metaObject->className()) + "::" + name),
|
||||
_bits(-1) {
|
||||
|
||||
setType(QMetaType::Int);
|
||||
}
|
||||
|
||||
EnumTypeStreamer::EnumTypeStreamer(const QMetaEnum& metaEnum) :
|
||||
_name(QByteArray(metaEnum.scope()) + "::" + metaEnum.name()),
|
||||
_metaEnum(metaEnum),
|
||||
_name(getEnumName(metaEnum)) {
|
||||
|
||||
setType(QMetaType::Int);
|
||||
|
||||
int highestValue = 0;
|
||||
for (int j = 0; j < metaEnum.keyCount(); j++) {
|
||||
highestValue = qMax(highestValue, metaEnum.value(j));
|
||||
}
|
||||
_bits = getBitsForHighestValue(highestValue);
|
||||
_bits(-1) {
|
||||
|
||||
setType(QMetaType::Int);
|
||||
}
|
||||
|
||||
const char* EnumTypeStreamer::getName() const {
|
||||
|
@ -1483,10 +1507,21 @@ TypeReader::Type EnumTypeStreamer::getReaderType() const {
|
|||
}
|
||||
|
||||
int EnumTypeStreamer::getBits() const {
|
||||
if (_bits == -1) {
|
||||
int highestValue = 0;
|
||||
QMetaEnum metaEnum = getMetaEnum();
|
||||
for (int j = 0; j < metaEnum.keyCount(); j++) {
|
||||
highestValue = qMax(highestValue, metaEnum.value(j));
|
||||
}
|
||||
const_cast<EnumTypeStreamer*>(this)->_bits = getBitsForHighestValue(highestValue);
|
||||
}
|
||||
return _bits;
|
||||
}
|
||||
|
||||
QMetaEnum EnumTypeStreamer::getMetaEnum() const {
|
||||
if (!_metaEnum.isValid()) {
|
||||
const_cast<EnumTypeStreamer*>(this)->_metaEnum = _metaObject->enumerator(_metaObject->indexOfEnumerator(_enumName));
|
||||
}
|
||||
return _metaEnum;
|
||||
}
|
||||
|
||||
|
@ -1496,12 +1531,12 @@ bool EnumTypeStreamer::equal(const QVariant& first, const QVariant& second) cons
|
|||
|
||||
void EnumTypeStreamer::write(Bitstream& out, const QVariant& value) const {
|
||||
int intValue = value.toInt();
|
||||
out.write(&intValue, _bits);
|
||||
out.write(&intValue, getBits());
|
||||
}
|
||||
|
||||
QVariant EnumTypeStreamer::read(Bitstream& in) const {
|
||||
int intValue = 0;
|
||||
in.read(&intValue, _bits);
|
||||
in.read(&intValue, getBits());
|
||||
return intValue;
|
||||
}
|
||||
|
||||
|
@ -1511,7 +1546,7 @@ void EnumTypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const Q
|
|||
out << false;
|
||||
} else {
|
||||
out << true;
|
||||
out.write(&intValue, _bits);
|
||||
out.write(&intValue, getBits());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1520,7 +1555,7 @@ void EnumTypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant&
|
|||
in >> changed;
|
||||
if (changed) {
|
||||
int intValue = 0;
|
||||
in.read(&intValue, _bits);
|
||||
in.read(&intValue, getBits());
|
||||
value = intValue;
|
||||
} else {
|
||||
value = reference;
|
||||
|
@ -1529,17 +1564,17 @@ void EnumTypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant&
|
|||
|
||||
void EnumTypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const {
|
||||
int intValue = value.toInt();
|
||||
out.write(&intValue, _bits);
|
||||
out.write(&intValue, getBits());
|
||||
}
|
||||
|
||||
void EnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const {
|
||||
int intValue = 0;
|
||||
in.read(&intValue, _bits);
|
||||
in.read(&intValue, getBits());
|
||||
value = intValue;
|
||||
}
|
||||
|
||||
void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash<int, int>& mappings) const {
|
||||
if (_metaEnum.isFlag()) {
|
||||
if (getMetaEnum().isFlag()) {
|
||||
int combined = 0;
|
||||
for (QHash<int, int>::const_iterator it = mappings.constBegin(); it != mappings.constEnd(); it++) {
|
||||
if (value & it.key()) {
|
||||
|
|
|
@ -400,8 +400,6 @@ private slots:
|
|||
|
||||
private:
|
||||
|
||||
const QVector<PropertyWriter>& getPropertyWriters(const QMetaObject* metaObject);
|
||||
|
||||
QDataStream& _underlying;
|
||||
quint8 _byte;
|
||||
int _position;
|
||||
|
@ -421,14 +419,17 @@ private:
|
|||
QHash<QByteArray, const QMetaObject*> _metaObjectSubstitutions;
|
||||
QHash<QByteArray, const TypeStreamer*> _typeStreamerSubstitutions;
|
||||
|
||||
QHash<const QMetaObject*, QVector<PropertyWriter> > _propertyWriters;
|
||||
|
||||
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
|
||||
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
||||
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& getEnumStreamers();
|
||||
static QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
|
||||
static QVector<PropertyReader> getPropertyReaders(const QMetaObject* metaObject);
|
||||
static const QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& getEnumStreamers();
|
||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> createEnumStreamers();
|
||||
static const QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
|
||||
static QHash<QByteArray, const TypeStreamer*> createEnumStreamersByName();
|
||||
static const QHash<const QMetaObject*, QVector<PropertyReader> >& getPropertyReaders();
|
||||
static QHash<const QMetaObject*, QVector<PropertyReader> > createPropertyReaders();
|
||||
static const QHash<const QMetaObject*, QVector<PropertyWriter> >& getPropertyWriters();
|
||||
static QHash<const QMetaObject*, QVector<PropertyWriter> > createPropertyWriters();
|
||||
};
|
||||
|
||||
template<class T> inline void Bitstream::writeDelta(const T& value, const T& reference) {
|
||||
|
@ -938,8 +939,9 @@ public:
|
|||
class EnumTypeStreamer : public TypeStreamer {
|
||||
public:
|
||||
|
||||
EnumTypeStreamer(const QMetaObject* metaObject, const char* name);
|
||||
EnumTypeStreamer(const QMetaEnum& metaEnum);
|
||||
|
||||
|
||||
virtual const char* getName() const;
|
||||
virtual TypeReader::Type getReaderType() const;
|
||||
virtual int getBits() const;
|
||||
|
@ -955,8 +957,10 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
QMetaEnum _metaEnum;
|
||||
const QMetaObject* _metaObject;
|
||||
const char* _enumName;
|
||||
QByteArray _name;
|
||||
QMetaEnum _metaEnum;
|
||||
int _bits;
|
||||
};
|
||||
|
||||
|
@ -1037,12 +1041,12 @@ public:
|
|||
};
|
||||
|
||||
/// Macro for registering simple type streamers.
|
||||
#define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \
|
||||
Bitstream::registerTypeStreamer(qMetaTypeId<x>(), new SimpleTypeStreamer<x>());
|
||||
#define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \
|
||||
Bitstream::registerTypeStreamer(qMetaTypeId<X>(), new SimpleTypeStreamer<X>());
|
||||
|
||||
/// Macro for registering collection type streamers.
|
||||
#define REGISTER_COLLECTION_TYPE_STREAMER(x) static int x##Streamer = \
|
||||
Bitstream::registerTypeStreamer(qMetaTypeId<x>(), new CollectionTypeStreamer<x>());
|
||||
#define REGISTER_COLLECTION_TYPE_STREAMER(X) static int x##Streamer = \
|
||||
Bitstream::registerTypeStreamer(qMetaTypeId<X>(), new CollectionTypeStreamer<X>());
|
||||
|
||||
/// Declares the metatype and the streaming operators. The last lines
|
||||
/// ensure that the generated file will be included in the link phase.
|
||||
|
@ -1077,14 +1081,42 @@ public:
|
|||
_Pragma(STRINGIFY(unused(_TypePtr##X)))
|
||||
#endif
|
||||
|
||||
#define DECLARE_ENUM_METATYPE(S, N) Q_DECLARE_METATYPE(S::N) \
|
||||
Bitstream& operator<<(Bitstream& out, const S::N& obj); \
|
||||
Bitstream& operator>>(Bitstream& in, S::N& obj); \
|
||||
template<> inline void Bitstream::writeRawDelta(const S::N& value, const S::N& reference) { *this << value; } \
|
||||
template<> inline void Bitstream::readRawDelta(S::N& value, const S::N& reference) { *this >> value; }
|
||||
|
||||
#define IMPLEMENT_ENUM_METATYPE(S, N) \
|
||||
static int S##N##MetaTypeId = registerEnumMetaType<S::N>(&S::staticMetaObject, #N); \
|
||||
Bitstream& operator<<(Bitstream& out, const S::N& obj) { \
|
||||
static int bits = Bitstream::getTypeStreamer(qMetaTypeId<S::N>())->getBits(); \
|
||||
return out.write(&obj, bits); \
|
||||
} \
|
||||
Bitstream& operator>>(Bitstream& in, S::N& obj) { \
|
||||
static int bits = Bitstream::getTypeStreamer(qMetaTypeId<S::N>())->getBits(); \
|
||||
obj = (S::N)0; \
|
||||
return in.read(&obj, bits); \
|
||||
}
|
||||
|
||||
/// Registers a simple type and its streamer.
|
||||
/// \return the metatype id
|
||||
template<class T> int registerSimpleMetaType() {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer<T>());
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Registers an enum type and its streamer.
|
||||
/// \return the metatype id
|
||||
template<class T> int registerEnumMetaType(const QMetaObject* metaObject, const char* name) {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new EnumTypeStreamer(metaObject, name));
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Registers a streamable type and its streamer.
|
||||
/// \return the metatype id
|
||||
template<class T> int registerStreamableMetaType() {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new StreamableTypeStreamer<T>());
|
||||
|
@ -1092,6 +1124,7 @@ template<class T> int registerStreamableMetaType() {
|
|||
}
|
||||
|
||||
/// Registers a collection type and its streamer.
|
||||
/// \return the metatype id
|
||||
template<class T> int registerCollectionMetaType() {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new CollectionTypeStreamer<T>());
|
||||
|
|
|
@ -20,12 +20,16 @@
|
|||
REGISTER_META_OBJECT(TestSharedObjectA)
|
||||
REGISTER_META_OBJECT(TestSharedObjectB)
|
||||
|
||||
IMPLEMENT_ENUM_METATYPE(TestSharedObjectA, TestEnum)
|
||||
|
||||
MetavoxelTests::MetavoxelTests(int& argc, char** argv) :
|
||||
QCoreApplication(argc, argv) {
|
||||
}
|
||||
|
||||
static int datagramsSent = 0;
|
||||
static int datagramsReceived = 0;
|
||||
static int bytesSent = 0;
|
||||
static int bytesReceived = 0;
|
||||
static int highPriorityMessagesSent = 0;
|
||||
static int highPriorityMessagesReceived = 0;
|
||||
static int unreliableMessagesSent = 0;
|
||||
|
@ -36,6 +40,7 @@ static int streamedBytesSent = 0;
|
|||
static int streamedBytesReceived = 0;
|
||||
static int sharedObjectsCreated = 0;
|
||||
static int sharedObjectsDestroyed = 0;
|
||||
static int objectMutationsPerformed = 0;
|
||||
|
||||
static QByteArray createRandomBytes(int minimumSize, int maximumSize) {
|
||||
QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0);
|
||||
|
@ -80,6 +85,7 @@ static TestMessageC createRandomMessageC() {
|
|||
message.bar = rand();
|
||||
message.baz = randFloat();
|
||||
message.bong.foo = createRandomBytes();
|
||||
message.bong.baz = getRandomTestEnum();
|
||||
return message;
|
||||
}
|
||||
|
||||
|
@ -180,7 +186,7 @@ bool MetavoxelTests::run() {
|
|||
bob.setOther(&alice);
|
||||
|
||||
// perform a large number of simulation iterations
|
||||
const int SIMULATION_ITERATIONS = 100000;
|
||||
const int SIMULATION_ITERATIONS = 10000;
|
||||
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||
if (alice.simulate(i) || bob.simulate(i)) {
|
||||
return true;
|
||||
|
@ -191,8 +197,10 @@ bool MetavoxelTests::run() {
|
|||
qDebug() << "Sent" << unreliableMessagesSent << "unreliable messages, received" << unreliableMessagesReceived;
|
||||
qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived;
|
||||
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived;
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" <<
|
||||
datagramsReceived << "with" << bytesReceived << "bytes";
|
||||
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
||||
qDebug() << "Performed" << objectMutationsPerformed << "object mutations";
|
||||
qDebug();
|
||||
|
||||
qDebug() << "Running serialization tests...";
|
||||
|
@ -226,6 +234,20 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
|||
connect(_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)),
|
||||
SLOT(handleHighPriorityMessage(const QVariant&)));
|
||||
|
||||
connect(_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int)));
|
||||
connect(_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int)));
|
||||
|
||||
// insert the baseline send record
|
||||
SendRecord sendRecord = { 0 };
|
||||
_sendRecords.append(sendRecord);
|
||||
|
||||
// insert the baseline receive record
|
||||
ReceiveRecord receiveRecord = { 0 };
|
||||
_receiveRecords.append(receiveRecord);
|
||||
|
||||
// create the object that represents out delta-encoded state
|
||||
_localState = new TestSharedObjectA();
|
||||
|
||||
connect(_sequencer->getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)),
|
||||
SLOT(handleReliableMessage(const QVariant&)));
|
||||
|
||||
|
@ -252,16 +274,40 @@ static QVariant createRandomMessage() {
|
|||
return QVariant::fromValue(message);
|
||||
}
|
||||
case 1: {
|
||||
TestMessageB message = { createRandomBytes(), createRandomSharedObject() };
|
||||
TestMessageB message = { createRandomBytes(), createRandomSharedObject(), getRandomTestEnum() };
|
||||
return QVariant::fromValue(message);
|
||||
}
|
||||
case 2:
|
||||
default: {
|
||||
return QVariant::fromValue(createRandomMessageC());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SharedObjectPointer mutate(const SharedObjectPointer& state) {
|
||||
switch(randIntInRange(0, 3)) {
|
||||
case 0: {
|
||||
SharedObjectPointer newState = state->clone(true);
|
||||
static_cast<TestSharedObjectA*>(newState.data())->setFoo(randFloat());
|
||||
objectMutationsPerformed++;
|
||||
return newState;
|
||||
}
|
||||
case 1: {
|
||||
SharedObjectPointer newState = state->clone(true);
|
||||
static_cast<TestSharedObjectA*>(newState.data())->setBaz(getRandomTestEnum());
|
||||
objectMutationsPerformed++;
|
||||
return newState;
|
||||
}
|
||||
case 2: {
|
||||
SharedObjectPointer newState = state->clone(true);
|
||||
static_cast<TestSharedObjectA*>(newState.data())->setBong(getRandomTestFlags());
|
||||
objectMutationsPerformed++;
|
||||
return newState;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMessage) {
|
||||
int type = firstMessage.userType();
|
||||
if (secondMessage.userType() != type) {
|
||||
|
@ -273,7 +319,7 @@ static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMe
|
|||
} else if (type == TestMessageB::Type) {
|
||||
TestMessageB first = firstMessage.value<TestMessageB>();
|
||||
TestMessageB second = secondMessage.value<TestMessageB>();
|
||||
return first.foo == second.foo && equals(first.bar, second.bar);
|
||||
return first.foo == second.foo && equals(first.bar, second.bar) && first.baz == second.baz;
|
||||
|
||||
} else if (type == TestMessageC::Type) {
|
||||
return firstMessage.value<TestMessageC>() == secondMessage.value<TestMessageC>();
|
||||
|
@ -320,10 +366,13 @@ bool Endpoint::simulate(int iterationNumber) {
|
|||
_reliableMessagesToSend -= 1.0f;
|
||||
}
|
||||
|
||||
// tweak the local state
|
||||
_localState = mutate(_localState);
|
||||
|
||||
// send a packet
|
||||
try {
|
||||
Bitstream& out = _sequencer->startPacket();
|
||||
SequencedTestMessage message = { iterationNumber, createRandomMessage() };
|
||||
SequencedTestMessage message = { iterationNumber, createRandomMessage(), _localState };
|
||||
_unreliableMessagesSent.append(message);
|
||||
unreliableMessagesSent++;
|
||||
out << message;
|
||||
|
@ -334,11 +383,16 @@ bool Endpoint::simulate(int iterationNumber) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// record the send
|
||||
SendRecord record = { _sequencer->getOutgoingPacketNumber(), _localState };
|
||||
_sendRecords.append(record);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Endpoint::sendDatagram(const QByteArray& datagram) {
|
||||
datagramsSent++;
|
||||
bytesSent += datagram.size();
|
||||
|
||||
// some datagrams are dropped
|
||||
const float DROP_PROBABILITY = 0.1f;
|
||||
|
@ -364,6 +418,7 @@ void Endpoint::sendDatagram(const QByteArray& datagram) {
|
|||
|
||||
_other->_sequencer->receivedDatagram(datagram);
|
||||
datagramsReceived++;
|
||||
bytesReceived += datagram.size();
|
||||
}
|
||||
|
||||
void Endpoint::handleHighPriorityMessage(const QVariant& message) {
|
||||
|
@ -384,12 +439,21 @@ void Endpoint::readMessage(Bitstream& in) {
|
|||
SequencedTestMessage message;
|
||||
in >> message;
|
||||
|
||||
_remoteState = message.state;
|
||||
|
||||
// record the receipt
|
||||
ReceiveRecord record = { _sequencer->getIncomingPacketNumber(), message.state };
|
||||
_receiveRecords.append(record);
|
||||
|
||||
for (QList<SequencedTestMessage>::iterator it = _other->_unreliableMessagesSent.begin();
|
||||
it != _other->_unreliableMessagesSent.end(); it++) {
|
||||
if (it->sequenceNumber == message.sequenceNumber) {
|
||||
if (!messagesEqual(it->submessage, message.submessage)) {
|
||||
throw QString("Sent/received unreliable message mismatch.");
|
||||
}
|
||||
if (!it->state->equals(message.state)) {
|
||||
throw QString("Delta-encoded object mismatch.");
|
||||
}
|
||||
_other->_unreliableMessagesSent.erase(_other->_unreliableMessagesSent.begin(), it + 1);
|
||||
unreliableMessagesReceived++;
|
||||
return;
|
||||
|
@ -427,6 +491,14 @@ void Endpoint::readReliableChannel() {
|
|||
streamedBytesReceived += bytes.size();
|
||||
}
|
||||
|
||||
void Endpoint::clearSendRecordsBefore(int index) {
|
||||
_sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1);
|
||||
}
|
||||
|
||||
void Endpoint::clearReceiveRecordsBefore(int index) {
|
||||
_receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1);
|
||||
}
|
||||
|
||||
TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) :
|
||||
_foo(foo),
|
||||
_baz(baz),
|
||||
|
|
|
@ -54,9 +54,30 @@ private slots:
|
|||
void handleReliableMessage(const QVariant& message);
|
||||
void readReliableChannel();
|
||||
|
||||
void clearSendRecordsBefore(int index);
|
||||
void clearReceiveRecordsBefore(int index);
|
||||
|
||||
private:
|
||||
|
||||
class SendRecord {
|
||||
public:
|
||||
int packetNumber;
|
||||
SharedObjectPointer localState;
|
||||
};
|
||||
|
||||
class ReceiveRecord {
|
||||
public:
|
||||
int packetNumber;
|
||||
SharedObjectPointer remoteState;
|
||||
};
|
||||
|
||||
DatagramSequencer* _sequencer;
|
||||
QList<SendRecord> _sendRecords;
|
||||
QList<ReceiveRecord> _receiveRecords;
|
||||
|
||||
SharedObjectPointer _localState;
|
||||
SharedObjectPointer _remoteState;
|
||||
|
||||
Endpoint* _other;
|
||||
QList<QPair<QByteArray, int> > _delayedDatagrams;
|
||||
float _highPriorityMessagesToSend;
|
||||
|
@ -106,6 +127,8 @@ private:
|
|||
TestFlags _bong;
|
||||
};
|
||||
|
||||
DECLARE_ENUM_METATYPE(TestSharedObjectA, TestEnum)
|
||||
|
||||
/// Another simple shared object.
|
||||
class TestSharedObjectB : public SharedObject {
|
||||
Q_OBJECT
|
||||
|
@ -169,6 +192,7 @@ public:
|
|||
|
||||
STREAM QByteArray foo;
|
||||
STREAM SharedObjectPointer bar;
|
||||
STREAM TestSharedObjectA::TestEnum baz;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(TestMessageB)
|
||||
|
@ -192,6 +216,7 @@ public:
|
|||
|
||||
STREAM int sequenceNumber;
|
||||
STREAM QVariant submessage;
|
||||
STREAM SharedObjectPointer state;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(SequencedTestMessage)
|
||||
|
|
Loading…
Reference in a new issue