From c7e9f79200ac487f3acda117358d739e042c0893 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 30 Jan 2019 13:55:12 -0800 Subject: [PATCH 01/35] Include the new scale float in min remaining size calculation --- libraries/avatars/src/AvatarData.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4e95774efb..c733cfa291 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -632,9 +632,11 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent // include jointData if there is room for the most minimal section. i.e. no translations or rotations. IF_AVATAR_SPACE(PACKET_HAS_JOINT_DATA, AvatarDataPacket::minJointDataSize(numJoints)) { - // Allow for faux joints + translation bit-vector: - const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat) - + jointBitVectorSize + AvatarDataPacket::FAUX_JOINTS_SIZE; + // Minimum space required for another rotation joint - + // size of joint + following translation bit-vector + translation scale + faux joints: + const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat) + jointBitVectorSize + + sizeof(float) + AvatarDataPacket::FAUX_JOINTS_SIZE; + auto startSection = destinationBuffer; // compute maxTranslationDimension before we send any joint data. @@ -724,6 +726,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent const JointData& data = joints[i]; const JointData& last = lastSentJointData[i]; + // Note minSizeForJoint is conservative since there isn't a following bit-vector + scale. if (packetEnd - destinationBuffer >= minSizeForJoint) { if (!data.translationIsDefaultPose) { if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation) From b09a9390666707506ba482c2999fe4d54bf3dccf Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 30 Jan 2019 17:03:16 -0800 Subject: [PATCH 02/35] Fix reload mechanic for entity server scripts --- .../src/scripts/EntityScriptServer.cpp | 16 ++++++++-------- .../src/scripts/EntityScriptServer.h | 2 +- .../entities-renderer/src/EntityTreeRenderer.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index ef0c807bc4..f1a6c97831 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -112,7 +112,6 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointerunloadEntityScript(entityID); checkAndCallPreload(entityID, true); } } @@ -184,7 +183,6 @@ void EntityScriptServer::updateEntityPPS() { pps = std::min(_maxEntityPPS, pps); } _entityEditSender.setPacketsPerSecond(pps); - qDebug() << QString("Updating entity PPS to: %1 @ %2 PPS per script = %3 PPS").arg(numRunningScripts).arg(_entityPPSPerScript).arg(pps); } void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer message, SharedNodePointer senderNode) { @@ -525,23 +523,25 @@ void EntityScriptServer::deletingEntity(const EntityItemID& entityID) { void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, bool reload) { if (_entityViewer.getTree() && !_shuttingDown) { - _entitiesScriptEngine->unloadEntityScript(entityID, true); checkAndCallPreload(entityID, reload); } } -void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool reload) { +void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload) { if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) { EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID); EntityScriptDetails details; - bool notRunning = !_entitiesScriptEngine->getEntityScriptDetails(entityID, details); - if (entity && (reload || notRunning || details.scriptText != entity->getServerScripts())) { + bool isRunning = _entitiesScriptEngine->getEntityScriptDetails(entityID, details); + if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) { + if (isRunning) { + _entitiesScriptEngine->unloadEntityScript(entityID, true); + } + QString scriptUrl = entity->getServerScripts(); if (!scriptUrl.isEmpty()) { scriptUrl = DependencyManager::get()->normalizeURL(scriptUrl); - qCDebug(entity_script_server) << "Loading entity server script" << scriptUrl << "for" << entityID; - _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload); + _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, forceRedownload); } } } diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h index 9c6c4c752e..944fee36a3 100644 --- a/assignment-client/src/scripts/EntityScriptServer.h +++ b/assignment-client/src/scripts/EntityScriptServer.h @@ -67,7 +67,7 @@ private: void addingEntity(const EntityItemID& entityID); void deletingEntity(const EntityItemID& entityID); void entityServerScriptChanging(const EntityItemID& entityID, bool reload); - void checkAndCallPreload(const EntityItemID& entityID, bool reload = false); + void checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload = false); void cleanupOldKilledListeners(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index c71b296a74..44025fc8f4 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1048,7 +1048,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool QString scriptUrl = entity->getScript(); if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) { if (_entitiesScriptEngine) { - _entitiesScriptEngine->unloadEntityScript(entityID); + _entitiesScriptEngine->unloadEntityScript(entityID); } entity->scriptHasUnloaded(); } From e2f82eb94927cc306e802131112c3f0d2fbba6b8 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 31 Jan 2019 09:35:42 -0700 Subject: [PATCH 03/35] Fix assertion on shapeInfo --- .../src/avatars-renderer/Avatar.cpp | 18 ++++++++++-------- libraries/shared/src/ShapeInfo.cpp | 3 ++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 07c1ca9a32..ba5529e1c0 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1733,15 +1733,17 @@ void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { void Avatar::computeDetailedShapeInfo(ShapeInfo& shapeInfo, int jointIndex) { if (jointIndex > -1 && jointIndex < (int)_multiSphereShapes.size()) { auto& data = _multiSphereShapes[jointIndex].getSpheresData(); - std::vector positions; - std::vector radiuses; - positions.reserve(data.size()); - radiuses.reserve(data.size()); - for (auto& sphere : data) { - positions.push_back(sphere._position); - radiuses.push_back(sphere._radius); + if (data.size() > 0) { + std::vector positions; + std::vector radiuses; + positions.reserve(data.size()); + radiuses.reserve(data.size()); + for (auto& sphere : data) { + positions.push_back(sphere._position); + radiuses.push_back(sphere._radius); + } + shapeInfo.setMultiSphere(positions, radiuses); } - shapeInfo.setMultiSphere(positions, radiuses); } } diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 564d79bfda..c256cf2b76 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -152,7 +152,8 @@ void ShapeInfo::setSphere(float radius) { void ShapeInfo::setMultiSphere(const std::vector& centers, const std::vector& radiuses) { _url = ""; _type = SHAPE_TYPE_MULTISPHERE; - assert(centers.size() == radiuses.size() && centers.size() > 0); + assert(centers.size() == radiuses.size()); + assert(centers.size() > 0); for (size_t i = 0; i < centers.size(); i++) { SphereData sphere = SphereData(centers[i], radiuses[i]); _sphereCollection.push_back(sphere); From fbdae1824505bca0d4b831f17cb5a2c8200b1b98 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 31 Jan 2019 19:37:21 +0100 Subject: [PATCH 04/35] ignore pickRay for zone shape visualizers --- scripts/system/modules/entityShapeVisualizer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/modules/entityShapeVisualizer.js b/scripts/system/modules/entityShapeVisualizer.js index fe950c2e2b..fdf8ee81e7 100644 --- a/scripts/system/modules/entityShapeVisualizer.js +++ b/scripts/system/modules/entityShapeVisualizer.js @@ -135,6 +135,7 @@ EntityShape.prototype = { overlayProperties.canCastShadows = false; overlayProperties.parentID = this.entityID; overlayProperties.collisionless = true; + overlayProperties.ignorePickIntersection = true; this.entity = Entities.addEntity(overlayProperties, "local"); var PROJECTED_MATERIALS = false; this.materialEntity = Entities.addEntity({ From 9082a3f4e5bed7b1f8365652a7dc1a086f2a9a6d Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 31 Jan 2019 10:52:48 -0800 Subject: [PATCH 05/35] to the spot on first launch --- interface/src/ui/AddressBarDialog.cpp | 2 +- libraries/networking/src/AddressManager.cpp | 3 ++- libraries/networking/src/AddressManager.h | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 789a2a2bdf..799d7ea182 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -59,7 +59,7 @@ void AddressBarDialog::loadHome() { auto locationBookmarks = DependencyManager::get(); QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK); if (homeLocation == "") { - homeLocation = DEFAULT_HIFI_ADDRESS; + homeLocation = DEFAULT_HOME_ADDRESS; } DependencyManager::get()->handleLookupString(homeLocation); } diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index e6957728e8..9145b4a79e 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -30,7 +30,8 @@ #include "UserActivityLogger.h" #include "udt/PacketHeaders.h" -const QString DEFAULT_HIFI_ADDRESS = "file:///~/serverless/tutorial.json"; +const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome"; +const QString DEFAULT_HOME_ADDRESS = "file:///~/serverless/tutorial.json"; const QString REDIRECT_HIFI_ADDRESS = "file:///~/serverless/redirect.json"; const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 5318822cdc..450b71023c 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -24,6 +24,7 @@ extern const QString DEFAULT_HIFI_ADDRESS; extern const QString REDIRECT_HIFI_ADDRESS; +extern const QString DEFAULT_HOME_ADDRESS; const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost"; const QString INDEX_PATH = "/"; From ca4695377fe5db088a78b4fa78b428842f2665ad Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 31 Jan 2019 22:12:35 +0100 Subject: [PATCH 06/35] also ignore picking on the material entity --- scripts/system/modules/entityShapeVisualizer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/modules/entityShapeVisualizer.js b/scripts/system/modules/entityShapeVisualizer.js index fdf8ee81e7..da28369cdd 100644 --- a/scripts/system/modules/entityShapeVisualizer.js +++ b/scripts/system/modules/entityShapeVisualizer.js @@ -147,6 +147,7 @@ EntityShape.prototype = { priority: 1, materialMappingMode: PROJECTED_MATERIALS ? "projected" : "uv", materialURL: Script.resolvePath("../assets/images/materials/GridPattern.json"), + ignorePickIntersection: true, }, "local"); }, update: function() { From ab6048e92296b5594bf7f30bb09baf33352b7339 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 1 Feb 2019 10:12:04 -0800 Subject: [PATCH 07/35] give tablet root color --- .../qml/hifi/commerce/common/sendAsset/SendAsset.qml | 3 +-- interface/resources/qml/hifi/tablet/TabletRoot.qml | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index bc8816e0ea..68d437a346 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -21,11 +21,10 @@ import "../../../../controls" as HifiControls import "../" as HifiCommerceCommon import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. -Rectangle { +Item { HifiConstants { id: hifi; } id: root; - color: hifi.colors.baseGray property int parentAppTitleBarHeight; property int parentAppNavBarHeight; property string currentActiveView: "sendAssetHome"; diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index b19dcbb919..93a23f1b9d 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -3,10 +3,13 @@ import Hifi 1.0 import "../../dialogs" import "../../controls" +import stylesUit 1.0 -Item { +Rectangle { + HifiConstants { id: hifi; } id: tabletRoot objectName: "tabletRoot" + color: hifi.colors.baseGray property string username: "Unknown user" property string usernameShort: "Unknown user" property var rootMenu; From 5f3e3309b8a26fca581c7fbff49addb45764a9d2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 4 Feb 2019 09:47:04 -0800 Subject: [PATCH 08/35] 0.79.0: trust the data in the packet, Luke --- libraries/octree/src/OctreePacketData.cpp | 27 ----------------------- 1 file changed, 27 deletions(-) diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index a79f0a0c2b..8ab502e951 100755 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -729,12 +729,6 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char *dataBytes, QVecto uint16_t length; memcpy(&length, dataBytes, sizeof(uint16_t)); dataBytes += sizeof(length); - - // FIXME - this size check is wrong if we allow larger packets - if (length * sizeof(glm::vec3) > MAX_OCTREE_UNCOMRESSED_PACKET_SIZE) { - result.resize(0); - return sizeof(uint16_t); - } result.resize(length); memcpy(result.data(), dataBytes, length * sizeof(glm::vec3)); return sizeof(uint16_t) + length * sizeof(glm::vec3); @@ -744,14 +738,7 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char *dataBytes, QVecto uint16_t length; memcpy(&length, dataBytes, sizeof(uint16_t)); dataBytes += sizeof(length); - - // FIXME - this size check is wrong if we allow larger packets - if (length * sizeof(glm::quat) > MAX_OCTREE_UNCOMRESSED_PACKET_SIZE) { - result.resize(0); - return sizeof(uint16_t); - } result.resize(length); - const unsigned char *start = dataBytes; for (int i = 0; i < length; i++) { dataBytes += unpackOrientationQuatFromBytes(dataBytes, result[i]); @@ -764,12 +751,6 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto uint16_t length; memcpy(&length, dataBytes, sizeof(uint16_t)); dataBytes += sizeof(length); - - // FIXME - this size check is wrong if we allow larger packets - if (length * sizeof(float) > MAX_OCTREE_UNCOMRESSED_PACKET_SIZE) { - result.resize(0); - return sizeof(uint16_t); - } result.resize(length); memcpy(result.data(), dataBytes, length * sizeof(float)); return sizeof(uint16_t) + length * sizeof(float); @@ -779,14 +760,7 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto uint16_t length; memcpy(&length, dataBytes, sizeof(uint16_t)); dataBytes += sizeof(length); - - // FIXME - this size check is wrong if we allow larger packets - if (length / 8 > MAX_OCTREE_UNCOMRESSED_PACKET_SIZE) { - result.resize(0); - return sizeof(uint16_t); - } result.resize(length); - int bit = 0; unsigned char current = 0; const unsigned char *start = dataBytes; @@ -797,7 +771,6 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto result[i] = (bool)(current & (1 << bit)); bit = (bit + 1) % BITS_IN_BYTE; } - return (dataBytes - start) + (int)sizeof(uint16_t); } From 17cafe7cce02e78fbe748292b646d133b053a00b Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 30 Jan 2019 17:17:39 -0800 Subject: [PATCH 09/35] fix getEntityProperties (cherry picked from commit 6e61c02d04b65bcc32211b7b6e1667d5cab06bcd) --- libraries/entities/src/EntityItem.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 41e4f43a5d..1fb1ebb1bc 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -88,13 +88,13 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_REGISTRATION_POINT; requestedProperties += PROP_CREATED; requestedProperties += PROP_LAST_EDITED_BY; - //requestedProperties += PROP_ENTITY_HOST_TYPE; // not sent over the wire - //requestedProperties += PROP_OWNING_AVATAR_ID; // not sent over the wire + requestedProperties += PROP_ENTITY_HOST_TYPE; + requestedProperties += PROP_OWNING_AVATAR_ID; requestedProperties += PROP_PARENT_ID; requestedProperties += PROP_PARENT_JOINT_INDEX; requestedProperties += PROP_QUERY_AA_CUBE; requestedProperties += PROP_CAN_CAST_SHADOW; - // requestedProperties += PROP_VISIBLE_IN_SECONDARY_CAMERA; // not sent over the wire + requestedProperties += PROP_VISIBLE_IN_SECONDARY_CAMERA; requestedProperties += PROP_RENDER_LAYER; requestedProperties += PROP_PRIMITIVE_MODE; requestedProperties += PROP_IGNORE_PICK_INTERSECTION; @@ -180,6 +180,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); + // these properties are not sent over the wire + requestedProperties -= PROP_ENTITY_HOST_TYPE; + requestedProperties -= PROP_OWNING_AVATAR_ID; + requestedProperties -= PROP_VISIBLE_IN_SECONDARY_CAMERA; + // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, // then our entityTreeElementExtraEncodeData should include data about which properties we need to append. if (entityTreeElementExtraEncodeData && entityTreeElementExtraEncodeData->entities.contains(getEntityItemID())) { From c8a0d61fd15dedf0ec28f1970a0c58436f8d1e97 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 5 Feb 2019 17:00:18 -0800 Subject: [PATCH 10/35] don't flushRepeatedMessages() in LogHandler dtor --- libraries/shared/src/LogHandler.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp index 65651373be..c51d9bf611 100644 --- a/libraries/shared/src/LogHandler.cpp +++ b/libraries/shared/src/LogHandler.cpp @@ -38,7 +38,6 @@ LogHandler::LogHandler() { } LogHandler::~LogHandler() { - flushRepeatedMessages(); } const char* stringForLogType(LogMsgType msgType) { From e0cb37af4b680b3754eb1b6acc92583a0ce64b5a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 7 Feb 2019 10:14:10 -0800 Subject: [PATCH 11/35] fix black albedo coloring (cherry picked from commit 7fe0e5909e7ef4e13eaeb8cfe7b94e5c40f36465) --- libraries/graphics/src/graphics/Material.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index 7befb7e053..7743c4bf50 100755 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -87,7 +87,7 @@ void Material::setUnlit(bool value) { } void Material::setAlbedo(const glm::vec3& albedo, bool isSRGB) { - _key.setAlbedo(glm::any(glm::greaterThan(albedo, glm::vec3(0.0f)))); + _key.setAlbedo(true); _albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo); } From 0e9e4b33321edb0ae14629d206511258f6592d04 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 7 Feb 2019 11:22:33 -0800 Subject: [PATCH 12/35] Fix normal textures not being visible --- .../model-baker/src/model-baker/CalculateMeshTangentsTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp b/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp index e94e15507e..c561a745b5 100644 --- a/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp +++ b/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp @@ -47,7 +47,7 @@ void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, co break; } } - if (needTangents) { + if (!needTangents) { continue; } From 466c2261937dd532e493bb1b4ae14525badd15e9 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 7 Feb 2019 11:27:08 -0800 Subject: [PATCH 13/35] Do not use continues in logic checking if we should calculate mesh tangents --- .../model-baker/CalculateMeshTangentsTask.cpp | 53 ++++++++----------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp b/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp index c561a745b5..6e12ec546d 100644 --- a/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp +++ b/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp @@ -13,6 +13,17 @@ #include "ModelMath.h" +bool needTangents(const hfm::Mesh& mesh, const QHash& materials) { + // Check if we actually need to calculate the tangents + for (const auto& meshPart : mesh.parts) { + auto materialIt = materials.find(meshPart.materialID); + if (materialIt != materials.end() && (*materialIt).needTangentSpace()) { + return true; + } + } + return false; +} + void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) { const auto& normalsPerMesh = input.get0(); const std::vector& meshes = input.get1(); @@ -28,38 +39,20 @@ void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, co auto& tangentsOut = tangentsPerMeshOut[tangentsPerMeshOut.size()-1]; // Check if we already have tangents and therefore do not need to do any calculation + // Otherwise confirm if we have the normals needed, and need to calculate the tangents if (!tangentsIn.empty()) { tangentsOut = tangentsIn.toStdVector(); - continue; + } else if (!normals.empty() && needTangents(mesh, materials)) { + tangentsOut.resize(normals.size()); + baker::calculateTangents(mesh, + [&mesh, &normals, &tangentsOut](int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec2* outTexCoords, glm::vec3& outNormal) { + outVertices[0] = mesh.vertices[firstIndex]; + outVertices[1] = mesh.vertices[secondIndex]; + outNormal = normals[firstIndex]; + outTexCoords[0] = mesh.texCoords[firstIndex]; + outTexCoords[1] = mesh.texCoords[secondIndex]; + return &(tangentsOut[firstIndex]); + }); } - - // Check if we have normals, and if not then tangents can't be calculated - if (normals.empty()) { - continue; - } - - // Check if we actually need to calculate the tangents - bool needTangents = false; - for (const auto& meshPart : mesh.parts) { - auto materialIt = materials.find(meshPart.materialID); - if (materialIt != materials.end() && (*materialIt).needTangentSpace()) { - needTangents = true; - break; - } - } - if (!needTangents) { - continue; - } - - tangentsOut.resize(normals.size()); - baker::calculateTangents(mesh, - [&mesh, &normals, &tangentsOut](int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec2* outTexCoords, glm::vec3& outNormal) { - outVertices[0] = mesh.vertices[firstIndex]; - outVertices[1] = mesh.vertices[secondIndex]; - outNormal = normals[firstIndex]; - outTexCoords[0] = mesh.texCoords[firstIndex]; - outTexCoords[1] = mesh.texCoords[secondIndex]; - return &(tangentsOut[firstIndex]); - }); } } From ff746c31413d2aebe4a651dd1681a8cff31f227c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 11 Feb 2019 12:19:33 -0800 Subject: [PATCH 14/35] flag collision group/mask dirty after grab --- libraries/entities/src/EntityItem.cpp | 33 +++++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 1fb1ebb1bc..777f9ba167 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1897,7 +1897,7 @@ glm::vec3 EntityItem::getUnscaledDimensions() const { void EntityItem::setRotation(glm::quat rotation) { if (getLocalOrientation() != rotation) { setLocalOrientation(rotation); - _flags |= Simulation::DIRTY_ROTATION; + markDirtyFlags(Simulation::DIRTY_ROTATION); forEachDescendant([&](SpatiallyNestablePointer object) { if (object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(object); @@ -1927,7 +1927,7 @@ void EntityItem::setVelocity(const glm::vec3& value) { velocity = value; } setLocalVelocity(velocity); - _flags |= Simulation::DIRTY_LINEAR_VELOCITY; + markDirtyFlags(Simulation::DIRTY_LINEAR_VELOCITY); } } } @@ -1982,7 +1982,7 @@ void EntityItem::setAngularVelocity(const glm::vec3& value) { angularVelocity = value; } setLocalAngularVelocity(angularVelocity); - _flags |= Simulation::DIRTY_ANGULAR_VELOCITY; + markDirtyFlags(Simulation::DIRTY_ANGULAR_VELOCITY); } } } @@ -2168,8 +2168,14 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPoin void EntityItem::enableNoBootstrap() { if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { - _flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; - _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + + // NOTE: unlike disableNoBootstrap() below, we do not call simulation->changeEntity() here + // because most enableNoBootstrap() cases are already correctly handled outside this scope + // and I didn't want to add redundant work. + // TODO: cleanup Grabs & dirtySimulationFlags to be more efficient and make more sense. + forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); @@ -2182,13 +2188,21 @@ void EntityItem::enableNoBootstrap() { void EntityItem::disableNoBootstrap() { if (!stillHasGrabActions()) { - _flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; - _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + + EntityTreePointer entityTree = getTree(); + assert(entityTree); + EntitySimulationPointer simulation = entityTree->getSimulation(); + assert(simulation); + simulation->changeEntity(getThisPointer()); + forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + simulation->changeEntity(entity); } }); } @@ -2574,7 +2588,7 @@ QList EntityItem::getActionsOfType(EntityDynamicType typeT void EntityItem::locationChanged(bool tellPhysics) { requiresRecalcBoxes(); if (tellPhysics) { - _flags |= Simulation::DIRTY_TRANSFORM; + markDirtyFlags(Simulation::DIRTY_TRANSFORM); EntityTreePointer tree = getTree(); if (tree) { tree->entityChanged(getThisPointer()); @@ -3445,7 +3459,7 @@ void EntityItem::addGrab(GrabPointer grab) { if (useAction) { EntityTreePointer entityTree = getTree(); assert(entityTree); - EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr; + EntitySimulationPointer simulation = entityTree->getSimulation(); assert(simulation); auto actionFactory = DependencyManager::get(); @@ -3494,7 +3508,6 @@ void EntityItem::removeGrab(GrabPointer grab) { setLocalVelocity(glm::vec3(0.0f)); setAngularVelocity(glm::vec3(0.0f)); } - markDirtyFlags(Simulation::DIRTY_MOTION_TYPE); QUuid actionID = grab->getActionID(); if (!actionID.isNull()) { From fab3e5e3fd32a8b61f7c110f045f10eed9cc20ba Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 11 Feb 2019 16:13:56 -0800 Subject: [PATCH 15/35] remember hash of AvatarEntityItemData --- interface/src/avatar/OtherAvatar.cpp | 5 +++++ interface/src/avatar/OtherAvatar.h | 10 ++++++++++ .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 5 ----- .../avatars-renderer/src/avatars-renderer/Avatar.h | 6 ++---- libraries/avatars/src/AvatarData.h | 2 +- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index a3950c8e96..67eb919b73 100755 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -510,6 +510,11 @@ void OtherAvatar::handleChangedAvatarEntityData() { } } stateItr.value().success = success; + if (success) { + stateItr.value().hash = newHash; + } else { + stateItr.value().hash = 0; + } } AvatarEntityIDs recentlyRemovedAvatarEntities = getAndClearRecentlyRemovedIDs(); diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 3ecd35413f..6461a0107c 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -76,6 +76,16 @@ protected: void onAddAttachedAvatarEntity(const QUuid& id); void onRemoveAttachedAvatarEntity(const QUuid& id); + class AvatarEntityDataHash { + public: + AvatarEntityDataHash(uint32_t h) : hash(h) {}; + uint32_t hash { 0 }; + bool success { false }; + }; + + using MapOfAvatarEntityDataHashes = QMap; + MapOfAvatarEntityDataHashes _avatarEntityDataHashes; + std::vector _attachedAvatarEntities; std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index ba5529e1c0..36ad6c6b82 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -305,11 +305,6 @@ void Avatar::setTargetScale(float targetScale) { } } -void Avatar::setAvatarEntityDataChanged(bool value) { - AvatarData::setAvatarEntityDataChanged(value); - _avatarEntityDataHashes.clear(); -} - void Avatar::removeAvatarEntitiesFromTree() { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index b43fe012b7..39b77f2b65 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -480,8 +480,6 @@ public: virtual void setModelScale(float scale) { _modelScale = scale; } virtual glm::vec3 scaleForChildren() const override { return glm::vec3(getModelScale()); } - virtual void setAvatarEntityDataChanged(bool value) override; - // Show hide the model representation of the avatar virtual void setEnableMeshVisible(bool isEnabled); virtual bool getEnableMeshVisible() const; @@ -647,8 +645,8 @@ protected: bool success { false }; }; - using MapOfAvatarEntityDataHashes = QMap; - MapOfAvatarEntityDataHashes _avatarEntityDataHashes; + //using MapOfAvatarEntityDataHashes = QMap; + //MapOfAvatarEntityDataHashes _avatarEntityDataHashes; uint64_t _lastRenderUpdateTime { 0 }; int _leftPointerGeometryID { 0 }; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index d071b74d6e..63396a59ac 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1147,7 +1147,7 @@ public: */ Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); - virtual void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } + void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } AvatarEntityIDs getAndClearRecentlyRemovedIDs(); /**jsdoc From a7e28f7a6618bc733c666184ff6b316d437ff515 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Mon, 11 Feb 2019 16:48:34 -0800 Subject: [PATCH 16/35] fix spatially nestable parent overwrite --- interface/src/avatar/OtherAvatar.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index a3950c8e96..8ad9c6b121 100755 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -489,6 +489,9 @@ void OtherAvatar::handleChangedAvatarEntityData() { bool success = true; if (entity) { QUuid oldParentID = entity->getParentID(); + const QUuid NULL_ID = QUuid("{00000000-0000-0000-0000-000000000005}"); + entity->setParentID(NULL_ID); + entity->setParentID(oldParentID); if (entityTree->updateEntity(entityID, properties)) { entity->updateLastEditedFromRemote(); } else { From 3ea6241cc91e9eafe224ebcaf1d313d3624dad2a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 12 Feb 2019 12:50:47 -0800 Subject: [PATCH 17/35] send update for AvatarEntity on deleteGrab --- interface/src/avatar/MyAvatar.cpp | 12 ++++++++++++ interface/src/avatar/MyAvatar.h | 1 + .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 7 +++++++ .../avatars-renderer/src/avatars-renderer/Avatar.h | 5 ++--- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 92d9270d20..1c7f2ae400 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -5312,3 +5312,15 @@ void MyAvatar::releaseGrab(const QUuid& grabID) { } } +void MyAvatar::sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const { + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (entityTree) { + entityTree->withWriteLock([&] { + // force an update packet + EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entityID, properties); + }); + } +} + diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0d27988543..c279673486 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1882,6 +1882,7 @@ private: bool didTeleport(); bool getIsAway() const { return _isAway; } void setAway(bool value); + void sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const override; std::mutex _pinnedJointsMutex; std::vector _pinnedJoints; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 36ad6c6b82..c17c7d9fc5 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -363,6 +363,13 @@ bool Avatar::applyGrabChanges() { target->removeGrab(grab); _avatarGrabs.erase(itr); grabAddedOrRemoved = true; + if (isMyAvatar()) { + const EntityItemPointer& entity = std::dynamic_pointer_cast(target); + if (entity && entity->getEntityHostType() == entity::HostType::AVATAR && entity->getSimulationOwner().getID() == getID()) { + EntityItemProperties properties = entity->getProperties(); + sendPacket(entity->getID(), properties); + } + } } else { undeleted.push_back(id); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 39b77f2b65..06942a13d8 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -601,6 +602,7 @@ protected: // protected methods... bool isLookingAtMe(AvatarSharedPointer avatar) const; + virtual void sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const { } bool applyGrabChanges(); void relayJointDataToChildren(); @@ -645,9 +647,6 @@ protected: bool success { false }; }; - //using MapOfAvatarEntityDataHashes = QMap; - //MapOfAvatarEntityDataHashes _avatarEntityDataHashes; - uint64_t _lastRenderUpdateTime { 0 }; int _leftPointerGeometryID { 0 }; int _rightPointerGeometryID { 0 }; From 9f9f512c59f9132bcc3799d573536f85a3df1a5e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 12 Feb 2019 13:22:22 -0800 Subject: [PATCH 18/35] undoing markDirty() overhead because _flags is atomic --- libraries/entities/src/EntityItem.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 777f9ba167..5c423b2fe5 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1897,7 +1897,7 @@ glm::vec3 EntityItem::getUnscaledDimensions() const { void EntityItem::setRotation(glm::quat rotation) { if (getLocalOrientation() != rotation) { setLocalOrientation(rotation); - markDirtyFlags(Simulation::DIRTY_ROTATION); + _flags |= Simulation::DIRTY_ROTATION; forEachDescendant([&](SpatiallyNestablePointer object) { if (object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(object); @@ -1927,7 +1927,7 @@ void EntityItem::setVelocity(const glm::vec3& value) { velocity = value; } setLocalVelocity(velocity); - markDirtyFlags(Simulation::DIRTY_LINEAR_VELOCITY); + _flags |= Simulation::DIRTY_LINEAR_VELOCITY; } } } @@ -1982,7 +1982,7 @@ void EntityItem::setAngularVelocity(const glm::vec3& value) { angularVelocity = value; } setLocalAngularVelocity(angularVelocity); - markDirtyFlags(Simulation::DIRTY_ANGULAR_VELOCITY); + _flags |= Simulation::DIRTY_ANGULAR_VELOCITY; } } } @@ -2168,8 +2168,8 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPoin void EntityItem::enableNoBootstrap() { if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { - markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + _flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar // NOTE: unlike disableNoBootstrap() below, we do not call simulation->changeEntity() here // because most enableNoBootstrap() cases are already correctly handled outside this scope @@ -2188,8 +2188,8 @@ void EntityItem::enableNoBootstrap() { void EntityItem::disableNoBootstrap() { if (!stillHasGrabActions()) { - markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + _flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar EntityTreePointer entityTree = getTree(); assert(entityTree); @@ -2588,7 +2588,7 @@ QList EntityItem::getActionsOfType(EntityDynamicType typeT void EntityItem::locationChanged(bool tellPhysics) { requiresRecalcBoxes(); if (tellPhysics) { - markDirtyFlags(Simulation::DIRTY_TRANSFORM); + _flags |= Simulation::DIRTY_TRANSFORM; EntityTreePointer tree = getTree(); if (tree) { tree->entityChanged(getThisPointer()); From 8033e93eda84b482f9806d39dbb3d2e09e5fcaab Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 19 Feb 2019 17:38:37 -0800 Subject: [PATCH 19/35] Add avatar-priority boolean to zone entities RC-79 version --- libraries/entities/src/EntityItemProperties.cpp | 14 ++++++++++++++ libraries/entities/src/EntityItemProperties.h | 3 +++ libraries/entities/src/EntityPropertyFlags.h | 3 +++ libraries/entities/src/ZoneEntityItem.cpp | 5 +++++ libraries/entities/src/ZoneEntityItem.h | 7 +++++++ libraries/networking/src/udt/PacketHeaders.h | 1 + scripts/system/assets/data/createAppTooltips.json | 3 +++ scripts/system/edit.js | 3 ++- scripts/system/html/js/entityProperties.js | 6 ++++++ 9 files changed, 44 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7cafaece7a..103f5dbab7 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -616,6 +616,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed); CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed); CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL); + CHECK_PROPERTY_CHANGE(PROP_AVATAR_PRIORITY, avatarPriority); CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode); CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); @@ -1420,7 +1421,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the * zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to * certain properties.
+ * + * @property {boolean} avatarPriority=false - If true avatars within this zone will have their movements distributed to other + * clients with priority over other avatars. Use, for example, on a performance stage with a few presenters. *
+ *
  * function filter(properties) {
  *     // Test and edit properties object values,
  *     // e.g., properties.modelURL, as required.
@@ -1748,6 +1753,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
         COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed);
         COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GHOSTING_ALLOWED, ghostingAllowed);
         COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FILTER_URL, filterURL);
+        COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AVATAR_PRIORITY, avatarPriority);
 
         COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString());
         COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString());
@@ -2108,6 +2114,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
     COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL);
+    COPY_PROPERTY_FROM_QSCRIPTVALUE(avatarPriority, bool, setAvatarPriority);
     COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode);
     COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode);
     COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode);
@@ -2386,6 +2393,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
     COPY_PROPERTY_IF_CHANGED(flyingAllowed);
     COPY_PROPERTY_IF_CHANGED(ghostingAllowed);
     COPY_PROPERTY_IF_CHANGED(filterURL);
+    COPY_PROPERTY_IF_CHANGED(avatarPriority);
     COPY_PROPERTY_IF_CHANGED(keyLightMode);
     COPY_PROPERTY_IF_CHANGED(ambientLightMode);
     COPY_PROPERTY_IF_CHANGED(skyboxMode);
@@ -2770,6 +2778,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
         ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool);
         ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool);
         ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString);
+        ADD_PROPERTY_TO_MAP(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, bool);
         ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t);
         ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t);
         ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t);
@@ -3169,6 +3178,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
                 APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, properties.getFlyingAllowed());
                 APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, properties.getGhostingAllowed());
                 APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, properties.getFilterURL());
+                APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, properties.getAvatarPriority());
 
                 APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode());
                 APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode());
@@ -3631,6 +3641,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed);
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed);
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FILTER_URL, QString, setFilterURL);
+        READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AVATAR_PRIORITY, bool, setAvatarPriority);
 
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode);
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode);
@@ -4596,6 +4607,9 @@ QList EntityItemProperties::listChangedProperties() {
     if (filterURLChanged()) {
         out += "filterURL";
     }
+    if (avatarPriorityChanged()) {
+        out += "avatarPriority";
+    }
     if (keyLightModeChanged()) {
         out += "keyLightMode";
     }
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index dcba60b004..ff5204efe2 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -315,6 +315,7 @@ public:
     DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED);
     DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED);
     DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL);
+    DEFINE_PROPERTY(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, bool, ZoneEntityItem::DEFAULT_AVATAR_PRIORITY);
     DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
     DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
     DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
@@ -679,6 +680,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, GhostingAllowed, ghostingAllowed, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, FilterURL, filterURL, "");
 
+    DEBUG_PROPERTY_IF_CHANGED(debug, properties, AvatarPriority, avatarPriority, "");
+
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityHostTypeAsString, entityHostType, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, "");
 
diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h
index b11ecff5bb..79303e3d61 100644
--- a/libraries/entities/src/EntityPropertyFlags.h
+++ b/libraries/entities/src/EntityPropertyFlags.h
@@ -155,6 +155,7 @@ enum EntityPropertyList {
     PROP_DERIVED_28,
     PROP_DERIVED_29,
     PROP_DERIVED_30,
+    PROP_DERIVED_31,
 
     PROP_AFTER_LAST_ITEM,
 
@@ -275,6 +276,8 @@ enum EntityPropertyList {
     PROP_SKYBOX_MODE = PROP_DERIVED_28,
     PROP_HAZE_MODE = PROP_DERIVED_29,
     PROP_BLOOM_MODE = PROP_DERIVED_30,
+    // Avatar priority
+    PROP_AVATAR_PRIORITY = PROP_DERIVED_31,
 
     // Polyvox
     PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0,
diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp
index 7f7f6170d4..13c7273d94 100644
--- a/libraries/entities/src/ZoneEntityItem.cpp
+++ b/libraries/entities/src/ZoneEntityItem.cpp
@@ -65,6 +65,7 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(filterURL, getFilterURL);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(avatarPriority, getAvatarPriority);
 
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode);
@@ -111,6 +112,7 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority);
 
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode);
@@ -186,6 +188,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
     READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed);
     READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed);
     READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL);
+    READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, bool, setAvatarPriority);
 
     READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode);
     READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode);
@@ -211,6 +214,7 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p
     requestedProperties += PROP_FLYING_ALLOWED;
     requestedProperties += PROP_GHOSTING_ALLOWED;
     requestedProperties += PROP_FILTER_URL;
+    requestedProperties += PROP_AVATAR_PRIORITY;
 
     requestedProperties += PROP_KEY_LIGHT_MODE;
     requestedProperties += PROP_AMBIENT_LIGHT_MODE;
@@ -250,6 +254,7 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
     APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed());
     APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed());
     APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL());
+    APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority());
 
     APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode());
     APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode());
diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h
index 813115add9..20bab7c710 100644
--- a/libraries/entities/src/ZoneEntityItem.h
+++ b/libraries/entities/src/ZoneEntityItem.h
@@ -96,6 +96,9 @@ public:
     QString getFilterURL() const;
     void setFilterURL(const QString url); 
 
+    bool getAvatarPriority() const { return _avatarPriority; }
+    void setAvatarPriority(bool value) { _avatarPriority = value; }
+
     bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; }
     bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; }
     bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; }
@@ -125,6 +128,7 @@ public:
     static const bool DEFAULT_FLYING_ALLOWED;
     static const bool DEFAULT_GHOSTING_ALLOWED;
     static const QString DEFAULT_FILTER_URL;
+    static const bool DEFAULT_AVATAR_PRIORITY = false;
 
 protected:
     KeyLightPropertyGroup _keyLightProperties;
@@ -149,6 +153,9 @@ protected:
     bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED };
     QString _filterURL { DEFAULT_FILTER_URL };
 
+    // Avatar-updates priority
+    bool _avatarPriority { DEFAULT_AVATAR_PRIORITY };
+
     // Dirty flags turn true when either keylight properties is changing values.
     bool _keyLightPropertiesChanged { false };
     bool _ambientLightPropertiesChanged { false };
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index d898c03597..c0983f24db 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -260,6 +260,7 @@ enum class EntityVersion : PacketVersion {
     MissingWebEntityProperties,
     PulseProperties,
     RingGizmoEntities,
+    AvatarPriorityZone,
 
     // Add new versions above here
     NUM_PACKET_TYPE,
diff --git a/scripts/system/assets/data/createAppTooltips.json b/scripts/system/assets/data/createAppTooltips.json
index 4c78da7306..7e5f2c8659 100644
--- a/scripts/system/assets/data/createAppTooltips.json
+++ b/scripts/system/assets/data/createAppTooltips.json
@@ -134,6 +134,9 @@
     "bloom.bloomSize": {
         "tooltip": "The radius of bloom. The higher the value, the larger the bloom."
     },
+    "avatarPriority": {
+        "tooltip":  "Avatars in this zone will have a higher update priority."
+    },
     "modelURL": {
         "tooltip": "A mesh model from an FBX or OBJ file."
     },
diff --git a/scripts/system/edit.js b/scripts/system/edit.js
index 9d807264aa..67a789c266 100644
--- a/scripts/system/edit.js
+++ b/scripts/system/edit.js
@@ -382,7 +382,8 @@ const DEFAULT_ENTITY_PROPERTIES = {
             },
         },
         shapeType: "box",
-        bloomMode: "inherit"
+        bloomMode: "inherit",
+        avatarPriority: false
     },
     Model: {
         collisionShape: "none",
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js
index ee95312fa4..404ded6ae2 100644
--- a/scripts/system/html/js/entityProperties.js
+++ b/scripts/system/html/js/entityProperties.js
@@ -428,6 +428,12 @@ const GROUPS = [
                 propertyID: "bloom.bloomSize",
                 showPropertyRule: { "bloomMode": "enabled" },
             },
+            {
+                label: "Avatar Priority",
+                type: "bool",
+                propertyID: "avatarPriority",
+            },
+
         ]
     },
     {

From 16bc667b95860b89f20e25494582cbf18fe9bdc0 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Fri, 22 Feb 2019 18:17:30 -0800
Subject: [PATCH 20/35] Looks like serialization order must be same a flag enum
 order!?

Also resuscitate EntityItem debug dumps.
---
 assignment-client/src/avatars/AvatarMixer.cpp | 62 ++++++++++++++++++-
 assignment-client/src/avatars/AvatarMixer.h   |  8 +++
 .../src/avatars/AvatarMixerClientData.cpp     | 54 +++++++++++++++-
 .../src/avatars/AvatarMixerClientData.h       |  2 +-
 .../src/avatars/AvatarMixerSlave.h            |  4 ++
 assignment-client/src/octree/OctreeServer.cpp |  3 +-
 libraries/entities/src/EntityItem.cpp         | 13 ++--
 libraries/entities/src/EntityItem.h           |  2 +-
 .../entities/src/EntityItemProperties.cpp     |  2 +-
 libraries/entities/src/ZoneEntityItem.cpp     | 20 +++++-
 libraries/entities/src/ZoneEntityItem.h       |  2 +
 11 files changed, 156 insertions(+), 16 deletions(-)

diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index 500772c1b5..f352e02afa 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -23,6 +23,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -32,6 +33,8 @@
 #include 
 #include 
 #include 
+#include "../AssignmentDynamicFactory.h"
+#include "../entities/AssignmentParentFinder.h"
 
 const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
 
@@ -42,6 +45,9 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
     ThreadedAssignment(message),
     _slavePool(&_slaveSharedData)
 {
+    DependencyManager::registerInheritance();
+    DependencyManager::set();
+
     // make sure we hear about node kills so we can tell the other nodes
     connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled);
 
@@ -56,6 +62,8 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
     packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
     packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket");
     packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket");
+    packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
+        this, "handleOctreePacket");
 
     packetReceiver.registerListenerForTypes({
         PacketType::ReplicatedAvatarIdentity,
@@ -227,6 +235,10 @@ void AvatarMixer::start() {
 
         int lockWait, nodeTransform, functor;
 
+        {
+            _entityViewer.queryOctree();
+        }
+
         // Allow nodes to process any pending/queued packets across our worker threads
         {
             auto start = usecTimestampNow();
@@ -847,13 +859,15 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node
 void AvatarMixer::domainSettingsRequestComplete() {
     auto nodeList = DependencyManager::get();
     nodeList->addSetOfNodeTypesToNodeInterestSet({
-        NodeType::Agent, NodeType::EntityScriptServer,
+        NodeType::Agent, NodeType::EntityScriptServer, NodeType::EntityServer,
         NodeType::UpstreamAvatarMixer, NodeType::DownstreamAvatarMixer
     });
 
     // parse the settings to pull out the values we need
     parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject());
 
+    setupEntityQuery();
+
     // start our tight loop...
     start();
 }
@@ -941,3 +955,49 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
         qCDebug(avatars) << "Avatars other than" << _slaveSharedData.skeletonURLWhitelist << "will be replaced by" << (_slaveSharedData.skeletonReplacementURL.isEmpty() ? "default" : _slaveSharedData.skeletonReplacementURL.toString());
     }
 }
+
+void AvatarMixer::setupEntityQuery() {
+    static char queryJsonString[] = R"({"avatarPriority": true})";
+
+    _entityViewer.init();
+    DependencyManager::registerInheritance();
+    DependencyManager::set(_entityViewer.getTree());
+    _slaveSharedData.entityTree = _entityViewer.getTree();
+
+    QJsonParseError jsonParseError;
+    const QJsonDocument priorityZoneQuery(QJsonDocument::fromJson(queryJsonString, &jsonParseError));
+    if (jsonParseError.error != QJsonParseError::NoError) {
+        qCDebug(avatars) << "Error parsing:" << queryJsonString << " - " << jsonParseError.errorString();
+        return;
+    }
+    _entityViewer.getOctreeQuery().setJSONParameters(priorityZoneQuery.object());
+
+}
+
+void AvatarMixer::handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode) {
+    PacketType packetType = message->getType();
+
+    switch (packetType) {
+    case PacketType::OctreeStats:
+        break;
+
+    case PacketType::EntityData:
+        _entityViewer.processDatagram(*message, senderNode);
+        break;
+
+    case PacketType::EntityErase:
+        _entityViewer.processEraseMessage(*message, senderNode);
+        break;
+
+    default:
+        qCDebug(avatars) << "Unexpected packet type:" << packetType;
+        break;
+    }
+}
+
+void AvatarMixer::aboutToFinish() {
+    DependencyManager::destroy();
+    DependencyManager::destroy();
+
+    ThreadedAssignment::aboutToFinish();
+}
diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h
index 764656a2d5..6d82172995 100644
--- a/assignment-client/src/avatars/AvatarMixer.h
+++ b/assignment-client/src/avatars/AvatarMixer.h
@@ -19,6 +19,7 @@
 #include 
 
 #include 
+#include "../entities/EntityTreeHeadlessViewer.h"
 #include "AvatarMixerClientData.h"
 
 #include "AvatarMixerSlavePool.h"
@@ -28,6 +29,7 @@ class AvatarMixer : public ThreadedAssignment {
     Q_OBJECT
 public:
     AvatarMixer(ReceivedMessage& message);
+    virtual void aboutToFinish() override;
 
     static bool shouldReplicateTo(const Node& from, const Node& to) {
         return to.getType() == NodeType::DownstreamAvatarMixer &&
@@ -56,6 +58,7 @@ private slots:
     void handleReplicatedBulkAvatarPacket(QSharedPointer message);
     void domainSettingsRequestComplete();
     void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
+    void handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode);
     void start();
 
 private:
@@ -70,8 +73,13 @@ private:
 
     void optionallyReplicatePacket(ReceivedMessage& message, const Node& node);
 
+    void setupEntityQuery();
+
     p_high_resolution_clock::time_point _lastFrameTimestamp;
 
+    // Attach to entity tree for avatar-priority zone info.
+    EntityTreeHeadlessViewer _entityViewer;
+
     // FIXME - new throttling - use these values somehow
     float _trailingMixRatio { 0.0f };
     float _throttlingRatio { 0.0f };
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp
index b7d2f5cdf8..9edbae12d8 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.cpp
+++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp
@@ -16,6 +16,8 @@
 
 #include 
 #include 
+#include 
+#include 
 
 #include "AvatarMixerSlave.h"
 
@@ -62,7 +64,7 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
 
         switch (packet->getType()) {
             case PacketType::AvatarData:
-                parseData(*packet);
+                parseData(*packet, slaveSharedData);
                 break;
             case PacketType::SetAvatarTraits:
                 processSetTraitsMessage(*packet, slaveSharedData, *node);
@@ -80,7 +82,38 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
     return packetsProcessed;
 }
 
-int AvatarMixerClientData::parseData(ReceivedMessage& message) {
+namespace {
+    using std::static_pointer_cast;
+
+    // Operator to find if a point is within an avatar-priority (hero) Zone Entity.
+    struct FindPriorityZone {
+        glm::vec3 position;
+        bool isInPriorityZone { false };
+        static bool operation(const OctreeElementPointer& element, void* extraData) {
+            auto findPriorityZone = static_cast(extraData);
+            if (element->getAACube().contains(findPriorityZone->position)) {
+                const EntityTreeElementPointer entityTreeElement = static_pointer_cast(element);
+                entityTreeElement->forEachEntity([&findPriorityZone](EntityItemPointer item) {
+                    if (item->getType() == EntityTypes::Zone 
+                        && item->contains(findPriorityZone->position)
+                        && static_pointer_cast(item)->getAvatarPriority()) {
+                            findPriorityZone->isInPriorityZone = true;
+                    }
+                });
+                if (findPriorityZone->isInPriorityZone) {
+                    return false;  // For now, stop at first hit.
+                } else {
+                    return true;
+                }
+            } else {  // Position isn't within this subspace, so end recursion.
+                return false;
+            }
+        }
+    };
+
+}  // Close anonymous namespace.
+
+int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveSharedData& slaveSharedData) {
     // pull the sequence number from the data first
     uint16_t sequenceNumber;
 
@@ -90,9 +123,24 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) {
         incrementNumOutOfOrderSends();
     }
     _lastReceivedSequenceNumber = sequenceNumber;
+    glm::vec3 oldPosition = getPosition();
 
     // compute the offset to the data payload
-    return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()));
+    if (!_avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()))) {
+        return false;
+    }
+
+    auto newPosition = getPosition();
+    if (newPosition != oldPosition) {
+        EntityTree& entityTree = *slaveSharedData.entityTree;
+        FindPriorityZone findPriorityZone { newPosition, false } ;
+        entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone);
+        if (findPriorityZone.isInPriorityZone) {
+            // Tag avatar as hero ...
+        }
+    }
+
+    return true;
 }
 
 void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h
index 843f19cf22..45d508088c 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.h
+++ b/assignment-client/src/avatars/AvatarMixerClientData.h
@@ -45,7 +45,7 @@ public:
     using HRCTime = p_high_resolution_clock::time_point;
     using PerNodeTraitVersions = std::unordered_map;
 
-    int parseData(ReceivedMessage& message) override;
+    int parseData(ReceivedMessage& message, const SlaveSharedData& SlaveSharedData);
     AvatarData& getAvatar() { return *_avatar; }
     const AvatarData& getAvatar() const { return *_avatar; }
     const AvatarData* getConstAvatarData() const { return _avatar.get(); }
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h
index 91bb02fd55..28d99748fa 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.h
+++ b/assignment-client/src/avatars/AvatarMixerSlave.h
@@ -90,9 +90,13 @@ public:
     }
 };
 
+class EntityTree;
+using EntityTreePointer = std::shared_ptr;
+
 struct SlaveSharedData {
     QStringList skeletonURLWhitelist;
     QUrl skeletonReplacementURL;
+    EntityTreePointer entityTree;
 };
 
 class AvatarMixerSlave {
diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index e993bea358..477d3dd612 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -1203,7 +1203,8 @@ void OctreeServer::beginRunning() {
     auto nodeList = DependencyManager::get();
 
     // we need to ask the DS about agents so we can ping/reply with them
-    nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer });
+    nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer,
+        NodeType::AvatarMixer });
 
     beforeRun(); // after payload has been processed
 
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 5c423b2fe5..25e5893078 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -41,6 +41,7 @@
 #include "EntitySimulation.h"
 #include "EntityDynamicFactoryInterface.h"
 
+//#define WANT_DEBUG
 
 Q_DECLARE_METATYPE(EntityItemPointer);
 int entityItemPointernMetaTypeId = qRegisterMetaType();
@@ -499,6 +500,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
     }
 
     #ifdef WANT_DEBUG
+    {
         quint64 lastEdited = getLastEdited();
         float editedAgo = getEditedAgo();
         QString agoAsString = formatSecondsElapsed(editedAgo);
@@ -512,6 +514,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
         qCDebug(entities) << "    age=" << getAge() << "seconds - " << ageAsString;
         qCDebug(entities) << "    lastEdited =" << lastEdited;
         qCDebug(entities) << "    ago=" << editedAgo << "seconds - " << agoAsString;
+    }
     #endif
 
     quint64 lastEditedFromBuffer = 0;
@@ -1096,7 +1099,7 @@ void EntityItem::simulate(const quint64& now) {
         qCDebug(entities) << "    hasGravity=" << hasGravity();
         qCDebug(entities) << "    hasAcceleration=" << hasAcceleration();
         qCDebug(entities) << "    hasAngularVelocity=" << hasAngularVelocity();
-        qCDebug(entities) << "    getAngularVelocity=" << getAngularVelocity();
+        qCDebug(entities) << "    getAngularVelocity=" << getLocalAngularVelocity();
         qCDebug(entities) << "    isMortal=" << isMortal();
         qCDebug(entities) << "    getAge()=" << getAge();
         qCDebug(entities) << "    getLifetime()=" << getLifetime();
@@ -1108,12 +1111,12 @@ void EntityItem::simulate(const quint64& now) {
             qCDebug(entities) << "        hasGravity=" << hasGravity();
             qCDebug(entities) << "        hasAcceleration=" << hasAcceleration();
             qCDebug(entities) << "        hasAngularVelocity=" << hasAngularVelocity();
-            qCDebug(entities) << "        getAngularVelocity=" << getAngularVelocity();
+            qCDebug(entities) << "        getAngularVelocity=" << getLocalAngularVelocity();
         }
         if (hasAngularVelocity()) {
             qCDebug(entities) << "    CHANGING...=";
             qCDebug(entities) << "        hasAngularVelocity=" << hasAngularVelocity();
-            qCDebug(entities) << "        getAngularVelocity=" << getAngularVelocity();
+            qCDebug(entities) << "        getAngularVelocity=" << getLocalAngularVelocity();
         }
         if (isMortal()) {
             qCDebug(entities) << "    MORTAL...=";
@@ -2649,13 +2652,13 @@ bool EntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
     // ALL entity properties. Some work will need to be done to the property system so that it can be more flexible
     // (to grab the value and default value of a property given the string representation of that property, for example)
 
-    // currently the only property filter we handle is '+' for serverScripts
+    // currently the only property filter we handle in EntityItem is '+' for serverScripts
     // which means that we only handle a filtered query asking for entities where the serverScripts property is non-default
 
     static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
 
     foreach(const auto& property, jsonFilters.keys()) {
-        if (property == SERVER_SCRIPTS_PROPERTY  && jsonFilters[property] == EntityQueryFilterSymbol::NonDefault) {
+        if (property == SERVER_SCRIPTS_PROPERTY && jsonFilters[property] == EntityQueryFilterSymbol::NonDefault) {
             // check if this entity has a non-default value for serverScripts
             if (_serverScripts != ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS) {
                 return true;
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index ec7ad78313..87dca3f314 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -516,7 +516,7 @@ public:
     QUuid getLastEditedBy() const { return _lastEditedBy; }
     void setLastEditedBy(QUuid value) { _lastEditedBy = value; }
 
-    bool matchesJSONFilters(const QJsonObject& jsonFilters) const;
+    virtual bool matchesJSONFilters(const QJsonObject& jsonFilters) const;
 
     virtual bool getMeshes(MeshProxyList& result) { return true; }
 
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 103f5dbab7..96160ebd93 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -3178,13 +3178,13 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
                 APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, properties.getFlyingAllowed());
                 APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, properties.getGhostingAllowed());
                 APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, properties.getFilterURL());
-                APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, properties.getAvatarPriority());
 
                 APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode());
                 APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode());
                 APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode());
                 APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode());
                 APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode());
+                APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, properties.getAvatarPriority());
             }
 
             if (properties.getType() == EntityTypes::PolyVox) {
diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp
index 13c7273d94..83619caa3c 100644
--- a/libraries/entities/src/ZoneEntityItem.cpp
+++ b/libraries/entities/src/ZoneEntityItem.cpp
@@ -112,13 +112,13 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL);
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority);
 
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority);
 
     somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged ||
         _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged;
@@ -188,13 +188,13 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
     READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed);
     READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed);
     READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL);
-    READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, bool, setAvatarPriority);
 
     READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode);
     READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode);
     READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode);
     READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode);
     READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode);
+    READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, bool, setAvatarPriority);
 
     return bytesRead;
 }
@@ -254,13 +254,13 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
     APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed());
     APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed());
     APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL());
-    APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority());
 
     APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode());
     APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode());
     APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode());
     APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode());
     APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode());
+    APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority());
 }
 
 void ZoneEntityItem::debugDump() const {
@@ -274,6 +274,7 @@ void ZoneEntityItem::debugDump() const {
     qCDebug(entities) << "   _ambientLightMode:" << EntityItemProperties::getComponentModeAsString(_ambientLightMode);
     qCDebug(entities) << "         _skyboxMode:" << EntityItemProperties::getComponentModeAsString(_skyboxMode);
     qCDebug(entities) << "          _bloomMode:" << EntityItemProperties::getComponentModeAsString(_bloomMode);
+    qCDebug(entities) << "     _avatarPriority:" << getAvatarPriority();
 
     _keyLightProperties.debugDump();
     _ambientLightProperties.debugDump();
@@ -469,3 +470,16 @@ void ZoneEntityItem::fetchCollisionGeometryResource() {
         _shapeResource = DependencyManager::get()->getCollisionGeometryResource(hullURL);
     }
 }
+
+bool ZoneEntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
+    // currently the only property filter we handle in ZoneEntityItem is value of avatarPriority
+
+    static const QString AVATAR_PRIORITY_PROPERTY = "avatarPriority";
+
+    if (jsonFilters.contains(AVATAR_PRIORITY_PROPERTY)) {
+        return (jsonFilters[AVATAR_PRIORITY_PROPERTY].toBool() == _avatarPriority);
+    }
+
+    // Chain to base:
+    return EntityItem::matchesJSONFilters(jsonFilters);
+}
diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h
index 20bab7c710..a3e668b6f6 100644
--- a/libraries/entities/src/ZoneEntityItem.h
+++ b/libraries/entities/src/ZoneEntityItem.h
@@ -66,6 +66,8 @@ public:
     QString getCompoundShapeURL() const;
     virtual void setCompoundShapeURL(const QString& url);
 
+    virtual bool matchesJSONFilters(const QJsonObject& jsonFilters) const override;
+
     KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock([&] { return _keyLightProperties; }); }
     AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock([&] { return _ambientLightProperties; }); }
 

From d3b3dfd76d14b82f11596f677b0be3e072399ac5 Mon Sep 17 00:00:00 2001
From: Clement 
Date: Fri, 22 Feb 2019 18:05:42 -0800
Subject: [PATCH 21/35] Don't send a KillAvatar packet on kick

    The DS already takes cares of removing nodes no longer allowed when
the permissions are updated and KillAvatar was never meant to be used
from the DS.
---
 domain-server/src/DomainServerSettingsManager.cpp | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp
index 780fad15f2..4e833f6b77 100644
--- a/domain-server/src/DomainServerSettingsManager.cpp
+++ b/domain-server/src/DomainServerSettingsManager.cpp
@@ -25,13 +25,13 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
-#include  //for KillAvatarReason
 #include 
 
 #include "DomainServerNodeData.h"
@@ -870,14 +870,6 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointerwrite(nodeUUID.toRfc4122());
-                packet->writePrimitive(KillAvatarReason::NoReason);
-
-                // send to avatar mixer, it sends the kill to everyone else
-                limitedNodeList->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer);
-
                 if (newPermissions) {
                     qDebug() << "Removing connect permission for node" << uuidStringWithoutCurlyBraces(matchingNode->getUUID())
                         << "after kick request from" << uuidStringWithoutCurlyBraces(sendingNode->getUUID());

From 50035a198366ad46ffc61fd42d2847302f501b6b Mon Sep 17 00:00:00 2001
From: Clement 
Date: Fri, 22 Feb 2019 18:07:26 -0800
Subject: [PATCH 22/35] Delete old nodes with identical socket

---
 libraries/networking/src/LimitedNodeList.cpp | 176 +++++++++----------
 1 file changed, 84 insertions(+), 92 deletions(-)

diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 8b9e37569c..eaa02f059e 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -645,103 +645,95 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
                                                    const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
                                                    Node::LocalID localID, bool isReplicated, bool isUpstream,
                                                    const QUuid& connectionSecret, const NodePermissions& permissions) {
-    QReadLocker readLocker(&_nodeMutex);
-    NodeHash::const_iterator it = _nodeHash.find(uuid);
+    {
+        QReadLocker readLocker(&_nodeMutex);
+        NodeHash::const_iterator it = _nodeHash.find(uuid);
 
-    if (it != _nodeHash.end()) {
-        SharedNodePointer& matchingNode = it->second;
+        if (it != _nodeHash.end()) {
+            SharedNodePointer& matchingNode = it->second;
 
-        matchingNode->setPublicSocket(publicSocket);
-        matchingNode->setLocalSocket(localSocket);
-        matchingNode->setPermissions(permissions);
-        matchingNode->setConnectionSecret(connectionSecret);
-        matchingNode->setIsReplicated(isReplicated);
-        matchingNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
-        matchingNode->setLocalID(localID);
+            matchingNode->setPublicSocket(publicSocket);
+            matchingNode->setLocalSocket(localSocket);
+            matchingNode->setPermissions(permissions);
+            matchingNode->setConnectionSecret(connectionSecret);
+            matchingNode->setIsReplicated(isReplicated);
+            matchingNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
+            matchingNode->setLocalID(localID);
 
-        return matchingNode;
-    } else {
-        auto it = _connectionIDs.find(uuid);
-        if (it == _connectionIDs.end()) {
-            _connectionIDs[uuid] = INITIAL_CONNECTION_ID;
+            return matchingNode;
         }
-
-        // we didn't have this node, so add them
-        Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket);
-        newNode->setIsReplicated(isReplicated);
-        newNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
-        newNode->setConnectionSecret(connectionSecret);
-        newNode->setPermissions(permissions);
-        newNode->setLocalID(localID);
-
-        // move the newly constructed node to the LNL thread
-        newNode->moveToThread(thread());
-
-        if (nodeType == NodeType::AudioMixer) {
-            LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer);
-        }
-
-        SharedNodePointer newNodePointer(newNode, &QObject::deleteLater);
-
-        // if this is a solo node type, we assume that the DS has replaced its assignment and we should kill the previous node
-        if (SOLO_NODE_TYPES.count(newNode->getType())) {
-            // while we still have the read lock, see if there is a previous solo node we'll need to remove
-            auto previousSoloIt = std::find_if(_nodeHash.cbegin(), _nodeHash.cend(), [newNode](const UUIDNodePair& nodePair){
-                return nodePair.second->getType() == newNode->getType();
-            });
-
-            if (previousSoloIt != _nodeHash.cend()) {
-                // we have a previous solo node, switch to a write lock so we can remove it
-                readLocker.unlock();
-
-                QWriteLocker writeLocker(&_nodeMutex);
-
-                auto oldSoloNode = previousSoloIt->second;
-
-                _localIDMap.unsafe_erase(oldSoloNode->getLocalID());
-                _nodeHash.unsafe_erase(previousSoloIt);
-                handleNodeKill(oldSoloNode);
-
-                // convert the current lock back to a read lock for insertion of new node
-                writeLocker.unlock();
-                readLocker.relock();
-            }
-        }
-
-        // insert the new node and release our read lock
-#if defined(Q_OS_ANDROID) || (defined(__clang__) && defined(Q_OS_LINUX))
-        _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer));
-        _localIDMap.insert(std::pair(localID, newNodePointer));
-#else
-        _nodeHash.emplace(newNode->getUUID(), newNodePointer);
-        _localIDMap.emplace(localID, newNodePointer);
-#endif
-        readLocker.unlock();
-
-        qCDebug(networking) << "Added" << *newNode;
-
-        auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambdas to hold a strong ref
-
-        emit nodeAdded(newNodePointer);
-        if (newNodePointer->getActiveSocket()) {
-            emit nodeActivated(newNodePointer);
-        } else {
-            connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [this, weakPtr] {
-                auto sharedPtr = weakPtr.lock();
-                if (sharedPtr) {
-                    emit nodeActivated(sharedPtr);
-                    disconnect(sharedPtr.data(), &NetworkPeer::socketActivated, this, 0);
-                }
-            });
-        }
-
-        // Signal when a socket changes, so we can start the hole punch over.
-        connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [this, weakPtr] {
-            emit nodeSocketUpdated(weakPtr);
-        });
-
-        return newNodePointer;
     }
+
+    auto removeOldNode = [&](auto node) {
+        if (node) {
+            QWriteLocker writeLocker(&_nodeMutex);
+            _localIDMap.unsafe_erase(node->getLocalID());
+            _nodeHash.unsafe_erase(node->getUUID());
+            handleNodeKill(node);
+        }
+    };
+
+    // if this is a solo node type, we assume that the DS has replaced its assignment and we should kill the previous node
+    if (SOLO_NODE_TYPES.count(nodeType)) {
+        removeOldNode(soloNodeOfType(nodeType));
+    }
+    // If there is a new node with the same socket, this is a reconnection, kill the old node
+    removeOldNode(findNodeWithAddr(publicSocket));
+    removeOldNode(findNodeWithAddr(localSocket));
+
+    auto it = _connectionIDs.find(uuid);
+    if (it == _connectionIDs.end()) {
+        _connectionIDs[uuid] = INITIAL_CONNECTION_ID;
+    }
+
+    // we didn't have this node, so add them
+    Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket);
+    newNode->setIsReplicated(isReplicated);
+    newNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
+    newNode->setConnectionSecret(connectionSecret);
+    newNode->setPermissions(permissions);
+    newNode->setLocalID(localID);
+
+    // move the newly constructed node to the LNL thread
+    newNode->moveToThread(thread());
+
+    if (nodeType == NodeType::AudioMixer) {
+        LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer);
+    }
+
+    SharedNodePointer newNodePointer(newNode, &QObject::deleteLater);
+
+
+    {
+        QReadLocker readLocker(&_nodeMutex);
+        // insert the new node and release our read lock
+        _nodeHash.insert({ newNode->getUUID(), newNodePointer });
+        _localIDMap.insert({ localID, newNodePointer });
+    }
+
+    qCDebug(networking) << "Added" << *newNode;
+
+    auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambdas to hold a strong ref
+
+    emit nodeAdded(newNodePointer);
+    if (newNodePointer->getActiveSocket()) {
+        emit nodeActivated(newNodePointer);
+    } else {
+        connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [this, weakPtr] {
+            auto sharedPtr = weakPtr.lock();
+            if (sharedPtr) {
+                emit nodeActivated(sharedPtr);
+                disconnect(sharedPtr.data(), &NetworkPeer::socketActivated, this, 0);
+            }
+        });
+    }
+
+    // Signal when a socket changes, so we can start the hole punch over.
+    connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [this, weakPtr] {
+        emit nodeSocketUpdated(weakPtr);
+    });
+
+    return newNodePointer;
 }
 
 std::unique_ptr LimitedNodeList::constructPingPacket(const QUuid& nodeId, PingType_t pingType) {

From e5091e7f59d6e078b9f2a2ddbd42aac7241f1d98 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Mon, 25 Feb 2019 15:44:52 -0800
Subject: [PATCH 23/35] Fixes for EntityItem contains test, add MixerAvatar
 class and use in sort

---
 .../src/avatars/AvatarMixerClientData.cpp     |  4 +-
 .../src/avatars/AvatarMixerClientData.h       | 12 +--
 .../src/avatars/AvatarMixerSlave.cpp          | 80 +++++++++++++------
 assignment-client/src/avatars/MixerAvatar.h   | 31 +++++++
 libraries/entities/src/EntityItem.cpp         |  2 +-
 5 files changed, 94 insertions(+), 35 deletions(-)
 create mode 100644 assignment-client/src/avatars/MixerAvatar.h

diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp
index 9edbae12d8..9ba1ea82ca 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.cpp
+++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp
@@ -135,9 +135,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
         EntityTree& entityTree = *slaveSharedData.entityTree;
         FindPriorityZone findPriorityZone { newPosition, false } ;
         entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone);
-        if (findPriorityZone.isInPriorityZone) {
-            // Tag avatar as hero ...
-        }
+        _avatar->setPriorityAvatar(findPriorityZone.isInPriorityZone);
     }
 
     return true;
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h
index 45d508088c..a11f218a7b 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.h
+++ b/assignment-client/src/avatars/AvatarMixerClientData.h
@@ -21,7 +21,7 @@
 #include 
 #include 
 
-#include 
+#include "MixerAvatar.h"
 #include 
 #include 
 #include 
@@ -46,10 +46,10 @@ public:
     using PerNodeTraitVersions = std::unordered_map;
 
     int parseData(ReceivedMessage& message, const SlaveSharedData& SlaveSharedData);
-    AvatarData& getAvatar() { return *_avatar; }
-    const AvatarData& getAvatar() const { return *_avatar; }
-    const AvatarData* getConstAvatarData() const { return _avatar.get(); }
-    AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; }
+    MixerAvatar& getAvatar() { return *_avatar; }
+    const MixerAvatar& getAvatar() const { return *_avatar; }
+    const MixerAvatar* getConstAvatarData() const { return _avatar.get(); }
+    MixerAvatarSharedPointer getAvatarSharedPointer() const { return _avatar; }
 
     uint16_t getLastBroadcastSequenceNumber(NLPacket::LocalID nodeID) const;
     void setLastBroadcastSequenceNumber(NLPacket::LocalID nodeID, uint16_t sequenceNumber)
@@ -163,7 +163,7 @@ private:
     };
     PacketQueue _packetQueue;
 
-    AvatarSharedPointer _avatar { new AvatarData() };
+    MixerAvatarSharedPointer _avatar { new MixerAvatar() };
 
     uint16_t _lastReceivedSequenceNumber { 0 };
     std::unordered_map _lastBroadcastSequenceNumbers;
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp
index 6b039e2c03..7d25268a6b 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp
@@ -281,7 +281,51 @@ AABox computeBubbleBox(const AvatarData& avatar, float bubbleExpansionFactor) {
     return box;
 }
 
+namespace {
+    class SortableAvatar : public PrioritySortUtil::Sortable {
+    public:
+        SortableAvatar() = delete;
+        SortableAvatar(const MixerAvatar* avatar, const Node* avatarNode, uint64_t lastEncodeTime)
+            : _avatar(avatar), _node(avatarNode), _lastEncodeTime(lastEncodeTime) {
+        }
+        glm::vec3 getPosition() const override { return _avatar->getClientGlobalPosition(); }
+        float getRadius() const override {
+            glm::vec3 nodeBoxScale = _avatar->getGlobalBoundingBox().getScale();
+            return 0.5f * glm::max(nodeBoxScale.x, glm::max(nodeBoxScale.y, nodeBoxScale.z));
+        }
+        uint64_t getTimestamp() const override {
+            return _lastEncodeTime;
+        }
+        const Node* getNode() const { return _node; }
+        const MixerAvatar* getAvatar() const { return _avatar; }
+
+    private:
+        const MixerAvatar* _avatar;
+        const Node* _node;
+        uint64_t _lastEncodeTime;
+    };
+
+}  // Close anonymous namespace.
+
+// Specialize computePriority() for avatars:
+template<> float PrioritySortUtil::PriorityQueue::computePriority(const SortableAvatar& thing) const {
+    static constexpr float AVATAR_HERO_BONUS { 25.0f };  // Higher than any normal priority.
+
+    float priority = std::numeric_limits::min();
+
+    for (const auto& view : _views) {
+        priority = std::max(priority, computePriority(view, thing));
+    }
+
+    if (thing.getAvatar()->getPriorityAvatar()) {
+        priority += AVATAR_HERO_BONUS;
+    }
+
+    return priority;
+}
+
 void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
+    const float AVATAR_HERO_FRACTION { 0.4f };
     const Node* destinationNode = node.data();
 
     auto nodeList = DependencyManager::get();
@@ -314,8 +358,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
     int identityBytesSent = 0;
     int traitBytesSent = 0;
 
-    // max number of avatarBytes per frame
-    int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND);
+    // max number of avatarBytes per frame (13 900, typical)
+    const int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND);
+    const int maxHeroBytesPerFrame = int(maxAvatarBytesPerFrame * AVATAR_HERO_FRACTION);  // 5555, typical
 
     // keep track of the number of other avatars held back in this frame
     int numAvatarsHeldBack = 0;
@@ -339,27 +384,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
     const float MY_AVATAR_BUBBLE_EXPANSION_FACTOR = 4.0f; // magic number determined emperically
     AABox nodeBox = computeBubbleBox(avatar, MY_AVATAR_BUBBLE_EXPANSION_FACTOR);
 
-    class SortableAvatar: public PrioritySortUtil::Sortable {
-    public:
-        SortableAvatar() = delete;
-        SortableAvatar(const AvatarData* avatar, const Node* avatarNode, uint64_t lastEncodeTime)
-            : _avatar(avatar), _node(avatarNode), _lastEncodeTime(lastEncodeTime) {}
-        glm::vec3 getPosition() const override { return _avatar->getClientGlobalPosition(); }
-        float getRadius() const override {
-            glm::vec3 nodeBoxScale = _avatar->getGlobalBoundingBox().getScale();
-            return 0.5f * glm::max(nodeBoxScale.x, glm::max(nodeBoxScale.y, nodeBoxScale.z));
-        }
-        uint64_t getTimestamp() const override {
-            return _lastEncodeTime;
-        }
-        const Node* getNode() const { return _node; }
-
-    private:
-        const AvatarData* _avatar;
-        const Node* _node;
-        uint64_t _lastEncodeTime;
-    };
-
     // prepare to sort
     const auto& cameraViews = nodeData->getViewFrustums();
     PrioritySortUtil::PriorityQueue sortedAvatars(cameraViews,
@@ -446,7 +470,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
 
         if (!shouldIgnore) {
             // sort this one for later
-            const AvatarData* avatarNodeData = avatarClientNodeData->getConstAvatarData();
+            const MixerAvatar* avatarNodeData = avatarClientNodeData->getConstAvatarData();
             auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNode->getLocalID());
 
             sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime));
@@ -508,10 +532,16 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
             }
         }
 
+        bool overHeroBudget = frameByteEstimate > maxHeroBytesPerFrame;
+
         auto startAvatarDataPacking = chrono::high_resolution_clock::now();
 
         const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData());
-        const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
+        const MixerAvatar* otherAvatar = otherNodeData->getConstAvatarData();
+
+        if (overHeroBudget && otherAvatar->getPriorityAvatar()) {
+            continue;  // No more heroes (this frame).
+        }
 
         // Typically all out-of-view avatars but such avatars' priorities will rise with time:
         bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD;
diff --git a/assignment-client/src/avatars/MixerAvatar.h b/assignment-client/src/avatars/MixerAvatar.h
new file mode 100644
index 0000000000..4781fdee1b
--- /dev/null
+++ b/assignment-client/src/avatars/MixerAvatar.h
@@ -0,0 +1,31 @@
+//
+//  MixerAvatar.h
+//  assignment-client/src/avatars
+//
+//  Created by Simon Walton Feb 2019.
+//  Copyright 2019 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
+//
+
+// Avatar class for use within the avatar mixer - encapsulates data required only for
+// sorting priorities within the mixer.
+
+#ifndef hifi_MixerAvatar_h
+#define hifi_MixerAvatar_h
+
+#include 
+
+class MixerAvatar : public AvatarData {
+public:
+    bool getPriorityAvatar() const { return  _bPriorityAvatar; }
+    void setPriorityAvatar(bool bPriorityAvatar) { _bPriorityAvatar = bPriorityAvatar; }
+
+private:
+    bool _bPriorityAvatar { false };
+};
+
+using MixerAvatarSharedPointer = std::shared_ptr;
+
+#endif  // hifi_MixerAvatar_h
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 25e5893078..3ecbdf497a 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -1738,7 +1738,7 @@ bool EntityItem::contains(const glm::vec3& point) const {
         // the above cases not yet supported --> fall through to BOX case
         case SHAPE_TYPE_BOX: {
             localPoint = glm::abs(localPoint);
-            return glm::any(glm::lessThanEqual(localPoint, glm::vec3(NORMALIZED_HALF_SIDE)));
+            return glm::all(glm::lessThanEqual(localPoint, glm::vec3(NORMALIZED_HALF_SIDE)));
         }
         case SHAPE_TYPE_ELLIPSOID: {
             // since we've transformed into the normalized space this is just a sphere-point intersection test

From d6f755955d44721be003b0dfb203fe57592d6308 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Mon, 25 Feb 2019 16:24:12 -0800
Subject: [PATCH 24/35] Add _avatarPriorityChanged to
 EntityItemProperties::markAllChanged()

---
 libraries/entities/src/EntityItemProperties.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 96160ebd93..a7cba157ae 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -4022,6 +4022,7 @@ void EntityItemProperties::markAllChanged() {
     _bloom.markAllChanged();
     _flyingAllowedChanged = true;
     _ghostingAllowedChanged = true;
+    _avatarPriorityChanged = true;
     _filterURLChanged = true;
     _keyLightModeChanged = true;
     _ambientLightModeChanged = true;

From a8dd7b7e1fb124f2380102b835658eb91aca80d2 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Mon, 25 Feb 2019 17:14:22 -0800
Subject: [PATCH 25/35] Fix gcc error about defining templates outside their
 namespace

Also warning about hiding virtual function.
---
 assignment-client/src/avatars/AvatarMixerClientData.h | 1 +
 assignment-client/src/avatars/AvatarMixerSlave.cpp    | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h
index a11f218a7b..98c8d7e15b 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.h
+++ b/assignment-client/src/avatars/AvatarMixerClientData.h
@@ -45,6 +45,7 @@ public:
     using HRCTime = p_high_resolution_clock::time_point;
     using PerNodeTraitVersions = std::unordered_map;
 
+    using NodeData::parseData;  // Avoid clang warning about hiding.
     int parseData(ReceivedMessage& message, const SlaveSharedData& SlaveSharedData);
     MixerAvatar& getAvatar() { return *_avatar; }
     const MixerAvatar& getAvatar() const { return *_avatar; }
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp
index 7d25268a6b..9ad3cff7e1 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp
@@ -308,7 +308,8 @@ namespace {
 }  // Close anonymous namespace.
 
 // Specialize computePriority() for avatars:
-template<> float PrioritySortUtil::PriorityQueue::computePriority(const SortableAvatar& thing) const {
+namespace PrioritySortUtil {
+template<> float PriorityQueue::computePriority(const SortableAvatar& thing) const {
     static constexpr float AVATAR_HERO_BONUS { 25.0f };  // Higher than any normal priority.
 
     float priority = std::numeric_limits::min();
@@ -323,6 +324,7 @@ template<> float PrioritySortUtil::PriorityQueue::computePriorit
 
     return priority;
 }
+}
 
 void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
     const float AVATAR_HERO_FRACTION { 0.4f };

From 0b080471ff975c89d7108844f554527caa900ef7 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Tue, 26 Feb 2019 10:57:35 -0800
Subject: [PATCH 26/35] Filter on scriptValue to reduce EntityItem load; bump
 ping timeouts for testing

---
 assignment-client/src/avatars/AvatarMixer.cpp   | 2 +-
 libraries/networking/src/DomainHandler.h        | 2 +-
 libraries/networking/src/ThreadedAssignment.cpp | 1 +
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index f352e02afa..df89194247 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -957,7 +957,7 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
 }
 
 void AvatarMixer::setupEntityQuery() {
-    static char queryJsonString[] = R"({"avatarPriority": true})";
+    static char queryJsonString[] = R"({"avatarPriority": true, "serverScripts": "+"})";
 
     _entityViewer.init();
     DependencyManager::registerInheritance();
diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h
index 620ffb9641..219af3338c 100644
--- a/libraries/networking/src/DomainHandler.h
+++ b/libraries/networking/src/DomainHandler.h
@@ -35,7 +35,7 @@ const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
 const quint16 DOMAIN_SERVER_HTTP_PORT = 40100;
 const quint16 DOMAIN_SERVER_HTTPS_PORT = 40101;
 
-const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
+const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 10;  // XXX
 
 class DomainHandler : public QObject {
     Q_OBJECT
diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp
index bdba47f0ed..62aec4a2a1 100644
--- a/libraries/networking/src/ThreadedAssignment.cpp
+++ b/libraries/networking/src/ThreadedAssignment.cpp
@@ -123,6 +123,7 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() {
 
         // increase the number of queued check ins
         _numQueuedCheckIns++;
+        qCDebug(networking) << "Number of queued checkins = " << _numQueuedCheckIns;
     }
 }
 

From b7cc6ecca4519626eac7035b53090cc94dd11989 Mon Sep 17 00:00:00 2001
From: howard-stearns 
Date: Tue, 26 Feb 2019 10:59:31 -0800
Subject: [PATCH 27/35] trigger for 79 hero branch


From 08d6a2ce7f0724b99bc0d8686a4d84de3acf8de6 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Tue, 26 Feb 2019 16:40:16 -0800
Subject: [PATCH 28/35] Avatar Hero debugging & logging

---
 assignment-client/src/avatars/AvatarMixer.cpp       |  1 +
 .../src/avatars/AvatarMixerClientData.cpp           | 13 +++++++++++++
 assignment-client/src/avatars/AvatarMixerSlave.cpp  |  3 +++
 assignment-client/src/avatars/AvatarMixerSlave.h    |  3 +++
 libraries/networking/src/ThreadedAssignment.cpp     |  4 +++-
 5 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index df89194247..3064a5cd14 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -752,6 +752,7 @@ void AvatarMixer::sendStatsPacket() {
     slavesAggregatObject["sent_4_averageDataBytes"] = TIGHT_LOOP_STAT(aggregateStats.numDataBytesSent);
     slavesAggregatObject["sent_5_averageTraitsBytes"] = TIGHT_LOOP_STAT(aggregateStats.numTraitsBytesSent);
     slavesAggregatObject["sent_6_averageIdentityBytes"] = TIGHT_LOOP_STAT(aggregateStats.numIdentityBytesSent);
+    slavesAggregatObject["sent_7_averageHeroAvatars"] = TIGHT_LOOP_STAT(aggregateStats.numHeroesIncluded);
 
     slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime);
     slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime);
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp
index 9ba1ea82ca..1fe6d7e13c 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.cpp
+++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp
@@ -19,6 +19,8 @@
 #include 
 #include 
 
+#include "AvatarLogging.h"
+
 #include "AvatarMixerSlave.h"
 
 AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : 
@@ -132,10 +134,21 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
 
     auto newPosition = getPosition();
     if (newPosition != oldPosition) {
+//#define AVATAR_HERO_TEST_HACK
+#ifdef AVATAR_HERO_TEST_HACK
+        {
+            const static QString heroKey { "HERO" };
+            _avatar->setPriorityAvatar(_avatar->getDisplayName().contains(heroKey));
+        }
+#else
         EntityTree& entityTree = *slaveSharedData.entityTree;
         FindPriorityZone findPriorityZone { newPosition, false } ;
         entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone);
         _avatar->setPriorityAvatar(findPriorityZone.isInPriorityZone);
+        if (findPriorityZone.isInPriorityZone) {
+            qCWarning(avatars) << "Avatar" << _avatar->getDisplayName() << "in hero zone";
+        }
+#endif
     }
 
     return true;
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp
index 9ad3cff7e1..5d2efdf830 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp
@@ -596,6 +596,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
 
         if (detail != AvatarData::NoData) {
             _stats.numOthersIncluded++;
+            if (otherAvatar->getPriorityAvatar()) {
+                _stats.numHeroesIncluded++;
+            }
 
             // increment the number of avatars sent to this receiver
             nodeData->incrementNumAvatarsSentLastFrame();
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h
index 28d99748fa..8c5ad6b181 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.h
+++ b/assignment-client/src/avatars/AvatarMixerSlave.h
@@ -32,6 +32,7 @@ public:
     int numIdentityPacketsSent { 0 };
     int numOthersIncluded { 0 };
     int overBudgetAvatars { 0 };
+    int numHeroesIncluded { 0 };
 
     quint64 ignoreCalculationElapsedTime { 0 };
     quint64 avatarDataPackingElapsedTime { 0 };
@@ -57,6 +58,7 @@ public:
         numIdentityPacketsSent = 0;
         numOthersIncluded = 0;
         overBudgetAvatars = 0;
+        numHeroesIncluded = 0;
 
         ignoreCalculationElapsedTime = 0;
         avatarDataPackingElapsedTime = 0;
@@ -80,6 +82,7 @@ public:
         numIdentityPacketsSent += rhs.numIdentityPacketsSent;
         numOthersIncluded += rhs.numOthersIncluded;
         overBudgetAvatars += rhs.overBudgetAvatars;
+        numHeroesIncluded += rhs.numHeroesIncluded;
 
         ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime;
         avatarDataPackingElapsedTime += rhs.avatarDataPackingElapsedTime;
diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp
index 62aec4a2a1..012ac242ab 100644
--- a/libraries/networking/src/ThreadedAssignment.cpp
+++ b/libraries/networking/src/ThreadedAssignment.cpp
@@ -123,7 +123,9 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() {
 
         // increase the number of queued check ins
         _numQueuedCheckIns++;
-        qCDebug(networking) << "Number of queued checkins = " << _numQueuedCheckIns;
+        if (_numQueuedCheckIns > 1) {
+            qCDebug(networking) << "Number of queued checkins = " << _numQueuedCheckIns;
+        }
     }
 }
 

From 167ddbecff127d7a33f0546744f1b5c2fef97968 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Tue, 26 Feb 2019 18:35:01 -0800
Subject: [PATCH 29/35] Use session display-name for debugging

---
 assignment-client/src/avatars/AvatarMixerClientData.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp
index 1fe6d7e13c..a63a76829b 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.cpp
+++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp
@@ -146,7 +146,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
         entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone);
         _avatar->setPriorityAvatar(findPriorityZone.isInPriorityZone);
         if (findPriorityZone.isInPriorityZone) {
-            qCWarning(avatars) << "Avatar" << _avatar->getDisplayName() << "in hero zone";
+            qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone";
         }
 #endif
     }

From 13cda8d212154e16565553d661da07275d35dde5 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Wed, 27 Feb 2019 13:14:08 -0800
Subject: [PATCH 30/35] Use separate priority-queues for hero & other avatars

---
 .../src/avatars/AvatarMixerSlave.cpp          | 271 ++++++++++--------
 1 file changed, 146 insertions(+), 125 deletions(-)

diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp
index 5d2efdf830..7627f6a2e5 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp
@@ -307,24 +307,24 @@ namespace {
 
 }  // Close anonymous namespace.
 
-// Specialize computePriority() for avatars:
-namespace PrioritySortUtil {
-template<> float PriorityQueue::computePriority(const SortableAvatar& thing) const {
-    static constexpr float AVATAR_HERO_BONUS { 25.0f };  // Higher than any normal priority.
-
-    float priority = std::numeric_limits::min();
-
-    for (const auto& view : _views) {
-        priority = std::max(priority, computePriority(view, thing));
-    }
-
-    if (thing.getAvatar()->getPriorityAvatar()) {
-        priority += AVATAR_HERO_BONUS;
-    }
-
-    return priority;
-}
-}
+//// Specialize computePriority() for avatars:
+//namespace PrioritySortUtil {
+//template<> float PriorityQueue::computePriority(const SortableAvatar& thing) const {
+//    static constexpr float AVATAR_HERO_BONUS { 25.0f };  // Higher than any normal priority.
+//
+//    float priority = std::numeric_limits::min();
+//
+//    for (const auto& view : _views) {
+//        priority = std::max(priority, computePriority(view, thing));
+//    }
+//
+//    if (thing.getAvatar()->getPriorityAvatar()) {
+//        priority += AVATAR_HERO_BONUS;
+//    }
+//
+//    return priority;
+//}
+//}
 
 void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
     const float AVATAR_HERO_FRACTION { 0.4f };
@@ -388,11 +388,23 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
 
     // prepare to sort
     const auto& cameraViews = nodeData->getViewFrustums();
-    PrioritySortUtil::PriorityQueue sortedAvatars(cameraViews,
-            AvatarData::_avatarSortCoefficientSize,
-            AvatarData::_avatarSortCoefficientCenter,
-            AvatarData::_avatarSortCoefficientAge);
-    sortedAvatars.reserve(_end - _begin);
+
+    using AvatarPriorityQueue = PrioritySortUtil::PriorityQueue;
+    // Keep two independent queues, one for heroes and one for the riff-raff.
+    enum PriorityVariants { kHero, kNonhero };
+    AvatarPriorityQueue avatarPriorityQueues[2] = { {cameraViews,
+        AvatarData::_avatarSortCoefficientSize, AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge},
+        {cameraViews,
+        AvatarData::_avatarSortCoefficientSize, AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge}
+    };
+
+    //PrioritySortUtil::PriorityQueue sortedAvatars(cameraViews,
+    //        AvatarData::_avatarSortCoefficientSize,
+    //        AvatarData::_avatarSortCoefficientCenter,
+    //        AvatarData::_avatarSortCoefficientAge);
+    //sortedAvatars.reserve(_end - _begin);
+
+    avatarPriorityQueues[kNonhero].reserve(_end - _begin);
 
     for (auto listedNode = _begin; listedNode != _end; ++listedNode) {
         Node* otherNodeRaw = (*listedNode).data();
@@ -475,7 +487,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
             const MixerAvatar* avatarNodeData = avatarClientNodeData->getConstAvatarData();
             auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNode->getLocalID());
 
-            sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime));
+            avatarPriorityQueues[avatarNodeData->getPriorityAvatar() ? kHero : kNonhero].push(
+                SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime));
         }
         
         // If Avatar A's PAL WAS open but is no longer open, AND
@@ -501,124 +514,132 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
 
     // loop through our sorted avatars and allocate our bandwidth to them accordingly
 
-    int remainingAvatars = (int)sortedAvatars.size();
+    int remainingAvatars = (int)avatarPriorityQueues[kHero].size() + (int)avatarPriorityQueues[kNonhero].size();
     auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
 
     auto avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
     const int avatarPacketCapacity = avatarPacket->getPayloadCapacity();
     int avatarSpaceAvailable = avatarPacketCapacity;
     int numPacketsSent = 0;
+    int numAvatarsSent = 0;
     auto identityPacketList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
 
-    const auto& sortedAvatarVector = sortedAvatars.getSortedVector(numToSendEst);
-    for (const auto& sortedAvatar : sortedAvatarVector) {
-        const Node* otherNode = sortedAvatar.getNode();
-        auto lastEncodeForOther = sortedAvatar.getTimestamp();
+    for (PriorityVariants currentVariant = kHero; currentVariant <= kNonhero; ++((int&)currentVariant)) {
+        const auto& sortedAvatarVector = avatarPriorityQueues[currentVariant].getSortedVector(numToSendEst);
+        for (const auto& sortedAvatar : sortedAvatarVector) {
+            const Node* otherNode = sortedAvatar.getNode();
+            auto lastEncodeForOther = sortedAvatar.getTimestamp();
 
-        assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map
+            assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map
 
-        AvatarData::AvatarDataDetail detail = AvatarData::NoData;
+            AvatarData::AvatarDataDetail detail = AvatarData::NoData;
 
-        // NOTE: Here's where we determine if we are over budget and drop remaining avatars,
-        // or send minimal avatar data in uncommon case of PALIsOpen.
-        int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;
-        auto frameByteEstimate = identityBytesSent + traitBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes;
-        bool overBudget = frameByteEstimate > maxAvatarBytesPerFrame;
-        if (overBudget) {
-            if (PALIsOpen) {
-                _stats.overBudgetAvatars++;
-                detail = AvatarData::PALMinimum;
-            } else {
-                _stats.overBudgetAvatars += remainingAvatars;
-                break;
-            }
-        }
-
-        bool overHeroBudget = frameByteEstimate > maxHeroBytesPerFrame;
-
-        auto startAvatarDataPacking = chrono::high_resolution_clock::now();
-
-        const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData());
-        const MixerAvatar* otherAvatar = otherNodeData->getConstAvatarData();
-
-        if (overHeroBudget && otherAvatar->getPriorityAvatar()) {
-            continue;  // No more heroes (this frame).
-        }
-
-        // Typically all out-of-view avatars but such avatars' priorities will rise with time:
-        bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD;
-
-        if (isLowerPriority) {
-            detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
-            nodeData->incrementAvatarOutOfView();
-        } else if (!overBudget) {
-            detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO ? AvatarData::SendAllData : AvatarData::CullSmallData;
-            nodeData->incrementAvatarInView();
-
-            // If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO
-            // the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
-            if (otherAvatar->hasProcessedFirstIdentity()
-                && nodeData->getLastBroadcastTime(otherNode->getLocalID()) <= otherNodeData->getIdentityChangeTimestamp()) {
-                identityBytesSent += sendIdentityPacket(*identityPacketList, otherNodeData, *destinationNode);
-
-                // remember the last time we sent identity details about this other node to the receiver
-                nodeData->setLastBroadcastTime(otherNode->getLocalID(), usecTimestampNow());
-            }
-        }
-
-        QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getLocalID());
-
-        const bool distanceAdjust = true;
-        const bool dropFaceTracking = false;
-        AvatarDataPacket::SendStatus sendStatus;
-        sendStatus.sendUUID = true;
-
-        do {
-            auto startSerialize = chrono::high_resolution_clock::now();
-            QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
-                sendStatus, dropFaceTracking, distanceAdjust, myPosition,
-                &lastSentJointsForOther, avatarSpaceAvailable);
-            auto endSerialize = chrono::high_resolution_clock::now();
-            _stats.toByteArrayElapsedTime +=
-                (quint64)chrono::duration_cast(endSerialize - startSerialize).count();
-
-            avatarPacket->write(bytes);
-            avatarSpaceAvailable -= bytes.size();
-            numAvatarDataBytes += bytes.size();
-            if (!sendStatus || avatarSpaceAvailable < (int)AvatarDataPacket::MIN_BULK_PACKET_SIZE) {
-                // Weren't able to fit everything.
-                nodeList->sendPacket(std::move(avatarPacket), *destinationNode);
-                ++numPacketsSent;
-                avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
-                avatarSpaceAvailable = avatarPacketCapacity;
-            }
-        } while (!sendStatus);
-
-        if (detail != AvatarData::NoData) {
-            _stats.numOthersIncluded++;
-            if (otherAvatar->getPriorityAvatar()) {
-                _stats.numHeroesIncluded++;
+            // NOTE: Here's where we determine if we are over budget and drop remaining avatars,
+            // or send minimal avatar data in uncommon case of PALIsOpen.
+            int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;
+            auto frameByteEstimate = identityBytesSent + traitBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes;
+            bool overBudget = frameByteEstimate > maxAvatarBytesPerFrame;
+            if (overBudget) {
+                if (PALIsOpen) {
+                    _stats.overBudgetAvatars++;
+                    detail = AvatarData::PALMinimum;
+                } else {
+                    _stats.overBudgetAvatars += remainingAvatars;
+                    break;
+                }
             }
 
-            // increment the number of avatars sent to this receiver
-            nodeData->incrementNumAvatarsSentLastFrame();
+            bool overHeroBudget = currentVariant == kHero && numAvatarDataBytes > maxHeroBytesPerFrame;
+            if (overHeroBudget) {
+                break;  // No more heroes (this frame).
+            }
 
-            // set the last sent sequence number for this sender on the receiver
-            nodeData->setLastBroadcastSequenceNumber(otherNode->getLocalID(),
-                otherNodeData->getLastReceivedSequenceNumber());
-            nodeData->setLastOtherAvatarEncodeTime(otherNode->getLocalID(), usecTimestampNow());
+            auto startAvatarDataPacking = chrono::high_resolution_clock::now();
+
+            const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData());
+            const MixerAvatar* otherAvatar = otherNodeData->getConstAvatarData();
+
+            // Typically all out-of-view avatars but such avatars' priorities will rise with time:
+            bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD;
+
+            if (isLowerPriority) {
+                detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
+                nodeData->incrementAvatarOutOfView();
+            } else if (!overBudget) {
+                detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO ? AvatarData::SendAllData : AvatarData::CullSmallData;
+                nodeData->incrementAvatarInView();
+
+                // If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO
+                // the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
+                if (otherAvatar->hasProcessedFirstIdentity()
+                    && nodeData->getLastBroadcastTime(otherNode->getLocalID()) <= otherNodeData->getIdentityChangeTimestamp()) {
+                    identityBytesSent += sendIdentityPacket(*identityPacketList, otherNodeData, *destinationNode);
+
+                    // remember the last time we sent identity details about this other node to the receiver
+                    nodeData->setLastBroadcastTime(otherNode->getLocalID(), usecTimestampNow());
+                }
+            }
+
+            QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getLocalID());
+
+            const bool distanceAdjust = true;
+            const bool dropFaceTracking = false;
+            AvatarDataPacket::SendStatus sendStatus;
+            sendStatus.sendUUID = true;
+
+            do {
+                auto startSerialize = chrono::high_resolution_clock::now();
+                QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
+                    sendStatus, dropFaceTracking, distanceAdjust, myPosition,
+                    &lastSentJointsForOther, avatarSpaceAvailable);
+                auto endSerialize = chrono::high_resolution_clock::now();
+                _stats.toByteArrayElapsedTime +=
+                    (quint64)chrono::duration_cast(endSerialize - startSerialize).count();
+
+                avatarPacket->write(bytes);
+                avatarSpaceAvailable -= bytes.size();
+                numAvatarDataBytes += bytes.size();
+                if (!sendStatus || avatarSpaceAvailable < (int)AvatarDataPacket::MIN_BULK_PACKET_SIZE) {
+                    // Weren't able to fit everything.
+                    nodeList->sendPacket(std::move(avatarPacket), *destinationNode);
+                    ++numPacketsSent;
+                    avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
+                    avatarSpaceAvailable = avatarPacketCapacity;
+                }
+            } while (!sendStatus);
+
+            if (detail != AvatarData::NoData) {
+                _stats.numOthersIncluded++;
+                if (otherAvatar->getPriorityAvatar()) {
+                    _stats.numHeroesIncluded++;
+                }
+
+                // increment the number of avatars sent to this receiver
+                nodeData->incrementNumAvatarsSentLastFrame();
+
+                // set the last sent sequence number for this sender on the receiver
+                nodeData->setLastBroadcastSequenceNumber(otherNode->getLocalID(),
+                    otherNodeData->getLastReceivedSequenceNumber());
+                nodeData->setLastOtherAvatarEncodeTime(otherNode->getLocalID(), usecTimestampNow());
+            }
+
+            auto endAvatarDataPacking = chrono::high_resolution_clock::now();
+            _stats.avatarDataPackingElapsedTime +=
+                (quint64)chrono::duration_cast(endAvatarDataPacking - startAvatarDataPacking).count();
+
+            if (!overBudget) {
+                // use helper to add any changed traits to our packet list
+                traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
+            }
+            numAvatarsSent++;
+            remainingAvatars--;
         }
 
-        auto endAvatarDataPacking = chrono::high_resolution_clock::now();
-        _stats.avatarDataPackingElapsedTime +=
-            (quint64) chrono::duration_cast(endAvatarDataPacking - startAvatarDataPacking).count();
-
-        if (!overBudget) {
-            // use helper to add any changed traits to our packet list
-            traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
+        if (currentVariant == kHero) {  // Dump any remaining heroes into the commoners.
+            for (auto avIter = sortedAvatarVector.begin() + numAvatarsSent; avIter < sortedAvatarVector.end(); ++avIter) {
+                avatarPriorityQueues[kNonhero].push(*avIter);
+            }
         }
-
-        remainingAvatars--;
     }
 
     if (nodeData->getNumAvatarsSentLastFrame() > numToSendEst) {

From fede8d052516a90ac183e4d448eeae7841ff9afe Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Wed, 27 Feb 2019 15:37:11 -0800
Subject: [PATCH 31/35] Minor clean-up

---
 .../src/avatars/AvatarMixerClientData.cpp     |  6 ++--
 .../src/avatars/AvatarMixerSlave.cpp          | 35 ++++---------------
 2 files changed, 9 insertions(+), 32 deletions(-)

diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp
index a63a76829b..e24d48a9ed 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.cpp
+++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp
@@ -145,9 +145,9 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
         FindPriorityZone findPriorityZone { newPosition, false } ;
         entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone);
         _avatar->setPriorityAvatar(findPriorityZone.isInPriorityZone);
-        if (findPriorityZone.isInPriorityZone) {
-            qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone";
-        }
+        //if (findPriorityZone.isInPriorityZone) {
+        //    qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone";
+        //}
 #endif
     }
 
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp
index 7627f6a2e5..a38553ddeb 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp
@@ -307,25 +307,6 @@ namespace {
 
 }  // Close anonymous namespace.
 
-//// Specialize computePriority() for avatars:
-//namespace PrioritySortUtil {
-//template<> float PriorityQueue::computePriority(const SortableAvatar& thing) const {
-//    static constexpr float AVATAR_HERO_BONUS { 25.0f };  // Higher than any normal priority.
-//
-//    float priority = std::numeric_limits::min();
-//
-//    for (const auto& view : _views) {
-//        priority = std::max(priority, computePriority(view, thing));
-//    }
-//
-//    if (thing.getAvatar()->getPriorityAvatar()) {
-//        priority += AVATAR_HERO_BONUS;
-//    }
-//
-//    return priority;
-//}
-//}
-
 void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
     const float AVATAR_HERO_FRACTION { 0.4f };
     const Node* destinationNode = node.data();
@@ -392,18 +373,14 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
     using AvatarPriorityQueue = PrioritySortUtil::PriorityQueue;
     // Keep two independent queues, one for heroes and one for the riff-raff.
     enum PriorityVariants { kHero, kNonhero };
-    AvatarPriorityQueue avatarPriorityQueues[2] = { {cameraViews,
-        AvatarData::_avatarSortCoefficientSize, AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge},
-        {cameraViews,
-        AvatarData::_avatarSortCoefficientSize, AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge}
+    AvatarPriorityQueue avatarPriorityQueues[2] =
+    {
+        {cameraViews, AvatarData::_avatarSortCoefficientSize, 
+            AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge},
+        {cameraViews, AvatarData::_avatarSortCoefficientSize,
+            AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge}
     };
 
-    //PrioritySortUtil::PriorityQueue sortedAvatars(cameraViews,
-    //        AvatarData::_avatarSortCoefficientSize,
-    //        AvatarData::_avatarSortCoefficientCenter,
-    //        AvatarData::_avatarSortCoefficientAge);
-    //sortedAvatars.reserve(_end - _begin);
-
     avatarPriorityQueues[kNonhero].reserve(_end - _begin);
 
     for (auto listedNode = _begin; listedNode != _end; ++listedNode) {

From 59b9831c1d32beede3bd467111d9f58e3cf8b582 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Wed, 27 Feb 2019 15:56:01 -0800
Subject: [PATCH 32/35] Revert max check-ins to 4

---
 libraries/networking/src/DomainHandler.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h
index 219af3338c..94b1c39a45 100644
--- a/libraries/networking/src/DomainHandler.h
+++ b/libraries/networking/src/DomainHandler.h
@@ -35,7 +35,7 @@ const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
 const quint16 DOMAIN_SERVER_HTTP_PORT = 40100;
 const quint16 DOMAIN_SERVER_HTTPS_PORT = 40101;
 
-const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 10;  // XXX
+const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 4;
 
 class DomainHandler : public QObject {
     Q_OBJECT

From 17c0e40347377b29b9b2effd6a047c0e8ba4d442 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Wed, 27 Feb 2019 16:42:36 -0800
Subject: [PATCH 33/35] Add ability to filter on zone-type with JSON filter

---
 assignment-client/src/avatars/AvatarMixer.cpp | 2 +-
 libraries/entities/src/EntityItem.cpp         | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index 3064a5cd14..e0f7ae59ee 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -958,7 +958,7 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
 }
 
 void AvatarMixer::setupEntityQuery() {
-    static char queryJsonString[] = R"({"avatarPriority": true, "serverScripts": "+"})";
+    static char queryJsonString[] = R"({"avatarPriority": true, "type": "Zone"})";
 
     _entityViewer.init();
     DependencyManager::registerInheritance();
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 3ecbdf497a..7ce8ae2ab9 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -2656,6 +2656,7 @@ bool EntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
     // which means that we only handle a filtered query asking for entities where the serverScripts property is non-default
 
     static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
+    static const QString ENTITY_TYPE_PROPERTY = "type";
 
     foreach(const auto& property, jsonFilters.keys()) {
         if (property == SERVER_SCRIPTS_PROPERTY && jsonFilters[property] == EntityQueryFilterSymbol::NonDefault) {
@@ -2665,6 +2666,8 @@ bool EntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
             } else {
                 return false;
             }
+        } else if (property == ENTITY_TYPE_PROPERTY) {
+            return (jsonFilters[property] == EntityTypes::getEntityTypeName(getType()) );
         }
     }
 

From db8bd661ce2acf284a1811ea66c12f02c470a009 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Wed, 27 Feb 2019 16:50:07 -0800
Subject: [PATCH 34/35] Max domain check-ins was 5, not 4, dammit

---
 libraries/networking/src/DomainHandler.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h
index 94b1c39a45..620ffb9641 100644
--- a/libraries/networking/src/DomainHandler.h
+++ b/libraries/networking/src/DomainHandler.h
@@ -35,7 +35,7 @@ const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
 const quint16 DOMAIN_SERVER_HTTP_PORT = 40100;
 const quint16 DOMAIN_SERVER_HTTPS_PORT = 40101;
 
-const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 4;
+const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
 
 class DomainHandler : public QObject {
     Q_OBJECT

From 041a561dbcaa7280fd2c14ba2051b2add756ca6f Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Wed, 27 Feb 2019 17:27:25 -0800
Subject: [PATCH 35/35] Handcraft the JSON filter-object rather than parse a
 string

---
 assignment-client/src/avatars/AvatarMixer.cpp | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index e0f7ae59ee..67bc9b4cf7 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -958,21 +958,17 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
 }
 
 void AvatarMixer::setupEntityQuery() {
-    static char queryJsonString[] = R"({"avatarPriority": true, "type": "Zone"})";
-
     _entityViewer.init();
     DependencyManager::registerInheritance();
     DependencyManager::set(_entityViewer.getTree());
     _slaveSharedData.entityTree = _entityViewer.getTree();
 
-    QJsonParseError jsonParseError;
-    const QJsonDocument priorityZoneQuery(QJsonDocument::fromJson(queryJsonString, &jsonParseError));
-    if (jsonParseError.error != QJsonParseError::NoError) {
-        qCDebug(avatars) << "Error parsing:" << queryJsonString << " - " << jsonParseError.errorString();
-        return;
-    }
-    _entityViewer.getOctreeQuery().setJSONParameters(priorityZoneQuery.object());
+    // ES query: {"avatarPriority": true, "type": "Zone"}
+    QJsonObject priorityZoneQuery;
+    priorityZoneQuery["avatarPriority"] = true;
+    priorityZoneQuery["type"] = "Zone";
 
+    _entityViewer.getOctreeQuery().setJSONParameters(priorityZoneQuery);
 }
 
 void AvatarMixer::handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode) {