From a256473ac5137e97c3c137b74af61bf1f366f600 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 25 Mar 2014 12:55:50 -0700 Subject: [PATCH 01/22] fixed lookWithTouch.js to not reset --- examples/lookWithTouch.js | 41 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/examples/lookWithTouch.js b/examples/lookWithTouch.js index 852573aea6..4406b4567e 100644 --- a/examples/lookWithTouch.js +++ b/examples/lookWithTouch.js @@ -9,6 +9,7 @@ // // +var startedTouching = false; var lastX = 0; var lastY = 0; var yawFromMouse = 0; @@ -21,12 +22,14 @@ function touchBeginEvent(event) { } lastX = event.x; lastY = event.y; + startedTouching = true; } function touchEndEvent(event) { if (wantDebugging) { print("touchEndEvent event.x,y=" + event.x + ", " + event.y); } + startedTouching = false; } function touchUpdateEvent(event) { @@ -44,24 +47,26 @@ function touchUpdateEvent(event) { } function update(deltaTime) { - // rotate body yaw for yaw received from mouse - var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollRadians(0, yawFromMouse, 0)); - if (wantDebugging) { - print("changing orientation" - + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," - + MyAvatar.orientation.z + "," + MyAvatar.orientation.w - + " newOrientation="+newOrientation.x + "," + newOrientation.y + "," + newOrientation.z + "," + newOrientation.w); - } - MyAvatar.orientation = newOrientation; - yawFromMouse = 0; + if (startedTouching) { + // rotate body yaw for yaw received from mouse + var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollRadians(0, yawFromMouse, 0)); + if (wantDebugging) { + print("changing orientation" + + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," + + MyAvatar.orientation.z + "," + MyAvatar.orientation.w + + " newOrientation="+newOrientation.x + "," + newOrientation.y + "," + newOrientation.z + "," + newOrientation.w); + } + MyAvatar.orientation = newOrientation; + yawFromMouse = 0; - // apply pitch from mouse - var newPitch = MyAvatar.headPitch + pitchFromMouse; - if (wantDebugging) { - print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch); + // apply pitch from mouse + var newPitch = MyAvatar.headPitch + pitchFromMouse; + if (wantDebugging) { + print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch); + } + MyAvatar.headPitch = newPitch; + pitchFromMouse = 0; } - MyAvatar.headPitch = newPitch; - pitchFromMouse = 0; } // Map the mouse events to our functions @@ -77,10 +82,6 @@ function scriptEnding() { Controller.releaseTouchEvents(); } -MyAvatar.bodyYaw = 0; -MyAvatar.bodyPitch = 0; -MyAvatar.bodyRoll = 0; - // would be nice to change to update Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); From 9a3533a38fbbe5affa853330881adb77dd9f395c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 25 Mar 2014 12:59:57 -0700 Subject: [PATCH 02/22] added inspect.js to defaultScripts.js --- examples/defaultScripts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index eae3c2eb8c..722b21844f 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -3,4 +3,5 @@ Script.include("lookWithTouch.js"); Script.include("editVoxels.js"); Script.include("selectAudioDevice.js"); -Script.include("hydraMove.js"); \ No newline at end of file +Script.include("hydraMove.js"); +Script.include("inspect.js"); \ No newline at end of file From f0b87bebc2d70074a2ee3629797a4125d396feaa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 13:02:09 -0700 Subject: [PATCH 03/22] cleanup debug and inactive node pinging --- libraries/shared/src/NodeList.cpp | 2 +- libraries/shared/src/ThreadedAssignment.cpp | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 95417f4f71..838e2f4fea 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -582,7 +582,7 @@ void NodeList::sendDomainServerCheckIn() { foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) { packetStream << nodeTypeOfInterest; } - + writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret()); const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index 642f471cc5..be49b18055 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -46,10 +46,6 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - QTimer* pingNodesTimer = new QTimer(this); - connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); - pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - QTimer* silentNodeRemovalTimer = new QTimer(this); connect(silentNodeRemovalTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeRemovalTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); @@ -84,7 +80,6 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { setFinished(true); } else { - qDebug() << "Sending DS check in. There are" << NodeList::getInstance()->getNumNoReplyDomainCheckIns() << "unreplied."; NodeList::getInstance()->sendDomainServerCheckIn(); } } From 08ed708ee6c27491f1ca1cf1c6e48235695eac2c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 13:05:58 -0700 Subject: [PATCH 04/22] add domain IP and packet size debugging --- libraries/shared/src/NodeList.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 838e2f4fea..24fa68c618 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -582,7 +582,8 @@ void NodeList::sendDomainServerCheckIn() { foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) { packetStream << nodeTypeOfInterest; } - + + qDebug() << "sending DS check in size" << domainServerPacket.size() << "to" << _domainInfo.getSockAddr(); writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret()); const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; From ce169dd87d25e126a3f6805290d20fce36b81664 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 13:06:44 -0700 Subject: [PATCH 05/22] add code return debug to DS check in packet --- libraries/shared/src/NodeList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 24fa68c618..4d95bbc6e9 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -584,7 +584,7 @@ void NodeList::sendDomainServerCheckIn() { } qDebug() << "sending DS check in size" << domainServerPacket.size() << "to" << _domainInfo.getSockAddr(); - writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret()); + qDebug() << "Code returned is" << writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret()); const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; From 6a8637230f372833157d58e3240762709c71812d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 13:17:10 -0700 Subject: [PATCH 06/22] add socket error debugging --- libraries/shared/src/NodeList.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 4d95bbc6e9..ab049d7fae 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -584,7 +584,11 @@ void NodeList::sendDomainServerCheckIn() { } qDebug() << "sending DS check in size" << domainServerPacket.size() << "to" << _domainInfo.getSockAddr(); - qDebug() << "Code returned is" << writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret()); + qint64 code = writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret()); + qDebug() << "Code returned is" << code; + if (code == -1) { + qDebug() << "the socket error is" << _nodeSocket.error(); + } const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; From 08bfc15b704472fbe61d0cb151d486cb1ebf86a0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 13:20:45 -0700 Subject: [PATCH 07/22] output more descriptive socket error --- libraries/shared/src/NodeList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index ab049d7fae..cda2963f53 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -587,7 +587,7 @@ void NodeList::sendDomainServerCheckIn() { qint64 code = writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret()); qDebug() << "Code returned is" << code; if (code == -1) { - qDebug() << "the socket error is" << _nodeSocket.error(); + qDebug() << "the socket error is" << _nodeSocket.errorString(); } const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; From 68d55152b9bac0e3154d9c23efa450642c147832 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 13:28:53 -0700 Subject: [PATCH 08/22] flush after each call to writeDatagram --- libraries/shared/src/NodeList.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index cda2963f53..0cc343b460 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -171,7 +171,13 @@ qint64 NodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& d ++_numCollectedPackets; _numCollectedBytes += datagram.size(); - return _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr.getAddress(), destinationSockAddr.getPort()); + qint64 bytesWritten = _nodeSocket.writeDatagram(datagramCopy, + destinationSockAddr.getAddress(), + destinationSockAddr.getPort()); + + // ask the underlying QUdpSocket to flush its buffers to avoid filling them up + _nodeSocket.flush(); + return bytesWritten; } From 4e77afb4b25e95b359b3bb0a12c4f7aa9914f3d5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 13:38:02 -0700 Subject: [PATCH 09/22] block and wait for bytes to be written during broadcastAvatarData loop --- assignment-client/src/avatars/AvatarMixer.cpp | 6 ++---- libraries/shared/src/NodeList.cpp | 8 +------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 65e0acd4a6..5eb3e54389 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -54,10 +54,6 @@ const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f; // NOTE: some additional optimizations to consider. // 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present // if the avatar is not in view or in the keyhole. -// 2) after culling for view frustum, sort order the avatars by distance, send the closest ones first. -// 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to -// determine which avatars are included in the packet stream -// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful). void AvatarMixer::broadcastAvatarData() { static QByteArray mixedAvatarByteArray; @@ -139,6 +135,8 @@ void AvatarMixer::broadcastAvatarData() { ++_sumIdentityPackets; } + + nodeList->getNodeSocket().waitForBytesWritten(-1); } } } diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 0cc343b460..cda2963f53 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -171,13 +171,7 @@ qint64 NodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& d ++_numCollectedPackets; _numCollectedBytes += datagram.size(); - qint64 bytesWritten = _nodeSocket.writeDatagram(datagramCopy, - destinationSockAddr.getAddress(), - destinationSockAddr.getPort()); - - // ask the underlying QUdpSocket to flush its buffers to avoid filling them up - _nodeSocket.flush(); - return bytesWritten; + return _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr.getAddress(), destinationSockAddr.getPort()); } From 0407c6d3244b2aab9bb62ae4ede64de5bad939fa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 13:50:22 -0700 Subject: [PATCH 10/22] use QTimer to clock broadcastAvatarData method --- assignment-client/src/avatars/AvatarMixer.cpp | 135 ++++++++---------- 1 file changed, 59 insertions(+), 76 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 5eb3e54389..314c0f93ff 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -27,7 +28,7 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer"; -const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000; +const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / 60.0f) * 1000; AvatarMixer::AvatarMixer(const QByteArray& packet) : ThreadedAssignment(packet), @@ -55,6 +56,58 @@ const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f; // 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present // if the avatar is not in view or in the keyhole. void AvatarMixer::broadcastAvatarData() { + + int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp; + + ++_numStatFrames; + + const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f; + const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f; + + const float RATIO_BACK_OFF = 0.02f; + + const int TRAILING_AVERAGE_FRAMES = 100; + int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; + + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; + const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; + + _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) + + (idleTime * CURRENT_FRAME_RATIO / (float) AVATAR_DATA_SEND_INTERVAL_MSECS); + + float lastCutoffRatio = _performanceThrottlingRatio; + bool hasRatioChanged = false; + + if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) { + if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { + // we're struggling - change our min required loudness to reduce some load + _performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio)); + + qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" + << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; + hasRatioChanged = true; + } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) { + // we've recovered and can back off the required loudness + _performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF; + + if (_performanceThrottlingRatio < 0) { + _performanceThrottlingRatio = 0; + } + + qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" + << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; + hasRatioChanged = true; + } + + if (hasRatioChanged) { + framesSinceCutoffEvent = 0; + } + } + + if (!hasRatioChanged) { + ++framesSinceCutoffEvent; + } + static QByteArray mixedAvatarByteArray; int numPacketHeaderBytes = populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData); @@ -246,80 +299,10 @@ void AvatarMixer::run() { nodeList->linkedDataCreateCallback = attachAvatarDataToNode; - int nextFrame = 0; - timeval startTime; + // setup and start a timer to broadcast avatar data + QTimer* broadcastTimer = new QTimer(this); + broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS); - gettimeofday(&startTime, NULL); - - int usecToSleep = AVATAR_DATA_SEND_INTERVAL_USECS; - - const int TRAILING_AVERAGE_FRAMES = 100; - int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; - - while (!_isFinished) { - - ++_numStatFrames; - - const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f; - const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f; - - const float RATIO_BACK_OFF = 0.02f; - - const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; - const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; - - if (usecToSleep < 0) { - usecToSleep = 0; - } - - _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) - + (usecToSleep * CURRENT_FRAME_RATIO / (float) AVATAR_DATA_SEND_INTERVAL_USECS); - - float lastCutoffRatio = _performanceThrottlingRatio; - bool hasRatioChanged = false; - - if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) { - if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { - // we're struggling - change our min required loudness to reduce some load - _performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio)); - - qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; - hasRatioChanged = true; - } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) { - // we've recovered and can back off the required loudness - _performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF; - - if (_performanceThrottlingRatio < 0) { - _performanceThrottlingRatio = 0; - } - - qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; - hasRatioChanged = true; - } - - if (hasRatioChanged) { - framesSinceCutoffEvent = 0; - } - } - - if (!hasRatioChanged) { - ++framesSinceCutoffEvent; - } - - broadcastAvatarData(); - - QCoreApplication::processEvents(); - - if (_isFinished) { - break; - } - - usecToSleep = usecTimestamp(&startTime) + (++nextFrame * AVATAR_DATA_SEND_INTERVAL_USECS) - usecTimestampNow(); - - if (usecToSleep > 0) { - usleep(usecToSleep); - } - } + connect(broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData); + broadcastTimer->start(); } From 269615bc40c366f5a51adff14baa38ff23a8f22b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 14:02:24 -0700 Subject: [PATCH 11/22] thread broadcast of avatar data in AvatarMixer --- assignment-client/src/avatars/AvatarMixer.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 314c0f93ff..55a60194c0 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -299,10 +299,18 @@ void AvatarMixer::run() { nodeList->linkedDataCreateCallback = attachAvatarDataToNode; - // setup and start a timer to broadcast avatar data - QTimer* broadcastTimer = new QTimer(this); - broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS); + // create a thead for broadcast of avatar data + QThread* broadcastThread = new QThread(this); - connect(broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData); - broadcastTimer->start(); + // setup the timer that will be fired on the broadcast thread + QTimer* broadcastTimer = new QTimer(); + broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS); + broadcastTimer->moveToThread(broadcastThread); + + // connect appropriate signals and slots + connect(broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData, Qt::DirectConnection); + connect(broadcastThread, SIGNAL(started()), broadcastTimer, SLOT(start())); + + // start the broadcastThread + broadcastThread->start(); } From f8c479f9c54388b84500206b2259d247345de5b6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 14:28:26 -0700 Subject: [PATCH 12/22] seed random number generator in ScriptEngine for Math.random use in JS --- libraries/script-engine/src/ScriptEngine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 9bd00a0019..e880760a1d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -235,6 +235,11 @@ void ScriptEngine::init() { // let the VoxelPacketSender know how frequently we plan to call it _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); + + // call srand to seed the random number generator + srand(QDateTime::currentMSecsSinceEpoch() + + QCoreApplication::applicationPid() + + NodeList::getInstance()->getNodeSocket().localPort()); } From 41e2ce2b1999e5bd8893e2f17c756adfc87f9681 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 14:29:59 -0700 Subject: [PATCH 13/22] remove DS check in debug --- libraries/shared/src/ThreadedAssignment.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index 642f471cc5..1f443146f8 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -84,7 +84,6 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { setFinished(true); } else { - qDebug() << "Sending DS check in. There are" << NodeList::getInstance()->getNumNoReplyDomainCheckIns() << "unreplied."; NodeList::getInstance()->sendDomainServerCheckIn(); } } From 7190ce9a88fda177eb3cc6b43104ceffd7ac2291 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 14:33:53 -0700 Subject: [PATCH 14/22] remove loadRandomIdentifier for STUN requests, use UUID --- libraries/shared/src/NodeList.cpp | 5 ++--- libraries/shared/src/SharedUtil.cpp | 9 --------- libraries/shared/src/SharedUtil.h | 2 -- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 95417f4f71..b2a494aa32 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -413,9 +413,8 @@ void NodeList::sendSTUNRequest() { // transaction ID (random 12-byte unsigned integer) const uint NUM_TRANSACTION_ID_BYTES = 12; - unsigned char transactionID[NUM_TRANSACTION_ID_BYTES]; - loadRandomIdentifier(transactionID, NUM_TRANSACTION_ID_BYTES); - memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID)); + QUuid randomUUID = QUuid::createUuid(); + memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); // lookup the IP for the STUN server static HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 882d4719c8..efd5180d03 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -206,15 +206,6 @@ bool isInEnvironment(const char* environment) { } } -void loadRandomIdentifier(unsigned char* identifierBuffer, int numBytes) { - // seed the the random number generator - srand(time(NULL)); - - for (int i = 0; i < numBytes; i++) { - identifierBuffer[i] = rand() % 256; - } -} - ////////////////////////////////////////////////////////////////////////////////////////// // Function: getCmdOption() // Description: Handy little function to tell you if a command line flag and option was diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 439b85aa54..d8d686c63b 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -96,8 +96,6 @@ int getNthBit(unsigned char byte, int ordinal); /// determines the bit placement bool isInEnvironment(const char* environment); -void loadRandomIdentifier(unsigned char* identifierBuffer, int numBytes); - const char* getCmdOption(int argc, const char * argv[],const char* option); bool cmdOptionExists(int argc, const char * argv[],const char* option); From f0f3cf7282d763e8e54b08578462cf739079273b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 14:36:02 -0700 Subject: [PATCH 15/22] remove seed to random number generator that is no longer needed --- libraries/script-engine/src/ScriptEngine.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e880760a1d..c0834aad9d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -235,12 +235,6 @@ void ScriptEngine::init() { // let the VoxelPacketSender know how frequently we plan to call it _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); - - // call srand to seed the random number generator - srand(QDateTime::currentMSecsSinceEpoch() - + QCoreApplication::applicationPid() - + NodeList::getInstance()->getNodeSocket().localPort()); - } void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { From 56869a769ded54ed3e3300649762fe15d06b36f0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 14:53:32 -0700 Subject: [PATCH 16/22] remove some extraneous domain-server check in debugging --- libraries/shared/src/NodeList.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index e6f542a358..b2a494aa32 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -582,12 +582,7 @@ void NodeList::sendDomainServerCheckIn() { packetStream << nodeTypeOfInterest; } - qDebug() << "sending DS check in size" << domainServerPacket.size() << "to" << _domainInfo.getSockAddr(); - qint64 code = writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret()); - qDebug() << "Code returned is" << code; - if (code == -1) { - qDebug() << "the socket error is" << _nodeSocket.errorString(); - } + writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret()); const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; From d5be3f1c9f17d037b5ca62991111383e1a328b6b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 15:01:24 -0700 Subject: [PATCH 17/22] output socket errors if they occur --- libraries/shared/src/NodeList.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index b2a494aa32..85e981490e 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -171,8 +171,13 @@ qint64 NodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& d ++_numCollectedPackets; _numCollectedBytes += datagram.size(); - return _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr.getAddress(), destinationSockAddr.getPort()); + qint64 bytesWritten = _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr.getAddress(), destinationSockAddr.getPort()); + if (bytesWritten < 0) { + qDebug() << "ERROR in writeDatagram:" << _nodeSocket.error() << "-" << _nodeSocket.errorString(); + } + + return bytesWritten; } qint64 NodeList::writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode, From 292fb51b43b4bca1f90c2005a5a783eeb706042e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 15:05:17 -0700 Subject: [PATCH 18/22] use performance throttling instead of distance to decide when to send --- assignment-client/src/avatars/AvatarMixer.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 55a60194c0..ba95040ba9 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -133,13 +133,9 @@ void AvatarMixer::broadcastAvatarData() { AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); AvatarData& otherAvatar = otherNodeData->getAvatar(); glm::vec3 otherPosition = otherAvatar.getPosition(); - float distanceToAvatar = glm::length(myPosition - otherPosition); - // The full rate distance is the distance at which EVERY update will be sent for this avatar - // at a distance of twice the full rate distance, there will be a 50% chance of sending this avatar's update - const float FULL_RATE_DISTANCE = 2.f; - // Decide whether to send this avatar's data based on it's distance from us - if ((distanceToAvatar == 0.f) || (randFloat() < FULL_RATE_DISTANCE / distanceToAvatar) - * (1 - _performanceThrottlingRatio)) { + + // Decide whether to send this avatar's data based on current performance throttling + if (_performanceThrottlingRatio == 0 || randFloat() < (1.0f - _performanceThrottlingRatio)) { QByteArray avatarByteArray; avatarByteArray.append(otherNode->getUUID().toRfc4122()); avatarByteArray.append(otherAvatar.toByteArray()); From 6b8c60e964c97d817f18b9e29f2970cc527daf21 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 15:10:53 -0700 Subject: [PATCH 19/22] remove waitForBytesWritten for initial tests --- assignment-client/src/avatars/AvatarMixer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index ba95040ba9..9df66d0362 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -184,8 +184,6 @@ void AvatarMixer::broadcastAvatarData() { ++_sumIdentityPackets; } - - nodeList->getNodeSocket().waitForBytesWritten(-1); } } } From 6f4f55038b3cb2c39dbe76c19ec1c4fde6d71cb7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 15:31:53 -0700 Subject: [PATCH 20/22] add back selective inclusion of Avatars based on distance --- assignment-client/src/avatars/AvatarMixer.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 9df66d0362..b9bc87ecf2 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -134,8 +134,14 @@ void AvatarMixer::broadcastAvatarData() { AvatarData& otherAvatar = otherNodeData->getAvatar(); glm::vec3 otherPosition = otherAvatar.getPosition(); - // Decide whether to send this avatar's data based on current performance throttling - if (_performanceThrottlingRatio == 0 || randFloat() < (1.0f - _performanceThrottlingRatio)) { + float distanceToAvatar = glm::length(myPosition - otherPosition); + // The full rate distance is the distance at which EVERY update will be sent for this avatar + // at a distance of twice the full rate distance, there will be a 50% chance of sending this avatar's update + const float FULL_RATE_DISTANCE = 2.f; + + // Decide whether to send this avatar's data based on it's distance from us + if ((_performanceThrottlingRatio == 0 || randFloat() < (1.0f - _performanceThrottlingRatio)) + && (distanceToAvatar == 0.f || randFloat() < FULL_RATE_DISTANCE / distanceToAvatar)) { QByteArray avatarByteArray; avatarByteArray.append(otherNode->getUUID().toRfc4122()); avatarByteArray.append(otherAvatar.toByteArray()); From c8b3ae0c405d43ff10a6dd532833979a68f8f4c7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 15:49:47 -0700 Subject: [PATCH 21/22] add a mutex to NodeData and leverage in AvatarMixer threads --- assignment-client/src/avatars/AvatarMixer.cpp | 20 ++++++++++++++----- .../src/avatars/AvatarMixerClientData.h | 1 + libraries/shared/src/NodeData.cpp | 6 ++++++ libraries/shared/src/NodeData.h | 7 ++++++- libraries/shared/src/NodeList.cpp | 2 ++ 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b9bc87ecf2..aaf58a7f42 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -114,21 +114,25 @@ void AvatarMixer::broadcastAvatarData() { NodeList* nodeList = NodeList::getInstance(); + AvatarMixerClientData* nodeData = NULL; + AvatarMixerClientData* otherNodeData = NULL; + foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - if (node->getLinkedData() && node->getType() == NodeType::Agent && node->getActiveSocket()) { + if (node->getLinkedData() && node->getType() == NodeType::Agent && node->getActiveSocket() + && (nodeData = reinterpret_cast(node->getLinkedData()))->getMutex().tryLock()) { ++_sumListeners; // reset packet pointers for this node mixedAvatarByteArray.resize(numPacketHeaderBytes); - AvatarMixerClientData* myData = reinterpret_cast(node->getLinkedData()); - AvatarData& avatar = myData->getAvatar(); + AvatarData& avatar = nodeData->getAvatar(); glm::vec3 myPosition = avatar.getPosition(); // this is an AGENT we have received head data from // send back a packet with other active node data to this node foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) { - if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()) { + if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID() + && (otherNodeData = reinterpret_cast(otherNode->getLinkedData()))->getMutex().tryLock()) { AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); AvatarData& otherAvatar = otherNodeData->getAvatar(); @@ -158,7 +162,7 @@ void AvatarMixer::broadcastAvatarData() { // if the receiving avatar has just connected make sure we send out the mesh and billboard // for this avatar (assuming they exist) - bool forceSend = !myData->checkAndSetHasReceivedFirstPackets(); + bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets(); // we will also force a send of billboard or identity packet // if either has changed in the last frame @@ -191,10 +195,14 @@ void AvatarMixer::broadcastAvatarData() { ++_sumIdentityPackets; } } + + otherNodeData->getMutex().unlock(); } } nodeList->writeDatagram(mixedAvatarByteArray, node); + + nodeData->getMutex().unlock(); } } @@ -238,6 +246,7 @@ void AvatarMixer::readPendingDatagrams() { // parse the identity packet and update the change timestamp if appropriate if (avatar.hasIdentityChangedAfterParsing(receivedPacket)) { + QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->setIdentityChangeTimestamp(QDateTime::currentMSecsSinceEpoch()); } } @@ -254,6 +263,7 @@ void AvatarMixer::readPendingDatagrams() { // parse the billboard packet and update the change timestamp if appropriate if (avatar.hasBillboardChangedAfterParsing(receivedPacket)) { + QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch()); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index bc0a54f06b..22ef69aa6e 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -9,6 +9,7 @@ #ifndef __hifi__AvatarMixerClientData__ #define __hifi__AvatarMixerClientData__ +#include #include #include diff --git a/libraries/shared/src/NodeData.cpp b/libraries/shared/src/NodeData.cpp index a7fa18f409..e3800f8b93 100644 --- a/libraries/shared/src/NodeData.cpp +++ b/libraries/shared/src/NodeData.cpp @@ -8,6 +8,12 @@ #include "NodeData.h" +NodeData::NodeData() : + _mutex() +{ + +} + NodeData::~NodeData() { } \ No newline at end of file diff --git a/libraries/shared/src/NodeData.h b/libraries/shared/src/NodeData.h index cf800fc3cd..99b5fda9be 100644 --- a/libraries/shared/src/NodeData.h +++ b/libraries/shared/src/NodeData.h @@ -16,9 +16,14 @@ class Node; class NodeData : public QObject { Q_OBJECT public: - + NodeData(); virtual ~NodeData() = 0; virtual int parseData(const QByteArray& packet) = 0; + + QMutex& getMutex() { return _mutex; } + +private: + QMutex _mutex; }; #endif diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 85e981490e..e80f25709a 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -317,6 +317,8 @@ int NodeList::updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode linkedDataCreateCallback(matchingNode.data()); } + QMutexLocker linkedDataLocker(&matchingNode->getLinkedData()->getMutex()); + return matchingNode->getLinkedData()->parseData(packet); } From 34634af975bbf940597ba3443de4413eedbe5bc5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 16:05:51 -0700 Subject: [PATCH 22/22] add mutex header to NodeData --- assignment-client/src/avatars/AvatarMixerClientData.h | 1 - libraries/shared/src/NodeData.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 22ef69aa6e..bc0a54f06b 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -9,7 +9,6 @@ #ifndef __hifi__AvatarMixerClientData__ #define __hifi__AvatarMixerClientData__ -#include #include #include diff --git a/libraries/shared/src/NodeData.h b/libraries/shared/src/NodeData.h index 99b5fda9be..b6b75443a2 100644 --- a/libraries/shared/src/NodeData.h +++ b/libraries/shared/src/NodeData.h @@ -9,6 +9,7 @@ #ifndef hifi_NodeData_h #define hifi_NodeData_h +#include #include class Node;