diff --git a/.gitignore b/.gitignore index df91e0ca7b..8d92fe770b 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,6 @@ interface/compiledResources # GPUCache interface/resources/GPUCache/* + +# package lock file for JSDoc tool +tools/jsdoc/package-lock.json diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index 64c0e9a643..0daef5ae05 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -11,11 +11,11 @@ Should you choose not to install Qt5 via a package manager that handles dependen ## Ubuntu 16.04 specific build guide ### Prepare environment - +hifiqt5.10.1 Install qt: ```bash -wget http://debian.highfidelity.com/pool/h/hi/hifi-qt5.10.1_5.10.1_amd64.deb -sudo dpkg -i hifi-qt5.10.1_5.10.1_amd64.deb +wget http://debian.highfidelity.com/pool/h/hi/hifiqt5.10.1_5.10.1_amd64.deb +sudo dpkg -i hifiqt5.10.1_5.10.1_amd64.deb ``` Install build dependencies: diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 10b8d44545..1df901dd98 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -380,7 +380,7 @@ void Agent::executeScript() { using namespace recording; static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME); - Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) { + Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [scriptedAvatar](Frame::ConstPointer frame) { auto recordingInterface = DependencyManager::get(); bool useFrameSkeleton = recordingInterface->getPlayerUseSkeletonModel(); diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 23df64be2e..22ed01fd00 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -61,13 +61,13 @@ static const ScriptBakeVersion CURRENT_SCRIPT_BAKE_VERSION = (ScriptBakeVersion) BakedAssetType assetTypeForExtension(const QString& extension) { auto extensionLower = extension.toLower(); if (BAKEABLE_MODEL_EXTENSIONS.contains(extensionLower)) { - return Model; + return BakedAssetType::Model; } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extensionLower.toLocal8Bit())) { - return Texture; + return BakedAssetType::Texture; } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extensionLower)) { - return Script; + return BakedAssetType::Script; } - return Undefined; + return BakedAssetType::Undefined; } BakedAssetType assetTypeForFilename(const QString& filename) { @@ -82,11 +82,11 @@ BakedAssetType assetTypeForFilename(const QString& filename) { QString bakedFilenameForAssetType(BakedAssetType type) { switch (type) { - case Model: + case BakedAssetType::Model: return BAKED_MODEL_SIMPLE_NAME; - case Texture: + case BakedAssetType::Texture: return BAKED_TEXTURE_SIMPLE_NAME; - case Script: + case BakedAssetType::Script: return BAKED_SCRIPT_SIMPLE_NAME; default: return ""; @@ -95,11 +95,11 @@ QString bakedFilenameForAssetType(BakedAssetType type) { BakeVersion currentBakeVersionForAssetType(BakedAssetType type) { switch (type) { - case Model: + case BakedAssetType::Model: return (BakeVersion)CURRENT_MODEL_BAKE_VERSION; - case Texture: + case BakedAssetType::Texture: return (BakeVersion)CURRENT_TEXTURE_BAKE_VERSION; - case Script: + case BakedAssetType::Script: return (BakeVersion)CURRENT_SCRIPT_BAKE_VERSION; default: return 0; @@ -222,7 +222,7 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU BakedAssetType type = assetTypeForFilename(path); - if (type == Undefined) { + if (type == BakedAssetType::Undefined) { return false; } @@ -241,7 +241,7 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU AssetMeta meta; std::tie(loaded, meta) = readMetaFile(assetHash); - if (type == Texture && !loaded) { + if (type == BakedAssetType::Texture && !loaded) { return false; } @@ -901,7 +901,7 @@ void AssetServer::handleAssetUpload(QSharedPointer message, Sha if (canWriteToAssetServer) { - qCDebug(asset_server) << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(message->getSourceID()); + qCDebug(asset_server) << "Starting an UploadAssetTask for upload from" << message->getSourceID(); auto task = new UploadAssetTask(message, senderNode, _filesDirectory, _filesizeLimit); _transferTaskPool.start(task); @@ -1546,7 +1546,7 @@ bool AssetServer::setBakingEnabled(const AssetUtils::AssetPathList& paths, bool auto it = _fileMappings.find(path); if (it != _fileMappings.end()) { auto type = assetTypeForFilename(path); - if (type == Undefined) { + if (type == BakedAssetType::Undefined) { continue; } QString bakedFilename = bakedFilenameForAssetType(type); diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index a55a15e6fc..fb88df0171 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -27,7 +27,7 @@ using BakeVersion = int; static const BakeVersion INITIAL_BAKE_VERSION = 0; static const BakeVersion NEEDS_BAKING_BAKE_VERSION = -1; -enum BakedAssetType : int { +enum class BakedAssetType : int { Model = 0, Texture, Script, diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 8af4eec934..34eb138697 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -117,12 +117,13 @@ void AudioMixer::queueAudioPacket(QSharedPointer message, Share void AudioMixer::queueReplicatedAudioPacket(QSharedPointer message) { // make sure we have a replicated node for the original sender of the packet auto nodeList = DependencyManager::get(); - - QUuid nodeID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + // Node ID is now part of user data, since replicated audio packets are non-sourced. + QUuid nodeID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); auto replicatedNode = nodeList->addOrUpdateNode(nodeID, NodeType::Agent, message->getSenderSockAddr(), message->getSenderSockAddr(), - true, true); + Node::NULL_LOCAL_ID, true, true); replicatedNode->setLastHeardMicrostamp(usecTimestampNow()); // construct a "fake" audio received message from the byte array and packet list information @@ -136,7 +137,7 @@ void AudioMixer::queueReplicatedAudioPacket(QSharedPointer mess auto replicatedMessage = QSharedPointer::create(audioData, rewrittenType, versionForPacketType(rewrittenType), - message->getSenderSockAddr(), nodeID); + message->getSenderSockAddr(), Node::NULL_LOCAL_ID); getOrCreateClientData(replicatedNode.data())->queuePacket(replicatedMessage, replicatedNode); } diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 3c8e38f278..b447048ac9 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -376,6 +376,11 @@ void AudioMixerSlave::addStream(AudioMixerClientData& listenerNodeData, const QU return; } + if (streamToAdd.getType() == PositionalAudioStream::Injector) { + // apply per-avatar gain to positional audio injectors, which wouldn't otherwise be affected by PAL sliders + hrtf.setGainAdjustment(listenerNodeData.hrtfForStream(sourceNodeID, QUuid()).getGainAdjustment()); + } + hrtf.render(_bufferSamples, _mixSamples, HRTF_DATASET_INDEX, azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 929941c05c..29340f6474 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -74,7 +74,7 @@ SharedNodePointer addOrUpdateReplicatedNode(const QUuid& nodeID, const HifiSockA auto replicatedNode = DependencyManager::get()->addOrUpdateNode(nodeID, NodeType::Agent, senderSockAddr, senderSockAddr, - true, true); + Node::NULL_LOCAL_ID, true, true); replicatedNode->setLastHeardMicrostamp(usecTimestampNow()); @@ -112,8 +112,8 @@ void AvatarMixer::handleReplicatedPacket(QSharedPointer message void AvatarMixer::handleReplicatedBulkAvatarPacket(QSharedPointer message) { while (message->getBytesLeftToRead()) { // first, grab the node ID for this replicated avatar + // Node ID is now part of user data, since ReplicatedBulkAvatarPacket is non-sourced. auto nodeID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - // make sure we have an upstream replicated node that matches auto replicatedNode = addOrUpdateReplicatedNode(nodeID, message->getSenderSockAddr()); @@ -127,7 +127,7 @@ void AvatarMixer::handleReplicatedBulkAvatarPacket(QSharedPointer::create(avatarByteArray, PacketType::AvatarData, versionForPacketType(PacketType::AvatarData), - message->getSenderSockAddr(), nodeID); + message->getSenderSockAddr(), Node::NULL_LOCAL_ID); // queue up the replicated avatar data with the client data for the replicated node auto start = usecTimestampNow(); diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index 594f423838..1e2bd15429 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -48,8 +48,6 @@ private: void preDistributionProcessing() override; bool hasSomethingToSend(OctreeQueryNode* nodeData) override { return !_sendQueue.empty(); } bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) override { return viewFrustumChanged || _traversal.finished(); } - void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene) override {}; - bool shouldTraverseAndSend(OctreeQueryNode* nodeData) override { return true; } DiffTraversal _traversal; EntityPriorityQueue _sendQueue; diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 715e83f403..de49bd461c 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -304,23 +304,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* return numPackets; } -void OctreeSendThread::preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene) { - // If we're starting a full scene, then definitely we want to empty the elementBag - if (isFullScene) { - nodeData->elementBag.deleteAll(); - } - - // This is the start of "resending" the scene. - bool dontRestartSceneOnMove = false; // this is experimental - if (dontRestartSceneOnMove) { - if (nodeData->elementBag.isEmpty()) { - nodeData->elementBag.insert(_myServer->getOctree()->getRoot()); - } - } else { - nodeData->elementBag.insert(_myServer->getOctree()->getRoot()); - } -} - /// Version of octree element distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { OctreeServer::didPacketDistributor(this); @@ -366,16 +349,8 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* // the current view frustum for things to send. if (shouldStartNewTraversal(nodeData, viewFrustumChanged)) { - // if our view has changed, we need to reset these things... - if (viewFrustumChanged) { - if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) { - nodeData->dumpOutOfView(); - } - } - // track completed scenes and send out the stats packet accordingly nodeData->stats.sceneCompleted(); - nodeData->setLastRootTimestamp(_myServer->getOctree()->getRoot()->getLastChanged()); _myServer->getOctree()->releaseSceneEncodeData(&nodeData->extraEncodeData); // TODO: add these to stats page @@ -389,111 +364,74 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* // TODO: add these to stats page //::startSceneSleepTime = _usleepTime; - nodeData->sceneStart(usecTimestampNow() - CHANGE_FUDGE); // start tracking our stats nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot()); - - preStartNewScene(nodeData, isFullScene); } - // If we have something in our elementBag, then turn them into packets and send them out... - if (shouldTraverseAndSend(nodeData)) { - quint64 start = usecTimestampNow(); + quint64 start = usecTimestampNow(); - _myServer->getOctree()->withReadLock([&]{ - traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene); - }); + _myServer->getOctree()->withReadLock([&]{ + traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene); + }); - // Here's where we can/should allow the server to send other data... - // send the environment packet - // TODO: should we turn this into a while loop to better handle sending multiple special packets - if (_myServer->hasSpecialPacketsToSend(node) && !nodeData->isShuttingDown()) { - int specialPacketsSent = 0; - int specialBytesSent = _myServer->sendSpecialPackets(node, nodeData, specialPacketsSent); - nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed - _truePacketsSent += specialPacketsSent; - _trueBytesSent += specialBytesSent; - _packetsSentThisInterval += specialPacketsSent; + // Here's where we can/should allow the server to send other data... + // send the environment packet + // TODO: should we turn this into a while loop to better handle sending multiple special packets + if (_myServer->hasSpecialPacketsToSend(node) && !nodeData->isShuttingDown()) { + int specialPacketsSent = 0; + int specialBytesSent = _myServer->sendSpecialPackets(node, nodeData, specialPacketsSent); + nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed + _truePacketsSent += specialPacketsSent; + _trueBytesSent += specialBytesSent; + _packetsSentThisInterval += specialPacketsSent; - _totalPackets += specialPacketsSent; - _totalBytes += specialBytesSent; + _totalPackets += specialPacketsSent; + _totalBytes += specialBytesSent; - _totalSpecialPackets += specialPacketsSent; - _totalSpecialBytes += specialBytesSent; + _totalSpecialPackets += specialPacketsSent; + _totalSpecialBytes += specialBytesSent; + } + + // calculate max number of packets that can be sent during this interval + int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND)); + int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); + + // Re-send packets that were nacked by the client + while (nodeData->hasNextNackedPacket() && _packetsSentThisInterval < maxPacketsPerInterval) { + const NLPacket* packet = nodeData->getNextNackedPacket(); + if (packet) { + DependencyManager::get()->sendUnreliablePacket(*packet, *node); + int numBytes = packet->getDataSize(); + _truePacketsSent++; + _trueBytesSent += numBytes; + _packetsSentThisInterval++; + + _totalPackets++; + _totalBytes += numBytes; + _totalWastedBytes += udt::MAX_PACKET_SIZE - packet->getDataSize(); } + } - // calculate max number of packets that can be sent during this interval - int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND)); - int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); + quint64 end = usecTimestampNow(); + int elapsedmsec = (end - start) / USECS_PER_MSEC; + OctreeServer::trackLoopTime(elapsedmsec); - // Re-send packets that were nacked by the client - while (nodeData->hasNextNackedPacket() && _packetsSentThisInterval < maxPacketsPerInterval) { - const NLPacket* packet = nodeData->getNextNackedPacket(); - if (packet) { - DependencyManager::get()->sendUnreliablePacket(*packet, *node); - int numBytes = packet->getDataSize(); - _truePacketsSent++; - _trueBytesSent += numBytes; - _packetsSentThisInterval++; + // if we've sent everything, then we want to remember that we've sent all + // the octree elements from the current view frustum + if (!hasSomethingToSend(nodeData)) { + nodeData->setViewSent(true); - _totalPackets++; - _totalBytes += numBytes; - _totalWastedBytes += udt::MAX_PACKET_SIZE - packet->getDataSize(); - } + // If this was a full scene then make sure we really send out a stats packet at this point so that + // the clients will know the scene is stable + if (isFullScene) { + nodeData->stats.sceneCompleted(); + handlePacketSend(node, nodeData, true); } - - quint64 end = usecTimestampNow(); - int elapsedmsec = (end - start) / USECS_PER_MSEC; - OctreeServer::trackLoopTime(elapsedmsec); - - // if after sending packets we've emptied our bag, then we want to remember that we've sent all - // the octree elements from the current view frustum - if (!hasSomethingToSend(nodeData)) { - nodeData->updateLastKnownViewFrustum(); - nodeData->setViewSent(true); - - // If this was a full scene then make sure we really send out a stats packet at this point so that - // the clients will know the scene is stable - if (isFullScene) { - nodeData->stats.sceneCompleted(); - handlePacketSend(node, nodeData, true); - } - } - - } // end if bag wasn't empty, and so we sent stuff... + } return _truePacketsSent; } -bool OctreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) { - bool somethingToSend = false; - OctreeQueryNode* nodeData = static_cast(params.nodeData); - if (!nodeData->elementBag.isEmpty()) { - quint64 encodeStart = usecTimestampNow(); - quint64 lockWaitStart = encodeStart; - - _myServer->getOctree()->withReadLock([&]{ - OctreeServer::trackTreeWaitTime((float)(usecTimestampNow() - lockWaitStart)); - - OctreeElementPointer subTree = nodeData->elementBag.extract(); - if (subTree) { - // NOTE: this is where the tree "contents" are actually packed - nodeData->stats.encodeStarted(); - _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->elementBag, params); - nodeData->stats.encodeStopped(); - - somethingToSend = true; - } - }); - - OctreeServer::trackEncodeTime((float)(usecTimestampNow() - encodeStart)); - } else { - OctreeServer::trackTreeWaitTime(OctreeServer::SKIP_TIME); - OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME); - } - return somethingToSend; -} - void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) { // calculate max number of packets that can be sent during this interval int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND)); @@ -502,21 +440,12 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre int extraPackingAttempts = 0; // init params once outside the while loop - int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); - int boundaryLevelAdjust = boundaryLevelAdjustClient + - (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - float octreeSizeScale = nodeData->getOctreeSizeScale(); - EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP, - viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale, - isFullScene, nodeData); + EncodeBitstreamParams params(WANT_EXISTS_BITS, nodeData); // Our trackSend() function is implemented by the server subclass, and will be called back as new entities/data elements are sent params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) { _myServer->trackSend(dataID, dataEdited, _nodeUuid); }; nodeData->copyCurrentViewFrustum(params.viewFrustum); - if (viewFrustumChanged) { - nodeData->copyLastKnownViewFrustum(params.lastViewFrustum); - } bool somethingToSend = true; // assume we have something bool hadSomething = hasSomethingToSend(nodeData); @@ -536,8 +465,8 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre extraPackingAttempts++; } - // If the bag had contents but is now empty then we know we've sent the entire scene. - bool completedScene = hadSomething && nodeData->elementBag.isEmpty(); + // If we had something to send, but now we don't, then we know we've sent the entire scene. + bool completedScene = hadSomething; if (completedScene || lastNodeDidntFit) { // we probably want to flush what has accumulated in nodeData but: // do we have more data to send? and is there room? diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 220952e209..91c0ec7adc 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -54,7 +54,7 @@ protected: virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene); - virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters); + virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) = 0; OctreePacketData _packetData; QWeakPointer _node; @@ -63,14 +63,12 @@ protected: private: /// Called before a packetDistributor pass to allow for pre-distribution processing - virtual void preDistributionProcessing() {}; + virtual void preDistributionProcessing() = 0; int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, bool dontSuppressDuplicate = false); int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged); - virtual bool hasSomethingToSend(OctreeQueryNode* nodeData) { return !nodeData->elementBag.isEmpty(); } - virtual bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) { return viewFrustumChanged || !hasSomethingToSend(nodeData); } - virtual void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene); - virtual bool shouldTraverseAndSend(OctreeQueryNode* nodeData) { return hasSomethingToSend(nodeData); } + virtual bool hasSomethingToSend(OctreeQueryNode* nodeData) = 0; + virtual bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) = 0; int _truePacketsSent { 0 }; // available for debug stats int _trueBytesSent { 0 }; // available for debug stats diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 0dbd24fe9c..fad2c1f026 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -876,10 +876,6 @@ void OctreeServer::parsePayload() { } } -OctreeServer::UniqueSendThread OctreeServer::newSendThread(const SharedNodePointer& node) { - return std::unique_ptr(new OctreeSendThread(this, node)); -} - OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) { auto sendThread = newSendThread(node); diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index e7efc731f2..b25e537d70 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -174,7 +174,7 @@ protected: void beginRunning(QByteArray replaceData); UniqueSendThread createSendThread(const SharedNodePointer& node); - virtual UniqueSendThread newSendThread(const SharedNodePointer& node); + virtual UniqueSendThread newSendThread(const SharedNodePointer& node) = 0; int _argc; const char** _argv; diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 7d0b538f6e..d78f0aaeb3 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -26,7 +27,7 @@ using SharedAssignmentPointer = QSharedPointer; DomainGatekeeper::DomainGatekeeper(DomainServer* server) : _server(server) { - + initLocalIDManagement(); } void DomainGatekeeper::addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID, @@ -451,11 +452,12 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect return SharedNodePointer(); } - QUuid hintNodeID; + QUuid existingNodeID; // in case this is a node that's failing to connect // double check we don't have the same node whose sockets match exactly already in the list limitedNodeList->eachNodeBreakable([&](const SharedNodePointer& node){ + if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) { // we have a node that already has these exact sockets - this can occur if a node // is failing to connect to the domain @@ -465,15 +467,20 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect auto existingNodeData = static_cast(node->getLinkedData()); if (existingNodeData->getUsername() == username) { - hintNodeID = node->getUUID(); + qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID(); + existingNodeID = node->getUUID(); return false; } } return true; }); + if (!existingNodeID.isNull()) { + limitedNodeList->killNodeWithUUID(existingNodeID); + } + // add the connecting node (or re-use the matched one from eachNodeBreakable above) - SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID); + SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection); // set the edit rights for this user newNode->setPermissions(userPerms); @@ -523,8 +530,10 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node auto limitedNodeList = DependencyManager::get(); + Node::LocalID newLocalID = findOrCreateLocalID(nodeID); SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeID, nodeConnection.nodeType, - nodeConnection.publicSockAddr, nodeConnection.localSockAddr); + nodeConnection.publicSockAddr, nodeConnection.localSockAddr, + newLocalID); // So that we can send messages to this node at will - we need to activate the correct socket on this node now newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket); @@ -1014,3 +1023,31 @@ void DomainGatekeeper::refreshGroupsCache() { _server->_settingsManager.debugDumpGroupsState(); #endif } + +void DomainGatekeeper::initLocalIDManagement() { + std::uniform_int_distribution sixteenBitRand; + std::random_device randomDevice; + std::default_random_engine engine { randomDevice() }; + _currentLocalID = sixteenBitRand(engine); + // Ensure increment is odd. + _idIncrement = sixteenBitRand(engine) | 1; +} + +Node::LocalID DomainGatekeeper::findOrCreateLocalID(const QUuid& uuid) { + auto existingLocalIDIt = _uuidToLocalID.find(uuid); + if (existingLocalIDIt != _uuidToLocalID.end()) { + return existingLocalIDIt->second; + } + + assert(_localIDs.size() < std::numeric_limits::max() - 2); + + Node::LocalID newLocalID; + do { + newLocalID = _currentLocalID; + _currentLocalID += _idIncrement; + } while (newLocalID == Node::NULL_LOCAL_ID || _localIDs.find(newLocalID) != _localIDs.end()); + + _uuidToLocalID.emplace(uuid, newLocalID); + _localIDs.insert(newLocalID); + return newLocalID; +} diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 09db075e07..8402e58559 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -15,6 +15,7 @@ #define hifi_DomainGatekeeper_h #include +#include #include #include @@ -41,6 +42,8 @@ public: void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); } + Node::LocalID findOrCreateLocalID(const QUuid& uuid); + static void sendProtocolMismatchConnectionDenial(const HifiSockAddr& senderSockAddr); public slots: void processConnectRequestPacket(QSharedPointer message); @@ -120,6 +123,16 @@ private: void getGroupMemberships(const QString& username); // void getIsGroupMember(const QString& username, const QUuid groupID); void getDomainOwnerFriendsList(); + + // Local ID management. + void initLocalIDManagement(); + using UUIDToLocalID = std::unordered_map ; + using LocalIDs = std::unordered_set; + LocalIDs _localIDs; + UUIDToLocalID _uuidToLocalID; + + Node::LocalID _currentLocalID; + Node::LocalID _idIncrement; }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index dbf2907cc0..ac157979bb 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -593,8 +593,8 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) { if (!PacketTypeEnum::getNonSourcedPackets().contains(headerType)) { // this is a sourced packet - first check if we have a node that matches - QUuid sourceID = NLPacket::sourceIDInHeader(packet); - SharedNodePointer sourceNode = nodeList->nodeWithUUID(sourceID); + Node::LocalID localSourceID = NLPacket::sourceIDInHeader(packet); + SharedNodePointer sourceNode = nodeList->nodeWithLocalID(localSourceID); if (sourceNode) { // unverified DS packets (due to a lack of connection secret between DS + node) @@ -611,23 +611,13 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) { // let the NodeList do its checks now (but pass it the sourceNode so it doesn't need to look it up again) return nodeList->isPacketVerifiedWithSource(packet, sourceNode.data()); } else { - static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unmatched IP for UUID"; - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX); - - qDebug() << "Packet of type" << headerType - << "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceID); - + HIFI_FDEBUG("Packet of type" << headerType + << "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceNode->getUUID())); return false; } } else { - static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown node with UUID"; - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX); - - qDebug() << "Packet of type" << headerType - << "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID); - + HIFI_FDEBUG("Packet of type" << headerType + << "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceNode->getUUID())); return false; } } @@ -691,6 +681,10 @@ void DomainServer::setupNodeListAndAssignments() { } } + // Create our own short session ID. + Node::LocalID serverSessionLocalID = _gatekeeper.findOrCreateLocalID(nodeList->getSessionUUID()); + nodeList->setSessionLocalID(serverSessionLocalID); + if (isMetaverseDomain) { // see if we think we're a temp domain (we have an API key) or a full domain const auto& temporaryDomainKey = DependencyManager::get()->getTemporaryDomainKey(getID()); @@ -1120,7 +1114,8 @@ void DomainServer::handleConnectedNode(SharedNodePointer newNode) { } void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr) { - const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NUM_BYTES_RFC4122_UUID + 2; + const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NLPacket::NUM_BYTES_LOCALID + + NUM_BYTES_RFC4122_UUID + NLPacket::NUM_BYTES_LOCALID + 4; // setup the extended header for the domain list packets // this data is at the beginning of each of the domain list packets @@ -1130,7 +1125,9 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif auto limitedNodeList = DependencyManager::get(); extendedHeaderStream << limitedNodeList->getSessionUUID(); + extendedHeaderStream << limitedNodeList->getSessionLocalID(); extendedHeaderStream << node->getUUID(); + extendedHeaderStream << node->getLocalID(); extendedHeaderStream << node->getPermissions(); auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader); @@ -1242,31 +1239,16 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer NOISY_MESSAGE_INTERVAL_MSECS) { - static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex - ("Received a request for assignment type [^ ]+ from [^ ]+"); + static bool printedAssignmentTypeMessage = false; + if (!printedAssignmentTypeMessage && requestAssignment.getType() != Assignment::AgentType) { + printedAssignmentTypeMessage = true; qDebug() << "Received a request for assignment type" << requestAssignment.getType() << "from" << message->getSenderSockAddr(); - noisyMessageTimer.restart(); } SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); @@ -1300,13 +1282,11 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointergetUUID(), requestAssignment.getWalletUUID(), requestAssignment.getNodeVersion()); } else { - if (requestAssignment.getType() != Assignment::AgentType - || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { - static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex - ("Unable to fulfill assignment request of type [^ ]+ from [^ ]+"); + static bool printedAssignmentRequestMessage = false; + if (!printedAssignmentRequestMessage && requestAssignment.getType() != Assignment::AgentType) { + printedAssignmentRequestMessage = true; qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() << "from" << message->getSenderSockAddr(); - noisyMessageTimer.restart(); } } } @@ -1552,10 +1532,12 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() { callbackParameters.jsonCallbackReceiver = this; callbackParameters.jsonCallbackMethod = "handleSuccessfulICEServerAddressUpdate"; - static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex - ("Updating ice-server address in High Fidelity Metaverse API to [^ \n]+"); - qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" - << (_iceServerSocket.isNull() ? "" : _iceServerSocket.getAddress().toString()); + static bool printedIceServerMessage = false; + if (!printedIceServerMessage) { + printedIceServerMessage = true; + qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" + << (_iceServerSocket.isNull() ? "" : _iceServerSocket.getAddress().toString()); + } static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address"; @@ -2864,7 +2846,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction) // manually add the replication node to our node list auto node = nodeList->addOrUpdateNode(QUuid::createUuid(), replicationServer.nodeType, replicationServer.sockAddr, replicationServer.sockAddr, - false, direction == Upstream); + Node::NULL_LOCAL_ID, false, direction == Upstream); node->setIsForcedNeverSilent(true); qDebug() << "Adding" << (direction == Upstream ? "upstream" : "downstream") @@ -2926,7 +2908,7 @@ void DomainServer::updateReplicatedNodes() { } auto nodeList = DependencyManager::get(); - nodeList->eachMatchingNode([this](const SharedNodePointer& otherNode) -> bool { + nodeList->eachMatchingNode([](const SharedNodePointer& otherNode) -> bool { return otherNode->getType() == NodeType::Agent; }, [this](const SharedNodePointer& otherNode) { auto shouldReplicate = shouldReplicateNode(*otherNode); @@ -3164,13 +3146,12 @@ void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer(); - const QUuid& nodeUUID = message->getSourceID(); - - qDebug() << "Received a disconnect request from node with UUID" << nodeUUID; + auto localID = message->getSourceID(); + qDebug() << "Received a disconnect request from node with local ID" << localID; // we want to check what type this node was before going to kill it so that we can avoid sending the RemovedNode // packet to nodes that don't care about this type - auto nodeToKill = limitedNodeList->nodeWithUUID(nodeUUID); + auto nodeToKill = limitedNodeList->nodeWithLocalID(localID); if (nodeToKill) { handleKillNode(nodeToKill); @@ -3438,7 +3419,7 @@ void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer message) { - auto node = DependencyManager::get()->nodeWithUUID(message->getSourceID()); + auto node = DependencyManager::get()->nodeWithLocalID(message->getSourceID()); if (node->getCanReplaceContent()) { handleOctreeFileReplacement(message->readAll()); } diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index fe00d86c3a..ac9441319b 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -180,8 +180,9 @@ else () add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif () + if (BUILD_TOOLS AND NPM_EXECUTABLE) - # require JSDoc to be build before interface is deployed (Console Auto-complete) + # require JSDoc to be build before interface is deployed add_dependencies(resources jsdoc) endif() @@ -322,6 +323,13 @@ if (APPLE) "${RESOURCES_DEV_DIR}/scripts" ) + # copy JSDoc files beside the executable + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" + "${RESOURCES_DEV_DIR}/jsdoc" + ) + # call the fixup_interface macro to add required bundling commands for installation fixup_interface() @@ -350,6 +358,13 @@ else() "${RESOURCES_DEV_DIR}/serverless/tutorial.json" ) + # copy JSDoc files beside the executable + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" + "${INTERFACE_EXEC_DIR}/jsdoc" + ) + # link target to external libraries if (WIN32) target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) diff --git a/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.eot b/interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.eot similarity index 82% rename from interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.eot rename to interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.eot index d3591e6499..1be5435ced 100644 Binary files a/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.eot and b/interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.eot differ diff --git a/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.svg b/interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.svg similarity index 92% rename from interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.svg rename to interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.svg index 43c4932879..c68cb63a0d 100644 --- a/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.svg +++ b/interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.svg @@ -25,7 +25,6 @@ - @@ -145,4 +144,14 @@ + + + + + + + + + + diff --git a/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.ttf similarity index 82% rename from interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.ttf rename to interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.ttf index 8907cf7858..edc447c132 100644 Binary files a/interface/resources/fonts/hifi-glyphs/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.woff b/interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.woff new file mode 100644 index 0000000000..f26000caf4 Binary files /dev/null and b/interface/resources/fonts/hifi-glyphs-1.31/fonts/hifi-glyphs.woff differ diff --git a/interface/resources/fonts/hifi-glyphs/icons-reference.html b/interface/resources/fonts/hifi-glyphs-1.31/icons-reference.html similarity index 93% rename from interface/resources/fonts/hifi-glyphs/icons-reference.html rename to interface/resources/fonts/hifi-glyphs-1.31/icons-reference.html index 2ca94d4110..b03f5214c9 100644 --- a/interface/resources/fonts/hifi-glyphs/icons-reference.html +++ b/interface/resources/fonts/hifi-glyphs-1.31/icons-reference.html @@ -12,7 +12,7 @@

HiFi Glyphs

-

This font was created for use inHigh Fidelity

+

This font was created for use in High Fidelity

CSS mapping

  • @@ -87,10 +87,6 @@
  • -
  • -
    - -
  • @@ -567,6 +563,46 @@
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +

Character mapping

    @@ -642,10 +678,6 @@
    -
  • -
    - -
  • @@ -1122,6 +1154,46 @@
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +