diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 3bf1632074..d0c0d4c781 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -69,7 +69,7 @@ void MetavoxelServer::readPendingDatagrams() { void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) { if (node->getType() == NodeType::Agent) { QMutexLocker locker(&node->getMutex()); - node->setLinkedData(new MetavoxelSession(this, NodeList::getInstance()->nodeWithUUID(node->getUUID()))); + node->setLinkedData(new MetavoxelSession(node, this)); } } @@ -77,7 +77,7 @@ void MetavoxelServer::sendDeltas() { // send deltas for all sessions foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::Agent) { - static_cast(node->getLinkedData())->sendDelta(); + static_cast(node->getLinkedData())->update(); } } @@ -89,59 +89,34 @@ void MetavoxelServer::sendDeltas() { _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - elapsed)); } -MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const SharedNodePointer& node) : - _server(server), - _sequencer(byteArrayWithPopulatedHeader(PacketTypeMetavoxelData)), - _node(node) { +MetavoxelSession::MetavoxelSession(const SharedNodePointer& node, MetavoxelServer* server) : + Endpoint(node, new PacketRecord(), NULL), + _server(server) { - connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); - connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); - connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int))); connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleMessage(const QVariant&))); connect(_sequencer.getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)), SLOT(handleMessage(const QVariant&))); - - // insert the baseline send record - SendRecord record = { 0 }; - _sendRecords.append(record); } -MetavoxelSession::~MetavoxelSession() { -} - -int MetavoxelSession::parseData(const QByteArray& packet) { - // process through sequencer - _sequencer.receivedDatagram(packet); - return packet.size(); -} - -void MetavoxelSession::sendDelta() { +void MetavoxelSession::update() { // wait until we have a valid lod - if (!_lod.isValid()) { - return; + if (_lod.isValid()) { + Endpoint::update(); } - Bitstream& out = _sequencer.startPacket(); +} + +void MetavoxelSession::writeUpdateMessage(Bitstream& out) { out << QVariant::fromValue(MetavoxelDeltaMessage()); - _server->getData().writeDelta(_sendRecords.first().data, _sendRecords.first().lod, out, _lod); - _sequencer.endPacket(); - - // record the send - SendRecord record = { _sequencer.getOutgoingPacketNumber(), _server->getData(), _lod }; - _sendRecords.append(record); + PacketRecord* sendRecord = getLastAcknowledgedSendRecord(); + _server->getData().writeDelta(sendRecord->getData(), sendRecord->getLOD(), out, _lod); } -void MetavoxelSession::sendData(const QByteArray& data) { - NodeList::getInstance()->writeDatagram(data, _node); -} - -void MetavoxelSession::readPacket(Bitstream& in) { - QVariant message; - in >> message; +void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { handleMessage(message); } -void MetavoxelSession::clearSendRecordsBefore(int index) { - _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1); +PacketRecord* MetavoxelSession::maybeCreateSendRecord() const { + return new PacketRecord(_lod, _server->getData()); } void MetavoxelSession::handleMessage(const QVariant& message) { diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index b01bb9b412..d9b010e282 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -17,8 +17,7 @@ #include -#include -#include +#include class MetavoxelEditMessage; class MetavoxelSession; @@ -53,46 +52,31 @@ private: }; /// Contains the state of a single client session. -class MetavoxelSession : public NodeData { +class MetavoxelSession : public Endpoint { Q_OBJECT public: - MetavoxelSession(MetavoxelServer* server, const SharedNodePointer& node); - virtual ~MetavoxelSession(); + MetavoxelSession(const SharedNodePointer& node, MetavoxelServer* server); - virtual int parseData(const QByteArray& packet); + virtual void update(); - void sendDelta(); +protected: + + virtual void writeUpdateMessage(Bitstream& out); + virtual void handleMessage(const QVariant& message, Bitstream& in); + + virtual PacketRecord* maybeCreateSendRecord() const; private slots: - void sendData(const QByteArray& data); - - void readPacket(Bitstream& in); - - void clearSendRecordsBefore(int index); - void handleMessage(const QVariant& message); private: - class SendRecord { - public: - int packetNumber; - MetavoxelData data; - MetavoxelLOD lod; - }; - MetavoxelServer* _server; - DatagramSequencer _sequencer; - - SharedNodePointer _node; - MetavoxelLOD _lod; - - QList _sendRecords; }; #endif // hifi_MetavoxelServer_h diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 78c97c1703..7a5119a62d 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -35,6 +35,8 @@ MetavoxelSystem::MetavoxelSystem() : } void MetavoxelSystem::init() { + MetavoxelClientManager::init(); + if (!_program.isLinked()) { _program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert"); _program.link(); @@ -43,62 +45,19 @@ void MetavoxelSystem::init() { } _buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); _buffer.create(); - - connect(NodeList::getInstance(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachClient(const SharedNodePointer&))); } -SharedObjectPointer MetavoxelSystem::findFirstRaySpannerIntersection( - const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance) { - SharedObjectPointer closestSpanner; - float closestDistance = FLT_MAX; - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = static_cast(node->getLinkedData()); - if (client) { - float clientDistance; - SharedObjectPointer clientSpanner = client->getData().findFirstRaySpannerIntersection( - origin, direction, attribute, clientDistance); - if (clientSpanner && clientDistance < closestDistance) { - closestSpanner = clientSpanner; - closestDistance = clientDistance; - } - } - } - } - if (closestSpanner) { - distance = closestDistance; - } - return closestSpanner; -} - -void MetavoxelSystem::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = static_cast(node->getLinkedData()); - if (client) { - client->applyEdit(edit, reliable); - } - } - } +MetavoxelLOD MetavoxelSystem::getLOD() const { + const float FIXED_LOD_THRESHOLD = 0.01f; + return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD); } void MetavoxelSystem::simulate(float deltaTime) { - // simulate the clients + // update the clients _points.clear(); _simulateVisitor.setDeltaTime(deltaTime); _simulateVisitor.setOrder(-Application::getInstance()->getViewFrustum()->getDirection()); - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = static_cast(node->getLinkedData()); - if (client) { - client->simulate(deltaTime); - client->guide(_simulateVisitor); - } - } - } + update(); _buffer.bind(); int bytes = _points.size() * sizeof(Point); @@ -153,7 +112,7 @@ void MetavoxelSystem::render() { foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = static_cast(node->getLinkedData()); + MetavoxelSystemClient* client = static_cast(node->getLinkedData()); if (client) { client->guide(_renderVisitor); } @@ -161,11 +120,13 @@ void MetavoxelSystem::render() { } } -void MetavoxelSystem::maybeAttachClient(const SharedNodePointer& node) { - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - node->setLinkedData(new MetavoxelClient(NodeList::getInstance()->nodeWithUUID(node->getUUID()))); - } +MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { + return new MetavoxelSystemClient(node, this); +} + +void MetavoxelSystem::updateClient(MetavoxelClient* client) { + MetavoxelClientManager::updateClient(client); + client->guide(_simulateVisitor); } MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector& points) : @@ -235,115 +196,22 @@ bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner, const glm::vec3& cl return true; } -MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) : - _node(node), - _sequencer(byteArrayWithPopulatedHeader(PacketTypeMetavoxelData)) { - - connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); - connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); - 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, _data }; - _receiveRecords.append(receiveRecord); +MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelSystem* system) : + MetavoxelClient(node, system) { } -MetavoxelClient::~MetavoxelClient() { - // close the session - Bitstream& out = _sequencer.startPacket(); - out << QVariant::fromValue(CloseSessionMessage()); - _sequencer.endPacket(); -} - -static MetavoxelLOD getLOD() { - const float FIXED_LOD_THRESHOLD = 0.01f; - return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD); -} - -void MetavoxelClient::guide(MetavoxelVisitor& visitor) { - visitor.setLOD(getLOD()); - _data.guide(visitor); -} - -void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { - if (reliable) { - _sequencer.getReliableOutputChannel()->sendMessage(QVariant::fromValue(edit)); - - } else { - // apply immediately to local tree - edit.apply(_data, _sequencer.getWeakSharedObjectHash()); - - // start sending it out - _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); - } -} - -void MetavoxelClient::simulate(float deltaTime) { - Bitstream& out = _sequencer.startPacket(); - - ClientStateMessage state = { getLOD() }; - out << QVariant::fromValue(state); - _sequencer.endPacket(); - - // record the send - SendRecord record = { _sequencer.getOutgoingPacketNumber(), state.lod }; - _sendRecords.append(record); -} - -int MetavoxelClient::parseData(const QByteArray& packet) { +int MetavoxelSystemClient::parseData(const QByteArray& packet) { // process through sequencer QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size()); return packet.size(); } -void MetavoxelClient::sendData(const QByteArray& data) { +void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { NodeList::getInstance()->writeDatagram(data, _node); Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); } -void MetavoxelClient::readPacket(Bitstream& in) { - QVariant message; - in >> message; - handleMessage(message, in); - - // record the receipt - ReceiveRecord record = { _sequencer.getIncomingPacketNumber(), _data, _sendRecords.first().lod }; - _receiveRecords.append(record); - - // reapply local edits - foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) { - if (message.data.userType() == MetavoxelEditMessage::Type) { - message.data.value().apply(_data, _sequencer.getWeakSharedObjectHash()); - } - } -} - -void MetavoxelClient::clearSendRecordsBefore(int index) { - _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1); -} - -void MetavoxelClient::clearReceiveRecordsBefore(int index) { - _receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1); -} - -void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { - int userType = message.userType(); - if (userType == MetavoxelDeltaMessage::Type) { - _data.readDelta(_receiveRecords.first().data, _receiveRecords.first().lod, in, _sendRecords.first().lod); - - } else if (userType == QMetaType::QVariantList) { - foreach (const QVariant& element, message.toList()) { - handleMessage(element, in); - } - } -} - static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { GLdouble coefficients[] = { x, y, z, w }; glClipPlane(plane, coefficients); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index f98a260ab1..f3a4fd4412 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -18,37 +18,31 @@ #include -#include - -#include -#include -#include +#include #include "renderer/ProgramObject.h" class Model; /// Renders a metavoxel tree. -class MetavoxelSystem : public QObject { +class MetavoxelSystem : public MetavoxelClientManager { Q_OBJECT public: MetavoxelSystem(); - void init(); - - SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, - const AttributePointer& attribute, float& distance); - - Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); + virtual void init(); + + virtual MetavoxelLOD getLOD() const; void simulate(float deltaTime); void render(); - -private slots: - void maybeAttachClient(const SharedNodePointer& node); +protected: + + virtual MetavoxelClient* createClient(const SharedNodePointer& node); + virtual void updateClient(MetavoxelClient* client); private: @@ -89,59 +83,18 @@ private: }; /// A client session associated with a single server. -class MetavoxelClient : public NodeData { +class MetavoxelSystemClient : public MetavoxelClient { Q_OBJECT public: - MetavoxelClient(const SharedNodePointer& node); - virtual ~MetavoxelClient(); - - MetavoxelData& getData() { return _data; } - - void guide(MetavoxelVisitor& visitor); - - void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); - - void simulate(float deltaTime); - + MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelSystem* system); + virtual int parseData(const QByteArray& packet); -private slots: +protected: - void sendData(const QByteArray& data); - - void readPacket(Bitstream& in); - - void clearSendRecordsBefore(int index); - - void clearReceiveRecordsBefore(int index); - -private: - - void handleMessage(const QVariant& message, Bitstream& in); - - class SendRecord { - public: - int packetNumber; - MetavoxelLOD lod; - }; - - class ReceiveRecord { - public: - int packetNumber; - MetavoxelData data; - MetavoxelLOD lod; - }; - - SharedNodePointer _node; - - DatagramSequencer _sequencer; - - MetavoxelData _data; - - QList _sendRecords; - QList _receiveRecords; + virtual void sendDatagram(const QByteArray& data); }; /// Base class for spanner renderers; provides clipping. diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index c1deb20111..1f0c2498c5 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -29,6 +29,7 @@ #include #include +#include #include "Application.h" #include "MetavoxelEditor.h" diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 0ef86e85c0..d18903f923 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1414,8 +1414,10 @@ Bitstream& Bitstream::operator<(const SharedObjectPointer& object) { *this << object->getOriginID(); QPointer reference = _sharedObjectReferences.value(object->getOriginID()); if (reference) { + *this << true; writeRawDelta((const QObject*)object.data(), (const QObject*)reference.data()); } else { + *this << false; *this << (QObject*)object.data(); } return *this; @@ -1430,19 +1432,27 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { } int originID; *this >> originID; + bool delta; + *this >> delta; QPointer reference = _sharedObjectReferences.value(originID); QPointer& pointer = _weakSharedObjectHash[id]; if (pointer) { ObjectStreamerPointer objectStreamer; _objectStreamerStreamer >> objectStreamer; - if (reference) { + if (delta) { + if (!reference) { + qWarning() << "Delta without reference" << id << originID; + } objectStreamer->readRawDelta(*this, reference.data(), pointer.data()); } else { objectStreamer->read(*this, pointer.data()); } } else { QObject* rawObject; - if (reference) { + if (delta) { + if (!reference) { + qWarning() << "Delta without reference" << id << originID; + } readRawDelta(rawObject, (const QObject*)reference.data()); } else { *this >> rawObject; diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index a75a4bf95b..eb02497321 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -140,6 +140,12 @@ void DatagramSequencer::endPacket() { _outgoingPacketStream.device()->seek(0); } +void DatagramSequencer::cancelPacket() { + _outputStream.reset(); + _outputStream.getAndResetWriteMappings(); + _outgoingPacketStream.device()->seek(0); +} + /// Simple RAII-style object to keep a device open when in scope. class QIODeviceOpener { public: diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 9a4cd1334b..aa8b6907ff 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -111,6 +111,9 @@ public: /// Sends the packet currently being written. void endPacket(); + /// Cancels the packet currently being written. + void cancelPacket(); + /// Processes a datagram received from the other party, emitting readyToRead when the entire packet /// has been successfully assembled. Q_INVOKABLE void receivedDatagram(const QByteArray& datagram); diff --git a/libraries/metavoxels/src/Endpoint.cpp b/libraries/metavoxels/src/Endpoint.cpp new file mode 100644 index 0000000000..c656054504 --- /dev/null +++ b/libraries/metavoxels/src/Endpoint.cpp @@ -0,0 +1,109 @@ +// +// Endpoint.cpp +// libraries/metavoxels/src +// +// Created by Andrzej Kapolka on 6/26/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "Endpoint.h" + +Endpoint::Endpoint(const SharedNodePointer& node, PacketRecord* baselineSendRecord, PacketRecord* baselineReceiveRecord) : + _node(node), + _sequencer(byteArrayWithPopulatedHeader(PacketTypeMetavoxelData)) { + + connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendDatagram(const QByteArray&))); + connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&))); + connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int))); + connect(&_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int))); + + // insert the baseline send and receive records + _sendRecords.append(baselineSendRecord); + _receiveRecords.append(baselineReceiveRecord); +} + +Endpoint::~Endpoint() { + foreach (PacketRecord* record, _sendRecords) { + delete record; + } + foreach (PacketRecord* record, _receiveRecords) { + delete record; + } +} + +void Endpoint::update() { + Bitstream& out = _sequencer.startPacket(); + writeUpdateMessage(out); + _sequencer.endPacket(); + + // record the send + _sendRecords.append(maybeCreateSendRecord()); +} + +int Endpoint::parseData(const QByteArray& packet) { + // process through sequencer + _sequencer.receivedDatagram(packet); + return packet.size(); +} + +void Endpoint::sendDatagram(const QByteArray& data) { + NodeList::getInstance()->writeDatagram(data, _node); +} + +void Endpoint::readMessage(Bitstream& in) { + QVariant message; + in >> message; + handleMessage(message, in); + + // record the receipt + _receiveRecords.append(maybeCreateReceiveRecord()); +} + +void Endpoint::clearSendRecordsBefore(int index) { + QList::iterator end = _sendRecords.begin() + index + 1; + for (QList::const_iterator it = _sendRecords.begin(); it != end; it++) { + delete *it; + } + _sendRecords.erase(_sendRecords.begin(), end); +} + +void Endpoint::clearReceiveRecordsBefore(int index) { + QList::iterator end = _receiveRecords.begin() + index + 1; + for (QList::const_iterator it = _receiveRecords.begin(); it != end; it++) { + delete *it; + } + _receiveRecords.erase(_receiveRecords.begin(), end); +} + +void Endpoint::writeUpdateMessage(Bitstream& out) { + out << QVariant(); +} + +void Endpoint::handleMessage(const QVariant& message, Bitstream& in) { + if (message.userType() == QMetaType::QVariantList) { + foreach (const QVariant& element, message.toList()) { + handleMessage(element, in); + } + } +} + +PacketRecord* Endpoint::maybeCreateSendRecord() const { + return NULL; +} + +PacketRecord* Endpoint::maybeCreateReceiveRecord() const { + return NULL; +} + +PacketRecord::PacketRecord(const MetavoxelLOD& lod, const MetavoxelData& data) : + _lod(lod), + _data(data) { +} + +PacketRecord::~PacketRecord() { +} diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h new file mode 100644 index 0000000000..d253a69ded --- /dev/null +++ b/libraries/metavoxels/src/Endpoint.h @@ -0,0 +1,78 @@ +// +// Endpoint.h +// libraries/metavoxels/src +// +// Created by Andrzej Kapolka on 6/26/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Endpoint_h +#define hifi_Endpoint_h + +#include + +#include "DatagramSequencer.h" +#include "MetavoxelData.h" + +class PacketRecord; + +/// Base class for communication endpoints: clients and server sessions. +class Endpoint : public NodeData { + Q_OBJECT + +public: + + Endpoint(const SharedNodePointer& node, PacketRecord* baselineSendRecord = NULL, + PacketRecord* baselineReceiveRecord = NULL); + virtual ~Endpoint(); + + virtual void update(); + + virtual int parseData(const QByteArray& packet); + +protected slots: + + virtual void sendDatagram(const QByteArray& data); + virtual void readMessage(Bitstream& in); + + void clearSendRecordsBefore(int index); + void clearReceiveRecordsBefore(int index); + +protected: + + virtual void writeUpdateMessage(Bitstream& out); + virtual void handleMessage(const QVariant& message, Bitstream& in); + + virtual PacketRecord* maybeCreateSendRecord() const; + virtual PacketRecord* maybeCreateReceiveRecord() const; + + PacketRecord* getLastAcknowledgedSendRecord() const { return _sendRecords.first(); } + PacketRecord* getLastAcknowledgedReceiveRecord() const { return _receiveRecords.first(); } + + SharedNodePointer _node; + DatagramSequencer _sequencer; + + QList _sendRecords; + QList _receiveRecords; +}; + +/// Base class for packet records. +class PacketRecord { +public: + + PacketRecord(const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData()); + virtual ~PacketRecord(); + + const MetavoxelLOD& getLOD() const { return _lod; } + const MetavoxelData& getData() const { return _data; } + +private: + + MetavoxelLOD _lod; + MetavoxelData _data; +}; + +#endif // hifi_Endpoint_h diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp new file mode 100644 index 0000000000..008a477187 --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -0,0 +1,142 @@ +// +// MetavoxelClientManager.cpp +// libraries/metavoxels/src +// +// Created by Andrzej Kapolka on 6/26/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MetavoxelClientManager.h" +#include "MetavoxelMessages.h" + +void MetavoxelClientManager::init() { + connect(NodeList::getInstance(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachClient(const SharedNodePointer&))); +} + +void MetavoxelClientManager::update() { + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getType() == NodeType::MetavoxelServer) { + QMutexLocker locker(&node->getMutex()); + MetavoxelClient* client = static_cast(node->getLinkedData()); + if (client) { + updateClient(client); + } + } + } +} + +SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(const glm::vec3& origin, + const glm::vec3& direction, const AttributePointer& attribute, float& distance) { + SharedObjectPointer closestSpanner; + float closestDistance = FLT_MAX; + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getType() == NodeType::MetavoxelServer) { + QMutexLocker locker(&node->getMutex()); + MetavoxelClient* client = static_cast(node->getLinkedData()); + if (client) { + float clientDistance; + SharedObjectPointer clientSpanner = client->getData().findFirstRaySpannerIntersection( + origin, direction, attribute, clientDistance); + if (clientSpanner && clientDistance < closestDistance) { + closestSpanner = clientSpanner; + closestDistance = clientDistance; + } + } + } + } + if (closestSpanner) { + distance = closestDistance; + } + return closestSpanner; +} + +void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getType() == NodeType::MetavoxelServer) { + QMutexLocker locker(&node->getMutex()); + MetavoxelClient* client = static_cast(node->getLinkedData()); + if (client) { + client->applyEdit(edit, reliable); + } + } + } +} + +MetavoxelLOD MetavoxelClientManager::getLOD() const { + return MetavoxelLOD(); +} + +void MetavoxelClientManager::maybeAttachClient(const SharedNodePointer& node) { + if (node->getType() == NodeType::MetavoxelServer) { + QMutexLocker locker(&node->getMutex()); + node->setLinkedData(createClient(node)); + } +} + +MetavoxelClient* MetavoxelClientManager::createClient(const SharedNodePointer& node) { + return new MetavoxelClient(node, this); +} + +void MetavoxelClientManager::updateClient(MetavoxelClient* client) { + client->update(); +} + +MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientManager* manager) : + Endpoint(node, new PacketRecord(), new PacketRecord()), + _manager(manager) { +} + +void MetavoxelClient::guide(MetavoxelVisitor& visitor) { + visitor.setLOD(_manager->getLOD()); + _data.guide(visitor); +} + +void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { + if (reliable) { + _sequencer.getReliableOutputChannel()->sendMessage(QVariant::fromValue(edit)); + + } else { + // apply immediately to local tree + edit.apply(_data, _sequencer.getWeakSharedObjectHash()); + + // start sending it out + _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); + } +} + +void MetavoxelClient::writeUpdateMessage(Bitstream& out) { + ClientStateMessage state = { _manager->getLOD() }; + out << QVariant::fromValue(state); +} + +void MetavoxelClient::readMessage(Bitstream& in) { + Endpoint::readMessage(in); + + // reapply local edits + foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) { + if (message.data.userType() == MetavoxelEditMessage::Type) { + message.data.value().apply(_data, _sequencer.getWeakSharedObjectHash()); + } + } +} + +void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { + if (message.userType() == MetavoxelDeltaMessage::Type) { + PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); + _data.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, getLastAcknowledgedSendRecord()->getLOD()); + + } else { + Endpoint::handleMessage(message, in); + } +} + +PacketRecord* MetavoxelClient::maybeCreateSendRecord() const { + return new PacketRecord(_manager->getLOD()); +} + +PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const { + return new PacketRecord(getLastAcknowledgedSendRecord()->getLOD(), _data); +} diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h new file mode 100644 index 0000000000..dd11e871ec --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -0,0 +1,75 @@ +// +// MetavoxelClientManager.h +// libraries/metavoxels/src +// +// Created by Andrzej Kapolka on 6/26/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MetavoxelClientManager_h +#define hifi_MetavoxelClientManager_h + +#include "Endpoint.h" + +class MetavoxelClient; +class MetavoxelEditMessage; + +/// Manages the set of connected metavoxel clients. +class MetavoxelClientManager : public QObject { + Q_OBJECT + +public: + + virtual void init(); + void update(); + + SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, + const AttributePointer& attribute, float& distance); + + Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); + + virtual MetavoxelLOD getLOD() const; + +private slots: + + void maybeAttachClient(const SharedNodePointer& node); + +protected: + + virtual MetavoxelClient* createClient(const SharedNodePointer& node); + virtual void updateClient(MetavoxelClient* client); +}; + +/// Base class for metavoxel clients. +class MetavoxelClient : public Endpoint { + Q_OBJECT + +public: + + MetavoxelClient(const SharedNodePointer& node, MetavoxelClientManager* manager); + + MetavoxelData& getData() { return _data; } + + void guide(MetavoxelVisitor& visitor); + + void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); + +protected: + + virtual void writeUpdateMessage(Bitstream& out); + virtual void readMessage(Bitstream& in); + virtual void handleMessage(const QVariant& message, Bitstream& in); + + virtual PacketRecord* maybeCreateSendRecord() const; + virtual PacketRecord* maybeCreateReceiveRecord() const; + +private: + + MetavoxelClientManager* _manager; + MetavoxelData _data; +}; + +#endif // hifi_MetavoxelClientManager_h diff --git a/tests/metavoxels/CMakeLists.txt b/tests/metavoxels/CMakeLists.txt index 5f11c7290e..ca141c5137 100644 --- a/tests/metavoxels/CMakeLists.txt +++ b/tests/metavoxels/CMakeLists.txt @@ -27,6 +27,7 @@ include_glm(${TARGET_NAME} "${ROOT_DIR}") # link in the shared libraries include(${MACRO_DIR}/LinkHifiLibrary.cmake) link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") IF (WIN32) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 68aaf7ec70..61ab664310 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -440,6 +440,8 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { } bool MetavoxelTests::run() { + LimitedNodeList::createInstance(); + // seed the random number generator so that our tests are reproducible srand(0xBAAAAABE); @@ -456,14 +458,13 @@ bool MetavoxelTests::run() { } } - QByteArray datagramHeader("testheader"); const int SIMULATION_ITERATIONS = 10000; if (test == 0 || test == 2) { qDebug() << "Running transmission test..."; qDebug(); // create two endpoints with the same header - Endpoint alice(datagramHeader), bob(datagramHeader); + TestEndpoint alice, bob; alice.setOther(&bob); bob.setOther(&alice); @@ -497,7 +498,7 @@ bool MetavoxelTests::run() { datagramsReceived = bytesReceived = maxDatagramsPerPacket = maxBytesPerPacket = 0; // create two endpoints with the same header - Endpoint alice(datagramHeader, Endpoint::CONGESTION_MODE), bob(datagramHeader, Endpoint::CONGESTION_MODE); + TestEndpoint alice(TestEndpoint::CONGESTION_MODE), bob(TestEndpoint::CONGESTION_MODE); alice.setOther(&bob); bob.setOther(&alice); @@ -537,8 +538,8 @@ bool MetavoxelTests::run() { datagramsSent = bytesSent = datagramsReceived = bytesReceived = maxDatagramsPerPacket = maxBytesPerPacket = 0; // create client and server endpoints - Endpoint client(datagramHeader, Endpoint::METAVOXEL_CLIENT_MODE); - Endpoint server(datagramHeader, Endpoint::METAVOXEL_SERVER_MODE); + TestEndpoint client(TestEndpoint::METAVOXEL_CLIENT_MODE); + TestEndpoint server(TestEndpoint::METAVOXEL_SERVER_MODE); client.setOther(&server); server.setOther(&client); @@ -599,28 +600,57 @@ int RandomVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } -Endpoint::Endpoint(const QByteArray& datagramHeader, Mode mode) : +class TestSendRecord : public PacketRecord { +public: + + TestSendRecord(const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData(), + const SharedObjectPointer& localState = SharedObjectPointer(), int packetNumber = 0); + + const SharedObjectPointer& getLocalState() const { return _localState; } + int getPacketNumber() const { return _packetNumber; } + +private: + + SharedObjectPointer _localState; + int _packetNumber; + +}; + +TestSendRecord::TestSendRecord(const MetavoxelLOD& lod, const MetavoxelData& data, + const SharedObjectPointer& localState, int packetNumber) : + PacketRecord(lod, data), + _localState(localState), + _packetNumber(packetNumber) { +} + +class TestReceiveRecord : public PacketRecord { +public: + + TestReceiveRecord(const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData(), + const SharedObjectPointer& remoteState = SharedObjectPointer()); + + const SharedObjectPointer& getRemoteState() const { return _remoteState; } + +private: + + SharedObjectPointer _remoteState; +}; + +TestReceiveRecord::TestReceiveRecord(const MetavoxelLOD& lod, + const MetavoxelData& data, const SharedObjectPointer& remoteState) : + PacketRecord(lod, data), + _remoteState(remoteState) { +} + +TestEndpoint::TestEndpoint(Mode mode) : + Endpoint(SharedNodePointer(), new TestSendRecord(), new TestReceiveRecord()), _mode(mode), - _sequencer(new DatagramSequencer(datagramHeader, this)), _highPriorityMessagesToSend(0.0f), _reliableMessagesToSend(0.0f) { - connect(_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendDatagram(const QByteArray&))); - connect(_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&))); - connect(_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), + 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); - if (mode == METAVOXEL_CLIENT_MODE) { _lod = MetavoxelLOD(glm::vec3(), 0.01f); return; @@ -643,15 +673,15 @@ Endpoint::Endpoint(const QByteArray& datagramHeader, Mode mode) : // create the object that represents out delta-encoded state _localState = new TestSharedObjectA(); - connect(_sequencer->getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)), + connect(_sequencer.getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)), SLOT(handleReliableMessage(const QVariant&))); - ReliableChannel* secondInput = _sequencer->getReliableInputChannel(1); + ReliableChannel* secondInput = _sequencer.getReliableInputChannel(1); secondInput->setMessagesEnabled(false); connect(&secondInput->getBuffer(), SIGNAL(readyRead()), SLOT(readReliableChannel())); // enqueue a large amount of data in a low-priority channel - ReliableChannel* output = _sequencer->getReliableOutputChannel(1); + ReliableChannel* output = _sequencer.getReliableOutputChannel(1); output->setPriority(0.25f); output->setMessagesEnabled(false); QByteArray bytes; @@ -800,11 +830,11 @@ int MutateVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } -bool Endpoint::simulate(int iterationNumber) { +bool TestEndpoint::simulate(int iterationNumber) { // update/send our delayed datagrams for (QList::iterator it = _delayedDatagrams.begin(); it != _delayedDatagrams.end(); ) { if (it->second-- == 1) { - _other->receiveDatagram(it->first); + _other->parseData(it->first); it = _delayedDatagrams.erase(it); } else { @@ -819,41 +849,39 @@ bool Endpoint::simulate(int iterationNumber) { ByteArrayVector datagrams = _pipeline.takeLast(); _pipeline.prepend(ByteArrayVector()); foreach (const QByteArray& datagram, datagrams) { - _sequencer->receivedDatagram(datagram); + _sequencer.receivedDatagram(datagram); datagramsReceived++; bytesReceived += datagram.size(); _remainingPipelineCapacity += datagram.size(); } - int packetCount = _sequencer->startPacketGroup(); + int packetCount = _sequencer.startPacketGroup(); groupsSent++; maxPacketsPerGroup = qMax(maxPacketsPerGroup, packetCount); for (int i = 0; i < packetCount; i++) { oldDatagramsSent = datagramsSent; oldBytesSent = bytesSent; - Bitstream& out = _sequencer->startPacket(); + Bitstream& out = _sequencer.startPacket(); out << QVariant(); - _sequencer->endPacket(); + _sequencer.endPacket(); maxDatagramsPerPacket = qMax(maxDatagramsPerPacket, datagramsSent - oldDatagramsSent); maxBytesPerPacket = qMax(maxBytesPerPacket, bytesSent - oldBytesSent); // record the send - SendRecord record = { _sequencer->getOutgoingPacketNumber() }; - _sendRecords.append(record); + _sendRecords.append(maybeCreateSendRecord()); } return false; } else if (_mode == METAVOXEL_CLIENT_MODE) { - Bitstream& out = _sequencer->startPacket(); + Bitstream& out = _sequencer.startPacket(); ClientStateMessage state = { _lod }; out << QVariant::fromValue(state); - _sequencer->endPacket(); + _sequencer.endPacket(); // record the send - SendRecord record = { _sequencer->getOutgoingPacketNumber(), SharedObjectPointer(), MetavoxelData(), _lod }; - _sendRecords.append(record); + _sendRecords.append(maybeCreateSendRecord()); } else if (_mode == METAVOXEL_SERVER_MODE) { // make a random change @@ -879,16 +907,15 @@ bool Endpoint::simulate(int iterationNumber) { if (!_lod.isValid()) { return false; } - Bitstream& out = _sequencer->startPacket(); + Bitstream& out = _sequencer.startPacket(); out << QVariant::fromValue(MetavoxelDeltaMessage()); - _data.writeDelta(_sendRecords.first().data, _sendRecords.first().lod, out, _lod); - + PacketRecord* sendRecord = getLastAcknowledgedSendRecord(); + _data.writeDelta(sendRecord->getData(), sendRecord->getLOD(), out, _lod); + _sequencer.endPacket(); + // record the send - SendRecord record = { _sequencer->getOutgoingPacketNumber() + 1, SharedObjectPointer(), _data, _lod }; - _sendRecords.append(record); - - _sequencer->endPacket(); - + _sendRecords.append(maybeCreateSendRecord()); + } else { // enqueue some number of high priority messages const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f; @@ -897,7 +924,7 @@ bool Endpoint::simulate(int iterationNumber) { while (_highPriorityMessagesToSend >= 1.0f) { QVariant message = createRandomMessage(); _highPriorityMessagesSent.append(message); - _sequencer->sendHighPriorityMessage(message); + _sequencer.sendHighPriorityMessage(message); highPriorityMessagesSent++; _highPriorityMessagesToSend -= 1.0f; } @@ -909,7 +936,7 @@ bool Endpoint::simulate(int iterationNumber) { while (_reliableMessagesToSend >= 1.0f) { QVariant message = createRandomMessage(); _reliableMessagesSent.append(message); - _sequencer->getReliableOutputChannel()->sendMessage(message); + _sequencer.getReliableOutputChannel()->sendMessage(message); reliableMessagesSent++; _reliableMessagesToSend -= 1.0f; } @@ -919,12 +946,12 @@ bool Endpoint::simulate(int iterationNumber) { // send a packet try { - Bitstream& out = _sequencer->startPacket(); + Bitstream& out = _sequencer.startPacket(); SequencedTestMessage message = { iterationNumber, createRandomMessage(), _localState }; _unreliableMessagesSent.append(message); unreliableMessagesSent++; out << message; - _sequencer->endPacket(); + _sequencer.endPacket(); } catch (const QString& message) { qDebug() << message; @@ -932,15 +959,29 @@ bool Endpoint::simulate(int iterationNumber) { } // record the send - SendRecord record = { _sequencer->getOutgoingPacketNumber(), _localState }; - _sendRecords.append(record); + _sendRecords.append(maybeCreateSendRecord()); } maxDatagramsPerPacket = qMax(maxDatagramsPerPacket, datagramsSent - oldDatagramsSent); maxBytesPerPacket = qMax(maxBytesPerPacket, bytesSent - oldBytesSent); return false; } -void Endpoint::sendDatagram(const QByteArray& datagram) { +int TestEndpoint::parseData(const QByteArray& packet) { + if (_mode == CONGESTION_MODE) { + if (packet.size() <= _remainingPipelineCapacity) { + // have to copy the datagram; the one we're passed is a reference to a shared buffer + _pipeline[0].append(QByteArray(packet.constData(), packet.size())); + _remainingPipelineCapacity -= packet.size(); + } + } else { + _sequencer.receivedDatagram(packet); + datagramsReceived++; + bytesReceived += packet.size(); + } + return packet.size(); +} + +void TestEndpoint::sendDatagram(const QByteArray& datagram) { datagramsSent++; bytesSent += datagram.size(); @@ -967,31 +1008,16 @@ void Endpoint::sendDatagram(const QByteArray& datagram) { } } - _other->receiveDatagram(datagram); + _other->parseData(datagram); } -void Endpoint::handleHighPriorityMessage(const QVariant& message) { - if (message.userType() == ClearSharedObjectMessage::Type) { - return; - } - if (_other->_highPriorityMessagesSent.isEmpty()) { - throw QString("Received unsent/already sent high priority message."); - } - QVariant sentMessage = _other->_highPriorityMessagesSent.takeFirst(); - if (!messagesEqual(message, sentMessage)) { - throw QString("Sent/received high priority message mismatch."); - } - highPriorityMessagesReceived++; -} - -void Endpoint::readMessage(Bitstream& in) { +void TestEndpoint::readMessage(Bitstream& in) { if (_mode == CONGESTION_MODE) { QVariant message; in >> message; // record the receipt - ReceiveRecord record = { _sequencer->getIncomingPacketNumber() }; - _receiveRecords.append(record); + _receiveRecords.append(maybeCreateReceiveRecord()); return; } if (_mode == METAVOXEL_CLIENT_MODE) { @@ -1000,10 +1026,11 @@ void Endpoint::readMessage(Bitstream& in) { handleMessage(message, in); // deep-compare data to sent version - int packetNumber = _sequencer->getIncomingPacketNumber(); - foreach (const SendRecord& sendRecord, _other->_sendRecords) { - if (sendRecord.packetNumber == packetNumber) { - if (!sendRecord.data.deepEquals(_data, _sendRecords.first().lod)) { + int packetNumber = _sequencer.getIncomingPacketNumber(); + foreach (PacketRecord* record, _other->_sendRecords) { + TestSendRecord* sendRecord = static_cast(record); + if (sendRecord->getPacketNumber() == packetNumber) { + if (!sendRecord->getData().deepEquals(_data, getLastAcknowledgedSendRecord()->getLOD())) { qDebug() << "Sent/received metavoxel data mismatch."; exit(true); } @@ -1012,8 +1039,7 @@ void Endpoint::readMessage(Bitstream& in) { } // record the receipt - ReceiveRecord record = { packetNumber, SharedObjectPointer(), _data, _sendRecords.first().lod }; - _receiveRecords.append(record); + _receiveRecords.append(maybeCreateReceiveRecord()); return; } if (_mode == METAVOXEL_SERVER_MODE) { @@ -1022,8 +1048,7 @@ void Endpoint::readMessage(Bitstream& in) { handleMessage(message, in); // record the receipt - ReceiveRecord record = { _sequencer->getIncomingPacketNumber() }; - _receiveRecords.append(record); + _receiveRecords.append(maybeCreateReceiveRecord()); return; } @@ -1033,8 +1058,7 @@ void Endpoint::readMessage(Bitstream& in) { _remoteState = message.state; // record the receipt - ReceiveRecord record = { _sequencer->getIncomingPacketNumber(), message.state }; - _receiveRecords.append(record); + _receiveRecords.append(maybeCreateReceiveRecord()); for (QList::iterator it = _other->_unreliableMessagesSent.begin(); it != _other->_unreliableMessagesSent.end(); it++) { @@ -1056,7 +1080,48 @@ void Endpoint::readMessage(Bitstream& in) { exit(true); } -void Endpoint::handleReliableMessage(const QVariant& message) { +void TestEndpoint::handleMessage(const QVariant& message, Bitstream& in) { + int userType = message.userType(); + if (userType == ClientStateMessage::Type) { + ClientStateMessage state = message.value(); + _lod = state.lod; + + } else if (userType == MetavoxelDeltaMessage::Type) { + PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); + _data.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, getLastAcknowledgedSendRecord()->getLOD()); + + } else if (userType == QMetaType::QVariantList) { + foreach (const QVariant& element, message.toList()) { + handleMessage(element, in); + } + } +} + +PacketRecord* TestEndpoint::maybeCreateSendRecord() const { + return new TestSendRecord(_lod, (_mode == METAVOXEL_CLIENT_MODE) ? MetavoxelData() : _data, + _localState, _sequencer.getOutgoingPacketNumber()); +} + +PacketRecord* TestEndpoint::maybeCreateReceiveRecord() const { + return new TestReceiveRecord(getLastAcknowledgedSendRecord()->getLOD(), + (_mode == METAVOXEL_SERVER_MODE) ? MetavoxelData() : _data, _remoteState); +} + +void TestEndpoint::handleHighPriorityMessage(const QVariant& message) { + if (message.userType() == ClearSharedObjectMessage::Type) { + return; + } + if (_other->_highPriorityMessagesSent.isEmpty()) { + throw QString("Received unsent/already sent high priority message."); + } + QVariant sentMessage = _other->_highPriorityMessagesSent.takeFirst(); + if (!messagesEqual(message, sentMessage)) { + throw QString("Sent/received high priority message mismatch."); + } + highPriorityMessagesReceived++; +} + +void TestEndpoint::handleReliableMessage(const QVariant& message) { if (message.userType() == ClearSharedObjectMessage::Type || message.userType() == ClearMainChannelSharedObjectMessage::Type) { return; @@ -1071,8 +1136,8 @@ void Endpoint::handleReliableMessage(const QVariant& message) { reliableMessagesReceived++; } -void Endpoint::readReliableChannel() { - CircularBuffer& buffer = _sequencer->getReliableInputChannel(1)->getBuffer(); +void TestEndpoint::readReliableChannel() { + CircularBuffer& buffer = _sequencer.getReliableInputChannel(1)->getBuffer(); QByteArray bytes = buffer.read(buffer.bytesAvailable()); if (_other->_dataStreamed.size() < bytes.size()) { throw QString("Received unsent/already sent streamed data."); @@ -1085,44 +1150,6 @@ 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); -} - -void Endpoint::receiveDatagram(const QByteArray& datagram) { - if (_mode == CONGESTION_MODE) { - if (datagram.size() <= _remainingPipelineCapacity) { - // have to copy the datagram; the one we're passed is a reference to a shared buffer - _pipeline[0].append(QByteArray(datagram.constData(), datagram.size())); - _remainingPipelineCapacity -= datagram.size(); - } - } else { - _sequencer->receivedDatagram(datagram); - datagramsReceived++; - bytesReceived += datagram.size(); - } -} - -void Endpoint::handleMessage(const QVariant& message, Bitstream& in) { - int userType = message.userType(); - if (userType == ClientStateMessage::Type) { - ClientStateMessage state = message.value(); - _lod = state.lod; - - } else if (userType == MetavoxelDeltaMessage::Type) { - _data.readDelta(_receiveRecords.first().data, _receiveRecords.first().lod, in, _sendRecords.first().lod); - - } else if (userType == QMetaType::QVariantList) { - foreach (const QVariant& element, message.toList()) { - handleMessage(element, in); - } - } -} - TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) : _foo(foo), _baz(baz), diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index f9a314dcd7..476a8c6295 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -15,8 +15,7 @@ #include #include -#include -#include +#include #include class SequencedTestMessage; @@ -35,60 +34,43 @@ public: }; /// Represents a simulated endpoint. -class Endpoint : public QObject { +class TestEndpoint : public Endpoint { Q_OBJECT public: enum Mode { BASIC_PEER_MODE, CONGESTION_MODE, METAVOXEL_SERVER_MODE, METAVOXEL_CLIENT_MODE }; - Endpoint(const QByteArray& datagramHeader, Mode mode = BASIC_PEER_MODE); + TestEndpoint(Mode mode = BASIC_PEER_MODE); - void setOther(Endpoint* other) { _other = other; } + void setOther(TestEndpoint* other) { _other = other; } /// Perform a simulation step. /// \return true if failure was detected bool simulate(int iterationNumber); -private slots: + virtual int parseData(const QByteArray& packet); + +protected: - void sendDatagram(const QByteArray& datagram); + virtual void sendDatagram(const QByteArray& data); + virtual void readMessage(Bitstream& in); + + virtual void handleMessage(const QVariant& message, Bitstream& in); + + virtual PacketRecord* maybeCreateSendRecord() const; + virtual PacketRecord* maybeCreateReceiveRecord() const; + +private slots: + void handleHighPriorityMessage(const QVariant& message); - void readMessage(Bitstream& in); void handleReliableMessage(const QVariant& message); void readReliableChannel(); - void clearSendRecordsBefore(int index); - void clearReceiveRecordsBefore(int index); - private: - void receiveDatagram(const QByteArray& datagram); - - void handleMessage(const QVariant& message, Bitstream& in); - - class SendRecord { - public: - int packetNumber; - SharedObjectPointer localState; - MetavoxelData data; - MetavoxelLOD lod; - }; - - class ReceiveRecord { - public: - int packetNumber; - SharedObjectPointer remoteState; - MetavoxelData data; - MetavoxelLOD lod; - }; - Mode _mode; - DatagramSequencer* _sequencer; - QList _sendRecords; - QList _receiveRecords; - SharedObjectPointer _localState; SharedObjectPointer _remoteState; @@ -97,7 +79,7 @@ private: SharedObjectPointer _sphere; - Endpoint* _other; + TestEndpoint* _other; typedef QPair ByteArrayIntPair; QList _delayedDatagrams; diff --git a/tools/bitstream2json/CMakeLists.txt b/tools/bitstream2json/CMakeLists.txt index fde80b4d33..d5b82adbd9 100644 --- a/tools/bitstream2json/CMakeLists.txt +++ b/tools/bitstream2json/CMakeLists.txt @@ -18,9 +18,14 @@ include(${MACRO_DIR}/SetupHifiProject.cmake) setup_hifi_project(${TARGET_NAME} TRUE) link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") include(${MACRO_DIR}/IncludeGLM.cmake) include_glm(${TARGET_NAME} "${ROOT_DIR}") +IF (WIN32) + target_link_libraries(${TARGET_NAME} Winmm Ws2_32) +ENDIF(WIN32) + target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script) diff --git a/tools/json2bitstream/CMakeLists.txt b/tools/json2bitstream/CMakeLists.txt index 51382d5858..b93c57b582 100644 --- a/tools/json2bitstream/CMakeLists.txt +++ b/tools/json2bitstream/CMakeLists.txt @@ -18,9 +18,14 @@ include(${MACRO_DIR}/SetupHifiProject.cmake) setup_hifi_project(${TARGET_NAME} TRUE) link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") include(${MACRO_DIR}/IncludeGLM.cmake) include_glm(${TARGET_NAME} "${ROOT_DIR}") +IF (WIN32) + target_link_libraries(${TARGET_NAME} Winmm Ws2_32) +ENDIF(WIN32) + target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script)