diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 637bdca67f..0449e0d682 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -214,7 +214,6 @@ void Agent::run() { QNetworkDiskCache* cache = new QNetworkDiskCache(); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache"); - cache->moveToThread(networkAccessManager.thread()); networkAccessManager.setCache(cache); qDebug() << "Downloading script at" << scriptURL.toString(); diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index d0c0d4c781..c601478f70 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -21,7 +21,8 @@ const int SEND_INTERVAL = 50; MetavoxelServer::MetavoxelServer(const QByteArray& packet) : - ThreadedAssignment(packet) { + ThreadedAssignment(packet), + _sendTimer(this) { _sendTimer.setSingleShot(true); connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas())); @@ -91,24 +92,54 @@ void MetavoxelServer::sendDeltas() { MetavoxelSession::MetavoxelSession(const SharedNodePointer& node, MetavoxelServer* server) : Endpoint(node, new PacketRecord(), NULL), - _server(server) { + _server(server), + _reliableDeltaChannel(NULL) { connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleMessage(const QVariant&))); - connect(_sequencer.getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)), - SLOT(handleMessage(const QVariant&))); + connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(checkReliableDeltaReceived())); + connect(_sequencer.getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&, Bitstream&)), + SLOT(handleMessage(const QVariant&, Bitstream&))); } void MetavoxelSession::update() { - // wait until we have a valid lod - if (_lod.isValid()) { - Endpoint::update(); + // wait until we have a valid lod before sending + if (!_lod.isValid()) { + return; } -} - -void MetavoxelSession::writeUpdateMessage(Bitstream& out) { + // if we're sending a reliable delta, wait until it's acknowledged + if (_reliableDeltaChannel) { + Bitstream& out = _sequencer.startPacket(); + out << QVariant::fromValue(MetavoxelDeltaPendingMessage()); + _sequencer.endPacket(); + return; + } + Bitstream& out = _sequencer.startPacket(); + int start = _sequencer.getOutputStream().getUnderlying().device()->pos(); out << QVariant::fromValue(MetavoxelDeltaMessage()); PacketRecord* sendRecord = getLastAcknowledgedSendRecord(); _server->getData().writeDelta(sendRecord->getData(), sendRecord->getLOD(), out, _lod); + out.flush(); + int end = _sequencer.getOutputStream().getUnderlying().device()->pos(); + if (end > _sequencer.getMaxPacketSize()) { + // we need to send the delta on the reliable channel + _reliableDeltaChannel = _sequencer.getReliableOutputChannel(RELIABLE_DELTA_CHANNEL_INDEX); + _reliableDeltaChannel->startMessage(); + _reliableDeltaChannel->getBuffer().write(_sequencer.getOutgoingPacketData().constData() + start, end - start); + _reliableDeltaChannel->endMessage(); + + _reliableDeltaWriteMappings = out.getAndResetWriteMappings(); + _reliableDeltaReceivedOffset = _reliableDeltaChannel->getBytesWritten(); + _reliableDeltaData = _server->getData(); + _reliableDeltaLOD = _lod; + + // go back to the beginning with the current packet and note that there's a delta pending + _sequencer.getOutputStream().getUnderlying().device()->seek(start); + out << QVariant::fromValue(MetavoxelDeltaPendingMessage()); + _sequencer.endPacket(); + + } else { + _sequencer.endPacket(); + } } void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { @@ -116,7 +147,8 @@ void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { } PacketRecord* MetavoxelSession::maybeCreateSendRecord() const { - return new PacketRecord(_lod, _server->getData()); + return _reliableDeltaChannel ? new PacketRecord(_reliableDeltaLOD, _reliableDeltaData) : + new PacketRecord(_lod, _server->getData()); } void MetavoxelSession::handleMessage(const QVariant& message) { @@ -134,3 +166,13 @@ void MetavoxelSession::handleMessage(const QVariant& message) { } } } + +void MetavoxelSession::checkReliableDeltaReceived() { + if (!_reliableDeltaChannel || _reliableDeltaChannel->getOffset() < _reliableDeltaReceivedOffset) { + return; + } + _sequencer.getOutputStream().persistWriteMappings(_reliableDeltaWriteMappings); + _reliableDeltaWriteMappings = Bitstream::WriteMappings(); + _reliableDeltaData = MetavoxelData(); + _reliableDeltaChannel = NULL; +} diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index d9b010e282..f2769f26f2 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -63,7 +63,6 @@ public: protected: - virtual void writeUpdateMessage(Bitstream& out); virtual void handleMessage(const QVariant& message, Bitstream& in); virtual PacketRecord* maybeCreateSendRecord() const; @@ -71,12 +70,19 @@ protected: private slots: void handleMessage(const QVariant& message); + void checkReliableDeltaReceived(); private: MetavoxelServer* _server; MetavoxelLOD _lod; + + ReliableChannel* _reliableDeltaChannel; + int _reliableDeltaReceivedOffset; + MetavoxelData _reliableDeltaData; + MetavoxelLOD _reliableDeltaLOD; + Bitstream::WriteMappings _reliableDeltaWriteMappings; }; #endif // hifi_MetavoxelServer_h diff --git a/examples/locationsMenu.js b/examples/locationsMenu.js index 6f4a28fe38..24b0dabf46 100644 --- a/examples/locationsMenu.js +++ b/examples/locationsMenu.js @@ -57,7 +57,7 @@ var LocationMenu = function(opts) { y: 0, width: menuWidth + 10, height: (menuHeight * (pageSize + 1)) + 10, - color: { red: 0, green: 0, blue: 0}, + backgroundColor: { red: 0, green: 0, blue: 0}, topMargin: 4, leftMargin: 4, text: "", @@ -71,7 +71,7 @@ var LocationMenu = function(opts) { y: 0, width: menuWidth, height: menuHeight, - color: inactiveColor, + backgroundColor: inactiveColor, topMargin: margin, leftMargin: margin, text: (i == 0) ? "Loading..." : "", @@ -85,7 +85,7 @@ var LocationMenu = function(opts) { y: 0, width: menuWidth / 2, height: menuHeight, - color: disabledColor, + backgroundColor: disabledColor, topMargin: margin, leftMargin: margin, text: "Previous", @@ -97,7 +97,7 @@ var LocationMenu = function(opts) { y: 0, width: menuWidth / 2, height: menuHeight, - color: disabledColor, + backgroundColor: disabledColor, topMargin: margin, leftMargin: margin, text: "Next", @@ -175,10 +175,10 @@ var LocationMenu = function(opts) { if (start + i < this.locations.length) { location = this.locations[start + i]; update.text = (start + i + 1) + ". " + location.username; - update.color = inactiveColor; + update.backgroundColor = inactiveColor; } else { update.text = ""; - update.color = disabledColor; + update.backgroundColor = disabledColor; } Overlays.editOverlay(this.menuItems[i].overlay, update); this.menuItems[i].location = location; @@ -187,8 +187,8 @@ var LocationMenu = function(opts) { this.previousEnabled = pageNumber > 0; this.nextEnabled = pageNumber < (this.numPages - 1); - Overlays.editOverlay(this.previousButton, { color: this.previousEnabled ? prevNextColor : disabledColor}); - Overlays.editOverlay(this.nextButton, { color: this.nextEnabled ? prevNextColor : disabledColor }); + Overlays.editOverlay(this.previousButton, { backgroundColor: this.previousEnabled ? prevNextColor : disabledColor}); + Overlays.editOverlay(this.nextButton, { backgroundColor: this.nextEnabled ? prevNextColor : disabledColor }); } this.mousePressEvent = function(event) { @@ -198,17 +198,17 @@ var LocationMenu = function(opts) { self.toggleMenu(); } else if (clickedOverlay == self.previousButton) { if (self.previousEnabled) { - Overlays.editOverlay(clickedOverlay, { color: activeColor }); + Overlays.editOverlay(clickedOverlay, { backgroundColor: activeColor }); } } else if (clickedOverlay == self.nextButton) { if (self.nextEnabled) { - Overlays.editOverlay(clickedOverlay, { color: activeColor }); + Overlays.editOverlay(clickedOverlay, { backgroundColor: activeColor }); } } else { for (var i = 0; i < self.menuItems.length; i++) { if (clickedOverlay == self.menuItems[i].overlay) { if (self.menuItems[i].location != null) { - Overlays.editOverlay(clickedOverlay, { color: activeColor }); + Overlays.editOverlay(clickedOverlay, { backgroundColor: activeColor }); } break; } @@ -221,19 +221,19 @@ var LocationMenu = function(opts) { if (clickedOverlay == self.previousButton) { if (self.previousEnabled) { - Overlays.editOverlay(clickedOverlay, { color: inactiveColor }); + Overlays.editOverlay(clickedOverlay, { backgroundColor: inactiveColor }); self.goToPage(self.page - 1); } } else if (clickedOverlay == self.nextButton) { if (self.nextEnabled) { - Overlays.editOverlay(clickedOverlay, { color: inactiveColor }); + Overlays.editOverlay(clickedOverlay, { backgroundColor: inactiveColor }); self.goToPage(self.page + 1); } } else { for (var i = 0; i < self.menuItems.length; i++) { if (clickedOverlay == self.menuItems[i].overlay) { if (self.menuItems[i].location != null) { - Overlays.editOverlay(clickedOverlay, { color: inactiveColor }); + Overlays.editOverlay(clickedOverlay, { backgroundColor: inactiveColor }); var location = self.menuItems[i].location; Window.location = "hifi://" + location.domain + "/" + location.x + "," + location.y + "," + location.z; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c263b71238..ace265ad4f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -316,12 +316,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - - // Make sure cache on same thread than its parent (NetworkAccessManager) QNetworkDiskCache* cache = new QNetworkDiskCache(); - cache->moveToThread(networkAccessManager.thread()); - cache->setParent(&networkAccessManager); - cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); networkAccessManager.setCache(cache); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index dba5feca9e..6b32eb5770 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1006,7 +1006,6 @@ void Menu::goToDomainDialog() { domainDialog.setWindowTitle("Go to Domain"); domainDialog.setLabelText("Domain server:"); domainDialog.setTextValue(currentDomainHostname); - domainDialog.setWindowFlags(Qt::Sheet); domainDialog.resize(domainDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, domainDialog.size().height()); int dialogReturn = domainDialog.exec(); @@ -1044,7 +1043,6 @@ void Menu::goTo() { QString destination = QString(); gotoDialog.setTextValue(destination); - gotoDialog.setWindowFlags(Qt::Sheet); gotoDialog.resize(gotoDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, gotoDialog.size().height()); int dialogReturn = gotoDialog.exec(); @@ -1160,7 +1158,6 @@ void Menu::goToLocation() { coordinateDialog.setWindowTitle("Go to Location"); coordinateDialog.setLabelText("Coordinate as x,y,z:"); coordinateDialog.setTextValue(currentLocation); - coordinateDialog.setWindowFlags(Qt::Sheet); coordinateDialog.resize(coordinateDialog.parentWidget()->size().width() * 0.30, coordinateDialog.size().height()); int dialogReturn = coordinateDialog.exec(); @@ -1225,7 +1222,6 @@ void Menu::nameLocation() { "(wherever you are standing and looking now) as you.\n\n" "Location name:"); - nameDialog.setWindowFlags(Qt::Sheet); nameDialog.resize((int) (nameDialog.parentWidget()->size().width() * 0.30), nameDialog.size().height()); if (nameDialog.exec() == QDialog::Accepted) { diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 8ec7cbace1..bc7096c471 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -22,7 +22,7 @@ Overlay::Overlay() : _parent(NULL), _alpha(DEFAULT_ALPHA), - _color(DEFAULT_BACKGROUND_COLOR), + _color(DEFAULT_OVERLAY_COLOR), _visible(true), _anchor(NO_ANCHOR) { diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 7667b3d3fd..f8d6400bf6 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -21,7 +21,7 @@ #include // for xColor -const xColor DEFAULT_BACKGROUND_COLOR = { 255, 255, 255 }; +const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; const float DEFAULT_ALPHA = 0.7f; class Overlay : public QObject { diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index 797d0be1a2..691179ec54 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -18,6 +18,7 @@ #include "ui/TextRenderer.h" TextOverlay::TextOverlay() : + _backgroundColor(DEFAULT_BACKGROUND_COLOR), _leftMargin(DEFAULT_MARGIN), _topMargin(DEFAULT_MARGIN), _fontSize(DEFAULT_FONTSIZE) @@ -33,7 +34,7 @@ void TextOverlay::render() { } const float MAX_COLOR = 255; - glColor4f(0 / MAX_COLOR, 0 / MAX_COLOR, 0 / MAX_COLOR, _alpha); + glColor4f(_backgroundColor.red / MAX_COLOR, _backgroundColor.green / MAX_COLOR, _backgroundColor.blue / MAX_COLOR, _alpha); glBegin(GL_QUADS); glVertex2f(_bounds.left(), _bounds.top()); @@ -82,6 +83,18 @@ void TextOverlay::setProperties(const QScriptValue& properties) { setText(text.toVariant().toString()); } + QScriptValue backgroundColor = properties.property("backgroundColor"); + if (backgroundColor.isValid()) { + QScriptValue red = backgroundColor.property("red"); + QScriptValue green = backgroundColor.property("green"); + QScriptValue blue = backgroundColor.property("blue"); + if (red.isValid() && green.isValid() && blue.isValid()) { + _backgroundColor.red = red.toVariant().toInt(); + _backgroundColor.green = green.toVariant().toInt(); + _backgroundColor.blue = blue.toVariant().toInt(); + } + } + if (properties.property("leftMargin").isValid()) { setLeftMargin(properties.property("leftMargin").toVariant().toInt()); } diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index c2aafb24e8..78a51179a0 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -27,6 +27,7 @@ #include "Overlay.h" #include "Overlay2D.h" +const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; const int DEFAULT_MARGIN = 10; const int DEFAULT_FONTSIZE = 11; @@ -54,6 +55,7 @@ public: private: QString _text; + xColor _backgroundColor; int _leftMargin; int _topMargin; int _fontSize; diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index d18903f923..bc662aa890 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -274,6 +274,26 @@ void Bitstream::persistAndResetReadMappings() { persistReadMappings(getAndResetReadMappings()); } +void Bitstream::copyPersistentMappings(const Bitstream& other) { + _objectStreamerStreamer.copyPersistentMappings(other._objectStreamerStreamer); + _typeStreamerStreamer.copyPersistentMappings(other._typeStreamerStreamer); + _attributeStreamer.copyPersistentMappings(other._attributeStreamer); + _scriptStringStreamer.copyPersistentMappings(other._scriptStringStreamer); + _sharedObjectStreamer.copyPersistentMappings(other._sharedObjectStreamer); + _sharedObjectReferences = other._sharedObjectReferences; + _weakSharedObjectHash = other._weakSharedObjectHash; +} + +void Bitstream::clearPersistentMappings() { + _objectStreamerStreamer.clearPersistentMappings(); + _typeStreamerStreamer.clearPersistentMappings(); + _attributeStreamer.clearPersistentMappings(); + _scriptStringStreamer.clearPersistentMappings(); + _sharedObjectStreamer.clearPersistentMappings(); + _sharedObjectReferences.clear(); + _weakSharedObjectHash.clear(); +} + void Bitstream::clearSharedObject(int id) { SharedObjectPointer object = _sharedObjectStreamer.takePersistentValue(id); if (object) { @@ -1122,7 +1142,7 @@ Bitstream& Bitstream::operator>(ObjectStreamerPointer& streamer) { } if (_metadataType == NO_METADATA) { if (!metaObject) { - qWarning() << "Unknown class name:" << className; + throw BitstreamException(QString("Unknown class name: ") + className); } return *this; } @@ -1232,7 +1252,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { } if (_metadataType == NO_METADATA) { if (!baseStreamer) { - qWarning() << "Unknown type name:" << typeName; + throw BitstreamException(QString("Unknown type name: ") + typeName); } return *this; } @@ -1240,7 +1260,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { *this >> category; if (category == TypeStreamer::SIMPLE_CATEGORY) { if (!streamer) { - qWarning() << "Unknown type name:" << typeName; + throw BitstreamException(QString("Unknown type name: ") + typeName); } return *this; } @@ -1441,7 +1461,7 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { _objectStreamerStreamer >> objectStreamer; if (delta) { if (!reference) { - qWarning() << "Delta without reference" << id << originID; + throw BitstreamException(QString("Delta without reference [id=%1, originID=%2]").arg(id).arg(originID)); } objectStreamer->readRawDelta(*this, reference.data(), pointer.data()); } else { @@ -1451,7 +1471,7 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { QObject* rawObject; if (delta) { if (!reference) { - qWarning() << "Delta without reference" << id << originID; + throw BitstreamException(QString("Delta without reference [id=%1, originID=%2]").arg(id).arg(originID)); } readRawDelta(rawObject, (const QObject*)reference.data()); } else { @@ -1682,6 +1702,10 @@ const TypeStreamer* Bitstream::createInvalidTypeStreamer() { return streamer; } +BitstreamException::BitstreamException(const QString& description) : + _description(description) { +} + QJsonValue JSONWriter::getData(bool value) { return value; } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index e32f93dbe2..70fde94b79 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -102,6 +102,9 @@ public: V takePersistentValue(int id) { V value = _persistentValues.take(id); _valueIDs.remove(value); return value; } + void copyPersistentMappings(const RepeatedValueStreamer& other); + void clearPersistentMappings(); + RepeatedValueStreamer& operator<<(K value); RepeatedValueStreamer& operator>>(V& value); @@ -199,6 +202,29 @@ template inline RepeatedValueStreamer& return *this; } +template inline void RepeatedValueStreamer::copyPersistentMappings( + const RepeatedValueStreamer& other) { + _lastPersistentID = other._lastPersistentID; + _idStreamer.setBitsFromValue(_lastPersistentID); + _persistentIDs = other._persistentIDs; + _transientOffsets.clear(); + _lastTransientOffset = 0; + _persistentValues = other._persistentValues; + _transientValues.clear(); + _valueIDs = other._valueIDs; +} + +template inline void RepeatedValueStreamer::clearPersistentMappings() { + _lastPersistentID = 0; + _idStreamer.setBitsFromValue(_lastPersistentID); + _persistentIDs.clear(); + _transientOffsets.clear(); + _lastTransientOffset = 0; + _persistentValues.clear(); + _transientValues.clear(); + _valueIDs.clear(); +} + /// A stream for bit-aligned data. Through a combination of code generation, reflection, macros, and templates, provides a /// serialization mechanism that may be used for both networking and persistent storage. For unreliable networking, the /// class provides a mapping system that resends mappings for ids until they are acknowledged (and thus persisted). For @@ -303,6 +329,9 @@ public: Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA, GenericsMode = NO_GENERICS, QObject* parent = NULL); + /// Returns a reference to the underlying data stream. + QDataStream& getUnderlying() { return _underlying; } + /// Substitutes the supplied metaobject for the given class name's default mapping. This is mostly useful for testing the /// process of mapping between different types, but may in the future be used for permanently renaming classes. void addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject); @@ -347,6 +376,12 @@ public: /// Immediately persists and resets the read mappings. void persistAndResetReadMappings(); + /// Copies the persistent mappings from the specified other stream. + void copyPersistentMappings(const Bitstream& other); + + /// Clears the persistent mappings for this stream. + void clearPersistentMappings(); + /// Returns a reference to the weak hash storing shared objects for this stream. const WeakSharedObjectHash& getWeakSharedObjectHash() const { return _weakSharedObjectHash; } @@ -823,6 +858,19 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } +/// Thrown for unrecoverable errors. +class BitstreamException { +public: + + BitstreamException(const QString& description); + + const QString& getDescription() const { return _description; } + +private: + + QString _description; +}; + /// Provides a means of writing Bitstream-able data to JSON rather than the usual binary format in a manner that allows it to /// be manipulated and re-read, converted to binary, etc. To use, create a JSONWriter, stream values in using the << operator, /// and call getDocument to obtain the JSON data. diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index eb02497321..2c594fc1ca 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -113,17 +113,16 @@ Bitstream& DatagramSequencer::startPacket() { _outgoingPacketStream << (quint32)record.packetNumber; } - // write the high-priority messages - _outgoingPacketStream << (quint32)_highPriorityMessages.size(); - foreach (const HighPriorityMessage& message, _highPriorityMessages) { - _outputStream << message.data; - } - // return the stream, allowing the caller to write the rest return _outputStream; } void DatagramSequencer::endPacket() { + // write the high-priority messages + _outputStream << _highPriorityMessages.size(); + foreach (const HighPriorityMessage& message, _highPriorityMessages) { + _outputStream << message.data; + } _outputStream.flush(); // if we have space remaining, send some data from our reliable channels @@ -222,22 +221,22 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { _sendRecords.erase(_sendRecords.begin(), it + 1); } + // alert external parties so that they can read the middle + emit readyToRead(_inputStream); + // read and dispatch the high-priority messages - quint32 highPriorityMessageCount; - _incomingPacketStream >> highPriorityMessageCount; + int highPriorityMessageCount; + _inputStream >> highPriorityMessageCount; int newHighPriorityMessages = highPriorityMessageCount - _receivedHighPriorityMessages; - for (quint32 i = 0; i < highPriorityMessageCount; i++) { + for (int i = 0; i < highPriorityMessageCount; i++) { QVariant data; _inputStream >> data; - if ((int)i >= _receivedHighPriorityMessages) { + if (i >= _receivedHighPriorityMessages) { emit receivedHighPriorityMessage(data); } } _receivedHighPriorityMessages = highPriorityMessageCount; - // alert external parties so that they can read the middle - emit readyToRead(_inputStream); - // read the reliable data, if any quint32 reliableChannels; _incomingPacketStream >> reliableChannels; @@ -253,6 +252,8 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { // record the receipt ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings(), newHighPriorityMessages }; _receiveRecords.append(record); + + emit receiveRecorded(); } void DatagramSequencer::sendClearSharedObjectMessage(int id) { @@ -274,6 +275,11 @@ void DatagramSequencer::handleHighPriorityMessage(const QVariant& data) { } } +void DatagramSequencer::clearReliableChannel(QObject* object) { + ReliableChannel* channel = static_cast(object); + (channel->isOutput() ? _reliableOutputChannels : _reliableInputChannels).remove(channel->getIndex()); +} + void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { // stop acknowledging the recorded packets while (!_receiveRecords.isEmpty() && _receiveRecords.first().packetNumber <= record.lastReceivedPacketNumber) { @@ -295,7 +301,10 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { // acknowledge the received spans foreach (const ChannelSpan& span, record.spans) { - getReliableOutputChannel(span.channel)->spanAcknowledged(span); + ReliableChannel* channel = _reliableOutputChannels.value(span.channel); + if (channel) { + channel->spanAcknowledged(span); + } } // increase the packet rate with every ack until we pass the slow start threshold; then, every round trip @@ -310,7 +319,10 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { void DatagramSequencer::sendRecordLost(const SendRecord& record) { // notify the channels of their lost spans foreach (const ChannelSpan& span, record.spans) { - getReliableOutputChannel(span.channel)->spanLost(record.packetNumber, _outgoingPacketNumber + 1); + ReliableChannel* channel = _reliableOutputChannels.value(span.channel); + if (channel) { + channel->spanLost(record.packetNumber, _outgoingPacketNumber + 1); + } } // halve the rate and remember as threshold @@ -364,6 +376,8 @@ void DatagramSequencer::sendPacket(const QByteArray& packet, const QVector().id); @@ -688,6 +710,7 @@ void ReliableChannel::handleMessage(const QVariant& message) { ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool output) : QObject(sequencer), _index(index), + _output(output), _dataStream(&_buffer), _bitstream(_dataStream), _priority(1.0f), @@ -700,7 +723,9 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o _dataStream.setByteOrder(QDataStream::LittleEndian); connect(&_bitstream, SIGNAL(sharedObjectCleared(int)), SLOT(sendClearSharedObjectMessage(int))); - connect(this, SIGNAL(receivedMessage(const QVariant&)), SLOT(handleMessage(const QVariant&))); + connect(this, SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); + + sequencer->connect(this, SIGNAL(destroyed(QObject*)), SLOT(clearReliableChannel(QObject*))); } void ReliableChannel::writeData(QDataStream& out, int bytes, QVector& spans) { @@ -843,9 +868,9 @@ void ReliableChannel::readData(QDataStream& in) { _dataStream.skipRawData(sizeof(quint32)); QVariant message; _bitstream >> message; + emit receivedMessage(message, _bitstream); _bitstream.reset(); _bitstream.persistAndResetReadMappings(); - emit receivedMessage(message); continue; } } diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index aa8b6907ff..b85916b561 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -78,6 +78,15 @@ public: /// Returns the packet number of the last packet received (or the packet currently being assembled). int getIncomingPacketNumber() const { return _incomingPacketNumber; } + /// Returns a reference to the stream used to read packets. + Bitstream& getInputStream() { return _inputStream; } + + /// Returns a reference to the stream used to write packets. + Bitstream& getOutputStream() { return _outputStream; } + + /// Returns a reference to the outgoing packet data. + const QByteArray& getOutgoingPacketData() const { return _outgoingPacketData; } + /// Returns the packet number of the sent packet at the specified index. int getSentPacketNumber(int index) const { return _sendRecords.at(index).packetNumber; } @@ -126,9 +135,15 @@ signals: /// Emitted when a packet is available to read. void readyToRead(Bitstream& input); - /// Emitted when we've received a high-priority message + /// Emitted when we've received a high-priority message. void receivedHighPriorityMessage(const QVariant& data); + /// Emitted when we've recorded the transmission of a packet. + void sendRecorded(); + + /// Emitted when we've recorded the receipt of a packet (that is, at the end of packet processing). + void receiveRecorded(); + /// Emitted when a sent packet has been acknowledged by the remote side. /// \param index the index of the packet in our list of send records void sendAcknowledged(int index); @@ -141,6 +156,7 @@ private slots: void sendClearSharedObjectMessage(int id); void handleHighPriorityMessage(const QVariant& data); + void clearReliableChannel(QObject* object); private: @@ -319,6 +335,9 @@ public: /// Returns the channel's index in the sequencer's channel map. int getIndex() const { return _index; } + /// Checks whether this is an output channel. + bool isOutput() const { return _output; } + /// Returns a reference to the buffer used to write/read data to/from this channel. CircularBuffer& getBuffer() { return _buffer; } @@ -336,22 +355,36 @@ public: /// Returns the number of bytes available to read from this channel. int getBytesAvailable() const; + /// Returns the offset, which represents the total number of bytes acknowledged + /// (on the write end) or received completely (on the read end). + int getOffset() const { return _offset; } + + /// Returns the total number of bytes written to this channel. + int getBytesWritten() const { return _offset + _buffer.pos(); } + /// Sets whether we expect to write/read framed messages. void setMessagesEnabled(bool enabled) { _messagesEnabled = enabled; } bool getMessagesEnabled() const { return _messagesEnabled; } - /// Sends a framed message on this channel. + /// Starts a framed message on this channel. + void startMessage(); + + /// Ends a framed message on this channel. + void endMessage(); + + /// Sends a framed message on this channel (convenience function that calls startMessage, + /// writes the message to the bitstream, then calls endMessage). void sendMessage(const QVariant& message); signals: /// Fired when a framed message has been received on this channel. - void receivedMessage(const QVariant& message); + void receivedMessage(const QVariant& message, Bitstream& in); private slots: void sendClearSharedObjectMessage(int id); - void handleMessage(const QVariant& message); + void handleMessage(const QVariant& message, Bitstream& in); private: @@ -370,6 +403,7 @@ private: void readData(QDataStream& in); int _index; + bool _output; CircularBuffer _buffer; CircularBuffer _assemblyBuffer; QDataStream _dataStream; @@ -381,6 +415,7 @@ private: int _writePositionResetPacketNumber; SpanList _acknowledged; bool _messagesEnabled; + int _messageLengthPlaceholder; }; #endif // hifi_DatagramSequencer_h diff --git a/libraries/metavoxels/src/Endpoint.cpp b/libraries/metavoxels/src/Endpoint.cpp index c656054504..666ffe52d9 100644 --- a/libraries/metavoxels/src/Endpoint.cpp +++ b/libraries/metavoxels/src/Endpoint.cpp @@ -19,6 +19,8 @@ Endpoint::Endpoint(const SharedNodePointer& node, PacketRecord* baselineSendReco connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendDatagram(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&))); + connect(&_sequencer, SIGNAL(sendRecorded()), SLOT(recordSend())); + connect(&_sequencer, SIGNAL(receiveRecorded()), SLOT(recordReceive())); connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int))); connect(&_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int))); @@ -40,9 +42,6 @@ void Endpoint::update() { Bitstream& out = _sequencer.startPacket(); writeUpdateMessage(out); _sequencer.endPacket(); - - // record the send - _sendRecords.append(maybeCreateSendRecord()); } int Endpoint::parseData(const QByteArray& packet) { @@ -59,8 +58,21 @@ void Endpoint::readMessage(Bitstream& in) { QVariant message; in >> message; handleMessage(message, in); - - // record the receipt +} + +void Endpoint::handleMessage(const QVariant& message, Bitstream& in) { + if (message.userType() == QMetaType::QVariantList) { + foreach (const QVariant& element, message.toList()) { + handleMessage(element, in); + } + } +} + +void Endpoint::recordSend() { + _sendRecords.append(maybeCreateSendRecord()); +} + +void Endpoint::recordReceive() { _receiveRecords.append(maybeCreateReceiveRecord()); } @@ -84,14 +96,6 @@ 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; } diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h index d253a69ded..3c681a7b98 100644 --- a/libraries/metavoxels/src/Endpoint.h +++ b/libraries/metavoxels/src/Endpoint.h @@ -24,6 +24,9 @@ class Endpoint : public NodeData { Q_OBJECT public: + + /// The index of the input/output channel used to transmit reliable deltas. + static const int RELIABLE_DELTA_CHANNEL_INDEX = 1; Endpoint(const SharedNodePointer& node, PacketRecord* baselineSendRecord = NULL, PacketRecord* baselineReceiveRecord = NULL); @@ -37,6 +40,10 @@ protected slots: virtual void sendDatagram(const QByteArray& data); virtual void readMessage(Bitstream& in); + virtual void handleMessage(const QVariant& message, Bitstream& in); + + void recordSend(); + void recordReceive(); void clearSendRecordsBefore(int index); void clearReceiveRecordsBefore(int index); @@ -44,7 +51,6 @@ protected slots: protected: virtual void writeUpdateMessage(Bitstream& out); - virtual void handleMessage(const QVariant& message, Bitstream& in); virtual PacketRecord* maybeCreateSendRecord() const; virtual PacketRecord* maybeCreateReceiveRecord() const; diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index 008a477187..e69794917f 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -86,7 +86,11 @@ void MetavoxelClientManager::updateClient(MetavoxelClient* client) { MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientManager* manager) : Endpoint(node, new PacketRecord(), new PacketRecord()), - _manager(manager) { + _manager(manager), + _reliableDeltaChannel(NULL) { + + connect(_sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX), + SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); } void MetavoxelClient::guide(MetavoxelVisitor& visitor) { @@ -112,31 +116,44 @@ void MetavoxelClient::writeUpdateMessage(Bitstream& out) { 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) { + int userType = message.userType(); + if (userType == MetavoxelDeltaMessage::Type) { PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); - _data.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, getLastAcknowledgedSendRecord()->getLOD()); - + if (_reliableDeltaChannel) { + _remoteData.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, _remoteDataLOD = _reliableDeltaLOD); + _sequencer.getInputStream().persistReadMappings(in.getAndResetReadMappings()); + in.clearPersistentMappings(); + _reliableDeltaChannel = NULL; + + } else { + _remoteData.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, + _remoteDataLOD = getLastAcknowledgedSendRecord()->getLOD()); + in.reset(); + } + // copy to local and reapply local edits + _data = _remoteData; + foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) { + if (message.data.userType() == MetavoxelEditMessage::Type) { + message.data.value().apply(_data, _sequencer.getWeakSharedObjectHash()); + } + } + } else if (userType == MetavoxelDeltaPendingMessage::Type) { + if (!_reliableDeltaChannel) { + _reliableDeltaChannel = _sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX); + _reliableDeltaChannel->getBitstream().copyPersistentMappings(_sequencer.getInputStream()); + _reliableDeltaLOD = getLastAcknowledgedSendRecord()->getLOD(); + } } else { Endpoint::handleMessage(message, in); } } PacketRecord* MetavoxelClient::maybeCreateSendRecord() const { - return new PacketRecord(_manager->getLOD()); + return new PacketRecord(_reliableDeltaChannel ? _reliableDeltaLOD : _manager->getLOD()); } PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const { - return new PacketRecord(getLastAcknowledgedSendRecord()->getLOD(), _data); + return new PacketRecord(_remoteDataLOD, _remoteData); } + diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index dd11e871ec..1f37b15c18 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -60,7 +60,6 @@ public: protected: virtual void writeUpdateMessage(Bitstream& out); - virtual void readMessage(Bitstream& in); virtual void handleMessage(const QVariant& message, Bitstream& in); virtual PacketRecord* maybeCreateSendRecord() const; @@ -70,6 +69,11 @@ private: MetavoxelClientManager* _manager; MetavoxelData _data; + MetavoxelData _remoteData; + MetavoxelLOD _remoteDataLOD; + + ReliableChannel* _reliableDeltaChannel; + MetavoxelLOD _reliableDeltaLOD; }; #endif // hifi_MetavoxelClientManager_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 8f819fe3d8..b822f1c561 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -61,6 +61,13 @@ class MetavoxelDeltaMessage { DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaMessage) +/// A message indicating that metavoxel delta information is being sent on a reliable channel. +class MetavoxelDeltaPendingMessage { + STREAMABLE +}; + +DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaPendingMessage) + /// A simple streamable edit. class MetavoxelEditMessage { STREAMABLE diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 7e5ba3f66e..e92760d303 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -9,153 +9,19 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include +#include #include "NetworkAccessManager.h" +QThreadStorage networkAccessManagers; + NetworkAccessManager& NetworkAccessManager::getInstance() { - static NetworkAccessManager sharedInstance; - return sharedInstance; + if (!networkAccessManagers.hasLocalData()) { + networkAccessManagers.setLocalData(new NetworkAccessManager()); + } + + return *networkAccessManagers.localData(); } NetworkAccessManager::NetworkAccessManager() { - qRegisterMetaType(); } - -QNetworkReply* NetworkAccessManager::get(const QNetworkRequest& request) { - if (QThread::currentThread() != thread()) { - QNetworkReply* result; - QMetaObject::invokeMethod(this, - "get", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QNetworkReply*, result), - Q_ARG(const QNetworkRequest, request)); - return result; - } - return QNetworkAccessManager::get(request); -} - -QNetworkReply* NetworkAccessManager::head(const QNetworkRequest& request) { - if (QThread::currentThread() != thread()) { - QNetworkReply* result; - QMetaObject::invokeMethod(this, - "head", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QNetworkReply*, result), - Q_ARG(const QNetworkRequest, request)); - return result; - } - return QNetworkAccessManager::head(request); -} - -QNetworkReply* NetworkAccessManager::post(const QNetworkRequest& request, QIODevice* data) { - if (QThread::currentThread() != thread()) { - QNetworkReply* result; - QMetaObject::invokeMethod(this, - "post", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QNetworkReply*, result), - Q_ARG(const QNetworkRequest, request), - Q_ARG(QIODevice*, data)); - return result; - } - return QNetworkAccessManager::post(request, data); -} - -QNetworkReply* NetworkAccessManager::post(const QNetworkRequest& request, const QByteArray& data) { - if (QThread::currentThread() != thread()) { - QNetworkReply* result; - QMetaObject::invokeMethod(this, - "post", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QNetworkReply*, result), - Q_ARG(const QNetworkRequest, request), - Q_ARG(const QByteArray, data)); - return result; - } - return QNetworkAccessManager::post(request, data); -} - -QNetworkReply* NetworkAccessManager::post(const QNetworkRequest& request, QHttpMultiPart* multiPart) { - if (QThread::currentThread() != thread()) { - QNetworkReply* result; - QMetaObject::invokeMethod(this, - "post", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QNetworkReply*, result), - Q_ARG(const QNetworkRequest, request), - Q_ARG(QHttpMultiPart*, multiPart)); - return result; - } - return QNetworkAccessManager::post(request, multiPart); -} - -QNetworkReply* NetworkAccessManager::put(const QNetworkRequest& request, QIODevice* data) { - if (QThread::currentThread() != thread()) { - QNetworkReply* result; - QMetaObject::invokeMethod(this, - "put", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QNetworkReply*, result), - Q_ARG(const QNetworkRequest, request), - Q_ARG(QIODevice*, data)); - return result; - } - return QNetworkAccessManager::put(request, data); -} - -QNetworkReply* NetworkAccessManager::put(const QNetworkRequest& request, QHttpMultiPart* multiPart) { - if (QThread::currentThread() != thread()) { - QNetworkReply* result; - QMetaObject::invokeMethod(this, - "put", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QNetworkReply*, result), - Q_ARG(const QNetworkRequest, request), - Q_ARG(QHttpMultiPart*, multiPart)); - return result; - } - return QNetworkAccessManager::put(request, multiPart); -} - -QNetworkReply* NetworkAccessManager::put(const QNetworkRequest & request, const QByteArray & data) { - if (QThread::currentThread() != thread()) { - QNetworkReply* result; - QMetaObject::invokeMethod(this, - "put", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QNetworkReply*, result), - Q_ARG(const QNetworkRequest, request), - Q_ARG(const QByteArray, data)); - return result; - } - return QNetworkAccessManager::put(request, data); -} - - -QNetworkReply* NetworkAccessManager::sendCustomRequest(const QNetworkRequest& request, const QByteArray& verb, QIODevice* data) { - if (QThread::currentThread() != thread()) { - QNetworkReply* result; - QMetaObject::invokeMethod(this, - "sendCustomRequest", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QNetworkReply*, result), - Q_ARG(const QNetworkRequest, request), - Q_ARG(const QByteArray, verb), - Q_ARG(QIODevice*, data)); - return result; - } - return QNetworkAccessManager::sendCustomRequest(request, verb, data); -} - -void NetworkAccessManager::setCache(QAbstractNetworkCache* cache) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, - "setCache", - Qt::BlockingQueuedConnection, - Q_ARG(QAbstractNetworkCache*, cache)); - } - QNetworkAccessManager::setCache(cache); -} \ No newline at end of file diff --git a/libraries/networking/src/NetworkAccessManager.h b/libraries/networking/src/NetworkAccessManager.h index 1b49cc9dee..9594170518 100644 --- a/libraries/networking/src/NetworkAccessManager.h +++ b/libraries/networking/src/NetworkAccessManager.h @@ -13,30 +13,13 @@ #define hifi_NetworkAccessManager_h #include -#include -#include -/// Wrapper around QNetworkAccessManager wo that we only use one instance -/// For any other method you should need, make sure to be on the right thread -/// or if it is not but is a slot, use QMetaObject::invokeMethod() -/// In the case what you want to call isn't a slot and you aren't on the same thread, -/// then add then method to the method to the wrapper with the Q_INVKABLE flag +/// Wrapper around QNetworkAccessManager to restrict at one instance by thread class NetworkAccessManager : public QNetworkAccessManager { Q_OBJECT public: static NetworkAccessManager& getInstance(); - Q_INVOKABLE QNetworkReply* get(const QNetworkRequest& request); - Q_INVOKABLE QNetworkReply* head(const QNetworkRequest& request); - Q_INVOKABLE QNetworkReply* post(const QNetworkRequest& request, QIODevice* data); - Q_INVOKABLE QNetworkReply* post(const QNetworkRequest& request, const QByteArray& data); - Q_INVOKABLE QNetworkReply* post(const QNetworkRequest& request, QHttpMultiPart* multiPart); - Q_INVOKABLE QNetworkReply* put(const QNetworkRequest& request, QIODevice* data); - Q_INVOKABLE QNetworkReply* put(const QNetworkRequest& request, QHttpMultiPart* multiPart); - Q_INVOKABLE QNetworkReply* put(const QNetworkRequest& request, const QByteArray& data); - Q_INVOKABLE QNetworkReply* sendCustomRequest(const QNetworkRequest& request, const QByteArray& verb, QIODevice* data = 0); - Q_INVOKABLE void setCache(QAbstractNetworkCache* cache); - private: NetworkAccessManager(); }; diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 61ab664310..4132270620 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -646,16 +646,23 @@ TestEndpoint::TestEndpoint(Mode mode) : Endpoint(SharedNodePointer(), new TestSendRecord(), new TestReceiveRecord()), _mode(mode), _highPriorityMessagesToSend(0.0f), - _reliableMessagesToSend(0.0f) { + _reliableMessagesToSend(0.0f), + _reliableDeltaChannel(NULL) { connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleHighPriorityMessage(const QVariant&))); - + connect(_sequencer.getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&, Bitstream&)), + SLOT(handleReliableMessage(const QVariant&, Bitstream&))); + if (mode == METAVOXEL_CLIENT_MODE) { _lod = MetavoxelLOD(glm::vec3(), 0.01f); + connect(_sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX), + SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleReliableMessage(const QVariant&, Bitstream&))); return; } if (mode == METAVOXEL_SERVER_MODE) { + connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(checkReliableDeltaReceived())); + _data.expand(); _data.expand(); @@ -673,9 +680,6 @@ TestEndpoint::TestEndpoint(Mode mode) : // create the object that represents out delta-encoded state _localState = new TestSharedObjectA(); - connect(_sequencer.getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)), - SLOT(handleReliableMessage(const QVariant&))); - ReliableChannel* secondInput = _sequencer.getReliableInputChannel(1); secondInput->setMessagesEnabled(false); connect(&secondInput->getBuffer(), SIGNAL(readyRead()), SLOT(readReliableChannel())); @@ -867,9 +871,6 @@ bool TestEndpoint::simulate(int iterationNumber) { maxDatagramsPerPacket = qMax(maxDatagramsPerPacket, datagramsSent - oldDatagramsSent); maxBytesPerPacket = qMax(maxBytesPerPacket, bytesSent - oldBytesSent); - - // record the send - _sendRecords.append(maybeCreateSendRecord()); } return false; @@ -880,9 +881,6 @@ bool TestEndpoint::simulate(int iterationNumber) { out << QVariant::fromValue(state); _sequencer.endPacket(); - // record the send - _sendRecords.append(maybeCreateSendRecord()); - } else if (_mode == METAVOXEL_SERVER_MODE) { // make a random change MutateVisitor visitor; @@ -907,15 +905,39 @@ bool TestEndpoint::simulate(int iterationNumber) { if (!_lod.isValid()) { return false; } + // if we're sending a reliable delta, wait until it's acknowledged + if (_reliableDeltaChannel) { + Bitstream& out = _sequencer.startPacket(); + out << QVariant::fromValue(MetavoxelDeltaPendingMessage()); + _sequencer.endPacket(); + return false; + } Bitstream& out = _sequencer.startPacket(); + int start = _sequencer.getOutputStream().getUnderlying().device()->pos(); out << QVariant::fromValue(MetavoxelDeltaMessage()); PacketRecord* sendRecord = getLastAcknowledgedSendRecord(); _data.writeDelta(sendRecord->getData(), sendRecord->getLOD(), out, _lod); - _sequencer.endPacket(); - - // record the send - _sendRecords.append(maybeCreateSendRecord()); - + out.flush(); + int end = _sequencer.getOutputStream().getUnderlying().device()->pos(); + if (end > _sequencer.getMaxPacketSize()) { + // we need to send the delta on the reliable channel + _reliableDeltaChannel = _sequencer.getReliableOutputChannel(RELIABLE_DELTA_CHANNEL_INDEX); + _reliableDeltaChannel->startMessage(); + _reliableDeltaChannel->getBuffer().write(_sequencer.getOutgoingPacketData().constData() + start, end - start); + _reliableDeltaChannel->endMessage(); + + _reliableDeltaWriteMappings = out.getAndResetWriteMappings(); + _reliableDeltaReceivedOffset = _reliableDeltaChannel->getBytesWritten(); + _reliableDeltaData = _data; + _reliableDeltaLOD = _lod; + + _sequencer.getOutputStream().getUnderlying().device()->seek(start); + out << QVariant::fromValue(MetavoxelDeltaPendingMessage()); + _sequencer.endPacket(); + + } else { + _sequencer.endPacket(); + } } else { // enqueue some number of high priority messages const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f; @@ -957,9 +979,6 @@ bool TestEndpoint::simulate(int iterationNumber) { qDebug() << message; return true; } - - // record the send - _sendRecords.append(maybeCreateSendRecord()); } maxDatagramsPerPacket = qMax(maxDatagramsPerPacket, datagramsSent - oldDatagramsSent); maxBytesPerPacket = qMax(maxBytesPerPacket, bytesSent - oldBytesSent); @@ -995,7 +1014,7 @@ void TestEndpoint::sendDatagram(const QByteArray& datagram) { // some are received out of order const float REORDER_PROBABILITY = 0.1f; if (randFloat() < REORDER_PROBABILITY * probabilityMultiplier) { - const int MIN_DELAY = 1; + const int MIN_DELAY = 2; const int MAX_DELAY = 5; // have to copy the datagram; the one we're passed is a reference to a shared buffer _delayedDatagrams.append(ByteArrayIntPair(QByteArray(datagram.constData(), datagram.size()), @@ -1008,58 +1027,32 @@ void TestEndpoint::sendDatagram(const QByteArray& datagram) { } } - _other->parseData(datagram); + _delayedDatagrams.append(ByteArrayIntPair(QByteArray(datagram.constData(), datagram.size()), 1)); } void TestEndpoint::readMessage(Bitstream& in) { if (_mode == CONGESTION_MODE) { QVariant message; in >> message; - - // record the receipt - _receiveRecords.append(maybeCreateReceiveRecord()); return; } if (_mode == METAVOXEL_CLIENT_MODE) { QVariant message; in >> message; handleMessage(message, in); - - // deep-compare data to sent version - 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); - } - break; - } - } - - // record the receipt - _receiveRecords.append(maybeCreateReceiveRecord()); return; } if (_mode == METAVOXEL_SERVER_MODE) { QVariant message; in >> message; handleMessage(message, in); - - // record the receipt - _receiveRecords.append(maybeCreateReceiveRecord()); return; } - SequencedTestMessage message; in >> message; _remoteState = message.state; - // record the receipt - _receiveRecords.append(maybeCreateReceiveRecord()); - for (QList::iterator it = _other->_unreliableMessagesSent.begin(); it != _other->_unreliableMessagesSent.end(); it++) { if (it->sequenceNumber == message.sequenceNumber) { @@ -1088,8 +1081,16 @@ void TestEndpoint::handleMessage(const QVariant& message, Bitstream& in) { } else if (userType == MetavoxelDeltaMessage::Type) { PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); - _data.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, getLastAcknowledgedSendRecord()->getLOD()); + _data.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, + _dataLOD = getLastAcknowledgedSendRecord()->getLOD()); + compareMetavoxelData(); + } else if (userType == MetavoxelDeltaPendingMessage::Type) { + if (!_reliableDeltaChannel) { + _reliableDeltaChannel = _sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX); + _reliableDeltaChannel->getBitstream().copyPersistentMappings(_sequencer.getInputStream()); + _reliableDeltaLOD = getLastAcknowledgedSendRecord()->getLOD(); + } } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { handleMessage(element, in); @@ -1098,13 +1099,15 @@ void TestEndpoint::handleMessage(const QVariant& message, Bitstream& in) { } PacketRecord* TestEndpoint::maybeCreateSendRecord() const { + if (_reliableDeltaChannel) { + return new TestSendRecord(_reliableDeltaLOD, _reliableDeltaData, _localState, _sequencer.getOutgoingPacketNumber()); + } 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); + return new TestReceiveRecord(_dataLOD, (_mode == METAVOXEL_SERVER_MODE) ? MetavoxelData() : _data, _remoteState); } void TestEndpoint::handleHighPriorityMessage(const QVariant& message) { @@ -1121,7 +1124,16 @@ void TestEndpoint::handleHighPriorityMessage(const QVariant& message) { highPriorityMessagesReceived++; } -void TestEndpoint::handleReliableMessage(const QVariant& message) { +void TestEndpoint::handleReliableMessage(const QVariant& message, Bitstream& in) { + if (message.userType() == MetavoxelDeltaMessage::Type) { + PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); + _data.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, _dataLOD = _reliableDeltaLOD); + _sequencer.getInputStream().persistReadMappings(in.getAndResetReadMappings()); + in.clearPersistentMappings(); + compareMetavoxelData(); + _reliableDeltaChannel = NULL; + return; + } if (message.userType() == ClearSharedObjectMessage::Type || message.userType() == ClearMainChannelSharedObjectMessage::Type) { return; @@ -1150,6 +1162,33 @@ void TestEndpoint::readReliableChannel() { streamedBytesReceived += bytes.size(); } +void TestEndpoint::checkReliableDeltaReceived() { + if (!_reliableDeltaChannel || _reliableDeltaChannel->getOffset() < _reliableDeltaReceivedOffset) { + return; + } + _sequencer.getOutputStream().persistWriteMappings(_reliableDeltaWriteMappings); + _reliableDeltaWriteMappings = Bitstream::WriteMappings(); + _reliableDeltaData = MetavoxelData(); + _reliableDeltaChannel = NULL; +} + +void TestEndpoint::compareMetavoxelData() { + // deep-compare data to sent version + 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); + } + return; + } + } + qDebug() << "Received metavoxel data with no corresponding send." << packetNumber; + exit(true); +} + 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 476a8c6295..5d719ccfdf 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -64,17 +64,21 @@ protected: private slots: void handleHighPriorityMessage(const QVariant& message); - void handleReliableMessage(const QVariant& message); + void handleReliableMessage(const QVariant& message, Bitstream& in); void readReliableChannel(); + void checkReliableDeltaReceived(); private: + void compareMetavoxelData(); + Mode _mode; SharedObjectPointer _localState; SharedObjectPointer _remoteState; MetavoxelData _data; + MetavoxelLOD _dataLOD; MetavoxelLOD _lod; SharedObjectPointer _sphere; @@ -94,6 +98,12 @@ private: float _reliableMessagesToSend; QVariantList _reliableMessagesSent; CircularBuffer _dataStreamed; + + ReliableChannel* _reliableDeltaChannel; + int _reliableDeltaReceivedOffset; + MetavoxelData _reliableDeltaData; + MetavoxelLOD _reliableDeltaLOD; + Bitstream::WriteMappings _reliableDeltaWriteMappings; }; /// A simple shared object.