diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f2e9fdb36e..1fd6045fe0 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -564,7 +564,7 @@ function MyController(hand) { "additiveBlending": 0, "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" } - + this.particleBeamObject = Entities.addEntity(particleBeamPropertiesObject); }; @@ -1588,7 +1588,9 @@ function MyController(hand) { ids.forEach(function(id) { var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); - if (props.name === 'pointer') { + if (!props || + !props.boundingBox || + props.name === 'pointer') { return; } var entityMinPoint = props.boundingBox.brn; diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 67ed261b5d..e3ad77870d 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -75,7 +75,7 @@ function createEmitNumberPropertyUpdateFunction(propertyName) { return function() { EventBridge.emitWebEvent( - '{ "type":"update", "properties":{"' + propertyName + '":' + Number(this.value.toFixed(4)) + '}}' + '{ "type":"update", "properties":{"' + propertyName + '":' + parseFloat(this.value).toFixed(4) + '}}' ); }; } diff --git a/examples/particle_explorer/particleExplorerTool.js b/examples/particle_explorer/particleExplorerTool.js index 20dfa6f1ba..007eb717ec 100644 --- a/examples/particle_explorer/particleExplorerTool.js +++ b/examples/particle_explorer/particleExplorerTool.js @@ -32,11 +32,9 @@ ParticleExplorerTool = function() { that.destroyWebView = function() { if (!that.webView) { - print("EBL CAN'ZT CLOSE WEB VIEW- IT DOESNT EXISTS!") return; } - print("EBL CLOSING WEB VIEW") that.webView.close(); that.webView = null; that.activeParticleEntity = 0; diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index c4ca0ab9a3..35cca311bb 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -10,12 +10,19 @@ import "../js/Utils.js" as Utils // windows will be childed. FocusScope { id: desktop - anchors.fill: parent; objectName: "desktop" + // Allow the scale of the desktop to be changed without screwing up the size relative to the parent. + height: parent.height / scale + width: parent.width / scale + onHeightChanged: d.repositionAll(); onWidthChanged: d.repositionAll(); + // Controls and windows can trigger this signal to ensure the desktop becomes visible + // when they're opened. + signal showDesktop(); + // Allows QML/JS to find the desktop through the parent chain property bool desktopRoot: true @@ -217,6 +224,8 @@ FocusScope { } reposition(targetWindow); + + showDesktop(); } function reposition(item) { @@ -314,7 +323,7 @@ FocusScope { enabled: DebugQML onTriggered: focusDebugger.visible = !focusDebugger.visible } - + } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64cd586e13..89ec196fad 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1198,7 +1198,8 @@ void Application::initializeUi() { // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to // support the window management and scripting proxies for VR use offscreenUi->createDesktop(QString("hifi/Desktop.qml")); - + connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); + // FIXME either expose so that dialogs can set this themselves or // do better detection in the offscreen UI of what has focus offscreenUi->setNavigationFocused(false); @@ -3772,8 +3773,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // The pending changes collecting the changes here render::PendingChanges pendingChanges; + // FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities // Background rendering decision - if (BackgroundRenderData::_item == 0) { + if (!render::Item::isValidID(BackgroundRenderData::_item)) { auto backgroundRenderData = make_shared(); auto backgroundRenderPayload = make_shared(backgroundRenderData); BackgroundRenderData::_item = _main3DScene->allocateID(); @@ -3798,8 +3800,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } } + // FIXME: Move this out of here!, WorldBox should be driven by the entity content just like the other entities // Make sure the WorldBox is in the scene - if (WorldBoxRenderData::_item == 0) { + if (!render::Item::isValidID(WorldBoxRenderData::_item)) { auto worldBoxRenderData = make_shared(); auto worldBoxRenderPayload = make_shared(worldBoxRenderData); @@ -5128,3 +5131,9 @@ void Application::readArgumentsFromLocalSocket() { qApp->openUrl(QString::fromUtf8(message)); } } + +void Application::showDesktop() { + if (!_overlayConductor.getEnabled()) { + _overlayConductor.setEnabled(true); + } +} \ No newline at end of file diff --git a/interface/src/Application.h b/interface/src/Application.h index 96bafce23f..8cc2a33038 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -280,6 +280,7 @@ public slots: void runTests(); private slots: + void showDesktop(); void clearDomainOctreeDetails(); void idle(uint64_t now); void aboutToQuit(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f62eb5a070..efa6f6004b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -315,6 +315,7 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_renderItemID); + render::Item::clearID(_renderItemID); _skeletonModel.removeFromScene(scene, pendingChanges); getHead()->getFaceModel().removeFromScene(scene, pendingChanges); for (auto& attachmentModel : _attachmentModels) { @@ -323,7 +324,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr>(_renderItemID, [](render::Payload& p) {}); } } diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 1e3390e49f..64b5e2aedf 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -229,6 +229,6 @@ bool Overlay::addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_renderItemID); - _renderItemID = render::Item::INVALID_ITEM_ID; + render::Item::clearID(_renderItemID); } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b53a5a0d05..2bc1286419 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -94,7 +94,7 @@ void Overlays::cleanupOverlaysToDelete() { Overlay::Pointer overlay = _overlaysToDelete.takeLast(); auto itemID = overlay->getRenderItemID(); - if (itemID != render::Item::INVALID_ITEM_ID) { + if (render::Item::isValidID(itemID)) { overlay->removeFromScene(overlay, scene, pendingChanges); } } while (!_overlaysToDelete.isEmpty()); @@ -241,7 +241,7 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { render::ItemKey itemKey = render::payloadGetKey(thisOverlay); if (itemKey != oldItemKey) { auto itemID = thisOverlay->getRenderItemID(); - if (itemID != render::Item::INVALID_ITEM_ID) { + if (render::Item::isValidID(itemID)) { render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; pendingChanges.resortItem(itemID, oldItemKey, itemKey); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 157e973f5b..ad6b436d02 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -104,7 +104,9 @@ void AudioInjector::restart() { // reset state to start sending from beginning again _nextFrame = 0; - _frameTimer->invalidate(); + if (_frameTimer) { + _frameTimer->invalidate(); + } _hasSentFirstFrame = false; // check our state to decide if we need extra handling for the restart request @@ -122,7 +124,9 @@ void AudioInjector::restart() { injectLocally(); } else { // wake the AudioInjectorManager back up if it's stuck waiting - injectorManager->restartFinishedInjector(this); + if (!injectorManager->restartFinishedInjector(this)) { + _state = State::Finished; // we're not playing, so reset the state used by isPlaying. + } } } } diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 032ad71145..b39da1c148 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -129,6 +129,15 @@ void AudioInjectorManager::run() { static const int MAX_INJECTORS_PER_THREAD = 40; // calculated based on AudioInjector time to send frame, with sufficient padding +bool AudioInjectorManager::wouldExceedLimits() { // Should be called inside of a lock. + if (_injectors.size() >= MAX_INJECTORS_PER_THREAD) { + qDebug() << "AudioInjectorManager::threadInjector could not thread AudioInjector - at max of" + << MAX_INJECTORS_PER_THREAD << "current audio injectors."; + return true; + } + return false; +} + bool AudioInjectorManager::threadInjector(AudioInjector* injector) { if (_shouldStop) { qDebug() << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; @@ -138,8 +147,9 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { // guard the injectors vector with a mutex Lock lock(_injectorsMutex); - // check if we'll be able to thread this injector (do we have < max concurrent injectors) - if (_injectors.size() < MAX_INJECTORS_PER_THREAD) { + if (wouldExceedLimits()) { + return false; + } else { if (!_thread) { createThread(); } @@ -156,23 +166,22 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { _injectorReady.notify_one(); return true; - } else { - // unable to thread this injector, at the max - qDebug() << "AudioInjectorManager::threadInjector could not thread AudioInjector - at max of" - << MAX_INJECTORS_PER_THREAD << "current audio injectors."; - return false; } } -void AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) { +bool AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) { if (!_shouldStop) { // guard the injectors vector with a mutex Lock lock(_injectorsMutex); + if (wouldExceedLimits()) { + return false; + } // add the injector to the queue with a send timestamp of now _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); // notify our wait condition so we can inject two frames for this injector immediately _injectorReady.notify_one(); } + return true; } diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h index 91648fff39..de5537856e 100644 --- a/libraries/audio/src/AudioInjectorManager.h +++ b/libraries/audio/src/AudioInjectorManager.h @@ -50,8 +50,9 @@ private: using Lock = std::unique_lock; bool threadInjector(AudioInjector* injector); - void restartFinishedInjector(AudioInjector* injector); + bool restartFinishedInjector(AudioInjector* injector); void notifyInjectorReadyCondition() { _injectorReady.notify_one(); } + bool wouldExceedLimits(); AudioInjectorManager() {}; AudioInjectorManager(const AudioInjectorManager&) = delete; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 7b3ea8edaa..30d3f9d83c 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -66,10 +66,11 @@ public: void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_myItem); + render::Item::clearID(_myItem); } void notifyChanged() { - if (_myItem == render::Item::INVALID_ITEM_ID) { + if (!render::Item::isValidID(_myItem)) { return; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 89f819b0da..e3870705c9 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -251,6 +251,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_myMetaItem); + render::Item::clearID(_myMetaItem); if (_model) { _model->removeFromScene(scene, pendingChanges); } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index e62dc82988..4d8eebf05b 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -179,6 +179,7 @@ void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_renderItemId); _scene = nullptr; + render::Item::clearID(_renderItemId); }; void RenderableParticleEffectEntityItem::update(const quint64& now) { @@ -199,7 +200,8 @@ void RenderableParticleEffectEntityItem::update(const quint64& now) { } void RenderableParticleEffectEntityItem::updateRenderItem() { - if (!_scene) { + // this 2 tests are synonyms for this class, but we would like to get rid of the _scene pointer ultimately + if (!_scene || !render::Item::isValidID(_renderItemId)) { return; } if (!getVisible()) { @@ -312,7 +314,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { } void RenderableParticleEffectEntityItem::notifyBoundChanged() { - if (_renderItemId == render::Item::INVALID_ITEM_ID) { + if (!render::Item::isValidID(_renderItemId)) { return; } render::PendingChanges pendingChanges; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 1029f3b3af..ef777df403 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -572,6 +572,7 @@ void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_myItem); + render::Item::clearID(_myItem); } namespace render { diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 56771296db..44345ac0a2 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -230,6 +230,7 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_pt void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_myMetaItem); + render::Item::clearID(_myMetaItem); if (_model) { _model->removeFromScene(scene, pendingChanges); } @@ -237,7 +238,7 @@ void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shar void RenderableZoneEntityItem::notifyBoundChanged() { - if (_myMetaItem == render::Item::INVALID_ITEM_ID) { + if (!render::Item::isValidID(_myMetaItem)) { return; } render::PendingChanges pendingChanges; diff --git a/libraries/networking/src/NLPacket.h b/libraries/networking/src/NLPacket.h index 57ada84607..4527094322 100644 --- a/libraries/networking/src/NLPacket.h +++ b/libraries/networking/src/NLPacket.h @@ -21,6 +21,17 @@ class NLPacket : public udt::Packet { Q_OBJECT public: + + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Packet Type | Packet Version | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Node UUID | Hash (only if verified) | Optional (only if sourced) + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // NLPacket Header Format + // this is used by the Octree classes - must be known at compile time static const int MAX_PACKET_HEADER_SIZE = sizeof(udt::Packet::SequenceNumberAndBitField) + sizeof(udt::Packet::MessageNumberAndBitField) + diff --git a/libraries/networking/src/udt/Constants.h b/libraries/networking/src/udt/Constants.h index 9f5f1db883..98569ead5a 100644 --- a/libraries/networking/src/udt/Constants.h +++ b/libraries/networking/src/udt/Constants.h @@ -26,10 +26,65 @@ namespace udt { static const int UDP_SEND_BUFFER_SIZE_BYTES = 1048576; static const int UDP_RECEIVE_BUFFER_SIZE_BYTES = 1048576; static const int DEFAULT_SYN_INTERVAL_USECS = 10 * 1000; - static const int SEQUENCE_NUMBER_BITS = sizeof(SequenceNumber) * 8; - static const int MESSAGE_LINE_NUMBER_BITS = 32; - static const int MESSAGE_NUMBER_BITS = 30; - static const uint32_t CONTROL_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 1); + + + // Header constants + + // Bit sizes (in order) + static const int CONTROL_BIT_SIZE = 1; + static const int RELIABILITY_BIT_SIZE = 1; + static const int MESSAGE_BIT_SIZE = 1; + static const int OBFUSCATION_LEVEL_SIZE = 2; + static const int SEQUENCE_NUMBER_SIZE= 27; + + static const int PACKET_POSITION_SIZE = 2; + static const int MESSAGE_NUMBER_SIZE = 30; + + static const int MESSAGE_PART_NUMBER_SIZE = 32; + + // Offsets + static const int SEQUENCE_NUMBER_OFFSET = 0; + static const int OBFUSCATION_LEVEL_OFFSET = SEQUENCE_NUMBER_OFFSET + SEQUENCE_NUMBER_SIZE; + static const int MESSAGE_BIT_OFFSET = OBFUSCATION_LEVEL_OFFSET + OBFUSCATION_LEVEL_SIZE; + static const int RELIABILITY_BIT_OFFSET = MESSAGE_BIT_OFFSET + MESSAGE_BIT_SIZE; + static const int CONTROL_BIT_OFFSET = RELIABILITY_BIT_OFFSET + RELIABILITY_BIT_SIZE; + + static const int MESSAGE_NUMBER_OFFSET = 0; + static const int PACKET_POSITION_OFFSET = MESSAGE_NUMBER_OFFSET + MESSAGE_NUMBER_SIZE; + + static const int MESSAGE_PART_NUMBER_OFFSET = 0; + + // Masks + static const uint32_t CONTROL_BIT_MASK = uint32_t(1) << CONTROL_BIT_OFFSET; + static const uint32_t RELIABILITY_BIT_MASK = uint32_t(1) << RELIABILITY_BIT_OFFSET; + static const uint32_t MESSAGE_BIT_MASK = uint32_t(1) << MESSAGE_BIT_OFFSET; + static const uint32_t OBFUSCATION_LEVEL_MASK = uint32_t(3) << OBFUSCATION_LEVEL_OFFSET; + static const uint32_t BIT_FIELD_MASK = CONTROL_BIT_MASK | RELIABILITY_BIT_MASK | MESSAGE_BIT_MASK | OBFUSCATION_LEVEL_MASK; + static const uint32_t SEQUENCE_NUMBER_MASK = ~BIT_FIELD_MASK; + + static const uint32_t PACKET_POSITION_MASK = uint32_t(3) << PACKET_POSITION_OFFSET; + static const uint32_t MESSAGE_NUMBER_MASK = ~PACKET_POSITION_MASK; + + static const uint32_t MESSAGE_PART_NUMBER_MASK = ~uint32_t(0); + + + // Static checks + static_assert(CONTROL_BIT_SIZE + RELIABILITY_BIT_SIZE + MESSAGE_BIT_SIZE + + OBFUSCATION_LEVEL_SIZE + SEQUENCE_NUMBER_SIZE == 32, "Sequence number line size incorrect"); + static_assert(PACKET_POSITION_SIZE + MESSAGE_NUMBER_SIZE == 32, "Message number line size incorrect"); + static_assert(MESSAGE_PART_NUMBER_SIZE == 32, "Message part number line size incorrect"); + + static_assert(CONTROL_BIT_MASK == 0x80000000, "CONTROL_BIT_MASK incorrect"); + static_assert(RELIABILITY_BIT_MASK == 0x40000000, "RELIABILITY_BIT_MASK incorrect"); + static_assert(MESSAGE_BIT_MASK == 0x20000000, "MESSAGE_BIT_MASK incorrect"); + static_assert(OBFUSCATION_LEVEL_MASK == 0x18000000, "OBFUSCATION_LEVEL_MASK incorrect"); + static_assert(BIT_FIELD_MASK == 0xF8000000, "BIT_FIELD_MASK incorrect"); + static_assert(SEQUENCE_NUMBER_MASK == 0x07FFFFFF, "SEQUENCE_NUMBER_MASK incorrect"); + + static_assert(PACKET_POSITION_MASK == 0xC0000000, "PACKET_POSITION_MASK incorrect"); + static_assert(MESSAGE_NUMBER_MASK == 0x3FFFFFFF, "MESSAGE_NUMBER_MASK incorrect"); + + static_assert(MESSAGE_PART_NUMBER_MASK == 0xFFFFFFFF, "MESSAGE_PART_NUMBER_MASK incorrect"); } #endif // hifi_udt_Constants_h diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index e068727e6f..088de49981 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -11,10 +11,35 @@ #include "Packet.h" +#include + +#include + using namespace udt; static int packetMetaTypeId = qRegisterMetaType("Packet*"); +using Key = uint64_t; +static const std::array KEYS {{ + 0x0, + 0x6362726973736574, + 0x7362697261726461, + 0x72687566666d616e, +}}; + +void xorHelper(char* start, int size, Key key) { + const auto end = start + size; + + auto p = start; + for (; p + sizeof(Key) < end; p += sizeof(Key)) { + *reinterpret_cast(p) ^= key; + } + + for (int i = 0; p < end; ++p || ++i) { + *p ^= *(reinterpret_cast(&key) + i); + } +} + int Packet::localHeaderSize(bool isPartOfMessage) { return sizeof(Packet::SequenceNumberAndBitField) + (isPartOfMessage ? sizeof(Packet::MessageNumberAndBitField) + sizeof(MessagePartNumber) : 0); @@ -69,44 +94,48 @@ Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& se readHeader(); adjustPayloadStartAndCapacity(Packet::localHeaderSize(_isPartOfMessage), _payloadSize > 0); + + if (getObfuscationLevel() != Packet::NoObfuscation) { +#ifdef UDT_CONNECTION_DEBUG + QString debugString = "Unobfuscating packet %1 with level %2"; + debugString = debugString.arg(QString::number((uint32_t)getSequenceNumber()), + QString::number(getObfuscationLevel())); + + if (isPartOfMessage()) { + debugString += "\n"; + debugString += " Message Number: %1, Part Number: %2."; + debugString = debugString.arg(QString::number(getMessageNumber()), + QString::number(getMessagePartNumber())); + } + + static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("^Unobfuscating packet .*"); + qDebug() << qPrintable(debugString); +#endif + + obfuscate(NoObfuscation); // Undo obfuscation + } } -Packet::Packet(const Packet& other) : - BasePacket(other) -{ - _isReliable = other._isReliable; - _isPartOfMessage = other._isPartOfMessage; - _sequenceNumber = other._sequenceNumber; +Packet::Packet(const Packet& other) : BasePacket(other) { + copyMembers(other); } Packet& Packet::operator=(const Packet& other) { BasePacket::operator=(other); - - _isReliable = other._isReliable; - _isPartOfMessage = other._isPartOfMessage; - _sequenceNumber = other._sequenceNumber; + + copyMembers(other); return *this; } -Packet::Packet(Packet&& other) : - BasePacket(std::move(other)) -{ - _isReliable = other._isReliable; - _isPartOfMessage = other._isPartOfMessage; - _sequenceNumber = other._sequenceNumber; - _packetPosition = other._packetPosition; - _messageNumber = other._messageNumber; +Packet::Packet(Packet&& other) : BasePacket(std::move(other)) { + copyMembers(other); } Packet& Packet::operator=(Packet&& other) { BasePacket::operator=(std::move(other)); - - _isReliable = other._isReliable; - _isPartOfMessage = other._isPartOfMessage; - _sequenceNumber = other._sequenceNumber; - _packetPosition = other._packetPosition; - _messageNumber = other._messageNumber; + + copyMembers(other); return *this; } @@ -124,13 +153,27 @@ void Packet::writeSequenceNumber(SequenceNumber sequenceNumber) const { writeHeader(); } -static const uint32_t RELIABILITY_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 2); -static const uint32_t MESSAGE_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 3); -static const uint32_t BIT_FIELD_MASK = CONTROL_BIT_MASK | RELIABILITY_BIT_MASK | MESSAGE_BIT_MASK; +void Packet::obfuscate(ObfuscationLevel level) { + auto obfuscationKey = KEYS[getObfuscationLevel()] ^ KEYS[level]; // Undo old and apply new one. + if (obfuscationKey != 0) { + xorHelper(getData() + localHeaderSize(isPartOfMessage()), + getDataSize() - localHeaderSize(isPartOfMessage()), obfuscationKey); -static const uint8_t PACKET_POSITION_OFFSET = 30; -static const uint32_t PACKET_POSITION_MASK = uint32_t(0x03) << PACKET_POSITION_OFFSET; -static const uint32_t MESSAGE_NUMBER_MASK = ~PACKET_POSITION_MASK; + // Update members and header + _obfuscationLevel = level; + writeHeader(); + } +} + +void Packet::copyMembers(const Packet& other) { + _isReliable = other._isReliable; + _isPartOfMessage = other._isPartOfMessage; + _obfuscationLevel = other._obfuscationLevel; + _sequenceNumber = other._sequenceNumber; + _packetPosition = other._packetPosition; + _messageNumber = other._messageNumber; + _messagePartNumber = other._messagePartNumber; +} void Packet::readHeader() const { SequenceNumberAndBitField* seqNumBitField = reinterpret_cast(_packet.get()); @@ -139,10 +182,12 @@ void Packet::readHeader() const { _isReliable = (bool) (*seqNumBitField & RELIABILITY_BIT_MASK); // Only keep reliability bit _isPartOfMessage = (bool) (*seqNumBitField & MESSAGE_BIT_MASK); // Only keep message bit - _sequenceNumber = SequenceNumber{ *seqNumBitField & ~BIT_FIELD_MASK }; // Remove the bit field + _obfuscationLevel = (ObfuscationLevel)((*seqNumBitField & OBFUSCATION_LEVEL_MASK) >> OBFUSCATION_LEVEL_OFFSET); + _sequenceNumber = SequenceNumber{ *seqNumBitField & SEQUENCE_NUMBER_MASK }; // Remove the bit field if (_isPartOfMessage) { MessageNumberAndBitField* messageNumberAndBitField = seqNumBitField + 1; + _messageNumber = *messageNumberAndBitField & MESSAGE_NUMBER_MASK; _packetPosition = static_cast(*messageNumberAndBitField >> PACKET_POSITION_OFFSET); @@ -163,6 +208,10 @@ void Packet::writeHeader() const { if (_isReliable) { *seqNumBitField |= RELIABILITY_BIT_MASK; } + + if (_obfuscationLevel != NoObfuscation) { + *seqNumBitField |= (_obfuscationLevel << OBFUSCATION_LEVEL_OFFSET); + } if (_isPartOfMessage) { *seqNumBitField |= MESSAGE_BIT_MASK; diff --git a/libraries/networking/src/udt/Packet.h b/libraries/networking/src/udt/Packet.h index 02d2c3d9bd..cad5cccb0e 100644 --- a/libraries/networking/src/udt/Packet.h +++ b/libraries/networking/src/udt/Packet.h @@ -25,6 +25,25 @@ namespace udt { class Packet : public BasePacket { Q_OBJECT public: + // Packet Header Format + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |C|R|M| O | Sequence Number | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | P | Message Number | Optional (only if M = 1) + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Message Part Number | Optional (only if M = 1) + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // C: Control bit + // R: Reliable bit + // M: Message bit + // O: Obfuscation level + // P: Position bits + + // NOTE: The SequenceNumber is only actually 29 bits to leave room for a bit field using SequenceNumberAndBitField = uint32_t; @@ -35,12 +54,20 @@ public: // Use same size as MessageNumberAndBitField so we can use the enum with bitwise operations enum PacketPosition : MessageNumberAndBitField { - ONLY = 0x0, - FIRST = 0x2, - MIDDLE = 0x3, - LAST = 0x1 + ONLY = 0x0, // 00 + FIRST = 0x2, // 10 + MIDDLE = 0x3, // 11 + LAST = 0x1 // 01 }; - + + // Use same size as SequenceNumberAndBitField so we can use the enum with bitwise operations + enum ObfuscationLevel : SequenceNumberAndBitField { + NoObfuscation = 0x0, // 00 + ObfuscationL1 = 0x1, // 01 + ObfuscationL2 = 0x2, // 10 + ObfuscationL3 = 0x3, // 11 + }; + static std::unique_ptr create(qint64 size = -1, bool isReliable = false, bool isPartOfMessage = false); static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); @@ -56,7 +83,8 @@ public: bool isPartOfMessage() const { return _isPartOfMessage; } bool isReliable() const { return _isReliable; } - + + ObfuscationLevel getObfuscationLevel() const { return _obfuscationLevel; } SequenceNumber getSequenceNumber() const { return _sequenceNumber; } MessageNumber getMessageNumber() const { return _messageNumber; } PacketPosition getPacketPosition() const { return _packetPosition; } @@ -64,6 +92,7 @@ public: void writeMessageNumber(MessageNumber messageNumber, PacketPosition position, MessagePartNumber messagePartNumber); void writeSequenceNumber(SequenceNumber sequenceNumber) const; + void obfuscate(ObfuscationLevel level); protected: Packet(qint64 size, bool isReliable = false, bool isPartOfMessage = false); @@ -76,6 +105,8 @@ protected: Packet& operator=(Packet&& other); private: + void copyMembers(const Packet& other); + // Header readers - these read data to member variables after pulling packet off wire void readHeader() const; void writeHeader() const; @@ -83,6 +114,7 @@ private: // Simple holders to prevent multiple reading and bitwise ops mutable bool _isReliable { false }; mutable bool _isPartOfMessage { false }; + mutable ObfuscationLevel _obfuscationLevel { NoObfuscation }; mutable SequenceNumber _sequenceNumber { 0 }; mutable MessageNumber _messageNumber { 0 }; mutable PacketPosition _packetPosition { PacketPosition::ONLY }; diff --git a/libraries/networking/src/udt/PacketQueue.cpp b/libraries/networking/src/udt/PacketQueue.cpp index bc3a1796e2..8ff2333a20 100644 --- a/libraries/networking/src/udt/PacketQueue.cpp +++ b/libraries/networking/src/udt/PacketQueue.cpp @@ -16,7 +16,7 @@ using namespace udt; MessageNumber PacketQueue::getNextMessageNumber() { - static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_BITS; + static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_SIZE; _currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER; return _currentMessageNumber; } diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 4d6e431b45..18269d1d43 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "../NetworkLogging.h" @@ -225,7 +226,9 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket, { // Insert the packet we have just sent in the sent list QWriteLocker locker(&_sentLock); - _sentPackets[newPacket->getSequenceNumber()].swap(newPacket); + auto& entry = _sentPackets[newPacket->getSequenceNumber()]; + entry.first = 0; // No resend + entry.second.swap(newPacket); } Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list"); @@ -354,14 +357,46 @@ bool SendQueue::maybeResendPacket() { auto it = _sentPackets.find(resendNumber); if (it != _sentPackets.end()) { + auto& entry = it->second; // we found the packet - grab it - auto& resendPacket = *(it->second); - - // send it off - sendPacket(resendPacket); - - // unlock the sent packets - sentLocker.unlock(); + auto& resendPacket = *(entry.second); + ++entry.first; // Add 1 resend + + Packet::ObfuscationLevel level = (Packet::ObfuscationLevel)(entry.first < 2 ? 0 : (entry.first - 2) % 4); + + if (level != Packet::NoObfuscation) { +#ifdef UDT_CONNECTION_DEBUG + QString debugString = "Obfuscating packet %1 with level %2"; + debugString = debugString.arg(QString::number((uint32_t)resendPacket.getSequenceNumber()), + QString::number(level)); + if (resendPacket.isPartOfMessage()) { + debugString += "\n"; + debugString += " Message Number: %1, Part Number: %2."; + debugString = debugString.arg(QString::number(resendPacket.getMessageNumber()), + QString::number(resendPacket.getMessagePartNumber())); + } + static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("^Obfuscating packet .*"); + qCritical() << qPrintable(debugString); +#endif + + // Create copy of the packet + auto packet = Packet::createCopy(resendPacket); + + // unlock the sent packets + sentLocker.unlock(); + + // Obfuscate packet + packet->obfuscate(level); + + // send it off + sendPacket(*packet); + } else { + // send it off + sendPacket(resendPacket); + + // unlock the sent packets + sentLocker.unlock(); + } emit packetRetransmitted(); diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 5428e7a26d..da8f5e6c3f 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -126,7 +126,8 @@ private: LossList _naks; // Sequence numbers of packets to resend mutable QReadWriteLock _sentLock; // Protects the sent packet list - std::unordered_map> _sentPackets; // Packets waiting for ACK. + using PacketResendPair = std::pair>; // Number of resend + packet ptr + std::unordered_map _sentPackets; // Packets waiting for ACK. std::mutex _handshakeMutex; // Protects the handshake ACK condition_variable std::atomic _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client diff --git a/libraries/networking/src/udt/SequenceNumber.h b/libraries/networking/src/udt/SequenceNumber.h index a75f3478b8..3abc80bdd8 100644 --- a/libraries/networking/src/udt/SequenceNumber.h +++ b/libraries/networking/src/udt/SequenceNumber.h @@ -24,9 +24,9 @@ public: using Type = int32_t; using UType = uint32_t; - // Values are for 29 bit SequenceNumber - static const Type THRESHOLD = 0x0FFFFFFF; // threshold for comparing sequence numbers - static const Type MAX = 0x1FFFFFFF; // maximum sequence number used in UDT + // Values are for 27 bit SequenceNumber + static const Type THRESHOLD = 0x03FFFFFF; // threshold for comparing sequence numbers + static const Type MAX = 0x07FFFFFF; // maximum sequence number used in UDT SequenceNumber() = default; SequenceNumber(const SequenceNumber& other) : _value(other._value) {} diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 8fb5db3853..1eb7c04331 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -277,7 +277,7 @@ void Socket::readPendingDatagrams() { if (packet->isReliable()) { // if this was a reliable packet then signal the matching connection with the sequence number auto& connection = findOrCreateConnection(senderSockAddr); - + if (!connection.processReceivedSequenceNumber(packet->getSequenceNumber(), packet->getDataSize(), packet->getPayloadSize())) { diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 74c832a0c4..8aaaf07300 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -217,6 +217,10 @@ public: static const ID INVALID_ITEM_ID = 0; static const ItemCell INVALID_CELL = -1; + // Convenient function to clear an ID or check it s valid + static void clearID(ID& id) { id = INVALID_ITEM_ID; } + static bool isValidID(const ID id) { return id != INVALID_ITEM_ID; } + // Bound is the AABBox fully containing this item typedef AABox Bound; diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index fa40fedb9b..7e84836c86 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -112,6 +112,7 @@ void OffscreenUi::create(QOpenGLContext* context) { } void OffscreenUi::show(const QUrl& url, const QString& name, std::function f) { + emit showDesktop(); QQuickItem* item = getRootItem()->findChild(name); // First load? if (!item) { @@ -127,6 +128,7 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::functionfindChild(name); // Already loaded? if (item) { + emit showDesktop(); item->setVisible(!item->isVisible()); return; } @@ -134,6 +136,7 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::functionfindChild(name); if (item && !item->isVisible()) { + emit showDesktop(); item->setVisible(true); } } @@ -439,6 +442,8 @@ void OffscreenUi::createDesktop(const QUrl& url) { new VrMenu(this); new KeyboardFocusHack(); + + connect(_desktop, SIGNAL(showDesktop()), this, SLOT(showDesktop())); } QQuickItem* OffscreenUi::getDesktop() { diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index de479853f3..8b00180d43 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -106,6 +106,9 @@ public: // Compatibility with QInputDialog::getItem static QString getItem(void *ignored, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone); +signals: + void showDesktop(); + private: QString fileDialog(const QVariantMap& properties);