From dbf4f2d23bda96b03ce3e708ec52299044928328 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 22 Dec 2016 21:58:10 -0500 Subject: [PATCH 01/45] mv shadow task setup to jobs --- .../render-utils/src/RenderShadowTask.cpp | 28 +++++++++++-------- libraries/render-utils/src/RenderShadowTask.h | 14 ++++++++++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index aed360823d..c92ebbeec0 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -115,6 +115,8 @@ RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) { skinProgram, state); } + const auto cachedMode = addJob("Setup"); + // CPU jobs: // Fetch and cull the items from the scene auto shadowFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); @@ -127,6 +129,8 @@ RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) { // GPU jobs: Render to shadow map addJob("RenderShadowMap", sortedShapes, shapePlumber); + + addJob("Teardown", cachedMode); } void RenderShadowTask::configure(const Config& configuration) { @@ -144,16 +148,18 @@ void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render return; } + for (auto job : _jobs) { + job.run(sceneContext, renderContext); + } +} + +void RenderShadowSetup::run(const SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, Output& output) { auto lightStage = DependencyManager::get()->getLightStage(); const auto globalShadow = lightStage->getShadow(0); - // If the global light is not set, bail - if (!globalShadow) { - return; - } - // Cache old render args - RenderArgs::RenderMode mode = args->_renderMode; + RenderArgs* args = renderContext->args; + output = args->_renderMode; auto nearClip = args->getViewFrustum().getNearClip(); float nearDepth = -args->_boomOffset.z; @@ -163,14 +169,12 @@ void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render // Set the keylight render args args->pushViewFrustum(*(globalShadow->getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; +} - // TODO: Allow runtime manipulation of culling ShouldRenderFunctor - - for (auto job : _jobs) { - job.run(sceneContext, renderContext); - } +void RenderShadowTeardown::run(const SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Input& input) { + RenderArgs* args = renderContext->args; // Reset the render args args->popViewFrustum(); - args->_renderMode = mode; + args->_renderMode = input; }; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 4a6ff1b0e9..8405f10c2a 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -52,4 +52,18 @@ public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); }; +class RenderShadowSetup { +public: + using Output = RenderArgs::RenderMode; + using JobModel = render::Job::ModelO; + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, Output& output); +}; + +class RenderShadowTeardown { +public: + using Input = RenderArgs::RenderMode; + using JobModel = render::Job::ModelI; + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Input& input); +}; + #endif // hifi_RenderShadowTask_h From 8d63067fa59ea2d4e168f96cbd2973f50dcd4e13 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 22 Dec 2016 22:21:51 -0500 Subject: [PATCH 02/45] templatize graphics engine tasks' run method --- .../render-utils/src/RenderDeferredTask.cpp | 20 ------------- .../render-utils/src/RenderDeferredTask.h | 1 - .../render-utils/src/RenderForwardTask.cpp | 20 ------------- .../render-utils/src/RenderForwardTask.h | 1 - .../render-utils/src/RenderShadowTask.cpp | 14 --------- libraries/render-utils/src/RenderShadowTask.h | 1 - libraries/render/src/render/Engine.cpp | 8 ----- libraries/render/src/render/Engine.h | 4 +-- libraries/render/src/render/Task.h | 29 ++++++++++--------- 9 files changed, 18 insertions(+), 80 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index d8f3b325f1..4296d5233a 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -228,26 +228,6 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { } -void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - // sanity checks - assert(sceneContext); - if (!sceneContext->_scene) { - return; - } - - - // Is it possible that we render without a viewFrustum ? - if (!(renderContext->args && renderContext->args->hasViewFrustum())) { - return; - } - - auto config = std::static_pointer_cast(renderContext->jobConfig); - - for (auto job : _jobs) { - job.run(sceneContext, renderContext); - } -} - void BeginGPURangeTimer::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) { timer = _gpuTimer; gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index fb15e34569..c2e9fb8b5e 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -200,7 +200,6 @@ public: RenderDeferredTask(render::CullFunctor cullFunctor); void configure(const Config& config) {} - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); using JobModel = Model; diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 4e971c6677..1f007678b9 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -86,26 +86,6 @@ RenderForwardTask::RenderForwardTask(CullFunctor cullFunctor) { addJob("Blit", framebuffer); } -void RenderForwardTask::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - // sanity checks - assert(sceneContext); - if (!sceneContext->_scene) { - return; - } - - - // Is it possible that we render without a viewFrustum ? - if (!(renderContext->args && renderContext->args->hasViewFrustum())) { - return; - } - - auto config = std::static_pointer_cast(renderContext->jobConfig); - - for (auto job : _jobs) { - job.run(sceneContext, renderContext); - } -} - void PrepareFramebuffer::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, gpu::FramebufferPointer& framebuffer) { auto framebufferCache = DependencyManager::get(); auto framebufferSize = framebufferCache->getFrameBufferSize(); diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index a4839e18ec..6c392643eb 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -24,7 +24,6 @@ public: RenderForwardTask(render::CullFunctor cullFunctor); void configure(const Config& config) {} - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); using JobModel = Model; }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index c92ebbeec0..eb46b8f46e 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -139,20 +139,6 @@ void RenderShadowTask::configure(const Config& configuration) { Task::configure(configuration); } -void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(sceneContext); - RenderArgs* args = renderContext->args; - - // sanity checks - if (!sceneContext->_scene || !args) { - return; - } - - for (auto job : _jobs) { - job.run(sceneContext, renderContext); - } -} - void RenderShadowSetup::run(const SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, Output& output) { auto lightStage = DependencyManager::get()->getLightStage(); const auto globalShadow = lightStage->getShadow(0); diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 8405f10c2a..d2e58f8362 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -49,7 +49,6 @@ public: RenderShadowTask(render::CullFunctor shouldRender); void configure(const Config& configuration); - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); }; class RenderShadowSetup { diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index 3c9f2b643a..970cc142e0 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -52,11 +52,3 @@ void Engine::load() { } } } - -void Engine::run() { - for (auto job : _jobs) { - job.run(_sceneContext, _renderContext); - } - -} - diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index d2bb42e5ff..de8340c33e 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -39,8 +39,8 @@ namespace render { RenderContextPointer getRenderContext() const { return _renderContext; } // Render a frame - // A frame must have a scene registered and a context set to render - void run(); + // Must have a scene registered and a context set + void run() { assert(_sceneContext && _renderContext); Task::run(_sceneContext, _renderContext); } protected: SceneContextPointer _sceneContext; diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index 68643110b8..27538127ca 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -593,22 +593,20 @@ public: using QConfigPointer = Job::QConfigPointer; using None = Job::None; - template class Model : public Job::Concept { + template class Model : public Job::Concept { public: using Data = T; - using Input = I; + using Input = None; using Output = O; Data _data; - Varying _input; Varying _output; - const Varying getInput() const override { return _input; } const Varying getOutput() const override { return _output; } template Model(const Varying& input, A&&... args) : - Concept(nullptr), _data(Data(std::forward(args)...)), _input(input), _output(Output()) { + Concept(nullptr), _data(Data(std::forward(args)...)), _output(Output()) { // Recreate the Config to use the templated type _data.template createConfiguration(); _config = _data.getConfiguration(); @@ -620,16 +618,15 @@ public: } void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) override { - renderContext->jobConfig = std::static_pointer_cast(_config); - if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { - jobRun(_data, sceneContext, renderContext, _input.get(), _output.edit()); + auto config = std::static_pointer_cast(_config); + if (config->alwaysEnabled || config->enabled) { + for (auto job : _data._jobs) { + job.run(sceneContext, renderContext); + } } - renderContext->jobConfig.reset(); } }; - template using ModelI = Model; - template using ModelO = Model; - template using ModelIO = Model; + template using ModelO = Model; using Jobs = std::vector; @@ -688,8 +685,14 @@ public: } } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + for (auto job : _jobs) { + job.run(sceneContext, renderContext); + } + } + protected: - template friend class Model; + template friend class Model; QConfigPointer _config; Jobs _jobs; From 0fa3439949df5d4ad17c81f04159d5d699a17bd3 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 23 Dec 2016 15:35:28 -0500 Subject: [PATCH 03/45] add setOutput for graphics engine tasks --- libraries/render/src/render/Task.h | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index 27538127ca..49273d79a5 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -45,8 +45,9 @@ public: } template Varying(const T& data) : _concept(std::make_shared>(data)) {} - template T& edit() { return std::static_pointer_cast>(_concept)->_data; } + template bool canCast() const { return !!std::dynamic_pointer_cast>(_concept); } template const T& get() const { return std::static_pointer_cast>(_concept)->_data; } + template T& edit() { return std::static_pointer_cast>(_concept)->_data; } // access potential sub varyings contained in this one. @@ -440,6 +441,9 @@ template void jobConfigure(T& data, const C& configuration) { template void jobConfigure(T&, const JobConfig&) { // nop, as the default JobConfig was used, so the data does not need a configure method } +template void jobConfigure(T&, const TaskConfig&) { + // nop, as the default TaskConfig was used, so the data does not need a configure method +} template void jobRun(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const JobNoIO& input, JobNoIO& output) { data.run(sceneContext, renderContext); } @@ -591,22 +595,20 @@ class Task { public: using Config = TaskConfig; using QConfigPointer = Job::QConfigPointer; - using None = Job::None; - template class Model : public Job::Concept { + template class Model : public Job::Concept { public: using Data = T; - using Input = None; - using Output = O; + using Config = C; + using Input = Job::None; Data _data; - Varying _output; - const Varying getOutput() const override { return _output; } + const Varying getOutput() const override { return _data._output; } template Model(const Varying& input, A&&... args) : - Concept(nullptr), _data(Data(std::forward(args)...)), _output(Output()) { + Concept(nullptr), _data(Data(std::forward(args)...)) { // Recreate the Config to use the templated type _data.template createConfiguration(); _config = _data.getConfiguration(); @@ -626,7 +628,7 @@ public: } } }; - template using ModelO = Model; + template using ModelO = Model; using Jobs = std::vector; @@ -652,6 +654,10 @@ public: return addJob(name, input, std::forward(args)...); } + template void setOutput(O&& output) { + _output = Varying(output); + } + template void createConfiguration() { auto config = std::make_shared(); if (_config) { @@ -692,10 +698,11 @@ public: } protected: - template friend class Model; + template friend class Model; QConfigPointer _config; Jobs _jobs; + Varying _output; }; } From ca1a14e5b0ca9d3c8cdef0acb5e9cc66aac1d589 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 23 Dec 2016 15:54:22 -0500 Subject: [PATCH 04/45] add RenderFetchSortCull task --- interface/src/Application.cpp | 7 ++- .../render-utils/src/RenderDeferredTask.cpp | 51 ++++------------ .../render-utils/src/RenderDeferredTask.h | 14 +---- .../render-utils/src/RenderForwardTask.cpp | 50 +++------------- .../render-utils/src/RenderForwardTask.h | 13 ++-- .../src/render/RenderFetchSortCullTask.cpp | 60 +++++++++++++++++++ .../src/render/RenderFetchSortCullTask.h | 28 +++++++++ tests/render-perf/src/main.cpp | 15 ++++- 8 files changed, 130 insertions(+), 108 deletions(-) create mode 100644 libraries/render/src/render/RenderFetchSortCullTask.cpp create mode 100644 libraries/render/src/render/RenderFetchSortCullTask.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b4b0ad10bb..81053a708c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -101,6 +101,7 @@ #include #include #include +#include #include #include #include @@ -1818,11 +1819,13 @@ void Application::initializeGL() { // Set up the render engine render::CullFunctor cullFunctor = LODManager::shouldRender; _renderEngine->addJob("RenderShadowTask", cullFunctor); + const auto items = _renderEngine->addJob("FetchSortCull", cullFunctor); + assert(items.canCast()); static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { - _renderEngine->addJob("RenderForwardTask", cullFunctor); + _renderEngine->addJob("RenderForwardTask", items.get()); } else { - _renderEngine->addJob("RenderDeferredTask", cullFunctor); + _renderEngine->addJob("RenderDeferredTask", items.get()); } _renderEngine->load(); _renderEngine->registerScene(_main3DScene); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 4296d5233a..4a99795a70 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -48,49 +48,18 @@ using namespace render; extern void initOverlay3DPipelines(render::ShapePlumber& plumber); extern void initDeferredPipelines(render::ShapePlumber& plumber); -RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { - cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; - +RenderDeferredTask::RenderDeferredTask(RenderFetchSortCullTask::Output items) { // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); initDeferredPipelines(*shapePlumber); - // CPU jobs: - // Fetch and cull the items from the scene - auto spatialFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); - const auto spatialSelection = addJob("FetchSceneSelection", spatialFilter); - const auto culledSpatialSelection = addJob("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter); - - // Overlays are not culled - const auto nonspatialSelection = addJob("FetchOverlaySelection"); - - // Multi filter visible items into different buckets - const int NUM_FILTERS = 3; - const int OPAQUE_SHAPE_BUCKET = 0; - const int TRANSPARENT_SHAPE_BUCKET = 1; - const int LIGHT_BUCKET = 2; - const int BACKGROUND_BUCKET = 2; - MultiFilterItem::ItemFilterArray spatialFilters = { { - ItemFilter::Builder::opaqueShape(), - ItemFilter::Builder::transparentShape(), - ItemFilter::Builder::light() - } }; - MultiFilterItem::ItemFilterArray nonspatialFilters = { { - ItemFilter::Builder::opaqueShape(), - ItemFilter::Builder::transparentShape(), - ItemFilter::Builder::background() - } }; - const auto filteredSpatialBuckets = addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters).get::ItemBoundsArray>(); - const auto filteredNonspatialBuckets = addJob>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters).get::ItemBoundsArray>(); - - // Extract / Sort opaques / Transparents / Lights / Overlays - const auto opaques = addJob("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]); - const auto transparents = addJob("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); - const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; - - const auto overlayOpaques = addJob("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); - const auto overlayTransparents = addJob("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); - const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; + // Extract opaques / transparents / lights / overlays + const auto opaques = items[0]; + const auto transparents = items[1]; + const auto lights = items[2]; + const auto overlayOpaques = items[3]; + const auto overlayTransparents = items[4]; + const auto background = items[5]; // Prepare deferred, generate the shared Deferred Frame Transform const auto deferredFrameTransform = addJob("DeferredFrameTransform"); @@ -202,8 +171,8 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Scene Octree Debuging job { - addJob("DrawSceneOctree", spatialSelection); - addJob("DrawItemSelection", spatialSelection); + // addJob("DrawSceneOctree", spatialSelection); + // addJob("DrawItemSelection", spatialSelection); } // Status icon rendering job diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index c2e9fb8b5e..a52741f2a3 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -13,7 +13,7 @@ #define hifi_RenderDeferredTask_h #include -#include +#include #include "LightingModel.h" @@ -192,19 +192,11 @@ public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer); }; -using RenderDeferredTaskConfig = render::GPUTaskConfig; - class RenderDeferredTask : public render::Task { public: - using Config = RenderDeferredTaskConfig; - RenderDeferredTask(render::CullFunctor cullFunctor); + using JobModel = Model; - void configure(const Config& config) {} - - using JobModel = Model; - -protected: - gpu::RangeTimerPointer _gpuTimer; + RenderDeferredTask(RenderFetchSortCullTask::Output items); }; #endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 1f007678b9..2d96dc2e38 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -19,9 +19,6 @@ #include #include -#include -#include - #include "FramebufferCache.h" #include "TextureCache.h" @@ -34,49 +31,18 @@ using namespace render; extern void initOverlay3DPipelines(render::ShapePlumber& plumber); extern void initDeferredPipelines(render::ShapePlumber& plumber); -RenderForwardTask::RenderForwardTask(CullFunctor cullFunctor) { +RenderForwardTask::RenderForwardTask(RenderFetchSortCullTask::Output items) { // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); initDeferredPipelines(*shapePlumber); - // CPU jobs: - // Fetch and cull the items from the scene - const auto spatialSelection = addJob("FetchSceneSelection"); - - cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; - auto spatialFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); - const auto culledSpatialSelection = addJob("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter); - - // Overlays are not culled - const auto nonspatialSelection = addJob("FetchOverlaySelection"); - - // Multi filter visible items into different buckets - const int NUM_FILTERS = 3; - const int OPAQUE_SHAPE_BUCKET = 0; - const int TRANSPARENT_SHAPE_BUCKET = 1; - const int LIGHT_BUCKET = 2; - const int BACKGROUND_BUCKET = 2; - MultiFilterItem::ItemFilterArray spatialFilters = { { - ItemFilter::Builder::opaqueShape(), - ItemFilter::Builder::transparentShape(), - ItemFilter::Builder::light() - } }; - MultiFilterItem::ItemFilterArray nonspatialFilters = { { - ItemFilter::Builder::opaqueShape(), - ItemFilter::Builder::transparentShape(), - ItemFilter::Builder::background() - } }; - const auto filteredSpatialBuckets = addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters).get::ItemBoundsArray>(); - const auto filteredNonspatialBuckets = addJob>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters).get::ItemBoundsArray>(); - - // Extract / Sort opaques / Transparents / Lights / Overlays - const auto opaques = addJob("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]); - const auto transparents = addJob("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); - const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; - - const auto overlayOpaques = addJob("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); - const auto overlayTransparents = addJob("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); - const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; + // Extract opaques / transparents / lights / overlays + const auto opaques = items[0]; + const auto transparents = items[1]; + const auto lights = items[2]; + const auto overlayOpaques = items[3]; + const auto overlayTransparents = items[4]; + const auto background = items[5]; const auto framebuffer = addJob("PrepareFramebuffer"); diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 6c392643eb..8615ecbec3 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -13,19 +13,14 @@ #define hifi_RenderForwardTask_h #include -#include +#include #include "LightingModel.h" -using RenderForwardTaskConfig = render::GPUTaskConfig; - class RenderForwardTask : public render::Task { public: - using Config = RenderForwardTaskConfig; - RenderForwardTask(render::CullFunctor cullFunctor); + using JobModel = Model; - void configure(const Config& config) {} - - using JobModel = Model; + RenderForwardTask(RenderFetchSortCullTask::Output items); }; class PrepareFramebuffer { @@ -52,4 +47,4 @@ private: int _scaleLocation { -1 }; }; -#endif // hifi_RenderForwardTask_h \ No newline at end of file +#endif // hifi_RenderForwardTask_h diff --git a/libraries/render/src/render/RenderFetchSortCullTask.cpp b/libraries/render/src/render/RenderFetchSortCullTask.cpp new file mode 100644 index 0000000000..43066e4f1b --- /dev/null +++ b/libraries/render/src/render/RenderFetchSortCullTask.cpp @@ -0,0 +1,60 @@ +// +// RenderFetchSortCullTask.cpp +// render/src/ +// +// Created by Zach Pomerantz on 12/22/2016. +// 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 "RenderFetchSortCullTask.h" + +#include "CullTask.h" +#include "SortTask.h" + +using namespace render; + +RenderFetchSortCullTask::RenderFetchSortCullTask(CullFunctor cullFunctor) { + cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; + + // CPU jobs: + // Fetch and cull the items from the scene + auto spatialFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); + const auto spatialSelection = addJob("FetchSceneSelection", spatialFilter); + const auto culledSpatialSelection = addJob("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter); + + // Overlays are not culled + const auto nonspatialSelection = addJob("FetchOverlaySelection"); + + // Multi filter visible items into different buckets + const int NUM_FILTERS = 3; + const int OPAQUE_SHAPE_BUCKET = 0; + const int TRANSPARENT_SHAPE_BUCKET = 1; + const int LIGHT_BUCKET = 2; + const int BACKGROUND_BUCKET = 2; + MultiFilterItem::ItemFilterArray spatialFilters = { { + ItemFilter::Builder::opaqueShape(), + ItemFilter::Builder::transparentShape(), + ItemFilter::Builder::light() + } }; + MultiFilterItem::ItemFilterArray nonspatialFilters = { { + ItemFilter::Builder::opaqueShape(), + ItemFilter::Builder::transparentShape(), + ItemFilter::Builder::background() + } }; + const auto filteredSpatialBuckets = addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters).get::ItemBoundsArray>(); + const auto filteredNonspatialBuckets = addJob>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters).get::ItemBoundsArray>(); + + // Extract opaques / transparents / lights / overlays + const auto opaques = addJob("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]); + const auto transparents = addJob("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); + const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; + + const auto overlayOpaques = addJob("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); + const auto overlayTransparents = addJob("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); + const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; + + setOutput(Output{{ opaques, transparents, lights, overlayOpaques, overlayTransparents, background }}); +} diff --git a/libraries/render/src/render/RenderFetchSortCullTask.h b/libraries/render/src/render/RenderFetchSortCullTask.h new file mode 100644 index 0000000000..17d9f1a005 --- /dev/null +++ b/libraries/render/src/render/RenderFetchSortCullTask.h @@ -0,0 +1,28 @@ +// +// RenderFetchSortCullTask.h +// render/src/ +// +// Created by Zach Pomerantz on 12/22/2016. +// 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 +// + +#ifndef hifi_RenderFetchSortCullTask_h +#define hifi_RenderFetchSortCullTask_h + +#include + +#include "Task.h" +#include "CullTask.h" + +class RenderFetchSortCullTask : public render::Task { +public: + using Output = std::array; + using JobModel = ModelO; + + RenderFetchSortCullTask(render::CullFunctor cullFunctor); +}; + +#endif // hifi_RenderFetchSortCullTask_h diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index bd731564e7..3f6d8b29b0 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include #include #include @@ -23,7 +25,6 @@ #include #include - #include #include #include @@ -33,7 +34,6 @@ #include #include - #include #include #include @@ -59,8 +59,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -534,7 +536,14 @@ public: _initContext.makeCurrent(); // Render engine init _renderEngine->addJob("RenderShadowTask", _cullFunctor); - _renderEngine->addJob("RenderDeferredTask", _cullFunctor); + const auto items = _renderEngine->addJob("FetchSortCull", _cullFunctor); + assert(items.canCast()); + static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; + if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { + _renderEngine->addJob("RenderForwardTask", items.get()); + } else { + _renderEngine->addJob("RenderDeferredTask", items.get()); + } _renderEngine->load(); _renderEngine->registerScene(_main3DScene); From 0ad9786f6b36f2422a8ae9195a10ec5524006bee Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 21 Dec 2016 16:12:00 -0800 Subject: [PATCH 05/45] requestsDomainsListData --- assignment-client/src/avatars/AvatarMixer.cpp | 34 ++++++++++++++++--- assignment-client/src/avatars/AvatarMixer.h | 1 + .../src/avatars/AvatarMixerClientData.h | 3 ++ libraries/avatars/src/AvatarHashMap.cpp | 5 +-- libraries/networking/src/NodeList.cpp | 16 +++++++++ libraries/networking/src/NodeList.h | 3 ++ libraries/networking/src/udt/PacketHeaders.h | 3 +- .../src/UsersScriptingInterface.cpp | 7 ++++ .../src/UsersScriptingInterface.h | 6 ++++ scripts/system/pal.js | 2 ++ 10 files changed, 72 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index d8d0b10fea..2aa140470e 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -50,6 +50,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); + packetReceiver.registerListener(PacketType::RequestDomainListData, this, "handleRequestDomainListDataPacket"); auto nodeList = DependencyManager::get(); connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &AvatarMixer::handlePacketVersionMismatch); @@ -205,6 +206,11 @@ void AvatarMixer::broadcastAvatarData() { // use the data rate specifically for avatar data for FRD adjustment checks float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps(); + // send extra data that is otherwise surpressed + bool getsOutOfView = nodeData->getRequestsDomainListData(); + bool getsAnyIgnored = node->getCanKick(); + bool getsIgnoredByMe = getsAnyIgnored || nodeData->getRequestsDomainListData(); + // Check if it is time to adjust what we send this client based on the observed // bandwidth to this node. We do this once a second, which is also the window for // the bandwidth reported by node->getOutboundBandwidth(); @@ -275,14 +281,14 @@ void AvatarMixer::broadcastAvatarData() { // or that has ignored the viewing node if (!otherNode->getLinkedData() || otherNode->getUUID() == node->getUUID() - || node->isIgnoringNodeWithID(otherNode->getUUID()) - || otherNode->isIgnoringNodeWithID(node->getUUID())) { + || (node->isIgnoringNodeWithID(otherNode->getUUID()) && !getsIgnoredByMe) + || (otherNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { return false; } else { AvatarMixerClientData* otherData = reinterpret_cast(otherNode->getLinkedData()); AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); // Check to see if the space bubble is enabled - if (node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled()) { + if ((node->isIgnoreRadiusEnabled() && !getsIgnoredByMe) || (otherNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { // Define the minimum bubble size static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f); // Define the scale of the box for the current node @@ -333,7 +339,7 @@ void AvatarMixer::broadcastAvatarData() { && (forceSend || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { - + qDebug() << "FIXME HRS sending identity to" << node->getUUID() << "from" << otherNode->getUUID(); sendIdentityPacket(otherNodeData, node); } @@ -349,6 +355,7 @@ void AvatarMixer::broadcastAvatarData() { maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar); if (distanceToAvatar != 0.0f + && !getsOutOfView && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { return; } @@ -388,7 +395,7 @@ void AvatarMixer::broadcastAvatarData() { AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); AvatarData::AvatarDataDetail detail; - if (!nodeData->otherAvatarInView(otherNodeBox)) { + if (!nodeData->otherAvatarInView(otherNodeBox) && !getsOutOfView) { detail = AvatarData::MinimumData; nodeData->incrementAvatarOutOfView(); } else { @@ -396,6 +403,7 @@ void AvatarMixer::broadcastAvatarData() { ? AvatarData::SendAllData : AvatarData::IncludeSmallData; nodeData->incrementAvatarInView(); } + //qDebug() << "FIXME HRS sending" << detail << "to" << node->getUUID() << "from" << otherNode->getUUID(); numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += avatarPacketList->write(otherAvatar.toByteArray(detail)); @@ -527,6 +535,22 @@ void AvatarMixer::handleViewFrustumPacket(QSharedPointer messag } } +void AvatarMixer::handleRequestDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode) { + auto nodeList = DependencyManager::get(); + nodeList->getOrCreateLinkedData(senderNode); + qDebug() << "HRS FIXME received requestDomainListData packet from" << senderNode->getUUID(); + + if (senderNode->getLinkedData()) { + AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData()); + if (nodeData != nullptr) { + bool isRequesting; + message->readPrimitive(&isRequesting); + qDebug() << "HRS FIXME handling requestDomainListData packet" << isRequesting << "from" << nodeData->getNodeID(); + nodeData->setRequestDomainListData(isRequesting); + } + } +} + void AvatarMixer::handleAvatarDataPacket(QSharedPointer message, SharedNodePointer senderNode) { auto nodeList = DependencyManager::get(); nodeList->updateNodeWithDataFromPacket(message, senderNode); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 521fe72f39..66cf9470c0 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -42,6 +42,7 @@ private slots: void handleKillAvatarPacket(QSharedPointer message); void handleNodeIgnoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handleRequestDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode); void domainSettingsRequestComplete(); void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 78a30d8206..462968eb37 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -101,6 +101,8 @@ public: void incrementAvatarOutOfView() { _recentOtherAvatarsOutOfView++; } const QString& getBaseDisplayName() { return _baseDisplayName; } void setBaseDisplayName(const QString& baseDisplayName) { _baseDisplayName = baseDisplayName; } + bool getRequestsDomainListData() { return _requestsDomainListData; } + void setRequestDomainListData(bool requesting) { _requestsDomainListData = requesting; } private: AvatarSharedPointer _avatar { new AvatarData() }; @@ -129,6 +131,7 @@ private: int _recentOtherAvatarsInView { 0 }; int _recentOtherAvatarsOutOfView { 0 }; QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary. + bool _requestsDomainListData { false }; }; #endif // hifi_AvatarMixerClientData_h diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 8871769261..c0aafa7f2b 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -112,7 +112,7 @@ void AvatarHashMap::processAvatarDataPacket(QSharedPointer mess // make sure this isn't our own avatar data or for a previously ignored node auto nodeList = DependencyManager::get(); - if (sessionUUID != _lastOwnerSessionUUID && !nodeList->isIgnoringNode(sessionUUID)) { + if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode); // have the matching (or new) avatar parse the data from the packet @@ -145,7 +145,8 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer identity.uuid = EMPTY; } } - if (!nodeList->isIgnoringNode(identity.uuid)) { + qDebug() << "FIXME HRS processing identity packet regarding" << identity.uuid << "ignoring:" << nodeList->isIgnoringNode(identity.uuid) << "reqestsDomainList:" << nodeList->getRequestsDomainListData(); + if (!nodeList->isIgnoringNode(identity.uuid) || nodeList->getRequestsDomainListData()) { // mesh URL for a UUID, find avatar in our list auto avatar = newOrExistingAvatar(identity.uuid, sendingNode); avatar->processAvatarIdentity(identity); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 27b3e11fda..d10e954f5d 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -930,3 +930,19 @@ void NodeList::processUsernameFromIDReply(QSharedPointer messag emit usernameFromIDReply(nodeUUIDString, username, machineFingerprintString); } + +void NodeList::setRequestsDomainListData(bool isRequesting) { + // Tell the avatar mixer whether I want to receive any additional data to which I might be entitiled . + if (_requestsDomainListData == isRequesting) { + return; + } + eachMatchingNode([](const SharedNodePointer& node)->bool { + return node->getType() == NodeType::AvatarMixer; + }, [this, isRequesting](const SharedNodePointer& destinationNode) { + auto packet = NLPacket::create(PacketType::RequestDomainListData, sizeof(bool), true); // reliable + packet->writePrimitive(isRequesting); + sendPacket(std::move(packet), *destinationNode); + qDebug() << "HRS FIXME sending requestDomainListData packet" << isRequesting; + }); + _requestsDomainListData = isRequesting; +} \ No newline at end of file diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index d3f04cedd8..9289d2c660 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -82,6 +82,8 @@ public: void kickNodeBySessionID(const QUuid& nodeID); void muteNodeBySessionID(const QUuid& nodeID); void requestUsernameFromSessionID(const QUuid& nodeID); + bool getRequestsDomainListData() { return _requestsDomainListData; } + void setRequestsDomainListData(bool isRequesting); public slots: void reset(); @@ -153,6 +155,7 @@ private: HifiSockAddr _assignmentServerSocket; bool _isShuttingDown { false }; QTimer _keepAlivePingTimer; + bool _requestsDomainListData; mutable QReadWriteLock _ignoredSetLock; tbb::concurrent_unordered_set _ignoredNodeIDs; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 162e565b83..ed40f1bdad 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -104,7 +104,8 @@ public: UsernameFromIDRequest, UsernameFromIDReply, ViewFrustum, - LAST_PACKET_TYPE = ViewFrustum + RequestDomainListData, + LAST_PACKET_TYPE = ViewFrustum // FIXME! RequestDomainListData }; }; diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 191952e354..61120b6e89 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -61,3 +61,10 @@ void UsersScriptingInterface::disableIgnoreRadius() { bool UsersScriptingInterface::getIgnoreRadiusEnabled() { return DependencyManager::get()->getIgnoreRadiusEnabled(); } + +bool UsersScriptingInterface::getRequestsDomainListData() { + return DependencyManager::get()->getRequestsDomainListData(); +} +void UsersScriptingInterface::setRequestsDomainListData(bool isRequesting) { + DependencyManager::get()->setRequestsDomainListData(isRequesting); +} \ No newline at end of file diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 855dc06c11..0ebd8797c0 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -24,6 +24,7 @@ class UsersScriptingInterface : public QObject, public Dependency { SINGLETON_DEPENDENCY Q_PROPERTY(bool canKick READ getCanKick) + Q_PROPERTY(bool requestsDomainListData READ getRequestsDomainListData WRITE setRequestsDomainListData) public: UsersScriptingInterface(); @@ -105,6 +106,11 @@ signals: * @function Users.usernameFromIDReply */ void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); + +private: + bool getRequestsDomainListData(); + void setRequestsDomainListData(bool requests); + bool _requestsDomainListData; }; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 9c23e1f775..77f939d2c5 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -252,9 +252,11 @@ function off() { } triggerMapping.disable(); // It's ok if we disable twice. removeOverlays(); + Users.requestsDomainListData = false; } function onClicked() { if (!pal.visible) { + Users.requestsDomainListData = true; populateUserList(); pal.raise(); isWired = true; From 344272f24a18fc50c5fe440e28d8888ef4d4b846 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Dec 2016 09:16:07 -0800 Subject: [PATCH 06/45] unignore checkpoint (untested) --- assignment-client/src/audio/AudioMixer.cpp | 5 +++++ assignment-client/src/audio/AudioMixer.h | 1 + assignment-client/src/avatars/AvatarMixer.cpp | 3 +++ assignment-client/src/avatars/AvatarMixer.h | 1 + libraries/networking/src/Node.cpp | 20 +++++++++++++++++++ libraries/networking/src/Node.h | 6 +++++- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 7 files changed, 37 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 01715497b1..9a80289911 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -65,6 +65,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::NegotiateAudioFormat, this, "handleNegotiateAudioFormat"); packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); + packetReceiver.registerListener(PacketType::NodeUnignoreRequest, this, "handleNodeUnignoreRequestPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); @@ -225,6 +226,10 @@ void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer p sendingNode->parseIgnoreRequestMessage(packet); } +void AudioMixer::handleNodeUnignoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + sendingNode->parseUnignoreRequestMessage(packet); +} + void AudioMixer::handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { sendingNode->parseIgnoreRadiusRequestMessage(packet); } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 59cdec7732..14a0167c3e 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -62,6 +62,7 @@ private slots: void handleNegotiateAudioFormat(QSharedPointer message, SharedNodePointer sendingNode); void handleNodeKilled(SharedNodePointer killedNode); void handleNodeIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handleNodeUnignoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleKillAvatarPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodeMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 2aa140470e..64353e65b1 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -585,6 +585,9 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer senderNode->parseIgnoreRequestMessage(message); } +void AvatarMixer::handleNodeUnignoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + sendingNode->parseUnignoreRequestMessage(packet); +} void AvatarMixer::handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { sendingNode->parseIgnoreRadiusRequestMessage(packet); } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 66cf9470c0..296b82f1aa 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -41,6 +41,7 @@ private slots: void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message); void handleNodeIgnoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); + void handleNodeUnignoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleRequestDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode); void domainSettingsRequestComplete(); diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 66086e583e..133c8adcab 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -90,8 +90,14 @@ void Node::parseIgnoreRequestMessage(QSharedPointer message) { } } +void Node::parseUnignoreRequestMessage(QSharedPointer message) { + QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + removeIgnoredNode(ignoredUUID); +} + void Node::addIgnoredNode(const QUuid& otherNodeID) { if (!otherNodeID.isNull() && otherNodeID != _uuid) { + QReadLocker lock { &_ignoredNodeIDSetLock }; qCDebug(networking) << "Adding" << uuidStringWithoutCurlyBraces(otherNodeID) << "to ignore set for" << uuidStringWithoutCurlyBraces(_uuid); @@ -102,6 +108,20 @@ void Node::addIgnoredNode(const QUuid& otherNodeID) { } } +void Node::removeIgnoredNode(const QUuid& otherNodeID) { + if (!otherNodeID.isNull() && otherNodeID != _uuid) { + // insert/find are read locked concurrently. unsafe_erase is not concurrent, and needs a write lock. + QWriteLocker lock { &_ignoredNodeIDSetLock }; + qCDebug(networking) << "Removing" << uuidStringWithoutCurlyBraces(otherNodeID) << "from ignore set for" + << uuidStringWithoutCurlyBraces(_uuid); + + // remove the session UUID from the set of ignored ones for this listening node + _ignoredNodeIDSet.unsafe_erase(otherNodeID); + } else { + qCWarning(networking) << "Node::addIgnoredNode called with null ID or ID of ignoring node."; + } +} + void Node::parseIgnoreRadiusRequestMessage(QSharedPointer message) { bool enabled; message->readPrimitive(&enabled); diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 2a64bb9943..0a344c9479 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -72,8 +73,10 @@ public: bool getCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } void parseIgnoreRequestMessage(QSharedPointer message); + void parseUnignoreRequestMessage(QSharedPointer message); void addIgnoredNode(const QUuid& otherNodeID); - bool isIgnoringNodeWithID(const QUuid& nodeID) const { return _ignoredNodeIDSet.find(nodeID) != _ignoredNodeIDSet.cend(); } + void removeIgnoredNode(const QUuid& otherNodeID); + bool isIgnoringNodeWithID(const QUuid& nodeID) const { QReadLocker lock { &_ignoredNodeIDSetLock }; return _ignoredNodeIDSet.find(nodeID) != _ignoredNodeIDSet.cend(); } void parseIgnoreRadiusRequestMessage(QSharedPointer message); friend QDataStream& operator<<(QDataStream& out, const Node& node); @@ -97,6 +100,7 @@ private: MovingPercentile _clockSkewMovingPercentile; NodePermissions _permissions; tbb::concurrent_unordered_set _ignoredNodeIDSet; + mutable QReadWriteLock _ignoredNodeIDSetLock; std::atomic_bool _ignoreRadiusEnabled; }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index ed40f1bdad..30bd6e8c54 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -105,7 +105,8 @@ public: UsernameFromIDReply, ViewFrustum, RequestDomainListData, - LAST_PACKET_TYPE = ViewFrustum // FIXME! RequestDomainListData + NodeUnignoreRequest, + LAST_PACKET_TYPE = ViewFrustum // FIXME! NodeUnignoreRequest }; }; From 2f2940b7cce9c121e7f708d769b2090a69468231 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 22 Dec 2016 11:10:27 -0800 Subject: [PATCH 07/45] fixes --- assignment-client/src/avatars/AvatarMixer.cpp | 7 ++-- libraries/networking/src/Node.cpp | 2 +- libraries/networking/src/NodeList.cpp | 32 +++++++++++++++++++ libraries/networking/src/NodeList.h | 2 ++ .../src/UsersScriptingInterface.cpp | 6 ++++ .../src/UsersScriptingInterface.h | 1 + 6 files changed, 46 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 64353e65b1..1192dfb200 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -49,6 +49,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); + packetReceiver.registerListener(PacketType::NodeUnignoreRequest, this, "handleNodeUnignoreRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::RequestDomainListData, this, "handleRequestDomainListDataPacket"); @@ -208,8 +209,8 @@ void AvatarMixer::broadcastAvatarData() { // send extra data that is otherwise surpressed bool getsOutOfView = nodeData->getRequestsDomainListData(); - bool getsAnyIgnored = node->getCanKick(); - bool getsIgnoredByMe = getsAnyIgnored || nodeData->getRequestsDomainListData(); + bool getsIgnoredByMe = nodeData->getRequestsDomainListData(); + bool getsAnyIgnored = getsIgnoredByMe && node->getCanKick(); // Check if it is time to adjust what we send this client based on the observed // bandwidth to this node. We do this once a second, which is also the window for @@ -339,7 +340,7 @@ void AvatarMixer::broadcastAvatarData() { && (forceSend || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { - qDebug() << "FIXME HRS sending identity to" << node->getUUID() << "from" << otherNode->getUUID(); + qDebug() << "FIXME HRS sending identity to" << node->getUUID() << "from" << otherNode->getUUID() << "gets mine/all/view:" << getsIgnoredByMe << getsAnyIgnored << getsOutOfView ; sendIdentityPacket(otherNodeData, node); } diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 133c8adcab..0f3a6ee30d 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -118,7 +118,7 @@ void Node::removeIgnoredNode(const QUuid& otherNodeID) { // remove the session UUID from the set of ignored ones for this listening node _ignoredNodeIDSet.unsafe_erase(otherNodeID); } else { - qCWarning(networking) << "Node::addIgnoredNode called with null ID or ID of ignoring node."; + qCWarning(networking) << "Node::removeIgnoredNode called with null ID or ID of ignoring node."; } } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index d10e954f5d..49c689b007 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -811,6 +811,38 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID) { } } +void NodeList::unignoreNodeBySessionID(const QUuid& nodeID) { + // enumerate the nodes to send a reliable ignore packet to each that can leverage it + if (!nodeID.isNull() && _sessionUUID != nodeID) { + eachMatchingNode([&nodeID](const SharedNodePointer& node)->bool { + if (node->getType() == NodeType::AudioMixer || node->getType() == NodeType::AvatarMixer) { + return true; + } else { + return false; + } + }, [&nodeID, this](const SharedNodePointer& destinationNode) { + // create a reliable NLPacket with space for the ignore UUID + auto ignorePacket = NLPacket::create(PacketType::NodeUnignoreRequest, NUM_BYTES_RFC4122_UUID, true); + + // write the node ID to the packet + ignorePacket->write(nodeID.toRfc4122()); + + qCDebug(networking) << "Sending packet to unignore node" << uuidStringWithoutCurlyBraces(nodeID); + + // send off this ignore packet reliably to the matching node + sendPacket(std::move(ignorePacket), *destinationNode); + }); + + QWriteLocker setLocker { &_ignoredSetLock }; // write lock for unsafe_erase + _ignoredNodeIDs.unsafe_erase(nodeID); + + emit unignoredNode(nodeID); + + } else { + qWarning() << "NodeList::unignoreNodeBySessionID called with an invalid ID or an ID which matches the current session ID."; + } +} + bool NodeList::isIgnoringNode(const QUuid& nodeID) const { QReadLocker setLocker { &_ignoredSetLock }; return _ignoredNodeIDs.find(nodeID) != _ignoredNodeIDs.cend(); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 9289d2c660..6d7026b562 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -77,6 +77,7 @@ public: void enableIgnoreRadius() { ignoreNodesInRadius(true); } void disableIgnoreRadius() { ignoreNodesInRadius(false); } void ignoreNodeBySessionID(const QUuid& nodeID); + void unignoreNodeBySessionID(const QUuid& nodeID); bool isIgnoringNode(const QUuid& nodeID) const; void kickNodeBySessionID(const QUuid& nodeID); @@ -112,6 +113,7 @@ signals: void limitOfSilentDomainCheckInsReached(); void receivedDomainServerList(); void ignoredNode(const QUuid& nodeID); + void unignoredNode(const QUuid& nodeID); void ignoreRadiusEnabledChanged(bool isIgnored); void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 61120b6e89..b118423236 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -26,6 +26,12 @@ void UsersScriptingInterface::ignore(const QUuid& nodeID) { DependencyManager::get()->ignoreNodeBySessionID(nodeID); } +void UsersScriptingInterface::unignore(const QUuid& nodeID) { + // ask the NodeList to ignore this user (based on the session ID of their node) + DependencyManager::get()->unignoreNodeBySessionID(nodeID); +} + + void UsersScriptingInterface::kick(const QUuid& nodeID) { // ask the NodeList to kick the user with the given session ID DependencyManager::get()->kickNodeBySessionID(nodeID); diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 0ebd8797c0..c8866376da 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -37,6 +37,7 @@ public slots: * @param {nodeID} nodeID The node or session ID of the user you want to ignore. */ void ignore(const QUuid& nodeID); + void unignore(const QUuid& nodeID); /**jsdoc * Kick another user. From 768b747b3aff236bb032837487b982861be68d28 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 22 Dec 2016 14:15:51 -0800 Subject: [PATCH 08/45] Make avatar go away when ignored, even when pal is up. (But currently, it comes back!) --- interface/resources/qml/hifi/Pal.qml | 32 ++++++++++++++++--- interface/src/avatar/Avatar.cpp | 8 +++++ interface/src/avatar/Avatar.h | 2 ++ interface/src/avatar/AvatarManager.cpp | 1 + interface/src/avatar/AvatarManager.h | 1 - .../src/UsersScriptingInterface.cpp | 2 ++ .../src/UsersScriptingInterface.h | 2 ++ scripts/system/pal.js | 7 ++++ 8 files changed, 49 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 4abaccfe2c..5c475f16dd 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -190,7 +190,7 @@ Item { id: nameCard // Properties displayName: styleData.value - userName: model.userName + userName: model && model.userName audioLevel: model.audioLevel visible: !isCheckBox // Size @@ -204,15 +204,26 @@ Item { HifiControls.CheckBox { visible: isCheckBox anchors.centerIn: parent + checked: model[styleData.role] boxSize: 24 onClicked: { var newValue = !model[styleData.role] var datum = userData[model.userIndex] datum[styleData.role] = model[styleData.role] = newValue - Users[styleData.role](model.sessionId) - // Just for now, while we cannot undo things: - userData.splice(model.userIndex, 1) - sortModel() + var key = styleData.role; + if (!newValue) { + key = 'un' + key; + } + if (styleData.role === 'ignore') { + if (newValue) { + ignored[datum.sessionId] = datum; + console.log('fixme hrs adding to ignored', JSON.stringify(datum), 'at', datum.sessionId); + } else { + delete ignored[datum.sessionId]; + } + } + console.log('fixme hrs pal action', key, model.sessionId); + Users[key](model.sessionId); } } } @@ -336,6 +347,7 @@ Item { property var userData: [] property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set property bool iAmAdmin: false + property var ignored: ({}); // FIXME: reset when changing domains function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml var i, data = optionalData || userData, length = data.length; for (var i = 0; i < length; i++) { @@ -354,6 +366,16 @@ Item { myData = data[myIndex]; data.splice(myIndex, 1); userData = data; + var ignoredID, index; + for (ignoredID in ignored) { + index = findSessionIndex(ignoredID); + console.log('fixme hrs adding back ignored', ignoredID, index, JSON.stringify(ignored[ignoredID])); + if (-1 === index) { // Add back any missing ignored, because they sometimes take a moment to show up. + userData.push(ignored[ignoredID]); + } else { // Mark existing ignored. + userData[index].ignored = true; + } + } sortModel(); break; case 'select': diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index dc5b6233aa..bcd6ef7527 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -292,7 +292,15 @@ void Avatar::updateAvatarEntities() { } } +void Avatar::setShouldDie() { + // This will cause the avatar to be shrunk away and removed (the actual Avatar gets removed), but then it comes back. + _owningAvatarMixer.clear(); + // This removes the avatar from physics and makes it shrink away, but does not actualy remvoe Avatar from Avatar Manager. + // FIXME hrs remove, unless it can be made to work cleanly. + // (In which case, this could be on AvatarList/AvatarManager instead, and consider moving pal.js usage to Pal.qml, and removing (un)ignoredNode signalling.) + //DependencyManager::get()->handleRemovedAvatar(AvatarSharedPointer(this), AvatarIgnored); +} void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 784f380028..3478b3ed21 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -178,6 +178,8 @@ public: glm::vec3 getUncachedRightPalmPosition() const; glm::quat getUncachedRightPalmRotation() const; + Q_INVOKABLE void setShouldDie(); + public slots: // FIXME - these should be migrated to use Pose data instead diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c5222641ff..25006d52a0 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -198,6 +198,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { if (avatar->getTargetScale() <= MIN_FADE_SCALE) { avatar->removeFromScene(*fadingIterator, scene, pendingChanges); // only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine + qDebug() << "fixme hrs at minimum fade scale" << _motionStatesToRemoveFromPhysics.empty(); if (_motionStatesToRemoveFromPhysics.empty()) { fadingIterator = _avatarFades.erase(fadingIterator); } else { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index f38796ca08..a423e34f8f 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -93,7 +93,6 @@ private: // virtual overrides virtual AvatarSharedPointer newSharedAvatar() override; virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; - virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarFades; diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index b118423236..e736bf2a84 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -19,6 +19,8 @@ UsersScriptingInterface::UsersScriptingInterface() { connect(nodeList.data(), &LimitedNodeList::canKickChanged, this, &UsersScriptingInterface::canKickChanged); connect(nodeList.data(), &NodeList::ignoreRadiusEnabledChanged, this, &UsersScriptingInterface::ignoreRadiusEnabledChanged); connect(nodeList.data(), &NodeList::usernameFromIDReply, this, &UsersScriptingInterface::usernameFromIDReply); + connect(nodeList.data(), &NodeList::ignoredNode, this, &UsersScriptingInterface::ignoredNode); + connect(nodeList.data(), &NodeList::unignoredNode, this, &UsersScriptingInterface::unignoredNode); } void UsersScriptingInterface::ignore(const QUuid& nodeID) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index c8866376da..394e006451 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -95,6 +95,8 @@ public slots: signals: void canKickChanged(bool canKick); void ignoreRadiusEnabledChanged(bool isEnabled); + void ignoredNode(const QUuid& nodeID); + void unignoredNode(const QUuid& nodeID); /**jsdoc * Notifies scripts that another user has entered the ignore radius diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 77f939d2c5..7e25496537 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -329,6 +329,12 @@ pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); +function onIgnore(sessionId) { // make it go away in the usual way, since we'll still get data keeping it live + // Why doesn't this work from .qml? (crashes) + AvatarList.getAvatar(sessionId).setShouldDie(); +} +Users.ignoredNode.connect(onIgnore); + // // Cleanup. // @@ -338,6 +344,7 @@ Script.scriptEnding.connect(function () { pal.visibleChanged.disconnect(onVisibleChanged); pal.closed.disconnect(off); Users.usernameFromIDReply.disconnect(usernameFromIDReply); + Users.ignoredNode.disconnect(onIgnore); off(); }); From 8b9c1446cddbc1a9d92517b142d50859aca03e71 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 22 Dec 2016 14:21:35 -0800 Subject: [PATCH 09/45] protocol versions --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 8c43aa2bc4..07e1ece0cb 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -53,7 +53,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::SessionDisplayName); + return static_cast(AvatarMixerPacketVersion::Unignore); case PacketType::ICEServerHeartbeat: return 18; // ICE Server Heartbeat signing case PacketType::AssetGetInfo: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 30bd6e8c54..c7e497bf79 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -106,7 +106,7 @@ public: ViewFrustum, RequestDomainListData, NodeUnignoreRequest, - LAST_PACKET_TYPE = ViewFrustum // FIXME! NodeUnignoreRequest + LAST_PACKET_TYPE = NodeUnignoreRequest }; }; @@ -209,7 +209,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { SensorToWorldMat, HandControllerJoints, HasKillAvatarReason, - SessionDisplayName + SessionDisplayName, + Unignore }; enum class DomainConnectRequestVersion : PacketVersion { From a9e781f56546680bac037b0bc1624d61d7a3db49 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 22 Dec 2016 15:25:43 -0800 Subject: [PATCH 10/45] naming consistentcy. No functional changes. --- assignment-client/src/avatars/AvatarMixer.cpp | 10 +++++----- assignment-client/src/avatars/AvatarMixer.h | 2 +- assignment-client/src/avatars/AvatarMixerClientData.h | 2 +- libraries/networking/src/NodeList.cpp | 4 ++-- libraries/networking/src/udt/PacketHeaders.h | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 1192dfb200..fdb6b10cab 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -51,7 +51,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::NodeUnignoreRequest, this, "handleNodeUnignoreRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); - packetReceiver.registerListener(PacketType::RequestDomainListData, this, "handleRequestDomainListDataPacket"); + packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); auto nodeList = DependencyManager::get(); connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &AvatarMixer::handlePacketVersionMismatch); @@ -536,18 +536,18 @@ void AvatarMixer::handleViewFrustumPacket(QSharedPointer messag } } -void AvatarMixer::handleRequestDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode) { +void AvatarMixer::handleRequestsDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode) { auto nodeList = DependencyManager::get(); nodeList->getOrCreateLinkedData(senderNode); - qDebug() << "HRS FIXME received requestDomainListData packet from" << senderNode->getUUID(); + qDebug() << "HRS FIXME received RequestsDomainListData packet from" << senderNode->getUUID(); if (senderNode->getLinkedData()) { AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData()); if (nodeData != nullptr) { bool isRequesting; message->readPrimitive(&isRequesting); - qDebug() << "HRS FIXME handling requestDomainListData packet" << isRequesting << "from" << nodeData->getNodeID(); - nodeData->setRequestDomainListData(isRequesting); + qDebug() << "HRS FIXME handling RequestsDomainListData packet" << isRequesting << "from" << nodeData->getNodeID(); + nodeData->setRequestsDomainListData(isRequesting); } } } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 296b82f1aa..865dd36106 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -43,7 +43,7 @@ private slots: void handleNodeIgnoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); void handleNodeUnignoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); - void handleRequestDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode); + void handleRequestsDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode); void domainSettingsRequestComplete(); void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 462968eb37..861086893a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -102,7 +102,7 @@ public: const QString& getBaseDisplayName() { return _baseDisplayName; } void setBaseDisplayName(const QString& baseDisplayName) { _baseDisplayName = baseDisplayName; } bool getRequestsDomainListData() { return _requestsDomainListData; } - void setRequestDomainListData(bool requesting) { _requestsDomainListData = requesting; } + void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } private: AvatarSharedPointer _avatar { new AvatarData() }; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 49c689b007..6905feb370 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -971,10 +971,10 @@ void NodeList::setRequestsDomainListData(bool isRequesting) { eachMatchingNode([](const SharedNodePointer& node)->bool { return node->getType() == NodeType::AvatarMixer; }, [this, isRequesting](const SharedNodePointer& destinationNode) { - auto packet = NLPacket::create(PacketType::RequestDomainListData, sizeof(bool), true); // reliable + auto packet = NLPacket::create(PacketType::RequestsDomainListData, sizeof(bool), true); // reliable packet->writePrimitive(isRequesting); sendPacket(std::move(packet), *destinationNode); - qDebug() << "HRS FIXME sending requestDomainListData packet" << isRequesting; + qDebug() << "HRS FIXME sending RequestsDomainListData packet" << isRequesting; }); _requestsDomainListData = isRequesting; } \ No newline at end of file diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index c7e497bf79..cae4eaa43e 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -104,7 +104,7 @@ public: UsernameFromIDRequest, UsernameFromIDReply, ViewFrustum, - RequestDomainListData, + RequestsDomainListData, NodeUnignoreRequest, LAST_PACKET_TYPE = NodeUnignoreRequest }; From 7faa689d1dc8a308029a6820d32f9586214dd427 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 22 Dec 2016 16:29:14 -0800 Subject: [PATCH 11/45] avatar body goes away when ignored, and comes back when unignored --- interface/src/avatar/Avatar.cpp | 21 +++++++++++++++++++++ interface/src/avatar/Avatar.h | 4 ++++ interface/src/avatar/AvatarManager.cpp | 13 ++----------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index bcd6ef7527..ed8433aade 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -44,6 +44,7 @@ #include "Util.h" #include "world.h" #include "InterfaceLogging.h" +#include "SceneScriptingInterface.h" #include "SoftAttachmentModel.h" #include @@ -467,6 +468,7 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr attachmentModel->addToScene(scene, pendingChanges); } + _inScene = true; return true; } @@ -477,6 +479,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptrremoveFromScene(scene, pendingChanges); } + _inScene = false; } void Avatar::updateRenderItem(render::PendingChanges& pendingChanges) { @@ -1336,3 +1339,21 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) { } } } + +void Avatar::addToScene(AvatarSharedPointer myHandle) { + render::ScenePointer scene = qApp->getMain3DScene(); + if (scene) { + render::PendingChanges pendingChanges; + if (DependencyManager::get()->shouldRenderAvatars() && !DependencyManager::get()->isIgnoringNode(getSessionUUID())) { + addToScene(myHandle, scene, pendingChanges); + } + scene->enqueuePendingChanges(pendingChanges); + } else { + qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown"; + } +} +void Avatar::ensureInScene(AvatarSharedPointer self) { + if (!_inScene) { + addToScene(self); + } +} \ No newline at end of file diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 3478b3ed21..d03a8e9a54 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -256,6 +256,9 @@ protected: ThreadSafeValueCache _rightPalmPositionCache { glm::vec3() }; ThreadSafeValueCache _rightPalmRotationCache { glm::quat() }; + void addToScene(AvatarSharedPointer self); + void ensureInScene(AvatarSharedPointer self); + private: int _leftPointerGeometryID { 0 }; int _rightPointerGeometryID { 0 }; @@ -264,6 +267,7 @@ private: bool _shouldAnimate { true }; bool _shouldSkipRender { false }; bool _isLookAtTarget { false }; + bool _inScene { false }; float getBoundingRadius() const; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 25006d52a0..c534e9b499 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -159,6 +159,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { removeAvatar(avatarIterator.key()); ++avatarIterator; } else { + avatar->ensureInScene(avatar); avatar->simulate(deltaTime); ++avatarIterator; @@ -198,7 +199,6 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { if (avatar->getTargetScale() <= MIN_FADE_SCALE) { avatar->removeFromScene(*fadingIterator, scene, pendingChanges); // only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine - qDebug() << "fixme hrs at minimum fade scale" << _motionStatesToRemoveFromPhysics.empty(); if (_motionStatesToRemoveFromPhysics.empty()) { fadingIterator = _avatarFades.erase(fadingIterator); } else { @@ -220,16 +220,7 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe auto newAvatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); auto rawRenderableAvatar = std::static_pointer_cast(newAvatar); - render::ScenePointer scene = qApp->getMain3DScene(); - if (scene) { - render::PendingChanges pendingChanges; - if (DependencyManager::get()->shouldRenderAvatars()) { - rawRenderableAvatar->addToScene(rawRenderableAvatar, scene, pendingChanges); - } - scene->enqueuePendingChanges(pendingChanges); - } else { - qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown"; - } + rawRenderableAvatar->addToScene(rawRenderableAvatar); return newAvatar; } From 38a3ee410455020e17bc8da695758c4742ac475c Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 23 Dec 2016 14:14:45 -0800 Subject: [PATCH 12/45] Some code review feedback more to come --- assignment-client/src/avatars/AvatarMixer.cpp | 8 ++++++-- libraries/networking/src/NodeList.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index fdb6b10cab..27be882c6e 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -207,9 +207,13 @@ void AvatarMixer::broadcastAvatarData() { // use the data rate specifically for avatar data for FRD adjustment checks float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps(); - // send extra data that is otherwise surpressed + // When this is true, the AvatarMixer will send Avatar data to a client about avatars that are not in the view frustrum bool getsOutOfView = nodeData->getRequestsDomainListData(); - bool getsIgnoredByMe = nodeData->getRequestsDomainListData(); + + // When this is true, the AvatarMixer will send Avatar data to a client about avatars that they've ignored + bool getsIgnoredByMe = getsOutOfView; + + // When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them bool getsAnyIgnored = getsIgnoredByMe && node->getCanKick(); // Check if it is time to adjust what we send this client based on the observed diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 6905feb370..b1d7f0b5fc 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -812,7 +812,7 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID) { } void NodeList::unignoreNodeBySessionID(const QUuid& nodeID) { - // enumerate the nodes to send a reliable ignore packet to each that can leverage it + // enumerate the nodes to send a reliable unignore packet to each that can leverage it if (!nodeID.isNull() && _sessionUUID != nodeID) { eachMatchingNode([&nodeID](const SharedNodePointer& node)->bool { if (node->getType() == NodeType::AudioMixer || node->getType() == NodeType::AvatarMixer) { @@ -821,7 +821,7 @@ void NodeList::unignoreNodeBySessionID(const QUuid& nodeID) { return false; } }, [&nodeID, this](const SharedNodePointer& destinationNode) { - // create a reliable NLPacket with space for the ignore UUID + // create a reliable NLPacket with space for the unignore UUID auto ignorePacket = NLPacket::create(PacketType::NodeUnignoreRequest, NUM_BYTES_RFC4122_UUID, true); // write the node ID to the packet @@ -829,7 +829,7 @@ void NodeList::unignoreNodeBySessionID(const QUuid& nodeID) { qCDebug(networking) << "Sending packet to unignore node" << uuidStringWithoutCurlyBraces(nodeID); - // send off this ignore packet reliably to the matching node + // send off this unignore packet reliably to the matching node sendPacket(std::move(ignorePacket), *destinationNode); }); @@ -977,4 +977,4 @@ void NodeList::setRequestsDomainListData(bool isRequesting) { qDebug() << "HRS FIXME sending RequestsDomainListData packet" << isRequesting; }); _requestsDomainListData = isRequesting; -} \ No newline at end of file +} From 4fdc093cfab470ca78d155a174b3249eba310ffa Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 23 Dec 2016 15:23:43 -0800 Subject: [PATCH 13/45] Throttle extra avatar data When PAL is open, we send AvatarData for avatars out of view, or those who have ignored us (if we are an admin). For those out of view, we really don't need their info at 45hz, so this is a super simple way to get it less often. Chose 1/10th (4.5 hz) randomly and could be much lower. Just wanted to push this and get the conversation going on how best to do it. --- assignment-client/src/avatars/AvatarMixer.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 27be882c6e..25acdb162d 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -110,6 +110,9 @@ void AvatarMixer::broadcastAvatarData() { const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; + // only send extra avatar data (avatars out of view, ignored) every Nth AvatarData frame + const int EXTRA_AVATAR_DATA_FRAME_RATIO = 10; + // NOTE: The following code calculates the _performanceThrottlingRatio based on how much the avatar-mixer was // able to sleep. This will eventually be used to ask for an additional avatar-mixer to help out. Currently the value // is unused as it is assumed this should not be hit before the avatar-mixer hits the desired bandwidth limit per client. @@ -368,6 +371,11 @@ void AvatarMixer::broadcastAvatarData() { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); + // this throttles the extra data to only be sent every Nth message + if (getsOutOfView && lastSeqToReceiver % EXTRA_AVATAR_DATA_FRAME_RATIO > 0) { + return; + } + if (lastSeqToReceiver > lastSeqFromSender && lastSeqToReceiver != UINT16_MAX) { // we got out out of order packets from the sender, track it otherNodeData->incrementNumOutOfOrderSends(); From d3c2ba5199b8c46224ce74f4eb277f1e14c6a3c0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 28 Dec 2016 13:39:53 -0800 Subject: [PATCH 14/45] Fix throttling extra data --- assignment-client/src/avatars/AvatarMixer.cpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 25acdb162d..6285150070 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -111,7 +111,7 @@ void AvatarMixer::broadcastAvatarData() { const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; // only send extra avatar data (avatars out of view, ignored) every Nth AvatarData frame - const int EXTRA_AVATAR_DATA_FRAME_RATIO = 10; + const int EXTRA_AVATAR_DATA_FRAME_RATIO = 16; // NOTE: The following code calculates the _performanceThrottlingRatio based on how much the avatar-mixer was // able to sleep. This will eventually be used to ask for an additional avatar-mixer to help out. Currently the value @@ -370,11 +370,6 @@ void AvatarMixer::broadcastAvatarData() { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); - - // this throttles the extra data to only be sent every Nth message - if (getsOutOfView && lastSeqToReceiver % EXTRA_AVATAR_DATA_FRAME_RATIO > 0) { - return; - } if (lastSeqToReceiver > lastSeqFromSender && lastSeqToReceiver != UINT16_MAX) { // we got out out of order packets from the sender, track it @@ -400,15 +395,21 @@ void AvatarMixer::broadcastAvatarData() { nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), otherNodeData->getLastReceivedSequenceNumber()); - // start a new segment in the PacketList for this avatar - avatarPacketList->startSegment(); - // determine if avatar is in view, to determine how much data to include... glm::vec3 otherNodeBoxScale = (otherNodeData->getPosition() - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + bool isInView = nodeData->otherAvatarInView(otherNodeBox); + + // this throttles the extra data to only be sent every Nth message + if (!isInView && getsOutOfView && (lastSeqToReceiver % EXTRA_AVATAR_DATA_FRAME_RATIO > 0)) { + return; + } + + // start a new segment in the PacketList for this avatar + avatarPacketList->startSegment(); AvatarData::AvatarDataDetail detail; - if (!nodeData->otherAvatarInView(otherNodeBox) && !getsOutOfView) { + if (!isInView && !getsOutOfView) { detail = AvatarData::MinimumData; nodeData->incrementAvatarOutOfView(); } else { From 800389cf20be3f0a002c50cd1ff879d5f80caa2d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 28 Dec 2016 14:37:19 -0800 Subject: [PATCH 15/45] Remove HRS logging --- assignment-client/src/avatars/AvatarMixer.cpp | 4 ---- interface/src/avatar/Avatar.cpp | 5 ----- libraries/avatars/src/AvatarHashMap.cpp | 1 - libraries/networking/src/NodeList.cpp | 1 - 4 files changed, 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 6285150070..1de1f093b7 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -347,7 +347,6 @@ void AvatarMixer::broadcastAvatarData() { && (forceSend || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { - qDebug() << "FIXME HRS sending identity to" << node->getUUID() << "from" << otherNode->getUUID() << "gets mine/all/view:" << getsIgnoredByMe << getsAnyIgnored << getsOutOfView ; sendIdentityPacket(otherNodeData, node); } @@ -417,7 +416,6 @@ void AvatarMixer::broadcastAvatarData() { ? AvatarData::SendAllData : AvatarData::IncludeSmallData; nodeData->incrementAvatarInView(); } - //qDebug() << "FIXME HRS sending" << detail << "to" << node->getUUID() << "from" << otherNode->getUUID(); numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += avatarPacketList->write(otherAvatar.toByteArray(detail)); @@ -552,14 +550,12 @@ void AvatarMixer::handleViewFrustumPacket(QSharedPointer messag void AvatarMixer::handleRequestsDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode) { auto nodeList = DependencyManager::get(); nodeList->getOrCreateLinkedData(senderNode); - qDebug() << "HRS FIXME received RequestsDomainListData packet from" << senderNode->getUUID(); if (senderNode->getLinkedData()) { AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData()); if (nodeData != nullptr) { bool isRequesting; message->readPrimitive(&isRequesting); - qDebug() << "HRS FIXME handling RequestsDomainListData packet" << isRequesting << "from" << nodeData->getNodeID(); nodeData->setRequestsDomainListData(isRequesting); } } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ed8433aade..1ade21930d 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -296,11 +296,6 @@ void Avatar::updateAvatarEntities() { void Avatar::setShouldDie() { // This will cause the avatar to be shrunk away and removed (the actual Avatar gets removed), but then it comes back. _owningAvatarMixer.clear(); - - // This removes the avatar from physics and makes it shrink away, but does not actualy remvoe Avatar from Avatar Manager. - // FIXME hrs remove, unless it can be made to work cleanly. - // (In which case, this could be on AvatarList/AvatarManager instead, and consider moving pal.js usage to Pal.qml, and removing (un)ignoredNode signalling.) - //DependencyManager::get()->handleRemovedAvatar(AvatarSharedPointer(this), AvatarIgnored); } void Avatar::simulate(float deltaTime) { diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index c0aafa7f2b..c708176da7 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -145,7 +145,6 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer identity.uuid = EMPTY; } } - qDebug() << "FIXME HRS processing identity packet regarding" << identity.uuid << "ignoring:" << nodeList->isIgnoringNode(identity.uuid) << "reqestsDomainList:" << nodeList->getRequestsDomainListData(); if (!nodeList->isIgnoringNode(identity.uuid) || nodeList->getRequestsDomainListData()) { // mesh URL for a UUID, find avatar in our list auto avatar = newOrExistingAvatar(identity.uuid, sendingNode); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b1d7f0b5fc..e745954d88 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -974,7 +974,6 @@ void NodeList::setRequestsDomainListData(bool isRequesting) { auto packet = NLPacket::create(PacketType::RequestsDomainListData, sizeof(bool), true); // reliable packet->writePrimitive(isRequesting); sendPacket(std::move(packet), *destinationNode); - qDebug() << "HRS FIXME sending RequestsDomainListData packet" << isRequesting; }); _requestsDomainListData = isRequesting; } From 46e8bf5283163eb031c0ff671545049ac42a4392 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 22 Dec 2016 12:42:50 -0800 Subject: [PATCH 16/45] First steps, holding off til Howard finishes --- assignment-client/src/audio/AudioMixer.cpp | 50 ++++++++++++++++ assignment-client/src/audio/AudioMixer.h | 2 + interface/resources/qml/hifi/Pal.qml | 33 +++++++---- libraries/networking/src/NodeList.cpp | 58 +++++++++++++++++++ libraries/networking/src/NodeList.h | 4 ++ libraries/networking/src/udt/PacketHeaders.h | 6 +- .../src/UsersScriptingInterface.cpp | 12 ++++ .../src/UsersScriptingInterface.h | 23 +++++++- scripts/system/pal.js | 2 + 9 files changed, 178 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 9a80289911..f7ea49a642 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -66,6 +66,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::NodeUnignoreRequest, this, "handleNodeUnignoreRequestPacket"); + packetReceiver.registerListener(PacketType::NodePersonalMuteRequest, this, "handleNodePersonalMuteRequestPacket"); + packetReceiver.registerListener(PacketType::NodePersonalMuteStatusRequest, this, "handleNodePersonalMuteStatusRequestPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); @@ -230,6 +232,54 @@ void AudioMixer::handleNodeUnignoreRequestPacket(QSharedPointer sendingNode->parseUnignoreRequestMessage(packet); } +void AudioMixer::handleNodePersonalMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + // parse out the UUID being muted from the packet + QUuid ignoredUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + bool enabled; + packet->readPrimitive(&enabled); + + if (!ignoredUUID.isNull() && ignoredUUID != _uuid) { + if (enabled) { + qDebug() << "Adding" << uuidStringWithoutCurlyBraces(ignoredUUID) << "to personally muted set for" + << uuidStringWithoutCurlyBraces(_uuid); + + // Add the session UUID to the set of personally muted ones for this listening node + sendingNode->addIgnoredNode(ignoredUUID); + } else { + qDebug() << "Removing" << uuidStringWithoutCurlyBraces(ignoredUUID) << "from personally muted set for" + << uuidStringWithoutCurlyBraces(_uuid); + + // Remove the session UUID to the set of personally muted ones for this listening node + sendingNode->_ignoredNodeIDSet.unsafe_erase(ignoredUUID); + } + } else { + qWarning() << "Node::addPersonalMutedNode called with null ID or ID of personal muting node."; + } +} + +void AudioMixer::handleNodePersonalMuteStatusRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + // parse out the UUID whose personal mute status is being requested from the packet + QUuid UUIDToCheck = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + if (!UUIDToCheck.isNull()) { + // First, make sure we actually have a node with this UUID + auto limitedNodeList = DependencyManager::get(); + auto matchingNode = limitedNodeList->nodeWithUUID(UUIDToCheck); + + // If we do have a matching node... + if (matchingNode) { + auto personalMuteStatusPacket = NLPacket::create(PacketType::NodePersonalMuteStatusReply, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); + + // write the node ID to the packet + personalMuteStatusPacket->write(UUIDToCheck.toRfc4122()); + personalMuteStatusPacket->writePrimitive(isIgnoringNodeWithID(UUIDToCheck)); + + qCDebug(networking) << "Sending Personal Mute Status Request Packet for node" << uuidStringWithoutCurlyBraces(nodeID); + + limitedNodeList->sendPacket(std::move(personalMuteStatusPacket), *sendingNode); + } + } +} void AudioMixer::handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { sendingNode->parseIgnoreRadiusRequestMessage(packet); } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 14a0167c3e..353db84a15 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -63,6 +63,8 @@ private slots: void handleNodeKilled(SharedNodePointer killedNode); void handleNodeIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodeUnignoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handleNodePersonalMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handleNodePersonalMuteStatusRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleKillAvatarPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodeMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 5c475f16dd..eb35664707 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -210,22 +210,28 @@ Item { var newValue = !model[styleData.role] var datum = userData[model.userIndex] datum[styleData.role] = model[styleData.role] = newValue - var key = styleData.role; - if (!newValue) { - key = 'un' + key; - } - if (styleData.role === 'ignore') { + if (styleData.role === "personalMute") { + Users[styleData.role](model.sessionId, newValue) + } else if (styleData.role === 'ignore') { + var key = styleData.role; + if (!newValue) { + key = 'un' + key; + } if (newValue) { ignored[datum.sessionId] = datum; console.log('fixme hrs adding to ignored', JSON.stringify(datum), 'at', datum.sessionId); } else { delete ignored[datum.sessionId]; - } + } + console.log('fixme hrs pal action', key, model.sessionId); + Users[key](model.sessionId); + } else { + Users[styleData.role](model.sessionId) + // Just for now, while we cannot undo things: + userData.splice(model.userIndex, 1) + sortModel() } - console.log('fixme hrs pal action', key, model.sessionId); - Users[key](model.sessionId); } - } } } // Refresh button @@ -422,7 +428,14 @@ Item { } } break; - default: + case 'updateMuted': + var userId = message.params[0]; + var enabled = message.params[1]; + var userIndex = findSessionIndex(userId); + userModel.get(userIndex).personalMute.property = enabled; + userData[userIndex].personalMute.property = enabled; // Defensive programming + break; + default: console.log('Unrecognized message:', JSON.stringify(message)); } } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e745954d88..f80dbad23d 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -875,6 +875,64 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { } } +void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool enabled) { + // cannot personal mute yourself, or nobody + if (!nodeID.isNull() && _sessionUUID != nodeID) { + auto audioMixer = soloNodeOfType(NodeType::AudioMixer); + if (audioMixer) { + // setup the packet + auto personalMutePacket = NLPacket::create(PacketType::NodePersonalMuteRequest, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); + + // write the node ID to the packet + personalMutePacket->write(nodeID.toRfc4122()); + personalMutePacket->writePrimitive(enabled); + + qCDebug(networking) << "Sending Personal Mute Packet to" << (enabled ? "mute" : "unmute") << "node" << uuidStringWithoutCurlyBraces(nodeID); + + sendPacket(std::move(personalMutePacket), *audioMixer); + } else { + qWarning() << "Couldn't find audio mixer to send node personal mute request"; + } + } else { + qWarning() << "NodeList::personalMuteNodeBySessionID called with an invalid ID or an ID which matches the current session ID."; + } +} + +void NodeList::requestPersonalMuteStatus(const QUuid& nodeID) { + // cannot personal mute yourself, or nobody; don't bother checking the status + if (!nodeID.isNull() && _sessionUUID != nodeID) { + auto audioMixer = soloNodeOfType(NodeType::AudioMixer); + if (audioMixer) { + // send a request to the audio mixer to get the personal mute status associated with the given session ID + // setup the packet + auto personalMuteStatusPacket = NLPacket::create(PacketType::NodePersonalMuteStatusRequest, NUM_BYTES_RFC4122_UUID, true); + + // write the node ID to the packet + personalMuteStatusPacket->write(nodeID.toRfc4122()); + + qCDebug(networking) << "Sending Personal Mute Status Request Packet for node" << uuidStringWithoutCurlyBraces(nodeID); + + sendPacket(std::move(personalMuteStatusPacket), *audioMixer); + } else { + qWarning() << "Couldn't find audio mixer to send node personal mute status request"; + } + } else { + qWarning() << "NodeList::requestPersonalMuteStatus called with an invalid ID or an ID which matches the current session ID."; + } +} + +void NodeList::processPersonalMuteStatusReply(QSharedPointer message) { + // read the UUID from the packet + QString nodeUUIDString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString(); + // read the personal mute status + bool isPersonalMuted; + message->readPrimitive(&isPersonalMuted); + + qCDebug(networking) << "Got personal muted status" << isPersonalMuted << "for node" << nodeUUIDString; + + emit personalMuteStatusReply(nodeUUIDString, isPersonalMuted); +} + void NodeList::kickNodeBySessionID(const QUuid& nodeID) { // send a request to domain-server to kick the node with the given session ID // the domain-server will handle the persistence of the kick (via username or IP) diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 6d7026b562..5b3f334366 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -79,12 +79,14 @@ public: void ignoreNodeBySessionID(const QUuid& nodeID); void unignoreNodeBySessionID(const QUuid& nodeID); bool isIgnoringNode(const QUuid& nodeID) const; + void personalMuteNodeBySessionID(const QUuid& nodeID, bool enabled); void kickNodeBySessionID(const QUuid& nodeID); void muteNodeBySessionID(const QUuid& nodeID); void requestUsernameFromSessionID(const QUuid& nodeID); bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool isRequesting); + void requestPersonalMuteStatus(const QUuid& nodeID); public slots: void reset(); @@ -104,6 +106,7 @@ public slots: void processICEPingPacket(QSharedPointer message); void processUsernameFromIDReply(QSharedPointer message); + void processPersonalMuteStatusReply(QSharedPointer message); #if (PR_BUILD || DEV_BUILD) void toggleSendNewerDSConnectVersion(bool shouldSendNewerVersion) { _shouldSendNewerVersion = shouldSendNewerVersion; } @@ -116,6 +119,7 @@ signals: void unignoredNode(const QUuid& nodeID); void ignoreRadiusEnabledChanged(bool isIgnored); void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); + void personalMuteStatusReply(const QString& nodeID, bool isPersonalMuted); private slots: void stopKeepalivePingTimer(); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index cae4eaa43e..76c5120823 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -106,7 +106,10 @@ public: ViewFrustum, RequestsDomainListData, NodeUnignoreRequest, - LAST_PACKET_TYPE = NodeUnignoreRequest + NodePersonalMuteRequest, + NodePersonalMuteStatusRequest, + NodePersonalMuteStatusReply, + LAST_PACKET_TYPE = NodePersonalMuteStatusReply }; }; @@ -245,6 +248,7 @@ enum class AudioVersion : PacketVersion { Exactly10msAudioPackets, TerminatingStreamStats, SpaceBubbleChanges, + HasPersonalMute, }; #endif // hifi_PacketHeaders_h diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index e736bf2a84..e25a9603b6 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -21,6 +21,7 @@ UsersScriptingInterface::UsersScriptingInterface() { connect(nodeList.data(), &NodeList::usernameFromIDReply, this, &UsersScriptingInterface::usernameFromIDReply); connect(nodeList.data(), &NodeList::ignoredNode, this, &UsersScriptingInterface::ignoredNode); connect(nodeList.data(), &NodeList::unignoredNode, this, &UsersScriptingInterface::unignoredNode); + connect(nodeList.data(), &NodeList::personalMuteStatusReply, this, &UsersScriptingInterface::personalMuteStatusReply); } void UsersScriptingInterface::ignore(const QUuid& nodeID) { @@ -33,6 +34,17 @@ void UsersScriptingInterface::unignore(const QUuid& nodeID) { DependencyManager::get()->unignoreNodeBySessionID(nodeID); } +void UsersScriptingInterface::personalMute(const QUuid& nodeID, bool enabled) { + // ask the NodeList to mute the user with the given session ID + // "Personal Mute" only applies one way and is not global + DependencyManager::get()->personalMuteNodeBySessionID(nodeID, enabled); +} + +void UsersScriptingInterface::requestPersonalMuteStatus(const QUuid& nodeID) { + // ask the Audio Mixer via the NodeList for the Personal Mute status associated with the given session ID + DependencyManager::get()->requestPersonalMuteStatus(nodeID); +} + void UsersScriptingInterface::kick(const QUuid& nodeID) { // ask the NodeList to kick the user with the given session ID diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 394e006451..a7e6ec3a3e 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -39,6 +39,21 @@ public slots: void ignore(const QUuid& nodeID); void unignore(const QUuid& nodeID); + /**jsdoc + * Mute another user for you and you only. + * @function Users.personalMute + * @param {nodeID} nodeID The node or session ID of the user you want to mute. + * @param {bool} enable True for enabled; false for disabled. + */ + void personalMute(const QUuid& nodeID, bool enabled); + + /**jsdoc + * Requests a bool containing whether you have given the given Avatar UUID. + * @function Users.requestPersonalMuteStatus + * @param {nodeID} nodeID The node or session ID of the user whose personal mute status you want. + */ + void requestPersonalMuteStatus(const QUuid& nodeID); + /**jsdoc * Kick another user. * @function Users.kick @@ -47,7 +62,7 @@ public slots: void kick(const QUuid& nodeID); /**jsdoc - * Mute another user. + * Mute another user for everyone. * @function Users.mute * @param {nodeID} nodeID The node or session ID of the user you want to mute. */ @@ -110,6 +125,12 @@ signals: */ void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); + /**jsdoc + * Notifies scripts of the Personal Mute status associated with a UUID. + * @function Users.usernameFromIDReply + */ + void personalMuteStatusReply(const QString& nodeID, bool isPersonalMuted); + private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7e25496537..7f38857702 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -132,6 +132,8 @@ function populateUserList() { // Request the username from the given UUID Users.requestUsernameFromID(id); } + // Request personal mute status from AudioMixer + Users.requestPersonalMuteStatus(id); data.push(avatarPalDatum); if (id) { // No overlay for ourself. addAvatarNode(id); From e26d3476822bf398d30279b0256ae3839d94038a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 22 Dec 2016 16:21:53 -0800 Subject: [PATCH 17/45] It seems to be working, minus removing from list --- assignment-client/src/audio/AudioMixer.cpp | 11 ++++++----- libraries/networking/src/NodeList.cpp | 7 ++++--- libraries/networking/src/NodeList.h | 2 +- .../script-engine/src/UsersScriptingInterface.cpp | 5 ++--- .../script-engine/src/UsersScriptingInterface.h | 2 +- scripts/system/pal.js | 15 ++++++++++++++- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index f7ea49a642..3589c025f4 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -250,16 +250,16 @@ void AudioMixer::handleNodePersonalMuteRequestPacket(QSharedPointer_ignoredNodeIDSet.unsafe_erase(ignoredUUID); + //sendingNode->removeIgnoredNode(ignoredUUID); } } else { - qWarning() << "Node::addPersonalMutedNode called with null ID or ID of personal muting node."; + qWarning() << "Node::handlePersonalMutedNode called with null ID or ID of personal muting node."; } } void AudioMixer::handleNodePersonalMuteStatusRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { // parse out the UUID whose personal mute status is being requested from the packet - QUuid UUIDToCheck = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + QUuid UUIDToCheck = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); if (!UUIDToCheck.isNull()) { // First, make sure we actually have a node with this UUID @@ -269,12 +269,13 @@ void AudioMixer::handleNodePersonalMuteStatusRequestPacket(QSharedPointerisIgnoringNodeWithID(UUIDToCheck); // write the node ID to the packet personalMuteStatusPacket->write(UUIDToCheck.toRfc4122()); - personalMuteStatusPacket->writePrimitive(isIgnoringNodeWithID(UUIDToCheck)); + personalMuteStatusPacket->writePrimitive(isMuted); - qCDebug(networking) << "Sending Personal Mute Status Request Packet for node" << uuidStringWithoutCurlyBraces(nodeID); + qDebug() << "Personal Mute Status: node" << uuidStringWithoutCurlyBraces(UUIDToCheck) << "mute status:" << isMuted; limitedNodeList->sendPacket(std::move(personalMuteStatusPacket), *sendingNode); } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index f80dbad23d..ab63c81b9a 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -129,6 +129,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) packetReceiver.registerListener(PacketType::DomainServerPathResponse, this, "processDomainServerPathResponse"); packetReceiver.registerListener(PacketType::DomainServerRemovedNode, this, "processDomainServerRemovedNode"); packetReceiver.registerListener(PacketType::UsernameFromIDReply, this, "processUsernameFromIDReply"); + packetReceiver.registerListener(PacketType::NodePersonalMuteStatusReply, this, "processPersonalMuteStatusReply"); } qint64 NodeList::sendStats(QJsonObject statsObject, HifiSockAddr destination) { @@ -875,7 +876,7 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { } } -void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool enabled) { +void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled) { // cannot personal mute yourself, or nobody if (!nodeID.isNull() && _sessionUUID != nodeID) { auto audioMixer = soloNodeOfType(NodeType::AudioMixer); @@ -885,9 +886,9 @@ void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool enabled) { // write the node ID to the packet personalMutePacket->write(nodeID.toRfc4122()); - personalMutePacket->writePrimitive(enabled); + personalMutePacket->writePrimitive(muteEnabled); - qCDebug(networking) << "Sending Personal Mute Packet to" << (enabled ? "mute" : "unmute") << "node" << uuidStringWithoutCurlyBraces(nodeID); + qCDebug(networking) << "Sending Personal Mute Packet to" << (muteEnabled ? "mute" : "unmute") << "node" << uuidStringWithoutCurlyBraces(nodeID); sendPacket(std::move(personalMutePacket), *audioMixer); } else { diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 5b3f334366..b8022c2d72 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -79,7 +79,7 @@ public: void ignoreNodeBySessionID(const QUuid& nodeID); void unignoreNodeBySessionID(const QUuid& nodeID); bool isIgnoringNode(const QUuid& nodeID) const; - void personalMuteNodeBySessionID(const QUuid& nodeID, bool enabled); + void personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled); void kickNodeBySessionID(const QUuid& nodeID); void muteNodeBySessionID(const QUuid& nodeID); diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index e25a9603b6..0d6983a1e9 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -34,10 +34,10 @@ void UsersScriptingInterface::unignore(const QUuid& nodeID) { DependencyManager::get()->unignoreNodeBySessionID(nodeID); } -void UsersScriptingInterface::personalMute(const QUuid& nodeID, bool enabled) { +void UsersScriptingInterface::personalMute(const QUuid& nodeID, bool muteEnabled) { // ask the NodeList to mute the user with the given session ID // "Personal Mute" only applies one way and is not global - DependencyManager::get()->personalMuteNodeBySessionID(nodeID, enabled); + DependencyManager::get()->personalMuteNodeBySessionID(nodeID, muteEnabled); } void UsersScriptingInterface::requestPersonalMuteStatus(const QUuid& nodeID) { @@ -45,7 +45,6 @@ void UsersScriptingInterface::requestPersonalMuteStatus(const QUuid& nodeID) { DependencyManager::get()->requestPersonalMuteStatus(nodeID); } - void UsersScriptingInterface::kick(const QUuid& nodeID) { // ask the NodeList to kick the user with the given session ID DependencyManager::get()->kickNodeBySessionID(nodeID); diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index a7e6ec3a3e..31024defc6 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -45,7 +45,7 @@ public slots: * @param {nodeID} nodeID The node or session ID of the user you want to mute. * @param {bool} enable True for enabled; false for disabled. */ - void personalMute(const QUuid& nodeID, bool enabled); + void personalMute(const QUuid& nodeID, bool muteEnabled); /**jsdoc * Requests a bool containing whether you have given the given Avatar UUID. diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7f38857702..f69a4eb369 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -133,7 +133,10 @@ function populateUserList() { Users.requestUsernameFromID(id); } // Request personal mute status from AudioMixer - Users.requestPersonalMuteStatus(id); + // (as long as we're not requesting it for our own ID) + if (id) { + Users.requestPersonalMuteStatus(id); + } data.push(avatarPalDatum); if (id) { // No overlay for ourself. addAvatarNode(id); @@ -160,6 +163,14 @@ function usernameFromIDReply(id, username, machineFingerprint) { pal.sendToQml({ method: 'updateUsername', params: data }); } +// The function that handles the personal muted status from the AudioMixer +function personalMuteStatusReply(id, isPersonalMuted) { + var data = [id, isPersonalMuted]; + print('Personal Muted Status Data:', JSON.stringify(data)); + // Ship the data off to QML + pal.sendToQml({ method: 'updatePersonalMutedStatus', params: data }); +} + var pingPong = true; function updateOverlays() { var eye = Camera.position; @@ -330,6 +341,7 @@ button.clicked.connect(onClicked); pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); +Users.personalMuteStatusReply.connect(personalMuteStatusReply); function onIgnore(sessionId) { // make it go away in the usual way, since we'll still get data keeping it live // Why doesn't this work from .qml? (crashes) @@ -347,6 +359,7 @@ Script.scriptEnding.connect(function () { pal.closed.disconnect(off); Users.usernameFromIDReply.disconnect(usernameFromIDReply); Users.ignoredNode.disconnect(onIgnore); + Users.personalMuteStatusReply.disconnect(personalMuteStatusReply); off(); }); From d3700fc9229c1ad9323a733202dff522d9cb7dd4 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 28 Dec 2016 15:47:28 -0800 Subject: [PATCH 18/45] Fix merge errors --- interface/resources/qml/hifi/Pal.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index eb35664707..819da7eea5 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -212,14 +212,14 @@ Item { datum[styleData.role] = model[styleData.role] = newValue if (styleData.role === "personalMute") { Users[styleData.role](model.sessionId, newValue) - } else if (styleData.role === 'ignore') { + } else if (styleData.role === "ignore") { var key = styleData.role; if (!newValue) { key = 'un' + key; } if (newValue) { ignored[datum.sessionId] = datum; - console.log('fixme hrs adding to ignored', JSON.stringify(datum), 'at', datum.sessionId); + console.log("fixme hrs adding to ignored", JSON.stringify(datum), "at", datum.sessionId); } else { delete ignored[datum.sessionId]; } @@ -232,6 +232,7 @@ Item { sortModel() } } + } } } // Refresh button From af1c67a252260f306655ece46885f8c9d3ae7881 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 28 Dec 2016 17:23:56 -0800 Subject: [PATCH 19/45] Progress! --- assignment-client/src/audio/AudioMixer.cpp | 5 -- assignment-client/src/avatars/AvatarMixer.cpp | 4 -- interface/resources/qml/hifi/Pal.qml | 37 +++------- libraries/networking/src/Node.cpp | 21 +++--- libraries/networking/src/Node.h | 1 - libraries/networking/src/NodeList.cpp | 67 +++++++------------ libraries/networking/src/NodeList.h | 5 +- libraries/networking/src/udt/PacketHeaders.h | 1 - .../src/UsersScriptingInterface.cpp | 10 +-- .../src/UsersScriptingInterface.h | 19 +++++- scripts/system/pal.js | 12 ++++ 11 files changed, 81 insertions(+), 101 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 3589c025f4..c9b00eb874 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -65,7 +65,6 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::NegotiateAudioFormat, this, "handleNegotiateAudioFormat"); packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); - packetReceiver.registerListener(PacketType::NodeUnignoreRequest, this, "handleNodeUnignoreRequestPacket"); packetReceiver.registerListener(PacketType::NodePersonalMuteRequest, this, "handleNodePersonalMuteRequestPacket"); packetReceiver.registerListener(PacketType::NodePersonalMuteStatusRequest, this, "handleNodePersonalMuteStatusRequestPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); @@ -228,10 +227,6 @@ void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer p sendingNode->parseIgnoreRequestMessage(packet); } -void AudioMixer::handleNodeUnignoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { - sendingNode->parseUnignoreRequestMessage(packet); -} - void AudioMixer::handleNodePersonalMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { // parse out the UUID being muted from the packet QUuid ignoredUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 1de1f093b7..43ad2cbaff 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -49,7 +49,6 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); - packetReceiver.registerListener(PacketType::NodeUnignoreRequest, this, "handleNodeUnignoreRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); @@ -595,9 +594,6 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer senderNode->parseIgnoreRequestMessage(message); } -void AvatarMixer::handleNodeUnignoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { - sendingNode->parseUnignoreRequestMessage(packet); -} void AvatarMixer::handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { sendingNode->parseIgnoreRadiusRequestMessage(packet); } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 819da7eea5..07f9386b2a 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -210,21 +210,8 @@ Item { var newValue = !model[styleData.role] var datum = userData[model.userIndex] datum[styleData.role] = model[styleData.role] = newValue - if (styleData.role === "personalMute") { - Users[styleData.role](model.sessionId, newValue) - } else if (styleData.role === "ignore") { - var key = styleData.role; - if (!newValue) { - key = 'un' + key; - } - if (newValue) { - ignored[datum.sessionId] = datum; - console.log("fixme hrs adding to ignored", JSON.stringify(datum), "at", datum.sessionId); - } else { - delete ignored[datum.sessionId]; - } - console.log('fixme hrs pal action', key, model.sessionId); - Users[key](model.sessionId); + if (styleData.role === "personalMute" || styleData.role === "ignore") { + Users[styleData.role](model.sessionId, newValue) } else { Users[styleData.role](model.sessionId) // Just for now, while we cannot undo things: @@ -354,7 +341,6 @@ Item { property var userData: [] property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set property bool iAmAdmin: false - property var ignored: ({}); // FIXME: reset when changing domains function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml var i, data = optionalData || userData, length = data.length; for (var i = 0; i < length; i++) { @@ -373,16 +359,6 @@ Item { myData = data[myIndex]; data.splice(myIndex, 1); userData = data; - var ignoredID, index; - for (ignoredID in ignored) { - index = findSessionIndex(ignoredID); - console.log('fixme hrs adding back ignored', ignoredID, index, JSON.stringify(ignored[ignoredID])); - if (-1 === index) { // Add back any missing ignored, because they sometimes take a moment to show up. - userData.push(ignored[ignoredID]); - } else { // Mark existing ignored. - userData[index].ignored = true; - } - } sortModel(); break; case 'select': @@ -429,13 +405,20 @@ Item { } } break; - case 'updateMuted': + case 'updatePersonalMutedStatus': var userId = message.params[0]; var enabled = message.params[1]; var userIndex = findSessionIndex(userId); userModel.get(userIndex).personalMute.property = enabled; userData[userIndex].personalMute.property = enabled; // Defensive programming break; + case 'updateIgnoredStatus': + var userId = message.params[0]; + var enabled = message.params[1]; + var userIndex = findSessionIndex(userId); + userModel.get(userIndex).ignore.property = enabled; + userData[userIndex].ignore.property = enabled; // Defensive programming + break; default: console.log('Unrecognized message:', JSON.stringify(message)); } diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 0f3a6ee30d..b2670fd9b0 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -81,18 +81,17 @@ void Node::updateClockSkewUsec(qint64 clockSkewSample) { _clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile(); } -void Node::parseIgnoreRequestMessage(QSharedPointer message) { - while (message->getBytesLeftToRead()) { - // parse out the UUID being ignored from the packet - QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - - addIgnoredNode(ignoredUUID); - } -} - -void Node::parseUnignoreRequestMessage(QSharedPointer message) { +void Node::parseIgnoreRequestMessage(QSharedPointer message) { + // parse out the UUID being ignored from the packet QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - removeIgnoredNode(ignoredUUID); + bool addToIgnore; + message->readPrimitive(&addToIgnore); + + if (addToIgnore) { + addIgnoredNode(ignoredUUID); + } else { + removeIgnoredNode(ignoredUUID); + } } void Node::addIgnoredNode(const QUuid& otherNodeID) { diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 0a344c9479..28afb8b943 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -73,7 +73,6 @@ public: bool getCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } void parseIgnoreRequestMessage(QSharedPointer message); - void parseUnignoreRequestMessage(QSharedPointer message); void addIgnoredNode(const QUuid& otherNodeID); void removeIgnoredNode(const QUuid& otherNodeID); bool isIgnoringNodeWithID(const QUuid& nodeID) const { QReadLocker lock { &_ignoredNodeIDSetLock }; return _ignoredNodeIDSet.find(nodeID) != _ignoredNodeIDSet.cend(); } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index ab63c81b9a..89b5590025 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -778,7 +778,7 @@ void NodeList::sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationN sendPacket(std::move(ignorePacket), *destinationNode); } -void NodeList::ignoreNodeBySessionID(const QUuid& nodeID) { +void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { // enumerate the nodes to send a reliable ignore packet to each that can leverage it if (!nodeID.isNull() && _sessionUUID != nodeID) { eachMatchingNode([&nodeID](const SharedNodePointer& node)->bool { @@ -787,63 +787,36 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID) { } else { return false; } - }, [&nodeID, this](const SharedNodePointer& destinationNode) { + }, [&nodeID, ignoreEnabled, this](const SharedNodePointer& destinationNode) { // create a reliable NLPacket with space for the ignore UUID - auto ignorePacket = NLPacket::create(PacketType::NodeIgnoreRequest, NUM_BYTES_RFC4122_UUID, true); + auto ignorePacket = NLPacket::create(PacketType::NodeIgnoreRequest, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); // write the node ID to the packet ignorePacket->write(nodeID.toRfc4122()); + ignorePacket->writePrimitive(ignoreEnabled); - qCDebug(networking) << "Sending packet to ignore node" << uuidStringWithoutCurlyBraces(nodeID); + qCDebug(networking) << "Sending packet to" << (ignoreEnabled ? "ignore" : "unignore") << "node" << uuidStringWithoutCurlyBraces(nodeID); // send off this ignore packet reliably to the matching node sendPacket(std::move(ignorePacket), *destinationNode); }); - QReadLocker setLocker { &_ignoredSetLock }; + QReadLocker setLocker { &_ignoredSetLock }; // write lock for insert and unsafe_erase - // add this nodeID to our set of ignored IDs - _ignoredNodeIDs.insert(nodeID); - - emit ignoredNode(nodeID); + if (ignoreEnabled) { + // add this nodeID to our set of ignored IDs + _ignoredNodeIDs.insert(nodeID); + emit ignoredNode(nodeID); + } else { + _ignoredNodeIDs.unsafe_erase(nodeID); + emit unignoredNode(nodeID); + } } else { qWarning() << "NodeList::ignoreNodeBySessionID called with an invalid ID or an ID which matches the current session ID."; } } -void NodeList::unignoreNodeBySessionID(const QUuid& nodeID) { - // enumerate the nodes to send a reliable unignore packet to each that can leverage it - if (!nodeID.isNull() && _sessionUUID != nodeID) { - eachMatchingNode([&nodeID](const SharedNodePointer& node)->bool { - if (node->getType() == NodeType::AudioMixer || node->getType() == NodeType::AvatarMixer) { - return true; - } else { - return false; - } - }, [&nodeID, this](const SharedNodePointer& destinationNode) { - // create a reliable NLPacket with space for the unignore UUID - auto ignorePacket = NLPacket::create(PacketType::NodeUnignoreRequest, NUM_BYTES_RFC4122_UUID, true); - - // write the node ID to the packet - ignorePacket->write(nodeID.toRfc4122()); - - qCDebug(networking) << "Sending packet to unignore node" << uuidStringWithoutCurlyBraces(nodeID); - - // send off this unignore packet reliably to the matching node - sendPacket(std::move(ignorePacket), *destinationNode); - }); - - QWriteLocker setLocker { &_ignoredSetLock }; // write lock for unsafe_erase - _ignoredNodeIDs.unsafe_erase(nodeID); - - emit unignoredNode(nodeID); - - } else { - qWarning() << "NodeList::unignoreNodeBySessionID called with an invalid ID or an ID which matches the current session ID."; - } -} - bool NodeList::isIgnoringNode(const QUuid& nodeID) const { QReadLocker setLocker { &_ignoredSetLock }; return _ignoredNodeIDs.find(nodeID) != _ignoredNodeIDs.cend(); @@ -876,6 +849,18 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { } } +void NodeList::processPersonalMuteStatusReply(QSharedPointer message) { + // read the UUID from the packet + QString nodeUUIDString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString(); + // read the personal mute status + bool isPersonalMuted; + message->readPrimitive(&isPersonalMuted); + + qCDebug(networking) << "Got personal muted status" << isPersonalMuted << "for node" << nodeUUIDString; + + emit personalMuteStatusReply(nodeUUIDString, isPersonalMuted); +} + void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled) { // cannot personal mute yourself, or nobody if (!nodeID.isNull() && _sessionUUID != nodeID) { diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index b8022c2d72..1e0a7fa8d1 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -76,17 +76,16 @@ public: void toggleIgnoreRadius() { ignoreNodesInRadius(!getIgnoreRadiusEnabled()); } void enableIgnoreRadius() { ignoreNodesInRadius(true); } void disableIgnoreRadius() { ignoreNodesInRadius(false); } - void ignoreNodeBySessionID(const QUuid& nodeID); - void unignoreNodeBySessionID(const QUuid& nodeID); + void ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled); bool isIgnoringNode(const QUuid& nodeID) const; void personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled); + void requestPersonalMuteStatus(const QUuid& nodeID); void kickNodeBySessionID(const QUuid& nodeID); void muteNodeBySessionID(const QUuid& nodeID); void requestUsernameFromSessionID(const QUuid& nodeID); bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool isRequesting); - void requestPersonalMuteStatus(const QUuid& nodeID); public slots: void reset(); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 76c5120823..78ae1e7ff0 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -105,7 +105,6 @@ public: UsernameFromIDReply, ViewFrustum, RequestsDomainListData, - NodeUnignoreRequest, NodePersonalMuteRequest, NodePersonalMuteStatusRequest, NodePersonalMuteStatusReply, diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 0d6983a1e9..b558c2572d 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -24,14 +24,14 @@ UsersScriptingInterface::UsersScriptingInterface() { connect(nodeList.data(), &NodeList::personalMuteStatusReply, this, &UsersScriptingInterface::personalMuteStatusReply); } -void UsersScriptingInterface::ignore(const QUuid& nodeID) { +void UsersScriptingInterface::ignore(const QUuid& nodeID, bool ignoreEnabled) { // ask the NodeList to ignore this user (based on the session ID of their node) - DependencyManager::get()->ignoreNodeBySessionID(nodeID); + DependencyManager::get()->ignoreNodeBySessionID(nodeID, ignoreEnabled); } -void UsersScriptingInterface::unignore(const QUuid& nodeID) { - // ask the NodeList to ignore this user (based on the session ID of their node) - DependencyManager::get()->unignoreNodeBySessionID(nodeID); +void UsersScriptingInterface::requestIgnoreStatus(const QUuid& nodeID) { + // ask the Audio Mixer via the NodeList for the Personal Mute status associated with the given session ID + DependencyManager::get()->isIgnoringNode(nodeID); } void UsersScriptingInterface::personalMute(const QUuid& nodeID, bool muteEnabled) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 31024defc6..05082602b8 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -35,9 +35,16 @@ public slots: * Ignore another user. * @function Users.ignore * @param {nodeID} nodeID The node or session ID of the user you want to ignore. + * @param {bool} enable True for ignored; false for un-ignored. */ - void ignore(const QUuid& nodeID); - void unignore(const QUuid& nodeID); + void ignore(const QUuid& nodeID, bool ignoreEnabled); + + /**jsdoc + * Requests a bool containing whether you have ignored the given Avatar UUID. + * @function Users.requestIgnoreStatus + * @param {nodeID} nodeID The node or session ID of the user whose ignore status you want. + */ + void requestIgnoreStatus(const QUuid& nodeID); /**jsdoc * Mute another user for you and you only. @@ -48,7 +55,7 @@ public slots: void personalMute(const QUuid& nodeID, bool muteEnabled); /**jsdoc - * Requests a bool containing whether you have given the given Avatar UUID. + * Requests a bool containing whether you have personally muted the given Avatar UUID. * @function Users.requestPersonalMuteStatus * @param {nodeID} nodeID The node or session ID of the user whose personal mute status you want. */ @@ -131,6 +138,12 @@ signals: */ void personalMuteStatusReply(const QString& nodeID, bool isPersonalMuted); + /**jsdoc + * Notifies scripts of the Ignore status associated with a UUID. + * @function Users.ignoreStatusReply + */ + void ignoreStatusReply(const QString& nodeID, bool isIgnored); + private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index f69a4eb369..2e35ff3d6e 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -133,9 +133,11 @@ function populateUserList() { Users.requestUsernameFromID(id); } // Request personal mute status from AudioMixer + // and ignore status from AudioMixer/AvatarMixer // (as long as we're not requesting it for our own ID) if (id) { Users.requestPersonalMuteStatus(id); + Users.requestIgnoreStatus(id); } data.push(avatarPalDatum); if (id) { // No overlay for ourself. @@ -171,6 +173,14 @@ function personalMuteStatusReply(id, isPersonalMuted) { pal.sendToQml({ method: 'updatePersonalMutedStatus', params: data }); } +// The function that handles the ignored status from the AudioMixer/AvatarMixer +function ignoreStatusReply(id, isIgnored) { + var data = [id, isIgnored]; + print('Ignored Status Data:', JSON.stringify(data)); + // Ship the data off to QML + pal.sendToQml({ method: 'updateIgnoredStatus', params: data }); +} + var pingPong = true; function updateOverlays() { var eye = Camera.position; @@ -342,6 +352,7 @@ pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); Users.personalMuteStatusReply.connect(personalMuteStatusReply); +Users.ignoreStatusReply.connect(ignoreStatusReply); function onIgnore(sessionId) { // make it go away in the usual way, since we'll still get data keeping it live // Why doesn't this work from .qml? (crashes) @@ -360,6 +371,7 @@ Script.scriptEnding.connect(function () { Users.usernameFromIDReply.disconnect(usernameFromIDReply); Users.ignoredNode.disconnect(onIgnore); Users.personalMuteStatusReply.disconnect(personalMuteStatusReply); + Users.ignoreStatusReply.disconnect(ignoreStatusReply); off(); }); From bb2b48d4246e77b0c7337a86d8af1205152a560e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 29 Dec 2016 11:35:20 -0800 Subject: [PATCH 20/45] Getting very close. --- assignment-client/src/audio/AudioMixer.h | 1 - assignment-client/src/avatars/AvatarMixer.h | 1 - interface/resources/qml/hifi/Pal.qml | 10 +--------- libraries/networking/src/NodeList.cpp | 15 ++------------- .../script-engine/src/UsersScriptingInterface.cpp | 6 +++--- .../script-engine/src/UsersScriptingInterface.h | 12 +++--------- scripts/system/pal.js | 12 +----------- 7 files changed, 10 insertions(+), 47 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 353db84a15..5c98475790 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -62,7 +62,6 @@ private slots: void handleNegotiateAudioFormat(QSharedPointer message, SharedNodePointer sendingNode); void handleNodeKilled(SharedNodePointer killedNode); void handleNodeIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); - void handleNodeUnignoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodePersonalMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodePersonalMuteStatusRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 865dd36106..5d54622ac9 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -41,7 +41,6 @@ private slots: void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message); void handleNodeIgnoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); - void handleNodeUnignoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleRequestsDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode); void domainSettingsRequestComplete(); diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 07f9386b2a..fc55916832 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -398,7 +398,6 @@ Item { myData.audioLevel = audioLevel; myCard.audioLevel = audioLevel; // Defensive programming } else { - console.log("userid:" + userId); var userIndex = findSessionIndex(userId); userModel.get(userIndex).audioLevel = audioLevel; userData[userIndex].audioLevel = audioLevel; // Defensive programming @@ -410,14 +409,7 @@ Item { var enabled = message.params[1]; var userIndex = findSessionIndex(userId); userModel.get(userIndex).personalMute.property = enabled; - userData[userIndex].personalMute.property = enabled; // Defensive programming - break; - case 'updateIgnoredStatus': - var userId = message.params[0]; - var enabled = message.params[1]; - var userIndex = findSessionIndex(userId); - userModel.get(userIndex).ignore.property = enabled; - userData[userIndex].ignore.property = enabled; // Defensive programming + userData[userIndex].personalMute.property = enabled; // Defensive programming break; default: console.log('Unrecognized message:', JSON.stringify(message)); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 89b5590025..0657b2c5ac 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -795,7 +795,8 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { ignorePacket->write(nodeID.toRfc4122()); ignorePacket->writePrimitive(ignoreEnabled); - qCDebug(networking) << "Sending packet to" << (ignoreEnabled ? "ignore" : "unignore") << "node" << uuidStringWithoutCurlyBraces(nodeID); + qCDebug(networking) << "Sending packet to" << (destinationNode->getType() == NodeType::AudioMixer ? "AudioMixer" : "AvatarMixer") << "to" + << (ignoreEnabled ? "ignore" : "unignore") << "node" << uuidStringWithoutCurlyBraces(nodeID); // send off this ignore packet reliably to the matching node sendPacket(std::move(ignorePacket), *destinationNode); @@ -849,18 +850,6 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { } } -void NodeList::processPersonalMuteStatusReply(QSharedPointer message) { - // read the UUID from the packet - QString nodeUUIDString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString(); - // read the personal mute status - bool isPersonalMuted; - message->readPrimitive(&isPersonalMuted); - - qCDebug(networking) << "Got personal muted status" << isPersonalMuted << "for node" << nodeUUIDString; - - emit personalMuteStatusReply(nodeUUIDString, isPersonalMuted); -} - void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled) { // cannot personal mute yourself, or nobody if (!nodeID.isNull() && _sessionUUID != nodeID) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index b558c2572d..f18eef27cb 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -29,9 +29,9 @@ void UsersScriptingInterface::ignore(const QUuid& nodeID, bool ignoreEnabled) { DependencyManager::get()->ignoreNodeBySessionID(nodeID, ignoreEnabled); } -void UsersScriptingInterface::requestIgnoreStatus(const QUuid& nodeID) { - // ask the Audio Mixer via the NodeList for the Personal Mute status associated with the given session ID - DependencyManager::get()->isIgnoringNode(nodeID); +bool UsersScriptingInterface::getIgnoreStatus(const QUuid& nodeID) { + // ask the NodeList for the Ignore status associated with the given session ID + return DependencyManager::get()->isIgnoringNode(nodeID); } void UsersScriptingInterface::personalMute(const QUuid& nodeID, bool muteEnabled) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 05082602b8..f7b25a72c2 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -40,11 +40,11 @@ public slots: void ignore(const QUuid& nodeID, bool ignoreEnabled); /**jsdoc - * Requests a bool containing whether you have ignored the given Avatar UUID. - * @function Users.requestIgnoreStatus + * Gets a bool containing whether you have ignored the given Avatar UUID. + * @function Users.getIgnoreStatus * @param {nodeID} nodeID The node or session ID of the user whose ignore status you want. */ - void requestIgnoreStatus(const QUuid& nodeID); + bool getIgnoreStatus(const QUuid& nodeID); /**jsdoc * Mute another user for you and you only. @@ -138,12 +138,6 @@ signals: */ void personalMuteStatusReply(const QString& nodeID, bool isPersonalMuted); - /**jsdoc - * Notifies scripts of the Ignore status associated with a UUID. - * @function Users.ignoreStatusReply - */ - void ignoreStatusReply(const QString& nodeID, bool isIgnored); - private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 2e35ff3d6e..a21eb8c6ef 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -137,7 +137,7 @@ function populateUserList() { // (as long as we're not requesting it for our own ID) if (id) { Users.requestPersonalMuteStatus(id); - Users.requestIgnoreStatus(id); + avatarPalDatum['ignore'] = Users.getIgnoreStatus(id); } data.push(avatarPalDatum); if (id) { // No overlay for ourself. @@ -173,14 +173,6 @@ function personalMuteStatusReply(id, isPersonalMuted) { pal.sendToQml({ method: 'updatePersonalMutedStatus', params: data }); } -// The function that handles the ignored status from the AudioMixer/AvatarMixer -function ignoreStatusReply(id, isIgnored) { - var data = [id, isIgnored]; - print('Ignored Status Data:', JSON.stringify(data)); - // Ship the data off to QML - pal.sendToQml({ method: 'updateIgnoredStatus', params: data }); -} - var pingPong = true; function updateOverlays() { var eye = Camera.position; @@ -352,7 +344,6 @@ pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); Users.personalMuteStatusReply.connect(personalMuteStatusReply); -Users.ignoreStatusReply.connect(ignoreStatusReply); function onIgnore(sessionId) { // make it go away in the usual way, since we'll still get data keeping it live // Why doesn't this work from .qml? (crashes) @@ -371,7 +362,6 @@ Script.scriptEnding.connect(function () { Users.usernameFromIDReply.disconnect(usernameFromIDReply); Users.ignoredNode.disconnect(onIgnore); Users.personalMuteStatusReply.disconnect(personalMuteStatusReply); - Users.ignoreStatusReply.disconnect(ignoreStatusReply); off(); }); From 24eac771159b57276001409a12d7f2d6412ca85f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 29 Dec 2016 11:57:51 -0800 Subject: [PATCH 21/45] I'm about to refactor this. --- assignment-client/src/audio/AudioMixer.cpp | 2 +- interface/resources/qml/hifi/Pal.qml | 6 +++--- libraries/networking/src/Node.cpp | 18 ++++++++++-------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index c9b00eb874..d1f7abf379 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -245,7 +245,7 @@ void AudioMixer::handleNodePersonalMuteRequestPacket(QSharedPointerremoveIgnoredNode(ignoredUUID); + sendingNode->removeIgnoredNode(ignoredUUID); } } else { qWarning() << "Node::handlePersonalMutedNode called with null ID or ID of personal muting node."; diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index fc55916832..2c75e8f049 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -386,7 +386,7 @@ Item { // Get the index in userModel and userData associated with the passed UUID var userIndex = findSessionIndex(userId); // Set the userName appropriately - userModel.get(userIndex).userName = userName; + userModel.setProperty(userIndex, "userName", userName); userData[userIndex].userName = userName; // Defensive programming } break; @@ -399,7 +399,7 @@ Item { myCard.audioLevel = audioLevel; // Defensive programming } else { var userIndex = findSessionIndex(userId); - userModel.get(userIndex).audioLevel = audioLevel; + userModel.setProperty(userIndex, "audioLevel", audioLevel); userData[userIndex].audioLevel = audioLevel; // Defensive programming } } @@ -408,7 +408,7 @@ Item { var userId = message.params[0]; var enabled = message.params[1]; var userIndex = findSessionIndex(userId); - userModel.get(userIndex).personalMute.property = enabled; + userModel.setProperty(userIndex, "personalMute", enabled); userData[userIndex].personalMute.property = enabled; // Defensive programming break; default: diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index b2670fd9b0..484ea882b5 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -82,15 +82,17 @@ void Node::updateClockSkewUsec(qint64 clockSkewSample) { } void Node::parseIgnoreRequestMessage(QSharedPointer message) { - // parse out the UUID being ignored from the packet - QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - bool addToIgnore; - message->readPrimitive(&addToIgnore); + while (message->getBytesLeftToRead()) { + // parse out the UUID being ignored from the packet + QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + bool addToIgnore; + message->readPrimitive(&addToIgnore); - if (addToIgnore) { - addIgnoredNode(ignoredUUID); - } else { - removeIgnoredNode(ignoredUUID); + if (addToIgnore) { + addIgnoredNode(ignoredUUID); + } else { + removeIgnoredNode(ignoredUUID); + } } } From 10b5b957f276ac468cc42c59576647383fa49186 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 29 Dec 2016 13:09:40 -0800 Subject: [PATCH 22/45] Ahh...It's beautiful and simple now. --- assignment-client/src/audio/AudioMixer.cpp | 51 ------- assignment-client/src/audio/AudioMixer.h | 2 - interface/resources/qml/hifi/Pal.qml | 14 +- libraries/networking/src/NodeList.cpp | 137 ++++++++++-------- libraries/networking/src/NodeList.h | 6 +- libraries/networking/src/udt/PacketHeaders.h | 5 +- .../src/UsersScriptingInterface.cpp | 7 +- .../src/UsersScriptingInterface.h | 8 +- scripts/system/pal.js | 12 +- 9 files changed, 88 insertions(+), 154 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index d1f7abf379..01715497b1 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -65,8 +65,6 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::NegotiateAudioFormat, this, "handleNegotiateAudioFormat"); packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); - packetReceiver.registerListener(PacketType::NodePersonalMuteRequest, this, "handleNodePersonalMuteRequestPacket"); - packetReceiver.registerListener(PacketType::NodePersonalMuteStatusRequest, this, "handleNodePersonalMuteStatusRequestPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); @@ -227,55 +225,6 @@ void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer p sendingNode->parseIgnoreRequestMessage(packet); } -void AudioMixer::handleNodePersonalMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { - // parse out the UUID being muted from the packet - QUuid ignoredUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - bool enabled; - packet->readPrimitive(&enabled); - - if (!ignoredUUID.isNull() && ignoredUUID != _uuid) { - if (enabled) { - qDebug() << "Adding" << uuidStringWithoutCurlyBraces(ignoredUUID) << "to personally muted set for" - << uuidStringWithoutCurlyBraces(_uuid); - - // Add the session UUID to the set of personally muted ones for this listening node - sendingNode->addIgnoredNode(ignoredUUID); - } else { - qDebug() << "Removing" << uuidStringWithoutCurlyBraces(ignoredUUID) << "from personally muted set for" - << uuidStringWithoutCurlyBraces(_uuid); - - // Remove the session UUID to the set of personally muted ones for this listening node - sendingNode->removeIgnoredNode(ignoredUUID); - } - } else { - qWarning() << "Node::handlePersonalMutedNode called with null ID or ID of personal muting node."; - } -} - -void AudioMixer::handleNodePersonalMuteStatusRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { - // parse out the UUID whose personal mute status is being requested from the packet - QUuid UUIDToCheck = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - - if (!UUIDToCheck.isNull()) { - // First, make sure we actually have a node with this UUID - auto limitedNodeList = DependencyManager::get(); - auto matchingNode = limitedNodeList->nodeWithUUID(UUIDToCheck); - - // If we do have a matching node... - if (matchingNode) { - auto personalMuteStatusPacket = NLPacket::create(PacketType::NodePersonalMuteStatusReply, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); - bool isMuted = sendingNode->isIgnoringNodeWithID(UUIDToCheck); - - // write the node ID to the packet - personalMuteStatusPacket->write(UUIDToCheck.toRfc4122()); - personalMuteStatusPacket->writePrimitive(isMuted); - - qDebug() << "Personal Mute Status: node" << uuidStringWithoutCurlyBraces(UUIDToCheck) << "mute status:" << isMuted; - - limitedNodeList->sendPacket(std::move(personalMuteStatusPacket), *sendingNode); - } - } -} void AudioMixer::handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { sendingNode->parseIgnoreRadiusRequestMessage(packet); } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 5c98475790..59cdec7732 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -62,8 +62,6 @@ private slots: void handleNegotiateAudioFormat(QSharedPointer message, SharedNodePointer sendingNode); void handleNodeKilled(SharedNodePointer killedNode); void handleNodeIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); - void handleNodePersonalMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); - void handleNodePersonalMuteStatusRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleKillAvatarPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodeMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 2c75e8f049..e6a762ecd8 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -202,7 +202,7 @@ Item { // This CheckBox belongs in the columns that contain the action buttons ("Mute", "Ban", etc) HifiControls.CheckBox { - visible: isCheckBox + visible: styleData.role === "personalMute" ? (model["ignore"] === true ? false : true) : isCheckBox anchors.centerIn: parent checked: model[styleData.role] boxSize: 24 @@ -211,7 +211,10 @@ Item { var datum = userData[model.userIndex] datum[styleData.role] = model[styleData.role] = newValue if (styleData.role === "personalMute" || styleData.role === "ignore") { - Users[styleData.role](model.sessionId, newValue) + Users[styleData.role](model.sessionId, newValue) + if (styleData.role === "ignore") { + datum["personalMute"] = model["personalMute"] = newValue + } } else { Users[styleData.role](model.sessionId) // Just for now, while we cannot undo things: @@ -404,13 +407,6 @@ Item { } } break; - case 'updatePersonalMutedStatus': - var userId = message.params[0]; - var enabled = message.params[1]; - var userIndex = findSessionIndex(userId); - userModel.setProperty(userIndex, "personalMute", enabled); - userData[userIndex].personalMute.property = enabled; // Defensive programming - break; default: console.log('Unrecognized message:', JSON.stringify(message)); } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 0657b2c5ac..8b4cb41cc9 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -129,7 +129,6 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) packetReceiver.registerListener(PacketType::DomainServerPathResponse, this, "processDomainServerPathResponse"); packetReceiver.registerListener(PacketType::DomainServerRemovedNode, this, "processDomainServerRemovedNode"); packetReceiver.registerListener(PacketType::UsernameFromIDReply, this, "processUsernameFromIDReply"); - packetReceiver.registerListener(PacketType::NodePersonalMuteStatusReply, this, "processPersonalMuteStatusReply"); } qint64 NodeList::sendStats(QJsonObject statsObject, HifiSockAddr destination) { @@ -802,14 +801,18 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { sendPacket(std::move(ignorePacket), *destinationNode); }); - QReadLocker setLocker { &_ignoredSetLock }; // write lock for insert and unsafe_erase + QReadLocker ignoredSetLocker { &_ignoredSetLock }; // write lock for insert and unsafe_erase + QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; // write lock for insert and unsafe_erase if (ignoreEnabled) { // add this nodeID to our set of ignored IDs _ignoredNodeIDs.insert(nodeID); + // add this nodeID to our set of personal muted IDs + _personalMutedNodeIDs.insert(nodeID); emit ignoredNode(nodeID); } else { _ignoredNodeIDs.unsafe_erase(nodeID); + _personalMutedNodeIDs.unsafe_erase(nodeID); emit unignoredNode(nodeID); } @@ -819,16 +822,82 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { } bool NodeList::isIgnoringNode(const QUuid& nodeID) const { - QReadLocker setLocker { &_ignoredSetLock }; + QReadLocker ignoredSetLocker{ &_ignoredSetLock }; return _ignoredNodeIDs.find(nodeID) != _ignoredNodeIDs.cend(); } +void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled) { + // cannot personal mute yourself, or nobody + if (!nodeID.isNull() && _sessionUUID != nodeID) { + auto audioMixer = soloNodeOfType(NodeType::AudioMixer); + if (audioMixer) { + if (isIgnoringNode(nodeID)) { + qCDebug(networking) << "You can't personally mute or unmute a node you're already ignoring."; + } + else { + // setup the packet + auto personalMutePacket = NLPacket::create(PacketType::NodeIgnoreRequest, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); + + // write the node ID to the packet + personalMutePacket->write(nodeID.toRfc4122()); + personalMutePacket->writePrimitive(muteEnabled); + + qCDebug(networking) << "Sending Personal Mute Packet to" << (muteEnabled ? "mute" : "unmute") << "node" << uuidStringWithoutCurlyBraces(nodeID); + + sendPacket(std::move(personalMutePacket), *audioMixer); + + QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; // write lock for insert and unsafe_erase + + if (muteEnabled) { + // add this nodeID to our set of personal muted IDs + _personalMutedNodeIDs.insert(nodeID); + } else { + _personalMutedNodeIDs.unsafe_erase(nodeID); + } + } + } else { + qWarning() << "Couldn't find audio mixer to send node personal mute request"; + } + } else { + qWarning() << "NodeList::personalMuteNodeBySessionID called with an invalid ID or an ID which matches the current session ID."; + } +} + +bool NodeList::isPersonalMutingNode(const QUuid& nodeID) const { + QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; + return _personalMutedNodeIDs.find(nodeID) != _personalMutedNodeIDs.cend(); +} + void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { - if (newNode->getType() == NodeType::AudioMixer || newNode->getType() == NodeType::AvatarMixer) { + if (newNode->getType() == NodeType::AudioMixer) { // this is a mixer that we just added - it's unlikely it knows who we were previously ignoring in this session, // so send that list along now (assuming it isn't empty) - QReadLocker setLocker { &_ignoredSetLock }; + QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; + + if (_personalMutedNodeIDs.size() > 0) { + // setup a packet list so we can send the stream of ignore IDs + auto personalMutePacketList = NLPacketList::create(PacketType::NodeIgnoreRequest, QByteArray(), true); + + // enumerate the ignored IDs and write them to the packet list + auto it = _personalMutedNodeIDs.cbegin(); + while (it != _personalMutedNodeIDs.end()) { + personalMutePacketList->write(it->toRfc4122()); + ++it; + } + + // send this NLPacketList to the new node + sendPacketList(std::move(personalMutePacketList), *newNode); + } + + // also send them the current ignore radius state. + sendIgnoreRadiusStateToNode(newNode); + } + if (newNode->getType() == NodeType::AvatarMixer) { + // this is a mixer that we just added - it's unlikely it knows who we were previously ignoring in this session, + // so send that list along now (assuming it isn't empty) + + QReadLocker ignoredSetLocker{ &_ignoredSetLock }; if (_ignoredNodeIDs.size() > 0) { // setup a packet list so we can send the stream of ignore IDs @@ -850,64 +919,6 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { } } -void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled) { - // cannot personal mute yourself, or nobody - if (!nodeID.isNull() && _sessionUUID != nodeID) { - auto audioMixer = soloNodeOfType(NodeType::AudioMixer); - if (audioMixer) { - // setup the packet - auto personalMutePacket = NLPacket::create(PacketType::NodePersonalMuteRequest, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); - - // write the node ID to the packet - personalMutePacket->write(nodeID.toRfc4122()); - personalMutePacket->writePrimitive(muteEnabled); - - qCDebug(networking) << "Sending Personal Mute Packet to" << (muteEnabled ? "mute" : "unmute") << "node" << uuidStringWithoutCurlyBraces(nodeID); - - sendPacket(std::move(personalMutePacket), *audioMixer); - } else { - qWarning() << "Couldn't find audio mixer to send node personal mute request"; - } - } else { - qWarning() << "NodeList::personalMuteNodeBySessionID called with an invalid ID or an ID which matches the current session ID."; - } -} - -void NodeList::requestPersonalMuteStatus(const QUuid& nodeID) { - // cannot personal mute yourself, or nobody; don't bother checking the status - if (!nodeID.isNull() && _sessionUUID != nodeID) { - auto audioMixer = soloNodeOfType(NodeType::AudioMixer); - if (audioMixer) { - // send a request to the audio mixer to get the personal mute status associated with the given session ID - // setup the packet - auto personalMuteStatusPacket = NLPacket::create(PacketType::NodePersonalMuteStatusRequest, NUM_BYTES_RFC4122_UUID, true); - - // write the node ID to the packet - personalMuteStatusPacket->write(nodeID.toRfc4122()); - - qCDebug(networking) << "Sending Personal Mute Status Request Packet for node" << uuidStringWithoutCurlyBraces(nodeID); - - sendPacket(std::move(personalMuteStatusPacket), *audioMixer); - } else { - qWarning() << "Couldn't find audio mixer to send node personal mute status request"; - } - } else { - qWarning() << "NodeList::requestPersonalMuteStatus called with an invalid ID or an ID which matches the current session ID."; - } -} - -void NodeList::processPersonalMuteStatusReply(QSharedPointer message) { - // read the UUID from the packet - QString nodeUUIDString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString(); - // read the personal mute status - bool isPersonalMuted; - message->readPrimitive(&isPersonalMuted); - - qCDebug(networking) << "Got personal muted status" << isPersonalMuted << "for node" << nodeUUIDString; - - emit personalMuteStatusReply(nodeUUIDString, isPersonalMuted); -} - void NodeList::kickNodeBySessionID(const QUuid& nodeID) { // send a request to domain-server to kick the node with the given session ID // the domain-server will handle the persistence of the kick (via username or IP) diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 1e0a7fa8d1..ba19f56f9f 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -79,7 +79,7 @@ public: void ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled); bool isIgnoringNode(const QUuid& nodeID) const; void personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled); - void requestPersonalMuteStatus(const QUuid& nodeID); + bool isPersonalMutingNode(const QUuid& nodeID) const; void kickNodeBySessionID(const QUuid& nodeID); void muteNodeBySessionID(const QUuid& nodeID); @@ -105,7 +105,6 @@ public slots: void processICEPingPacket(QSharedPointer message); void processUsernameFromIDReply(QSharedPointer message); - void processPersonalMuteStatusReply(QSharedPointer message); #if (PR_BUILD || DEV_BUILD) void toggleSendNewerDSConnectVersion(bool shouldSendNewerVersion) { _shouldSendNewerVersion = shouldSendNewerVersion; } @@ -118,7 +117,6 @@ signals: void unignoredNode(const QUuid& nodeID); void ignoreRadiusEnabledChanged(bool isIgnored); void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); - void personalMuteStatusReply(const QString& nodeID, bool isPersonalMuted); private slots: void stopKeepalivePingTimer(); @@ -164,6 +162,8 @@ private: mutable QReadWriteLock _ignoredSetLock; tbb::concurrent_unordered_set _ignoredNodeIDs; + mutable QReadWriteLock _personalMutedSetLock; + tbb::concurrent_unordered_set _personalMutedNodeIDs; void sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationNode); Setting::Handle _ignoreRadiusEnabled { "IgnoreRadiusEnabled", true }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 78ae1e7ff0..1867d2193c 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -105,10 +105,7 @@ public: UsernameFromIDReply, ViewFrustum, RequestsDomainListData, - NodePersonalMuteRequest, - NodePersonalMuteStatusRequest, - NodePersonalMuteStatusReply, - LAST_PACKET_TYPE = NodePersonalMuteStatusReply + LAST_PACKET_TYPE = RequestsDomainListData }; }; diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index f18eef27cb..58680b944d 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -21,7 +21,6 @@ UsersScriptingInterface::UsersScriptingInterface() { connect(nodeList.data(), &NodeList::usernameFromIDReply, this, &UsersScriptingInterface::usernameFromIDReply); connect(nodeList.data(), &NodeList::ignoredNode, this, &UsersScriptingInterface::ignoredNode); connect(nodeList.data(), &NodeList::unignoredNode, this, &UsersScriptingInterface::unignoredNode); - connect(nodeList.data(), &NodeList::personalMuteStatusReply, this, &UsersScriptingInterface::personalMuteStatusReply); } void UsersScriptingInterface::ignore(const QUuid& nodeID, bool ignoreEnabled) { @@ -40,9 +39,9 @@ void UsersScriptingInterface::personalMute(const QUuid& nodeID, bool muteEnabled DependencyManager::get()->personalMuteNodeBySessionID(nodeID, muteEnabled); } -void UsersScriptingInterface::requestPersonalMuteStatus(const QUuid& nodeID) { - // ask the Audio Mixer via the NodeList for the Personal Mute status associated with the given session ID - DependencyManager::get()->requestPersonalMuteStatus(nodeID); +bool UsersScriptingInterface::getPersonalMuteStatus(const QUuid& nodeID) { + // ask the NodeList for the Personal Mute status associated with the given session ID + return DependencyManager::get()->isPersonalMutingNode(nodeID); } void UsersScriptingInterface::kick(const QUuid& nodeID) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index f7b25a72c2..223ddb879b 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -59,7 +59,7 @@ public slots: * @function Users.requestPersonalMuteStatus * @param {nodeID} nodeID The node or session ID of the user whose personal mute status you want. */ - void requestPersonalMuteStatus(const QUuid& nodeID); + bool getPersonalMuteStatus(const QUuid& nodeID); /**jsdoc * Kick another user. @@ -132,12 +132,6 @@ signals: */ void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); - /**jsdoc - * Notifies scripts of the Personal Mute status associated with a UUID. - * @function Users.usernameFromIDReply - */ - void personalMuteStatusReply(const QString& nodeID, bool isPersonalMuted); - private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index a21eb8c6ef..d2312e9ed4 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -136,7 +136,7 @@ function populateUserList() { // and ignore status from AudioMixer/AvatarMixer // (as long as we're not requesting it for our own ID) if (id) { - Users.requestPersonalMuteStatus(id); + avatarPalDatum['personalMute'] = Users.getPersonalMuteStatus(id); avatarPalDatum['ignore'] = Users.getIgnoreStatus(id); } data.push(avatarPalDatum); @@ -165,14 +165,6 @@ function usernameFromIDReply(id, username, machineFingerprint) { pal.sendToQml({ method: 'updateUsername', params: data }); } -// The function that handles the personal muted status from the AudioMixer -function personalMuteStatusReply(id, isPersonalMuted) { - var data = [id, isPersonalMuted]; - print('Personal Muted Status Data:', JSON.stringify(data)); - // Ship the data off to QML - pal.sendToQml({ method: 'updatePersonalMutedStatus', params: data }); -} - var pingPong = true; function updateOverlays() { var eye = Camera.position; @@ -343,7 +335,6 @@ button.clicked.connect(onClicked); pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); -Users.personalMuteStatusReply.connect(personalMuteStatusReply); function onIgnore(sessionId) { // make it go away in the usual way, since we'll still get data keeping it live // Why doesn't this work from .qml? (crashes) @@ -361,7 +352,6 @@ Script.scriptEnding.connect(function () { pal.closed.disconnect(off); Users.usernameFromIDReply.disconnect(usernameFromIDReply); Users.ignoredNode.disconnect(onIgnore); - Users.personalMuteStatusReply.disconnect(personalMuteStatusReply); off(); }); From f96508e197b6c62f5770554b9c16026b2f14a1df Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 29 Dec 2016 14:19:40 -0800 Subject: [PATCH 23/45] Gotta fix these bugs... --- .../resources/qml/controls-uit/CheckBox.qml | 17 +++++++- interface/resources/qml/hifi/Pal.qml | 40 ++++++++++--------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index aec579755a..503e6b8739 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -27,7 +27,9 @@ Original.CheckBox { readonly property int checkRadius: 2 style: CheckBoxStyle { - indicator: Rectangle { + indicator: + + Rectangle { id: box width: boxSize height: boxSize @@ -51,6 +53,19 @@ Original.CheckBox { } } + Rectangle { + id: disabledOverlay + visible: !enabled + z: 100 + width: boxSize + height: boxSize + radius: boxRadius + border.width: 1 + border.color: hifi.colors.baseGrayHighlight + color: hifi.colors.baseGrayHighlight + opacity: 0.5 + } + Rectangle { visible: pressed || hovered anchors.centerIn: parent diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index e6a762ecd8..0d5cdb0503 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -40,7 +40,7 @@ Item { property int myCardHeight: 70 property int rowHeight: 70 property int actionButtonWidth: 75 - property int nameCardWidth: width - actionButtonWidth*(iAmAdmin ? 4 : 2) + property int nameCardWidth: width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 // This contains the current user's NameCard and will contain other information in the future Rectangle { @@ -85,7 +85,7 @@ Item { Rectangle { id: adminTab // Size - width: actionButtonWidth * 2 - 2 + width: actionButtonWidth * 2 + 2 height: 40 // Anchors anchors.bottom: myInfo.bottom @@ -116,6 +116,19 @@ Item { verticalAlignment: Text.AlignTop } } + // Separator between user and admin functions + Rectangle { + // Size + width: 2 + height: table.height + // Anchors + anchors.left: adminTab.left + anchors.top: table.top + // Properties + z: 100 + visible: iAmAdmin + color: hifi.colors.lightGrayText + } // This TableView refers to the table (below the current user's NameCard) HifiControls.Table { id: table @@ -191,7 +204,7 @@ Item { // Properties displayName: styleData.value userName: model && model.userName - audioLevel: model.audioLevel + audioLevel: model && model.audioLevel visible: !isCheckBox // Size width: nameCardWidth @@ -202,12 +215,16 @@ Item { // This CheckBox belongs in the columns that contain the action buttons ("Mute", "Ban", etc) HifiControls.CheckBox { - visible: styleData.role === "personalMute" ? (model["ignore"] === true ? false : true) : isCheckBox + visible: isCheckBox anchors.centerIn: parent - checked: model[styleData.role] + checked: model && model[styleData.role] + enabled: styleData.role === "personalMute" ? (model["ignore"] === true ? false : true) : true boxSize: 24 onClicked: { var newValue = !model[styleData.role] + if (newValue === undefined) { + newValue = false + } var datum = userData[model.userIndex] datum[styleData.role] = model[styleData.role] = newValue if (styleData.role === "personalMute" || styleData.role === "ignore") { @@ -264,19 +281,6 @@ Item { onExited: reloadButton.color = (pressed ? hifi.colors.lightGrayText: hifi.colors.darkGray) } } - - // Separator between user and admin functions - Rectangle { - // Size - width: 2 - height: table.height - // Anchors - anchors.left: adminTab.left - anchors.top: table.top - // Properties - visible: iAmAdmin - color: hifi.colors.lightGrayText - } // This Rectangle refers to the [?] popup button Rectangle { color: hifi.colors.tableBackgroundLight From be4050aa78e1b808bb0a94baa865f6b1138fa7d7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 29 Dec 2016 15:43:42 -0800 Subject: [PATCH 24/45] One bug down! --- interface/resources/qml/hifi/Pal.qml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 0d5cdb0503..a977de1c3f 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -182,7 +182,9 @@ Item { movable: false resizable: false } - model: userModel + model: ListModel { + id: userModel + } // This Rectangle refers to each Row in the table. rowDelegate: Rectangle { // The only way I know to specify a row height. @@ -217,7 +219,7 @@ Item { HifiControls.CheckBox { visible: isCheckBox anchors.centerIn: parent - checked: model && model[styleData.role] + checked: model[styleData.role] enabled: styleData.role === "personalMute" ? (model["ignore"] === true ? false : true) : true boxSize: 24 onClicked: { @@ -225,12 +227,13 @@ Item { if (newValue === undefined) { newValue = false } - var datum = userData[model.userIndex] - datum[styleData.role] = model[styleData.role] = newValue + userModel.setProperty(model.userIndex, styleData.role, newValue) + userData[model.userIndex][styleData.role] = newValue // Defensive programming if (styleData.role === "personalMute" || styleData.role === "ignore") { Users[styleData.role](model.sessionId, newValue) if (styleData.role === "ignore") { - datum["personalMute"] = model["personalMute"] = newValue + userModel.setProperty(model.userIndex, "personalMute", newValue) + userData[model.userIndex]["personalMute"] = newValue // Defensive programming } } else { Users[styleData.role](model.sessionId) @@ -238,6 +241,10 @@ Item { userData.splice(model.userIndex, 1) sortModel() } + // http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html#creating-property-bindings-from-javascript + // I'm using an explicit binding here because clicking a checkbox breaks the implicit binding as set by + // "checked: model[stayleData.role]" above. + checked = Qt.binding(function() { return model[styleData.role] }) } } } @@ -415,9 +422,6 @@ Item { console.log('Unrecognized message:', JSON.stringify(message)); } } - ListModel { - id: userModel - } function sortModel() { var sortProperty = table.getColumn(table.sortIndicatorColumn).role; var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1; From 3bd68f5e3e2213ce287577945323ae880f6ae8d6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 29 Dec 2016 16:09:31 -0800 Subject: [PATCH 25/45] Checkpoint for removing userData --- interface/resources/qml/hifi/Pal.qml | 39 +++++++++++++++++----------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index a977de1c3f..8514cb36b2 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -228,17 +228,15 @@ Item { newValue = false } userModel.setProperty(model.userIndex, styleData.role, newValue) - userData[model.userIndex][styleData.role] = newValue // Defensive programming if (styleData.role === "personalMute" || styleData.role === "ignore") { Users[styleData.role](model.sessionId, newValue) if (styleData.role === "ignore") { userModel.setProperty(model.userIndex, "personalMute", newValue) - userData[model.userIndex]["personalMute"] = newValue // Defensive programming } } else { Users[styleData.role](model.sessionId) // Just for now, while we cannot undo things: - userData.splice(model.userIndex, 1) + userModel.remove(model.userIndex, 1) sortModel() } // http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html#creating-property-bindings-from-javascript @@ -352,13 +350,11 @@ Item { } } - property var userData: [] property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set property bool iAmAdmin: false function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml - var i, data = optionalData || userData, length = data.length; - for (var i = 0; i < length; i++) { - if (data[i].sessionId === sessionId) { + for (var i = 0; i < userModel.count; i++) { + if (userModel.get(i).sessionId === sessionId) { return i; } } @@ -372,8 +368,18 @@ Item { iAmAdmin = Users.canKick; myData = data[myIndex]; data.splice(myIndex, 1); - userData = data; - sortModel(); + userModel.clear(); + var userIndex = 0; + data.forEach(function (datum) { + function init(property) { + if (datum[property] === undefined) { + datum[property] = false; + } + } + ['personalMute', 'ignore', 'mute', 'kick'].forEach(init); + datum.userIndex = userIndex++; + userModel.append(datum); + }); break; case 'select': var sessionId = message.params[0]; @@ -397,11 +403,10 @@ Item { myData.userName = userName; myCard.userName = userName; // Defensive programming } else { - // Get the index in userModel and userData associated with the passed UUID + // Get the index in userModel associated with the passed UUID var userIndex = findSessionIndex(userId); // Set the userName appropriately userModel.setProperty(userIndex, "userName", userName); - userData[userIndex].userName = userName; // Defensive programming } break; case 'updateAudioLevel': @@ -414,7 +419,6 @@ Item { } else { var userIndex = findSessionIndex(userId); userModel.setProperty(userIndex, "audioLevel", audioLevel); - userData[userIndex].audioLevel = audioLevel; // Defensive programming } } break; @@ -423,10 +427,15 @@ Item { } } function sortModel() { + var sortable = []; + for (var i = 0; i < userModel.count; i++) { + sortable.push(userModel.get(i)); + } + var sortProperty = table.getColumn(table.sortIndicatorColumn).role; var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1; var after = -1 * before; - userData.sort(function (a, b) { + sortable.sort(function (a, b) { var aValue = a[sortProperty].toString().toLowerCase(), bValue = b[sortProperty].toString().toLowerCase(); switch (true) { case (aValue < bValue): return before; @@ -437,7 +446,7 @@ Item { table.selection.clear(); userModel.clear(); var userIndex = 0; - userData.forEach(function (datum) { + sortable.forEach(function (datum) { function init(property) { if (datum[property] === undefined) { datum[property] = false; @@ -452,7 +461,7 @@ Item { function noticeSelection() { var userIds = []; table.selection.forEach(function (userIndex) { - userIds.push(userData[userIndex].sessionId); + userIds.push(userModel.get(userIndex).sessionId); }); pal.sendToScript({method: 'selected', params: userIds}); } From 0e9da875491be8fc6d04df91fc9bab234f28fa3c Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 29 Dec 2016 19:20:15 -0500 Subject: [PATCH 26/45] FetchSortCull->FetchCullSort --- interface/src/Application.cpp | 10 +++++----- .../render-utils/src/RenderDeferredTask.cpp | 2 +- libraries/render-utils/src/RenderDeferredTask.h | 16 ++++++++-------- libraries/render-utils/src/RenderForwardTask.cpp | 2 +- libraries/render-utils/src/RenderForwardTask.h | 4 ++-- ...tCullTask.cpp => RenderFetchCullSortTask.cpp} | 6 +++--- ...hSortCullTask.h => RenderFetchCullSortTask.h} | 14 +++++++------- tests/render-perf/src/main.cpp | 10 +++++----- 8 files changed, 32 insertions(+), 32 deletions(-) rename libraries/render/src/render/{RenderFetchSortCullTask.cpp => RenderFetchCullSortTask.cpp} (95%) rename libraries/render/src/render/{RenderFetchSortCullTask.h => RenderFetchCullSortTask.h} (55%) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 81053a708c..ed2f48d521 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -101,7 +101,7 @@ #include #include #include -#include +#include #include #include #include @@ -1819,13 +1819,13 @@ void Application::initializeGL() { // Set up the render engine render::CullFunctor cullFunctor = LODManager::shouldRender; _renderEngine->addJob("RenderShadowTask", cullFunctor); - const auto items = _renderEngine->addJob("FetchSortCull", cullFunctor); - assert(items.canCast()); + const auto items = _renderEngine->addJob("FetchCullSort", cullFunctor); + assert(items.canCast()); static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { - _renderEngine->addJob("RenderForwardTask", items.get()); + _renderEngine->addJob("RenderForwardTask", items.get()); } else { - _renderEngine->addJob("RenderDeferredTask", items.get()); + _renderEngine->addJob("RenderDeferredTask", items.get()); } _renderEngine->load(); _renderEngine->registerScene(_main3DScene); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 4a99795a70..1c1b760f04 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -48,7 +48,7 @@ using namespace render; extern void initOverlay3DPipelines(render::ShapePlumber& plumber); extern void initDeferredPipelines(render::ShapePlumber& plumber); -RenderDeferredTask::RenderDeferredTask(RenderFetchSortCullTask::Output items) { +RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); initDeferredPipelines(*shapePlumber); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index a52741f2a3..8a95447e67 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -13,18 +13,18 @@ #define hifi_RenderDeferredTask_h #include -#include +#include #include "LightingModel.h" class BeginGPURangeTimer { public: using JobModel = render::Job::ModelO; - + BeginGPURangeTimer(const std::string& name) : _gpuTimer(std::make_shared(name)) {} - + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer); - + protected: gpu::RangeTimerPointer _gpuTimer; }; @@ -35,12 +35,12 @@ class EndGPURangeTimer { public: using Config = GPURangeTimerConfig; using JobModel = render::Job::ModelI; - + EndGPURangeTimer() {} - + void configure(const Config& config) {} void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer); - + protected: }; @@ -196,7 +196,7 @@ class RenderDeferredTask : public render::Task { public: using JobModel = Model; - RenderDeferredTask(RenderFetchSortCullTask::Output items); + RenderDeferredTask(RenderFetchCullSortTask::Output items); }; #endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 2d96dc2e38..c3e5fec7e9 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -31,7 +31,7 @@ using namespace render; extern void initOverlay3DPipelines(render::ShapePlumber& plumber); extern void initDeferredPipelines(render::ShapePlumber& plumber); -RenderForwardTask::RenderForwardTask(RenderFetchSortCullTask::Output items) { +RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) { // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); initDeferredPipelines(*shapePlumber); diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 8615ecbec3..c653f27e3c 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -13,14 +13,14 @@ #define hifi_RenderForwardTask_h #include -#include +#include #include "LightingModel.h" class RenderForwardTask : public render::Task { public: using JobModel = Model; - RenderForwardTask(RenderFetchSortCullTask::Output items); + RenderForwardTask(RenderFetchCullSortTask::Output items); }; class PrepareFramebuffer { diff --git a/libraries/render/src/render/RenderFetchSortCullTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp similarity index 95% rename from libraries/render/src/render/RenderFetchSortCullTask.cpp rename to libraries/render/src/render/RenderFetchCullSortTask.cpp index 43066e4f1b..40d8e84568 100644 --- a/libraries/render/src/render/RenderFetchSortCullTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -1,5 +1,5 @@ // -// RenderFetchSortCullTask.cpp +// RenderFetchCullSortTask.cpp // render/src/ // // Created by Zach Pomerantz on 12/22/2016. @@ -9,14 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "RenderFetchSortCullTask.h" +#include "RenderFetchCullSortTask.h" #include "CullTask.h" #include "SortTask.h" using namespace render; -RenderFetchSortCullTask::RenderFetchSortCullTask(CullFunctor cullFunctor) { +RenderFetchCullSortTask::RenderFetchCullSortTask(CullFunctor cullFunctor) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // CPU jobs: diff --git a/libraries/render/src/render/RenderFetchSortCullTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h similarity index 55% rename from libraries/render/src/render/RenderFetchSortCullTask.h rename to libraries/render/src/render/RenderFetchCullSortTask.h index 17d9f1a005..1f8c5e83c5 100644 --- a/libraries/render/src/render/RenderFetchSortCullTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -1,5 +1,5 @@ // -// RenderFetchSortCullTask.h +// RenderFetchCullSortTask.h // render/src/ // // Created by Zach Pomerantz on 12/22/2016. @@ -9,20 +9,20 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_RenderFetchSortCullTask_h -#define hifi_RenderFetchSortCullTask_h +#ifndef hifi_RenderFetchCullSortTask_h +#define hifi_RenderFetchCullSortTask_h #include #include "Task.h" #include "CullTask.h" -class RenderFetchSortCullTask : public render::Task { +class RenderFetchCullSortTask : public render::Task { public: using Output = std::array; - using JobModel = ModelO; + using JobModel = ModelO; - RenderFetchSortCullTask(render::CullFunctor cullFunctor); + RenderFetchCullSortTask(render::CullFunctor cullFunctor); }; -#endif // hifi_RenderFetchSortCullTask_h +#endif // hifi_RenderFetchCullSortTask_h diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 3f6d8b29b0..324e440799 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -59,7 +59,7 @@ #include #include #include -#include +#include #include #include #include @@ -536,13 +536,13 @@ public: _initContext.makeCurrent(); // Render engine init _renderEngine->addJob("RenderShadowTask", _cullFunctor); - const auto items = _renderEngine->addJob("FetchSortCull", _cullFunctor); - assert(items.canCast()); + const auto items = _renderEngine->addJob("FetchCullSort", _cullFunctor); + assert(items.canCast()); static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { - _renderEngine->addJob("RenderForwardTask", items.get()); + _renderEngine->addJob("RenderForwardTask", items.get()); } else { - _renderEngine->addJob("RenderDeferredTask", items.get()); + _renderEngine->addJob("RenderDeferredTask", items.get()); } _renderEngine->load(); _renderEngine->registerScene(_main3DScene); From 7c7d239e00dfd8a9dc9c4e791cd0f49a1b9d7f4c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Dec 2016 09:13:21 -0800 Subject: [PATCH 27/45] first cut at using wait signals to improve RPC timing --- libraries/script-engine/src/ScriptEngine.cpp | 38 ++++++++++++++++++- libraries/script-engine/src/ScriptEngine.h | 3 ++ .../system/controllers/handControllerGrab.js | 1 + unpublishedScripts/marketplace/bow/bow.js | 1 + 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 03b01e1b9c..02f2efd063 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -573,6 +573,7 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { QMetaObject::invokeMethod(this, "registerValue", Q_ARG(const QString&, valueName), Q_ARG(QScriptValue, value)); + _hasInvokeMethod.wakeAll(); return; } @@ -603,6 +604,7 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { QMetaObject::invokeMethod(this, "registerGlobalObject", Q_ARG(const QString&, name), Q_ARG(QObject*, object)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -628,6 +630,7 @@ void ScriptEngine::registerFunction(const QString& name, QScriptEngine::Function Q_ARG(const QString&, name), Q_ARG(QScriptEngine::FunctionSignature, functionSignature), Q_ARG(int, numArguments)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -647,6 +650,7 @@ void ScriptEngine::registerFunction(const QString& parent, const QString& name, Q_ARG(const QString&, name), Q_ARG(QScriptEngine::FunctionSignature, functionSignature), Q_ARG(int, numArguments)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -672,6 +676,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func Q_ARG(QScriptEngine::FunctionSignature, getter), Q_ARG(QScriptEngine::FunctionSignature, setter), Q_ARG(const QString&, parent)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -704,6 +709,7 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, eventName), Q_ARG(QScriptValue, handler)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -735,6 +741,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, eventName), Q_ARG(QScriptValue, handler)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -821,6 +828,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi Q_ARG(const QString&, sourceCode), Q_ARG(const QString&, fileName), Q_ARG(int, lineNumber)); + _hasInvokeMethod.wakeAll(); return result; } @@ -920,7 +928,25 @@ void ScriptEngine::run() { auto remainingSleepUntil = std::chrono::duration_cast(sleepUntil - clock::now()); auto closestUntil = std::min(remainingSleepUntil, untilTimer); auto thisSleepUntil = std::min(sleepUntil, clock::now() + closestUntil); - std::this_thread::sleep_until(thisSleepUntil); + + // if the sleep until is more than 1 ms away, then don't sleep, and instead use a wait condition + // for any possible invoke methods that might also be pending. + const std::chrono::microseconds MAX_USECS_TO_SLEEP(USECS_PER_MSEC); + if (closestUntil < MAX_USECS_TO_SLEEP) { + qDebug() << "about to sleep:" << closestUntil.count() << "usec"; + std::this_thread::sleep_until(thisSleepUntil); + } else { + // FIXME - we might want to make this 1 less ms to wait + unsigned long maxWait = closestUntil.count() / USECS_PER_MSEC; + qDebug() << "about to wait:" << maxWait << "ms --- closestUntil:" << closestUntil.count() << "usec"; + _waitingOnInvokeMethod.lock(); + bool signalled = _hasInvokeMethod.wait(&_waitingOnInvokeMethod, maxWait); + _waitingOnInvokeMethod.unlock(); + if (signalled) { + qDebug() << "SIGNALLED!! done waiting... now:" << usecTimestampNow(); + } + + } } #ifdef SCRIPT_DELAY_DEBUG @@ -1058,6 +1084,7 @@ void ScriptEngine::stop(bool marshal) { if (marshal) { QMetaObject::invokeMethod(this, "stop"); + _hasInvokeMethod.wakeAll(); return; } if (!_isFinished) { @@ -1078,6 +1105,7 @@ void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantM Q_ARG(QStringList, names), Q_ARG(bool, useNames), Q_ARG(AnimVariantResultHandler, resultHandler)); + _hasInvokeMethod.wakeAll(); return; } QScriptValue javascriptParameters = parameters.animVariantMapToScriptValue(this, names, useNames); @@ -1428,6 +1456,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co Q_ARG(const QString&, contents), Q_ARG(bool, isURL), Q_ARG(bool, success)); + _hasInvokeMethod.wakeAll(); return; } @@ -1526,6 +1555,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { QMetaObject::invokeMethod(this, "unloadEntityScript", Q_ARG(const EntityItemID&, entityID)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -1547,6 +1577,7 @@ void ScriptEngine::unloadAllEntityScripts() { #endif QMetaObject::invokeMethod(this, "unloadAllEntityScripts"); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -1639,10 +1670,13 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS "entityID:" << entityID << "methodName:" << methodName; #endif + qDebug() << "about to invokeMethod callEntityScriptMethod() now:" << usecTimestampNow(); + QMetaObject::invokeMethod(this, "callEntityScriptMethod", Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, methodName), Q_ARG(const QStringList&, params)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -1675,6 +1709,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, methodName), Q_ARG(const PointerEvent&, event)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -1708,6 +1743,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS Q_ARG(const QString&, methodName), Q_ARG(const EntityItemID&, otherID), Q_ARG(const Collision&, collision)); + _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 803aa7fa22..8e6184f62f 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -217,6 +217,9 @@ protected: void init(); + QWaitCondition _hasInvokeMethod; + QMutex _waitingOnInvokeMethod; + bool evaluatePending() const { return _evaluatesPending > 0; } quint64 getTimersRemainingTime(); void timerFired(); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 1f536b9567..23a98ccc1a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2141,6 +2141,7 @@ function MyController(hand) { }; this.nearGrabbing = function(deltaTime, timestamp) { + print("nearGrabbing() deltaTime:" + deltaTime); this.grabPointSphereOff(); diff --git a/unpublishedScripts/marketplace/bow/bow.js b/unpublishedScripts/marketplace/bow/bow.js index 818960e335..a735400a3c 100644 --- a/unpublishedScripts/marketplace/bow/bow.js +++ b/unpublishedScripts/marketplace/bow/bow.js @@ -154,6 +154,7 @@ }, continueEquip: function(entityID, args) { this.deltaTime = checkInterval(); + print("continueEquip delta:" + this.deltaTime); //debounce during debugging -- maybe we're updating too fast? if (USE_DEBOUNCE === true) { this.sinceLastUpdate = this.sinceLastUpdate + this.deltaTime; From f26b7c01342d9efe5bda7e30523cfdfc6bb07143 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 30 Dec 2016 09:51:43 -0800 Subject: [PATCH 28/45] Checkpoint --- interface/resources/qml/hifi/Pal.qml | 42 ++++++++++++++++++---------- scripts/system/pal.js | 9 ++---- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 8514cb36b2..95c196fc3a 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -41,6 +41,8 @@ Item { property int rowHeight: 70 property int actionButtonWidth: 75 property int nameCardWidth: width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 + property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set + property bool iAmAdmin: false // This contains the current user's NameCard and will contain other information in the future Rectangle { @@ -350,9 +352,7 @@ Item { } } - property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set - property bool iAmAdmin: false - function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml + function findSessionIndexInUserModel(sessionId) { // no findIndex in .qml for (var i = 0; i < userModel.count; i++) { if (userModel.get(i).sessionId === sessionId) { return i; @@ -364,10 +364,20 @@ Item { switch (message.method) { case 'users': var data = message.params; - var myIndex = findSessionIndex('', data); - iAmAdmin = Users.canKick; - myData = data[myIndex]; - data.splice(myIndex, 1); + var myIndex = -1; + for (var i = 0; i < data.length; i++) { + if (data[i].sessionId === "") { + myIndex = i; + break; + } + } + if (myIndex !== -1) { + iAmAdmin = Users.canKick; + myData = data[myIndex]; + data.splice(myIndex, 1); + } else { + console.log("This user's data was not found in the user list. PAL will not function properly."); + } userModel.clear(); var userIndex = 0; data.forEach(function (datum) { @@ -384,7 +394,7 @@ Item { case 'select': var sessionId = message.params[0]; var selected = message.params[1]; - var userIndex = findSessionIndex(sessionId); + var userIndex = findSessionIndexInUserModel(sessionId); if (selected) { table.selection.clear(); // for now, no multi-select table.selection.select(userIndex); @@ -404,7 +414,7 @@ Item { myCard.userName = userName; // Defensive programming } else { // Get the index in userModel associated with the passed UUID - var userIndex = findSessionIndex(userId); + var userIndex = findSessionIndexInUserModel(userId); // Set the userName appropriately userModel.setProperty(userIndex, "userName", userName); } @@ -417,25 +427,27 @@ Item { myData.audioLevel = audioLevel; myCard.audioLevel = audioLevel; // Defensive programming } else { - var userIndex = findSessionIndex(userId); + var userIndex = findSessionIndexInUserModel(userId); userModel.setProperty(userIndex, "audioLevel", audioLevel); } } break; - default: + default: console.log('Unrecognized message:', JSON.stringify(message)); } } function sortModel() { - var sortable = []; + var sortedList = []; + console.log('sortedList before:', JSON.stringify(sortedList)); for (var i = 0; i < userModel.count; i++) { - sortable.push(userModel.get(i)); + sortedList.push(userModel.get(i)); } + console.log('sortedList:', JSON.stringify(sortedList)); var sortProperty = table.getColumn(table.sortIndicatorColumn).role; var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1; var after = -1 * before; - sortable.sort(function (a, b) { + sortedList.sort(function (a, b) { var aValue = a[sortProperty].toString().toLowerCase(), bValue = b[sortProperty].toString().toLowerCase(); switch (true) { case (aValue < bValue): return before; @@ -446,7 +458,7 @@ Item { table.selection.clear(); userModel.clear(); var userIndex = 0; - sortable.forEach(function (datum) { + sortedList.forEach(function (datum) { function init(property) { if (datum[property] === undefined) { datum[property] = false; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index d2312e9ed4..2bc016dd06 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -132,17 +132,14 @@ function populateUserList() { // Request the username from the given UUID Users.requestUsernameFromID(id); } - // Request personal mute status from AudioMixer - // and ignore status from AudioMixer/AvatarMixer - // (as long as we're not requesting it for our own ID) + // Request personal mute status and ignore status + // from NodeList (as long as we're not requesting it for our own ID) if (id) { avatarPalDatum['personalMute'] = Users.getPersonalMuteStatus(id); avatarPalDatum['ignore'] = Users.getIgnoreStatus(id); + addAvatarNode(id); // No overlay for ourselves } data.push(avatarPalDatum); - if (id) { // No overlay for ourself. - addAvatarNode(id); - } print('PAL data:', JSON.stringify(avatarPalDatum)); }); pal.sendToQml({method: 'users', params: data}); From 82ab8635d715165fef53342834b1a77b44ab541c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Dec 2016 12:02:48 -0800 Subject: [PATCH 29/45] debugging --- .../entities/src/EntityScriptingInterface.cpp | 2 ++ libraries/script-engine/src/ScriptEngine.cpp | 17 +++++++++++------ .../system/controllers/handControllerGrab.js | 13 +++++++++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 498e0959c6..55f640022a 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -526,6 +526,8 @@ void EntityScriptingInterface::setEntitiesScriptEngine(EntitiesScriptEngineProvi } void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) { + qDebug() << __FUNCTION__ << "method:" << method << "now:" << usecTimestampNow(); + std::lock_guard lock(_entitiesScriptEngineLock); if (_entitiesScriptEngine) { EntityItemID entityID{ id }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 02f2efd063..61b6b3c13b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -928,22 +928,23 @@ void ScriptEngine::run() { auto remainingSleepUntil = std::chrono::duration_cast(sleepUntil - clock::now()); auto closestUntil = std::min(remainingSleepUntil, untilTimer); auto thisSleepUntil = std::min(sleepUntil, clock::now() + closestUntil); + auto untilThisSleepUntil = std::chrono::duration_cast(thisSleepUntil - clock::now()); // if the sleep until is more than 1 ms away, then don't sleep, and instead use a wait condition // for any possible invoke methods that might also be pending. const std::chrono::microseconds MAX_USECS_TO_SLEEP(USECS_PER_MSEC); - if (closestUntil < MAX_USECS_TO_SLEEP) { - qDebug() << "about to sleep:" << closestUntil.count() << "usec"; + if (untilThisSleepUntil < MAX_USECS_TO_SLEEP) { + qDebug() << "[" << getFilename() << "]" << "about to sleep untilThisSleepUntil:" << untilThisSleepUntil.count() << "usec -- averageTimerPerFrame:" << averageTimerPerFrame.count(); std::this_thread::sleep_until(thisSleepUntil); } else { // FIXME - we might want to make this 1 less ms to wait - unsigned long maxWait = closestUntil.count() / USECS_PER_MSEC; - qDebug() << "about to wait:" << maxWait << "ms --- closestUntil:" << closestUntil.count() << "usec"; + unsigned long maxWait = untilThisSleepUntil.count() / USECS_PER_MSEC; + qDebug() << "[" << getFilename() << "]" << "about to wait:" << maxWait << "ms --- untilThisSleepUntil:" << untilThisSleepUntil.count() << "usec -- averageTimerPerFrame:" << averageTimerPerFrame.count(); _waitingOnInvokeMethod.lock(); bool signalled = _hasInvokeMethod.wait(&_waitingOnInvokeMethod, maxWait); _waitingOnInvokeMethod.unlock(); if (signalled) { - qDebug() << "SIGNALLED!! done waiting... now:" << usecTimestampNow(); + qDebug() << "[" << getFilename() << "]" << "SIGNALLED!! done waiting... now:" << usecTimestampNow(); } } @@ -1664,13 +1665,15 @@ void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, const QUrl& } void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) { + qDebug() << "[" << getFilename() << "]" << __FUNCTION__ << "method:" << methodName << "now:" << usecTimestampNow(); + if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName; #endif - qDebug() << "about to invokeMethod callEntityScriptMethod() now:" << usecTimestampNow(); + qDebug() << "[" << getFilename() << "]" << "about to invokeMethod callEntityScriptMethod() now:" << usecTimestampNow(); QMetaObject::invokeMethod(this, "callEntityScriptMethod", Q_ARG(const EntityItemID&, entityID), @@ -1692,6 +1695,8 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS QScriptValueList args; args << entityID.toScriptValue(this); args << qScriptValueFromSequence(this, params); + + qDebug() << "[" << getFilename() << "]" << __FUNCTION__ << "about to callWithEnvironment()... method:" << methodName << "now:" << usecTimestampNow(); callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 23a98ccc1a..84e3acf430 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -816,6 +816,7 @@ function MyController(hand) { }; this.update = function(deltaTime, timestamp) { + print("this.update() deltaTime:" + deltaTime + " timestamp:" + timestamp + " now:" + Date.now()); this.updateSmoothedTrigger(); @@ -844,6 +845,7 @@ function MyController(hand) { this.callEntityMethodOnGrabbed = function(entityMethodName) { var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + print("this.callEntityMethodOnGrabbed() entityMethodName:" + entityMethodName + " now:" + Date.now()); Entities.callEntityMethod(this.grabbedEntity, entityMethodName, args); }; @@ -2141,7 +2143,7 @@ function MyController(hand) { }; this.nearGrabbing = function(deltaTime, timestamp) { - print("nearGrabbing() deltaTime:" + deltaTime); + print("nearGrabbing() deltaTime:" + deltaTime + " now:" + Date.now()); this.grabPointSphereOff(); @@ -3068,8 +3070,15 @@ var handleHandMessages = function(channel, message, sender) { Messages.messageReceived.connect(handleHandMessages); var BASIC_TIMER_INTERVAL_MS = 20; // 20ms = 50hz good enough +var lastInterval = Date.now(); var updateIntervalTimer = Script.setInterval(function(){ - update(BASIC_TIMER_INTERVAL_MS / 1000); + var thisInterval = Date.now(); + var deltaTimeMsec = thisInterval - lastInterval; + var deltaTime = deltaTimeMsec / 1000; + lastInterval = thisInterval; + + print("setInterval function() deltaTimeMsec:" + deltaTimeMsec + " deltaTime:" + deltaTime + "(sec) now:" + Date.now()); + update(deltaTime); }, BASIC_TIMER_INTERVAL_MS); function cleanup() { From 15656a059240bcb0205d0e2c8280c64fd809406c Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 30 Dec 2016 15:08:16 -0500 Subject: [PATCH 30/45] include spatialSelection in RenderFetchCullSortTask::Output --- libraries/render-utils/src/RenderDeferredTask.cpp | 7 ++++--- libraries/render/src/render/RenderFetchCullSortTask.cpp | 3 ++- libraries/render/src/render/RenderFetchCullSortTask.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 1c1b760f04..f8cd112007 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -60,6 +60,7 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { const auto overlayOpaques = items[3]; const auto overlayTransparents = items[4]; const auto background = items[5]; + const auto spatialSelection = items[6]; // Prepare deferred, generate the shared Deferred Frame Transform const auto deferredFrameTransform = addJob("DeferredFrameTransform"); @@ -169,10 +170,10 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { addJob("DebugAmbientOcclusion", debugAmbientOcclusionInputs); - // Scene Octree Debuging job + // Scene Octree Debugging job { - // addJob("DrawSceneOctree", spatialSelection); - // addJob("DrawItemSelection", spatialSelection); + addJob("DrawSceneOctree", spatialSelection); + addJob("DrawItemSelection", spatialSelection); } // Status icon rendering job diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index 40d8e84568..3195d8c5f8 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -56,5 +56,6 @@ RenderFetchCullSortTask::RenderFetchCullSortTask(CullFunctor cullFunctor) { const auto overlayTransparents = addJob("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; - setOutput(Output{{ opaques, transparents, lights, overlayOpaques, overlayTransparents, background }}); + setOutput(Output{{ + opaques, transparents, lights, overlayOpaques, overlayTransparents, background, spatialSelection }}); } diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index 1f8c5e83c5..bea45247b0 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -19,7 +19,7 @@ class RenderFetchCullSortTask : public render::Task { public: - using Output = std::array; + using Output = std::array; using JobModel = ModelO; RenderFetchCullSortTask(render::CullFunctor cullFunctor); From ea40582afb067d8d24c9a7cfe807b1f54eda2fa9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 30 Dec 2016 12:55:46 -0800 Subject: [PATCH 31/45] Remaining bug: Hover when sort --- interface/resources/qml/hifi/Pal.qml | 46 +++++++++++++--------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 95c196fc3a..05f0334615 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -118,19 +118,6 @@ Item { verticalAlignment: Text.AlignTop } } - // Separator between user and admin functions - Rectangle { - // Size - width: 2 - height: table.height - // Anchors - anchors.left: adminTab.left - anchors.top: table.top - // Properties - z: 100 - visible: iAmAdmin - color: hifi.colors.lightGrayText - } // This TableView refers to the table (below the current user's NameCard) HifiControls.Table { id: table @@ -249,6 +236,18 @@ Item { } } } + // Separator between user and admin functions + Rectangle { + // Size + width: 2 + height: table.height + // Anchors + anchors.left: adminTab.left + anchors.top: table.top + // Properties + visible: iAmAdmin + color: hifi.colors.lightGrayText + } // Refresh button Rectangle { // Size @@ -390,6 +389,7 @@ Item { datum.userIndex = userIndex++; userModel.append(datum); }); + sortModel(); break; case 'select': var sessionId = message.params[0]; @@ -438,11 +438,9 @@ Item { } function sortModel() { var sortedList = []; - console.log('sortedList before:', JSON.stringify(sortedList)); for (var i = 0; i < userModel.count; i++) { sortedList.push(userModel.get(i)); } - console.log('sortedList:', JSON.stringify(sortedList)); var sortProperty = table.getColumn(table.sortIndicatorColumn).role; var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1; @@ -456,18 +454,18 @@ Item { } }); table.selection.clear(); - userModel.clear(); - var userIndex = 0; - sortedList.forEach(function (datum) { - function init(property) { - if (datum[property] === undefined) { - datum[property] = false; + var currentUserIndex = 0; + for (var i = 0; i < sortedList.length; i++) { + function init(prop) { + if (sortedList[i][prop] === undefined) { + sortedList[i][prop] = false; } } + sortedList[i].userIndex = currentUserIndex++; ['personalMute', 'ignore', 'mute', 'kick'].forEach(init); - datum.userIndex = userIndex++; - userModel.append(datum); - }); + userModel.append(sortedList[i]); + } + userModel.remove(0, sortedList.length); } signal sendToScript(var message); function noticeSelection() { From 951db2e2dae9f9c3f07515ca283450ea338a3af1 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 30 Dec 2016 15:12:17 -0800 Subject: [PATCH 32/45] Bugs fixed! --- interface/resources/qml/hifi/Pal.qml | 54 ++++++++++++++++++---------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 05f0334615..366e0ab30d 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -194,8 +194,8 @@ Item { id: nameCard // Properties displayName: styleData.value - userName: model && model.userName - audioLevel: model && model.audioLevel + userName: model ? model.userName : "" + audioLevel: model ? model.audioLevel : 0.0 visible: !isCheckBox // Size width: nameCardWidth @@ -205,11 +205,19 @@ Item { } // This CheckBox belongs in the columns that contain the action buttons ("Mute", "Ban", etc) + // KNOWN BUG with the Checkboxes: When clicking in the center of the sorting header, the checkbox + // will appear in the "hovered" state. Hovering over the checkbox will fix it. + // Clicking on the sides of the sorting header doesn't cause this problem. + // I'm guessing this is a QT bug and not anything I can fix. I spent too long trying to work around it... + // I'm just going to leave the minor visual bug in. HifiControls.CheckBox { + id: actionCheckBox visible: isCheckBox anchors.centerIn: parent - checked: model[styleData.role] - enabled: styleData.role === "personalMute" ? (model["ignore"] === true ? false : true) : true + checked: model ? model[styleData.role] : false + // If this is a "personal mute" checkbox, and the model is valid, Check the checkbox if the Ignore + // checkbox is checked. + enabled: styleData.role === "personalMute" ? ((model ? model["ignore"] : false) === true ? false : true) : true boxSize: 24 onClicked: { var newValue = !model[styleData.role] @@ -225,13 +233,13 @@ Item { } else { Users[styleData.role](model.sessionId) // Just for now, while we cannot undo things: - userModel.remove(model.userIndex, 1) + userModel.remove(model.userIndex) sortModel() } // http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html#creating-property-bindings-from-javascript // I'm using an explicit binding here because clicking a checkbox breaks the implicit binding as set by - // "checked: model[stayleData.role]" above. - checked = Qt.binding(function() { return model[styleData.role] }) + // "checked: model[styleData.role]" above. + checked = Qt.binding(function() { return (model && model[styleData.role]) ? model[styleData.role] : false}) } } } @@ -327,18 +335,22 @@ Item { radius: hifi.dimensions.borderRadius } Rectangle { - width: Math.min(parent.width * 0.75, 400) - height: popupText.contentHeight*2 + width: Math.max(parent.width * 0.75, 400) + height: popupText.contentHeight*1.5 anchors.centerIn: parent radius: hifi.dimensions.borderRadius color: "white" FiraSansSemiBold { id: popupText - text: "This is temporary text. It will eventually be used to explain what 'Names' means." + text: "Bold names in the list are Avatar Display Names.\n" + + "If a Display Name isn't set, a unique Session Display Name is assigned to them." + + "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each present avatar." size: hifi.fontSizes.textFieldInput color: hifi.colors.darkGray horizontalAlignment: Text.AlignHCenter anchors.fill: parent + anchors.leftMargin: 15 + anchors.rightMargin: 15 wrapMode: Text.WordWrap } } @@ -395,11 +407,13 @@ Item { var sessionId = message.params[0]; var selected = message.params[1]; var userIndex = findSessionIndexInUserModel(sessionId); - if (selected) { - table.selection.clear(); // for now, no multi-select - table.selection.select(userIndex); - } else { - table.selection.deselect(userIndex); + if (userIndex != -1) { + if (selected) { + table.selection.clear(); // for now, no multi-select + table.selection.select(userIndex); + } else { + table.selection.deselect(userIndex); + } } break; // Received an "updateUsername()" request from the JS @@ -415,8 +429,10 @@ Item { } else { // Get the index in userModel associated with the passed UUID var userIndex = findSessionIndexInUserModel(userId); - // Set the userName appropriately - userModel.setProperty(userIndex, "userName", userName); + if (userIndex != -1) { + // Set the userName appropriately + userModel.setProperty(userIndex, "userName", userName); + } } break; case 'updateAudioLevel': @@ -428,7 +444,9 @@ Item { myCard.audioLevel = audioLevel; // Defensive programming } else { var userIndex = findSessionIndexInUserModel(userId); - userModel.setProperty(userIndex, "audioLevel", audioLevel); + if (userIndex != -1) { + userModel.setProperty(userIndex, "audioLevel", audioLevel); + } } } break; From 4a21eaa33fbf7a305a0275958e9d98053388faa5 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 30 Dec 2016 15:18:01 -0800 Subject: [PATCH 33/45] Cleanup before PR --- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- interface/resources/qml/hifi/Pal.qml | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 43ad2cbaff..30ce210eb6 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -368,7 +368,7 @@ void AvatarMixer::broadcastAvatarData() { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); - + if (lastSeqToReceiver > lastSeqFromSender && lastSeqToReceiver != UINT16_MAX) { // we got out out of order packets from the sender, track it otherNodeData->incrementNumOutOfOrderSends(); diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 366e0ab30d..2196692e1c 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -244,18 +244,6 @@ Item { } } } - // Separator between user and admin functions - Rectangle { - // Size - width: 2 - height: table.height - // Anchors - anchors.left: adminTab.left - anchors.top: table.top - // Properties - visible: iAmAdmin - color: hifi.colors.lightGrayText - } // Refresh button Rectangle { // Size @@ -295,6 +283,18 @@ Item { onExited: reloadButton.color = (pressed ? hifi.colors.lightGrayText: hifi.colors.darkGray) } } + // Separator between user and admin functions + Rectangle { + // Size + width: 2 + height: table.height + // Anchors + anchors.left: adminTab.left + anchors.top: table.top + // Properties + visible: iAmAdmin + color: hifi.colors.lightGrayText + } // This Rectangle refers to the [?] popup button Rectangle { color: hifi.colors.tableBackgroundLight From d440532cff46d4f5d2ffda360b90ca375adf223a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 3 Jan 2017 11:10:07 -0800 Subject: [PATCH 34/45] make sure RPC is timely --- .../entities/src/EntityScriptingInterface.cpp | 2 - libraries/script-engine/src/ScriptEngine.cpp | 75 +----- libraries/script-engine/src/ScriptEngine.h | 3 - scripts/developer/tests/testInterval.js | 253 ++++++++++++++++++ .../tests/testIntervalRpcFunction.js | 77 ++++++ .../system/controllers/handControllerGrab.js | 58 +++- 6 files changed, 387 insertions(+), 81 deletions(-) create mode 100644 scripts/developer/tests/testInterval.js create mode 100644 scripts/developer/tests/testIntervalRpcFunction.js diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 55f640022a..498e0959c6 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -526,8 +526,6 @@ void EntityScriptingInterface::setEntitiesScriptEngine(EntitiesScriptEngineProvi } void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) { - qDebug() << __FUNCTION__ << "method:" << method << "now:" << usecTimestampNow(); - std::lock_guard lock(_entitiesScriptEngineLock); if (_entitiesScriptEngine) { EntityItemID entityID{ id }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 61b6b3c13b..6bcf1b1c01 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -573,7 +573,6 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { QMetaObject::invokeMethod(this, "registerValue", Q_ARG(const QString&, valueName), Q_ARG(QScriptValue, value)); - _hasInvokeMethod.wakeAll(); return; } @@ -604,7 +603,6 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { QMetaObject::invokeMethod(this, "registerGlobalObject", Q_ARG(const QString&, name), Q_ARG(QObject*, object)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -630,7 +628,6 @@ void ScriptEngine::registerFunction(const QString& name, QScriptEngine::Function Q_ARG(const QString&, name), Q_ARG(QScriptEngine::FunctionSignature, functionSignature), Q_ARG(int, numArguments)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -650,7 +647,6 @@ void ScriptEngine::registerFunction(const QString& parent, const QString& name, Q_ARG(const QString&, name), Q_ARG(QScriptEngine::FunctionSignature, functionSignature), Q_ARG(int, numArguments)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -676,7 +672,6 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func Q_ARG(QScriptEngine::FunctionSignature, getter), Q_ARG(QScriptEngine::FunctionSignature, setter), Q_ARG(const QString&, parent)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -709,7 +704,6 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, eventName), Q_ARG(QScriptValue, handler)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -741,7 +735,6 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, eventName), Q_ARG(QScriptValue, handler)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -828,7 +821,6 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi Q_ARG(const QString&, sourceCode), Q_ARG(const QString&, fileName), Q_ARG(int, lineNumber)); - _hasInvokeMethod.wakeAll(); return result; } @@ -916,38 +908,11 @@ void ScriptEngine::run() { break; } - // determine how long before the next timer should fire, we'd ideally like to sleep just - // that long, so the next processEvents() will allow the timers to fire on time. - const std::chrono::microseconds minTimerTimeRemaining(USECS_PER_MSEC * getTimersRemainingTime()); - - // However, if we haven't yet slept at least as long as our average timer per frame, then we will - // punish the timers to at least wait as long as the average run time of the timers. - auto untilTimer = std::max(minTimerTimeRemaining, averageTimerPerFrame); - - // choose the closest time point, our - auto remainingSleepUntil = std::chrono::duration_cast(sleepUntil - clock::now()); - auto closestUntil = std::min(remainingSleepUntil, untilTimer); - auto thisSleepUntil = std::min(sleepUntil, clock::now() + closestUntil); - auto untilThisSleepUntil = std::chrono::duration_cast(thisSleepUntil - clock::now()); - - // if the sleep until is more than 1 ms away, then don't sleep, and instead use a wait condition - // for any possible invoke methods that might also be pending. - const std::chrono::microseconds MAX_USECS_TO_SLEEP(USECS_PER_MSEC); - if (untilThisSleepUntil < MAX_USECS_TO_SLEEP) { - qDebug() << "[" << getFilename() << "]" << "about to sleep untilThisSleepUntil:" << untilThisSleepUntil.count() << "usec -- averageTimerPerFrame:" << averageTimerPerFrame.count(); - std::this_thread::sleep_until(thisSleepUntil); - } else { - // FIXME - we might want to make this 1 less ms to wait - unsigned long maxWait = untilThisSleepUntil.count() / USECS_PER_MSEC; - qDebug() << "[" << getFilename() << "]" << "about to wait:" << maxWait << "ms --- untilThisSleepUntil:" << untilThisSleepUntil.count() << "usec -- averageTimerPerFrame:" << averageTimerPerFrame.count(); - _waitingOnInvokeMethod.lock(); - bool signalled = _hasInvokeMethod.wait(&_waitingOnInvokeMethod, maxWait); - _waitingOnInvokeMethod.unlock(); - if (signalled) { - qDebug() << "[" << getFilename() << "]" << "SIGNALLED!! done waiting... now:" << usecTimestampNow(); - } - - } + // We only want to sleep a small amount so that any pending events (like timers or invokeMethod events) + // will be able to process quickly. + static const int SMALL_SLEEP_AMOUNT = 100; + auto smallSleepUntil = clock::now() + static_cast(SMALL_SLEEP_AMOUNT); + std::this_thread::sleep_until(smallSleepUntil); } #ifdef SCRIPT_DELAY_DEBUG @@ -1037,21 +1002,6 @@ void ScriptEngine::run() { emit doneRunning(); } -quint64 ScriptEngine::getTimersRemainingTime() { - quint64 minimumTime = USECS_PER_SECOND; // anything larger than this can be ignored - QMutableHashIterator i(_timerFunctionMap); - while (i.hasNext()) { - i.next(); - QTimer* timer = i.key(); - int remainingTime = timer->remainingTime(); - if (remainingTime >= 0) { - minimumTime = std::min((quint64)remainingTime, minimumTime); - } - } - return minimumTime; -} - - // NOTE: This is private because it must be called on the same thread that created the timers, which is why // we want to only call it in our own run "shutdown" processing. void ScriptEngine::stopAllTimers() { @@ -1062,6 +1012,7 @@ void ScriptEngine::stopAllTimers() { stopTimer(timer); } } + void ScriptEngine::stopAllTimersForEntityScript(const EntityItemID& entityID) { // We could maintain a separate map of entityID => QTimer, but someone will have to prove to me that it's worth the complexity. -HRS QVector toDelete; @@ -1085,7 +1036,6 @@ void ScriptEngine::stop(bool marshal) { if (marshal) { QMetaObject::invokeMethod(this, "stop"); - _hasInvokeMethod.wakeAll(); return; } if (!_isFinished) { @@ -1106,7 +1056,6 @@ void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantM Q_ARG(QStringList, names), Q_ARG(bool, useNames), Q_ARG(AnimVariantResultHandler, resultHandler)); - _hasInvokeMethod.wakeAll(); return; } QScriptValue javascriptParameters = parameters.animVariantMapToScriptValue(this, names, useNames); @@ -1457,7 +1406,6 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co Q_ARG(const QString&, contents), Q_ARG(bool, isURL), Q_ARG(bool, success)); - _hasInvokeMethod.wakeAll(); return; } @@ -1556,7 +1504,6 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { QMetaObject::invokeMethod(this, "unloadEntityScript", Q_ARG(const EntityItemID&, entityID)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -1578,7 +1525,6 @@ void ScriptEngine::unloadAllEntityScripts() { #endif QMetaObject::invokeMethod(this, "unloadAllEntityScripts"); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -1665,21 +1611,16 @@ void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, const QUrl& } void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) { - qDebug() << "[" << getFilename() << "]" << __FUNCTION__ << "method:" << methodName << "now:" << usecTimestampNow(); - if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName; #endif - qDebug() << "[" << getFilename() << "]" << "about to invokeMethod callEntityScriptMethod() now:" << usecTimestampNow(); - QMetaObject::invokeMethod(this, "callEntityScriptMethod", Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, methodName), Q_ARG(const QStringList&, params)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -1695,8 +1636,6 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS QScriptValueList args; args << entityID.toScriptValue(this); args << qScriptValueFromSequence(this, params); - - qDebug() << "[" << getFilename() << "]" << __FUNCTION__ << "about to callWithEnvironment()... method:" << methodName << "now:" << usecTimestampNow(); callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); } @@ -1714,7 +1653,6 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, methodName), Q_ARG(const PointerEvent&, event)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING @@ -1748,7 +1686,6 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS Q_ARG(const QString&, methodName), Q_ARG(const EntityItemID&, otherID), Q_ARG(const Collision&, collision)); - _hasInvokeMethod.wakeAll(); return; } #ifdef THREAD_DEBUGGING diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 8e6184f62f..803aa7fa22 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -217,9 +217,6 @@ protected: void init(); - QWaitCondition _hasInvokeMethod; - QMutex _waitingOnInvokeMethod; - bool evaluatePending() const { return _evaluatesPending > 0; } quint64 getTimersRemainingTime(); void timerFired(); diff --git a/scripts/developer/tests/testInterval.js b/scripts/developer/tests/testInterval.js new file mode 100644 index 0000000000..0b6b70686b --- /dev/null +++ b/scripts/developer/tests/testInterval.js @@ -0,0 +1,253 @@ + +// Tester, try testing these different settings. +// +// +-----------------+---------------------+----------------------+------------------------------------------------------------------------------------------------------------------ +// | TIMER_INTERVAL | TIMER_WORK_EFFORT | UPDATE_WORK_EFFORT | Expected result +// +-----------------+---------------------+----------------------+------------------------------------------------------------------------------------------------------------------ +// | 20 | < 1000 | < 1000 | timer 20ms/work 0ms, update 16.6ms/work 0ms +// | 5 | < 1000 | < 1000 | timer 5ms/work 0ms, update 16.6ms/work 0ms +// | 11 | < 1000 | < 1000 | timer 11ms/work 0ms, update 16.6ms/work 0ms +// | 11 | ~100000 | < 1000 | timer 11ms/work 3ms, update 16.6ms/work 0ms +// | 11 | ~500000 | < 1000 | timer 17.3ms/work 16.4ms, update 17.4ms/work 0ms, punishing the script, timer & update delayed +// | 40 | ~1000000 | < 1000 | timer 40ms/work 32ms, update 31ms/work 0ms, punishing the script, update delayed, timer on time +// | 10 | ~1000 | ~1000000 | timer 16ms/work 0ms, update 63ms/work 33ms, punishing the script, update delayed, timer slightly delayed +// | 10 | ~1000000 | ~1000000 | timer 90ms/work 33ms, update ~100-120ms/work 33ms, punishing the script, timer & update delayed +// +-----------------+---------------------+----------------------+------------------------------------------------------------------------------------------------------------------ + +var TIMER_HZ = 50; +var TIMER_INTERVAL = 1000 / TIMER_HZ; +var TIMER_WORK_EFFORT = 0; // 1000 is light work, 1000000 ~= 30ms +var UPDATE_WORK_EFFORT = 0; // 1000 is light work, 1000000 ~= 30ms + +var basePosition = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + +var timerBox = Entities.addEntity( + { type: "Box", + position: basePosition, + dimensions: { x: 0.1, y: 0.1, z: 0.1 }, + color: { red: 255, green: 0, blue: 255 }, + dynamic: false, + collisionless: true + }); + +var lastTick = Date.now(); +var deltaTick = 0; +var tickSamples = 0; +var totalVariance = 0; +var tickCount = 0; +var totalWork = 0; +var highVarianceCount = 0; +var varianceCount = 0; + +print("Set interval = " + TIMER_INTERVAL); + +var timerTime = 0.0; +var range = 0.5; +var rotationFactor = 0.5; // smaller == faster +var omega = 2.0 * Math.PI / rotationFactor; + +var ticker = Script.setInterval(function() { + + tickCount++; + var tickNow = Date.now(); + deltaTick = tickNow - lastTick; + + var variance = Math.abs(deltaTick - TIMER_INTERVAL); + totalVariance += variance; + + if (variance > 1) { + varianceCount++; + } + if (variance > 5) { + highVarianceCount++; + } + + var preWork = Date.now(); + var y = 2; + for (var x = 0; x < TIMER_WORK_EFFORT; x++) { + y = y * y; + } + var postWork = Date.now(); + deltaWork = postWork - preWork; + totalWork += deltaWork; + + // move a box + var deltaTime = deltaTick / 1000; + timerTime += deltaTime; + rotation = Quat.angleAxis(timerTime * omega / Math.PI * 180.0, { x: 0, y: 1, z: 0 }); + Entities.editEntity(timerBox, + { + position: { x: basePosition.x + Math.sin(timerTime * omega) / 2.0 * range, + y: basePosition.y, + z: basePosition.z }, + //rotation: rotation + }); + + tickSamples = tickSamples + deltaTick; + lastTick = tickNow; + + // report about every 5 seconds + if(tickCount == (TIMER_HZ * 5)) { + print("TIMER -- For " + tickCount + " samples average interval = " + tickSamples/tickCount + " ms" + + " average variance:" + totalVariance/tickCount + " ms" + + " min variance:" + varianceCount + " [" + (varianceCount/tickCount) * 100 + " %] " + + " high variance:" + highVarianceCount + " [" + (highVarianceCount/tickCount) * 100 + " %] " + + " average work:" + totalWork/tickCount + " ms"); + tickCount = 0; + tickSamples = 0; + totalWork = 0; + totalVariance = 0; + varianceCount = 0; + highVarianceCount = 0; + } +}, TIMER_INTERVAL); + +///////////////////////////////////////////////////////////////////////// + +var rpcPosition = Vec3.sum(basePosition, { x:0, y: 0.2, z: 0}); + +var theRpcFunctionInclude = Script.resolvePath("testIntervalRpcFunction.js"); + +print("theRpcFunctionInclude:" + theRpcFunctionInclude); + +var rpcBox = Entities.addEntity( + { type: "Box", + position: rpcPosition, + dimensions: { x: 0.1, y: 0.1, z: 0.1 }, + color: { red: 255, green: 255, blue: 0 }, + dynamic: false, + collisionless: true, + script: theRpcFunctionInclude + }); + +var rpcLastTick = Date.now(); +var rpcTotalTicks = 0; +var rpcCount = 0; +var rpcTotalVariance = 0; +var rpcTickCount = 0; +var rpcVarianceCount = 0; +var rpcHighVarianceCount = 0; + +var rpcTicker = Script.setInterval(function() { + + rpcTickCount++; + var tickNow = Date.now(); + var deltaTick = tickNow - rpcLastTick; + var variance = Math.abs(deltaTick - TIMER_INTERVAL); + rpcTotalVariance += variance; + + if (variance > 1) { + rpcVarianceCount++; + } + if (variance > 5) { + rpcHighVarianceCount++; + } + + rpcTotalTicks += deltaTick; + rpcLastTick = tickNow; + + var args = [range, rotationFactor, omega, TIMER_INTERVAL, TIMER_HZ]; + + Entities.callEntityMethod(rpcBox, "doRPC", args); + + // report about every 5 seconds + if(rpcTickCount == (TIMER_HZ * 5)) { + print("RPCTIMER- For " + rpcTickCount + " samples average interval = " + rpcTotalTicks/rpcTickCount + " ms" + + " average variance:" + rpcTotalVariance/rpcTickCount + " ms" + + " min variance:" + rpcVarianceCount + " [" + (rpcVarianceCount/rpcTickCount) * 100 + " %] " + + " high variance:" + rpcHighVarianceCount + " [" + (rpcHighVarianceCount/rpcTickCount) * 100 + " %] " + ); + rpcTickCount = 0; + rpcTotalTicks = 0; + rpcTotalVariance = 0; + rpcVarianceCount = 0; + rpcHighVarianceCount = 0; + } +}, TIMER_INTERVAL); + + +var updateCount = 0; +var updateTotalElapsed = 0; +var lastUpdate = Date.now(); +var updateTotalWork = 0; +var updateTotalVariance = 0; +var updateVarianceCount = 0; +var updateHighVarianceCount = 0; + +var updatePosition = Vec3.sum(basePosition, { x:0, y: -0.2, z: 0}); + +var updateBox = Entities.addEntity( + { type: "Box", + position: updatePosition, + dimensions: { x: 0.1, y: 0.1, z: 0.1 }, + color: { red: 0, green: 255, blue: 255 }, + dynamic: false, + collisionless: true + }); + + +var UPDATE_HZ = 60; // standard script update interval +var UPDATE_INTERVAL = 1000/UPDATE_HZ; // standard script update interval +var updateTime = 0; +var updateFunction = function(deltaTime){ + updateCount++; + var updateAt = Date.now(); + deltaUpdate = updateAt - lastUpdate; + updateTotalElapsed += deltaUpdate; + lastUpdate = updateAt; + + var variance = Math.abs(deltaUpdate - UPDATE_INTERVAL); + updateTotalVariance += variance; + + if (variance > 1) { + updateVarianceCount++; + } + + if (variance > 5) { + updateHighVarianceCount++; + } + + var preWork = Date.now(); + var y = 2; + for (var x = 0; x < UPDATE_WORK_EFFORT; x++) { + y = y * y; + } + var postWork = Date.now(); + deltaWork = postWork - preWork; + updateTotalWork += deltaWork; + + // move a box + + updateTime += deltaTime; + rotation = Quat.angleAxis(updateTime * omega / Math.PI * 180.0, { x: 0, y: 1, z: 0 }); + Entities.editEntity(updateBox, + { + position: { x: updatePosition.x + Math.sin(updateTime * omega) / 2.0 * range, + y: updatePosition.y, + z: updatePosition.z }, + }); + + + if(updateCount == (UPDATE_HZ * 5)) { + print("UPDATE -- For " + updateCount + " samples average update = " + updateTotalElapsed/updateCount + " ms" + + " average variance:" + updateTotalVariance/updateCount + " ms" + + " min variance:" + updateVarianceCount + " [" + (updateVarianceCount/updateCount) * 100 + " %] " + + " high variance:" + updateHighVarianceCount + " [" + (updateHighVarianceCount/updateCount) * 100 + " %] " + + " average work:" + updateTotalWork/updateCount + " ms"); + + updateCount = 0; + updateTotalElapsed = 0; + updateTotalWork = 0; + updateTotalVariance = 0; + updateVarianceCount = 0; + updateHighVarianceCount = 0; + } +}; + +Script.update.connect(updateFunction); + +Script.scriptEnding.connect(function(){ + Entities.deleteEntity(timerBox); + Entities.deleteEntity(rpcBox); + Entities.deleteEntity(updateBox); +}); diff --git a/scripts/developer/tests/testIntervalRpcFunction.js b/scripts/developer/tests/testIntervalRpcFunction.js new file mode 100644 index 0000000000..86caef3f61 --- /dev/null +++ b/scripts/developer/tests/testIntervalRpcFunction.js @@ -0,0 +1,77 @@ +(function() { + var x = false; + var y = false; + var z = false; + + var entityLastTick = Date.now(); + var entityTotalTicks = 0; + var entityCount = 0; + var entityTotalVariance = 0; + var entityTickCount = 0; + var entityVarianceCount = 0; + var entityHighVarianceCount = 0; + var _entityID; + var time = 0; + + function Foo() { return; }; + Foo.prototype = { + preload: function(entityID) { print('Foo preload'); _entityID = entityID; }, + doRPC: function(entityID, args) { + var range = args[0]; + var rotationFactor = args[1]; + var omega = args[2]; + var TIMER_INTERVAL = args[3]; + var TIMER_HZ = args[4]; + + // first time, set our x,y,z + if (x === false) { + var position = Entities.getEntityProperties(_entityID, "position").position; + x = position.x; + y = position.y; + z = position.z; + } + entityTickCount++; + var tickNow = Date.now(); + var deltaTick = tickNow - entityLastTick; + var variance = Math.abs(deltaTick - TIMER_INTERVAL); + entityTotalVariance += variance; + + if (variance > 1) { + entityVarianceCount++; + } + if (variance > 5) { + entityHighVarianceCount++; + } + + entityTotalTicks += deltaTick; + entityLastTick = tickNow; + + // move self!! + var deltaTime = deltaTick / 1000; + time += deltaTime; + rotation = Quat.angleAxis(time * omega / Math.PI * 180.0, { x: 0, y: 1, z: 0 }); + Entities.editEntity(_entityID, + { + position: { x: x + Math.sin(time * omega) / 2.0 * range, + y: y, + z: z }, + }); + + + if(entityTickCount == (TIMER_HZ * 5)) { + print("ENTITY -- For " + entityTickCount + " samples average interval = " + entityTotalTicks/entityTickCount + " ms" + + " average variance:" + entityTotalVariance/entityTickCount + " ms" + + " min variance:" + entityVarianceCount + " [" + (entityVarianceCount/entityTickCount) * 100 + " %] " + + " high variance:" + entityHighVarianceCount + " [" + (entityHighVarianceCount/entityTickCount) * 100 + " %] " + ); + entityTickCount = 0; + entityTotalTicks = 0; + entityTotalVariance = 0; + entityVarianceCount = 0; + entityHighVarianceCount = 0; + } + } + }; + + return new Foo(); +}); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 84e3acf430..513e70d18c 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -816,8 +816,6 @@ function MyController(hand) { }; this.update = function(deltaTime, timestamp) { - print("this.update() deltaTime:" + deltaTime + " timestamp:" + timestamp + " now:" + Date.now()); - this.updateSmoothedTrigger(); // If both trigger and grip buttons squeezed and nothing is held, rescale my avatar! @@ -845,7 +843,6 @@ function MyController(hand) { this.callEntityMethodOnGrabbed = function(entityMethodName) { var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - print("this.callEntityMethodOnGrabbed() entityMethodName:" + entityMethodName + " now:" + Date.now()); Entities.callEntityMethod(this.grabbedEntity, entityMethodName, args); }; @@ -2143,8 +2140,6 @@ function MyController(hand) { }; this.nearGrabbing = function(deltaTime, timestamp) { - print("nearGrabbing() deltaTime:" + deltaTime + " now:" + Date.now()); - this.grabPointSphereOff(); if (this.state == STATE_NEAR_GRABBING && (!this.triggerClicked && this.secondaryReleased())) { @@ -3069,16 +3064,65 @@ var handleHandMessages = function(channel, message, sender) { Messages.messageReceived.connect(handleHandMessages); -var BASIC_TIMER_INTERVAL_MS = 20; // 20ms = 50hz good enough +var TARGET_UPDATE_HZ = 50; // 50hz good enough (no change in logic) +var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; var lastInterval = Date.now(); + +var intervalCount = 0; +var totalDelta = 0; +var totalVariance = 0; +var highVarianceCount = 0; +var veryhighVarianceCount = 0; +var updateTotalWork = 0; + +var UPDATE_PERFORMANCE_DEBUGGING = false; + var updateIntervalTimer = Script.setInterval(function(){ + + intervalCount++; var thisInterval = Date.now(); var deltaTimeMsec = thisInterval - lastInterval; var deltaTime = deltaTimeMsec / 1000; lastInterval = thisInterval; - print("setInterval function() deltaTimeMsec:" + deltaTimeMsec + " deltaTime:" + deltaTime + "(sec) now:" + Date.now()); + totalDelta += deltaTimeMsec; + + var variance = Math.abs(deltaTimeMsec - BASIC_TIMER_INTERVAL_MS); + totalVariance += variance; + + if (variance > 1) { + highVarianceCount++; + } + + if (variance > 5) { + veryhighVarianceCount++; + } + + // will call update for both hands + var preWork = Date.now(); update(deltaTime); + var postWork = Date.now(); + var workDelta = postWork - preWork; + updateTotalWork += workDelta; + + if (intervalCount == 100) { + + if (UPDATE_PERFORMANCE_DEBUGGING) { + print("handControllerGrab.js -- For " + intervalCount + " samples average= " + totalDelta/intervalCount + " ms" + + " average variance:" + totalVariance/intervalCount + " ms" + + " high variance count:" + highVarianceCount + " [ " + (highVarianceCount/intervalCount) * 100 + "% ] " + + " VERY high variance count:" + veryhighVarianceCount + " [ " + (veryhighVarianceCount/intervalCount) * 100 + "% ] " + + " average work:" + updateTotalWork/intervalCount + " ms"); + } + + intervalCount = 0; + totalDelta = 0; + totalVariance = 0; + highVarianceCount = 0; + veryhighVarianceCount = 0; + updateTotalWork = 0; + } + }, BASIC_TIMER_INTERVAL_MS); function cleanup() { From ecaf3b7b3d5ba6c53cc54d6df87d51b2d3fe4eaf Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 3 Jan 2017 11:11:48 -0800 Subject: [PATCH 35/45] remove debugging --- unpublishedScripts/marketplace/bow/bow.js | 1 - 1 file changed, 1 deletion(-) diff --git a/unpublishedScripts/marketplace/bow/bow.js b/unpublishedScripts/marketplace/bow/bow.js index a735400a3c..818960e335 100644 --- a/unpublishedScripts/marketplace/bow/bow.js +++ b/unpublishedScripts/marketplace/bow/bow.js @@ -154,7 +154,6 @@ }, continueEquip: function(entityID, args) { this.deltaTime = checkInterval(); - print("continueEquip delta:" + this.deltaTime); //debounce during debugging -- maybe we're updating too fast? if (USE_DEBOUNCE === true) { this.sinceLastUpdate = this.sinceLastUpdate + this.deltaTime; From 4c1aaa376d47d6cd9daeb09b57afafcef061702d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 3 Jan 2017 11:13:03 -0800 Subject: [PATCH 36/45] remove cruft --- libraries/script-engine/src/ScriptEngine.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 803aa7fa22..d9a1249b1c 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -218,7 +218,6 @@ protected: void init(); bool evaluatePending() const { return _evaluatesPending > 0; } - quint64 getTimersRemainingTime(); void timerFired(); void stopAllTimers(); void stopAllTimersForEntityScript(const EntityItemID& entityID); From 3c7fa41be9ff6043fc70d089166077798a73099c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 3 Jan 2017 11:29:52 -0800 Subject: [PATCH 37/45] update test script --- scripts/developer/tests/testInterval.js | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/scripts/developer/tests/testInterval.js b/scripts/developer/tests/testInterval.js index 0b6b70686b..94a5fe1fa5 100644 --- a/scripts/developer/tests/testInterval.js +++ b/scripts/developer/tests/testInterval.js @@ -1,22 +1,15 @@ - // Tester, try testing these different settings. // -// +-----------------+---------------------+----------------------+------------------------------------------------------------------------------------------------------------------ -// | TIMER_INTERVAL | TIMER_WORK_EFFORT | UPDATE_WORK_EFFORT | Expected result -// +-----------------+---------------------+----------------------+------------------------------------------------------------------------------------------------------------------ -// | 20 | < 1000 | < 1000 | timer 20ms/work 0ms, update 16.6ms/work 0ms -// | 5 | < 1000 | < 1000 | timer 5ms/work 0ms, update 16.6ms/work 0ms -// | 11 | < 1000 | < 1000 | timer 11ms/work 0ms, update 16.6ms/work 0ms -// | 11 | ~100000 | < 1000 | timer 11ms/work 3ms, update 16.6ms/work 0ms -// | 11 | ~500000 | < 1000 | timer 17.3ms/work 16.4ms, update 17.4ms/work 0ms, punishing the script, timer & update delayed -// | 40 | ~1000000 | < 1000 | timer 40ms/work 32ms, update 31ms/work 0ms, punishing the script, update delayed, timer on time -// | 10 | ~1000 | ~1000000 | timer 16ms/work 0ms, update 63ms/work 33ms, punishing the script, update delayed, timer slightly delayed -// | 10 | ~1000000 | ~1000000 | timer 90ms/work 33ms, update ~100-120ms/work 33ms, punishing the script, timer & update delayed -// +-----------------+---------------------+----------------------+------------------------------------------------------------------------------------------------------------------ +// Changing the TIMER_HZ will show different performance results. It's expected that at 90hz you'll see a fair +// amount of variance, as Qt Timers simply aren't accurate enough. In general RPC peformance should match the timer +// without significant difference in variance. -var TIMER_HZ = 50; +var TIMER_HZ = 50; // Change this for different values var TIMER_INTERVAL = 1000 / TIMER_HZ; var TIMER_WORK_EFFORT = 0; // 1000 is light work, 1000000 ~= 30ms + +var UPDATE_HZ = 60; // standard script update rate +var UPDATE_INTERVAL = 1000/UPDATE_HZ; // standard script update interval var UPDATE_WORK_EFFORT = 0; // 1000 is light work, 1000000 ~= 30ms var basePosition = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); @@ -186,8 +179,6 @@ var updateBox = Entities.addEntity( }); -var UPDATE_HZ = 60; // standard script update interval -var UPDATE_INTERVAL = 1000/UPDATE_HZ; // standard script update interval var updateTime = 0; var updateFunction = function(deltaTime){ updateCount++; From 899dd21328cb790d4c150278e1aadd711474985d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 3 Jan 2017 15:43:30 -0800 Subject: [PATCH 38/45] Lots of CR feedback --- assignment-client/src/avatars/AvatarMixer.cpp | 4 + .../resources/qml/controls-uit/CheckBox.qml | 4 +- interface/resources/qml/hifi/Pal.qml | 125 +++++++++++------- libraries/networking/src/Node.cpp | 4 +- libraries/networking/src/NodeList.cpp | 22 ++- 5 files changed, 96 insertions(+), 63 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 30ce210eb6..3cacfdedf1 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -110,6 +110,10 @@ void AvatarMixer::broadcastAvatarData() { const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; // only send extra avatar data (avatars out of view, ignored) every Nth AvatarData frame + // Extra avatar data will be sent (AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND/EXTRA_AVATAR_DATA_FRAME_RATIO) times + // per second. + // This value should be a power of two for performance purposes, as the mixer performs a modulo operation every frame + // to determine whether the extra data should be sent. const int EXTRA_AVATAR_DATA_FRAME_RATIO = 16; // NOTE: The following code calculates the _performanceThrottlingRatio based on how much the avatar-mixer was diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 503e6b8739..9db4f621f9 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -27,9 +27,7 @@ Original.CheckBox { readonly property int checkRadius: 2 style: CheckBoxStyle { - indicator: - - Rectangle { + indicator: Rectangle { id: box width: boxSize height: boxSize diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 2196692e1c..3c3cde7a67 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -42,6 +42,9 @@ Item { property int actionButtonWidth: 75 property int nameCardWidth: width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set + // FIXME: Need to determine & handle when this list gets reset. + property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring. + property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities. property bool iAmAdmin: false // This contains the current user's NameCard and will contain other information in the future @@ -187,7 +190,8 @@ Item { // This Item refers to the contents of each Cell itemDelegate: Item { id: itemCell - property bool isCheckBox: typeof(styleData.value) === 'boolean' + property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore" + property bool isButton: styleData.role === "mute" || styleData.role === "kick" // This NameCard refers to the cell that contains an avatar's // DisplayName and UserName NameCard { @@ -196,7 +200,7 @@ Item { displayName: styleData.value userName: model ? model.userName : "" audioLevel: model ? model.audioLevel : 0.0 - visible: !isCheckBox + visible: !isCheckBox && !isButton // Size width: nameCardWidth height: parent.height @@ -204,7 +208,7 @@ Item { anchors.left: parent.left } - // This CheckBox belongs in the columns that contain the action buttons ("Mute", "Ban", etc) + // This CheckBox belongs in the columns that contain the stateful action buttons ("Mute" & "Ignore" for now) // KNOWN BUG with the Checkboxes: When clicking in the center of the sorting header, the checkbox // will appear in the "hovered" state. Hovering over the checkbox will fix it. // Clicking on the sides of the sorting header doesn't cause this problem. @@ -225,16 +229,20 @@ Item { newValue = false } userModel.setProperty(model.userIndex, styleData.role, newValue) + userModelData[model.userIndex][styleData.role] = newValue // Defensive programming if (styleData.role === "personalMute" || styleData.role === "ignore") { Users[styleData.role](model.sessionId, newValue) if (styleData.role === "ignore") { userModel.setProperty(model.userIndex, "personalMute", newValue) + userModelData[model.userIndex]["personalMute"] = newValue // Defensive programming + if (newValue) { + ignored[model.sessionId] = userModelData[model.userIndex] + } else { + delete ignored[model.sessionId] + } } } else { - Users[styleData.role](model.sessionId) - // Just for now, while we cannot undo things: - userModel.remove(model.userIndex) - sortModel() + console.log("User clicked on an unknown checkbox."); } // http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html#creating-property-bindings-from-javascript // I'm using an explicit binding here because clicking a checkbox breaks the implicit binding as set by @@ -242,6 +250,29 @@ Item { checked = Qt.binding(function() { return (model && model[styleData.role]) ? model[styleData.role] : false}) } } + + // This Button belongs in the columns that contain the stateless action buttons ("Silence" & "Ban" for now) + HifiControls.Button { + id: actionButton + color: 2 // Red + visible: isButton + anchors.centerIn: parent + width: 24 + height: 24 + onClicked: { + if (styleData.role === "mute" || styleData.role === "kick") { + Users[styleData.role](model.sessionId) + if (styleData.role === "kick") { + // Just for now, while we cannot undo "Ban": + userModel.remove(model.userIndex) + delete userModelData[model.userIndex] // Defensive programming + sortModel() + } + } else { + console.log("User clicked on an unknown checkbox."); + } + } + } } } // Refresh button @@ -363,9 +394,10 @@ Item { } } - function findSessionIndexInUserModel(sessionId) { // no findIndex in .qml - for (var i = 0; i < userModel.count; i++) { - if (userModel.get(i).sessionId === sessionId) { + function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml + var data = optionalData || userModelData, length = data.length; + for (var i = 0; i < length; i++) { + if (data[i].sessionId === sessionId) { return i; } } @@ -375,38 +407,30 @@ Item { switch (message.method) { case 'users': var data = message.params; - var myIndex = -1; - for (var i = 0; i < data.length; i++) { - if (data[i].sessionId === "") { - myIndex = i; - break; - } - } - if (myIndex !== -1) { + var index = -1; + index = findSessionIndex('', data); + if (index !== -1) { iAmAdmin = Users.canKick; - myData = data[myIndex]; - data.splice(myIndex, 1); + myData = data[index]; + data.splice(index, 1); } else { console.log("This user's data was not found in the user list. PAL will not function properly."); } - userModel.clear(); - var userIndex = 0; - data.forEach(function (datum) { - function init(property) { - if (datum[property] === undefined) { - datum[property] = false; - } + userModelData = data; + for (var ignoredID in ignored) { + index = findSessionIndex(ignoredID); + if (index === -1) { // Add back any missing ignored to the PAL, because they sometimes take a moment to show up. + userModelData.push(ignored[ignoredID]); + } else { // Already appears in PAL; update properties of existing element in model data + userModelData[index] = ignored[ignoredID]; } - ['personalMute', 'ignore', 'mute', 'kick'].forEach(init); - datum.userIndex = userIndex++; - userModel.append(datum); - }); + } sortModel(); break; case 'select': var sessionId = message.params[0]; var selected = message.params[1]; - var userIndex = findSessionIndexInUserModel(sessionId); + var userIndex = findSessionIndex(sessionId); if (userIndex != -1) { if (selected) { table.selection.clear(); // for now, no multi-select @@ -427,11 +451,12 @@ Item { myData.userName = userName; myCard.userName = userName; // Defensive programming } else { - // Get the index in userModel associated with the passed UUID - var userIndex = findSessionIndexInUserModel(userId); + // Get the index in userModel and userModelData associated with the passed UUID + var userIndex = findSessionIndex(userId); if (userIndex != -1) { // Set the userName appropriately userModel.setProperty(userIndex, "userName", userName); + userModelData[userIndex].userName = userName; // Defensive programming } } break; @@ -443,9 +468,10 @@ Item { myData.audioLevel = audioLevel; myCard.audioLevel = audioLevel; // Defensive programming } else { - var userIndex = findSessionIndexInUserModel(userId); + var userIndex = findSessionIndex(userId); if (userIndex != -1) { userModel.setProperty(userIndex, "audioLevel", audioLevel); + userModelData[userIndex].audioLevel = audioLevel; // Defensive programming } } } @@ -455,15 +481,10 @@ Item { } } function sortModel() { - var sortedList = []; - for (var i = 0; i < userModel.count; i++) { - sortedList.push(userModel.get(i)); - } - var sortProperty = table.getColumn(table.sortIndicatorColumn).role; var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1; var after = -1 * before; - sortedList.sort(function (a, b) { + userModelData.sort(function (a, b) { var aValue = a[sortProperty].toString().toLowerCase(), bValue = b[sortProperty].toString().toLowerCase(); switch (true) { case (aValue < bValue): return before; @@ -472,24 +493,26 @@ Item { } }); table.selection.clear(); - var currentUserIndex = 0; - for (var i = 0; i < sortedList.length; i++) { - function init(prop) { - if (sortedList[i][prop] === undefined) { - sortedList[i][prop] = false; + + userModel.clear(); + var userIndex = 0; + userModelData.forEach(function (datum) { + function init(property) { + if (datum[property] === undefined) { + datum[property] = false; } } - sortedList[i].userIndex = currentUserIndex++; ['personalMute', 'ignore', 'mute', 'kick'].forEach(init); - userModel.append(sortedList[i]); - } - userModel.remove(0, sortedList.length); + datum.userIndex = userIndex++; + userModel.append(datum); + console.log('appending to userModel:', JSON.stringify(datum)); + }); } signal sendToScript(var message); function noticeSelection() { var userIds = []; table.selection.forEach(function (userIndex) { - userIds.push(userModel.get(userIndex).sessionId); + userIds.push(userModelData[userIndex].sessionId); }); pal.sendToScript({method: 'selected', params: userIds}); } diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 484ea882b5..8063075e22 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -82,11 +82,11 @@ void Node::updateClockSkewUsec(qint64 clockSkewSample) { } void Node::parseIgnoreRequestMessage(QSharedPointer message) { + bool addToIgnore; + message->readPrimitive(&addToIgnore); while (message->getBytesLeftToRead()) { // parse out the UUID being ignored from the packet QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - bool addToIgnore; - message->readPrimitive(&addToIgnore); if (addToIgnore) { addIgnoredNode(ignoredUUID); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 8b4cb41cc9..64f0479a51 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -790,9 +790,9 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { // create a reliable NLPacket with space for the ignore UUID auto ignorePacket = NLPacket::create(PacketType::NodeIgnoreRequest, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); + ignorePacket->writePrimitive(ignoreEnabled); // write the node ID to the packet ignorePacket->write(nodeID.toRfc4122()); - ignorePacket->writePrimitive(ignoreEnabled); qCDebug(networking) << "Sending packet to" << (destinationNode->getType() == NodeType::AudioMixer ? "AudioMixer" : "AvatarMixer") << "to" << (ignoreEnabled ? "ignore" : "unignore") << "node" << uuidStringWithoutCurlyBraces(nodeID); @@ -801,16 +801,17 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { sendPacket(std::move(ignorePacket), *destinationNode); }); - QReadLocker ignoredSetLocker { &_ignoredSetLock }; // write lock for insert and unsafe_erase - QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; // write lock for insert and unsafe_erase - if (ignoreEnabled) { + QReadLocker ignoredSetLocker{ &_ignoredSetLock }; // read lock for insert + QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; // read lock for insert // add this nodeID to our set of ignored IDs _ignoredNodeIDs.insert(nodeID); // add this nodeID to our set of personal muted IDs _personalMutedNodeIDs.insert(nodeID); emit ignoredNode(nodeID); } else { + QWriteLocker ignoredSetLocker{ &_ignoredSetLock }; // write lock for unsafe_erase + QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock }; // write lock for unsafe_erase _ignoredNodeIDs.unsafe_erase(nodeID); _personalMutedNodeIDs.unsafe_erase(nodeID); emit unignoredNode(nodeID); @@ -838,20 +839,21 @@ void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled // setup the packet auto personalMutePacket = NLPacket::create(PacketType::NodeIgnoreRequest, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); + personalMutePacket->writePrimitive(muteEnabled); // write the node ID to the packet personalMutePacket->write(nodeID.toRfc4122()); - personalMutePacket->writePrimitive(muteEnabled); qCDebug(networking) << "Sending Personal Mute Packet to" << (muteEnabled ? "mute" : "unmute") << "node" << uuidStringWithoutCurlyBraces(nodeID); sendPacket(std::move(personalMutePacket), *audioMixer); - QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; // write lock for insert and unsafe_erase if (muteEnabled) { + QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; // read lock for insert // add this nodeID to our set of personal muted IDs _personalMutedNodeIDs.insert(nodeID); } else { + QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock }; // write lock for unsafe_erase _personalMutedNodeIDs.unsafe_erase(nodeID); } } @@ -879,6 +881,9 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { // setup a packet list so we can send the stream of ignore IDs auto personalMutePacketList = NLPacketList::create(PacketType::NodeIgnoreRequest, QByteArray(), true); + // Force the "enabled" flag in this packet to true + personalMutePacketList->writePrimitive(true); + // enumerate the ignored IDs and write them to the packet list auto it = _personalMutedNodeIDs.cbegin(); while (it != _personalMutedNodeIDs.end()) { @@ -903,6 +908,9 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { // setup a packet list so we can send the stream of ignore IDs auto ignorePacketList = NLPacketList::create(PacketType::NodeIgnoreRequest, QByteArray(), true); + // Force the "enabled" flag in this packet to true + ignorePacketList->writePrimitive(true); + // enumerate the ignored IDs and write them to the packet list auto it = _ignoredNodeIDs.cbegin(); while (it != _ignoredNodeIDs.end()) { @@ -1008,7 +1016,7 @@ void NodeList::processUsernameFromIDReply(QSharedPointer messag } void NodeList::setRequestsDomainListData(bool isRequesting) { - // Tell the avatar mixer whether I want to receive any additional data to which I might be entitiled . + // Tell the avatar mixer whether I want to receive any additional data to which I might be entitled if (_requestsDomainListData == isRequesting) { return; } From 14def61eb8158147eb1087ed67d0e0044a604ab9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 3 Jan 2017 17:32:47 -0800 Subject: [PATCH 39/45] More CR comments; 1 bug remaining re: sig slot --- interface/resources/fonts/hifi-glyphs.ttf | Bin 24428 -> 24436 bytes .../resources/qml/controls-uit/CheckBox.qml | 25 +++--- interface/resources/qml/hifi/Pal.qml | 82 +++++++++--------- interface/src/avatar/AvatarManager.cpp | 6 +- libraries/networking/src/NodeList.cpp | 4 +- libraries/networking/src/NodeList.h | 3 +- .../src/UsersScriptingInterface.cpp | 1 - .../src/UsersScriptingInterface.h | 3 +- scripts/system/pal.js | 8 +- 9 files changed, 68 insertions(+), 64 deletions(-) diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 1c98f4e6f38fa3fd1516ee0b64d2dd36e6ab6d88..3dc3069ef562a2411b242bc5ad93e83162e6e5ac 100644 GIT binary patch delta 1395 zcmYk6U1*zC9LAsjdC&Wi@3cwZv}v+5Y5TtE(yydxlCB?HyY*vRx_+)}y1Fjg+Gbt5 z71rr`p_x!|h&S8Y$(Z17bl%8ND??-q5jR9cL>-8DA;Sw9Lnf$K+Df{Bo{RtUz~Kky zc{t}hxkrE8qdW+J4VNHbxUX+~@Iv-x4w#t);OpPl+lL#FfSDA4s-dx_*wEedFhGU< ziIYp&<<$Cj?*MEaVA_24{PNZo1Yo8afHJ?hI`_&${V5<`2gd4iv)NPTp3Z9kegcrl zGCOkK8tdQLF}q< z8EeK>Hk99mc66WIrO0iT}UE@1PM;KPQ+1<1~j4xt%xFq7L4F1M$wCLv>C^R*1!==BZDbS z;yE0{42~m<6F7-en8Rr-7#|7xo?Y!7oxAsR5A5AH*1!M2!RFyedT>71*VC0uB@Q1N zn(+ApvsJ+}i=pb8T1^kv?TptqG&Z$HV=W^`M|;QHj7sk1N2W7VMmJwGhPh(g=Uw>$ z>$3Hh^^t9d?Y4c?{)1!A@u4&Ayj^;+^oeWA^@-~@x8i=@eW$FkY@zIys;bNCXP$A- zjq*hKTNUn#zKYi?e)NXC3*Jw?k1Io!Cn~?L{M)Da+`cZ~oNwLt$e-}f`LFwb-SJ*v zD)4dDiK+*|UqTb1r`30Awc2mAVO4uc`$}Ka-wyl2;cz^BJiK04U-!nwbunAM@q6Hl zRG#B9uF)#x;6@UII9<3rl58hg@~Fv#uIY{2R*&nNEXh)}6k#FBPj1!IqPE0#kVkEa zB@>-YFKc3rq-)h$gtfM_STd3H#I@E&YSnbDz=CfUT4QqIlpbs~}!L6SsSurlUkjEPK8 zL_4;42^SpPBw9+epxZ5SE}GO-sU+FTZA3P6zhWoi-n?RB+}$2Yy-Bi(mvBbi+^qJJ z%($KVTrx3HG6#Y+HHsiIGSvX!Si==sr3J`vAc}r~WJ%ji302m$M1WLDk8gM7pa>Pa zQrju!`F~lu)QUgOr@qaQ)y*Ub0Mp0BSu!m;vmYOoA(#eGO0o`Gs)G_&~}qV!6Z6GChp~`6~>*( zI$gu7+Yh{u(1Hg=FT#SyMK8fZc0KJy#y718C#iJ<9ues z5k~`}Xh00jh$3k`%v34yhG;{qIhr&!GiQyZsrwQ_G(w{rsiW<*@$&S$%r0LzyL4e? S_LR6ijqps$`rlWxnf@1s&HXn3 delta 1439 zcmYL}No*5W9LB%*X7!B6@iLxq9B)~eiJh1@PU0klC4jR4*#jmCA;Ezp4s{?ZVHK@G z0*T&G2elEGR`h~6pq3!YA>vR0YEf@p)I&v4RaK~>>ZO&6R7B-qweRrWPfz+jz5geD z@6QM1#RF0R0ifUl7z}lH@9kf1c>Elg9s&^PNq2SQI(T3@0U$Cs+K?Pv>l*^d7=X;- z#q3fhck3ZQuL0DU&z@L%{Td7~T?;^*&!3%p_w3w<0Jj2+Msl;+BR1!(7r>7IQn}o0 zAcn4`;{jAG-&zYemsw_RJEVkSTz)6(4jgyEywt zYApkd-3Q3(((x0^uO5E%12Bo;QH5&MU;_-8s6`Yp)S(_JY(xVRXhbWT(2N$0;vn{-2N`r&hnSY|IHqs_ zlbFCX4k3#f9L5pMVh%aHg}il_F*`SJ*}84}j-9)^y7%qc)7PIIi4P1e9PLeawr^_N zKQufR2!`h)l~vU>8w|5H8mp^MZEQ$1wl+1lj2_(Elj*Q3SU)>Hbzsuk&R(*HSkZdK zZY?}k>Xo#TS8gaz9e&5HvVpR1oioncu3pzI&$8#ano;kmzj;~j+ul1`OqL6ZTS2evUsV8+!*P4>8sV&s>Hc-Pf z>X?=elGKZLt$MT3(rgqR-gtPmqSG4zNfAmp!UztrP{uN%=nz?{*5h`tE`?{QA}KCH z9Fj|98S3@e90KDBQ&F-2f4IV;f0nf@!3}MB~@P;)_{T@j;?$x>n5O zzo=5C*EAaFdR9!bwUBBhD``oS)3O>)%bJvy)bpy_sRRh8NQ%cPadw3zs@LXVL=TBj zp2`&r>lE#bO(PszLx?{usd8G@21+(7UQu*9LX6A4Pj(W*s6LqrdQY>jevR^s9AXAp zv)Ew}T*2zA;KMfS(U?&%@!op1i$luzs>B>*{9a-nGI4QKdE{}{IyB+63gg4BMkEkNJK{*L*EwbVG#;@vCgSb! zWTG);4Ne$Ve4^J{o_NfYVKRcM)y@MQL=jFNTRgcudxR_QtLG+fQY$gtX8-?Na@+p} DWX1v; diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 9db4f621f9..d3cbb87e5b 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -51,19 +51,6 @@ Original.CheckBox { } } - Rectangle { - id: disabledOverlay - visible: !enabled - z: 100 - width: boxSize - height: boxSize - radius: boxRadius - border.width: 1 - border.color: hifi.colors.baseGrayHighlight - color: hifi.colors.baseGrayHighlight - opacity: 0.5 - } - Rectangle { visible: pressed || hovered anchors.centerIn: parent @@ -85,6 +72,18 @@ Original.CheckBox { border.color: hifi.colors.checkboxCheckedBorder visible: checked && !pressed || !checked && pressed } + + Rectangle { + id: disabledOverlay + visible: !enabled + width: boxSize + height: boxSize + radius: boxRadius + border.width: 1 + border.color: hifi.colors.baseGrayHighlight + color: hifi.colors.baseGrayHighlight + opacity: 0.5 + } } label: Label { diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 3c3cde7a67..2c3137c8a0 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -198,8 +198,8 @@ Item { id: nameCard // Properties displayName: styleData.value - userName: model ? model.userName : "" - audioLevel: model ? model.audioLevel : 0.0 + userName: model && model.userName + audioLevel: model && model.audioLevel visible: !isCheckBox && !isButton // Size width: nameCardWidth @@ -218,31 +218,24 @@ Item { id: actionCheckBox visible: isCheckBox anchors.centerIn: parent - checked: model ? model[styleData.role] : false + checked: model && model[styleData.role] // If this is a "personal mute" checkbox, and the model is valid, Check the checkbox if the Ignore // checkbox is checked. - enabled: styleData.role === "personalMute" ? ((model ? model["ignore"] : false) === true ? false : true) : true + enabled: styleData.role === "personalMute" ? ((model && model["ignore"]) === true ? false : true) : true boxSize: 24 onClicked: { var newValue = !model[styleData.role] - if (newValue === undefined) { - newValue = false - } userModel.setProperty(model.userIndex, styleData.role, newValue) userModelData[model.userIndex][styleData.role] = newValue // Defensive programming - if (styleData.role === "personalMute" || styleData.role === "ignore") { - Users[styleData.role](model.sessionId, newValue) - if (styleData.role === "ignore") { - userModel.setProperty(model.userIndex, "personalMute", newValue) - userModelData[model.userIndex]["personalMute"] = newValue // Defensive programming - if (newValue) { - ignored[model.sessionId] = userModelData[model.userIndex] - } else { - delete ignored[model.sessionId] - } + Users[styleData.role](model.sessionId, newValue) + if (styleData.role === "ignore") { + userModel.setProperty(model.userIndex, "personalMute", newValue) + userModelData[model.userIndex]["personalMute"] = newValue // Defensive programming + if (newValue) { + ignored[model.sessionId] = userModelData[model.userIndex] + } else { + delete ignored[model.sessionId] } - } else { - console.log("User clicked on an unknown checkbox."); } // http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html#creating-property-bindings-from-javascript // I'm using an explicit binding here because clicking a checkbox breaks the implicit binding as set by @@ -257,21 +250,29 @@ Item { color: 2 // Red visible: isButton anchors.centerIn: parent - width: 24 + width: 32 height: 24 onClicked: { - if (styleData.role === "mute" || styleData.role === "kick") { - Users[styleData.role](model.sessionId) - if (styleData.role === "kick") { - // Just for now, while we cannot undo "Ban": - userModel.remove(model.userIndex) - delete userModelData[model.userIndex] // Defensive programming - sortModel() - } - } else { - console.log("User clicked on an unknown checkbox."); + Users[styleData.role](model.sessionId) + if (styleData.role === "kick") { + // Just for now, while we cannot undo "Ban": + userModel.remove(model.userIndex) + delete userModelData[model.userIndex] // Defensive programming + sortModel() } } + // muted/error glyphs + HiFiGlyphs { + text: (styleData.role === "kick") ? hifi.glyphs.error : hifi.glyphs.muted + // Size + size: parent.height*1.3 + // Anchors + anchors.fill: parent + // Style + horizontalAlignment: Text.AlignHCenter + color: enabled ? hifi.buttons.textColor[actionButton.color] + : hifi.buttons.disabledTextColor[actionButton.colorScheme] + } } } } @@ -374,8 +375,8 @@ Item { FiraSansSemiBold { id: popupText text: "Bold names in the list are Avatar Display Names.\n" + - "If a Display Name isn't set, a unique Session Display Name is assigned to them." + - "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each present avatar." + "If a Display Name isn't set, a unique Session Display Name is assigned." + + "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present." size: hifi.fontSizes.textFieldInput color: hifi.colors.darkGray horizontalAlignment: Text.AlignHCenter @@ -431,13 +432,11 @@ Item { var sessionId = message.params[0]; var selected = message.params[1]; var userIndex = findSessionIndex(sessionId); - if (userIndex != -1) { - if (selected) { - table.selection.clear(); // for now, no multi-select - table.selection.select(userIndex); - } else { - table.selection.deselect(userIndex); - } + if (selected) { + table.selection.clear(); // for now, no multi-select + table.selection.select(userIndex); + } else { + table.selection.deselect(userIndex); } break; // Received an "updateUsername()" request from the JS @@ -457,6 +456,8 @@ Item { // Set the userName appropriately userModel.setProperty(userIndex, "userName", userName); userModelData[userIndex].userName = userName; // Defensive programming + } else { + console.log("updateUsername() called with unknown UUID: ", userId); } } break; @@ -472,6 +473,8 @@ Item { if (userIndex != -1) { userModel.setProperty(userIndex, "audioLevel", audioLevel); userModelData[userIndex].audioLevel = audioLevel; // Defensive programming + } else { + console.log("updateUsername() called with unknown UUID: ", userId); } } } @@ -505,7 +508,6 @@ Item { ['personalMute', 'ignore', 'mute', 'kick'].forEach(init); datum.userIndex = userIndex++; userModel.append(datum); - console.log('appending to userModel:', JSON.stringify(datum)); }); } signal sendToScript(var message); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c534e9b499..e3ccc10a65 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -82,7 +82,11 @@ AvatarManager::AvatarManager(QObject* parent) : // when we hear that the user has ignored an avatar by session UUID // immediately remove that avatar instead of waiting for the absence of packets from avatar mixer - connect(nodeList.data(), "ignoredNode", this, "removeAvatar"); + connect(nodeList.data(), &NodeList::ignoredNode, this, [=](const QUuid& nodeID, bool enabled) { + if (enabled) { + removeAvatar(nodeID); + } + }); } AvatarManager::~AvatarManager() { diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 64f0479a51..d0281d0029 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -808,13 +808,13 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { _ignoredNodeIDs.insert(nodeID); // add this nodeID to our set of personal muted IDs _personalMutedNodeIDs.insert(nodeID); - emit ignoredNode(nodeID); + emit ignoredNode(nodeID, true); } else { QWriteLocker ignoredSetLocker{ &_ignoredSetLock }; // write lock for unsafe_erase QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock }; // write lock for unsafe_erase _ignoredNodeIDs.unsafe_erase(nodeID); _personalMutedNodeIDs.unsafe_erase(nodeID); - emit unignoredNode(nodeID); + emit ignoredNode(nodeID, false); } } else { diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index ba19f56f9f..75958f1847 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -113,8 +113,7 @@ public slots: signals: void limitOfSilentDomainCheckInsReached(); void receivedDomainServerList(); - void ignoredNode(const QUuid& nodeID); - void unignoredNode(const QUuid& nodeID); + void ignoredNode(const QUuid& nodeID, bool enabled); void ignoreRadiusEnabledChanged(bool isIgnored); void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 58680b944d..81ed0c9c63 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -20,7 +20,6 @@ UsersScriptingInterface::UsersScriptingInterface() { connect(nodeList.data(), &NodeList::ignoreRadiusEnabledChanged, this, &UsersScriptingInterface::ignoreRadiusEnabledChanged); connect(nodeList.data(), &NodeList::usernameFromIDReply, this, &UsersScriptingInterface::usernameFromIDReply); connect(nodeList.data(), &NodeList::ignoredNode, this, &UsersScriptingInterface::ignoredNode); - connect(nodeList.data(), &NodeList::unignoredNode, this, &UsersScriptingInterface::unignoredNode); } void UsersScriptingInterface::ignore(const QUuid& nodeID, bool ignoreEnabled) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 223ddb879b..06f1fb6fae 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -117,8 +117,7 @@ public slots: signals: void canKickChanged(bool canKick); void ignoreRadiusEnabledChanged(bool isEnabled); - void ignoredNode(const QUuid& nodeID); - void unignoredNode(const QUuid& nodeID); + void ignoredNode(const QUuid& nodeID, bool enabled); /**jsdoc * Notifies scripts that another user has entered the ignore radius diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 2bc016dd06..d61c75099f 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -333,9 +333,11 @@ pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); -function onIgnore(sessionId) { // make it go away in the usual way, since we'll still get data keeping it live - // Why doesn't this work from .qml? (crashes) - AvatarList.getAvatar(sessionId).setShouldDie(); +function onIgnore(sessionId, enabled) { // make it go away in the usual way, since we'll still get data keeping it live + if (enabled) { + // Why doesn't this work from .qml? (crashes) + AvatarList.getAvatar(sessionId).setShouldDie(); + } } Users.ignoredNode.connect(onIgnore); From dde5f69ac9ff44505b50c397b77be4a8376b70b7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Jan 2017 12:41:30 -0800 Subject: [PATCH 40/45] More CR & simplifications --- interface/resources/qml/hifi/Pal.qml | 11 +++++------ .../script-engine/src/UsersScriptingInterface.cpp | 1 - libraries/script-engine/src/UsersScriptingInterface.h | 1 - scripts/system/pal.js | 9 --------- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 2c3137c8a0..c9adb03d24 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -218,10 +218,9 @@ Item { id: actionCheckBox visible: isCheckBox anchors.centerIn: parent - checked: model && model[styleData.role] - // If this is a "personal mute" checkbox, and the model is valid, Check the checkbox if the Ignore - // checkbox is checked. - enabled: styleData.role === "personalMute" ? ((model && model["ignore"]) === true ? false : true) : true + checked: model[styleData.role] + // If this is a "Personal Mute" checkbox, disable the checkbox if the "Ignore" checkbox is checked. + enabled: !(styleData.role === "personalMute" && model["ignore"]) boxSize: 24 onClicked: { var newValue = !model[styleData.role] @@ -239,8 +238,8 @@ Item { } // http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html#creating-property-bindings-from-javascript // I'm using an explicit binding here because clicking a checkbox breaks the implicit binding as set by - // "checked: model[styleData.role]" above. - checked = Qt.binding(function() { return (model && model[styleData.role]) ? model[styleData.role] : false}) + // "checked:" statement above. + checked = Qt.binding(function() { return (model[styleData.role])}) } } diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 81ed0c9c63..d0ad699846 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -19,7 +19,6 @@ UsersScriptingInterface::UsersScriptingInterface() { connect(nodeList.data(), &LimitedNodeList::canKickChanged, this, &UsersScriptingInterface::canKickChanged); connect(nodeList.data(), &NodeList::ignoreRadiusEnabledChanged, this, &UsersScriptingInterface::ignoreRadiusEnabledChanged); connect(nodeList.data(), &NodeList::usernameFromIDReply, this, &UsersScriptingInterface::usernameFromIDReply); - connect(nodeList.data(), &NodeList::ignoredNode, this, &UsersScriptingInterface::ignoredNode); } void UsersScriptingInterface::ignore(const QUuid& nodeID, bool ignoreEnabled) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 06f1fb6fae..4182d5244c 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -117,7 +117,6 @@ public slots: signals: void canKickChanged(bool canKick); void ignoreRadiusEnabledChanged(bool isEnabled); - void ignoredNode(const QUuid& nodeID, bool enabled); /**jsdoc * Notifies scripts that another user has entered the ignore radius diff --git a/scripts/system/pal.js b/scripts/system/pal.js index d61c75099f..378d62c69c 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -333,14 +333,6 @@ pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); -function onIgnore(sessionId, enabled) { // make it go away in the usual way, since we'll still get data keeping it live - if (enabled) { - // Why doesn't this work from .qml? (crashes) - AvatarList.getAvatar(sessionId).setShouldDie(); - } -} -Users.ignoredNode.connect(onIgnore); - // // Cleanup. // @@ -350,7 +342,6 @@ Script.scriptEnding.connect(function () { pal.visibleChanged.disconnect(onVisibleChanged); pal.closed.disconnect(off); Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Users.ignoredNode.disconnect(onIgnore); off(); }); From c6958780255a5aec42b4385f4f9f4e30a804a7c1 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Jan 2017 13:31:16 -0800 Subject: [PATCH 41/45] Clear local ignored list upon domain change --- interface/resources/qml/hifi/Pal.qml | 4 +++- scripts/system/pal.js | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index c9adb03d24..565809762e 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -42,7 +42,6 @@ Item { property int actionButtonWidth: 75 property int nameCardWidth: width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set - // FIXME: Need to determine & handle when this list gets reset. property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring. property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities. property bool iAmAdmin: false @@ -478,6 +477,9 @@ Item { } } break; + case 'clearIgnored': + ignored = ({}); + break; default: console.log('Unrecognized message:', JSON.stringify(message)); } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 378d62c69c..505c1840da 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -332,6 +332,13 @@ button.clicked.connect(onClicked); pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); +function clearIgnoredInQMLAndRefreshPAL() { + pal.sendToQml({ method: 'clearIgnored' }); + removeOverlays(); + populateUserList(); +} +Window.domainChanged.connect(clearIgnoredInQMLAndRefreshPAL); +Window.domainConnectionRefused.connect(clearIgnoredInQMLAndRefreshPAL); // // Cleanup. @@ -342,6 +349,8 @@ Script.scriptEnding.connect(function () { pal.visibleChanged.disconnect(onVisibleChanged); pal.closed.disconnect(off); Users.usernameFromIDReply.disconnect(usernameFromIDReply); + Window.domainChanged.disconnect(clearIgnoredInQMLAndRefreshPAL); + Window.domainConnectionRefused.disconnect(clearIgnoredInQMLAndRefreshPAL); off(); }); From 03fbfc85f953274e9d480f11fd1b3714e48772bc Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Jan 2017 13:50:37 -0800 Subject: [PATCH 42/45] Short delay before populating PAL when changing domains --- scripts/system/pal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 505c1840da..bc9784fb17 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -335,7 +335,7 @@ Users.usernameFromIDReply.connect(usernameFromIDReply); function clearIgnoredInQMLAndRefreshPAL() { pal.sendToQml({ method: 'clearIgnored' }); removeOverlays(); - populateUserList(); + Script.setTimeout(populateUserList, 200); // Short delay before populating the PAL to allow the HashMap to populate } Window.domainChanged.connect(clearIgnoredInQMLAndRefreshPAL); Window.domainConnectionRefused.connect(clearIgnoredInQMLAndRefreshPAL); From ac41e8abc41cf453ed4f469686695324775cbe13 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Jan 2017 14:06:51 -0800 Subject: [PATCH 43/45] Bugfix re: population; remove user image for now --- interface/resources/qml/hifi/NameCard.qml | 4 +++- scripts/system/pal.js | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 9b90ae6c3b..0ad8bfd57e 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -35,6 +35,7 @@ Row { property int usernameTextHeight: 12 property real audioLevel: 0.0 + /* User image commented out for now - will probably be re-introduced later. Column { id: avatarImage // Size @@ -48,10 +49,11 @@ Row { height: parent.height } } + */ Column { id: textContainer // Size - width: parent.width - avatarImage.width - parent.anchors.leftMargin - parent.anchors.rightMargin - parent.spacing + width: parent.width - /*avatarImage.width - */parent.anchors.leftMargin - parent.anchors.rightMargin - parent.spacing height: contentHeight // DisplayName Text diff --git a/scripts/system/pal.js b/scripts/system/pal.js index bc9784fb17..8d7422109c 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -334,8 +334,10 @@ pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); function clearIgnoredInQMLAndRefreshPAL() { pal.sendToQml({ method: 'clearIgnored' }); - removeOverlays(); - Script.setTimeout(populateUserList, 200); // Short delay before populating the PAL to allow the HashMap to populate + if (pal.visible) { + removeOverlays(); + Script.setTimeout(populateUserList, 200); // Short delay before populating the PAL to allow the HashMap to populate + } } Window.domainChanged.connect(clearIgnoredInQMLAndRefreshPAL); Window.domainConnectionRefused.connect(clearIgnoredInQMLAndRefreshPAL); From 675159be45bdf617dcaaeff7f7afd8ae1b170c64 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Jan 2017 14:33:02 -0800 Subject: [PATCH 44/45] Last commit to fix final bugs --- interface/resources/qml/hifi/Pal.qml | 2 +- scripts/system/pal.js | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 565809762e..833cf4efe2 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -478,7 +478,7 @@ Item { } break; case 'clearIgnored': - ignored = ({}); + ignored = {}; break; default: console.log('Unrecognized message:', JSON.stringify(message)); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 8d7422109c..5eb35c2595 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -332,15 +332,14 @@ button.clicked.connect(onClicked); pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); -function clearIgnoredInQMLAndRefreshPAL() { +function clearIgnoredInQMLAndClosePAL() { pal.sendToQml({ method: 'clearIgnored' }); if (pal.visible) { - removeOverlays(); - Script.setTimeout(populateUserList, 200); // Short delay before populating the PAL to allow the HashMap to populate + onClicked(); // Close the PAL } } -Window.domainChanged.connect(clearIgnoredInQMLAndRefreshPAL); -Window.domainConnectionRefused.connect(clearIgnoredInQMLAndRefreshPAL); +Window.domainChanged.connect(clearIgnoredInQMLAndClosePAL); +Window.domainConnectionRefused.connect(clearIgnoredInQMLAndClosePAL); // // Cleanup. @@ -351,8 +350,8 @@ Script.scriptEnding.connect(function () { pal.visibleChanged.disconnect(onVisibleChanged); pal.closed.disconnect(off); Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Window.domainChanged.disconnect(clearIgnoredInQMLAndRefreshPAL); - Window.domainConnectionRefused.disconnect(clearIgnoredInQMLAndRefreshPAL); + Window.domainChanged.disconnect(clearIgnoredInQMLAndClosePAL); + Window.domainConnectionRefused.disconnect(clearIgnoredInQMLAndClosePAL); off(); }); From e80ae92327ac230771d9d2763a7d653924abc72c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Jan 2017 15:06:18 -0800 Subject: [PATCH 45/45] Also clear personalmutedlist upon reset() --- libraries/networking/src/NodeList.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 27d9aa4572..bd3203150e 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -242,6 +242,10 @@ void NodeList::reset() { _ignoredSetLock.lockForWrite(); _ignoredNodeIDs.clear(); _ignoredSetLock.unlock(); + // lock and clear our set of personally muted IDs + _personalMutedSetLock.lockForWrite(); + _personalMutedNodeIDs.clear(); + _personalMutedSetLock.unlock(); // refresh the owner UUID to the NULL UUID setSessionUUID(QUuid());