From 51669e1ac304ff3b01b7a31cc0ad0f653227499f Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Fri, 11 Oct 2019 11:06:10 -0700
Subject: [PATCH] Revert "DEV-1811: apply deletion rules to linked entities"

---
 interface/src/InterfaceParentFinder.cpp       |   4 +
 interface/src/avatar/AvatarManager.cpp        |  20 +-
 interface/src/avatar/MyAvatar.cpp             |  61 ++-
 interface/src/avatar/OtherAvatar.cpp          |   8 +-
 interface/src/ui/AvatarCertifyBanner.cpp      |  11 +-
 .../src/avatars-renderer/Avatar.cpp           |  12 +-
 libraries/avatars/src/AvatarData.cpp          |   5 +-
 libraries/avatars/src/AvatarData.h            |   2 +-
 .../src/EntityTreeRenderer.cpp                |  14 +-
 .../src/EntityTreeRenderer.h                  |   1 -
 .../src/RenderableEntityItem.cpp              |   5 +-
 .../entities/src/DeleteEntityOperator.cpp     |   9 -
 libraries/entities/src/DeleteEntityOperator.h |   1 -
 libraries/entities/src/EntityEditFilters.cpp  |   2 +-
 libraries/entities/src/EntityEditFilters.h    |   2 +-
 .../entities/src/EntityEditPacketSender.cpp   |   8 +-
 libraries/entities/src/EntityItem.cpp         |  44 +--
 libraries/entities/src/EntityItem.h           |   7 +-
 .../entities/src/EntityItemProperties.cpp     |   2 +-
 .../entities/src/EntityScriptingInterface.cpp |  87 ++---
 libraries/entities/src/EntitySimulation.cpp   |  96 ++---
 libraries/entities/src/EntitySimulation.h     |  46 ++-
 libraries/entities/src/EntityTree.cpp         | 354 ++++++++----------
 libraries/entities/src/EntityTree.h           |  19 +-
 libraries/entities/src/EntityTreeElement.cpp  |   2 +-
 .../entities/src/SimpleEntitySimulation.cpp   |  17 +-
 .../entities/src/SimpleEntitySimulation.h     |  10 +-
 libraries/physics/src/EntityMotionState.cpp   |  24 +-
 libraries/physics/src/EntityMotionState.h     |   2 +-
 .../physics/src/PhysicalEntitySimulation.cpp  | 111 ++----
 .../physics/src/PhysicalEntitySimulation.h    |  24 +-
 libraries/shared/src/PhysicsHelpers.cpp       |   6 +-
 libraries/shared/src/SpatialParentFinder.h    |   2 +-
 tests/octree/src/ModelTests.cpp               |   7 +-
 34 files changed, 455 insertions(+), 570 deletions(-)

diff --git a/interface/src/InterfaceParentFinder.cpp b/interface/src/InterfaceParentFinder.cpp
index 0f1c8876a9..33328f54cc 100644
--- a/interface/src/InterfaceParentFinder.cpp
+++ b/interface/src/InterfaceParentFinder.cpp
@@ -45,6 +45,10 @@ SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& s
         success = true;
         return parent;
     }
+    if (parentID == AVATAR_SELF_ID) {
+        success = true;
+        return avatarManager->getMyAvatar();
+    }
 
     success = false;
     return parent;
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 32e725388c..553033f394 100755
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -543,8 +543,26 @@ void AvatarManager::removeDeadAvatarEntities(const SetOfEntities& deadEntities)
     for (auto entity : deadEntities) {
         QUuid entityOwnerID = entity->getOwningAvatarID();
         AvatarSharedPointer avatar = getAvatarBySessionID(entityOwnerID);
+        const bool REQUIRES_REMOVAL_FROM_TREE = false;
         if (avatar) {
-            avatar->clearAvatarEntity(entity->getID());
+            avatar->clearAvatarEntity(entity->getID(), REQUIRES_REMOVAL_FROM_TREE);
+        }
+        if (entityTree && entity->isMyAvatarEntity()) {
+            entityTree->withWriteLock([&] {
+                // We only need to delete the direct children (rather than the descendants) because
+                // when the child is deleted, it will take care of its own children.  If the child
+                // is also an avatar-entity, we'll end up back here.  If it's not, the entity-server
+                // will take care of it in the usual way.
+                entity->forEachChild([&](SpatiallyNestablePointer child) {
+                    EntityItemPointer childEntity = std::dynamic_pointer_cast<EntityItem>(child);
+                    if (childEntity) {
+                        entityTree->deleteEntity(childEntity->getID(), true, true);
+                        if (avatar) {
+                            avatar->clearAvatarEntity(childEntity->getID(), REQUIRES_REMOVAL_FROM_TREE);
+                        }
+                    }
+                });
+            });
         }
     }
 }
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 386e15b041..6c9b62cfcf 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1577,8 +1577,7 @@ void MyAvatar::storeAvatarEntityDataPayload(const QUuid& entityID, const QByteAr
 }
 
 void MyAvatar::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree) {
-    // NOTE: the requiresRemovalFromTree argument is unused
-    AvatarData::clearAvatarEntity(entityID);
+    AvatarData::clearAvatarEntity(entityID, requiresRemovalFromTree);
     _avatarEntitiesLock.withWriteLock([&] {
         _cachedAvatarEntityBlobsToDelete.push_back(entityID);
     });
@@ -1586,12 +1585,7 @@ void MyAvatar::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFrom
 
 void MyAvatar::sanitizeAvatarEntityProperties(EntityItemProperties& properties) const {
     properties.setEntityHostType(entity::HostType::AVATAR);
-
-    // Note: we store AVATAR_SELF_ID in EntityItem::_owningAvatarID and we usually
-    // store the actual sessionUUID in EntityItemProperties::_owningAvatarID (for JS
-    // consumption, for example).  However at this context we are preparing properties
-    // for outgoing packet, in which case we use AVATAR_SELF_ID.
-    properties.setOwningAvatarID(AVATAR_SELF_ID);
+    properties.setOwningAvatarID(getID());
 
     // there's no entity-server to tell us we're the simulation owner, so always set the
     // simulationOwner to the owningAvatarID and a high priority.
@@ -1648,16 +1642,13 @@ void MyAvatar::handleChangedAvatarEntityData() {
     // move the lists to minimize lock time
     std::vector<QUuid> cachedBlobsToDelete;
     std::vector<QUuid> cachedBlobsToUpdate;
-    QSet<EntityItemID> idsToDelete;
+    std::vector<QUuid> entitiesToDelete;
     std::vector<QUuid> entitiesToAdd;
     std::vector<QUuid> entitiesToUpdate;
     _avatarEntitiesLock.withWriteLock([&] {
         cachedBlobsToDelete = std::move(_cachedAvatarEntityBlobsToDelete);
         cachedBlobsToUpdate = std::move(_cachedAvatarEntityBlobsToAddOrUpdate);
-        foreach (auto id, _entitiesToDelete) {
-            idsToDelete.insert(id);
-        }
-        _entitiesToDelete.clear();
+        entitiesToDelete = std::move(_entitiesToDelete);
         entitiesToAdd = std::move(_entitiesToAdd);
         entitiesToUpdate = std::move(_entitiesToUpdate);
     });
@@ -1675,7 +1666,7 @@ void MyAvatar::handleChangedAvatarEntityData() {
     };
 
     // remove delete-add and delete-update overlap
-    for (const auto& id : idsToDelete) {
+    for (const auto& id : entitiesToDelete) {
         removeAllInstancesHelper(id, cachedBlobsToUpdate);
         removeAllInstancesHelper(id, entitiesToAdd);
         removeAllInstancesHelper(id, entitiesToUpdate);
@@ -1689,9 +1680,11 @@ void MyAvatar::handleChangedAvatarEntityData() {
     }
 
     // DELETE real entities
-    entityTree->withWriteLock([&] {
-        entityTree->deleteEntitiesByID(idsToDelete);
-    });
+    for (const auto& id : entitiesToDelete) {
+        entityTree->withWriteLock([&] {
+            entityTree->deleteEntity(id);
+        });
+    }
 
     // ADD real entities
     EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
@@ -1804,7 +1797,7 @@ void MyAvatar::handleChangedAvatarEntityData() {
         // we have a client traits handler
         // flag removed entities as deleted so that changes are sent next frame
         _avatarEntitiesLock.withWriteLock([&] {
-            for (const auto& id : idsToDelete) {
+            for (const auto& id : entitiesToDelete) {
                 if (_packedAvatarEntityData.find(id) != _packedAvatarEntityData.end()) {
                     _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, id);
                 }
@@ -2533,11 +2526,18 @@ bool isWearableEntity(const EntityItemPointer& entity) {
 void MyAvatar::removeWornAvatarEntity(const EntityItemID& entityID) {
     auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
     EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
+
     if (entityTree) {
         auto entity = entityTree->findEntityByID(entityID);
         if (entity && isWearableEntity(entity)) {
-            treeRenderer->deleteEntity(entityID);
-            clearAvatarEntity(entityID);
+            entityTree->withWriteLock([&entityID, &entityTree] {
+                // remove this entity first from the entity tree
+                entityTree->deleteEntity(entityID, true, true);
+            });
+
+            // remove the avatar entity from our internal list
+            // (but indicate it doesn't need to be pulled from the tree)
+            clearAvatarEntity(entityID, false);
         }
     }
 }
@@ -2547,16 +2547,8 @@ void MyAvatar::clearWornAvatarEntities() {
     _avatarEntitiesLock.withReadLock([&] {
         avatarEntityIDs = _packedAvatarEntityData.keys();
     });
-    auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
-    EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
-    if (entityTree) {
-        for (auto entityID : avatarEntityIDs) {
-            auto entity = entityTree->findEntityByID(entityID);
-            if (entity && isWearableEntity(entity)) {
-                treeRenderer->deleteEntity(entityID);
-                clearAvatarEntity(entityID);
-            }
-        }
+    for (auto entityID : avatarEntityIDs) {
+        removeWornAvatarEntity(entityID);
     }
 }
 
@@ -4009,10 +4001,6 @@ float MyAvatar::getGravity() {
 void MyAvatar::setSessionUUID(const QUuid& sessionUUID) {
     QUuid oldSessionID = getSessionUUID();
     Avatar::setSessionUUID(sessionUUID);
-    bool sendPackets = !DependencyManager::get<NodeList>()->getSessionUUID().isNull();
-    if (!sendPackets) {
-        return;
-    }
     QUuid newSessionID = getSessionUUID();
     if (newSessionID != oldSessionID) {
         auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
@@ -4022,6 +4010,7 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) {
             _avatarEntitiesLock.withReadLock([&] {
                 avatarEntityIDs = _packedAvatarEntityData.keys();
             });
+            bool sendPackets = !DependencyManager::get<NodeList>()->getSessionUUID().isNull();
             EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
             entityTree->withWriteLock([&] {
                 for (const auto& entityID : avatarEntityIDs) {
@@ -4029,9 +4018,11 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) {
                     if (!entity) {
                         continue;
                     }
+                    // update OwningAvatarID so entity can be identified as "ours" later
+                    entity->setOwningAvatarID(newSessionID);
                     // NOTE: each attached AvatarEntity already have the correct updated parentID
                     // via magic in SpatiallyNestable, hence we check against newSessionID
-                    if (entity->getParentID() == newSessionID) {
+                    if (sendPackets && entity->getParentID() == newSessionID) {
                         // but when we have a real session and the AvatarEntity is parented to MyAvatar
                         // we need to update the "packedAvatarEntityData" sent to the avatar-mixer
                         // because it contains a stale parentID somewhere deep inside
diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp
index 5a9d20bd81..5673c2443f 100755
--- a/interface/src/avatar/OtherAvatar.cpp
+++ b/interface/src/avatar/OtherAvatar.cpp
@@ -558,17 +558,11 @@ void OtherAvatar::handleChangedAvatarEntityData() {
             _avatarEntitiesLock.withReadLock([&] {
                 packedAvatarEntityData = _packedAvatarEntityData;
             });
-            QSet<EntityItemID> idsToDelete;
             foreach (auto entityID, recentlyRemovedAvatarEntities) {
                 if (!packedAvatarEntityData.contains(entityID)) {
-                    idsToDelete.insert(entityID);
+                    entityTree->deleteEntity(entityID, true, true);
                 }
             }
-            if (!idsToDelete.empty()) {
-                bool force = true;
-                bool ignoreWarnings = true;
-                entityTree->deleteEntitiesByID(idsToDelete, force, ignoreWarnings);
-            }
 
             // TODO: move this outside of tree lock
             // remove stale data hashes
diff --git a/interface/src/ui/AvatarCertifyBanner.cpp b/interface/src/ui/AvatarCertifyBanner.cpp
index 3fe2ed2027..5101188885 100644
--- a/interface/src/ui/AvatarCertifyBanner.cpp
+++ b/interface/src/ui/AvatarCertifyBanner.cpp
@@ -62,7 +62,16 @@ void AvatarCertifyBanner::show(const QUuid& avatarID) {
 
 void AvatarCertifyBanner::clear() {
     if (_active) {
-        DependencyManager::get<EntityTreeRenderer>()->deleteEntity(_bannerID);
+        auto entityTreeRenderer = DependencyManager::get<EntityTreeRenderer>();
+        EntityTreePointer entityTree = entityTreeRenderer->getTree();
+        if (!entityTree) {
+            return;
+        }
+
+        entityTree->withWriteLock([&] {
+            entityTree->deleteEntity(_bannerID);
+        });
+
         _active = false;
     }
 }
diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
index 54b9334540..7a2ea5321f 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
+++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
@@ -357,13 +357,11 @@ void Avatar::removeAvatarEntitiesFromTree() {
         _avatarEntitiesLock.withReadLock([&] {
             avatarEntityIDs = _packedAvatarEntityData.keys();
         });
-        QSet<EntityItemID> ids;
-        foreach (auto id, avatarEntityIDs) {
-            ids.insert(id);
-        }
-        bool force = true;
-        bool ignoreWarnings = true;
-        entityTree->deleteEntitiesByID(ids, force, ignoreWarnings); // locks tree
+        entityTree->withWriteLock([&] {
+            for (const auto& entityID : avatarEntityIDs) {
+                entityTree->deleteEntity(entityID, true, true);
+            }
+        });
     }
 }
 
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 25a9543b18..710bfb8d2a 100755
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -2985,12 +2985,15 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent
 }
 
 void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree) {
-    // NOTE: requiresRemovalFromTree is unused
+
     bool removedEntity = false;
+
     _avatarEntitiesLock.withWriteLock([this, &removedEntity, &entityID] {
         removedEntity = _packedAvatarEntityData.remove(entityID);
     });
+
     insertRemovedEntityID(entityID);
+
     if (removedEntity && _clientTraitsHandler) {
         // we have a client traits handler, so we need to mark this removed instance trait as deleted
         // so that changes are sent next frame
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index abb21bd926..df0783ef4b 100755
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -1158,7 +1158,7 @@ public:
     /**jsdoc
      * @function Avatar.clearAvatarEntity
      * @param {Uuid} entityID - The entity ID.
-     * @param {boolean} [requiresRemovalFromTree=true] - unused
+     * @param {boolean} [requiresRemovalFromTree=true] - Requires removal from tree.
      * @deprecated This function is deprecated and will be removed.
      */
     Q_INVOKABLE virtual void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true);
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index fa18783b98..ab3f4c5243 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -226,7 +226,7 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
             EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
 
             if (entityItem && !entityItem->getScript().isEmpty()) {
-                if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) {
+                if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
                     if (_currentEntitiesInside.contains(entityID)) {
                         _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
                     }
@@ -240,6 +240,7 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
 void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
     stopDomainAndNonOwnedEntities();
 
+    auto sessionUUID = getTree()->getMyAvatarSessionUUID();
     std::unordered_map<EntityItemID, EntityRendererPointer> savedEntities;
     std::unordered_set<EntityRendererPointer> savedRenderables;
     // remove all entities from the scene
@@ -248,7 +249,7 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
         for (const auto& entry :  _entitiesInScene) {
             const auto& renderer = entry.second;
             const EntityItemPointer& entityItem = renderer->getEntity();
-            if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) {
+            if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == sessionUUID))) {
                 fadeOutRenderable(renderer);
             } else {
                 savedEntities[entry.first] = entry.second;
@@ -260,7 +261,6 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
     _renderablesToUpdate = savedRenderables;
     _entitiesInScene = savedEntities;
 
-    auto sessionUUID = getTree()->getMyAvatarSessionUUID();
     if (_layeredZones.clearDomainAndNonOwnedZones(sessionUUID)) {
         applyLayeredZones();
     }
@@ -683,7 +683,7 @@ void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() {
         QSet<EntityItemID> currentEntitiesInsideToSave;
         foreach (const EntityItemID& entityID, _currentEntitiesInside) {
             EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
-            if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) {
+            if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
                 emit leaveEntity(entityID);
                 if (_entitiesScriptEngine) {
                     _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
@@ -1221,7 +1221,7 @@ bool EntityTreeRenderer::LayeredZones::clearDomainAndNonOwnedZones(const QUuid&
     auto it = begin();
     while (it != end()) {
         auto zone = it->zone.lock();
-        if (!zone || !(zone->isLocalEntity() || zone->isMyAvatarEntity())) {
+        if (!zone || !(zone->isLocalEntity() || (zone->isAvatarEntity() && zone->getOwningAvatarID() == sessionUUID))) {
             zonesChanged = true;
             it = erase(it);
         } else {
@@ -1362,10 +1362,6 @@ EntityItemPointer EntityTreeRenderer::getEntity(const EntityItemID& id) {
     return result;
 }
 
-void EntityTreeRenderer::deleteEntity(const EntityItemID& id) const {
-    DependencyManager::get<EntityScriptingInterface>()->deleteEntity(id);
-}
-
 void EntityTreeRenderer::onEntityChanged(const EntityItemID& id) {
     _changedEntitiesGuard.withWriteLock([&] {
         _changedEntities.insert(id);
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h
index 6a5152b219..6dbaedc123 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.h
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.h
@@ -118,7 +118,6 @@ public:
     void setProxyWindow(const EntityItemID& id, QWindow* proxyWindow);
     void setCollisionSound(const EntityItemID& id, const SharedSoundPointer& sound);
     EntityItemPointer getEntity(const EntityItemID& id);
-    void deleteEntity(const EntityItemID& id) const;
     void onEntityChanged(const EntityItemID& id);
 
     // Access the workload Space
diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp
index 0e5dab6524..fb3d2f1bf5 100644
--- a/libraries/entities-renderer/src/RenderableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp
@@ -43,7 +43,6 @@ const Transform& EntityRenderer::getModelTransform() const {
 
 void EntityRenderer::makeStatusGetters(const EntityItemPointer& entity, Item::Status::Getters& statusGetters) {
     auto nodeList = DependencyManager::get<NodeList>();
-    // DANGER: nodeList->getSessionUUID() will return null id when not connected to domain.
     const QUuid& myNodeID = nodeList->getSessionUUID();
 
     statusGetters.push_back([entity]() -> render::Item::Status::Value {
@@ -104,9 +103,9 @@ void EntityRenderer::makeStatusGetters(const EntityItemPointer& entity, Item::St
             (unsigned char)render::Item::Status::Icon::HAS_ACTIONS);
     });
 
-    statusGetters.push_back([entity] () -> render::Item::Status::Value {
+    statusGetters.push_back([entity, myNodeID] () -> render::Item::Status::Value {
         if (entity->isAvatarEntity()) {
-            if (entity->isMyAvatarEntity()) {
+            if (entity->getOwningAvatarID() == myNodeID) {
                 return render::Item::Status::Value(1.0f, render::Item::Status::Value::GREEN,
                     (unsigned char)render::Item::Status::Icon::ENTITY_HOST_TYPE);
             } else {
diff --git a/libraries/entities/src/DeleteEntityOperator.cpp b/libraries/entities/src/DeleteEntityOperator.cpp
index a4ecb532e5..1dca171ae3 100644
--- a/libraries/entities/src/DeleteEntityOperator.cpp
+++ b/libraries/entities/src/DeleteEntityOperator.cpp
@@ -53,15 +53,6 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt
     }
 }
 
-void DeleteEntityOperator::addEntityToDeleteList(const EntityItemPointer& entity) {
-    assert(entity && entity->getElement());
-    EntityToDeleteDetails details;
-    details.entity = entity;
-    details.containingElement = entity->getElement();
-    details.cube = details.containingElement->getAACube();
-    _entitiesToDelete << details;
-    _lookingCount++;
-}
 
 // does this entity tree element contain the old entity
 bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(const OctreeElementPointer& element) {
diff --git a/libraries/entities/src/DeleteEntityOperator.h b/libraries/entities/src/DeleteEntityOperator.h
index 1449e2caad..3b3ee2a868 100644
--- a/libraries/entities/src/DeleteEntityOperator.h
+++ b/libraries/entities/src/DeleteEntityOperator.h
@@ -42,7 +42,6 @@ public:
     ~DeleteEntityOperator();
 
     void addEntityIDToDeleteList(const EntityItemID& searchEntityID);
-    void addEntityToDeleteList(const EntityItemPointer& entity);
     virtual bool preRecursion(const OctreeElementPointer& element) override;
     virtual bool postRecursion(const OctreeElementPointer& element) override;
 
diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp
index 16dace0fc8..a222ca8216 100644
--- a/libraries/entities/src/EntityEditFilters.cpp
+++ b/libraries/entities/src/EntityEditFilters.cpp
@@ -43,7 +43,7 @@ QList<EntityItemID> EntityEditFilters::getZonesByPosition(glm::vec3& position) {
 }
 
 bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut,
-        bool& wasChanged, EntityTree::FilterType filterType, EntityItemID& itemID, const EntityItemPointer& existingEntity) {
+        bool& wasChanged, EntityTree::FilterType filterType, EntityItemID& itemID, EntityItemPointer& existingEntity) {
     
     // get the ids of all the zones (plus the global entity edit filter) that the position
     // lies within
diff --git a/libraries/entities/src/EntityEditFilters.h b/libraries/entities/src/EntityEditFilters.h
index 69fd920998..cb99c97762 100644
--- a/libraries/entities/src/EntityEditFilters.h
+++ b/libraries/entities/src/EntityEditFilters.h
@@ -55,7 +55,7 @@ public:
     void removeFilter(EntityItemID entityID);
 
     bool filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, 
-                EntityTree::FilterType filterType, EntityItemID& entityID, const EntityItemPointer& existingEntity);
+                EntityTree::FilterType filterType, EntityItemID& entityID, EntityItemPointer& existingEntity);
 
 signals:
     void filterAdded(EntityItemID id, bool success);
diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp
index aaaf7d645a..dbb3ab076e 100644
--- a/libraries/entities/src/EntityEditPacketSender.cpp
+++ b/libraries/entities/src/EntityEditPacketSender.cpp
@@ -73,12 +73,8 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
     if (properties.getEntityHostType() == entity::HostType::AVATAR) {
         if (!_myAvatar) {
             qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit with no myAvatar";
-        } else if (properties.getOwningAvatarID() == _myAvatar->getID() || properties.getOwningAvatarID() == AVATAR_SELF_ID) {
-            // this is a local avatar-entity --> update our avatar-data rather than sending to the entity-server
-            // Note: we store AVATAR_SELF_ID in EntityItem::_owningAvatarID and we usually
-            // store the actual sessionUUID in EntityItemProperties::_owningAvatarID.
-            // However at this context we check for both cases just in case.  Really we just want to know
-            // where to route the data: entity-server or avatar-mixer.
+        } else if (properties.getOwningAvatarID() == _myAvatar->getID()) {
+            // this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server
             queueEditAvatarEntityMessage(entityTree, entityItemID);
         } else {
             qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit for another avatar";
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 15aacd934c..598ac17510 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -1347,7 +1347,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(created, getCreated);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityHostType, getEntityHostType);
-    COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarIDForProperties);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarID);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(canCastShadow, getCanCastShadow);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(isVisibleInSecondaryCamera, isVisibleInSecondaryCamera);
@@ -3203,7 +3203,6 @@ void EntityItem::somethingChangedNotification() {
     });
 }
 
-// static
 void EntityItem::retrieveMarketplacePublicKey() {
     QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
     QNetworkRequest networkRequest;
@@ -3235,26 +3234,6 @@ void EntityItem::retrieveMarketplacePublicKey() {
     });
 }
 
-void EntityItem::collectChildrenForDelete(SetOfEntities& entitiesToDelete, SetOfEntities& domainEntities, const QUuid& sessionID) const {
-    // Deleting an entity has consequences for its children, however there are rules dictating what can be deleted.
-    // This method helps enforce those rules for the children of entity (not for this entity).
-    for (SpatiallyNestablePointer child : getChildren()) {
-        if (child && child->getNestableType() == NestableType::Entity) {
-            EntityItemPointer childEntity = std::static_pointer_cast<EntityItem>(child);
-            // NOTE: null sessionID means "collect ALL known entities", else we only collect: local-entities and authorized avatar-entities
-            if (sessionID.isNull() || childEntity->isLocalEntity() || (childEntity->isAvatarEntity() &&
-                    (childEntity->isMyAvatarEntity() || childEntity->getOwningAvatarID() == sessionID))) {
-                if (entitiesToDelete.find(childEntity) == entitiesToDelete.end()) {
-                    entitiesToDelete.insert(childEntity);
-                    childEntity->collectChildrenForDelete(entitiesToDelete, domainEntities, sessionID);
-                }
-            } else if (childEntity->isDomainEntity()) {
-                domainEntities.insert(childEntity);
-            }
-        }
-    }
-}
-
 void EntityItem::setSpaceIndex(int32_t index) {
     assert(_spaceIndex == -1);
     _spaceIndex = index;
@@ -3419,7 +3398,6 @@ void EntityItem::prepareForSimulationOwnershipBid(EntityItemProperties& properti
     properties.setSimulationOwner(Physics::getSessionUUID(), priority);
     setPendingOwnershipPriority(priority);
 
-    // ANDREW TODO: figure out if it would be OK to NOT bother set these properties here
     properties.setEntityHostType(getEntityHostType());
     properties.setOwningAvatarID(getOwningAvatarID());
     setLastBroadcast(now); // for debug/physics status icons
@@ -3431,27 +3409,9 @@ bool EntityItem::isWearable() const {
 }
 
 bool EntityItem::isMyAvatarEntity() const {
-    return _hostType == entity::HostType::AVATAR && AVATAR_SELF_ID == _owningAvatarID;
+    return _hostType == entity::HostType::AVATAR && Physics::getSessionUUID() == _owningAvatarID;
 };
 
-QUuid EntityItem::getOwningAvatarIDForProperties() const {
-    if (isMyAvatarEntity()) {
-        // NOTE: we always store AVATAR_SELF_ID for MyAvatar's avatar entities,
-        // however for EntityItemProperties to be consumed by outside contexts (e.g. JS)
-        // we use the actual "sessionUUID" which is conveniently cached in the Physics namespace
-        return Physics::getSessionUUID();
-    }
-    return _owningAvatarID;
-}
-
-void EntityItem::setOwningAvatarID(const QUuid& owningAvatarID) {
-    if (!owningAvatarID.isNull() && owningAvatarID == Physics::getSessionUUID()) {
-        _owningAvatarID = AVATAR_SELF_ID;
-    } else {
-        _owningAvatarID = owningAvatarID;
-    }
-}
-
 void EntityItem::addGrab(GrabPointer grab) {
     enableNoBootstrap();
     SpatiallyNestable::addGrab(grab);
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index fef198c006..3274379ee9 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -18,7 +18,6 @@
 #include <glm/glm.hpp>
 
 #include <QtGui/QWindow>
-#include <QSet>
 
 #include <Octree.h> // for EncodeBitstreamParams class
 #include <OctreeElement.h> // for OctreeElement::AppendState
@@ -50,7 +49,6 @@ typedef std::shared_ptr<EntityTree> EntityTreePointer;
 typedef std::shared_ptr<EntityDynamicInterface> EntityDynamicPointer;
 typedef std::shared_ptr<EntityTreeElement> EntityTreeElementPointer;
 using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElementExtraEncodeData>;
-using SetOfEntities = QSet<EntityItemPointer>;
 
 #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
 #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { };
@@ -516,8 +514,7 @@ public:
 
     // if this entity is an avatar entity, which avatar is it associated with?
     QUuid getOwningAvatarID() const { return _owningAvatarID; }
-    QUuid getOwningAvatarIDForProperties() const;
-    void setOwningAvatarID(const QUuid& owningAvatarID);
+    virtual void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; }
 
     virtual bool wantsHandControllerPointerEvents() const { return false; }
     virtual bool wantsKeyboardFocus() const { return false; }
@@ -543,8 +540,6 @@ public:
     static QString _marketplacePublicKey;
     static void retrieveMarketplacePublicKey();
 
-    void collectChildrenForDelete(SetOfEntities& entitiesToDelete, SetOfEntities& domainEntities, const QUuid& sessionID) const;
-
     float getBoundingRadius() const { return _boundingRadius; }
     void setSpaceIndex(int32_t index);
     int32_t getSpaceIndex() const { return _spaceIndex; }
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index ae2e83affe..2b8f2b4c14 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -3936,7 +3936,7 @@ bool EntityItemProperties::decodeCloneEntityMessage(const QByteArray& buffer, in
     processedBytes = 0;
 
     if (NUM_BYTES_RFC4122_UUID * 2 > packetLength) {
-        qCDebug(entities) << "EntityItemProperties::decodeCloneEntityMessage().... bailing because not enough bytes in buffer";
+        qCDebug(entities) << "EntityItemProperties::processEraseMessageDetails().... bailing because not enough bytes in buffer";
         return false; // bail to prevent buffer overflow
     }
 
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index 1ca6a5804f..3305d9ba00 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -480,11 +480,17 @@ QUuid EntityScriptingInterface::addEntityInternal(const EntityItemProperties& pr
 
     _activityTracking.addedEntityCount++;
 
+    auto nodeList = DependencyManager::get<NodeList>();
+    auto sessionID = nodeList->getSessionUUID();
+
     EntityItemProperties propertiesWithSimID = properties;
     propertiesWithSimID.setEntityHostType(entityHostType);
     if (entityHostType == entity::HostType::AVATAR) {
-        // only allow adding our own avatar entities from script
-        propertiesWithSimID.setOwningAvatarID(AVATAR_SELF_ID);
+        if (sessionID.isNull()) {
+            // null sessionID is unacceptable in this case
+            sessionID = AVATAR_SELF_ID;
+        }
+        propertiesWithSimID.setOwningAvatarID(sessionID);
     } else if (entityHostType == entity::HostType::LOCAL) {
         // For now, local entities are always collisionless
         // TODO: create a separate, local physics simulation that just handles local entities (and MyAvatar?)
@@ -492,8 +498,6 @@ QUuid EntityScriptingInterface::addEntityInternal(const EntityItemProperties& pr
     }
 
     // the created time will be set in EntityTree::addEntity by recordCreationTime()
-    auto nodeList = DependencyManager::get<NodeList>();
-    auto sessionID = nodeList->getSessionUUID();
     propertiesWithSimID.setLastEditedBy(sessionID);
 
     bool scalesWithParent = propertiesWithSimID.getScalesWithParent();
@@ -801,7 +805,7 @@ QUuid EntityScriptingInterface::editEntity(const QUuid& id, const EntityItemProp
             return;
         }
 
-        if (entity->isAvatarEntity() && !entity->isMyAvatarEntity()) {
+        if (entity->isAvatarEntity() && entity->getOwningAvatarID() != sessionID && entity->getOwningAvatarID() != AVATAR_SELF_ID) {
             // don't edit other avatar's avatarEntities
             properties = EntityItemProperties();
             return;
@@ -966,52 +970,43 @@ void EntityScriptingInterface::deleteEntity(const QUuid& id) {
 
     _activityTracking.deletedEntityCount++;
 
-    if (!_entityTree) {
-        return;
-    }
-
     EntityItemID entityID(id);
+    bool shouldSendDeleteToServer = true;
 
     // If we have a local entity tree set, then also update it.
-    SetOfEntities entitiesToDeleteImmediately;
-    SetOfEntities domainEntities;
-    _entityTree->withWriteLock([&] {
-        EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
-        if (entity) {
-            if (entity->isAvatarEntity() && !entity->isMyAvatarEntity()) {
-                // don't delete other avatar's avatarEntities
-                return;
-            }
-            if (entity->getLocked()) {
-                return;
-            }
+    if (_entityTree) {
+        _entityTree->withWriteLock([&] {
+            EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
+            if (entity) {
 
-            // Deleting an entity has consequences for linked children: some can be deleted but others can't.
-            // Local- and my-avatar-entities can be deleted immediately, but other-avatar-entities can't be deleted
-            // by this context, and domain-entity deletes must rountrip through the entity-server for authorization.
-            // So we recurse down the linked hierarchy and snarf children into two categories:
-            // (a) entitiesToDeleteImmediately and (b) domainEntntities.
-            if (entity->isDomainEntity()) {
-                domainEntities.insert(entity);
-            } else {
-                entitiesToDeleteImmediately.insert(entity);
-                const auto sessionID = DependencyManager::get<NodeList>()->getSessionUUID();
-                entity->collectChildrenForDelete(entitiesToDeleteImmediately, domainEntities, sessionID);
-            }
-            if (!entitiesToDeleteImmediately.empty()) {
-                _entityTree->deleteEntitiesByPointer(entitiesToDeleteImmediately);
-            }
-        }
-    });
+                auto nodeList = DependencyManager::get<NodeList>();
+                const QUuid myNodeID = nodeList->getSessionUUID();
+                if (entity->isAvatarEntity() && entity->getOwningAvatarID() != myNodeID) {
+                    // don't delete other avatar's avatarEntities
+                    shouldSendDeleteToServer = false;
+                    return;
+                }
 
-    foreach (auto entity, entitiesToDeleteImmediately) {
-        if (entity->isMyAvatarEntity()) {
-            getEntityPacketSender()->getMyAvatar()->clearAvatarEntity(entityID, false);
-        }
+                if (entity->getLocked()) {
+                    shouldSendDeleteToServer = false;
+                } else {
+                    // only delete local entities, server entities will round trip through the server filters
+                    if (!entity->isDomainEntity() || _entityTree->isServerlessMode()) {
+                        shouldSendDeleteToServer = false;
+                        _entityTree->deleteEntity(entityID);
+
+                        if (entity->isAvatarEntity() && getEntityPacketSender()->getMyAvatar()) {
+                            getEntityPacketSender()->getMyAvatar()->clearAvatarEntity(entityID, false);
+                        }
+                    }
+                }
+            }
+        });
     }
-    // finally ask entity-server to delete domainEntities
-    foreach (auto entity, domainEntities) {
-        getEntityPacketSender()->queueEraseEntityMessage(entity->getID());
+
+    // if at this point, we know the id, and we should still delete the entity, send the update to the entity server
+    if (shouldSendDeleteToServer) {
+        getEntityPacketSender()->queueEraseEntityMessage(entityID);
     }
 }
 
@@ -1676,7 +1671,7 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
             return;
         }
 
-        if (entity->isAvatarEntity() && !entity->isMyAvatarEntity()) {
+        if (entity->isAvatarEntity() && entity->getOwningAvatarID() != myNodeID) {
             return;
         }
 
diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp
index 5576f21cc5..9f81572a4a 100644
--- a/libraries/entities/src/EntitySimulation.cpp
+++ b/libraries/entities/src/EntitySimulation.cpp
@@ -19,36 +19,41 @@
 
 void EntitySimulation::setEntityTree(EntityTreePointer tree) {
     if (_entityTree && _entityTree != tree) {
-        _entitiesToSort.clear();
-        _simpleKinematicEntities.clear();
-        _changedEntities.clear();
-        _entitiesToUpdate.clear();
         _mortalEntities.clear();
         _nextExpiry = std::numeric_limits<uint64_t>::max();
+        _entitiesToUpdate.clear();
+        _entitiesToSort.clear();
+        _simpleKinematicEntities.clear();
     }
     _entityTree = tree;
 }
 
 void EntitySimulation::updateEntities() {
-    PerformanceTimer perfTimer("EntitySimulation::updateEntities");
     QMutexLocker lock(&_mutex);
     uint64_t now = usecTimestampNow();
+    PerformanceTimer perfTimer("EntitySimulation::updateEntities");
 
     // these methods may accumulate entries in _entitiesToBeDeleted
     expireMortalEntities(now);
     callUpdateOnEntitiesThatNeedIt(now);
     moveSimpleKinematics(now);
+    updateEntitiesInternal(now);
     sortEntitiesThatMoved();
-    processDeadEntities();
 }
 
-void EntitySimulation::removeEntityFromInternalLists(EntityItemPointer entity) {
-    // remove from all internal lists except _deadEntitiesToRemoveFromTree
+void EntitySimulation::takeDeadEntities(SetOfEntities& entitiesToDelete) {
+    QMutexLocker lock(&_mutex);
+    entitiesToDelete.swap(_deadEntities);
+    _deadEntities.clear();
+}
+
+void EntitySimulation::removeEntityInternal(EntityItemPointer entity) {
+    // remove from all internal lists except _deadEntities
+    _mortalEntities.remove(entity);
+    _entitiesToUpdate.remove(entity);
     _entitiesToSort.remove(entity);
     _simpleKinematicEntities.remove(entity);
     _allEntities.remove(entity);
-    _entitiesToUpdate.remove(entity);
-    _mortalEntities.remove(entity);
     entity->setSimulated(false);
 }
 
@@ -57,9 +62,10 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
     assert(entity->isDead());
     if (entity->isSimulated()) {
         QMutexLocker lock(&_mutex);
-        removeEntityFromInternalLists(entity);
+        entity->clearActions(getThisPointer());
+        removeEntityInternal(entity);
         if (entity->getElement()) {
-            _deadEntitiesToRemoveFromTree.insert(entity);
+            _deadEntities.insert(entity);
             _entityTree->cleanupCloneIDs(entity->getEntityItemID());
         }
     }
@@ -143,7 +149,10 @@ void EntitySimulation::sortEntitiesThatMoved() {
     _entitiesToSort.clear();
 }
 
-void EntitySimulation::addEntityToInternalLists(EntityItemPointer entity) {
+void EntitySimulation::addEntity(EntityItemPointer entity) {
+    QMutexLocker lock(&_mutex);
+    assert(entity);
+    entity->deserializeActions();
     if (entity->isMortal()) {
         _mortalEntities.insert(entity);
         uint64_t expiry = entity->getExpiry();
@@ -154,14 +163,10 @@ void EntitySimulation::addEntityToInternalLists(EntityItemPointer entity) {
     if (entity->needsToCallUpdate()) {
         _entitiesToUpdate.insert(entity);
     }
+    addEntityInternal(entity);
+
     _allEntities.insert(entity);
     entity->setSimulated(true);
-}
-
-void EntitySimulation::addEntity(EntityItemPointer entity) {
-    QMutexLocker lock(&_mutex);
-    assert(entity);
-    addEntityToInternalLists(entity);
 
     // DirtyFlags are used to signal changes to entities that have already been added,
     // so we can clear them for this entity which has just been added.
@@ -213,14 +218,16 @@ void EntitySimulation::processChangedEntity(const EntityItemPointer& entity) {
 
 void EntitySimulation::clearEntities() {
     QMutexLocker lock(&_mutex);
-    _entitiesToSort.clear();
-    _simpleKinematicEntities.clear();
-    _changedEntities.clear();
-    _allEntities.clear();
-    _deadEntitiesToRemoveFromTree.clear();
-    _entitiesToUpdate.clear();
     _mortalEntities.clear();
     _nextExpiry = std::numeric_limits<uint64_t>::max();
+    _entitiesToUpdate.clear();
+    _entitiesToSort.clear();
+    _simpleKinematicEntities.clear();
+
+    clearEntitiesInternal();
+
+    _allEntities.clear();
+    _deadEntities.clear();
 }
 
 void EntitySimulation::moveSimpleKinematics(uint64_t now) {
@@ -256,22 +263,25 @@ void EntitySimulation::moveSimpleKinematics(uint64_t now) {
     }
 }
 
-void EntitySimulation::processDeadEntities() {
-    if (_deadEntitiesToRemoveFromTree.empty()) {
-        return;
-    }
-    SetOfEntities entitiesToDeleteImmediately;
-    // NOTE: dummyList will be empty because this base-class implementation is only used server-side
-    // for which ATM we only process domain-entities, and since we are passing nullSessionID for authorization
-    // EntityItem::collectChildrenForDelete() will not collect domain-entities into this side list.
-    SetOfEntities dummyList;
-    QUuid nullSessionID;
-    foreach (auto entity, _deadEntitiesToRemoveFromTree) {
-        entitiesToDeleteImmediately.insert(entity);
-        entity->collectChildrenForDelete(entitiesToDeleteImmediately, dummyList, nullSessionID);
-    }
-    if (_entityTree) {
-        _entityTree->deleteEntitiesByPointer(entitiesToDeleteImmediately);
-    }
-    _deadEntitiesToRemoveFromTree.clear();
+void EntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
+    QMutexLocker lock(&_dynamicsMutex);
+    _dynamicsToAdd += dynamic;
+}
+
+void EntitySimulation::removeDynamic(const QUuid dynamicID) {
+    QMutexLocker lock(&_dynamicsMutex);
+    _dynamicsToRemove += dynamicID;
+}
+
+void EntitySimulation::removeDynamics(QList<QUuid> dynamicIDsToRemove) {
+    QMutexLocker lock(&_dynamicsMutex);
+    foreach(QUuid uuid, dynamicIDsToRemove) {
+        _dynamicsToRemove.insert(uuid);
+    }
+}
+
+void EntitySimulation::applyDynamicChanges() {
+    QMutexLocker lock(&_dynamicsMutex);
+    _dynamicsToAdd.clear();
+    _dynamicsToRemove.clear();
 }
diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h
index 646e5a0f67..1dd0369561 100644
--- a/libraries/entities/src/EntitySimulation.h
+++ b/libraries/entities/src/EntitySimulation.h
@@ -16,14 +16,17 @@
 #include <unordered_set>
 
 #include <QtCore/QObject>
+#include <QSet>
 #include <QVector>
 
 #include <PerfStat.h>
 
+#include "EntityDynamicInterface.h"
 #include "EntityItem.h"
 #include "EntityTree.h"
 
 using EntitySimulationPointer = std::shared_ptr<EntitySimulation>;
+using SetOfEntities = QSet<EntityItemPointer>;
 using VectorOfEntities = QVector<EntityItemPointer>;
 
 // the EntitySimulation needs to know when these things change on an entity,
@@ -44,8 +47,8 @@ const int DIRTY_SIMULATION_FLAGS =
 
 class EntitySimulation : public QObject, public std::enable_shared_from_this<EntitySimulation> {
 public:
-    EntitySimulation() : _mutex(QMutex::Recursive), _nextExpiry(std::numeric_limits<uint64_t>::max()), _entityTree(nullptr) { }
-    virtual ~EntitySimulation() { setEntityTree(nullptr); }
+    EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL), _nextExpiry(std::numeric_limits<uint64_t>::max()) { }
+    virtual ~EntitySimulation() { setEntityTree(NULL); }
 
     inline EntitySimulationPointer getThisPointer() const {
         return std::const_pointer_cast<EntitySimulation>(shared_from_this());
@@ -54,12 +57,12 @@ public:
     /// \param tree pointer to EntityTree which is stored internally
     void setEntityTree(EntityTreePointer tree);
 
-    virtual void updateEntities();
+    void updateEntities();
 
-    // FIXME: remove these
-    virtual void addDynamic(EntityDynamicPointer dynamic) {}
-    virtual void removeDynamic(const QUuid dynamicID) {}
-    virtual void applyDynamicChanges() {};
+    virtual void addDynamic(EntityDynamicPointer dynamic);
+    virtual void removeDynamic(const QUuid dynamicID);
+    virtual void removeDynamics(QList<QUuid> dynamicIDsToRemove);
+    virtual void applyDynamicChanges();
 
     /// \param entity pointer to EntityItem to be added
     /// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked
@@ -69,22 +72,27 @@ public:
     /// call this whenever an entity was changed from some EXTERNAL event (NOT by the EntitySimulation itself)
     void changeEntity(EntityItemPointer entity);
 
-    virtual void clearEntities();
+    void clearEntities();
 
     void moveSimpleKinematics(uint64_t now);
 
     EntityTreePointer getEntityTree() { return _entityTree; }
 
+    virtual void takeDeadEntities(SetOfEntities& entitiesToDelete);
+
+    /// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others.
     virtual void prepareEntityForDelete(EntityItemPointer entity);
 
     void processChangedEntities();
-    virtual void queueEraseDomainEntities(const SetOfEntities& domainEntities) const { }
 
 protected:
-    virtual void addEntityToInternalLists(EntityItemPointer entity);
-    virtual void removeEntityFromInternalLists(EntityItemPointer entity);
+    // These pure virtual methods are protected because they are not to be called will-nilly. The base class
+    // calls them in the right places.
+    virtual void updateEntitiesInternal(uint64_t now) = 0;
+    virtual void addEntityInternal(EntityItemPointer entity) = 0;
+    virtual void removeEntityInternal(EntityItemPointer entity);
     virtual void processChangedEntity(const EntityItemPointer& entity);
-    virtual void processDeadEntities();
+    virtual void clearEntitiesInternal() = 0;
 
     void expireMortalEntities(uint64_t now);
     void callUpdateOnEntitiesThatNeedIt(uint64_t now);
@@ -94,21 +102,27 @@ protected:
 
     SetOfEntities _entitiesToSort; // entities moved by simulation (and might need resort in EntityTree)
     SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion
-    SetOfEntities _deadEntitiesToRemoveFromTree;
+    QList<EntityDynamicPointer> _dynamicsToAdd;
+    QSet<QUuid> _dynamicsToRemove;
+    QMutex _dynamicsMutex { QMutex::Recursive };
+
+protected:
+    SetOfEntities _deadEntities; // dead entities that might still be in the _entityTree
 
 private:
     void moveSimpleKinematics();
 
+    // back pointer to EntityTree structure
+    EntityTreePointer _entityTree;
+
     // We maintain multiple lists, each for its distinct purpose.
     // An entity may be in more than one list.
     std::unordered_set<EntityItemPointer> _changedEntities; // all changes this frame
     SetOfEntities _allEntities; // tracks all entities added the simulation
-    SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update()
     SetOfEntities _mortalEntities; // entities that have an expiry
     uint64_t _nextExpiry;
 
-    // back pointer to EntityTree structure
-    EntityTreePointer _entityTree;
+    SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update()
 };
 
 #endif // hifi_EntitySimulation_h
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index f44f2eb7c5..6c12c6d019 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -98,15 +98,14 @@ void EntityTree::eraseDomainAndNonOwnedEntities() {
             if (element) {
                 element->cleanupDomainAndNonOwnedEntities();
             }
-            if (!getIsServer()) {
-                if (entity->isLocalEntity() || entity->isMyAvatarEntity()) {
-                    savedEntities[entity->getEntityItemID()] = entity;
-                } else {
-                    int32_t spaceIndex = entity->getSpaceIndex();
-                    if (spaceIndex != -1) {
-                        // stale spaceIndices will be freed later
-                        _staleProxies.push_back(spaceIndex);
-                    }
+
+            if (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID())) {
+                savedEntities[entity->getEntityItemID()] = entity;
+            } else {
+                int32_t spaceIndex = entity->getSpaceIndex();
+                if (spaceIndex != -1) {
+                    // stale spaceIndices will be freed later
+                    _staleProxies.push_back(spaceIndex);
                 }
             }
         }
@@ -122,7 +121,7 @@ void EntityTree::eraseDomainAndNonOwnedEntities() {
 
         foreach (EntityItemWeakPointer entityItem, _needsParentFixup) {
             auto entity = entityItem.lock();
-            if (entity && (entity->isLocalEntity() || entity->isMyAvatarEntity())) {
+            if (entity && (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID()))) {
                 needParentFixup.push_back(entityItem);
             }
         }
@@ -145,12 +144,10 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
             if (element) {
                 element->cleanupEntities();
             }
-            if (!getIsServer()) {
-                int32_t spaceIndex = entity->getSpaceIndex();
-                if (spaceIndex != -1) {
-                    // assume stale spaceIndices will be freed later
-                    _staleProxies.push_back(spaceIndex);
-                }
+            int32_t spaceIndex = entity->getSpaceIndex();
+            if (spaceIndex != -1) {
+                // assume stale spaceIndices will be freed later
+                _staleProxies.push_back(spaceIndex);
             }
         }
     });
@@ -608,21 +605,61 @@ void EntityTree::setSimulation(EntitySimulationPointer simulation) {
 }
 
 void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ignoreWarnings) {
-    // NOTE: can be called without lock because deleteEntitiesByID() will lock
-    QSet<EntityItemID> ids;
-    ids << entityID;
-    deleteEntitiesByID(ids, force, ignoreWarnings);
+    EntityTreeElementPointer containingElement = getContainingElement(entityID);
+    if (!containingElement) {
+        if (!ignoreWarnings) {
+            qCWarning(entities) << "EntityTree::deleteEntity() on non-existent entityID=" << entityID;
+        }
+        return;
+    }
+
+    EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID);
+    if (!existingEntity) {
+        if (!ignoreWarnings) {
+            qCWarning(entities) << "EntityTree::deleteEntity() on non-existant entity item with entityID=" << entityID;
+        }
+        return;
+    }
+
+    if (existingEntity->getLocked() && !force) {
+        if (!ignoreWarnings) {
+            qCDebug(entities) << "ERROR! EntityTree::deleteEntity() trying to delete locked entity. entityID=" << entityID;
+        }
+        return;
+    }
+
+    cleanupCloneIDs(entityID);
+    unhookChildAvatar(entityID);
+    emit deletingEntity(entityID);
+    emit deletingEntityPointer(existingEntity.get());
+
+    // NOTE: callers must lock the tree before using this method
+    DeleteEntityOperator theOperator(getThisPointer(), entityID);
+
+    existingEntity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
+        auto descendantID = descendant->getID();
+        theOperator.addEntityIDToDeleteList(descendantID);
+        emit deletingEntity(descendantID);
+        EntityItemPointer descendantEntity = std::dynamic_pointer_cast<EntityItem>(descendant);
+        if (descendantEntity) {
+            emit deletingEntityPointer(descendantEntity.get());
+        }
+    });
+
+    recurseTreeWithOperator(&theOperator);
+    processRemovedEntities(theOperator);
+    _isDirty = true;
 }
 
 void EntityTree::unhookChildAvatar(const EntityItemID entityID) {
-    if (!getIsServer()) {
-        EntityItemPointer entity = findEntityByEntityItemID(entityID);
-        entity->forEachDescendant([&](SpatiallyNestablePointer child) {
-            if (child->getNestableType() == NestableType::Avatar) {
-                child->setParentID(nullptr);
-            }
-        });
-    }
+
+    EntityItemPointer entity = findEntityByEntityItemID(entityID);
+
+    entity->forEachDescendant([&](SpatiallyNestablePointer child) {
+        if (child->getNestableType() == NestableType::Avatar) {
+            child->setParentID(nullptr);
+        }
+    });
 }
 
 void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) {
@@ -647,100 +684,39 @@ void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) {
     }
 }
 
-void EntityTree::recursivelyFilterAndCollectForDelete(const EntityItemPointer& entity, SetOfEntities& entitiesToDelete, bool force) const {
-    // tree must be read-locked before calling this method
-    //TODO: assert(treeIsLocked);
-    assert(entity);
-    if (entity->getElement() && (entitiesToDelete.find(entity) == entitiesToDelete.end())) {
-        // filter
-        bool allowed = force;
-        if (!allowed) {
-            bool wasChanged = false;
-            auto startFilter = usecTimestampNow();
-            EntityItemProperties dummyProperties;
-            allowed = force || filterProperties(entity, dummyProperties, dummyProperties, wasChanged, FilterType::Delete);
-            auto endFilter = usecTimestampNow();
-            _totalFilterTime += endFilter - startFilter;
-        }
-        if (allowed) {
-            entitiesToDelete.insert(entity);
-            for (SpatiallyNestablePointer child : entity->getChildren()) {
-                if (child && child->getNestableType() == NestableType::Entity) {
-                    EntityItemPointer childEntity = std::static_pointer_cast<EntityItem>(child);
-                    recursivelyFilterAndCollectForDelete(childEntity, entitiesToDelete, force);
-                }
-            }
-        }
-    }
-}
-
-void EntityTree::deleteEntitiesByID(const QSet<EntityItemID>& ids, bool force, bool ignoreWarnings) {
-    // this method has two paths:
-    // (a) entity-server: applies delete filter
-    // (b) interface-client: deletes local- and my-avatar-entities immediately, submits domainEntity deletes to the entity-server
-    if (getIsServer()) {
-        withWriteLock([&] {
-            SetOfEntities entitiesToDelete;
-            for (auto id : ids) {
-                EntityItemPointer entity;
-                {
-                    QReadLocker locker(&_entityMapLock);
-                    entity = _entityMap.value(id);
-                }
-                if (entity) {
-                    recursivelyFilterAndCollectForDelete(entity, entitiesToDelete, force);
-                }
-            }
-            if (!entitiesToDelete.empty()) {
-                deleteEntitiesByPointer(entitiesToDelete);
-            }
-        });
-    } else {
-        SetOfEntities entitiesToDelete;
-        SetOfEntities domainEntities;
-        QUuid sessionID = DependencyManager::get<NodeList>()->getSessionUUID();
-        withWriteLock([&] {
-            for (auto id : ids) {
-                EntityItemPointer entity;
-                {
-                    QReadLocker locker(&_entityMapLock);
-                    entity = _entityMap.value(id);
-                }
-                if (entity) {
-                    if (entity->isDomainEntity()) {
-                        domainEntities.insert(entity);
-                    } else if (entity->isLocalEntity() || entity->isMyAvatarEntity()) {
-                        entitiesToDelete.insert(entity);
-                        entity->collectChildrenForDelete(entitiesToDelete, domainEntities, sessionID);
-                    }
-                }
-            }
-            if (!entitiesToDelete.empty()) {
-                deleteEntitiesByPointer(entitiesToDelete);
-            }
-        });
-        if (!domainEntities.empty() && _simulation) {
-            // interface-client can't delete domainEntities outright, they must roundtrip through the entity-server
-            _simulation->queueEraseDomainEntities(domainEntities);
-        }
-    }
-}
-
-void EntityTree::deleteEntitiesByPointer(const SetOfEntities& entities) {
-    // tree must be write-locked before calling this method
-    //TODO: assert(treeIsLocked);
-    // NOTE: there is no entity validation (i.e. is entity in tree?) nor snarfing of children beyond this point.
-    // Get those done BEFORE calling this method.
-    for (auto entity : entities) {
-        cleanupCloneIDs(entity->getID());
-    }
+void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool ignoreWarnings) {
+    // NOTE: callers must lock the tree before using this method
     DeleteEntityOperator theOperator(getThisPointer());
-    for (auto entity : entities) {
-        if (entity->getElement()) {
-            theOperator.addEntityToDeleteList(entity);
-            emit deletingEntity(entity->getID());
-            emit deletingEntityPointer(entity.get());
+    foreach(const EntityItemID& entityID, entityIDs) {
+        EntityTreeElementPointer containingElement = getContainingElement(entityID);
+        if (!containingElement) {
+            if (!ignoreWarnings) {
+                qCWarning(entities) << "EntityTree::deleteEntities() on non-existent entityID=" << entityID;
+            }
+            continue;
         }
+
+        EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID);
+        if (!existingEntity) {
+            if (!ignoreWarnings) {
+                qCWarning(entities) << "EntityTree::deleteEntities() on non-existent entity item with entityID=" << entityID;
+            }
+            continue;
+        }
+
+        if (existingEntity->getLocked() && !force) {
+            if (!ignoreWarnings) {
+                qCDebug(entities) << "ERROR! EntityTree::deleteEntities() trying to delete locked entity. entityID=" << entityID;
+            }
+            continue;
+        }
+
+        // tell our delete operator about this entityID
+        cleanupCloneIDs(entityID);
+        unhookChildAvatar(entityID);
+        theOperator.addEntityIDToDeleteList(entityID);
+        emit deletingEntity(entityID);
+        emit deletingEntityPointer(existingEntity.get());
     }
 
     if (!theOperator.getEntities().empty()) {
@@ -751,11 +727,23 @@ void EntityTree::deleteEntitiesByPointer(const SetOfEntities& entities) {
 }
 
 void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) {
-    // NOTE: assume tree already write-locked because this method only called in deleteEntitiesByPointer()
     quint64 deletedAt = usecTimestampNow();
     const RemovedEntities& entities = theOperator.getEntities();
     foreach(const EntityToDeleteDetails& details, entities) {
         EntityItemPointer theEntity = details.entity;
+
+        if (getIsServer()) {
+            QSet<EntityItemID> childrenIDs;
+            theEntity->forEachChild([&](SpatiallyNestablePointer child) {
+                if (child->getNestableType() == NestableType::Entity) {
+                    childrenIDs += child->getID();
+                }
+            });
+            deleteEntities(childrenIDs, true, true);
+        }
+
+        theEntity->die();
+
         if (getIsServer()) {
             removeCertifiedEntityOnServer(theEntity);
 
@@ -763,24 +751,19 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
             QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock);
             _recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID());
         } else {
-            theEntity->forEachDescendant([&](SpatiallyNestablePointer child) {
-                if (child->getNestableType() == NestableType::Avatar) {
-                    child->setParentID(nullptr);
-                }
-            });
-
             // on the client side, we also remember that we deleted this entity, we don't care about the time
             trackDeletedEntity(theEntity->getEntityItemID());
-
-            int32_t spaceIndex = theEntity->getSpaceIndex();
-            if (spaceIndex != -1) {
-                // stale spaceIndices will be freed later
-                _staleProxies.push_back(spaceIndex);
-            }
         }
+
         if (theEntity->isSimulated()) {
             _simulation->prepareEntityForDelete(theEntity);
         }
+
+        int32_t spaceIndex = theEntity->getSpaceIndex();
+        if (spaceIndex != -1) {
+            // stale spaceIndices will be freed later
+            _staleProxies.push_back(spaceIndex);
+        }
     }
 }
 
@@ -1386,7 +1369,7 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
 }
 
 
-bool EntityTree::filterProperties(const EntityItemPointer& existingEntity, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType) const {
+bool EntityTree::filterProperties(EntityItemPointer& existingEntity, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType) {
     bool accepted = true;
     auto entityEditFilters = DependencyManager::get<EntityEditFilters>();
     if (entityEditFilters) {
@@ -1766,9 +1749,9 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const
     }
 }
 
-// NOTE: Caller must lock the tree before calling this.
 int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,
                                      const SharedNodePointer& senderNode) {
+
     if (!getIsServer()) {
         qCWarning(entities) << "EntityTree::processEditPacketData() should only be called on a server tree.";
         return 0;
@@ -2234,9 +2217,7 @@ void EntityTree::fixupNeedsParentFixups() {
 
 void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) {
     if (_childrenOfAvatars.contains(avatarID)) {
-        bool force = true;
-        bool ignoreWarnings = true;
-        deleteEntitiesByID(_childrenOfAvatars[avatarID], force, ignoreWarnings);
+        deleteEntities(_childrenOfAvatars[avatarID]);
         _childrenOfAvatars.remove(avatarID);
     }
 }
@@ -2268,6 +2249,22 @@ void EntityTree::update(bool simulate) {
     if (simulate && _simulation) {
         withWriteLock([&] {
             _simulation->updateEntities();
+            {
+                PROFILE_RANGE(simulation_physics, "Deletes");
+                SetOfEntities deadEntities;
+                _simulation->takeDeadEntities(deadEntities);
+                if (!deadEntities.empty()) {
+                    // translate into list of ID's
+                    QSet<EntityItemID> idsToDelete;
+
+                    for (auto entity : deadEntities) {
+                        idsToDelete.insert(entity->getEntityItemID());
+                    }
+
+                    // delete these things the roundabout way
+                    deleteEntities(idsToDelete, true);
+                }
+            }
         });
     }
 }
@@ -2356,17 +2353,12 @@ bool EntityTree::shouldEraseEntity(EntityItemID entityID, const SharedNodePointe
     return allowed;
 }
 
+
+// TODO: consider consolidating processEraseMessageDetails() and processEraseMessage()
 int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
-    // NOTE: this is only called by the interface-client on receipt of deleteEntity message from entity-server.
-    // Which means this is a state synchronization message from the the entity-server.  It is saying
-    // "The following domain-entities have already been deleted". While need to perform sanity checking
-    // (e.g. verify these are domain entities) permissions need NOT checked for the domain-entities.
-    assert(!getIsServer());
-    // TODO: remove this stuff out of EntityTree:: and into interface-client code.
     #ifdef EXTRA_ERASE_DEBUGGING
         qCDebug(entities) << "EntityTree::processEraseMessage()";
     #endif
-    SetOfEntities consequentialDomainEntities;
     withWriteLock([&] {
         message.seek(sizeof(OCTREE_PACKET_FLAGS) + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME));
 
@@ -2374,9 +2366,10 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo
         message.readPrimitive(&numberOfIDs);
 
         if (numberOfIDs > 0) {
-            QSet<EntityItemID> idsToDelete;
+            QSet<EntityItemID> entityItemIDsToDelete;
 
             for (size_t i = 0; i < numberOfIDs; i++) {
+
                 if (NUM_BYTES_RFC4122_UUID > message.getBytesLeftToRead()) {
                     qCDebug(entities) << "EntityTree::processEraseMessage().... bailing because not enough bytes in buffer";
                     break; // bail to prevent buffer overflow
@@ -2388,85 +2381,64 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo
                 #endif
 
                 EntityItemID entityItemID(entityID);
-                idsToDelete << entityItemID;
-            }
 
-            // domain-entity deletion can trigger deletion of other entities the entity-server doesn't know about
-            // so we must recurse down the children and collect consequential deletes however
-            // we must first identify all domain-entities in idsToDelete so as to not overstep entity-server's authority
-            SetOfEntities domainEntities;
-            for (auto id : idsToDelete) {
-                EntityItemPointer entity = _entityMap.value(id);
-                if (entity && entity->isDomainEntity()) {
-                    domainEntities.insert(entity);
+                if (shouldEraseEntity(entityID, sourceNode)) {
+                    entityItemIDsToDelete << entityItemID;
+                    cleanupCloneIDs(entityItemID);
                 }
             }
-            // now we recurse domain-entities children and snarf consequential entities
-            auto nodeList = DependencyManager::get<NodeList>();
-            QUuid sessionID = nodeList->getSessionUUID();
-            // NOTE: normally a null sessionID would be bad, as that would cause the collectDhildrenForDelete() method below
-            // to snarf domain entities for which the interface-client is not authorized to delete without explicit instructions
-            // from the entity-server, however it is ok here because that would mean:
-            // (a) interface-client is not connected to a domain which means...
-            // (b) we should never get here (since this would correspond to a message from the entity-server) but...
-            // (c) who cares? When not connected to a domain the interface-client can do whatever it wants.
-            SetOfEntities entitiesToDelete;
-            for (auto entity : domainEntities) {
-                entitiesToDelete.insert(entity);
-                entity->collectChildrenForDelete(entitiesToDelete, consequentialDomainEntities, sessionID);
-            }
-
-            if (!entitiesToDelete.empty()) {
-                deleteEntitiesByPointer(entitiesToDelete);
-            }
+            deleteEntities(entityItemIDsToDelete, true, true);
         }
     });
-    if (!consequentialDomainEntities.empty() && _simulation) {
-        _simulation->queueEraseDomainEntities(consequentialDomainEntities);
-    }
     return message.getPosition();
 }
 
 // This version skips over the header
-// NOTE: Caller must write-lock the tree before calling this.
+// NOTE: Caller must lock the tree before calling this.
+// TODO: consider consolidating processEraseMessageDetails() and processEraseMessage()
 int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {
-    //TODO: assert(treeIsLocked);
-    assert(getIsServer());
     #ifdef EXTRA_ERASE_DEBUGGING
         qCDebug(entities) << "EntityTree::processEraseMessageDetails()";
     #endif
+    const unsigned char* packetData = (const unsigned char*)dataByteArray.constData();
+    const unsigned char* dataAt = packetData;
     size_t packetLength = dataByteArray.size();
     size_t processedBytes = 0;
 
     uint16_t numberOfIds = 0; // placeholder for now
-    memcpy(&numberOfIds, dataByteArray.constData(), sizeof(numberOfIds));
+    memcpy(&numberOfIds, dataAt, sizeof(numberOfIds));
+    dataAt += sizeof(numberOfIds);
     processedBytes += sizeof(numberOfIds);
 
     if (numberOfIds > 0) {
-        QSet<EntityItemID> ids;
+        QSet<EntityItemID> entityItemIDsToDelete;
 
-        // extract ids from packet
         for (size_t i = 0; i < numberOfIds; i++) {
+
+
             if (processedBytes + NUM_BYTES_RFC4122_UUID > packetLength) {
                 qCDebug(entities) << "EntityTree::processEraseMessageDetails().... bailing because not enough bytes in buffer";
                 break; // bail to prevent buffer overflow
             }
 
             QByteArray encodedID = dataByteArray.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID);
-            QUuid id = QUuid::fromRfc4122(encodedID);
+            QUuid entityID = QUuid::fromRfc4122(encodedID);
+            dataAt += encodedID.size();
             processedBytes += encodedID.size();
 
             #ifdef EXTRA_ERASE_DEBUGGING
-                qCDebug(entities) << "    ---- EntityTree::processEraseMessageDetails() contains id:" << id;
+                qCDebug(entities) << "    ---- EntityTree::processEraseMessageDetails() contains id:" << entityID;
             #endif
 
-            EntityItemID entityID(id);
-            ids << entityID;
-        }
+            EntityItemID entityItemID(entityID);
 
-        bool force = sourceNode->isAllowedEditor();
-        bool ignoreWarnings = true;
-        deleteEntitiesByID(ids, force, ignoreWarnings);
+            if (shouldEraseEntity(entityID, sourceNode)) {
+                entityItemIDsToDelete << entityItemID;
+                cleanupCloneIDs(entityItemID);
+            }
+
+        }
+        deleteEntities(entityItemIDsToDelete, true, true);
     }
     return (int)processedBytes;
 }
diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h
index 6574b9d601..9108f8d8d2 100644
--- a/libraries/entities/src/EntityTree.h
+++ b/libraries/entities/src/EntityTree.h
@@ -126,9 +126,7 @@ public:
     void unhookChildAvatar(const EntityItemID entityID);
     void cleanupCloneIDs(const EntityItemID& entityID);
     void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true);
-
-    void deleteEntitiesByID(const QSet<EntityItemID>& entityIDs, bool force = false, bool ignoreWarnings = true);
-    void deleteEntitiesByPointer(const SetOfEntities& entities);
+    void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = true);
 
     EntityItemPointer findEntityByID(const QUuid& id) const;
     EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID) const;
@@ -293,7 +291,6 @@ signals:
 
 protected:
 
-    void recursivelyFilterAndCollectForDelete(const EntityItemPointer& entity, SetOfEntities& entitiesToDelete, bool force) const;
     void processRemovedEntities(const DeleteEntityOperator& theOperator);
     bool updateEntity(EntityItemPointer entity, const EntityItemProperties& properties,
             const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
@@ -342,12 +339,12 @@ protected:
     int _totalEditMessages = 0;
     int _totalUpdates = 0;
     int _totalCreates = 0;
-    mutable quint64 _totalDecodeTime = 0;
-    mutable quint64 _totalLookupTime = 0;
-    mutable quint64 _totalUpdateTime = 0;
-    mutable quint64 _totalCreateTime = 0;
-    mutable quint64 _totalLoggingTime = 0;
-    mutable quint64 _totalFilterTime = 0;
+    quint64 _totalDecodeTime = 0;
+    quint64 _totalLookupTime = 0;
+    quint64 _totalUpdateTime = 0;
+    quint64 _totalCreateTime = 0;
+    quint64 _totalLoggingTime = 0;
+    quint64 _totalFilterTime = 0;
 
     // these performance statistics are only used in the client
     void resetClientEditStats();
@@ -367,7 +364,7 @@ protected:
 
     float _maxTmpEntityLifetime { DEFAULT_MAX_TMP_ENTITY_LIFETIME };
 
-    bool filterProperties(const EntityItemPointer& existingEntity, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType) const;
+    bool filterProperties(EntityItemPointer& existingEntity, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType);
     bool _hasEntityEditFilter{ false };
     QStringList _entityScriptSourceWhitelist;
 
diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp
index 0096319081..60eaafc0dd 100644
--- a/libraries/entities/src/EntityTreeElement.cpp
+++ b/libraries/entities/src/EntityTreeElement.cpp
@@ -705,7 +705,7 @@ void EntityTreeElement::cleanupDomainAndNonOwnedEntities() {
     withWriteLock([&] {
         EntityItems savedEntities;
         foreach(EntityItemPointer entity, _entityItems) {
-            if (!(entity->isLocalEntity() || entity->isMyAvatarEntity())) {
+            if (!(entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
                 entity->preDelete();
                 entity->_element = NULL;
             } else {
diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp
index d64efdf87f..b8e3df2d03 100644
--- a/libraries/entities/src/SimpleEntitySimulation.cpp
+++ b/libraries/entities/src/SimpleEntitySimulation.cpp
@@ -47,17 +47,14 @@ void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
     }
 }
 
-void SimpleEntitySimulation::updateEntities() {
-    EntitySimulation::updateEntities();
-    QMutexLocker lock(&_mutex);
-    uint64_t now = usecTimestampNow();
+void SimpleEntitySimulation::updateEntitiesInternal(uint64_t now) {
     expireStaleOwnerships(now);
     stopOwnerlessEntities(now);
 }
 
-void SimpleEntitySimulation::addEntityToInternalLists(EntityItemPointer entity) {
-    EntitySimulation::addEntityToInternalLists(entity);
+void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
     if (entity->getSimulatorID().isNull()) {
+        QMutexLocker lock(&_mutex);
         if (entity->getDynamic()) {
             // we don't allow dynamic objects to move without an owner so nothing to do here
         } else if (entity->isMovingRelativeToParent()) {
@@ -68,6 +65,7 @@ void SimpleEntitySimulation::addEntityToInternalLists(EntityItemPointer entity)
             }
         }
     } else {
+        QMutexLocker lock(&_mutex);
         _entitiesWithSimulationOwner.insert(entity);
         _nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
 
@@ -81,10 +79,10 @@ void SimpleEntitySimulation::addEntityToInternalLists(EntityItemPointer entity)
     }
 }
 
-void SimpleEntitySimulation::removeEntityFromInternalLists(EntityItemPointer entity) {
+void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
+    EntitySimulation::removeEntityInternal(entity);
     _entitiesWithSimulationOwner.remove(entity);
     _entitiesThatNeedSimulationOwner.remove(entity);
-    EntitySimulation::removeEntityFromInternalLists(entity);
 }
 
 void SimpleEntitySimulation::processChangedEntity(const EntityItemPointer& entity) {
@@ -137,11 +135,10 @@ void SimpleEntitySimulation::processChangedEntity(const EntityItemPointer& entit
     entity->clearDirtyFlags();
 }
 
-void SimpleEntitySimulation::clearEntities() {
+void SimpleEntitySimulation::clearEntitiesInternal() {
     QMutexLocker lock(&_mutex);
     _entitiesWithSimulationOwner.clear();
     _entitiesThatNeedSimulationOwner.clear();
-    EntitySimulation::clearEntities();
 }
 
 void SimpleEntitySimulation::sortEntitiesThatMoved() {
diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h
index e984b72ed4..1b240a8bf0 100644
--- a/libraries/entities/src/SimpleEntitySimulation.h
+++ b/libraries/entities/src/SimpleEntitySimulation.h
@@ -23,16 +23,16 @@ using SimpleEntitySimulationPointer = std::shared_ptr<SimpleEntitySimulation>;
 class SimpleEntitySimulation : public EntitySimulation {
 public:
     SimpleEntitySimulation() : EntitySimulation() { }
-    ~SimpleEntitySimulation() { clearEntities(); }
+    ~SimpleEntitySimulation() { clearEntitiesInternal(); }
 
     void clearOwnership(const QUuid& ownerID);
-    void clearEntities() override;
-    void updateEntities() override;
 
 protected:
-    void addEntityToInternalLists(EntityItemPointer entity) override;
-    void removeEntityFromInternalLists(EntityItemPointer entity) override;
+    void updateEntitiesInternal(uint64_t now) override;
+    void addEntityInternal(EntityItemPointer entity) override;
+    void removeEntityInternal(EntityItemPointer entity) override;
     void processChangedEntity(const EntityItemPointer& entity) override;
+    void clearEntitiesInternal() override;
 
     void sortEntitiesThatMoved() override;
 
diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index f3d129871f..e48f0603bd 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -62,7 +62,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
     // rather than pass the legit shape pointer to the ObjectMotionState ctor above.
     setShape(shape);
 
-    if (_entity->isAvatarEntity() && !_entity->isMyAvatarEntity()) {
+    if (_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID()) {
         // avatar entities are always thus, so we cache this fact in _ownershipState
         _ownershipState = EntityMotionState::OwnershipState::Unownable;
     }
@@ -407,8 +407,8 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
     // NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
     // after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
 
-    // this case is prevented by setting _ownershipState to OwnershipState::Unownable in EntityMotionState::ctor
-    assert(!(_entity->isAvatarEntity() && !_entity->isMyAvatarEntity()));
+    // this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
+    assert(!(_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
 
     if (_entity->getTransitingWithAvatar()) {
         return false;
@@ -768,7 +768,7 @@ uint8_t EntityMotionState::computeFinalBidPriority() const {
 }
 
 bool EntityMotionState::isLocallyOwned() const {
-    return _entity->getSimulatorID() == Physics::getSessionUUID() || _entity->isMyAvatarEntity();
+    return _entity->getSimulatorID() == Physics::getSessionUUID();
 }
 
 bool EntityMotionState::isLocallyOwnedOrShouldBe() const {
@@ -786,21 +786,13 @@ void EntityMotionState::setRegion(uint8_t region) {
 }
 
 void EntityMotionState::initForBid() {
-    if (_ownershipState != EntityMotionState::OwnershipState::Unownable) {
-        _ownershipState = EntityMotionState::OwnershipState::PendingBid;
-    }
+    assert(_ownershipState != EntityMotionState::OwnershipState::Unownable);
+    _ownershipState = EntityMotionState::OwnershipState::PendingBid;
 }
 
 void EntityMotionState::initForOwned() {
-    if (_ownershipState != EntityMotionState::OwnershipState::Unownable) {
-        _ownershipState = EntityMotionState::OwnershipState::LocallyOwned;
-    }
-}
-
-void EntityMotionState::clearOwnershipState() {
-    if (_ownershipState != OwnershipState::Unownable) {
-        _ownershipState = OwnershipState::NotLocallyOwned;
-    }
+    assert(_ownershipState != EntityMotionState::OwnershipState::Unownable);
+    _ownershipState = EntityMotionState::OwnershipState::LocallyOwned;
 }
 
 void EntityMotionState::clearObjectVelocities() const {
diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h
index be6f5c0658..7456837777 100644
--- a/libraries/physics/src/EntityMotionState.h
+++ b/libraries/physics/src/EntityMotionState.h
@@ -107,7 +107,7 @@ protected:
     uint64_t getNextBidExpiry() const { return _nextBidExpiry; }
     void initForBid();
     void initForOwned();
-    void clearOwnershipState();
+    void clearOwnershipState() { _ownershipState = OwnershipState::NotLocallyOwned; }
     void updateServerPhysicsVariables();
     bool remoteSimulationOutOfSync(uint32_t simulationStep);
 
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index 0375e1dfd9..df8c3fa32e 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -40,9 +40,14 @@ void PhysicalEntitySimulation::init(
 }
 
 // begin EntitySimulation overrides
-void PhysicalEntitySimulation::addEntityToInternalLists(EntityItemPointer entity) {
-    EntitySimulation::addEntityToInternalLists(entity);
-    entity->deserializeActions(); // TODO: do this elsewhere
+void PhysicalEntitySimulation::updateEntitiesInternal(uint64_t now) {
+    // Do nothing here because the "internal" update the PhysicsEngine::stepSimulation() which is done elsewhere.
+}
+
+void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
+    QMutexLocker lock(&_mutex);
+    assert(entity);
+    assert(!entity->isDead());
     uint8_t region = _space->getRegion(entity->getSpaceIndex());
     bool maybeShouldBePhysical = (region < workload::Region::R3 || region == workload::Region::UNKNOWN) && entity->shouldBePhysical();
     bool canBeKinematic = region <= workload::Region::R3;
@@ -61,20 +66,23 @@ void PhysicalEntitySimulation::addEntityToInternalLists(EntityItemPointer entity
     }
 }
 
-void PhysicalEntitySimulation::removeEntityFromInternalLists(EntityItemPointer entity) {
-    _entitiesToAddToPhysics.remove(entity);
-    EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
-    if (motionState) {
-        removeOwnershipData(motionState);
-        _entitiesToRemoveFromPhysics.insert(entity);
-    }
-    if (entity->isDead() && entity->getElement()) {
-        _deadEntitiesToRemoveFromTree.insert(entity);
+void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
+    if (entity->isSimulated()) {
+        EntitySimulation::removeEntityInternal(entity);
+        _entitiesToAddToPhysics.remove(entity);
+
+        EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
+        if (motionState) {
+            removeOwnershipData(motionState);
+            _entitiesToRemoveFromPhysics.insert(entity);
+        }
+        if (entity->isDead() && entity->getElement()) {
+            _deadEntities.insert(entity);
+        }
     }
     if (entity->isAvatarEntity()) {
         _deadAvatarEntities.insert(entity);
     }
-    EntitySimulation::removeEntityFromInternalLists(entity);
 }
 
 void PhysicalEntitySimulation::removeOwnershipData(EntityMotionState* motionState) {
@@ -107,6 +115,18 @@ void PhysicalEntitySimulation::clearOwnershipData() {
     _bids.clear();
 }
 
+void PhysicalEntitySimulation::takeDeadEntities(SetOfEntities& deadEntities) {
+    QMutexLocker lock(&_mutex);
+    for (auto entity : _deadEntities) {
+        EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
+        if (motionState) {
+            _entitiesToRemoveFromPhysics.insert(entity);
+        }
+    }
+    _deadEntities.swap(deadEntities);
+    _deadEntities.clear();
+}
+
 void PhysicalEntitySimulation::takeDeadAvatarEntities(SetOfEntities& deadEntities) {
     _deadAvatarEntities.swap(deadEntities);
     _deadAvatarEntities.clear();
@@ -170,44 +190,11 @@ void PhysicalEntitySimulation::processChangedEntity(const EntityItemPointer& ent
     }
 }
 
-void PhysicalEntitySimulation::processDeadEntities() {
-    if (_deadEntitiesToRemoveFromTree.empty()) {
-        return;
-    }
-    PROFILE_RANGE(simulation_physics, "Deletes");
-    SetOfEntities entitiesToDeleteImmediately;
-    SetOfEntities domainEntities;
-    QUuid sessionID = Physics::getSessionUUID();
-    QMutexLocker lock(&_mutex);
-    for (auto entity : _deadEntitiesToRemoveFromTree) {
-        EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
-        if (motionState) {
-            _entitiesToRemoveFromPhysics.insert(entity);
-        }
-        if (entity->isDomainEntity()) {
-            domainEntities.insert(entity);
-        } else if (entity->isLocalEntity() || entity->isMyAvatarEntity()) {
-            entitiesToDeleteImmediately.insert(entity);
-            entity->collectChildrenForDelete(entitiesToDeleteImmediately, domainEntities, sessionID);
-        }
-    }
-    _deadEntitiesToRemoveFromTree.clear();
-
-    // interface-client can't delete domainEntities outright, they must roundtrip through the entity-server
-    for (auto entity : domainEntities) {
-        _entityPacketSender->queueEraseEntityMessage(entity->getID());
-    }
-    if (!entitiesToDeleteImmediately.empty()) {
-        getEntityTree()->deleteEntitiesByPointer(entitiesToDeleteImmediately);
-    }
-}
-
-void PhysicalEntitySimulation::clearEntities() {
+void PhysicalEntitySimulation::clearEntitiesInternal() {
     // TODO: we should probably wait to lock the _physicsEngine so we don't mess up data structures
     // while it is in the middle of a simulation step.  As it is, we're probably in shutdown mode
     // anyway, so maybe the simulation was already properly shutdown?  Cross our fingers...
 
-    QMutexLocker lock(&_mutex);
     // remove the objects (aka MotionStates) from physics
     _physicsEngine->removeSetOfObjects(_physicalObjects);
 
@@ -229,23 +216,11 @@ void PhysicalEntitySimulation::clearEntities() {
     _entitiesToAddToPhysics.clear();
     _incomingChanges.clear();
     _entitiesToDeleteLater.clear();
-
-    EntitySimulation::clearEntities();
-}
-
-void PhysicalEntitySimulation::queueEraseDomainEntities(const SetOfEntities& domainEntities) const {
-    if (_entityPacketSender) {
-        for (auto domainEntity : domainEntities) {
-            assert(domainEntity->isDomainEntity());
-            _entityPacketSender->queueEraseEntityMessage(domainEntity->getID());
-        }
-    }
 }
 
 // virtual
 void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
-    // DANGER! this can be called on any thread
-    // do no dirty deeds here --> assemble list for later
+    // this can be called on any thread
     assert(entity);
     assert(entity->isDead());
     QMutexLocker lock(&_mutex);
@@ -253,11 +228,11 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity)
 }
 
 void PhysicalEntitySimulation::removeDeadEntities() {
-    // DANGER! only ever call this on the main thread
+    // only ever call this on the main thread
     QMutexLocker lock(&_mutex);
     for (auto& entity : _entitiesToDeleteLater) {
         entity->clearActions(getThisPointer());
-        EntitySimulation::prepareEntityForDelete(entity);
+        removeEntityInternal(entity);
     }
     _entitiesToDeleteLater.clear();
 }
@@ -672,16 +647,10 @@ void PhysicalEntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
                     "dynamic that was already in _physicsEngine";
             }
         }
-        QMutexLocker lock(&_dynamicsMutex);
-        _dynamicsToAdd += dynamic;
+        EntitySimulation::addDynamic(dynamic);
     }
 }
 
-void PhysicalEntitySimulation::removeDynamic(const QUuid dynamicID) {
-    QMutexLocker lock(&_dynamicsMutex);
-    _dynamicsToRemove += dynamicID;
-}
-
 void PhysicalEntitySimulation::applyDynamicChanges() {
     QList<EntityDynamicPointer> dynamicsFailedToAdd;
     if (_physicsEngine) {
@@ -696,8 +665,8 @@ void PhysicalEntitySimulation::applyDynamicChanges() {
                 }
             }
         }
-        _dynamicsToAdd.clear();
-        _dynamicsToRemove.clear();
+        // applyDynamicChanges will clear _dynamicsToRemove and _dynamicsToAdd
+        EntitySimulation::applyDynamicChanges();
     }
 
     // put back the ones that couldn't yet be added
diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h
index a3bd60b96e..f5213f7fef 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.h
+++ b/libraries/physics/src/PhysicalEntitySimulation.h
@@ -19,7 +19,6 @@
 #include <btBulletDynamicsCommon.h>
 #include <BulletCollision/CollisionDispatch/btGhostObject.h>
 
-#include <EntityDynamicInterface.h>
 #include <EntityItem.h>
 #include <EntitySimulation.h>
 #include <workload/Space.h>
@@ -59,24 +58,22 @@ public:
     void init(EntityTreePointer tree, PhysicsEnginePointer engine, EntityEditPacketSender* packetSender);
     void setWorkloadSpace(const workload::SpacePointer space) { _space = space; }
 
-    void addDynamic(EntityDynamicPointer dynamic) override;
-    void removeDynamic(const QUuid dynamicID) override;
-    void applyDynamicChanges() override;
+    virtual void addDynamic(EntityDynamicPointer dynamic) override;
+    virtual void applyDynamicChanges() override;
 
+    virtual void takeDeadEntities(SetOfEntities& deadEntities) override;
     void takeDeadAvatarEntities(SetOfEntities& deadEntities);
 
-    virtual void clearEntities() override;
-    void queueEraseDomainEntities(const SetOfEntities& domainEntities) const override;
-
 signals:
     void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
 
 protected: // only called by EntitySimulation
     // overrides for EntitySimulation
-    void addEntityToInternalLists(EntityItemPointer entity) override;
-    void removeEntityFromInternalLists(EntityItemPointer entity) override;
+    virtual void updateEntitiesInternal(uint64_t now) override;
+    virtual void addEntityInternal(EntityItemPointer entity) override;
+    virtual void removeEntityInternal(EntityItemPointer entity) override;
     void processChangedEntity(const EntityItemPointer& entity) override;
-    void processDeadEntities() override;
+    virtual void clearEntitiesInternal() override;
 
     void removeOwnershipData(EntityMotionState* motionState);
     void clearOwnershipData();
@@ -124,13 +121,8 @@ private:
 
     VectorOfEntityMotionStates _owned;
     VectorOfEntityMotionStates _bids;
-    SetOfEntities _deadAvatarEntities; // to remove from Avatar's lists
+    SetOfEntities _deadAvatarEntities;
     std::vector<EntityItemPointer> _entitiesToDeleteLater;
-
-    QList<EntityDynamicPointer> _dynamicsToAdd;
-    QSet<QUuid> _dynamicsToRemove;
-    QMutex _dynamicsMutex { QMutex::Recursive };
-
     workload::SpacePointer _space;
     uint64_t _nextBidExpiry;
     uint32_t _lastStepSendPackets { 0 };
diff --git a/libraries/shared/src/PhysicsHelpers.cpp b/libraries/shared/src/PhysicsHelpers.cpp
index b7f5242429..092b9d078a 100644
--- a/libraries/shared/src/PhysicsHelpers.cpp
+++ b/libraries/shared/src/PhysicsHelpers.cpp
@@ -10,12 +10,10 @@
 //
 
 #include "PhysicsHelpers.h"
-
+#include "NumericalConstants.h"
 #include <QUuid>
 
-#include "NumericalConstants.h"
 #include "PhysicsCollisionGroups.h"
-#include "SharedUtil.h"
 
 // This chunk of code was copied from Bullet-2.82, so we include the Bullet license here:
 /*
@@ -93,7 +91,7 @@ int32_t Physics::getDefaultCollisionMask(int32_t group) {
 QUuid _sessionID;
 
 void Physics::setSessionUUID(const QUuid& sessionID) {
-    _sessionID = sessionID.isNull() ? AVATAR_SELF_ID : sessionID;
+    _sessionID = sessionID;
 }
 
 const QUuid& Physics::getSessionUUID() {
diff --git a/libraries/shared/src/SpatialParentFinder.h b/libraries/shared/src/SpatialParentFinder.h
index 8300359b65..c19babbc7f 100644
--- a/libraries/shared/src/SpatialParentFinder.h
+++ b/libraries/shared/src/SpatialParentFinder.h
@@ -21,7 +21,7 @@ using SpatiallyNestableWeakPointer = std::weak_ptr<SpatiallyNestable>;
 using SpatiallyNestablePointer = std::shared_ptr<SpatiallyNestable>;
 class SpatialParentTree {
 public:
-    virtual SpatiallyNestablePointer findByID(const QUuid& id) const = 0;
+    virtual SpatiallyNestablePointer findByID(const QUuid& id) const { return nullptr; }
 };
 class SpatialParentFinder : public Dependency {
 
diff --git a/tests/octree/src/ModelTests.cpp b/tests/octree/src/ModelTests.cpp
index 1ab32217d9..f3e17a56a5 100644
--- a/tests/octree/src/ModelTests.cpp
+++ b/tests/octree/src/ModelTests.cpp
@@ -363,8 +363,7 @@ void EntityTests::entityTreeTests(bool verbose) {
             }
 
             quint64 startDelete = usecTimestampNow();
-            bool force = true;
-            tree.deleteEntity(entityID, force);
+            tree.deleteEntity(entityID);
             quint64 endDelete = usecTimestampNow();
             totalElapsedDelete += (endDelete - startDelete);
 
@@ -447,9 +446,7 @@ void EntityTests::entityTreeTests(bool verbose) {
             }
 
             quint64 startDelete = usecTimestampNow();
-            bool force = true;
-            bool ignoreWarnings = true;
-            tree.deleteEntitiesByID(entitiesToDelete, force, ignoreWarnings);
+            tree.deleteEntities(entitiesToDelete);
             quint64 endDelete = usecTimestampNow();
             totalElapsedDelete += (endDelete - startDelete);