From e88207a73e2b714e4efe36e2b7c00dfdba83b12f Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 29 Jun 2018 11:34:22 -0700 Subject: [PATCH 01/57] Move script-engine creation to run thread ScriptsModel should be created and destroyed on same thread, or else crash on Windows occurs. See https://github.com/highfidelity/hifi/pull/13492 --- assignment-client/src/Agent.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 42924a8487..956b1d88de 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -76,12 +76,6 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); - DependencyManager::set(ScriptEngine::AGENT_SCRIPT); - - DependencyManager::set(); - DependencyManager::set(); - // Needed to ensure the creation of the DebugDraw instance on the main thread DebugDraw::getInstance(); @@ -157,6 +151,11 @@ void Agent::handleAudioPacket(QSharedPointer message) { static const QString AGENT_LOGGING_NAME = "agent"; void Agent::run() { + DependencyManager::set(); + DependencyManager::set(ScriptEngine::AGENT_SCRIPT); + + DependencyManager::set(); + DependencyManager::set(); // make sure we request our script once the agent connects to the domain auto nodeList = DependencyManager::get(); From 2b997ec159c622c45a19f66bc54c5914948d9026 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 5 Jul 2018 14:43:34 -0700 Subject: [PATCH 02/57] Don't reference Agent members after scriptEngine returns The Agent calls the script engine execute loop, which calls the Qt event processor, which may delete the Agent via deleteLater(). Take out unnecessary call to setFinished() to avoid crash. Probably this was a latent bug exposed by the eralier PR changes. --- assignment-client/src/Agent.cpp | 3 +-- libraries/script-engine/src/ScriptEngine.cpp | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 956b1d88de..9a5d69f64b 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -494,8 +494,6 @@ void Agent::executeScript() { Frame::clearFrameHandler(AVATAR_FRAME_TYPE); DependencyManager::destroy(); - - setFinished(true); } QUuid Agent::getSessionUUID() const { @@ -829,6 +827,7 @@ void Agent::aboutToFinish() { // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(nullptr); + DependencyManager::get()->setPacketSender(nullptr); DependencyManager::get()->cleanup(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 99c02ba1f6..97364a6119 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1199,7 +1199,8 @@ void ScriptEngine::run() { stopAllTimers(); // make sure all our timers are stopped if the script is ending emit scriptEnding(); - if (entityScriptingInterface->getEntityPacketSender()->serversExist()) { + if (entityScriptingInterface->getEntityPacketSender() && + entityScriptingInterface->getEntityPacketSender()->serversExist()) { // release the queue of edit entity messages. entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages(); From 363f27cb032728b0cbb4d452b924485ecacc5c3f Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 6 Jul 2018 10:27:20 -0700 Subject: [PATCH 03/57] Run Agent script on its own thread Uses ScriptEngine::runInThread() so that on takedown the Agent is not deleted under its own executeScript() method. --- assignment-client/src/Agent.cpp | 13 +++++++------ libraries/script-engine/src/ScriptEngine.cpp | 3 +-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 9a5d69f64b..7398e70634 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -488,12 +488,13 @@ void Agent::executeScript() { avatarDataTimer->setTimerType(Qt::PreciseTimer); avatarDataTimer->start(); - _scriptEngine->run(); + connect(_scriptEngine.data(), &ScriptEngine::doneRunning, this, [=]() { + Frame::clearFrameHandler(AUDIO_FRAME_TYPE); + Frame::clearFrameHandler(AVATAR_FRAME_TYPE); + DependencyManager::destroy(); + setFinished(true); }); - Frame::clearFrameHandler(AUDIO_FRAME_TYPE); - Frame::clearFrameHandler(AVATAR_FRAME_TYPE); - - DependencyManager::destroy(); + _scriptEngine->runInThread(); } QUuid Agent::getSessionUUID() const { @@ -823,11 +824,11 @@ void Agent::aboutToFinish() { if (_scriptEngine) { _scriptEngine->stop(); + _scriptEngine->waitTillDoneRunning(); } // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(nullptr); - DependencyManager::get()->setPacketSender(nullptr); DependencyManager::get()->cleanup(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 97364a6119..99c02ba1f6 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1199,8 +1199,7 @@ void ScriptEngine::run() { stopAllTimers(); // make sure all our timers are stopped if the script is ending emit scriptEnding(); - if (entityScriptingInterface->getEntityPacketSender() && - entityScriptingInterface->getEntityPacketSender()->serversExist()) { + if (entityScriptingInterface->getEntityPacketSender()->serversExist()) { // release the queue of edit entity messages. entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages(); From 000423673e8ccec268e652c356d6771595225ea9 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 9 Jul 2018 13:30:44 -0700 Subject: [PATCH 04/57] Don't explicitly destroy script engine; emit finished when actually finished --- assignment-client/src/Agent.cpp | 2 +- libraries/networking/src/ThreadedAssignment.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 42924a8487..caa087e120 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -497,6 +497,7 @@ void Agent::executeScript() { DependencyManager::destroy(); setFinished(true); + emit finished(); } QUuid Agent::getSessionUUID() const { @@ -838,7 +839,6 @@ void Agent::aboutToFinish() { // destroy all other created dependencies DependencyManager::destroy(); - DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 9a69d9b3d8..ad1fc83c65 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -67,7 +67,7 @@ void ThreadedAssignment::setFinished(bool isFinished) { // call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup aboutToFinish(); - emit finished(); + //emit finished(); } } } From 9505f5c6feeec338ecbf9bd61dc09be08b90c386 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 9 Jul 2018 13:38:17 -0700 Subject: [PATCH 05/57] Revert current approach --- assignment-client/src/Agent.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 7398e70634..42924a8487 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -76,6 +76,12 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(ScriptEngine::AGENT_SCRIPT); + + DependencyManager::set(); + DependencyManager::set(); + // Needed to ensure the creation of the DebugDraw instance on the main thread DebugDraw::getInstance(); @@ -151,11 +157,6 @@ void Agent::handleAudioPacket(QSharedPointer message) { static const QString AGENT_LOGGING_NAME = "agent"; void Agent::run() { - DependencyManager::set(); - DependencyManager::set(ScriptEngine::AGENT_SCRIPT); - - DependencyManager::set(); - DependencyManager::set(); // make sure we request our script once the agent connects to the domain auto nodeList = DependencyManager::get(); @@ -488,13 +489,14 @@ void Agent::executeScript() { avatarDataTimer->setTimerType(Qt::PreciseTimer); avatarDataTimer->start(); - connect(_scriptEngine.data(), &ScriptEngine::doneRunning, this, [=]() { - Frame::clearFrameHandler(AUDIO_FRAME_TYPE); - Frame::clearFrameHandler(AVATAR_FRAME_TYPE); - DependencyManager::destroy(); - setFinished(true); }); + _scriptEngine->run(); - _scriptEngine->runInThread(); + Frame::clearFrameHandler(AUDIO_FRAME_TYPE); + Frame::clearFrameHandler(AVATAR_FRAME_TYPE); + + DependencyManager::destroy(); + + setFinished(true); } QUuid Agent::getSessionUUID() const { @@ -824,7 +826,6 @@ void Agent::aboutToFinish() { if (_scriptEngine) { _scriptEngine->stop(); - _scriptEngine->waitTillDoneRunning(); } // our entity tree is going to go away so tell that to the EntityScriptingInterface From 1c23fc75a476879c9c5f5967f54109eeea544420 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 9 Jul 2018 16:56:54 -0700 Subject: [PATCH 06/57] Emit finished signal from AC derived classes To avoid Agent destruction-in-use move emit finished from setFinished to Agent methods. For other ACs emit from aboutToFinish(). --- assignment-client/src/Agent.cpp | 2 ++ assignment-client/src/assets/AssetServer.cpp | 1 + assignment-client/src/entities/EntityServer.cpp | 2 ++ assignment-client/src/scripts/EntityScriptServer.cpp | 2 ++ libraries/networking/src/ThreadedAssignment.cpp | 2 -- libraries/networking/src/ThreadedAssignment.h | 2 +- 6 files changed, 8 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index caa087e120..1b36cd7207 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -335,6 +335,7 @@ void Agent::scriptRequestFinished() { } setFinished(true); + emit finished(); } request->deleteLater(); @@ -839,6 +840,7 @@ void Agent::aboutToFinish() { // destroy all other created dependencies DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index e0c35b7148..7999d55fae 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -342,6 +342,7 @@ void AssetServer::aboutToFinish() { while (_pendingBakes.size() > 0) { QCoreApplication::processEvents(); } + emit finished(); } void AssetServer::run() { diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 8b86ba5eb2..4162a4f156 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -72,6 +72,8 @@ void EntityServer::aboutToFinish() { DependencyManager::get()->cleanup(); OctreeServer::aboutToFinish(); + + emit finished(); } void EntityServer::handleEntityPacket(QSharedPointer message, SharedNodePointer senderNode) { diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index eea8e8b470..6198b03fbe 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -582,4 +582,6 @@ void EntityScriptServer::aboutToFinish() { _codec->releaseEncoder(_encoder); _encoder = nullptr; } + + emit finished(); } diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index ad1fc83c65..d6c09d2a6c 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -66,8 +66,6 @@ void ThreadedAssignment::setFinished(bool isFinished) { // call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup aboutToFinish(); - - //emit finished(); } } } diff --git a/libraries/networking/src/ThreadedAssignment.h b/libraries/networking/src/ThreadedAssignment.h index 9372cfa667..f245ec917f 100644 --- a/libraries/networking/src/ThreadedAssignment.h +++ b/libraries/networking/src/ThreadedAssignment.h @@ -25,7 +25,7 @@ public: ~ThreadedAssignment() { stop(); } void setFinished(bool isFinished); - virtual void aboutToFinish() { }; + virtual void aboutToFinish() { emit finished(); }; void addPacketStatsAndSendStatsPacket(QJsonObject statsObject); public slots: From 2c629e2ec8fefde1e48ade09e4efe44a93666d82 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 9 Jul 2018 17:38:39 -0700 Subject: [PATCH 07/57] Don't destroy ScriptEngines in aboutToFinish() --- assignment-client/src/Agent.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 1b36cd7207..6a637223fe 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -840,7 +840,6 @@ void Agent::aboutToFinish() { // destroy all other created dependencies DependencyManager::destroy(); - DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); From a305a6a534ba514d40a2bba85496f022f2df8139 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 12 Jul 2018 16:48:41 -0700 Subject: [PATCH 08/57] Emit finished in Agent::aboutToFinish() if script execution hasn't started yet --- assignment-client/src/Agent.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 6a637223fe..1cbe1f66ed 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -828,6 +828,8 @@ void Agent::aboutToFinish() { if (_scriptEngine) { _scriptEngine->stop(); + } else { + emit finished(); } // our entity tree is going to go away so tell that to the EntityScriptingInterface From ab4faca2fd4ad609bed961c4876050b11022f6bf Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 13 Jul 2018 11:18:28 -0700 Subject: [PATCH 09/57] Emit finished() in new AudioMixer::aboutToFinish(). --- assignment-client/src/audio/AudioMixer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index d56b22466e..2db5a296f7 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -109,6 +109,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : void AudioMixer::aboutToFinish() { DependencyManager::destroy(); + emit finished(); } void AudioMixer::queueAudioPacket(QSharedPointer message, SharedNodePointer node) { From 806228299e20838bca9ffab77abd303c6f025921 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 15 Jul 2018 10:31:48 +1200 Subject: [PATCH 10/57] Stub out Audio JSDoc --- interface/src/scripting/Audio.h | 54 ++++++++++------- libraries/audio/src/AudioEffectOptions.cpp | 43 +++++++------- libraries/audio/src/AudioEffectOptions.h | 47 ++++++++------- libraries/audio/src/AudioInjectorOptions.cpp | 12 ++++ libraries/audio/src/Sound.h | 8 ++- .../src/AudioScriptingInterface.h | 45 +++++++------- .../script-engine/src/ScriptAudioInjector.h | 58 +++++++++++++++++++ 7 files changed, 182 insertions(+), 85 deletions(-) diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 8d16b06995..f7dd252c54 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -26,7 +26,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { SINGLETON_DEPENDENCY /**jsdoc - * The Audio API features tools to help control audio contexts and settings. + * The Audio API ... TODO * * @namespace Audio * @@ -35,12 +35,13 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { * @hifi-server-entity * @hifi-assignment-client * - * @property {boolean} muted - * @property {boolean} noiseReduction - * @property {number} inputVolume - * @property {number} inputLevel Read-only. - * @property {string} context Read-only. - * @property {} devices Read-only. + * @property {boolean} muted - true if TODO, otherwise false. + * @property {boolean} noiseReduction - true if TODO, otherwise false. + * @property {number} inputVolume - TODO + * @property {number} inputLevel - TODO Read-only. + * @property {string} context - TODO Read-only. + * @property {object} devices Read-only. Deprecated: This property is deprecated and will be + * removed. */ Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) @@ -69,87 +70,100 @@ public: /**jsdoc * @function Audio.setInputDevice - * @param {} device + * @param {object} device * @param {boolean} isHMD + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD); /**jsdoc * @function Audio.setOutputDevice - * @param {} device + * @param {object} device * @param {boolean} isHMD + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD); /**jsdoc + * TODO * @function Audio.setReverb - * @param {boolean} enable + * @param {boolean} enable - TODO */ Q_INVOKABLE void setReverb(bool enable); /**jsdoc + * TODO * @function Audio.setReverbOptions - * @param {AudioEffectOptions} options + * @param {AudioEffectOptions} options - TODO */ Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); /**jsdoc + * TODO * @function Audio.startRecording - * @param {string} filename - * @returns {boolean} + * @param {string} filename - TODO + * @returns {boolean} true if TODO, otherwise false. */ Q_INVOKABLE bool startRecording(const QString& filename); /**jsdoc + * TODO * @function Audio.stopRecording */ Q_INVOKABLE void stopRecording(); /**jsdoc + * TODO * @function Audio.getRecording - * @returns {boolean} + * @returns {boolean} true if TODO, otherwise false. */ Q_INVOKABLE bool getRecording(); signals: /**jsdoc + * Triggered when ... TODO * @function Audio.nop * @returns {Signal} */ void nop(); /**jsdoc + * Triggered when ... TODO * @function Audio.mutedChanged - * @param {boolean} isMuted + * @param {boolean} isMuted - TODO * @returns {Signal} */ void mutedChanged(bool isMuted); /**jsdoc + * Triggered when ... TODO * @function Audio.noiseReductionChanged - * @param {boolean} isEnabled + * @param {boolean} isEnabled - TODO * @returns {Signal} */ void noiseReductionChanged(bool isEnabled); /**jsdoc + * Triggered when ... TODO * @function Audio.inputVolumeChanged - * @param {number} volume + * @param {number} volume - TODO * @returns {Signal} */ void inputVolumeChanged(float volume); /**jsdoc + * Triggered when ... TODO * @function Audio.inputLevelChanged - * @param {number} level + * @param {number} level - TODO * @returns {Signal} */ void inputLevelChanged(float level); /**jsdoc + * Triggered when ... TODO * @function Audio.contextChanged - * @param {string} context + * @param {string} context - TODO * @returns {Signal} */ void contextChanged(const QString& context); @@ -158,7 +172,7 @@ public slots: /**jsdoc * @function Audio.onContextChanged - * @returns {Signal} + * @deprecated This function is deprecated and will be removed. */ void onContextChanged(); diff --git a/libraries/audio/src/AudioEffectOptions.cpp b/libraries/audio/src/AudioEffectOptions.cpp index edb0ff52ae..3ed1296b63 100644 --- a/libraries/audio/src/AudioEffectOptions.cpp +++ b/libraries/audio/src/AudioEffectOptions.cpp @@ -59,28 +59,29 @@ static void setOption(QScriptValue arguments, const QString name, float defaultV } /**jsdoc + * TODO * @typedef {object} AudioEffectOptions.ReverbOptions - * @property {number} bandwidth - * @property {number} preDelay - * @property {number} lateDelay - * @property {number} reverbTime - * @property {number} earlyDiffusion - * @property {number} lateDiffusion - * @property {number} roomSize - * @property {number} density - * @property {number} bassMult - * @property {number} bassFreq - * @property {number} highGain - * @property {number} highFreq - * @property {number} modRate - * @property {number} modDepth - * @property {number} earlyGain - * @property {number} lateGain - * @property {number} earlyMixLeft - * @property {number} earlyMixRight - * @property {number} lateMixLeft - * @property {number} lateMixRight - * @property {number} wetDryMix + * @property {number} bandwidth - TODO + * @property {number} preDelay - TODO + * @property {number} lateDelay - TODO + * @property {number} reverbTime - TODO + * @property {number} earlyDiffusion - TODO + * @property {number} lateDiffusion - TODO + * @property {number} roomSize - TODO + * @property {number} density - TODO + * @property {number} bassMult - TODO + * @property {number} bassFreq - TODO + * @property {number} highGain - TODO + * @property {number} highFreq - TODO + * @property {number} modRate - TODO + * @property {number} modDepth - TODO + * @property {number} earlyGain - TODO + * @property {number} lateGain - TODO + * @property {number} earlyMixLeft - TODO + * @property {number} earlyMixRight - TODO + * @property {number} lateMixLeft - TODO + * @property {number} lateMixRight - TODO + * @property {number} wetDryMix - TODO */ AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) { setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth); diff --git a/libraries/audio/src/AudioEffectOptions.h b/libraries/audio/src/AudioEffectOptions.h index 1afd4e21be..0e2e017d49 100644 --- a/libraries/audio/src/AudioEffectOptions.h +++ b/libraries/audio/src/AudioEffectOptions.h @@ -16,35 +16,38 @@ #include /**jsdoc + * TODO + * Used in the {@link Audio} API. + * * @class AudioEffectOptions - * @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null] + * @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null] - TODO * * @hifi-interface * @hifi-client-entity * @hifi-server-entity * @hifi-assignment-client * - * @property {number} bandwidth=10000 - * @property {number} preDelay=20 - * @property {number} lateDelay=0 - * @property {number} reverbTime=2 - * @property {number} earlyDiffusion=100 - * @property {number} lateDiffusion=100 - * @property {number} roomSize=50 - * @property {number} density=100 - * @property {number} bassMult=1.5 - * @property {number} bassFreq=250 - * @property {number} highGain=-6 - * @property {number} highFreq=3000 - * @property {number} modRate=2.3 - * @property {number} modDepth=50 - * @property {number} earlyGain=0 - * @property {number} lateGain=0 - * @property {number} earlyMixLeft=20 - * @property {number} earlyMixRight=20 - * @property {number} lateMixLeft=90 - * @property {number} lateMixRight=90 - * @property {number} wetDryMix=50 + * @property {number} bandwidth=10000 - TODO + * @property {number} preDelay=20 - TODO + * @property {number} lateDelay=0 - TODO + * @property {number} reverbTime=2 - TODO + * @property {number} earlyDiffusion=100 - TODO + * @property {number} lateDiffusion=100 - TODO + * @property {number} roomSize=50 - TODO + * @property {number} density=100 - TODO + * @property {number} bassMult=1.5 - TODO + * @property {number} bassFreq=250 - TODO + * @property {number} highGain=-6 - TODO + * @property {number} highFreq=3000 - TODO + * @property {number} modRate=2.3 - TODO + * @property {number} modDepth=50 - TODO + * @property {number} earlyGain=0 - TODO + * @property {number} lateGain=0 - TODO + * @property {number} earlyMixLeft=20 - TODO + * @property {number} earlyMixRight=20 - TODO + * @property {number} lateMixLeft=90 - TODO + * @property {number} lateMixRight=90 - TODO + * @property {number} wetDryMix=50 - TODO */ class AudioEffectOptions : public QObject { diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 2f82cae137..8691432bcd 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -45,6 +45,18 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje return obj; } +/**jsdoc + * TODO + * @typedef {object} AudioInjector.AudioInjectorOptions + * @property {Vec3} position=Vec3.ZERO - TODO + * @property {Quat} orientation=Quat.IDENTITY - TODO + * @property {number} volume=1 - TODO + * @property {boolean} loop=false - true if TODO, otherwise false. + * @property {boolean} ignorePenumbra=false - true if TODO, otherwise false. + * @property {boolean} localOnly=false - true if TODO, otherwise false. + * @property {number} secondOffset=0 - TODO + * @property {number} pitch=1 - TODO + */ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions) { if (!object.isObject()) { qWarning() << "Audio injector options is not an object."; diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 061c4a2417..0edcb59cca 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -79,6 +79,9 @@ private: typedef QSharedPointer SharedSoundPointer; /**jsdoc + * TODO + * Created by {@link SoundCache.getSound}. + * * @class SoundObject * * @hifi-interface @@ -86,8 +89,8 @@ typedef QSharedPointer SharedSoundPointer; * @hifi-server-entity * @hifi-assignment-client * - * @property {boolean} downloaded - * @property {number} duration + * @property {boolean} downloaded - true if TODO, otherwise false. + * @property {number} duration - TODO */ class SoundScriptingInterface : public QObject { Q_OBJECT @@ -103,6 +106,7 @@ public: float getDuration() { return _sound->getDuration(); } /**jsdoc + * Triggered when TODO * @function SoundObject.ready * @returns {Signal} */ diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 20ca977da1..6b87b0ee67 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -35,91 +35,96 @@ protected: // these methods are protected to stop C++ callers from calling, but invokable from script /**jsdoc + * TODO * @function Audio.playSound - * @param {} sound - * @param {} [injectorOptions=null] - * @returns {object} + * @param {SoundObject} sound - TODO + * @param {AudioInjector.AudioInjectorOptions} [injectorOptions={}] - TODO + * @returns {AudioInjector} TODO */ Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); /**jsdoc + * TODO * @function Audio.playSystemSound - * @param {} sound - * @param {} position - * @returns {object} + * @param {SoundObject} sound - TODO + * @param {Vec3} position - TODO + * @returns {AudioInjector} TODO */ // FIXME: there is no way to play a positionless sound Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound, const QVector3D& position); /**jsdoc + * TODO * @function Audio.setStereoInput - * @param {boolean} stereo + * @param {boolean} stereo - TODO */ Q_INVOKABLE void setStereoInput(bool stereo); /**jsdoc + * TODO * @function Audio.isStereoInput - * @returns {boolean} + * @returns {boolean} true if TODO, otherwise false. */ Q_INVOKABLE bool isStereoInput(); signals: /**jsdoc - * The client has been muted by the mixer. + * Triggered when the client is muted by the mixer. * @function Audio.mutedByMixer * @returns {Signal} */ void mutedByMixer(); /**jsdoc - * The entire environment has been muted by the mixer. + * Triggered when the entire environment is muted by the mixer. TODO: What is the "whole environment"? * @function Audio.environmentMuted * @returns {Signal} */ void environmentMuted(); /**jsdoc - * The client has received its first packet from the audio mixer. + * Triggered when the client receives its first packet from the audio mixer. * @function Audio.receivedFirstPacket * @returns {Signal} */ void receivedFirstPacket(); /**jsdoc - * The client has been disconnected from the audio mixer. + * Triggered when the client is disconnected from the audio mixer. * @function Audio.disconnected * @returns {Signal} */ void disconnected(); /**jsdoc - * The noise gate has opened. + * Triggered when the noise gate is opened. TODO: Description of noise gate. * @function Audio.noiseGateOpened * @returns {Signal} */ void noiseGateOpened(); /**jsdoc - * The noise gate has closed. + * Triggered when the noise gate is closed. TODO: Description of noise gate. * @function Audio.noiseGateClosed * @returns {Signal} */ void noiseGateClosed(); /**jsdoc - * A frame of mic input audio has been received and processed. + * Triggered when a frame of microphone audio input is processed. * @function Audio.inputReceived - * @param {} inputSamples + * @param {Int16Array} inputSamples - TODO * @returns {Signal} */ void inputReceived(const QByteArray& inputSamples); /**jsdoc - * @function Audio.isStereoInputChanged - * @param {boolean} isStereo - * @returns {Signal} - */ + * TODO + * @function Audio.isStereoInputChanged + * @param {boolean} isStereo - TODO + * @returns {Signal} + */ void isStereoInputChanged(bool isStereo); private: diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h index 4c2871dd34..5d66461160 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.h +++ b/libraries/script-engine/src/ScriptAudioInjector.h @@ -16,6 +16,21 @@ #include +/**jsdoc + * TODO + * Used in the {@link Audio} API. + * + * @class AudioInjector + * + * @hifi-interface + * @hifi-client-entity + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {boolean} playing - true if TODO, otherwise false. Read-only. + * @property {number} loudness - TODO Read-only. + * @property {AudioInjector.AudioInjectorOptions} options - TODO + */ class ScriptAudioInjector : public QObject { Q_OBJECT @@ -26,19 +41,62 @@ public: ScriptAudioInjector(const AudioInjectorPointer& injector); ~ScriptAudioInjector(); public slots: + + /**jsdoc + * TODO + * @function AudioInjector.restart + */ void restart() { _injector->restart(); } + + /**jsdoc + * TODO + * @function AudioInjector.stop + */ void stop() { _injector->stop(); } + /**jsdoc + * TODO + * @function AudioInjector.getOptions + * @returns {AudioInjector.AudioInjectorOptions} TODO + */ const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); } + + /**jsdoc + * TODO + * @function AudioInjector.setOptions + * @param {AudioInjector.AudioInjectorOptions} options - TODO + */ void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); } + /**jsdoc + * TODO + * @function AudioInjector.getLoudness + * @returns {number} TODO + */ float getLoudness() const { return _injector->getLoudness(); } + + /**jsdoc + * TODO + * @function AudioInjector.isPlaying + * @returns {boolean} true if TODO, otherwise false. + */ bool isPlaying() const { return _injector->isPlaying(); } signals: + + /**jsdoc + * Triggered when TODO + * @function AudioInjector.finished + * @returns {Signal} + */ void finished(); protected slots: + + /**jsdoc + * TODO + * @function AudioInjector.stopInjectorImmediately + */ void stopInjectorImmediately(); private: AudioInjectorPointer _injector; From b60ea1f6a28f5a67b60928872f12185a53d1f9b3 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 16 Jul 2018 18:25:46 -0700 Subject: [PATCH 11/57] Fixes from reviewer Call base version from aboutToFinish(); add comment for script-engine logic. --- assignment-client/src/Agent.cpp | 8 +++++--- assignment-client/src/assets/AssetServer.cpp | 2 +- assignment-client/src/audio/AudioMixer.cpp | 2 +- assignment-client/src/entities/EntityServer.cpp | 3 +-- assignment-client/src/scripts/EntityScriptServer.cpp | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index ca2cf30d68..0be691662d 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -336,7 +336,7 @@ void Agent::scriptRequestFinished() { } setFinished(true); - emit finished(); + ThreadedAssignment::aboutToFinish(); } request->deleteLater(); @@ -499,7 +499,7 @@ void Agent::executeScript() { DependencyManager::destroy(); setFinished(true); - emit finished(); + ThreadedAssignment::aboutToFinish(); } QUuid Agent::getSessionUUID() const { @@ -827,10 +827,12 @@ void Agent::processAgentAvatarAudio() { void Agent::aboutToFinish() { setIsAvatar(false);// will stop timers for sending identity packets + // If script engine not started yet then finish up, else will be done when + // script engine exits. if (_scriptEngine) { _scriptEngine->stop(); } else { - emit finished(); + ThreadedAssignment::aboutToFinish(); } // our entity tree is going to go away so tell that to the EntityScriptingInterface diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 7999d55fae..4ff4078255 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -342,7 +342,7 @@ void AssetServer::aboutToFinish() { while (_pendingBakes.size() > 0) { QCoreApplication::processEvents(); } - emit finished(); + ThreadedAssignment::aboutToFinish(); } void AssetServer::run() { diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 2db5a296f7..ec9f26b53c 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -109,7 +109,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : void AudioMixer::aboutToFinish() { DependencyManager::destroy(); - emit finished(); + ThreadedAssignment::aboutToFinish(); } void AudioMixer::queueAudioPacket(QSharedPointer message, SharedNodePointer node) { diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 4162a4f156..6303cafde8 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -72,8 +72,7 @@ void EntityServer::aboutToFinish() { DependencyManager::get()->cleanup(); OctreeServer::aboutToFinish(); - - emit finished(); + ThreadedAssignment::aboutToFinish(); } void EntityServer::handleEntityPacket(QSharedPointer message, SharedNodePointer senderNode) { diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index a1258fe8db..b40ffbcfb3 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -588,5 +588,5 @@ void EntityScriptServer::aboutToFinish() { _encoder = nullptr; } - emit finished(); + ThreadedAssignment::aboutToFinish(); } From 396382afd7bc1930825ffcb50fb814b927f51330 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 19 Jul 2018 09:31:48 +1200 Subject: [PATCH 12/57] Fill in Audio API JSDoc --- interface/src/scripting/Audio.h | 115 +++++++++++++----- libraries/audio/src/AudioEffectOptions.cpp | 44 +++---- libraries/audio/src/AudioEffectOptions.h | 49 ++++---- libraries/audio/src/AudioInjectorOptions.cpp | 23 ++-- libraries/audio/src/Sound.h | 16 ++- libraries/audio/src/SoundCache.h | 3 +- .../src/AudioScriptingInterface.h | 65 +++++++--- .../script-engine/src/ScriptAudioInjector.h | 76 +++++++++--- 8 files changed, 263 insertions(+), 128 deletions(-) diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index f7dd252c54..4b8eb6aabc 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -26,7 +26,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { SINGLETON_DEPENDENCY /**jsdoc - * The Audio API ... TODO + * The Audio API provides facilities to interact with audio inputs and outputs and to play sounds. * * @namespace Audio * @@ -35,15 +35,23 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { * @hifi-server-entity * @hifi-assignment-client * - * @property {boolean} muted - true if TODO, otherwise false. - * @property {boolean} noiseReduction - true if TODO, otherwise false. - * @property {number} inputVolume - TODO - * @property {number} inputLevel - TODO Read-only. - * @property {string} context - TODO Read-only. - * @property {object} devices Read-only. Deprecated: This property is deprecated and will be + * @property {boolean} muted - true if the audio input is muted, otherwise false. + * @property {boolean} noiseReduction - true if noise reduction is enabled, otherwise false. When + * enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just + * above the noise floor. + * @property {number} inputLevel - The loudness of the audio input, range 0.0 (no sound) – + * 1.0 (the onset of clipping). Read-only. + * @property {number} inputVolume - Adjusts the volume of the input audio; range 0.01.0. + * If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some + * devices, and others might only support values of 0.0 and 1.0. + * @property {boolean} isStereoInput - true if the input audio is being used in stereo, otherwise + * false. Some devices do not support stereo, in which case the value is always false. + * @property {string} context - The current context of the audio: either "Desktop" or "HMD". + * Read-only. + * @property {object} devices Read-only. Deprecated: This property is deprecated and will be * removed. */ - + Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged) Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged) @@ -85,85 +93,132 @@ public: Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD); /**jsdoc - * TODO + * Enable or disable reverberation. Reverberation is done by the client, on the post-mix audio. The reverberation options + * come from either the domain's audio zone if used — configured on the server — or as scripted by + * {@link Audio.setReverbOptions|setReverbOptions}. * @function Audio.setReverb - * @param {boolean} enable - TODO - */ + * @param {boolean} enable - true to enable reverberation, false to disable. + * @example Enable reverberation for a short while. + * var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav"); + * var injector; + * var injectorOptions = { + * position: MyAvatar.position + * }; + * + * Script.setTimeout(function () { + * print("Reverb OFF"); + * Audio.setReverb(false); + * injector = Audio.playSound(sound, injectorOptions); + * }, 1000); + * + * Script.setTimeout(function () { + * var reverbOptions = new AudioEffectOptions(); + * reverbOptions.roomSize = 100; + * Audio.setReverbOptions(reverbOptions); + * print("Reverb ON"); + * Audio.setReverb(true); + * }, 4000); + * + * Script.setTimeout(function () { + * print("Reverb OFF"); + * Audio.setReverb(false); + * }, 8000); */ Q_INVOKABLE void setReverb(bool enable); /**jsdoc - * TODO + * Configure reverberation options. Use {@link Audio.setReverb|setReverb} to enable or disable reverberation. * @function Audio.setReverbOptions - * @param {AudioEffectOptions} options - TODO + * @param {AudioEffectOptions} options - The reverberation options. */ Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); /**jsdoc - * TODO + * Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format. * @function Audio.startRecording - * @param {string} filename - TODO - * @returns {boolean} true if TODO, otherwise false. + * @param {string} filename - The path and name of the file to make the recording in. Should have a .wav + * extension. The file is overwritten if it already exists. + * @returns {boolean} true if the specified file could be opened and audio recording has started, otherwise + * false. + * @example Make a 10 second audio recording. + * var filename = File.getTempDir() + "/audio.wav"; + * if (Audio.startRecording(filename)) { + * Script.setTimeout(function () { + * Audio.stopRecording(); + * print("Audio recording made in: " + filename); + * }, 10000); + * + * } else { + * print("Could not make an audio recording in: " + filename); + * } */ Q_INVOKABLE bool startRecording(const QString& filename); /**jsdoc - * TODO + * Finish making an audio recording started with {@link Audio.startRecording|startRecording}. * @function Audio.stopRecording */ Q_INVOKABLE void stopRecording(); /**jsdoc - * TODO + * Check whether an audio recording is currently being made. * @function Audio.getRecording - * @returns {boolean} true if TODO, otherwise false. + * @returns {boolean} true if an audio recording is currently being made, otherwise false. */ Q_INVOKABLE bool getRecording(); signals: /**jsdoc - * Triggered when ... TODO * @function Audio.nop * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. */ void nop(); /**jsdoc - * Triggered when ... TODO + * Triggered when the audio input is muted or unmuted. * @function Audio.mutedChanged - * @param {boolean} isMuted - TODO + * @param {boolean} isMuted - true if the audio input is muted, otherwise false. * @returns {Signal} + * @example Report when audio input is muted or unmuted + * Audio.mutedChanged.connect(function (isMuted) { + * print("Audio muted: " + isMuted); + * }); */ void mutedChanged(bool isMuted); /**jsdoc - * Triggered when ... TODO + * Triggered when the audio input noise reduction is enabled or disabled. * @function Audio.noiseReductionChanged - * @param {boolean} isEnabled - TODO + * @param {boolean} isEnabled - true if audio input noise reduction is enabled, otherwise false. * @returns {Signal} */ void noiseReductionChanged(bool isEnabled); /**jsdoc - * Triggered when ... TODO + * Triggered when the input audio volume changes. * @function Audio.inputVolumeChanged - * @param {number} volume - TODO + * @param {number} volume - The requested volume to be applied to the audio input, range 0.0 – + * 1.0. The resulting value of Audio.inputVolume depends on the capabilities of the device: + * for example, the volume can't be changed on some devices, and others might only support values of 0.0 + * and 1.0. * @returns {Signal} */ void inputVolumeChanged(float volume); /**jsdoc - * Triggered when ... TODO + * Triggered when the input audio level changes. * @function Audio.inputLevelChanged - * @param {number} level - TODO + * @param {number} level - The loudness of the input audio, range 0.0 (no sound) – 1.0 (the + * onset of clipping). * @returns {Signal} */ void inputLevelChanged(float level); /**jsdoc - * Triggered when ... TODO + * Triggered when the current context of the audio changes. * @function Audio.contextChanged - * @param {string} context - TODO + * @param {string} context - The current context of the audio: either "Desktop" or "HMD". * @returns {Signal} */ void contextChanged(const QString& context); diff --git a/libraries/audio/src/AudioEffectOptions.cpp b/libraries/audio/src/AudioEffectOptions.cpp index 3ed1296b63..873ce7df87 100644 --- a/libraries/audio/src/AudioEffectOptions.cpp +++ b/libraries/audio/src/AudioEffectOptions.cpp @@ -59,29 +59,29 @@ static void setOption(QScriptValue arguments, const QString name, float defaultV } /**jsdoc - * TODO + * Reverberation options that can be used to initialize an {@link AudioEffectOptions} object when created. * @typedef {object} AudioEffectOptions.ReverbOptions - * @property {number} bandwidth - TODO - * @property {number} preDelay - TODO - * @property {number} lateDelay - TODO - * @property {number} reverbTime - TODO - * @property {number} earlyDiffusion - TODO - * @property {number} lateDiffusion - TODO - * @property {number} roomSize - TODO - * @property {number} density - TODO - * @property {number} bassMult - TODO - * @property {number} bassFreq - TODO - * @property {number} highGain - TODO - * @property {number} highFreq - TODO - * @property {number} modRate - TODO - * @property {number} modDepth - TODO - * @property {number} earlyGain - TODO - * @property {number} lateGain - TODO - * @property {number} earlyMixLeft - TODO - * @property {number} earlyMixRight - TODO - * @property {number} lateMixLeft - TODO - * @property {number} lateMixRight - TODO - * @property {number} wetDryMix - TODO + * @property {number} bandwidth=10000 - The corner frequency (Hz) of the low-pass filter at reverb input. + * @property {number} preDelay=20 - The delay (milliseconds) between dry signal and the onset of early reflections. + * @property {number} lateDelay=0 - The delay (milliseconds) between early reflections and the onset of reverb tail. + * @property {number} reverbTime=2 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60. + * @property {number} earlyDiffusion=100 - Adjusts the buildup of echo density in the early reflections, normally 100%. + * @property {number} lateDiffusion=100 - Adjusts the buildup of echo density in the reverb tail, normally 100%. + * @property {number} roomSize=50 - The apparent room size, from small (0%) to large (100%). + * @property {number} density=100 - Adjusts the echo density in the reverb tail, normally 100%. + * @property {number} bassMult=1.5 - Adjusts the bass-frequency reverb time, as multiple of reverbTime. + * @property {number} bassFreq=250 - The crossover frequency (Hz) for the onset of bassMult. + * @property {number} highGain=-6 - Reduces the high-frequency reverb time, as attenuation (dB). + * @property {number} highFreq=3000 - The crossover frequency (Hz) for the onset of highGain. + * @property {number} modRate=2.3 - The rate of modulation (Hz) of the LFO-modulated delay lines. + * @property {number} modDepth=50 - The depth of modulation (percent) of the LFO-modulated delay lines. + * @property {number} earlyGain=0 - Adjusts the relative level (dB) of the early reflections. + * @property {number} lateGain=0 - Adjusts the relative level (dB) of the reverb tail. + * @property {number} earlyMixLeft=20 - The apparent distance of the source (percent) in the early reflections. + * @property {number} earlyMixRight=20 - The apparent distance of the source (percent) in the early reflections. + * @property {number} lateMixLeft=90 - The apparent distance of the source (percent) in the reverb tail. + * @property {number} lateMixRight=90 - The apparent distance of the source (percent) in the reverb tail. + * @property {number} wetDryMix=50 - Adjusts the wet/dry ratio, from completely dry (0%) to completely wet (100%). */ AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) { setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth); diff --git a/libraries/audio/src/AudioEffectOptions.h b/libraries/audio/src/AudioEffectOptions.h index 0e2e017d49..4bc7957142 100644 --- a/libraries/audio/src/AudioEffectOptions.h +++ b/libraries/audio/src/AudioEffectOptions.h @@ -16,38 +16,39 @@ #include /**jsdoc - * TODO - * Used in the {@link Audio} API. + * Audio effect options used by the {@link Audio} API. + * + *

Create using new AudioEffectOptions(reverbOptions).

* * @class AudioEffectOptions - * @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null] - TODO + * @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null] - Reverberation options. * * @hifi-interface * @hifi-client-entity * @hifi-server-entity * @hifi-assignment-client * - * @property {number} bandwidth=10000 - TODO - * @property {number} preDelay=20 - TODO - * @property {number} lateDelay=0 - TODO - * @property {number} reverbTime=2 - TODO - * @property {number} earlyDiffusion=100 - TODO - * @property {number} lateDiffusion=100 - TODO - * @property {number} roomSize=50 - TODO - * @property {number} density=100 - TODO - * @property {number} bassMult=1.5 - TODO - * @property {number} bassFreq=250 - TODO - * @property {number} highGain=-6 - TODO - * @property {number} highFreq=3000 - TODO - * @property {number} modRate=2.3 - TODO - * @property {number} modDepth=50 - TODO - * @property {number} earlyGain=0 - TODO - * @property {number} lateGain=0 - TODO - * @property {number} earlyMixLeft=20 - TODO - * @property {number} earlyMixRight=20 - TODO - * @property {number} lateMixLeft=90 - TODO - * @property {number} lateMixRight=90 - TODO - * @property {number} wetDryMix=50 - TODO + * @property {number} bandwidth=10000 - The corner frequency (Hz) of the low-pass filter at reverb input. + * @property {number} preDelay=20 - The delay (milliseconds) between dry signal and the onset of early reflections. + * @property {number} lateDelay=0 - The delay (milliseconds) between early reflections and the onset of reverb tail. + * @property {number} reverbTime=2 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60. + * @property {number} earlyDiffusion=100 - Adjusts the buildup of echo density in the early reflections, normally 100%. + * @property {number} lateDiffusion=100 - Adjusts the buildup of echo density in the reverb tail, normally 100%. + * @property {number} roomSize=50 - The apparent room size, from small (0%) to large (100%). + * @property {number} density=100 - Adjusts the echo density in the reverb tail, normally 100%. + * @property {number} bassMult=1.5 - Adjusts the bass-frequency reverb time, as multiple of reverbTime. + * @property {number} bassFreq=250 - The crossover frequency (Hz) for the onset of bassMult. + * @property {number} highGain=-6 - Reduces the high-frequency reverb time, as attenuation (dB). + * @property {number} highFreq=3000 - The crossover frequency (Hz) for the onset of highGain. + * @property {number} modRate=2.3 - The rate of modulation (Hz) of the LFO-modulated delay lines. + * @property {number} modDepth=50 - The depth of modulation (percent) of the LFO-modulated delay lines. + * @property {number} earlyGain=0 - Adjusts the relative level (dB) of the early reflections. + * @property {number} lateGain=0 - Adjusts the relative level (dB) of the reverb tail. + * @property {number} earlyMixLeft=20 - The apparent distance of the source (percent) in the early reflections. + * @property {number} earlyMixRight=20 - The apparent distance of the source (percent) in the early reflections. + * @property {number} lateMixLeft=90 - The apparent distance of the source (percent) in the reverb tail. + * @property {number} lateMixRight=90 - The apparent distance of the source (percent) in the reverb tail. + * @property {number} wetDryMix=50 - Adjusts the wet/dry ratio, from completely dry (0%) to completely wet (100%). */ class AudioEffectOptions : public QObject { diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 8691432bcd..0f4ab7ff42 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -46,16 +46,21 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje } /**jsdoc - * TODO + * Configures how an audio injector plays its audio. * @typedef {object} AudioInjector.AudioInjectorOptions - * @property {Vec3} position=Vec3.ZERO - TODO - * @property {Quat} orientation=Quat.IDENTITY - TODO - * @property {number} volume=1 - TODO - * @property {boolean} loop=false - true if TODO, otherwise false. - * @property {boolean} ignorePenumbra=false - true if TODO, otherwise false. - * @property {boolean} localOnly=false - true if TODO, otherwise false. - * @property {number} secondOffset=0 - TODO - * @property {number} pitch=1 - TODO + * @property {Vec3} position=Vec3.ZERO - The position in the domain to play the sound. + * @property {Quat} orientation=Quat.IDENTITY - The orientation in the domain to play the sound in. + * @property {number} volume=1.0 - Playback volume, between 0.0 and 1.0. + * @property {number} pitch=1.0 - Alter the pitch of the sound, within +/- 2 octaves. The value is the relative sample rate to + * resample the sound at, range 0.062516.0. A value of 0.0625 lowers the + * pitch by 2 octaves; 1.0 is no change in pitch; 16.0 raises the pitch by 2 octaves. + * @property {boolean} loop=false - If true, the sound is played repeatedly until playback is stopped. + * @property {number} secondOffset=0 - Starts playback from a specified time (seconds) within the sound file, ≥ + * 0. + * @property {boolean} localOnly=false - IF true, the sound is played back locally on the client rather than to + * others via the audio mixer. + * @property {boolean} ignorePenumbra=false - Deprecated: This property is deprecated and will be + * removed. */ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions) { if (!object.isObject()) { diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 0edcb59cca..348600e4ae 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -79,8 +79,13 @@ private: typedef QSharedPointer SharedSoundPointer; /**jsdoc - * TODO - * Created by {@link SoundCache.getSound}. + * An audio resource, created by {@link SoundCache.getSound}, to be played back using {@link Audio.playSound}. + *

Supported formats:

+ *
    + *
  • WAV: 16-bit uncompressed WAV at any sample rate, with 1 (mono), 2(stereo), or 4 (ambisonic) channels.
  • + *
  • MP3: Mono or stereo, at any sample rate.
  • + *
  • RAW: 48khz 16-bit mono or stereo. Filename must include ".stereo" to be interpreted as stereo.
  • + *
* * @class SoundObject * @@ -89,8 +94,9 @@ typedef QSharedPointer SharedSoundPointer; * @hifi-server-entity * @hifi-assignment-client * - * @property {boolean} downloaded - true if TODO, otherwise false. - * @property {number} duration - TODO + * @property {boolean} downloaded - true if the sound has been downloaded and is ready to be played, otherwise + * false. + * @property {number} duration - The duration of the sound, in seconds. */ class SoundScriptingInterface : public QObject { Q_OBJECT @@ -106,7 +112,7 @@ public: float getDuration() { return _sound->getDuration(); } /**jsdoc - * Triggered when TODO + * Triggered when the sound has been downloaded and is ready to be played. * @function SoundObject.ready * @returns {Signal} */ diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h index 4352b1d459..ecb4451153 100644 --- a/libraries/audio/src/SoundCache.h +++ b/libraries/audio/src/SoundCache.h @@ -78,8 +78,9 @@ public: /**jsdoc + * Loads the content of an audio file into an object, ready for playback by {@link Audio.playSound}. * @function SoundCache.getSound - * @param {string} url + * @param {string} url - The URL of the audio file to load. See {@link SoundObject} for supported formats. * @returns {SoundObject} */ Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 6b87b0ee67..72b9541763 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -23,6 +23,7 @@ class AudioScriptingInterface : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY + // JSDoc for property is in Audio.h. Q_PROPERTY(bool isStereoInput READ isStereoInput WRITE setStereoInput NOTIFY isStereoInputChanged) public: @@ -35,49 +36,69 @@ protected: // these methods are protected to stop C++ callers from calling, but invokable from script /**jsdoc - * TODO + * Starts playing — "injecting" — the content of an audio file. The sound is played globally (sent to the audio + * mixer) so that everyone hears it, unless the injectorOptions has localOnly set to + * true in which case only the client hears the sound played. No sound is played if sent to the audio mixer + * but the client is not connected to an audio mixer. The {@link AudioInjector} object returned by the function can be used + * to control the playback and get information about its current state. * @function Audio.playSound - * @param {SoundObject} sound - TODO - * @param {AudioInjector.AudioInjectorOptions} [injectorOptions={}] - TODO - * @returns {AudioInjector} TODO + * @param {SoundObject} sound - The content of an audio file, loaded using {@link SoundCache.getSound}. See + * {@link SoundObject} for supported formats. + * @param {AudioInjector.AudioInjectorOptions} [injectorOptions={}] - Audio injector configuration. + * @returns {AudioInjector} The audio injector that plays the audio file. + * @example Play a sound. + * var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav"); + * var injector; + * var injectorOptions = { + * position: MyAvatar.position + * }; + * + * Script.setTimeout(function () { // Give the sound time to load. + * injector = Audio.playSound(sound, injectorOptions); + * }, 1000); */ Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); /**jsdoc - * TODO + * Start playing the content of an audio file, locally (isn't sent to the audio mixer). This is the same as calling + * {@link Audio.playSound} with {@link AudioInjector.AudioInjectorOptions} localOnly set true and + * the specified position. * @function Audio.playSystemSound - * @param {SoundObject} sound - TODO - * @param {Vec3} position - TODO - * @returns {AudioInjector} TODO + * @param {SoundObject} sound - The content of an audio file, loaded using {@link SoundCache.getSound}. See + * {@link SoundObject} for supported formats. + * @param {Vec3} position - The position in the domain to play the sound. + * @returns {AudioInjector} The audio injector that plays the audio file. */ // FIXME: there is no way to play a positionless sound Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound, const QVector3D& position); /**jsdoc - * TODO + * Set whether or not the audio input should be used in stereo. If the audio input does not support stereo then setting a + * value of true has no effect. * @function Audio.setStereoInput - * @param {boolean} stereo - TODO + * @param {boolean} stereo - true if the audio input should be used in stereo, otherwise false. */ Q_INVOKABLE void setStereoInput(bool stereo); /**jsdoc - * TODO + * Get whether or not the audio input is used in stereo. * @function Audio.isStereoInput - * @returns {boolean} true if TODO, otherwise false. + * @returns {boolean} true if the audio input is used in stereo, otherwise false. */ Q_INVOKABLE bool isStereoInput(); signals: /**jsdoc - * Triggered when the client is muted by the mixer. + * Triggered when the client is muted by the mixer because their loudness value for the noise background has reached the + * threshold set for the domain in the server settings. * @function Audio.mutedByMixer * @returns {Signal} */ void mutedByMixer(); /**jsdoc - * Triggered when the entire environment is muted by the mixer. TODO: What is the "whole environment"? + * Triggered when everyone is muted by the mixer. * @function Audio.environmentMuted * @returns {Signal} */ @@ -98,31 +119,35 @@ signals: void disconnected(); /**jsdoc - * Triggered when the noise gate is opened. TODO: Description of noise gate. + * Triggered when the noise gate is opened: the input audio signal is no longer blocked (fully attenuated) because it has + * risen above an adaptive threshold set just above the noise floor. Only occurs if Audio.noiseReduction is + * true. * @function Audio.noiseGateOpened * @returns {Signal} */ void noiseGateOpened(); /**jsdoc - * Triggered when the noise gate is closed. TODO: Description of noise gate. + * Triggered when the noise gate is closed: the input audio signal is blocked (fully attenuated) because it has fallen + * below an adaptive threshold set just above the noise floor. Only occurs if Audio.noiseReduction is + * true. * @function Audio.noiseGateClosed * @returns {Signal} */ void noiseGateClosed(); /**jsdoc - * Triggered when a frame of microphone audio input is processed. + * Triggered when a frame of audio input is processed. * @function Audio.inputReceived - * @param {Int16Array} inputSamples - TODO + * @param {Int16Array} inputSamples - The audio input processed. * @returns {Signal} */ void inputReceived(const QByteArray& inputSamples); /**jsdoc - * TODO + * Triggered when the input audio use changes between mono and stereo. * @function Audio.isStereoInputChanged - * @param {boolean} isStereo - TODO + * @param {boolean} isStereo - true if the input audio is stereo, otherwise false. * @returns {Signal} */ void isStereoInputChanged(bool isStereo); diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h index 5d66461160..2c88d618e1 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.h +++ b/libraries/script-engine/src/ScriptAudioInjector.h @@ -17,8 +17,7 @@ #include /**jsdoc - * TODO - * Used in the {@link Audio} API. + * Plays — "injects" — the content of an audio file. Used in the {@link Audio} API. * * @class AudioInjector * @@ -27,9 +26,11 @@ * @hifi-server-entity * @hifi-assignment-client * - * @property {boolean} playing - true if TODO, otherwise false. Read-only. - * @property {number} loudness - TODO Read-only. - * @property {AudioInjector.AudioInjectorOptions} options - TODO + * @property {boolean} playing - true if the audio is currently playing, otherwise false. + * Read-only. + * @property {number} loudness - The loudness in the last frame of audio, range 0.01.0. + * Read-only. + * @property {AudioInjector.AudioInjectorOptions} options - Configures how the injector plays the audio. */ class ScriptAudioInjector : public QObject { Q_OBJECT @@ -43,58 +44,99 @@ public: public slots: /**jsdoc - * TODO + * Stop current playback, if any, and start playing from the beginning. * @function AudioInjector.restart */ void restart() { _injector->restart(); } /**jsdoc - * TODO + * Stop audio playback. * @function AudioInjector.stop + * @example Stop playing a sound before it finishes. + * var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav"); + * var injector; + * var injectorOptions = { + * position: MyAvatar.position + * }; + * + * Script.setTimeout(function () { // Give the sound time to load. + * injector = Audio.playSound(sound, injectorOptions); + * }, 1000); + * + * Script.setTimeout(function () { + * injector.stop(); + * }, 2000); */ void stop() { _injector->stop(); } /**jsdoc - * TODO + * Get the current configuration of the audio injector. * @function AudioInjector.getOptions - * @returns {AudioInjector.AudioInjectorOptions} TODO + * @returns {AudioInjector.AudioInjectorOptions} Configuration of how the injector plays the audio. */ const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); } /**jsdoc - * TODO + * Configure how the injector plays the audio. * @function AudioInjector.setOptions - * @param {AudioInjector.AudioInjectorOptions} options - TODO + * @param {AudioInjector.AudioInjectorOptions} options - Configuration of how the injector plays the audio. */ void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); } /**jsdoc - * TODO + * Get the loudness of the most recent frame of audio played. * @function AudioInjector.getLoudness - * @returns {number} TODO + * @returns {number} The loudness of the most recent frame of audio played, range 0.01.0. */ float getLoudness() const { return _injector->getLoudness(); } /**jsdoc - * TODO + * Get whether or not the audio is currently playing. * @function AudioInjector.isPlaying - * @returns {boolean} true if TODO, otherwise false. + * @returns {boolean} true if the audio is currently playing, otherwise false. + * @example See if a sound is playing. + * var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav"); + * var injector; + * var injectorOptions = { + * position: MyAvatar.position + * }; + * + * Script.setTimeout(function () { // Give the sound time to load. + * injector = Audio.playSound(sound, injectorOptions); + * }, 1000); + * + * Script.setTimeout(function () { + * print("Sound is playing: " + injector.isPlaying()); + * }, 2000); */ bool isPlaying() const { return _injector->isPlaying(); } signals: /**jsdoc - * Triggered when TODO + * Triggered when the audio has finished playing. * @function AudioInjector.finished * @returns {Signal} + * @example Report when a sound has finished playing. + * var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav"); + * var injector; + * var injectorOptions = { + * position: MyAvatar.position + * }; + * + * Script.setTimeout(function () { // Give the sound time to load. + * injector = Audio.playSound(sound, injectorOptions); + * injector.finished.connect(function () { + * print("Finished playing sound"); + * }); + * }, 1000); */ void finished(); protected slots: /**jsdoc - * TODO + * Stop audio playback. (Synonym of {@link AudioInjector.stop|stop}.) * @function AudioInjector.stopInjectorImmediately */ void stopInjectorImmediately(); From 3002abeddc5500692279e17b66d7a90a82712451 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 18 Jul 2018 18:22:36 -0700 Subject: [PATCH 13/57] Add DS option to turn off packet authentication --- domain-server/resources/describe-settings.json | 8 ++++++++ domain-server/src/DomainServer.cpp | 6 +++++- libraries/networking/src/LimitedNodeList.cpp | 7 ++++--- libraries/networking/src/LimitedNodeList.h | 3 +++ libraries/networking/src/NodeList.cpp | 4 ++++ 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 83dd633d22..4e68d29e85 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -46,6 +46,14 @@ "default": "40102", "type": "int", "advanced": true + }, + { + "name": "enable_authentication", + "label": "Enable Authentication", + "help": "Enable secure checksums on communication that uses the High Fidelity protocol. Increases security with possibly a small performance penalty.", + "default": true, + "type": "checkbox", + "advanced": true } ] }, diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index eccd1c1e20..5324bb407d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -630,6 +630,7 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) { void DomainServer::setupNodeListAndAssignments() { const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port"; + static const QString ENABLE_PACKET_AUTHENTICATION = "metaverse.enable_authentication"; QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION); int domainServerPort = localPortValue.toInt(); @@ -696,6 +697,9 @@ void DomainServer::setupNodeListAndAssignments() { } } + bool isAuthEnabled = _settingsManager.valueOrDefaultValueForKeyPath(ENABLE_PACKET_AUTHENTICATION).toBool(); + nodeList->setAuthenticatePackets(isAuthEnabled); + connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded); connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled); @@ -1133,7 +1137,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif extendedHeaderStream << node->getUUID(); extendedHeaderStream << node->getLocalID(); extendedHeaderStream << node->getPermissions(); - + extendedHeaderStream << limitedNodeList->getAuthenticatePackets(); auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader); // always send the node their own UUID back diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 502874fbfb..b6b2369703 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -328,9 +328,10 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe if (sourceNode) { bool verifiedPacket = !PacketTypeEnum::getNonVerifiedPackets().contains(headerType); - bool ignoreVerification = isDomainServer() && PacketTypeEnum::getDomainIgnoredVerificationPackets().contains(headerType); + bool verificationEnabled = !(isDomainServer() && PacketTypeEnum::getDomainIgnoredVerificationPackets().contains(headerType)) + && _useAuthentication; - if (verifiedPacket && !ignoreVerification) { + if (verifiedPacket && verificationEnabled) { QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet); QByteArray expectedHash; @@ -383,7 +384,7 @@ void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAut packet.writeSourceID(getSessionLocalID()); } - if (hmacAuth + if (_useAuthentication && hmacAuth && !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType()) && !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) { packet.writeVerificationHash(*hmacAuth); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 05374bbfbb..cffc49521a 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -307,6 +307,8 @@ public: bool isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode = nullptr); bool isPacketVerified(const udt::Packet& packet) { return isPacketVerifiedWithSource(packet); } + void setAuthenticatePackets(bool useAuthentication) { _useAuthentication = useAuthentication; } + bool getAuthenticatePackets() const { return _useAuthentication; } static void makeSTUNRequestPacket(char* stunRequestPacket); @@ -394,6 +396,7 @@ protected: HifiSockAddr _publicSockAddr; HifiSockAddr _stunSockAddr { STUN_SERVER_HOSTNAME, STUN_SERVER_PORT }; bool _hasTCPCheckedLocalSocket { false }; + bool _useAuthentication { true }; PacketReceiver* _packetReceiver; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 2ce734dd26..dd351ef940 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -665,6 +665,10 @@ void NodeList::processDomainServerList(QSharedPointer message) NodePermissions newPermissions; packetStream >> newPermissions; setPermissions(newPermissions); + // Is packet authentication enabled? + bool isAuthenticated; + packetStream >> isAuthenticated; + setAuthenticatePackets(isAuthenticated); // pull each node in the packet while (packetStream.device()->pos() < message->getSize()) { From 5d13f6c030676dac17911679b37c1fe4c19b23ef Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 19 Jul 2018 09:55:28 -0700 Subject: [PATCH 14/57] Bump DomainListVersion for new protocol --- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 6e1aca83e5..3c47cdcf09 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -313,7 +313,8 @@ enum class DomainListVersion : PacketVersion { PrePermissionsGrid = 18, PermissionsGrid, GetUsernameFromUUIDSupport, - GetMachineFingerprintFromUUIDSupport + GetMachineFingerprintFromUUIDSupport, + AuthenticationOptional }; enum class AudioVersion : PacketVersion { From d147d17b707f53c32c5b8b784dbf2ee49ebb0868 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 19 Jul 2018 10:04:29 -0700 Subject: [PATCH 15/57] Update PacketType version for domain list --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 13ffcb5120..3536ab832c 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -27,7 +27,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::StunResponse: return 17; case PacketType::DomainList: - return static_cast(DomainListVersion::GetMachineFingerprintFromUUIDSupport); + return static_cast(DomainListVersion::AuthenticationOptional); case PacketType::EntityAdd: case PacketType::EntityClone: case PacketType::EntityEdit: From 47e99cdeacbc2023fd0762669e22bb644ee3df18 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 24 Jul 2018 16:50:37 -0700 Subject: [PATCH 16/57] Initial code for AC side of Enabling Physics plan When interface sets query flag send only entities close to main frustrum origin, until no more available, then send unreliabe sequence # reliably. --- .../src/entities/EntityTreeSendThread.cpp | 9 ++++ .../src/entities/EntityTreeSendThread.h | 2 + .../src/octree/OctreeSendThread.cpp | 30 +++++++---- .../src/octree/OctreeSendThread.h | 1 + interface/src/Application.cpp | 1 + .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/octree/src/OctreeQuery.cpp | 19 ++++++- libraries/octree/src/OctreeQuery.h | 8 +++ .../shared/src/shared/ConicalViewFrustum.cpp | 5 ++ .../shared/src/shared/ConicalViewFrustum.h | 3 ++ tools/dissectors/1-hfudt.lua | 51 +++++++++++++++++-- 12 files changed, 116 insertions(+), 16 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index f008ef9925..af0c09aec7 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -17,6 +17,8 @@ #include "EntityServer.h" +// Initially just send all items within this distance. +const float EntityTreeSendThread::INITIAL_RADIUS = 50.0f; EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) : OctreeSendThread(myServer, node) @@ -112,6 +114,13 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); newView.lodScaleFactor = powf(2.0f, lodLevelOffset); + if (nodeData->wantReportInitialResult() && !newView.viewFrustums.empty()) { + auto& mainView = newView.viewFrustums[0]; + // Force acceptance within INITIAL_RADIUS. + mainView.setSimpleRadius(INITIAL_RADIUS); + newView.lodScaleFactor = 0.0f; + } + startNewTraversal(newView, root); // When the viewFrustum changed the sort order may be incorrect, so we re-sort diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index 1305d7bfc7..8c66f47aea 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -58,6 +58,8 @@ private: int32_t _numEntitiesOffset { 0 }; uint16_t _numEntities { 0 }; + const static float INITIAL_RADIUS; + private slots: void editingEntityPointer(const EntityItemPointer& entity); void deletingEntityPointer(EntityItem* entity); diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index e9aa44b970..98b06d6344 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -195,6 +195,7 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* // actually send it OctreeServer::didCallWriteDatagram(this); DependencyManager::get()->sendUnreliablePacket(statsPacket, *node); + _lastSequenceNumber = (decltype(_lastSequenceNumber)) statsPacket.getSequenceNumber(); } else { // not enough room in the packet, send two packets @@ -211,10 +212,9 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* //_totalWastedBytes += 0; _trueBytesSent += numBytes; numPackets++; + NLPacket& sentPacket = nodeData->getPacket(); if (debug) { - NLPacket& sentPacket = nodeData->getPacket(); - sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS)); OCTREE_PACKET_SEQUENCE sequence; @@ -231,9 +231,10 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* // second packet OctreeServer::didCallWriteDatagram(this); - DependencyManager::get()->sendUnreliablePacket(nodeData->getPacket(), *node); + DependencyManager::get()->sendUnreliablePacket(sentPacket, *node); + _lastSequenceNumber = (decltype(_lastSequenceNumber)) sentPacket.getSequenceNumber(); - numBytes = nodeData->getPacket().getDataSize(); + numBytes = sentPacket.getDataSize(); _totalBytes += numBytes; _totalPackets++; // we count wasted bytes here because we were unable to fit the stats packet @@ -243,8 +244,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* numPackets++; if (debug) { - NLPacket& sentPacket = nodeData->getPacket(); - sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS)); OCTREE_PACKET_SEQUENCE sequence; @@ -265,9 +264,11 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) { // just send the octree packet OctreeServer::didCallWriteDatagram(this); - DependencyManager::get()->sendUnreliablePacket(nodeData->getPacket(), *node); + NLPacket& sentPacket = nodeData->getPacket(); + DependencyManager::get()->sendUnreliablePacket(sentPacket, *node); + _lastSequenceNumber = (decltype(_lastSequenceNumber)) sentPacket.getSequenceNumber(); - int numBytes = nodeData->getPacket().getDataSize(); + int numBytes = sentPacket.getDataSize(); _totalBytes += numBytes; _totalPackets++; int thisWastedBytes = udt::MAX_PACKET_SIZE - numBytes; @@ -276,8 +277,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* _trueBytesSent += numBytes; if (debug) { - NLPacket& sentPacket = nodeData->getPacket(); - sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS)); OCTREE_PACKET_SEQUENCE sequence; @@ -512,6 +511,17 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre OctreeServer::trackInsideTime((float)(usecTimestampNow() - startInside)); } + if (params.stopReason == EncodeBitstreamParams::FINISHED && nodeData->wantReportInitialResult()) { + // Dealt with nearby entities. + nodeData->setReportInitialResult(false); + + // send EntityQueryInitialResultsComplete reliable packet ... + auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, -1, true); + QDataStream initialCompletionStream(initialCompletion.get()); + initialCompletionStream << _lastSequenceNumber; + DependencyManager::get()->sendPacket(std::move(initialCompletion), *node.data()); + } + if (somethingToSend && _myServer->wantsVerboseDebug()) { qCDebug(octree) << "Hit PPS Limit, packetsSentThisInterval =" << _packetsSentThisInterval << " maxPacketsPerInterval = " << maxPacketsPerInterval diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 91c0ec7adc..58d2e3a160 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -60,6 +60,7 @@ protected: QWeakPointer _node; OctreeServer* _myServer { nullptr }; QUuid _nodeUuid; + udt::SequenceNumber::Type _lastSequenceNumber { 0 }; private: /// Called before a packetDistributor pass to allow for pre-distribution processing diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 311c08b858..55e634275d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6134,6 +6134,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { auto lodManager = DependencyManager::get(); _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); + _octreeQuery.setReportInitialResult(true); auto nodeList = DependencyManager::get(); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 13ffcb5120..bb9666ee37 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -95,7 +95,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarIdentityRequest: return 22; default: - return 21; + return 22; } } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 6e1aca83e5..7cba3baaf0 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -126,6 +126,7 @@ public: EntityScriptCallMethod, ChallengeOwnershipRequest, ChallengeOwnershipReply, + EntityQueryInitialResultsComplete, OctreeDataFileRequest, OctreeDataFileReply, diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index 0d56dbb88f..5fb886df10 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -27,6 +27,10 @@ OctreeQuery::OctreeQuery(bool randomizeConnectionID) { } } +const OctreeQuery::OctreeQueryFlags operator|=(OctreeQuery::OctreeQueryFlags& lhs, int rhs) { + return lhs = OctreeQuery::OctreeQueryFlags(lhs | rhs); +} + int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { unsigned char* bufferStart = destinationBuffer; @@ -76,7 +80,12 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { memcpy(destinationBuffer, binaryParametersDocument.data(), binaryParametersBytes); destinationBuffer += binaryParametersBytes; } - + + OctreeQueryFlags queryFlags { NoFlags }; + queryFlags |= (_reportInitialResult ? OctreeQuery::WantInitialResult : 0); + memcpy(destinationBuffer, &queryFlags, sizeof(queryFlags)); + destinationBuffer += sizeof(queryFlags); + return destinationBuffer - bufferStart; } @@ -150,6 +159,12 @@ int OctreeQuery::parseData(ReceivedMessage& message) { QWriteLocker jsonParameterLocker { &_jsonParametersLock }; _jsonParameters = newJsonDocument.object(); } - + + OctreeQueryFlags queryFlags; + memcpy(&queryFlags, sourceBuffer, sizeof(queryFlags)); + sourceBuffer += sizeof(queryFlags); + + _reportInitialResult = bool(queryFlags & OctreeQueryFlags::WantInitialResult); + return sourceBuffer - startPosition; } diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index 0ca75bdeb0..711a22e9b5 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -52,6 +52,10 @@ public: bool hasReceivedFirstQuery() const { return _hasReceivedFirstQuery; } + // Want a report when the initial query is complete. + bool wantReportInitialResult() const { return _reportInitialResult; } + void setReportInitialResult(bool reportInitialResult) { _reportInitialResult = reportInitialResult; } + signals: void incomingConnectionIDChanged(); @@ -73,8 +77,12 @@ protected: QJsonObject _jsonParameters; QReadWriteLock _jsonParametersLock; + + enum OctreeQueryFlags : uint16_t { NoFlags = 0x0, WantInitialResult = 0x1 }; + friend const OctreeQuery::OctreeQueryFlags operator|=(OctreeQuery::OctreeQueryFlags& lhs, const int rhs); bool _hasReceivedFirstQuery { false }; + bool _reportInitialResult { false }; }; #endif // hifi_OctreeQuery_h diff --git a/libraries/shared/src/shared/ConicalViewFrustum.cpp b/libraries/shared/src/shared/ConicalViewFrustum.cpp index 2ef096e3a8..3d56683c82 100644 --- a/libraries/shared/src/shared/ConicalViewFrustum.cpp +++ b/libraries/shared/src/shared/ConicalViewFrustum.cpp @@ -144,3 +144,8 @@ int ConicalViewFrustum::deserialize(const unsigned char* sourceBuffer) { return sourceBuffer - startPosition; } + +void ConicalViewFrustum::setSimpleRadius(float radius) { + _radius = radius; + _farClip = radius / 2.0f; +} diff --git a/libraries/shared/src/shared/ConicalViewFrustum.h b/libraries/shared/src/shared/ConicalViewFrustum.h index dc372d560e..2180516441 100644 --- a/libraries/shared/src/shared/ConicalViewFrustum.h +++ b/libraries/shared/src/shared/ConicalViewFrustum.h @@ -54,6 +54,9 @@ public: int serialize(unsigned char* destinationBuffer) const; int deserialize(const unsigned char* sourceBuffer); + // Just test for within radius. + void setSimpleRadius(float radius); + private: glm::vec3 _position { 0.0f, 0.0f, 0.0f }; glm::vec3 _direction { 0.0f, 0.0f, 1.0f }; diff --git a/tools/dissectors/1-hfudt.lua b/tools/dissectors/1-hfudt.lua index 137bee659b..9bed892885 100644 --- a/tools/dissectors/1-hfudt.lua +++ b/tools/dissectors/1-hfudt.lua @@ -86,12 +86,12 @@ local packet_types = { [22] = "ICEServerPeerInformation", [23] = "ICEServerQuery", [24] = "OctreeStats", - [25] = "Jurisdiction", + [25] = "UNUSED_PACKET_TYPE_1", [26] = "AvatarIdentityRequest", [27] = "AssignmentClientStatus", [28] = "NoisyMute", [29] = "AvatarIdentity", - [30] = "AvatarBillboard", + [30] = "NodeIgnoreRequest", [31] = "DomainConnectRequest", [32] = "DomainServerRequireDTLS", [33] = "NodeJsonStats", @@ -115,7 +115,52 @@ local packet_types = { [51] = "AssetUpload", [52] = "AssetUploadReply", [53] = "AssetGetInfo", - [54] = "AssetGetInfoReply" + [54] = "AssetGetInfoReply", + [55] = "DomainDisconnectRequest", + [56] = "DomainServerRemovedNode", + [57] = "MessagesData", + [58] = "MessagesSubscribe", + [59] = "MessagesUnsubscribe", + [60] = "ICEServerHeartbeatDenied", + [61] = "AssetMappingOperation", + [62] = "AssetMappingOperationReply", + [63] = "ICEServerHeartbeatACK", + [64] = "NegotiateAudioFormat", + [65] = "SelectedAudioFormat", + [66] = "MoreEntityShapes", + [67] = "NodeKickRequest", + [68] = "NodeMuteRequest", + [69] = "RadiusIgnoreRequest", + [70] = "UsernameFromIDRequest", + [71] = "UsernameFromIDReply", + [72] = "AvatarQuery", + [73] = "RequestsDomainListData", + [74] = "PerAvatarGainSet", + [75] = "EntityScriptGetStatus", + [76] = "EntityScriptGetStatusReply", + [77] = "ReloadEntityServerScript", + [78] = "EntityPhysics", + [79] = "EntityServerScriptLog", + [80] = "AdjustAvatarSorting", + [81] = "OctreeFileReplacement", + [82] = "CollisionEventChanges", + [83] = "ReplicatedMicrophoneAudioNoEcho", + [84] = "ReplicatedMicrophoneAudioWithEcho", + [85] = "ReplicatedInjectAudio", + [86] = "ReplicatedSilentAudioFrame", + [87] = "ReplicatedAvatarIdentity", + [88] = "ReplicatedKillAvatar", + [89] = "ReplicatedBulkAvatarData", + [90] = "DomainContentReplacementFromUrl", + [91] = "ChallengeOwnership", + [92] = "EntityScriptCallMethod", + [93] = "ChallengeOwnershipRequest", + [94] = "ChallengeOwnershipReply", + [95] = "EntityQueryInitialResultsComplete", + [96] = "OctreeDataFileRequest", + [97] = "OctreeDataFileReply", + [98] = "OctreeDataPersist", + [99] = "EntityClone" } local unsourced_packet_types = { From 81c8fe611445434203ce33612084ac34fa0d2e92 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 24 Jul 2018 17:06:09 -0700 Subject: [PATCH 17/57] Change 'Enable Authentication' to 'Enable Packet Verification' --- domain-server/resources/describe-settings.json | 4 ++-- domain-server/src/DomainServer.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 4e68d29e85..db25e247d3 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -48,8 +48,8 @@ "advanced": true }, { - "name": "enable_authentication", - "label": "Enable Authentication", + "name": "enable_packet_verification", + "label": "Enable Packet Verification", "help": "Enable secure checksums on communication that uses the High Fidelity protocol. Increases security with possibly a small performance penalty.", "default": true, "type": "checkbox", diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 5324bb407d..b381da159d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -630,7 +630,7 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) { void DomainServer::setupNodeListAndAssignments() { const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port"; - static const QString ENABLE_PACKET_AUTHENTICATION = "metaverse.enable_authentication"; + static const QString ENABLE_PACKET_AUTHENTICATION = "metaverse.enable_packet_verification"; QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION); int domainServerPort = localPortValue.toInt(); From 0cc96f2be14163f7bee0650781e5ce4df1b977a5 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 17 Jul 2018 11:04:25 -0700 Subject: [PATCH 18/57] Raise posix descriptor limit if necessary --- .../src/AssignmentClientMonitor.cpp | 24 +++++++++++++++++++ .../src/AssignmentClientMonitor.h | 2 ++ 2 files changed, 26 insertions(+) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 2847d4ebf1..163e5229bd 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -25,6 +25,9 @@ #include "AssignmentClientChildData.h" #include "SharedUtil.h" #include +#ifdef POSIX_SOURCE +#include +#endif const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor"; const int WAIT_FOR_CHILD_MSECS = 1000; @@ -71,6 +74,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket"); + adjustOsResources(_numAssignmentClientForks); // use QProcess to fork off a process for each of the child assignment clients for (unsigned int i = 0; i < _numAssignmentClientForks; i++) { spawnChildClient(); @@ -372,3 +376,23 @@ bool AssignmentClientMonitor::handleHTTPRequest(HTTPConnection* connection, cons return true; } + +void AssignmentClientMonitor::adjustOsResources(unsigned int numForks) const +{ +#ifdef _POSIX_SOURCE + // QProcess on Unix uses six descriptors, some temporarily, for each child proc. + unsigned requiredDescriptors = 30 + 6 * numForks; + struct rlimit descLimits; + if (getrlimit(RLIMIT_NOFILE, &descLimits) == 0) { + if (descLimits.rlim_cur < requiredDescriptors) { + descLimits.rlim_cur = requiredDescriptors; + if (setrlimit(RLIMIT_NOFILE, &descLimits) == 0) { + qDebug() << "Resetting descriptor limit to" << requiredDescriptors; + } else { + const char *const errorString = strerror(errno); + qDebug() << "Failed to reset descriptor limit to" << requiredDescriptors << ":" << errorString; + } + } + } +#endif +} diff --git a/assignment-client/src/AssignmentClientMonitor.h b/assignment-client/src/AssignmentClientMonitor.h index 8848d503ae..331eed4599 100644 --- a/assignment-client/src/AssignmentClientMonitor.h +++ b/assignment-client/src/AssignmentClientMonitor.h @@ -23,6 +23,7 @@ #include "AssignmentClientChildData.h" #include #include +#include extern const char* NUM_FORKS_PARAMETER; @@ -55,6 +56,7 @@ public slots: private: void spawnChildClient(); void simultaneousWaitOnChildren(int waitMsecs); + void adjustOsResources(unsigned int numForks) const; QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children From 6098c53d32641b73dc4d2f50c6d908857c5afa36 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 17 Jul 2018 11:29:31 -0700 Subject: [PATCH 19/57] Remove #include from header --- assignment-client/src/AssignmentClientMonitor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/assignment-client/src/AssignmentClientMonitor.h b/assignment-client/src/AssignmentClientMonitor.h index 331eed4599..659ff4b001 100644 --- a/assignment-client/src/AssignmentClientMonitor.h +++ b/assignment-client/src/AssignmentClientMonitor.h @@ -23,7 +23,6 @@ #include "AssignmentClientChildData.h" #include #include -#include extern const char* NUM_FORKS_PARAMETER; From 7181c3ad487fd39f8760e5333e3d097798db33e5 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 17 Jul 2018 12:11:22 -0700 Subject: [PATCH 20/57] Fix typo in preprocessor symbol --- assignment-client/src/AssignmentClientMonitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 163e5229bd..9809c1b7d6 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -25,7 +25,7 @@ #include "AssignmentClientChildData.h" #include "SharedUtil.h" #include -#ifdef POSIX_SOURCE +#ifdef _POSIX_SOURCE #include #endif From 06d1602c0dd42495acd671fe0105c5cefc719c4c Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 17 Jul 2018 14:39:21 -0700 Subject: [PATCH 21/57] Use max of --max & --n for resources; other clean-up --- assignment-client/src/AssignmentClientMonitor.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 9809c1b7d6..eb764a128c 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -74,7 +74,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket"); - adjustOsResources(_numAssignmentClientForks); + adjustOsResources(std::max(_numAssignmentClientForks, _maxAssignmentClientForks)); // use QProcess to fork off a process for each of the child assignment clients for (unsigned int i = 0; i < _numAssignmentClientForks; i++) { spawnChildClient(); @@ -380,7 +380,8 @@ bool AssignmentClientMonitor::handleHTTPRequest(HTTPConnection* connection, cons void AssignmentClientMonitor::adjustOsResources(unsigned int numForks) const { #ifdef _POSIX_SOURCE - // QProcess on Unix uses six descriptors, some temporarily, for each child proc. + // QProcess on Unix uses six (I think) descriptors, some temporarily, for each child proc. + // Formula based on tests with a Ubuntu 16.04 VM. unsigned requiredDescriptors = 30 + 6 * numForks; struct rlimit descLimits; if (getrlimit(RLIMIT_NOFILE, &descLimits) == 0) { @@ -393,6 +394,9 @@ void AssignmentClientMonitor::adjustOsResources(unsigned int numForks) const qDebug() << "Failed to reset descriptor limit to" << requiredDescriptors << ":" << errorString; } } + } else { + const char *const errorString = strerror(errno); + qDebug() << "Failed to read descriptor limit:" << errorString; } #endif } From 187259c838cfc950192a86ab8488501294c0caf6 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 17 Jul 2018 11:04:25 -0700 Subject: [PATCH 22/57] Raise posix descriptor limit if necessary --- assignment-client/src/AssignmentClientMonitor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/AssignmentClientMonitor.h b/assignment-client/src/AssignmentClientMonitor.h index 659ff4b001..331eed4599 100644 --- a/assignment-client/src/AssignmentClientMonitor.h +++ b/assignment-client/src/AssignmentClientMonitor.h @@ -23,6 +23,7 @@ #include "AssignmentClientChildData.h" #include #include +#include extern const char* NUM_FORKS_PARAMETER; From 2d653db0640832bc0875fcf01f4fc4afc7e214ea Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 17 Jul 2018 11:29:31 -0700 Subject: [PATCH 23/57] Remove #include from header --- assignment-client/src/AssignmentClientMonitor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/assignment-client/src/AssignmentClientMonitor.h b/assignment-client/src/AssignmentClientMonitor.h index 331eed4599..659ff4b001 100644 --- a/assignment-client/src/AssignmentClientMonitor.h +++ b/assignment-client/src/AssignmentClientMonitor.h @@ -23,7 +23,6 @@ #include "AssignmentClientChildData.h" #include #include -#include extern const char* NUM_FORKS_PARAMETER; From 7cb917f7355bdfea0dfb3d4fd0cb8441a3c1a2eb Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 25 Jul 2018 14:45:26 -0700 Subject: [PATCH 24/57] Angles aren't boolean! --- libraries/shared/src/shared/ConicalViewFrustum.cpp | 6 +++--- libraries/shared/src/shared/ConicalViewFrustum.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/shared/src/shared/ConicalViewFrustum.cpp b/libraries/shared/src/shared/ConicalViewFrustum.cpp index 3d56683c82..78f4f7d1bc 100644 --- a/libraries/shared/src/shared/ConicalViewFrustum.cpp +++ b/libraries/shared/src/shared/ConicalViewFrustum.cpp @@ -69,7 +69,7 @@ bool ConicalViewFrustum::intersects(const AABox& box) const { return intersects(position, distance, radius); } -bool ConicalViewFrustum::getAngularSize(const AACube& cube) const { +float ConicalViewFrustum::getAngularSize(const AACube& cube) const { auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere auto position = cube.calcCenter() - _position; // position of bounding sphere in view-frame float distance = glm::length(position); @@ -77,7 +77,7 @@ bool ConicalViewFrustum::getAngularSize(const AACube& cube) const { return getAngularSize(distance, radius); } -bool ConicalViewFrustum::getAngularSize(const AABox& box) const { +float ConicalViewFrustum::getAngularSize(const AABox& box) const { auto radius = 0.5f * glm::length(box.getScale()); // radius of bounding sphere auto position = box.calcCenter() - _position; // position of bounding sphere in view-frame float distance = glm::length(position); @@ -107,7 +107,7 @@ bool ConicalViewFrustum::intersects(const glm::vec3& relativePosition, float dis sqrtf(distance * distance - radius * radius) * _cosAngle - radius * _sinAngle; } -bool ConicalViewFrustum::getAngularSize(float distance, float radius) const { +float ConicalViewFrustum::getAngularSize(float distance, float radius) const { const float AVOID_DIVIDE_BY_ZERO = 0.001f; float angularSize = radius / (distance + AVOID_DIVIDE_BY_ZERO); return angularSize; diff --git a/libraries/shared/src/shared/ConicalViewFrustum.h b/libraries/shared/src/shared/ConicalViewFrustum.h index 2180516441..6a2cc53f03 100644 --- a/libraries/shared/src/shared/ConicalViewFrustum.h +++ b/libraries/shared/src/shared/ConicalViewFrustum.h @@ -45,11 +45,11 @@ public: bool intersects(const AACube& cube) const; bool intersects(const AABox& box) const; - bool getAngularSize(const AACube& cube) const; - bool getAngularSize(const AABox& box) const; + float getAngularSize(const AACube& cube) const; + float getAngularSize(const AABox& box) const; bool intersects(const glm::vec3& relativePosition, float distance, float radius) const; - bool getAngularSize(float distance, float radius) const; + float getAngularSize(float distance, float radius) const; int serialize(unsigned char* destinationBuffer) const; int deserialize(const unsigned char* sourceBuffer); From 8f0283444719a88fe853f327e35cc3e74cb30dfa Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 25 Jul 2018 14:46:47 -0700 Subject: [PATCH 25/57] Set params.stopReason in another place --- assignment-client/src/entities/EntityTreeSendThread.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index af0c09aec7..5ca4c3588e 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -310,6 +310,7 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) { if (_sendQueue.empty()) { + params.stopReason = EncodeBitstreamParams::FINISHED; OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME); return false; } From 2c06487df086d9bab9682152939ecbb7324f6bd3 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 25 Jul 2018 17:05:05 -0700 Subject: [PATCH 26/57] Move sending of completion packet up to EntityTreeSendThread I hadn't appreciated the continuous nature of the scene traversal. Looks good now. Also changed some identifier names. --- .../src/entities/EntityTreeSendThread.cpp | 21 +++++++++++++++---- .../src/entities/EntityTreeSendThread.h | 2 +- .../src/octree/OctreeSendThread.cpp | 15 +++---------- .../src/octree/OctreeSendThread.h | 2 +- libraries/octree/src/OctreeQuery.cpp | 4 ++-- libraries/octree/src/OctreeQuery.h | 8 +++---- 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 5ca4c3588e..19dc92686a 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -18,7 +18,7 @@ #include "EntityServer.h" // Initially just send all items within this distance. -const float EntityTreeSendThread::INITIAL_RADIUS = 50.0f; +const float EntityTreeSendThread::INITIAL_RADIUS = 10.0f; EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) : OctreeSendThread(myServer, node) @@ -102,7 +102,7 @@ void EntityTreeSendThread::preDistributionProcessing() { } } -void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, +bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) { if (viewFrustumChanged || _traversal.finished()) { EntityTreeElementPointer root = std::dynamic_pointer_cast(_myServer->getOctree()->getRoot()); @@ -114,7 +114,7 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); newView.lodScaleFactor = powf(2.0f, lodLevelOffset); - if (nodeData->wantReportInitialResult() && !newView.viewFrustums.empty()) { + if (nodeData->wantReportInitialCompletion() && !newView.viewFrustums.empty()) { auto& mainView = newView.viewFrustums[0]; // Force acceptance within INITIAL_RADIUS. mainView.setSimpleRadius(INITIAL_RADIUS); @@ -165,7 +165,20 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O OctreeServer::trackTreeTraverseTime((float)(usecTimestampNow() - startTime)); } - OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene); + bool sendComplete = OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene); + + if (sendComplete && nodeData->wantReportInitialCompletion() && _traversal.finished()) { + // Dealt with all nearby entities. + nodeData->setReportInitialCompletion(false); + + // Send EntityQueryInitialResultsComplete reliable packet ... + auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, -1, true); + QDataStream initialCompletionStream(initialCompletion.get()); + initialCompletionStream << _lastSequenceNumber; + DependencyManager::get()->sendPacket(std::move(initialCompletion), *node.data()); + } + + return sendComplete; } bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index 8c66f47aea..c9f4d06164 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -31,7 +31,7 @@ public: EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node); protected: - void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, + bool traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) override; private slots: diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 98b06d6344..54a7bbfa2d 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -433,7 +433,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* return _truePacketsSent; } -void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) { +bool 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)); int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); @@ -511,20 +511,11 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre OctreeServer::trackInsideTime((float)(usecTimestampNow() - startInside)); } - if (params.stopReason == EncodeBitstreamParams::FINISHED && nodeData->wantReportInitialResult()) { - // Dealt with nearby entities. - nodeData->setReportInitialResult(false); - - // send EntityQueryInitialResultsComplete reliable packet ... - auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, -1, true); - QDataStream initialCompletionStream(initialCompletion.get()); - initialCompletionStream << _lastSequenceNumber; - DependencyManager::get()->sendPacket(std::move(initialCompletion), *node.data()); - } - if (somethingToSend && _myServer->wantsVerboseDebug()) { qCDebug(octree) << "Hit PPS Limit, packetsSentThisInterval =" << _packetsSentThisInterval << " maxPacketsPerInterval = " << maxPacketsPerInterval << " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval; } + + return params.stopReason == EncodeBitstreamParams::FINISHED; } diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 58d2e3a160..fc529c3e3a 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -52,7 +52,7 @@ protected: /// Implements generic processing behavior for this thread. virtual bool process() override; - virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, + virtual bool traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene); virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) = 0; diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index 5fb886df10..77977bf01e 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -82,7 +82,7 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { } OctreeQueryFlags queryFlags { NoFlags }; - queryFlags |= (_reportInitialResult ? OctreeQuery::WantInitialResult : 0); + queryFlags |= (_reportInitialCompletion ? OctreeQuery::WantInitialCompletion : 0); memcpy(destinationBuffer, &queryFlags, sizeof(queryFlags)); destinationBuffer += sizeof(queryFlags); @@ -164,7 +164,7 @@ int OctreeQuery::parseData(ReceivedMessage& message) { memcpy(&queryFlags, sourceBuffer, sizeof(queryFlags)); sourceBuffer += sizeof(queryFlags); - _reportInitialResult = bool(queryFlags & OctreeQueryFlags::WantInitialResult); + _reportInitialCompletion = bool(queryFlags & OctreeQueryFlags::WantInitialCompletion); return sourceBuffer - startPosition; } diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index 711a22e9b5..04d6793158 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -53,8 +53,8 @@ public: bool hasReceivedFirstQuery() const { return _hasReceivedFirstQuery; } // Want a report when the initial query is complete. - bool wantReportInitialResult() const { return _reportInitialResult; } - void setReportInitialResult(bool reportInitialResult) { _reportInitialResult = reportInitialResult; } + bool wantReportInitialCompletion() const { return _reportInitialCompletion; } + void setReportInitialCompletion(bool reportInitialCompletion) { _reportInitialCompletion = reportInitialCompletion; } signals: void incomingConnectionIDChanged(); @@ -78,11 +78,11 @@ protected: QJsonObject _jsonParameters; QReadWriteLock _jsonParametersLock; - enum OctreeQueryFlags : uint16_t { NoFlags = 0x0, WantInitialResult = 0x1 }; + enum OctreeQueryFlags : uint16_t { NoFlags = 0x0, WantInitialCompletion = 0x1 }; friend const OctreeQuery::OctreeQueryFlags operator|=(OctreeQuery::OctreeQueryFlags& lhs, const int rhs); bool _hasReceivedFirstQuery { false }; - bool _reportInitialResult { false }; + bool _reportInitialCompletion { false }; }; #endif // hifi_OctreeQuery_h From a15e78a0f4420b5e51a73b487ba3f126a87f96ee Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 25 Jul 2018 17:36:53 -0700 Subject: [PATCH 27/57] Missed an identifier change --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 55e634275d..94f89a66ad 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6134,7 +6134,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { auto lodManager = DependencyManager::get(); _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); - _octreeQuery.setReportInitialResult(true); + _octreeQuery.setReportInitialCompletion(true); auto nodeList = DependencyManager::get(); From 0cec9a72d629b3888273e8ef7c169f0e5e7314c2 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 26 Jul 2018 18:10:38 -0700 Subject: [PATCH 28/57] Implement client-side enable physics once indicated EntityData has arrived Also now use Entity Server Protocol sequence numbers. --- .../src/entities/EntityTreeSendThread.cpp | 2 +- .../src/octree/OctreeSendThread.cpp | 3 -- .../src/octree/OctreeSendThread.h | 1 - interface/src/Application.cpp | 8 +++-- .../src/octree/OctreePacketProcessor.cpp | 34 +++++++++++++++++-- interface/src/octree/OctreePacketProcessor.h | 10 ++++++ libraries/octree/src/OctreeProcessor.cpp | 2 ++ libraries/octree/src/OctreeProcessor.h | 3 ++ 8 files changed, 52 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 19dc92686a..154d22f253 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -174,7 +174,7 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O // Send EntityQueryInitialResultsComplete reliable packet ... auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, -1, true); QDataStream initialCompletionStream(initialCompletion.get()); - initialCompletionStream << _lastSequenceNumber; + initialCompletionStream << OCTREE_PACKET_SEQUENCE(nodeData->getSequenceNumber() - 1U); DependencyManager::get()->sendPacket(std::move(initialCompletion), *node.data()); } diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 54a7bbfa2d..ab357f4146 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -195,7 +195,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* // actually send it OctreeServer::didCallWriteDatagram(this); DependencyManager::get()->sendUnreliablePacket(statsPacket, *node); - _lastSequenceNumber = (decltype(_lastSequenceNumber)) statsPacket.getSequenceNumber(); } else { // not enough room in the packet, send two packets @@ -232,7 +231,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* // second packet OctreeServer::didCallWriteDatagram(this); DependencyManager::get()->sendUnreliablePacket(sentPacket, *node); - _lastSequenceNumber = (decltype(_lastSequenceNumber)) sentPacket.getSequenceNumber(); numBytes = sentPacket.getDataSize(); _totalBytes += numBytes; @@ -266,7 +264,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* OctreeServer::didCallWriteDatagram(this); NLPacket& sentPacket = nodeData->getPacket(); DependencyManager::get()->sendUnreliablePacket(sentPacket, *node); - _lastSequenceNumber = (decltype(_lastSequenceNumber)) sentPacket.getSequenceNumber(); int numBytes = sentPacket.getDataSize(); _totalBytes += numBytes; diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index fc529c3e3a..bdf0f03364 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -60,7 +60,6 @@ protected: QWeakPointer _node; OctreeServer* _myServer { nullptr }; QUuid _nodeUuid; - udt::SequenceNumber::Type _lastSequenceNumber { 0 }; private: /// Called before a packetDistributor pass to allow for pre-distribution processing diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 94f89a66ad..ed67b7464c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5476,8 +5476,9 @@ void Application::update(float deltaTime) { // for nearby entities before starting bullet up. quint64 now = usecTimestampNow(); const int PHYSICS_CHECK_TIMEOUT = 2 * USECS_PER_SECOND; - - if (now - _lastPhysicsCheckTime > PHYSICS_CHECK_TIMEOUT || _fullSceneReceivedCounter > _fullSceneCounterAtLastPhysicsCheck) { + auto entityTreeRenderer = getEntities(); + if (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) ) { + /*if (now - _lastPhysicsCheckTime > PHYSICS_CHECK_TIMEOUT || _fullSceneReceivedCounter > _fullSceneCounterAtLastPhysicsCheck) {*/ // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; _fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter; @@ -6134,7 +6135,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { auto lodManager = DependencyManager::get(); _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); - _octreeQuery.setReportInitialCompletion(true); + _octreeQuery.setReportInitialCompletion(!_physicsEnabled); auto nodeList = DependencyManager::get(); @@ -6279,6 +6280,7 @@ void Application::clearDomainOctreeDetails() { _octreeServerSceneStats.clear(); }); + _octreeProcessor.resetCompletionSequenceNumber(); // reset the model renderer getEntities()->clear(); diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 7d38e29710..5ab2218f67 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -21,9 +21,9 @@ OctreePacketProcessor::OctreePacketProcessor() { setObjectName("Octree Packet Processor"); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - - packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, - this, "handleOctreePacket"); + const PacketReceiver::PacketTypeList octreePackets = + { PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase, PacketType::EntityQueryInitialResultsComplete }; + packetReceiver.registerDirectListenerForTypes(octreePackets, this, "handleOctreePacket"); } void OctreePacketProcessor::handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode) { @@ -111,8 +111,36 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag } } break; + case PacketType::EntityQueryInitialResultsComplete: { + // Read sequence # + OCTREE_PACKET_SEQUENCE completionNumber; + message->readPrimitive(&completionNumber); + _completionSequenceNumber = completionNumber; + _completionSequenceNumberValid = true; + } break; + default: { // nothing to do } break; } } + +void OctreePacketProcessor::resetCompletionSequenceNumber() { + _completionSequenceNumber = false; +} + +namespace { + template constexpr bool lessThanWraparound(int a, int b) { + static const int MAX_T_VALUE = std::numeric_limits::max(); + if (b < a) { + b += MAX_T_VALUE; + } + return (b - a) < (MAX_T_VALUE / 2); + } +} + +bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const { + const int completionSequenceNumber = _completionSequenceNumber; + return _completionSequenceNumberValid && + !lessThanWraparound(completionSequenceNumber, sequenceNumber); +} diff --git a/interface/src/octree/OctreePacketProcessor.h b/interface/src/octree/OctreePacketProcessor.h index d04cab3584..f7e001fbde 100644 --- a/interface/src/octree/OctreePacketProcessor.h +++ b/interface/src/octree/OctreePacketProcessor.h @@ -22,13 +22,23 @@ class OctreePacketProcessor : public ReceivedPacketProcessor { public: OctreePacketProcessor(); + bool octreeSequenceIsComplete(int sequenceNumber) const; + signals: void packetVersionMismatch(); +public slots: + void resetCompletionSequenceNumber(); + protected: virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode) override; private slots: void handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode); + +private: + bool _completionSequenceNumberValid { false }; + std::atomic _completionSequenceNumber { 0 }; + }; #endif // hifi_OctreePacketProcessor_h diff --git a/libraries/octree/src/OctreeProcessor.cpp b/libraries/octree/src/OctreeProcessor.cpp index beaac1198c..206ff399d9 100644 --- a/libraries/octree/src/OctreeProcessor.cpp +++ b/libraries/octree/src/OctreeProcessor.cpp @@ -192,6 +192,8 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe _elementsInLastWindow = 0; _entitiesInLastWindow = 0; } + + _lastOctreeMessageSequence = sequence; } } diff --git a/libraries/octree/src/OctreeProcessor.h b/libraries/octree/src/OctreeProcessor.h index 325b33cd15..8085682fa5 100644 --- a/libraries/octree/src/OctreeProcessor.h +++ b/libraries/octree/src/OctreeProcessor.h @@ -56,6 +56,8 @@ public: float getAverageUncompressPerPacket() const { return _uncompressPerPacket.getAverage(); } float getAverageReadBitstreamPerPacket() const { return _readBitstreamPerPacket.getAverage(); } + OCTREE_PACKET_SEQUENCE getLastOctreeMessageSequence() const { return _lastOctreeMessageSequence; } + protected: virtual OctreePointer createTree() = 0; @@ -77,6 +79,7 @@ protected: int _packetsInLastWindow = 0; int _elementsInLastWindow = 0; int _entitiesInLastWindow = 0; + std::atomic _lastOctreeMessageSequence = 0; }; From 4d559bbaeeb59063515fc7c4c8ab60528d1acbfc Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 27 Jul 2018 10:05:34 -0700 Subject: [PATCH 29/57] Don't initialize an atomic; squelch gcc warning --- libraries/octree/src/OctreeProcessor.h | 2 +- libraries/octree/src/OctreeQuery.cpp | 2 +- libraries/octree/src/OctreeQuery.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/octree/src/OctreeProcessor.h b/libraries/octree/src/OctreeProcessor.h index 8085682fa5..1bc3bd10f9 100644 --- a/libraries/octree/src/OctreeProcessor.h +++ b/libraries/octree/src/OctreeProcessor.h @@ -79,7 +79,7 @@ protected: int _packetsInLastWindow = 0; int _elementsInLastWindow = 0; int _entitiesInLastWindow = 0; - std::atomic _lastOctreeMessageSequence = 0; + std::atomic _lastOctreeMessageSequence; }; diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index 77977bf01e..8c3685dc69 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -27,7 +27,7 @@ OctreeQuery::OctreeQuery(bool randomizeConnectionID) { } } -const OctreeQuery::OctreeQueryFlags operator|=(OctreeQuery::OctreeQueryFlags& lhs, int rhs) { +OctreeQuery::OctreeQueryFlags operator|=(OctreeQuery::OctreeQueryFlags& lhs, int rhs) { return lhs = OctreeQuery::OctreeQueryFlags(lhs | rhs); } diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index 04d6793158..2c3c00ef05 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -79,7 +79,7 @@ protected: QReadWriteLock _jsonParametersLock; enum OctreeQueryFlags : uint16_t { NoFlags = 0x0, WantInitialCompletion = 0x1 }; - friend const OctreeQuery::OctreeQueryFlags operator|=(OctreeQuery::OctreeQueryFlags& lhs, const int rhs); + friend OctreeQuery::OctreeQueryFlags operator|=(OctreeQuery::OctreeQueryFlags& lhs, const int rhs); bool _hasReceivedFirstQuery { false }; bool _reportInitialCompletion { false }; From 3d390203e20fa88f73d4a2a35132177e0fae6b48 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 27 Jul 2018 11:34:08 -0700 Subject: [PATCH 30/57] More fixes for clang & gcc C++14 issues, I think. --- interface/src/Application.cpp | 2 +- interface/src/octree/OctreePacketProcessor.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ed67b7464c..1c4de2165a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5475,7 +5475,7 @@ void Application::update(float deltaTime) { // we haven't yet enabled physics. we wait until we think we have all the collision information // for nearby entities before starting bullet up. quint64 now = usecTimestampNow(); - const int PHYSICS_CHECK_TIMEOUT = 2 * USECS_PER_SECOND; + //const int PHYSICS_CHECK_TIMEOUT = 2 * USECS_PER_SECOND; auto entityTreeRenderer = getEntities(); if (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) ) { /*if (now - _lastPhysicsCheckTime > PHYSICS_CHECK_TIMEOUT || _fullSceneReceivedCounter > _fullSceneCounterAtLastPhysicsCheck) {*/ diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 5ab2218f67..696fa4ffd4 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -130,8 +130,8 @@ void OctreePacketProcessor::resetCompletionSequenceNumber() { } namespace { - template constexpr bool lessThanWraparound(int a, int b) { - static const int MAX_T_VALUE = std::numeric_limits::max(); + template bool lessThanWraparound(int a, int b) { + constexpr int MAX_T_VALUE = std::numeric_limits::max(); if (b < a) { b += MAX_T_VALUE; } From 5315b020ec2ec2087c7484b882a77a3610c9ff55 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 27 Jul 2018 14:51:24 -0700 Subject: [PATCH 31/57] Use locking for the OctreePacketProcessor additions --- interface/src/octree/OctreePacketProcessor.cpp | 8 ++++++-- interface/src/octree/OctreePacketProcessor.h | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 696fa4ffd4..62dcd056e9 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -115,6 +115,8 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag // Read sequence # OCTREE_PACKET_SEQUENCE completionNumber; message->readPrimitive(&completionNumber); + + Locker lock(_completionMutex); _completionSequenceNumber = completionNumber; _completionSequenceNumberValid = true; } break; @@ -126,6 +128,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag } void OctreePacketProcessor::resetCompletionSequenceNumber() { + Locker lock(_completionMutex); _completionSequenceNumber = false; } @@ -140,7 +143,8 @@ namespace { } bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const { - const int completionSequenceNumber = _completionSequenceNumber; + Locker lock(_completionMutex); + // If we've received the flagged seq # and the current one is >= it. return _completionSequenceNumberValid && - !lessThanWraparound(completionSequenceNumber, sequenceNumber); + !lessThanWraparound(_completionSequenceNumber, sequenceNumber); } diff --git a/interface/src/octree/OctreePacketProcessor.h b/interface/src/octree/OctreePacketProcessor.h index f7e001fbde..ad1ab6c36c 100644 --- a/interface/src/octree/OctreePacketProcessor.h +++ b/interface/src/octree/OctreePacketProcessor.h @@ -37,8 +37,10 @@ private slots: void handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode); private: + mutable std::mutex _completionMutex; + using Locker = std::lock_guard; bool _completionSequenceNumberValid { false }; - std::atomic _completionSequenceNumber { 0 }; + int _completionSequenceNumber { 0 }; }; #endif // hifi_OctreePacketProcessor_h From 2ac6ca3291015d0b5ebdcc676ada0bf9413f31bc Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 27 Jul 2018 17:32:35 -0700 Subject: [PATCH 32/57] Remove old physics-enabling code --- interface/src/Application.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1c4de2165a..6c5efee807 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5475,10 +5475,9 @@ void Application::update(float deltaTime) { // we haven't yet enabled physics. we wait until we think we have all the collision information // for nearby entities before starting bullet up. quint64 now = usecTimestampNow(); - //const int PHYSICS_CHECK_TIMEOUT = 2 * USECS_PER_SECOND; + // Check for flagged EntityData having arrived. auto entityTreeRenderer = getEntities(); if (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) ) { - /*if (now - _lastPhysicsCheckTime > PHYSICS_CHECK_TIMEOUT || _fullSceneReceivedCounter > _fullSceneCounterAtLastPhysicsCheck) {*/ // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; _fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter; From 3d1fe7da9f09e86118e844c6c33d0b38c5e4506e Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 27 Jul 2018 18:22:36 -0700 Subject: [PATCH 33/57] Reset known entity-state when leaving initial send state --- assignment-client/src/entities/EntityTreeSendThread.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 154d22f253..6079f03c26 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -170,6 +170,7 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O if (sendComplete && nodeData->wantReportInitialCompletion() && _traversal.finished()) { // Dealt with all nearby entities. nodeData->setReportInitialCompletion(false); + resetState(); // Send EntityQueryInitialResultsComplete reliable packet ... auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, -1, true); From be1bbc07c26de990ea3040a0a77269c5f7afec4b Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 30 Jul 2018 10:20:55 -0700 Subject: [PATCH 34/57] Don't wait for (nonexistant) ES when in serverless mode --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6c5efee807..0037c9500f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5477,7 +5477,8 @@ void Application::update(float deltaTime) { quint64 now = usecTimestampNow(); // Check for flagged EntityData having arrived. auto entityTreeRenderer = getEntities(); - if (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) ) { + if (isServerlessMode() || + (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) )) { // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; _fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter; From 8c512ec19bd8c13443faa388f48e0ec5e9781d23 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 30 Jul 2018 18:03:45 -0700 Subject: [PATCH 35/57] Send spherical view from Interface rather than creating in ES --- .../src/entities/EntityTreeSendThread.cpp | 12 +--------- .../src/entities/EntityTreeSendThread.h | 2 -- interface/src/Application.cpp | 22 ++++++++++++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 6079f03c26..2e777ae0f1 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -17,9 +17,6 @@ #include "EntityServer.h" -// Initially just send all items within this distance. -const float EntityTreeSendThread::INITIAL_RADIUS = 10.0f; - EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) : OctreeSendThread(myServer, node) { @@ -113,14 +110,7 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); newView.lodScaleFactor = powf(2.0f, lodLevelOffset); - - if (nodeData->wantReportInitialCompletion() && !newView.viewFrustums.empty()) { - auto& mainView = newView.viewFrustums[0]; - // Force acceptance within INITIAL_RADIUS. - mainView.setSimpleRadius(INITIAL_RADIUS); - newView.lodScaleFactor = 0.0f; - } - + startNewTraversal(newView, root); // When the viewFrustum changed the sort order may be incorrect, so we re-sort diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index c9f4d06164..199769ca09 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -58,8 +58,6 @@ private: int32_t _numEntitiesOffset { 0 }; uint16_t _numEntities { 0 }; - const static float INITIAL_RADIUS; - private slots: void editingEntityPointer(const EntityItemPointer& entity); void deletingEntityPointer(EntityItem* entity); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0037c9500f..bbb8dc8161 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -374,6 +374,7 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI static const uint32_t INVALID_FRAME = UINT32_MAX; static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation +static const float INITIAL_QUERY_RADIUS = 10.0f; // priority radius for entities before physics enabled static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); @@ -6130,12 +6131,23 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { return; // bail early if settings are not loaded } - _octreeQuery.setConicalViews(_conicalViews); + const bool isModifiedQuery = !_physicsEnabled; + if (isModifiedQuery) { + // Create modified view that is a simple sphere. + ConicalViewFrustum sphericalView; + sphericalView.setSimpleRadius(INITIAL_QUERY_RADIUS); + _octreeQuery.setConicalViews({ sphericalView }); + _octreeQuery.setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE); + static constexpr float MIN_LOD_ADJUST = -20.0f; + _octreeQuery.setBoundaryLevelAdjust(MIN_LOD_ADJUST); + } else { + _octreeQuery.setConicalViews(_conicalViews); + auto lodManager = DependencyManager::get(); + _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); + _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); + } + _octreeQuery.setReportInitialCompletion(isModifiedQuery); - auto lodManager = DependencyManager::get(); - _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); - _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); - _octreeQuery.setReportInitialCompletion(!_physicsEnabled); auto nodeList = DependencyManager::get(); From a320308eaf7a8b1a9f94a962c2200e7a2e65c859 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 31 Jul 2018 13:54:22 -0700 Subject: [PATCH 36/57] Fix sequence number encoding issue Don't mix QDataStream and readPrimitive. Also fix logic issues in completion check; move new packet type; don't reset known sent entities upon initial completion; other reviewer recommendations. --- .../src/entities/EntityTreeSendThread.cpp | 7 +++---- interface/src/Application.cpp | 1 + interface/src/octree/OctreePacketProcessor.cpp | 4 ++-- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 2 +- tools/dissectors/1-hfudt.lua | 10 +++++----- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 2e777ae0f1..7e4ccc88cd 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -160,12 +160,11 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O if (sendComplete && nodeData->wantReportInitialCompletion() && _traversal.finished()) { // Dealt with all nearby entities. nodeData->setReportInitialCompletion(false); - resetState(); // Send EntityQueryInitialResultsComplete reliable packet ... - auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, -1, true); - QDataStream initialCompletionStream(initialCompletion.get()); - initialCompletionStream << OCTREE_PACKET_SEQUENCE(nodeData->getSequenceNumber() - 1U); + auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, + sizeof(OCTREE_PACKET_SEQUENCE), true); + initialCompletion->writePrimitive(OCTREE_PACKET_SEQUENCE(nodeData->getSequenceNumber() - 1U)); DependencyManager::get()->sendPacket(std::move(initialCompletion), *node.data()); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bbb8dc8161..3969e7617e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5483,6 +5483,7 @@ void Application::update(float deltaTime) { // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; _fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter; + _lastQueriedViews.clear(); // Force new view. // process octree stats packets are sent in between full sends of a scene (this isn't currently true). // We keep physics disabled until we've received a full scene and everything near the avatar in that diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 62dcd056e9..76f62bcc4e 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -135,7 +135,7 @@ void OctreePacketProcessor::resetCompletionSequenceNumber() { namespace { template bool lessThanWraparound(int a, int b) { constexpr int MAX_T_VALUE = std::numeric_limits::max(); - if (b < a) { + if (b <= a) { b += MAX_T_VALUE; } return (b - a) < (MAX_T_VALUE / 2); @@ -146,5 +146,5 @@ bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const { Locker lock(_completionMutex); // If we've received the flagged seq # and the current one is >= it. return _completionSequenceNumberValid && - !lessThanWraparound(_completionSequenceNumber, sequenceNumber); + !lessThanWraparound(sequenceNumber, _completionSequenceNumber); } diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index bb9666ee37..13ffcb5120 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -95,7 +95,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarIdentityRequest: return 22; default: - return 22; + return 21; } } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 7cba3baaf0..073f3f7c42 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -126,13 +126,13 @@ public: EntityScriptCallMethod, ChallengeOwnershipRequest, ChallengeOwnershipReply, - EntityQueryInitialResultsComplete, OctreeDataFileRequest, OctreeDataFileReply, OctreeDataPersist, EntityClone, + EntityQueryInitialResultsComplete, NUM_PACKET_TYPE }; diff --git a/tools/dissectors/1-hfudt.lua b/tools/dissectors/1-hfudt.lua index 9bed892885..26494bb515 100644 --- a/tools/dissectors/1-hfudt.lua +++ b/tools/dissectors/1-hfudt.lua @@ -156,11 +156,11 @@ local packet_types = { [92] = "EntityScriptCallMethod", [93] = "ChallengeOwnershipRequest", [94] = "ChallengeOwnershipReply", - [95] = "EntityQueryInitialResultsComplete", - [96] = "OctreeDataFileRequest", - [97] = "OctreeDataFileReply", - [98] = "OctreeDataPersist", - [99] = "EntityClone" + [95] = "OctreeDataFileRequest", + [96] = "OctreeDataFileReply", + [97] = "OctreeDataPersist", + [98] = "EntityClone", + [99] = "EntityQueryInitialResultsComplete" } local unsourced_packet_types = { From 64fc3e1091c3ee4a7777c7641341117e54f3d624 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 31 Jul 2018 15:30:33 -0700 Subject: [PATCH 37/57] ThreadedAssignment calls virtual stop() upon domain disconnect --- assignment-client/src/Agent.cpp | 16 ++++++---------- assignment-client/src/Agent.h | 6 ++++++ assignment-client/src/audio/AudioMixer.cpp | 1 - assignment-client/src/entities/EntityServer.cpp | 1 - .../src/scripts/EntityScriptServer.cpp | 2 -- libraries/networking/src/ThreadedAssignment.cpp | 6 ++++-- libraries/networking/src/ThreadedAssignment.h | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 0be691662d..cd4f59332a 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -336,7 +336,6 @@ void Agent::scriptRequestFinished() { } setFinished(true); - ThreadedAssignment::aboutToFinish(); } request->deleteLater(); @@ -499,7 +498,6 @@ void Agent::executeScript() { DependencyManager::destroy(); setFinished(true); - ThreadedAssignment::aboutToFinish(); } QUuid Agent::getSessionUUID() const { @@ -827,14 +825,6 @@ void Agent::processAgentAvatarAudio() { void Agent::aboutToFinish() { setIsAvatar(false);// will stop timers for sending identity packets - // If script engine not started yet then finish up, else will be done when - // script engine exits. - if (_scriptEngine) { - _scriptEngine->stop(); - } else { - ThreadedAssignment::aboutToFinish(); - } - // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(nullptr); @@ -864,3 +854,9 @@ void Agent::aboutToFinish() { _encoder = nullptr; } } + +void Agent::stop() { + if (_scriptEngine) { + _scriptEngine->stop(); + } +} diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 0fc3fbe1f9..a5883805bb 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -97,6 +97,12 @@ public slots: */ bool isAvatar() const { return _isAvatar; } + /**jsdoc + * @function Agent.stop + * @deprecated This function is being removed from the API. + */ + Q_INVOKABLE virtual void stop() override; + private slots: void requestScript(); void scriptRequestFinished(); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index ec9f26b53c..d56b22466e 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -109,7 +109,6 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : void AudioMixer::aboutToFinish() { DependencyManager::destroy(); - ThreadedAssignment::aboutToFinish(); } void AudioMixer::queueAudioPacket(QSharedPointer message, SharedNodePointer node) { diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 6303cafde8..8b86ba5eb2 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -72,7 +72,6 @@ void EntityServer::aboutToFinish() { DependencyManager::get()->cleanup(); OctreeServer::aboutToFinish(); - ThreadedAssignment::aboutToFinish(); } void EntityServer::handleEntityPacket(QSharedPointer message, SharedNodePointer senderNode) { diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index b40ffbcfb3..ebe25b11bf 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -587,6 +587,4 @@ void EntityScriptServer::aboutToFinish() { _codec->releaseEncoder(_encoder); _encoder = nullptr; } - - ThreadedAssignment::aboutToFinish(); } diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index d6c09d2a6c..13d4e0bf8b 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -66,6 +66,8 @@ void ThreadedAssignment::setFinished(bool isFinished) { // call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup aboutToFinish(); + + emit finished(); } } } @@ -118,7 +120,7 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() { if (_numQueuedCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { qCDebug(networking) << "At least" << MAX_SILENT_DOMAIN_SERVER_CHECK_INS << "have been queued without a response from domain-server" << "Stopping the current assignment"; - setFinished(true); + stop(); } else { auto nodeList = DependencyManager::get(); QMetaObject::invokeMethod(nodeList.data(), "sendDomainServerCheckIn"); @@ -130,5 +132,5 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() { void ThreadedAssignment::domainSettingsRequestFailed() { qCDebug(networking) << "Failed to retreive settings object from domain-server. Bailing on assignment."; - setFinished(true); + stop(); } diff --git a/libraries/networking/src/ThreadedAssignment.h b/libraries/networking/src/ThreadedAssignment.h index f245ec917f..9372cfa667 100644 --- a/libraries/networking/src/ThreadedAssignment.h +++ b/libraries/networking/src/ThreadedAssignment.h @@ -25,7 +25,7 @@ public: ~ThreadedAssignment() { stop(); } void setFinished(bool isFinished); - virtual void aboutToFinish() { emit finished(); }; + virtual void aboutToFinish() { }; void addPacketStatsAndSendStatsPacket(QJsonObject statsObject); public slots: From d991dd230faa7e14887c97392c853729b685922f Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 31 Jul 2018 15:39:38 -0700 Subject: [PATCH 38/57] Remove call to aboutToFinish from AssetServer --- assignment-client/src/assets/AssetServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 4ff4078255..e0c35b7148 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -342,7 +342,6 @@ void AssetServer::aboutToFinish() { while (_pendingBakes.size() > 0) { QCoreApplication::processEvents(); } - ThreadedAssignment::aboutToFinish(); } void AssetServer::run() { From 33a46a6a827082956192af2d47d61b84474a19b8 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 31 Jul 2018 15:46:43 -0700 Subject: [PATCH 39/57] Make ThreadedAssignment::setFinished() protected --- libraries/networking/src/ThreadedAssignment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ThreadedAssignment.h b/libraries/networking/src/ThreadedAssignment.h index 9372cfa667..6eb72cfe0b 100644 --- a/libraries/networking/src/ThreadedAssignment.h +++ b/libraries/networking/src/ThreadedAssignment.h @@ -24,7 +24,6 @@ public: ThreadedAssignment(ReceivedMessage& message); ~ThreadedAssignment() { stop(); } - void setFinished(bool isFinished); virtual void aboutToFinish() { }; void addPacketStatsAndSendStatsPacket(QJsonObject statsObject); @@ -61,6 +60,7 @@ signals: protected: void commonInit(const QString& targetName, NodeType_t nodeType); + void setFinished(bool isFinished); bool _isFinished; QTimer _domainServerTimer; From 5a92032bdd5f4812a8a8961f427cb6e96181a29e Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 31 Jul 2018 16:14:00 -0700 Subject: [PATCH 40/57] ThreadedAssignment::stop no longer deprecated --- assignment-client/src/Agent.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 4bc6beb431..7d47c8e713 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -67,10 +67,6 @@ public slots: void setIsAvatar(bool isAvatar); bool isAvatar() const { return _isAvatar; } - /**jsdoc - * @function Agent.stop - * @deprecated This function is being removed from the API. - */ Q_INVOKABLE virtual void stop() override; private slots: From 6f61642cb09e75e2f21596a30d11a1878a52d076 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 31 Jul 2018 18:10:49 -0700 Subject: [PATCH 41/57] Call setFinished() if script engine hasn't started up --- assignment-client/src/Agent.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index fb09efe287..23090e0d84 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -857,5 +857,7 @@ void Agent::aboutToFinish() { void Agent::stop() { if (_scriptEngine) { _scriptEngine->stop(); + } else { + setFinished(true); } } From ed75fe673e82b1efdc15356c6ba714bfee0a9b4a Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 31 Jul 2018 18:28:14 -0700 Subject: [PATCH 42/57] Replace int+bool with single atomic int --- interface/src/octree/OctreePacketProcessor.cpp | 8 ++------ interface/src/octree/OctreePacketProcessor.h | 5 +---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 76f62bcc4e..9196699473 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -116,9 +116,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag OCTREE_PACKET_SEQUENCE completionNumber; message->readPrimitive(&completionNumber); - Locker lock(_completionMutex); _completionSequenceNumber = completionNumber; - _completionSequenceNumberValid = true; } break; default: { @@ -128,8 +126,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag } void OctreePacketProcessor::resetCompletionSequenceNumber() { - Locker lock(_completionMutex); - _completionSequenceNumber = false; + _completionSequenceNumber = -1; } namespace { @@ -143,8 +140,7 @@ namespace { } bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const { - Locker lock(_completionMutex); // If we've received the flagged seq # and the current one is >= it. - return _completionSequenceNumberValid && + return _completionSequenceNumber != -1 && !lessThanWraparound(sequenceNumber, _completionSequenceNumber); } diff --git a/interface/src/octree/OctreePacketProcessor.h b/interface/src/octree/OctreePacketProcessor.h index ad1ab6c36c..9c5eaca8ec 100644 --- a/interface/src/octree/OctreePacketProcessor.h +++ b/interface/src/octree/OctreePacketProcessor.h @@ -37,10 +37,7 @@ private slots: void handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode); private: - mutable std::mutex _completionMutex; - using Locker = std::lock_guard; - bool _completionSequenceNumberValid { false }; - int _completionSequenceNumber { 0 }; + std::atomic _completionSequenceNumber { -1 }; }; #endif // hifi_OctreePacketProcessor_h From b18c1f0e1eed058b9bc30dc36588c82e25334bcb Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 1 Aug 2018 10:10:45 -0700 Subject: [PATCH 43/57] Use defined value for invalid sequence number --- interface/src/octree/OctreePacketProcessor.cpp | 4 ++-- interface/src/octree/OctreePacketProcessor.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 9196699473..063b07f699 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -126,7 +126,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag } void OctreePacketProcessor::resetCompletionSequenceNumber() { - _completionSequenceNumber = -1; + _completionSequenceNumber = INVALID_SEQUENCE; } namespace { @@ -141,6 +141,6 @@ namespace { bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const { // If we've received the flagged seq # and the current one is >= it. - return _completionSequenceNumber != -1 && + return _completionSequenceNumber != INVALID_SEQUENCE && !lessThanWraparound(sequenceNumber, _completionSequenceNumber); } diff --git a/interface/src/octree/OctreePacketProcessor.h b/interface/src/octree/OctreePacketProcessor.h index 9c5eaca8ec..edba48a238 100644 --- a/interface/src/octree/OctreePacketProcessor.h +++ b/interface/src/octree/OctreePacketProcessor.h @@ -37,7 +37,8 @@ private slots: void handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode); private: - std::atomic _completionSequenceNumber { -1 }; + static constexpr int INVALID_SEQUENCE = -1; + std::atomic _completionSequenceNumber { INVALID_SEQUENCE }; }; #endif // hifi_OctreePacketProcessor_h From 9d42a6e08fbbbb9eab4ba52335621eac2beefa02 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 1 Aug 2018 10:37:28 -0700 Subject: [PATCH 44/57] Change method name per coding standards --- assignment-client/src/AssignmentClientMonitor.cpp | 4 ++-- assignment-client/src/AssignmentClientMonitor.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index eb764a128c..330023dae0 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -74,7 +74,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket"); - adjustOsResources(std::max(_numAssignmentClientForks, _maxAssignmentClientForks)); + adjustOSResources(std::max(_numAssignmentClientForks, _maxAssignmentClientForks)); // use QProcess to fork off a process for each of the child assignment clients for (unsigned int i = 0; i < _numAssignmentClientForks; i++) { spawnChildClient(); @@ -377,7 +377,7 @@ bool AssignmentClientMonitor::handleHTTPRequest(HTTPConnection* connection, cons return true; } -void AssignmentClientMonitor::adjustOsResources(unsigned int numForks) const +void AssignmentClientMonitor::adjustOSResources(unsigned int numForks) const { #ifdef _POSIX_SOURCE // QProcess on Unix uses six (I think) descriptors, some temporarily, for each child proc. diff --git a/assignment-client/src/AssignmentClientMonitor.h b/assignment-client/src/AssignmentClientMonitor.h index 659ff4b001..5e32c50e0d 100644 --- a/assignment-client/src/AssignmentClientMonitor.h +++ b/assignment-client/src/AssignmentClientMonitor.h @@ -55,7 +55,7 @@ public slots: private: void spawnChildClient(); void simultaneousWaitOnChildren(int waitMsecs); - void adjustOsResources(unsigned int numForks) const; + void adjustOSResources(unsigned int numForks) const; QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children From 0d92e2de0ac3dfe2cc75ac2b74950431e21af1e2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 Aug 2018 11:21:54 -0700 Subject: [PATCH 45/57] remove bits from other-avatar collision mask --- libraries/shared/src/PhysicsCollisionGroups.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h index 9d99ec3532..ef18cb0b0e 100644 --- a/libraries/shared/src/PhysicsCollisionGroups.h +++ b/libraries/shared/src/PhysicsCollisionGroups.h @@ -59,7 +59,11 @@ const int32_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_STATIC; // MY_AVATAR does not collide with itself const int32_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR); -const int32_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_MASK_DEFAULT; +// OTHER_AVATARs are dynamic, but are slammed to whatever the avatar-mixer says, which means +// their motion can't actually be affected by the local physics simulation -- we rely on the remote simulation +// to move its avatar around correctly and to communicate its motion through the avatar-mixer. +// Therefore, they only need to collide against things that can be affected by their motion: dynamic and MyAvatar +const int32_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_GROUP_DYNAMIC | BULLET_COLLISION_GROUP_MY_AVATAR; // COLLISIONLESS gets an empty mask. const int32_t BULLET_COLLISION_MASK_COLLISIONLESS = 0; From c9396998796d10768bdac8e43ea70a6ff66e73c8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 Aug 2018 11:22:58 -0700 Subject: [PATCH 46/57] ignore other-avatar angularVelocity in physics simulation --- interface/src/avatar/AvatarMotionState.cpp | 29 ++++++++++++++++++--- interface/src/avatar/AvatarMotionState.h | 3 +++ libraries/physics/src/ObjectMotionState.cpp | 2 +- libraries/physics/src/ObjectMotionState.h | 2 +- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 50c715b14a..298701ca5d 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -19,6 +19,7 @@ AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); _type = MOTIONSTATE_TYPE_AVATAR; + cacheShapeDiameter(); } void AvatarMotionState::handleEasyChanges(uint32_t& flags) { @@ -57,9 +58,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const { const btCollisionShape* AvatarMotionState::computeNewShape() { ShapeInfo shapeInfo; std::static_pointer_cast(_avatar)->computeShapeInfo(shapeInfo); - glm::vec3 halfExtents = shapeInfo.getHalfExtents(); - halfExtents.y = 0.0f; - _diameter = 2.0f * glm::length(halfExtents); return getShapeManager()->getShape(shapeInfo); } @@ -141,7 +139,10 @@ glm::vec3 AvatarMotionState::getObjectLinearVelocity() const { // virtual glm::vec3 AvatarMotionState::getObjectAngularVelocity() const { - return _avatar->getWorldAngularVelocity(); + // HACK: avatars use a capusle collision shape and their angularVelocity in the local simulation is unimportant. + // Therefore, as optimization toward support for larger crowds we ignore it and return zero. + //return _avatar->getWorldAngularVelocity(); + return glm::vec3(0.0f); } // virtual @@ -174,3 +175,23 @@ float AvatarMotionState::getMass() const { return std::static_pointer_cast(_avatar)->computeMass(); } +void AvatarMotionState::cacheShapeDiameter() { + if (_shape) { + // measure XZ diameter of capsule shape + btVector3 aabbMin, aabbMax; + btTransform transform; + transform.setIdentity(); + _shape->getAabb(transform, aabbMin, aabbMax); + aabbMax -= aabbMin; + aabbMax.setY(0.0f); + const float SQRT_TWO = 1.414213562f; + _diameter = SQRT_TWO * aabbMax.length(); + } else { + _diameter = 0.0f; + } +} + +void AvatarMotionState::setShape(const btCollisionShape* shape) { + cacheShapeDiameter(); + ObjectMotionState::setShape(shape); +} diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 9228641b25..62fbc566f3 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -74,6 +74,9 @@ public: friend class Avatar; protected: + void setShape(const btCollisionShape* shape) override; + void cacheShapeDiameter(); + // the dtor had been made protected to force the compiler to verify that it is only // ever called by the Avatar class dtor. ~AvatarMotionState(); diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 310cf7cec1..c814140930 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -64,9 +64,9 @@ ShapeManager* ObjectMotionState::getShapeManager() { } ObjectMotionState::ObjectMotionState(const btCollisionShape* shape) : - _shape(shape), _lastKinematicStep(worldSimulationStep) { + setShape(shape); } ObjectMotionState::~ObjectMotionState() { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 7439c1c38d..269117b28c 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -181,7 +181,7 @@ protected: MotionStateType _type { MOTIONSTATE_TYPE_INVALID }; // type of MotionState PhysicsMotionType _motionType { MOTION_TYPE_STATIC }; // type of motion: KINEMATIC, DYNAMIC, or STATIC - const btCollisionShape* _shape; + const btCollisionShape* _shape { nullptr }; btRigidBody* _body { nullptr }; float _density { 1.0f }; From bd6f00e707c4df82b8b9c26db4f2297f4c3bb919 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 Aug 2018 11:58:52 -0700 Subject: [PATCH 47/57] eliminate angular dynamics of other-avatars --- interface/src/avatar/AvatarMotionState.cpp | 12 ++++++++++++ interface/src/avatar/AvatarMotionState.h | 1 + libraries/physics/src/ObjectMotionState.h | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 298701ca5d..bff5233f1d 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -96,6 +96,10 @@ void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) { btVector3 velocity = glmToBullet(getObjectLinearVelocity()) + (1.0f / SPRING_TIMESCALE) * offsetToTarget; _body->setLinearVelocity(velocity); _body->setAngularVelocity(glmToBullet(getObjectAngularVelocity())); + // slam its rotation + btTransform newTransform = worldTrans; + newTransform.setRotation(glmToBullet(getObjectRotation())); + _body->setWorldTransform(newTransform); } } @@ -191,6 +195,14 @@ void AvatarMotionState::cacheShapeDiameter() { } } +void AvatarMotionState::setRigidBody(btRigidBody* body) { + ObjectMotionState::setRigidBody(body); + if (_body) { + // remove angular dynamics from this body + _body->setAngularFactor(0.0f); + } +} + void AvatarMotionState::setShape(const btCollisionShape* shape) { cacheShapeDiameter(); ObjectMotionState::setShape(shape); diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 62fbc566f3..a458704b1a 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -74,6 +74,7 @@ public: friend class Avatar; protected: + void setRigidBody(btRigidBody* body) override; void setShape(const btCollisionShape* shape) override; void cacheShapeDiameter(); diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 269117b28c..74173c3f47 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -175,7 +175,7 @@ protected: virtual void setMotionType(PhysicsMotionType motionType); void updateCCDConfiguration(); - void setRigidBody(btRigidBody* body); + virtual void setRigidBody(btRigidBody* body); virtual void setShape(const btCollisionShape* shape); MotionStateType _type { MOTIONSTATE_TYPE_INVALID }; // type of MotionState From 4b317a84d811e4e04c0909ee1e87df018aab92c5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 27 Jul 2018 15:25:57 -0700 Subject: [PATCH 48/57] Don't allow Web surfaces created by non-client scripts to access files --- interface/resources/qml/QmlWebWindow.qml | 1 + interface/src/Application.cpp | 7 ++-- libraries/qml/src/qml/OffscreenSurface.cpp | 11 +++-- libraries/qml/src/qml/OffscreenSurface.h | 31 +++++++++----- libraries/ui/src/QmlFragmentClass.cpp | 6 +-- libraries/ui/src/QmlFragmentClass.h | 14 ++++++- libraries/ui/src/QmlWebWindowClass.cpp | 4 +- libraries/ui/src/QmlWebWindowClass.h | 12 +++++- libraries/ui/src/QmlWindowClass.cpp | 30 ++++++++++--- libraries/ui/src/QmlWindowClass.h | 17 +++++++- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 24 +++++------ .../ui/src/ui/TabletScriptingInterface.cpp | 2 + .../ui/src/ui/types/ContextAwareProfile.cpp | 32 ++++++++++++++ .../ui/src/ui/types/ContextAwareProfile.h | 42 +++++++++++++++++++ libraries/ui/src/ui/types/FileTypeProfile.cpp | 21 ++++++++-- libraries/ui/src/ui/types/FileTypeProfile.h | 17 ++++++-- .../ui/types/FileTypeRequestInterceptor.cpp | 25 ----------- .../src/ui/types/FileTypeRequestInterceptor.h | 30 ------------- .../src/ui/types/HFTabletWebEngineProfile.h | 23 ---------- .../HFTabletWebEngineRequestInterceptor.h | 30 ------------- .../ui/src/ui/types/HFWebEngineProfile.cpp | 20 ++++++--- .../ui/src/ui/types/HFWebEngineProfile.h | 17 ++++++-- .../types/HFWebEngineRequestInterceptor.cpp | 25 ----------- .../ui/types/HFWebEngineRequestInterceptor.h | 30 ------------- libraries/ui/src/ui/types/RequestFilters.cpp | 32 +++++++++++--- libraries/ui/src/ui/types/RequestFilters.h | 6 ++- 26 files changed, 275 insertions(+), 234 deletions(-) create mode 100644 libraries/ui/src/ui/types/ContextAwareProfile.cpp create mode 100644 libraries/ui/src/ui/types/ContextAwareProfile.h delete mode 100644 libraries/ui/src/ui/types/FileTypeRequestInterceptor.cpp delete mode 100644 libraries/ui/src/ui/types/FileTypeRequestInterceptor.h delete mode 100644 libraries/ui/src/ui/types/HFTabletWebEngineProfile.h delete mode 100644 libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h delete mode 100644 libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.cpp delete mode 100644 libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.h diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index d73a574081..8c4d6145ec 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -62,6 +62,7 @@ Windows.ScrollingWindow { url: "about:blank" anchors.fill: parent focus: true + profile: HFWebEngineProfile; property string userScriptUrl: "" diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 65fbefa381..ecd091ba2b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6603,11 +6603,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, LocationScriptingInterface::locationSetter); + bool clientScript = scriptEngine->isClientScript(); + scriptEngine->registerFunction("OverlayWindow", clientScript ? QmlWindowClass::constructor : QmlWindowClass::restricted_constructor); #if !defined(Q_OS_ANDROID) - scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor); + scriptEngine->registerFunction("OverlayWebWindow", clientScript ? QmlWebWindowClass::constructor : QmlWebWindowClass::restricted_constructor); #endif - scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor); - scriptEngine->registerFunction("QmlFragment", QmlFragmentClass::constructor); + scriptEngine->registerFunction("QmlFragment", clientScript ? QmlFragmentClass::constructor : QmlFragmentClass::restricted_constructor); scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get().data()); diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index ea6f1ce324..bbffee2407 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -40,7 +40,8 @@ static QSize clampSize(const QSize& qsize, uint32_t maxDimension) { return fromGlm(clampSize(toGlm(qsize), maxDimension)); } -const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QQuickItem*) {}; +const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_OBJECT_CALLBACK = [](QQmlContext*, QQuickItem*) {}; +const QmlContextCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*) {}; void OffscreenSurface::initializeEngine(QQmlEngine* engine) { } @@ -266,8 +267,8 @@ void OffscreenSurface::load(const QUrl& qmlSource, bool createNewContext, const loadInternal(qmlSource, createNewContext, nullptr, callback); } -void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback) { - load(qmlSource, true, callback); +void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback, const QmlContextCallback& contextCallback) { + loadInternal(qmlSource, true, nullptr, callback, contextCallback); } void OffscreenSurface::load(const QUrl& qmlSource, const QmlContextObjectCallback& callback) { @@ -281,7 +282,8 @@ void OffscreenSurface::load(const QString& qmlSourceFile, const QmlContextObject void OffscreenSurface::loadInternal(const QUrl& qmlSource, bool createNewContext, QQuickItem* parent, - const QmlContextObjectCallback& callback) { + const QmlContextObjectCallback& callback, + const QmlContextCallback& contextCallback) { PROFILE_RANGE_EX(app, "OffscreenSurface::loadInternal", 0xffff00ff, 0, { std::make_pair("url", qmlSource.toDisplayString()) }); if (QThread::currentThread() != thread()) { qFatal("Called load on a non-surface thread"); @@ -310,6 +312,7 @@ void OffscreenSurface::loadInternal(const QUrl& qmlSource, } auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext); + contextCallback(targetContext); QQmlComponent* qmlComponent; { PROFILE_RANGE(app, "new QQmlComponent"); diff --git a/libraries/qml/src/qml/OffscreenSurface.h b/libraries/qml/src/qml/OffscreenSurface.h index 555f2ee6a4..b3539e7709 100644 --- a/libraries/qml/src/qml/OffscreenSurface.h +++ b/libraries/qml/src/qml/OffscreenSurface.h @@ -37,13 +37,15 @@ namespace impl { class SharedObject; } +using QmlContextCallback = ::std::function; using QmlContextObjectCallback = ::std::function; class OffscreenSurface : public QObject { Q_OBJECT public: - static const QmlContextObjectCallback DEFAULT_CONTEXT_CALLBACK; + static const QmlContextObjectCallback DEFAULT_CONTEXT_OBJECT_CALLBACK; + static const QmlContextCallback DEFAULT_CONTEXT_CALLBACK; using TextureAndFence = std::pair; using MouseTranslator = std::function; @@ -85,10 +87,15 @@ public: Q_INVOKABLE void load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback); // For use from C++ - Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK); - Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK); - Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK); - Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK); + Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK); + Q_INVOKABLE void load(const QUrl& qmlSource, + bool createNewContext, + const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK); + Q_INVOKABLE void load(const QString& qmlSourceFile, + const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK); + Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, + const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK, + const QmlContextCallback& contextCallback = DEFAULT_CONTEXT_CALLBACK); public slots: virtual void onFocusObjectChanged(QObject* newFocus) {} @@ -103,19 +110,21 @@ protected: virtual void initializeEngine(QQmlEngine* engine); virtual void loadInternal(const QUrl& qmlSource, - bool createNewContext, - QQuickItem* parent, - const QmlContextObjectCallback& callback) final; + bool createNewContext, + QQuickItem* parent, + const QmlContextObjectCallback& callback, + const QmlContextCallback& contextCallback = DEFAULT_CONTEXT_CALLBACK) final; virtual void finishQmlLoad(QQmlComponent* qmlComponent, - QQmlContext* qmlContext, - QQuickItem* parent, - const QmlContextObjectCallback& onQmlLoadedCallback) final; + QQmlContext* qmlContext, + QQuickItem* parent, + const QmlContextObjectCallback& onQmlLoadedCallback) final; virtual void onRootCreated() {} virtual void onItemCreated(QQmlContext* context, QQuickItem* newItem) {} virtual void onRootContextCreated(QQmlContext* qmlContext) {} virtual QQmlContext* contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext); + private: MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } }; friend class hifi::qml::impl::SharedObject; diff --git a/libraries/ui/src/QmlFragmentClass.cpp b/libraries/ui/src/QmlFragmentClass.cpp index c4cac73b2d..13e3527ded 100644 --- a/libraries/ui/src/QmlFragmentClass.cpp +++ b/libraries/ui/src/QmlFragmentClass.cpp @@ -20,10 +20,10 @@ std::mutex QmlFragmentClass::_mutex; std::map QmlFragmentClass::_fragments; -QmlFragmentClass::QmlFragmentClass(QString id) : qml(id) { } +QmlFragmentClass::QmlFragmentClass(bool restricted, QString id) : QmlWindowClass(restricted), qml(id) { } // Method called by Qt scripts to create a new bottom menu bar in Android -QScriptValue QmlFragmentClass::constructor(QScriptContext* context, QScriptEngine* engine) { +QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) { std::lock_guard guard(_mutex); auto qml = context->argument(0).toVariant().toMap().value("qml"); @@ -41,7 +41,7 @@ QScriptValue QmlFragmentClass::constructor(QScriptContext* context, QScriptEngin auto properties = parseArguments(context); auto offscreenUi = DependencyManager::get(); - QmlFragmentClass* retVal = new QmlFragmentClass(qml.toString()); + QmlFragmentClass* retVal = new QmlFragmentClass(restricted, qml.toString()); Q_ASSERT(retVal); if (QThread::currentThread() != qApp->thread()) { retVal->moveToThread(qApp->thread()); diff --git a/libraries/ui/src/QmlFragmentClass.h b/libraries/ui/src/QmlFragmentClass.h index 8a8d0e1732..ea80b2bd13 100644 --- a/libraries/ui/src/QmlFragmentClass.h +++ b/libraries/ui/src/QmlFragmentClass.h @@ -13,9 +13,19 @@ class QmlFragmentClass : public QmlWindowClass { Q_OBJECT + +private: + static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted); public: - QmlFragmentClass(QString id); - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { + return internal_constructor(context, engine, false); + } + + static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){ + return internal_constructor(context, engine, true); + } + + QmlFragmentClass(bool restricted, QString id); /**jsdoc * Creates a new button, adds it to this and returns it. diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index 44a0af7787..282161497a 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -20,10 +20,10 @@ static const char* const URL_PROPERTY = "source"; static const char* const SCRIPT_PROPERTY = "scriptUrl"; // Method called by Qt scripts to create a new web window in the overlay -QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { +QScriptValue QmlWebWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) { auto properties = parseArguments(context); auto offscreenUi = DependencyManager::get(); - QmlWebWindowClass* retVal = new QmlWebWindowClass(); + QmlWebWindowClass* retVal = new QmlWebWindowClass(restricted); Q_ASSERT(retVal); if (QThread::currentThread() != qApp->thread()) { retVal->moveToThread(qApp->thread()); diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h index 8cf77e4286..e3aea22e3d 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -57,8 +57,18 @@ class QmlWebWindowClass : public QmlWindowClass { Q_OBJECT Q_PROPERTY(QString url READ getURL CONSTANT) +private: + static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted); public: - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + QmlWebWindowClass(bool restricted) : QmlWindowClass(restricted) {} + + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { + return internal_constructor(context, engine, false); + } + + static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){ + return internal_constructor(context, engine, true); + } public slots: diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 64fa27c8c6..0182e3adc3 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -25,6 +25,8 @@ #include #include "OffscreenUi.h" +#include "ui/types/HFWebEngineProfile.h" +#include "ui/types/FileTypeProfile.h" static const char* const SOURCE_PROPERTY = "source"; static const char* const TITLE_PROPERTY = "title"; @@ -68,10 +70,10 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) { // Method called by Qt scripts to create a new web window in the overlay -QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { +QScriptValue QmlWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) { auto properties = parseArguments(context); auto offscreenUi = DependencyManager::get(); - QmlWindowClass* retVal = new QmlWindowClass(); + QmlWindowClass* retVal = new QmlWindowClass(restricted); Q_ASSERT(retVal); if (QThread::currentThread() != qApp->thread()) { retVal->moveToThread(qApp->thread()); @@ -83,7 +85,7 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* return engine->newQObject(retVal); } -QmlWindowClass::QmlWindowClass() { +QmlWindowClass::QmlWindowClass(bool restricted) : _restricted(restricted) { } @@ -99,8 +101,7 @@ void QmlWindowClass::initQml(QVariantMap properties) { auto offscreenUi = DependencyManager::get(); _source = properties[SOURCE_PROPERTY].toString(); - // Build the event bridge and wrapper on the main thread - offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) { + auto objectInitLambda = [&](QQmlContext* context, QObject* object) { _qmlWindow = object; context->setContextProperty(EVENT_BRIDGE_PROPERTY, this); context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); @@ -128,7 +129,24 @@ void QmlWindowClass::initQml(QVariantMap properties) { if (metaObject->indexOfSignal("moved") >= 0) connect(_qmlWindow, SIGNAL(moved(QVector2D)), this, SLOT(hasMoved(QVector2D)), Qt::QueuedConnection); connect(_qmlWindow, SIGNAL(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection); - }); + }; + + auto contextInitLambda = [&](QQmlContext* context) { +#if !defined(Q_OS_ANDROID) + // If the restricted flag is on, override the FileTypeProfile and HFWebEngineProfile objects in the + // QML surface root context with local ones + qDebug() << "Context initialization lambda"; + if (_restricted) { + qDebug() << "Restricting web content"; + ContextAwareProfile::restrictContext(context); + FileTypeProfile::registerWithContext(context); + HFWebEngineProfile::registerWithContext(context); + } +#endif + }; + + // Build the event bridge and wrapper on the main thread + offscreenUi->loadInNewContext(qmlSource(), objectInitLambda, contextInitLambda); Q_ASSERT(_qmlWindow); Q_ASSERT(dynamic_cast(_qmlWindow.data())); diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index 2b01c028ea..18ee1fedd5 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -38,9 +38,18 @@ class QmlWindowClass : public QObject { Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) +private: + static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted); public: - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); - QmlWindowClass(); + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { + return internal_constructor(context, engine, false); + } + + static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){ + return internal_constructor(context, engine, true); + } + + QmlWindowClass(bool restricted); ~QmlWindowClass(); /**jsdoc @@ -51,6 +60,8 @@ public: QQuickItem* asQuickItem() const; + + public slots: /**jsdoc @@ -250,10 +261,12 @@ protected: QPointer _qmlWindow; QString _source; + const bool _restricted; private: // QmlWindow content may include WebView requiring EventBridge. void setKeyboardRaised(QObject* object, bool raised, bool numeric = false); + }; #endif diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 48e778c063..2c947aea20 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -265,19 +265,6 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) { if (!javaScriptToInject.isEmpty()) { rootContext->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject)); } -#if !defined(Q_OS_ANDROID) - rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext)); - rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext)); - { - PROFILE_RANGE(startup, "FileTypeProfile"); - rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext)); - } - { - PROFILE_RANGE(startup, "HFWebEngineProfile"); - rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext)); - - } -#endif rootContext->setContextProperty("Paths", DependencyManager::get().data()); rootContext->setContextProperty("Tablet", DependencyManager::get().data()); rootContext->setContextProperty("Toolbars", DependencyManager::get().data()); @@ -300,6 +287,17 @@ void OffscreenQmlSurface::onRootContextCreated(QQmlContext* qmlContext) { // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper // Find a way to flag older scripts using this mechanism and wanr that this is deprecated qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, qmlContext)); +#if !defined(Q_OS_ANDROID) + { + PROFILE_RANGE(startup, "FileTypeProfile"); + FileTypeProfile::registerWithContext(qmlContext); + } + { + PROFILE_RANGE(startup, "HFWebEngineProfile"); + HFWebEngineProfile::registerWithContext(qmlContext); + + } +#endif } QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 6f00e046af..4e920e430b 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -334,6 +334,8 @@ static const char* VRMENU_SOURCE_URL = "hifi/tablet/TabletMenu.qml"; class TabletRootWindow : public QmlWindowClass { virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; } +public: + TabletRootWindow() : QmlWindowClass(false) {} }; TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent), _name(name) { diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp new file mode 100644 index 0000000000..98cc94ec10 --- /dev/null +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -0,0 +1,32 @@ +// +// FileTypeProfile.cpp +// interface/src/networking +// +// Created by Kunal Gosar on 2017-03-10. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ContextAwareProfile.h" + +#if !defined(Q_OS_ANDROID) + +#include + +static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess"; + +ContextAwareProfile::ContextAwareProfile(QQmlContext* parent) : + QQuickWebEngineProfile(parent), _context(parent) { } + + +void ContextAwareProfile::restrictContext(QQmlContext* context) { + context->setContextProperty(RESTRICTED_FLAG_PROPERTY, true); +} + +bool ContextAwareProfile::isRestricted(QQmlContext* context) { + return context->contextProperty(RESTRICTED_FLAG_PROPERTY).toBool(); +} + +#endif \ No newline at end of file diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.h b/libraries/ui/src/ui/types/ContextAwareProfile.h new file mode 100644 index 0000000000..8fa5b98878 --- /dev/null +++ b/libraries/ui/src/ui/types/ContextAwareProfile.h @@ -0,0 +1,42 @@ +// +// Created by Bradley Austin Davis on 2018/07/27 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once + +#ifndef hifi_ContextAwareProfile_h +#define hifi_ContextAwareProfile_h + +#include + +#if !defined(Q_OS_ANDROID) +#include +#include + +class QQmlContext; + +class ContextAwareProfile : public QQuickWebEngineProfile { +public: + static void restrictContext(QQmlContext* context); + static bool isRestricted(QQmlContext* context); + QQmlContext* getContext() const { return _context; } +protected: + + class RequestInterceptor : public QWebEngineUrlRequestInterceptor { + public: + RequestInterceptor(ContextAwareProfile* parent) : QWebEngineUrlRequestInterceptor(parent), _profile(parent) {} + QQmlContext* getContext() const { return _profile->getContext(); } + protected: + ContextAwareProfile* _profile; + }; + + ContextAwareProfile(QQmlContext* parent); + QQmlContext* _context; +}; +#endif + +#endif // hifi_FileTypeProfile_h diff --git a/libraries/ui/src/ui/types/FileTypeProfile.cpp b/libraries/ui/src/ui/types/FileTypeProfile.cpp index 90a2c6ba18..073460903e 100644 --- a/libraries/ui/src/ui/types/FileTypeProfile.cpp +++ b/libraries/ui/src/ui/types/FileTypeProfile.cpp @@ -11,18 +11,31 @@ #include "FileTypeProfile.h" -#include "FileTypeRequestInterceptor.h" +#include + +#include "RequestFilters.h" #if !defined(Q_OS_ANDROID) static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine"; -FileTypeProfile::FileTypeProfile(QObject* parent) : - QQuickWebEngineProfile(parent) +FileTypeProfile::FileTypeProfile(QQmlContext* parent) : + ContextAwareProfile(parent) { static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)"; setHttpUserAgent(WEB_ENGINE_USER_AGENT); - auto requestInterceptor = new FileTypeRequestInterceptor(this); + auto requestInterceptor = new RequestInterceptor(this); setRequestInterceptor(requestInterceptor); } + +void FileTypeProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { + RequestFilters::interceptHFWebEngineRequest(info, getContext()); + RequestFilters::interceptFileType(info, getContext()); +} + +void FileTypeProfile::registerWithContext(QQmlContext* context) { + context->setContextProperty("FileTypeProfile", new FileTypeProfile(context)); +} + + #endif \ No newline at end of file diff --git a/libraries/ui/src/ui/types/FileTypeProfile.h b/libraries/ui/src/ui/types/FileTypeProfile.h index c7d07cd822..7ddfdd0aed 100644 --- a/libraries/ui/src/ui/types/FileTypeProfile.h +++ b/libraries/ui/src/ui/types/FileTypeProfile.h @@ -17,12 +17,23 @@ #include #if !defined(Q_OS_ANDROID) -#include +#include "ContextAwareProfile.h" + +class FileTypeProfile : public ContextAwareProfile { + using Parent = ContextAwareProfile; -class FileTypeProfile : public QQuickWebEngineProfile { public: - FileTypeProfile(QObject* parent = Q_NULLPTR); + static void registerWithContext(QQmlContext* parent); + +protected: + FileTypeProfile(QQmlContext* parent); + class RequestInterceptor : public Parent::RequestInterceptor { + public: + RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {} + void interceptRequest(QWebEngineUrlRequestInfo& info) override; + }; }; + #endif #endif // hifi_FileTypeProfile_h diff --git a/libraries/ui/src/ui/types/FileTypeRequestInterceptor.cpp b/libraries/ui/src/ui/types/FileTypeRequestInterceptor.cpp deleted file mode 100644 index 25866ad395..0000000000 --- a/libraries/ui/src/ui/types/FileTypeRequestInterceptor.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// -// FileTypeRequestInterceptor.cpp -// interface/src/networking -// -// Created by Kunal Gosar on 2017-03-10. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "FileTypeRequestInterceptor.h" - -#include - -#include "RequestFilters.h" - -#if !defined(Q_OS_ANDROID) - -void FileTypeRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { - RequestFilters::interceptHFWebEngineRequest(info); - RequestFilters::interceptFileType(info); -} - -#endif \ No newline at end of file diff --git a/libraries/ui/src/ui/types/FileTypeRequestInterceptor.h b/libraries/ui/src/ui/types/FileTypeRequestInterceptor.h deleted file mode 100644 index b8a01a53fa..0000000000 --- a/libraries/ui/src/ui/types/FileTypeRequestInterceptor.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// FileTypeRequestInterceptor.h -// interface/src/networking -// -// Created by Kunal Gosar on 2017-03-10. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#pragma once - -#ifndef hifi_FileTypeRequestInterceptor_h -#define hifi_FileTypeRequestInterceptor_h - -#include - -#if !defined(Q_OS_ANDROID) -#include - -class FileTypeRequestInterceptor : public QWebEngineUrlRequestInterceptor { -public: - FileTypeRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {}; - - virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override; -}; -#endif - -#endif // hifi_FileTypeRequestInterceptor_h diff --git a/libraries/ui/src/ui/types/HFTabletWebEngineProfile.h b/libraries/ui/src/ui/types/HFTabletWebEngineProfile.h deleted file mode 100644 index 406cb1a19a..0000000000 --- a/libraries/ui/src/ui/types/HFTabletWebEngineProfile.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// HFTabletWebEngineProfile.h -// interface/src/networking -// -// Created by Dante Ruiz on 2017-03-31. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -#ifndef hifi_HFTabletWebEngineProfile_h -#define hifi_HFTabletWebEngineProfile_h - -#include - -class HFTabletWebEngineProfile : public QQuickWebEngineProfile { -public: - HFTabletWebEngineProfile(QObject* parent = Q_NULLPTR); -}; - -#endif // hifi_HFTabletWebEngineProfile_h diff --git a/libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h b/libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h deleted file mode 100644 index 8be2974782..0000000000 --- a/libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// HFTabletWebEngineRequestInterceptor.h -// interface/src/networking -// -// Created by Dante Ruiz on 2017-3-31. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_HFTabletWebEngineRequestInterceptor_h -#define hifi_HFTabletWebEngineRequestInterceptor_h -#if !defined(Q_OS_ANDROID) -#include - -#include - -class HFTabletWebEngineRequestInterceptor - : public QWebEngineUrlRequestInterceptor -{ -public: - HFTabletWebEngineRequestInterceptor(QObject* parent) - : QWebEngineUrlRequestInterceptor(parent) - {}; - virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override; -}; -#endif - -#endif // hifi_HFWebEngineRequestInterceptor_h diff --git a/libraries/ui/src/ui/types/HFWebEngineProfile.cpp b/libraries/ui/src/ui/types/HFWebEngineProfile.cpp index 381bdb10bd..ef1d009f09 100644 --- a/libraries/ui/src/ui/types/HFWebEngineProfile.cpp +++ b/libraries/ui/src/ui/types/HFWebEngineProfile.cpp @@ -11,20 +11,28 @@ #include "HFWebEngineProfile.h" -#include "HFWebEngineRequestInterceptor.h" +#include + +#include "RequestFilters.h" #if !defined(Q_OS_ANDROID) static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine"; -HFWebEngineProfile::HFWebEngineProfile(QObject* parent) : - QQuickWebEngineProfile(parent) +HFWebEngineProfile::HFWebEngineProfile(QQmlContext* parent) : Parent(parent) { setStorageName(QML_WEB_ENGINE_STORAGE_NAME); // we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user - auto requestInterceptor = new HFWebEngineRequestInterceptor(this); - setRequestInterceptor(requestInterceptor); + setRequestInterceptor(new RequestInterceptor(this)); } -#endif \ No newline at end of file +void HFWebEngineProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { + RequestFilters::interceptHFWebEngineRequest(info, getContext()); +} + +void HFWebEngineProfile::registerWithContext(QQmlContext* context) { + context->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(context)); +} + +#endif diff --git a/libraries/ui/src/ui/types/HFWebEngineProfile.h b/libraries/ui/src/ui/types/HFWebEngineProfile.h index 30da489c92..6b84ad6f80 100644 --- a/libraries/ui/src/ui/types/HFWebEngineProfile.h +++ b/libraries/ui/src/ui/types/HFWebEngineProfile.h @@ -14,15 +14,24 @@ #ifndef hifi_HFWebEngineProfile_h #define hifi_HFWebEngineProfile_h -#include +#include "ContextAwareProfile.h" #if !defined(Q_OS_ANDROID) -#include -class HFWebEngineProfile : public QQuickWebEngineProfile { +class HFWebEngineProfile : public ContextAwareProfile { + using Parent = ContextAwareProfile; public: - HFWebEngineProfile(QObject* parent = Q_NULLPTR); + static void registerWithContext(QQmlContext* parent); + +protected: + HFWebEngineProfile(QQmlContext* parent); + class RequestInterceptor : public Parent::RequestInterceptor { + public: + RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {} + void interceptRequest(QWebEngineUrlRequestInfo& info) override; + }; }; + #endif #endif // hifi_HFWebEngineProfile_h diff --git a/libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.cpp b/libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.cpp deleted file mode 100644 index 5a11c32efa..0000000000 --- a/libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// -// HFWebEngineRequestInterceptor.cpp -// interface/src/networking -// -// Created by Stephen Birarda on 2016-10-14. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "HFWebEngineRequestInterceptor.h" - -#include - -#include "AccountManager.h" -#include "RequestFilters.h" - -#if !defined(Q_OS_ANDROID) - -void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { - RequestFilters::interceptHFWebEngineRequest(info); -} - -#endif \ No newline at end of file diff --git a/libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.h b/libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.h deleted file mode 100644 index b5521a106e..0000000000 --- a/libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// HFWebEngineRequestInterceptor.h -// interface/src/networking -// -// Created by Stephen Birarda on 2016-10-14. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#pragma once - -#ifndef hifi_HFWebEngineRequestInterceptor_h -#define hifi_HFWebEngineRequestInterceptor_h - -#include - -#if !defined(Q_OS_ANDROID) -#include - -class HFWebEngineRequestInterceptor : public QWebEngineUrlRequestInterceptor { -public: - HFWebEngineRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {}; - - virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override; -}; -#endif - -#endif // hifi_HFWebEngineRequestInterceptor_h diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 4cd51c6d98..9d831a1758 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -10,12 +10,15 @@ // #include "RequestFilters.h" -#include "NetworkingConstants.h" #include -#include +#include -#include "AccountManager.h" +#include +#include +#include + +#include "ContextAwareProfile.h" #if !defined(Q_OS_ANDROID) @@ -42,9 +45,28 @@ namespace { return filename.endsWith(".json", Qt::CaseInsensitive); } + bool blockLocalFiles(QWebEngineUrlRequestInfo& info) { + auto requestUrl = info.requestUrl(); + if (!requestUrl.isLocalFile()) { + // Not a local file, do not block + return false; + } + + QString targetFilePath = QFileInfo(requestUrl.toLocalFile()).canonicalFilePath(); + + // If we get here, then it's a local file that isn't whitelisted and the + // developer mode environment variable is not enabled. Block access to the file + qWarning() << "Blocking web access to local file path" << targetFilePath; + info.block(true); + return true; + } } -void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) { +void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context) { + if (ContextAwareProfile::isRestricted(context) && blockLocalFiles(info)) { + return; + } + // check if this is a request to a highfidelity URL bool isAuthable = isAuthableHighFidelityURL(info.requestUrl()); if (isAuthable) { @@ -71,7 +93,7 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit()); } -void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) { +void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context) { QString filename = info.requestUrl().fileName(); if (isScript(filename) || isJSON(filename)) { static const QString CONTENT_HEADER = "Accept"; diff --git a/libraries/ui/src/ui/types/RequestFilters.h b/libraries/ui/src/ui/types/RequestFilters.h index ccab6a6ee3..8fde94a1b4 100644 --- a/libraries/ui/src/ui/types/RequestFilters.h +++ b/libraries/ui/src/ui/types/RequestFilters.h @@ -20,10 +20,12 @@ #include #include +class QQmlContext; + class RequestFilters : public QObject { public: - static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info); - static void interceptFileType(QWebEngineUrlRequestInfo& info); + static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context); + static void interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context); }; #endif From 67528cb64636c70bcef7d8ab09471b2019b06ab5 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 1 Aug 2018 15:34:53 -0700 Subject: [PATCH 49/57] Fix MS17215: Auto-show Confirmed txns in Recent Activity when many Pending txns present --- .../qml/hifi/commerce/wallet/WalletHome.qml | 50 +++++++++++++++---- .../qml/hifi/models/PSFListModel.qml | 33 ++++++++++++ 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index a0c6057b3b..50208793fe 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -142,7 +142,7 @@ Item { Timer { id: refreshTimer; - interval: 4000; + interval: 6000; onTriggered: { if (transactionHistory.atYBeginning) { console.log("Refreshing 1st Page of Recent Activity..."); @@ -211,6 +211,7 @@ Item { HifiModels.PSFListModel { id: transactionHistoryModel; + property int lastPendingCount: 0; listModelName: "transaction history"; // For debugging. Alternatively, we could specify endpoint for that purpose, even though it's not used directly. listView: transactionHistory; itemsPerPage: 6; @@ -221,8 +222,26 @@ Item { processPage: function (data) { console.debug('processPage', transactionHistoryModel.listModelName, JSON.stringify(data)); var result, pending; // Set up or get the accumulator for pending. - if (transactionHistoryModel.currentPageToRetrieve == 1) { - pending = {transaction_type: "pendingCount", count: 0}; + if (transactionHistoryModel.currentPageToRetrieve === 1) { + // The initial data elements inside the ListModel MUST contain all keys + // that will be used in future data. + pending = { + transaction_type: "pendingCount", + count: 0, + created_at: 0, + hfc_text: "", + id: "", + message: "", + place_name: "", + received_certs: 0, + received_money: 0, + recipient_name: "", + sender_name: "", + sent_certs: 0, + sent_money: 0, + status: "", + transaction_text: "" + }; result = [pending]; } else { pending = transactionHistoryModel.get(0); @@ -239,6 +258,15 @@ Item { } }); + if (lastPendingCount === 0) { + lastPendingCount = pending.count; + } else { + if (lastPendingCount !== pending.count) { + transactionHistoryModel.getNextPageIfNotEnoughVerticalResults(); + } + lastPendingCount = pending.count; + } + // Only auto-refresh if the user hasn't scrolled // and there is more data to grab if (transactionHistory.atYBeginning && data.history.length) { @@ -257,13 +285,13 @@ Item { ListView { id: transactionHistory; ScrollBar.vertical: ScrollBar { - policy: transactionHistory.contentHeight > parent.parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded; - parent: transactionHistory.parent; - anchors.top: transactionHistory.top; - anchors.left: transactionHistory.right; - anchors.leftMargin: 4; - anchors.bottom: transactionHistory.bottom; - width: 20; + policy: transactionHistory.contentHeight > parent.parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded; + parent: transactionHistory.parent; + anchors.top: transactionHistory.top; + anchors.left: transactionHistory.right; + anchors.leftMargin: 4; + anchors.bottom: transactionHistory.bottom; + width: 20; } anchors.centerIn: parent; width: parent.width - 12; @@ -340,7 +368,7 @@ Item { } HifiControlsUit.Separator { - colorScheme: 1; + colorScheme: 1; anchors.left: parent.left; anchors.right: parent.right; anchors.bottom: parent.bottom; diff --git a/interface/resources/qml/hifi/models/PSFListModel.qml b/interface/resources/qml/hifi/models/PSFListModel.qml index 542145904f..ad9fbcc8ef 100644 --- a/interface/resources/qml/hifi/models/PSFListModel.qml +++ b/interface/resources/qml/hifi/models/PSFListModel.qml @@ -38,6 +38,16 @@ ListModel { onSearchFilterChanged: if (initialized) { getFirstPage('delayClear'); } onTagsFilterChanged: if (initialized) { getFirstPage('delayClear'); } + // When considering a value for `itemsPerPage` in YOUR model, consider the following: + // - If your ListView delegates are of variable width/height, ensure you select + // an `itemsPerPage` value that would be sufficient to show one full page of data + // if all of the delegates were at their minimum heights. + // - If your first ListView delegate contains some special data (as in WalletHome's + // "Recent Activity" view), beware that your `itemsPerPage` value may _never_ reasonably be + // high enough such that the first page of data causes the view to be one-screen in height + // after retrieving the first page. This means data will automatically pop-in (after a short delay) + // until the combined heights of your View's delegates reach one-screen in height OR there is + // no more data to retrieve. See "needsMoreVerticalResults()" below. property int itemsPerPage: 100; // State. @@ -81,12 +91,35 @@ ListModel { function getNextPageIfVerticalScroll() { if (needsEarlyYFetch()) { getNextPage(); } } + function needsMoreHorizontalResults() { + return flickable + && currentPageToRetrieve > 0 + && flickable.contentWidth < flickable.width; + } + function needsMoreVerticalResults() { + return flickable + && currentPageToRetrieve > 0 + && flickable.contentHeight < flickable.height; + } + function getNextPageIfNotEnoughHorizontalResults() { + if (needsMoreHorizontalResults()) { + getNextPage(); + } + } + function getNextPageIfNotEnoughVerticalResults() { + if (needsMoreVerticalResults()) { + getNextPage(); + } + } + Component.onCompleted: { initialized = true; if (flickable && pageAhead > 0.0) { // Pun: Scrollers are usually one direction or another, such that only one of the following will actually fire. flickable.contentXChanged.connect(getNextPageIfHorizontalScroll); flickable.contentYChanged.connect(getNextPageIfVerticalScroll); + flickable.contentWidthChanged.connect(getNextPageIfNotEnoughHorizontalResults); + flickable.contentHeightChanged.connect(getNextPageIfNotEnoughVerticalResults); } } From 6f1487bbf8f779f5876c812e2543001eed228c56 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 1 Aug 2018 17:11:26 -0700 Subject: [PATCH 50/57] Don't explicitly use data method of shared node pointer --- assignment-client/src/entities/EntityTreeSendThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 7e4ccc88cd..f7ca05fbf2 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -165,7 +165,7 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, sizeof(OCTREE_PACKET_SEQUENCE), true); initialCompletion->writePrimitive(OCTREE_PACKET_SEQUENCE(nodeData->getSequenceNumber() - 1U)); - DependencyManager::get()->sendPacket(std::move(initialCompletion), *node.data()); + DependencyManager::get()->sendPacket(std::move(initialCompletion), *node); } return sendComplete; From e336c9276dde27f5e3f6ca73f4e08d6f3c37c508 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 Aug 2018 17:39:57 -0700 Subject: [PATCH 51/57] first set shape THEN use it --- interface/src/avatar/AvatarMotionState.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index bff5233f1d..07e6b3f6b0 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -181,15 +181,12 @@ float AvatarMotionState::getMass() const { void AvatarMotionState::cacheShapeDiameter() { if (_shape) { - // measure XZ diameter of capsule shape + // shape is capsuleY btVector3 aabbMin, aabbMax; btTransform transform; transform.setIdentity(); _shape->getAabb(transform, aabbMin, aabbMax); - aabbMax -= aabbMin; - aabbMax.setY(0.0f); - const float SQRT_TWO = 1.414213562f; - _diameter = SQRT_TWO * aabbMax.length(); + _diameter = (aabbMax - aabbMin).getX(); } else { _diameter = 0.0f; } @@ -204,6 +201,6 @@ void AvatarMotionState::setRigidBody(btRigidBody* body) { } void AvatarMotionState::setShape(const btCollisionShape* shape) { - cacheShapeDiameter(); ObjectMotionState::setShape(shape); + cacheShapeDiameter(); } From da252a70c1ddb3180929e8169c5b9e75b0931083 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 3 Aug 2018 09:22:29 -0700 Subject: [PATCH 52/57] Same as https://github.com/highfidelity/hifi/pull/13747 --- interface/src/avatar/AvatarManager.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index fab512f787..dbafd06611 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -412,9 +412,6 @@ void AvatarManager::clearOtherAvatars() { while (avatarIterator != _avatarHash.end()) { auto avatar = std::static_pointer_cast(avatarIterator.value()); if (avatar != _myAvatar) { - if (avatar->isInScene()) { - avatar->removeFromScene(avatar, scene, transaction); - } handleRemovedAvatar(avatar); avatarIterator = _avatarHash.erase(avatarIterator); } else { From 8e9c1e5c0a80b8d64d0d1a8116526d00f555cca2 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 3 Aug 2018 11:38:39 -0700 Subject: [PATCH 53/57] Fix out of date comment --- libraries/ui/src/ui/types/RequestFilters.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 9d831a1758..7f192d6e52 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -52,10 +52,11 @@ namespace { return false; } + // We can potentially add whitelisting logic or development environment variables that + // will allow people to override this setting on a per-client basis here. QString targetFilePath = QFileInfo(requestUrl.toLocalFile()).canonicalFilePath(); - // If we get here, then it's a local file that isn't whitelisted and the - // developer mode environment variable is not enabled. Block access to the file + // If we get here, we've determined it's a local file and we have no reason not to block it qWarning() << "Blocking web access to local file path" << targetFilePath; info.block(true); return true; @@ -101,4 +102,4 @@ void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info, QQmlConte info.setHttpHeader(CONTENT_HEADER.toLocal8Bit(), TYPE_VALUE.toLocal8Bit()); } } -#endif \ No newline at end of file +#endif From 2270255d88fdaa4c23051e9b45f48471c862717d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 4 Aug 2018 07:22:12 +1200 Subject: [PATCH 54/57] JSDoc review --- libraries/audio/src/SoundCacheScriptingInterface.h | 7 ++++--- libraries/script-engine/src/AudioScriptingInterface.h | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/audio/src/SoundCacheScriptingInterface.h b/libraries/audio/src/SoundCacheScriptingInterface.h index 9e660a2d04..c985e8c211 100644 --- a/libraries/audio/src/SoundCacheScriptingInterface.h +++ b/libraries/audio/src/SoundCacheScriptingInterface.h @@ -48,10 +48,11 @@ public: SoundCacheScriptingInterface(); /**jsdoc - * Loads the content of an audio file into an object, ready for playback by {@link Audio.playSound}. + * Loads the content of an audio file into a {@link SoundObject}, ready for playback by {@link Audio.playSound}. * @function SoundCache.getSound - * @param {string} url - The URL of the audio file to load. See {@link SoundObject} for supported formats. - * @returns {SoundObject} + * @param {string} url - The URL of the audio file to load — Web, ATP, or file. See {@link SoundObject} for supported + * formats. + * @returns {SoundObject} The sound ready for playback. */ Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); }; diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 72b9541763..1220a9b769 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -98,7 +98,8 @@ signals: void mutedByMixer(); /**jsdoc - * Triggered when everyone is muted by the mixer. + * Triggered when the client is muted by the mixer because they're within a certain radius (50m) of someone who requested + * the mute through Developer > Audio > Mute Environment. * @function Audio.environmentMuted * @returns {Signal} */ From fdd6b7e07b5e9b073d8acf447ba2665ed9ac1dad Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 4 Aug 2018 07:29:57 +1200 Subject: [PATCH 55/57] Delete erroneous merge file --- libraries/audio/src/SoundCache.h.orig | 97 --------------------------- 1 file changed, 97 deletions(-) delete mode 100644 libraries/audio/src/SoundCache.h.orig diff --git a/libraries/audio/src/SoundCache.h.orig b/libraries/audio/src/SoundCache.h.orig deleted file mode 100644 index f1ff40ae42..0000000000 --- a/libraries/audio/src/SoundCache.h.orig +++ /dev/null @@ -1,97 +0,0 @@ -// -// SoundCache.h -// libraries/audio/src -// -// Created by Stephen Birarda on 2014-11-13. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_SoundCache_h -#define hifi_SoundCache_h - -#include - -#include "Sound.h" - -class SoundCache : public ResourceCache, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - -public: -<<<<<<< .merge_file_a15692 - - // Properties are copied over from ResourceCache (see ResourceCache.h for reason). - - /**jsdoc - * API to manage sound cache resources. - * @namespace SoundCache - * - * @hifi-interface - * @hifi-client-entity - * @hifi-server-entity - * @hifi-assignment-client - * - * @property {number} numTotal - Total number of total resources. Read-only. - * @property {number} numCached - Total number of cached resource. Read-only. - * @property {number} sizeTotal - Size in bytes of all resources. Read-only. - * @property {number} sizeCached - Size in bytes of all cached resources. Read-only. - */ - - - // Functions are copied over from ResourceCache (see ResourceCache.h for reason). - - /**jsdoc - * Get the list of all resource URLs. - * @function SoundCache.getResourceList - * @returns {string[]} - */ - - /**jsdoc - * @function SoundCache.dirty - * @returns {Signal} - */ - - /**jsdoc - * @function SoundCache.updateTotalSize - * @param {number} deltaSize - */ - - /**jsdoc - * Prefetches a resource. - * @function SoundCache.prefetch - * @param {string} url - URL of the resource to prefetch. - * @param {object} [extra=null] - * @returns {ResourceObject} - */ - - /**jsdoc - * Asynchronously loads a resource from the specified URL and returns it. - * @function SoundCache.getResource - * @param {string} url - URL of the resource to load. - * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails. - * @param {} [extra=null] - * @returns {object} - */ - - - /**jsdoc - * Loads the content of an audio file into an object, ready for playback by {@link Audio.playSound}. - * @function SoundCache.getSound - * @param {string} url - The URL of the audio file to load. See {@link SoundObject} for supported formats. - * @returns {SoundObject} - */ -======= ->>>>>>> .merge_file_a28756 - Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); - -protected: - virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra) override; -private: - SoundCache(QObject* parent = NULL); -}; - -#endif // hifi_SoundCache_h From f30b552e1953ec77efcd0544bb2dc54a2c16aba4 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 3 Aug 2018 15:50:11 -0700 Subject: [PATCH 56/57] fix-flying --- interface/src/avatar/MyAvatar.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 98fbd8fea2..3f738ea4cb 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1135,7 +1135,6 @@ void MyAvatar::saveData() { settings.setValue("collisionSoundURL", _collisionSoundURL); settings.setValue("useSnapTurn", _useSnapTurn); settings.setValue("userHeight", getUserHeight()); - settings.setValue("flyingDesktop", getFlyingDesktopPref()); settings.setValue("flyingHMD", getFlyingHMDPref()); settings.endGroup(); @@ -1289,7 +1288,6 @@ void MyAvatar::loadData() { // Flying preferences must be loaded before calling setFlyingEnabled() Setting::Handle firstRunVal { Settings::firstRun, true }; - setFlyingDesktopPref(firstRunVal.get() ? true : settings.value("flyingDesktop").toBool()); setFlyingHMDPref(firstRunVal.get() ? false : settings.value("flyingHMD").toBool()); setFlyingEnabled(getFlyingEnabled()); From 9bebd1e6232e1b8bdcf7b32356c394fb4d429039 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 3 Aug 2018 18:11:55 -0700 Subject: [PATCH 57/57] making changes --- .../qml/hifi/tablet/ControllerSettings.qml | 2 +- interface/src/ui/PreferencesDialog.cpp | 38 +------------------ 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index 0a45feb61f..135c1379e2 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -299,7 +299,7 @@ Item { anchors.fill: stackView id: controllerPrefereneces objectName: "TabletControllerPreferences" - showCategories: [( (HMD.active) ? "VR Movement" : "Movement"), "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"] + showCategories: ["VR Movement", "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"] categoryProperties: { "VR Movement" : { "User real-world height (meters)" : { "anchors.right" : "undefined" }, diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 50a4d17cae..682dad74e8 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -266,42 +266,6 @@ void setupPreferences() { preferences->addPreference(new SliderPreference(FACE_TRACKING, "Eye Deflection", getter, setter)); } - static const QString MOVEMENT{ "Movement" }; - { - - static const QString movementsControlChannel = QStringLiteral("Hifi-Advanced-Movement-Disabler"); - auto getter = [=]()->bool { return myAvatar->useAdvancedMovementControls(); }; - auto setter = [=](bool value) { myAvatar->setUseAdvancedMovementControls(value); }; - preferences->addPreference(new CheckPreference(MOVEMENT, - QStringLiteral("Advanced movement for hand controllers"), - getter, setter)); - } - { - auto getter = [=]()->int { return myAvatar->getSnapTurn() ? 0 : 1; }; - auto setter = [=](int value) { myAvatar->setSnapTurn(value == 0); }; - auto preference = new RadioButtonsPreference(MOVEMENT, "Snap turn / Smooth turn", getter, setter); - QStringList items; - items << "Snap turn" << "Smooth turn"; - preference->setItems(items); - preferences->addPreference(preference); - } - { - auto getter = [=]()->float { return myAvatar->getUserHeight(); }; - auto setter = [=](float value) { myAvatar->setUserHeight(value); }; - auto preference = new SpinnerPreference(MOVEMENT, "User real-world height (meters)", getter, setter); - preference->setMin(1.0f); - preference->setMax(2.2f); - preference->setDecimals(3); - preference->setStep(0.001f); - preferences->addPreference(preference); - } - { - auto preference = new ButtonPreference(MOVEMENT, "RESET SENSORS", [] { - qApp->resetSensors(); - }); - preferences->addPreference(preference); - } - static const QString VR_MOVEMENT{ "VR Movement" }; { @@ -315,7 +279,7 @@ void setupPreferences() { { auto getter = [=]()->bool { return myAvatar->getFlyingHMDPref(); }; auto setter = [=](bool value) { myAvatar->setFlyingHMDPref(value); }; - preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping", getter, setter)); + preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping (HMD)", getter, setter)); } { auto getter = [=]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };