diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 12f5abc796..4157e55754 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -88,7 +88,7 @@ void AssignmentClientMonitor::stopChildProcesses() { nodeList->eachNode([&](const SharedNodePointer& node) { qDebug() << "asking child" << node->getUUID() << "to exit."; node->activateLocalSocket(); - QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); + QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, *node->getActiveSocket()); }); @@ -193,7 +193,7 @@ void AssignmentClientMonitor::checkSpares() { qDebug() << "asking child" << aSpareId << "to exit."; SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId); childNode->activateLocalSocket(); - QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); + QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, childNode); } } @@ -229,7 +229,7 @@ void AssignmentClientMonitor::readPendingDatagrams() { } else { // tell unknown assignment-client child to exit. qDebug() << "asking unknown child to exit."; - QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); + QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr); } } diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 94439cb18b..098569b4f8 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -65,12 +65,6 @@ const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; const QString AUDIO_ENV_GROUP_KEY = "audio_env"; const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer"; -void attachNewNodeDataToNode(Node *newNode) { - if (!newNode->getLinkedData()) { - newNode->setLinkedData(new AudioMixerClientData()); - } -} - InboundAudioStream::Settings AudioMixer::_streamSettings; bool AudioMixer::_printStreamStats = false; @@ -514,7 +508,8 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { bool sendData = dataChanged || (randFloat() < CHANCE_OF_SEND); if (sendData) { - int numBytesEnvPacketHeader = populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment); + auto nodeList = DependencyManager::get(); + int numBytesEnvPacketHeader = nodeList->populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment); char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader; unsigned char bitset = 0; @@ -531,7 +526,7 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { memcpy(envDataAt, &wetLevel, sizeof(float)); envDataAt += sizeof(float); } - DependencyManager::get()->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node); + nodeList->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node); } } @@ -552,7 +547,7 @@ void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const Hif SharedNodePointer sendingNode = nodeList->sendingNodeForPacket(receivedPacket); if (sendingNode->getCanAdjustLocks()) { QByteArray packet = receivedPacket; - populatePacketHeader(packet, PacketTypeMuteEnvironment); + nodeList->populatePacketHeader(packet, PacketTypeMuteEnvironment); nodeList->eachNode([&](const SharedNodePointer& node){ if (node->getType() == NodeType::Agent && node->getActiveSocket() && @@ -686,7 +681,9 @@ void AudioMixer::run() { nodeList->addNodeTypeToInterestSet(NodeType::Agent); - nodeList->linkedDataCreateCallback = attachNewNodeDataToNode; + nodeList->linkedDataCreateCallback = [](Node* node) { + node->setLinkedData(new AudioMixerClientData()); + }; // wait until we have the domain-server settings, otherwise we bail DomainHandler& domainHandler = nodeList->getDomainHandler(); @@ -794,7 +791,7 @@ void AudioMixer::run() { // if the stream should be muted, send mute packet if (nodeData->getAvatarAudioStream() && shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) { - QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeNoisyMute); + QByteArray packet = nodeList->byteArrayWithPopulatedHeader(PacketTypeNoisyMute); nodeList->writeDatagram(packet, node); } @@ -806,7 +803,7 @@ void AudioMixer::run() { char* mixDataAt; if (streamsMixed > 0) { // pack header - int numBytesMixPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio); + int numBytesMixPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio); mixDataAt = clientMixBuffer + numBytesMixPacketHeader; // pack sequence number @@ -819,7 +816,7 @@ void AudioMixer::run() { mixDataAt += AudioConstants::NETWORK_FRAME_BYTES_STEREO; } else { // pack header - int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame); + int numBytesPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame); mixDataAt = clientMixBuffer + numBytesPacketHeader; // pack sequence number diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 4db5611bb5..8848758f86 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -157,7 +157,7 @@ void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& quint8 appendFlag = 0; // pack header - int numBytesPacketHeader = populatePacketHeader(packet, PacketTypeAudioStreamStats); + int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeAudioStreamStats); char* headerEndAt = packet + numBytesPacketHeader; // calculate how many stream stat structs we can fit in each packet diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 961cbecebb..0b510be151 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -56,14 +56,6 @@ AvatarMixer::~AvatarMixer() { _broadcastThread.wait(); } -void attachAvatarDataToNode(Node* newNode) { - if (!newNode->getLinkedData()) { - // setup the client linked data - default the number of frames since adjustment - // to our number of frames per second - newNode->setLinkedData(new AvatarMixerClientData()); - } -} - const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f; // NOTE: some additional optimizations to consider. @@ -129,10 +121,9 @@ void AvatarMixer::broadcastAvatarData() { static QByteArray mixedAvatarByteArray; - int numPacketHeaderBytes = populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData); - auto nodeList = DependencyManager::get(); - + int numPacketHeaderBytes = nodeList->populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData); + // setup for distributed random floating point values std::random_device randomDevice; std::mt19937 generator(randomDevice()); @@ -161,7 +152,7 @@ void AvatarMixer::broadcastAvatarData() { // reset packet pointers for this node mixedAvatarByteArray.resize(numPacketHeaderBytes); - + AvatarData& avatar = nodeData->getAvatar(); glm::vec3 myPosition = avatar.getPosition(); @@ -179,7 +170,13 @@ void AvatarMixer::broadcastAvatarData() { // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; - + + // keep track of the number of other avatars held back in this frame + int numAvatarsHeldBack = 0; + + // keep track of the number of other avatar frames skipped + int numAvatarsWithSkippedFrames = 0; + // use the data rate specifically for avatar data for FRD adjustment checks float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps(); @@ -257,8 +254,38 @@ void AvatarMixer::broadcastAvatarData() { && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { return; } + + PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); + PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData); + + if (lastSeqToReceiver > lastSeqFromSender) { + // Did we somehow get out of order packets from the sender? + // We don't expect this to happen - in RELEASE we add this to a trackable stat + // and in DEBUG we crash on the assert + + otherNodeData->incrementNumOutOfOrderSends(); + + assert(false); + } + // make sure we haven't already sent this data from this sender to this receiver + // or that somehow we haven't sent + if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { + ++numAvatarsHeldBack; + return; + } else if (lastSeqFromSender - lastSeqToReceiver > 1) { + // this is a skip - we still send the packet but capture the presence of the skip so we see it happening + ++numAvatarsWithSkippedFrames; + } + + // we're going to send this avatar + + // increment the number of avatars sent to this reciever nodeData->incrementNumAvatarsSentLastFrame(); + + // set the last sent sequence number for this sender on the receiver + nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), + otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData)); QByteArray avatarByteArray; avatarByteArray.append(otherNode->getUUID().toRfc4122()); @@ -287,7 +314,7 @@ void AvatarMixer::broadcastAvatarData() { && (forceSend || otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); + QByteArray billboardPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); billboardPacket.append(otherNode->getUUID().toRfc4122()); billboardPacket.append(otherNodeData->getAvatar().getBillboard()); @@ -301,7 +328,7 @@ void AvatarMixer::broadcastAvatarData() { || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); + QByteArray identityPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122()); @@ -318,6 +345,10 @@ void AvatarMixer::broadcastAvatarData() { // record the bytes sent for other avatar data in the AvatarMixerClientData nodeData->recordSentAvatarData(numAvatarDataBytes + mixedAvatarByteArray.size()); + + // record the number of avatars held back this frame + nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); + nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); if (numOtherAvatars == 0) { // update the full rate distance to FLOAT_MAX since we didn't have any other avatars to send @@ -334,13 +365,36 @@ void AvatarMixer::broadcastAvatarData() { void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { if (killedNode->getType() == NodeType::Agent && killedNode->getLinkedData()) { + auto nodeList = DependencyManager::get(); + // this was an avatar we were sending to other people // send a kill packet for it to our other nodes - QByteArray killPacket = byteArrayWithPopulatedHeader(PacketTypeKillAvatar); + QByteArray killPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeKillAvatar); killPacket += killedNode->getUUID().toRfc4122(); - DependencyManager::get()->broadcastToNodes(killPacket, - NodeSet() << NodeType::Agent); + nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::Agent); + + // we also want to remove sequence number data for this avatar on our other avatars + // so invoke the appropriate method on the AvatarMixerClientData for other avatars + nodeList->eachMatchingNode( + [&](const SharedNodePointer& node)->bool { + if (!node->getLinkedData()) { + return false; + } + + if (node->getUUID() == killedNode->getUUID()) { + return false; + } + + return true; + }, + [&](const SharedNodePointer& node) { + QMetaObject::invokeMethod(node->getLinkedData(), + "removeLastBroadcastSequenceNumber", + Qt::AutoConnection, + Q_ARG(const QUuid&, QUuid(killedNode->getUUID()))); + } + ); } } @@ -430,11 +484,14 @@ void AvatarMixer::sendStatsPacket() { AvatarMixerClientData* clientData = static_cast(node->getLinkedData()); if (clientData) { - clientData->loadJSONStats(avatarStats); + MutexTryLocker lock(clientData->getMutex()); + if (lock.isLocked()) { + clientData->loadJSONStats(avatarStats); - // add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only - avatarStats["delta_full_vs_avatar_data_kbps"] = - avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble(); + // add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only + avatarStats["delta_full_vs_avatar_data_kbps"] = + avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble(); + } } avatarsObject[uuidStringWithoutCurlyBraces(node->getUUID())] = avatarStats; @@ -455,7 +512,9 @@ void AvatarMixer::run() { auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - nodeList->linkedDataCreateCallback = attachAvatarDataToNode; + nodeList->linkedDataCreateCallback = [] (Node* node) { + node->setLinkedData(new AvatarMixerClientData()); + }; // setup the timer that will be fired on the broadcast thread _broadcastTimer = new QTimer(); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 17330ac891..197e9baf5e 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -25,11 +25,24 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() { return oldValue; } +PacketSequenceNumber AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const { + // return the matching PacketSequenceNumber, or the default if we don't have it + auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID); + if (nodeMatch != _lastBroadcastSequenceNumbers.end()) { + return nodeMatch->second; + } else { + return DEFAULT_SEQUENCE_NUMBER; + } +} + void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["display_name"] = _avatar.getDisplayName(); jsonObject["full_rate_distance"] = _fullRateDistance; jsonObject["max_avatar_distance"] = _maxAvatarDistance; jsonObject["num_avatars_sent_last_frame"] = _numAvatarsSentLastFrame; + jsonObject["avg_other_avatar_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond(); + jsonObject["avg_other_avatar_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond(); + jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends; jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps(); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index cccc5ee60f..3e10b8473a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -21,7 +22,9 @@ #include #include #include +#include #include +#include const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps"; @@ -32,7 +35,12 @@ public: AvatarData& getAvatar() { return _avatar; } bool checkAndSetHasReceivedFirstPackets(); - + + PacketSequenceNumber getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const; + void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, PacketSequenceNumber sequenceNumber) + { _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; } + Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } + quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; } void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; } @@ -49,6 +57,14 @@ public: void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } int getNumAvatarsSentLastFrame() const { return _numAvatarsSentLastFrame; } + void recordNumOtherAvatarStarves(int numAvatarsHeldBack) { _otherAvatarStarves.updateAverage((float) numAvatarsHeldBack); } + float getAvgNumOtherAvatarStarvesPerSecond() const { return _otherAvatarStarves.getAverageSampleValuePerSecond(); } + + void recordNumOtherAvatarSkips(int numOtherAvatarSkips) { _otherAvatarSkips.updateAverage((float) numOtherAvatarSkips); } + float getAvgNumOtherAvatarSkipsPerSecond() const { return _otherAvatarSkips.getAverageSampleValuePerSecond(); } + + void incrementNumOutOfOrderSends() { ++_numOutOfOrderSends; } + int getNumFramesSinceFRDAdjustment() const { return _numFramesSinceAdjustment; } void incrementNumFramesSinceFRDAdjustment() { ++_numFramesSinceAdjustment; } void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; } @@ -61,13 +77,23 @@ public: void loadJSONStats(QJsonObject& jsonObject) const; private: AvatarData _avatar; + + std::unordered_map _lastBroadcastSequenceNumbers; + bool _hasReceivedFirstPackets = false; quint64 _billboardChangeTimestamp = 0; quint64 _identityChangeTimestamp = 0; + float _fullRateDistance = FLT_MAX; float _maxAvatarDistance = FLT_MAX; + int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; + + SimpleMovingAverage _otherAvatarStarves; + SimpleMovingAverage _otherAvatarSkips; + int _numOutOfOrderSends = 0; + SimpleMovingAverage _avgOtherAvatarDataRate; }; diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index bb5042f4b4..0f5332192a 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -64,7 +64,9 @@ void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePo unsigned char outputBuffer[MAX_PACKET_SIZE]; unsigned char* copyAt = outputBuffer; - int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(outputBuffer), PacketTypeEntityAddResponse); + auto nodeList = DependencyManager::get(); + + int numBytesPacketHeader = nodeList->populatePacketHeader(reinterpret_cast(outputBuffer), PacketTypeEntityAddResponse); int packetLength = numBytesPacketHeader; copyAt += numBytesPacketHeader; @@ -81,7 +83,7 @@ void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePo copyAt += sizeof(entityID); packetLength += sizeof(entityID); - DependencyManager::get()->writeDatagram((char*) outputBuffer, packetLength, senderNode); + nodeList->writeDatagram((char*) outputBuffer, packetLength, senderNode); } diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index f6ab12f421..28506b033e 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -277,9 +277,11 @@ int OctreeInboundPacketProcessor::sendNackPackets() { char* dataAt = packet; int bytesRemaining = MAX_PACKET_SIZE; + + auto nodeList = DependencyManager::get(); // pack header - int numBytesPacketHeader = populatePacketHeader(packet, _myServer->getMyEditNackType()); + int numBytesPacketHeader = nodeList->populatePacketHeader(packet, _myServer->getMyEditNackType()); dataAt += numBytesPacketHeader; bytesRemaining -= numBytesPacketHeader; @@ -301,7 +303,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() { numSequenceNumbersAvailable -= numSequenceNumbers; // send it - DependencyManager::get()->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode); + nodeList->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode); packetsSent++; qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID; diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index 2d8d8d357e..b6504863e0 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -189,7 +189,9 @@ void OctreeQueryNode::resetOctreePacket() { } _octreePacketAvailableBytes = MAX_PACKET_SIZE; - int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(_octreePacket), _myPacketType); + int numBytesPacketHeader = DependencyManager::get()->populatePacketHeader(reinterpret_cast(_octreePacket), + _myPacketType); + _octreePacketAt = _octreePacket + numBytesPacketHeader; _octreePacketAvailableBytes -= numBytesPacketHeader; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 9abace0c5b..361a619744 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -213,14 +213,6 @@ void OctreeServer::trackProcessWaitTime(float time) { _averageProcessWaitTime.updateAverage(time); } -void OctreeServer::attachQueryNodeToNode(Node* newNode) { - if (!newNode->getLinkedData() && _instance) { - OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(); - newQueryNodeData->init(); - newNode->setLinkedData(newQueryNodeData); - } -} - OctreeServer::OctreeServer(const QByteArray& packet) : ThreadedAssignment(packet), _argc(0), @@ -1132,7 +1124,11 @@ void OctreeServer::run() { setvbuf(stdout, NULL, _IOLBF, 0); #endif - nodeList->linkedDataCreateCallback = &OctreeServer::attachQueryNodeToNode; + nodeList->linkedDataCreateCallback = [] (Node* node) { + OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(); + newQueryNodeData->init(); + node->setLinkedData(newQueryNodeData); + }; srand((unsigned)time(0)); diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 41cd3259cf..ab75efe346 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -75,8 +75,6 @@ public: virtual bool hasSpecialPacketToSend(const SharedNodePointer& node) { return false; } virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; } - static void attachQueryNodeToNode(Node* newNode); - static float SKIP_TIME; // use this for trackXXXTime() calls for non-times static void trackLoopTime(float time) { _averageLoopTime.updateAverage(time); } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 6ee1d6c765..7841381422 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -619,10 +619,12 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock packetStream >> nodeInterestList >> username >> usernameSignature; + auto limitedNodeList = DependencyManager::get(); + QString reason; if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr, reason)) { // this is an agent and we've decided we won't let them connect - send them a packet to deny connection - QByteArray connectionDeniedByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied); + QByteArray connectionDeniedByteArray = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied); QDataStream out(&connectionDeniedByteArray, QIODevice::WriteOnly | QIODevice::Append); out << reason; // tell client it has been refused. @@ -664,10 +666,9 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock canRez = canAdjustLocks; } - SharedNodePointer newNode = - DependencyManager::get()->addOrUpdateNode(nodeUUID, nodeType, - publicSockAddr, localSockAddr, - canAdjustLocks, canRez); + SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeType, + publicSockAddr, localSockAddr, + canAdjustLocks, canRez); // when the newNode is created the linked data is also created // if this was a static assignment set the UUID, set the sendingSockAddr DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); @@ -926,8 +927,8 @@ NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int n void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr, const NodeSet& nodeInterestList) { - - QByteArray broadcastPacket = byteArrayWithPopulatedHeader(PacketTypeDomainList); + auto limitedNodeList = DependencyManager::get(); + QByteArray broadcastPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainList); // always send the node their own UUID back QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append); @@ -939,8 +940,6 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - auto nodeList = DependencyManager::get(); - // if we've established a connection via ICE with this peer, use that socket // otherwise just try to reply back to them on their sending socket (although that may not work) HifiSockAddr destinationSockAddr = _connectedICEPeers.value(node->getUUID()); @@ -955,7 +954,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif if (nodeData->isAuthenticated()) { // if this authenticated node has any interest types, send back those nodes as well - nodeList->eachNode([&](const SharedNodePointer& otherNode){ + limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){ // reset our nodeByteArray and nodeDataStream QByteArray nodeByteArray; QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append); @@ -986,7 +985,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif // we need to break here and start a new packet // so send the current one - nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); + limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr); // reset the broadcastPacket structure broadcastPacket.resize(numBroadcastPacketLeadBytes); @@ -999,26 +998,26 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif }); } } - + // always write the last broadcastPacket - nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); + limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr); } void DomainServer::readAvailableDatagrams() { - auto nodeList = DependencyManager::get(); + auto limitedNodeList = DependencyManager::get(); HifiSockAddr senderSockAddr; QByteArray receivedPacket; - static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment); + static QByteArray assignmentPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeCreateAssignment); static int numAssignmentPacketHeaderBytes = assignmentPacket.size(); - while (nodeList->getNodeSocket().hasPendingDatagrams()) { - receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); - nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), + while (limitedNodeList->getNodeSocket().hasPendingDatagrams()) { + receivedPacket.resize(limitedNodeList->getNodeSocket().pendingDatagramSize()); + limitedNodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); if (packetTypeForPacket(receivedPacket) == PacketTypeRequestAssignment - && nodeList->packetVersionAndHashMatch(receivedPacket)) { + && limitedNodeList->packetVersionAndHashMatch(receivedPacket)) { // construct the requested assignment from the packet data Assignment requestAssignment(receivedPacket); @@ -1059,7 +1058,7 @@ void DomainServer::readAvailableDatagrams() { assignmentStream << uniqueAssignment; - nodeList->getNodeSocket().writeDatagram(assignmentPacket, + limitedNodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); // add the information for that deployed assignment to the hash of pending assigned nodes @@ -1081,16 +1080,16 @@ void DomainServer::readAvailableDatagrams() { processDatagram(receivedPacket, senderSockAddr); } else { // we're using DTLS, so tell the sender to get back to us using DTLS - static QByteArray dtlsRequiredPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS); + static QByteArray dtlsRequiredPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS); static int numBytesDTLSHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerRequireDTLS); if (dtlsRequiredPacket.size() == numBytesDTLSHeader) { // pack the port that we accept DTLS traffic on - unsigned short dtlsPort = nodeList->getDTLSSocket().localPort(); + unsigned short dtlsPort = limitedNodeList->getDTLSSocket().localPort(); dtlsRequiredPacket.replace(numBytesDTLSHeader, sizeof(dtlsPort), reinterpret_cast(&dtlsPort)); } - nodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr); + limitedNodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr); } } } diff --git a/examples/edit.js b/examples/edit.js index 585b45f3f9..64050d92aa 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1197,7 +1197,11 @@ PropertiesTool = function(opts) { webView.eventBridge.webEventReceived.connect(function(data) { data = JSON.parse(data); - if (data.type == "update") { + if (data.type == "print") { + if (data.message) { + print(data.message); + } + } else if (data.type == "update") { selectionManager.saveProperties(); if (selectionManager.selections.length > 1) { properties = { diff --git a/examples/example/entities/changingAtmosphereExample.js b/examples/example/entities/changingAtmosphereExample.js index 88a25b6a08..02103fc0fb 100644 --- a/examples/example/entities/changingAtmosphereExample.js +++ b/examples/example/entities/changingAtmosphereExample.js @@ -35,12 +35,14 @@ var zoneEntityA = Entities.addEntity({ scatteringWavelengths: { x: 0.650, y: 0.570, z: 0.475 }, hasStars: true }, - stageLatitude: 37.777, - stageLongitude: 122.407, - stageAltitude: 0.03, - stageDay: 183, - stageHour: 5, - stageSunModelEnabled: true + stage: { + latitude: 37.777, + longitude: 122.407, + altitude: 0.03, + day: 183, + hour: 5, + sunModelEnabled: true + } }); diff --git a/examples/example/entities/zoneAtmosphereExample.js b/examples/example/entities/zoneAtmosphereExample.js index b31a453300..dfebf09f2a 100644 --- a/examples/example/entities/zoneAtmosphereExample.js +++ b/examples/example/entities/zoneAtmosphereExample.js @@ -33,12 +33,14 @@ var zoneEntityA = Entities.addEntity({ scatteringWavelengths: { x: 0.650, y: 0.570, z: 0.475 }, hasStars: false }, - stageLatitude: 37.777, - stageLongitude: 122.407, - stageAltitude: 0.03, - stageDay: 60, - stageHour: 0, - stageSunModelEnabled: true + stage: { + latitude: 37.777, + longitude: 122.407, + altitude: 0.03, + day: 60, + hour: 0, + sunModelEnabled: true + } }); diff --git a/examples/example/entities/zoneEntityExample.js b/examples/example/entities/zoneEntityExample.js index b5831a2bb5..2d8b587d53 100644 --- a/examples/example/entities/zoneEntityExample.js +++ b/examples/example/entities/zoneEntityExample.js @@ -34,17 +34,18 @@ var zoneEntityB = Entities.addEntity({ dimensions: { x: 2, y: 2, z: 2 }, keyLightColor: { red: 0, green: 255, blue: 0 }, keyLightIntensity: 0.9, - stageLatitude: 37.777, - stageLongitude: 122.407, - stageAltitude: 0.03, - stageDay: 60, - stageHour: 12, - stageSunModelEnabled: true + stage: { + latitude: 37.777, + longitude: 122.407, + altitude: 0.03, + day: 60, + hour: 0, + sunModelEnabled: true + } }); print("zoneEntityB:" + zoneEntityB); - var zoneEntityC = Entities.addEntity({ type: "Zone", position: { x: 5, y: 10, z: 5 }, @@ -59,7 +60,6 @@ var zoneEntityC = Entities.addEntity({ print("zoneEntityC:" + zoneEntityC); - // register the call back so it fires before each data send Script.update.connect(function(deltaTime) { // stop it... diff --git a/examples/example/entities/zoneSkyboxExample.js b/examples/example/entities/zoneSkyboxExample.js index a1f5f1917a..420482f14d 100644 --- a/examples/example/entities/zoneSkyboxExample.js +++ b/examples/example/entities/zoneSkyboxExample.js @@ -37,12 +37,14 @@ var zoneEntityA = Entities.addEntity({ color: { red: 255, green: 0, blue: 255 }, url: "" }, - stageLatitude: 37.777, - stageLongitude: 122.407, - stageAltitude: 0.03, - stageDay: 60, - stageHour: 0, - stageSunModelEnabled: true + stage: { + latitude: 37.777, + longitude: 122.407, + altitude: 0.03, + day: 60, + hour: 0, + sunModelEnabled: true + } }); var props = Entities.getEntityProperties(zoneEntityA); diff --git a/examples/grab.js b/examples/grab.js index df2042350e..efacc26612 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -27,6 +27,7 @@ var ANGULAR_DAMPING_RATE = 0.40; var SCREEN_TO_METERS = 0.001; var currentPosition, currentVelocity, cameraEntityDistance, currentRotation; var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition; +var originalGravity; var shouldRotate = false; var dQ, theta, axisAngle, dT; var angularVelocity = { @@ -65,6 +66,7 @@ function mousePressEvent(event) { grabbedEntity = intersection.entityID; var props = Entities.getEntityProperties(grabbedEntity) isGrabbing = true; + originalGravity = props.gravity; targetPosition = props.position; currentPosition = props.position; currentVelocity = props.velocity; @@ -96,6 +98,11 @@ function updateDropLine(position) { function mouseReleaseEvent() { if (isGrabbing) { isGrabbing = false; + + Entities.editEntity(grabbedEntity, { + gravity: originalGravity + }); + Overlays.editOverlay(dropLine, { visible: false }); @@ -194,7 +201,7 @@ function update(deltaTime) { newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE)); // Update entity } else { - newVelocity = entityProps.velocity; + newVelocity = {x: 0, y: 0, z: 0}; } if (shouldRotate) { angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE)); @@ -204,7 +211,8 @@ function update(deltaTime) { Entities.editEntity(grabbedEntity, { velocity: newVelocity, - angularVelocity: angularVelocity + angularVelocity: angularVelocity, + gravity: {x: 0, y: 0, z: 0} }) updateDropLine(targetPosition); } @@ -215,4 +223,4 @@ Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); -Script.update.connect(update); \ No newline at end of file +Script.update.connect(update); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 26bcc63f15..56fc84ef96 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -18,6 +18,12 @@ els[i].setAttribute('disabled', 'disabled'); } } + + function showElements(els, show) { + for (var i = 0; i < els.length; i++) { + els[i].style.display = (show) ? 'block' : 'none'; + } + } function createEmitCheckedPropertyUpdateFunction(propertyName) { return function() { @@ -280,6 +286,7 @@ var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude"); var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude"); var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude"); + var elZoneStageAutomaticHourDay = document.getElementById("property-zone-stage-automatic-hour-day"); var elZoneStageDay = document.getElementById("property-zone-stage-day"); var elZoneStageHour = document.getElementById("property-zone-stage-hour"); @@ -467,7 +474,7 @@ elZoneSections[i].style.display = 'block'; } - elZoneStageSunModelEnabled.checked = properties.stageSunModelEnabled; + elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled; elZoneKeyLightColorRed.value = properties.keyLightColor.red; elZoneKeyLightColorGreen.value = properties.keyLightColor.green; elZoneKeyLightColorBlue.value = properties.keyLightColor.blue; @@ -477,11 +484,12 @@ elZoneKeyLightDirectionY.value = properties.keyLightDirection.y.toFixed(2); elZoneKeyLightDirectionZ.value = properties.keyLightDirection.z.toFixed(2); - elZoneStageLatitude.value = properties.stageLatitude.toFixed(2); - elZoneStageLongitude.value = properties.stageLongitude.toFixed(2); - elZoneStageAltitude.value = properties.stageAltitude.toFixed(2); - elZoneStageDay.value = properties.stageDay; - elZoneStageHour.value = properties.stageHour; + elZoneStageLatitude.value = properties.stage.latitude.toFixed(2); + elZoneStageLongitude.value = properties.stage.longitude.toFixed(2); + elZoneStageAltitude.value = properties.stage.altitude.toFixed(2); + elZoneStageAutomaticHourDay.checked = properties.stage.automaticHourDay; + elZoneStageDay.value = properties.stage.day; + elZoneStageHour.value = properties.stage.hour; elShapeType.value = properties.shapeType; elCompoundShapeURL.value = properties.compoundShapeURL; @@ -504,6 +512,8 @@ elZoneAtmosphereScatteringWavelengthsZ.value = properties.atmosphere.scatteringWavelengths.z; elZoneAtmosphereHasStars.checked = properties.atmosphere.hasStars; + showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox'); + showElements(document.getElementsByClassName('atmosphere-section'), elZoneBackgroundMode.value == 'atmosphere'); } if (selected) { @@ -619,7 +629,7 @@ elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction); elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction); - elZoneStageSunModelEnabled.addEventListener('change', createEmitCheckedPropertyUpdateFunction('stageSunModelEnabled')); + elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled')); var zoneKeyLightColorChangeFunction = createEmitColorPropertyUpdateFunction( 'keyLightColor', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction); @@ -633,11 +643,12 @@ elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); elZoneKeyLightDirectionZ.addEventListener('change', zoneKeyLightDirectionChangeFunction); - elZoneStageLatitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLatitude')); - elZoneStageLongitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLongitude')); - elZoneStageAltitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageAltitude')); - elZoneStageDay.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageDay')); - elZoneStageHour.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageHour')); + elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude')); + elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude')); + elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude')); + elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','automaticHourDay')); + elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','day')); + elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','hour')); elZoneBackgroundMode.addEventListener('change', createEmitTextPropertyUpdateFunction('backgroundMode')); @@ -1094,6 +1105,14 @@ + +
+ Automatically calculate stage hour and day from location and clock. + + + +
+
Stage Day
@@ -1117,7 +1136,7 @@
-
+
Skybox Color
R
@@ -1125,13 +1144,13 @@
B
-
+
Skybox URL
-
+
Atmosphere Center
X
@@ -1139,31 +1158,31 @@
Z
-
+
Atmosphere Inner Radius
-
+
Atmosphere Outer Radius
-
+
Atmosphere Mie Scattering
-
+
Atmosphere Rayleigh Scattering
-
+
Atmosphere Scattering Wavelenghts
X
@@ -1171,7 +1190,7 @@
Z
-
+