From 8033e93eda84b482f9806d39dbb3d2e09e5fcaab Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 19 Feb 2019 17:38:37 -0800 Subject: [PATCH 01/14] 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 02/14] 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 e5091e7f59d6e078b9f2a2ddbd42aac7241f1d98 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Mon, 25 Feb 2019 15:44:52 -0800
Subject: [PATCH 03/14] 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 04/14] 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 05/14] 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 06/14] 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 08d6a2ce7f0724b99bc0d8686a4d84de3acf8de6 Mon Sep 17 00:00:00 2001
From: Simon Walton 
Date: Tue, 26 Feb 2019 16:40:16 -0800
Subject: [PATCH 07/14] 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 08/14] 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 09/14] 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 10/14] 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 11/14] 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 12/14] 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 13/14] 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 14/14] 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) {