From e88207a73e2b714e4efe36e2b7c00dfdba83b12f Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 29 Jun 2018 11:34:22 -0700 Subject: [PATCH 01/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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 81c8fe611445434203ce33612084ac34fa0d2e92 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 24 Jul 2018 17:06:09 -0700 Subject: [PATCH 16/33] 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 64fc3e1091c3ee4a7777c7641341117e54f3d624 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 31 Jul 2018 15:30:33 -0700 Subject: [PATCH 17/33] 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 18/33] 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 19/33] 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 20/33] 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 21/33] 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 0d92e2de0ac3dfe2cc75ac2b74950431e21af1e2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 Aug 2018 11:21:54 -0700 Subject: [PATCH 22/33] 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 23/33] 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 24/33] 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 25/33] 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 26/33] 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 e336c9276dde27f5e3f6ca73f4e08d6f3c37c508 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 Aug 2018 17:39:57 -0700 Subject: [PATCH 27/33] 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 28/33] 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 29/33] 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 30/33] 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 31/33] 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 32/33] 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 33/33] 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; };