void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
    EntitySimulation::changeEntityInternal(entity);
    if (!entity->getSimulatorID().isNull()) {
        _entitiesWithSimulator.insert(entity);
    }
    entity->clearDirtyFlags();
}
Beispiel #2
0
void EntitySimulation::changeEntity(EntityItemPointer entity) {
    assert(entity);
    if (!entity->_simulated) {
        // This entity was either never added to the simulation or has been removed
        // (probably for pending delete), so we don't want to keep a pointer to it 
        // on any internal lists.
        return;
    }

    // Although it is not the responsibility of the EntitySimulation to sort the tree for EXTERNAL changes
    // it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence 
    // we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag.
    bool wasRemoved = false;
    uint32_t dirtyFlags = entity->getDirtyFlags();
    if (dirtyFlags & EntityItem::DIRTY_POSITION) {
        AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), (float)TREE_SCALE);
        AACube newCube = entity->getMaximumAACube();
        if (!domainBounds.touches(newCube)) {
            qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
            _entitiesToDelete.insert(entity);
            _mortalEntities.remove(entity);
            _entitiesToUpdate.remove(entity);
            _entitiesToSort.remove(entity);
            _simpleKinematicEntities.remove(entity);
            removeEntityInternal(entity);
            entity->_simulated = false;
            wasRemoved = true;
        }
    }
    if (!wasRemoved) {
        if (dirtyFlags & EntityItem::DIRTY_LIFETIME) {
            if (entity->isMortal()) {
                _mortalEntities.insert(entity);
                quint64 expiry = entity->getExpiry();
                if (expiry < _nextExpiry) {
                    _nextExpiry = expiry;
                }
            } else {
                _mortalEntities.remove(entity);
            }
            entity->clearDirtyFlags(EntityItem::DIRTY_LIFETIME);
        }
        if (entity->needsToCallUpdate()) {
            _entitiesToUpdate.insert(entity);
        } else {
            _entitiesToUpdate.remove(entity);
        }
        changeEntityInternal(entity);
    }
}
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
    EntitySimulation::changeEntityInternal(entity);
    if (entity->getSimulatorID().isNull()) {
        QMutexLocker lock(&_mutex);
        _entitiesWithSimulationOwner.remove(entity);
        if (entity->getDynamic() && entity->hasLocalVelocity()) {
            _entitiesThatNeedSimulationOwner.insert(entity);
            quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
            if (expiry < _nextOwnerlessExpiry) {
                _nextOwnerlessExpiry = expiry;
            }
        }
    } else {
        QMutexLocker lock(&_mutex);
        _entitiesWithSimulationOwner.insert(entity);
        _entitiesThatNeedSimulationOwner.remove(entity);
    }
    entity->clearDirtyFlags();
}
Beispiel #4
0
void EntitySimulation::addEntity(EntityItemPointer entity) {
    assert(entity);
    entity->deserializeActions();
    if (entity->isMortal()) {
        _mortalEntities.insert(entity);
        quint64 expiry = entity->getExpiry();
        if (expiry < _nextExpiry) {
            _nextExpiry = expiry;
        }
    }
    if (entity->needsToCallUpdate()) {
        _entitiesToUpdate.insert(entity);
    }
    addEntityInternal(entity);

    _allEntities.insert(entity);
    entity->_simulated = true;

    // 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.
    entity->clearDirtyFlags();
}
Beispiel #5
0
bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& origProperties,
                                         EntityTreeElementPointer containingElement, const SharedNodePointer& senderNode) {
    EntityItemProperties properties = origProperties;

    bool allowLockChange;
    QUuid senderID;
    if (senderNode.isNull()) {
        auto nodeList = DependencyManager::get<NodeList>();
        allowLockChange = nodeList->getThisNodeCanAdjustLocks();
        senderID = nodeList->getSessionUUID();
    } else {
        allowLockChange = senderNode->getCanAdjustLocks();
        senderID = senderNode->getUUID();
    }

    if (!allowLockChange && (entity->getLocked() != properties.getLocked())) {
        qCDebug(entities) << "Refusing disallowed lock adjustment.";
        return false;
    }

    // enforce support for locked entities. If an entity is currently locked, then the only
    // property we allow you to change is the locked property.
    if (entity->getLocked()) {
        if (properties.lockedChanged()) {
            bool wantsLocked = properties.getLocked();
            if (!wantsLocked) {
                EntityItemProperties tempProperties;
                tempProperties.setLocked(wantsLocked);
                UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, tempProperties);
                recurseTreeWithOperator(&theOperator);
                _isDirty = true;
            }
        }
    } else {
        if (getIsServer()) {
            bool simulationBlocked = !entity->getSimulatorID().isNull();
            if (properties.simulationOwnerChanged()) {
                QUuid submittedID = properties.getSimulationOwner().getID();
                // a legit interface will only submit their own ID or NULL:
                if (submittedID.isNull()) {
                    if (entity->getSimulatorID() == senderID) {
                        // We only allow the simulation owner to clear their own simulationID's.
                        simulationBlocked = false;
                        properties.clearSimulationOwner(); // clear everything
                    }
                    // else: We assume the sender really did believe it was the simulation owner when it sent
                } else if (submittedID == senderID) {
                    // the sender is trying to take or continue ownership
                    if (entity->getSimulatorID().isNull()) {
                        // the sender it taking ownership
                        properties.promoteSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
                        simulationBlocked = false;
                    } else if (entity->getSimulatorID() == senderID) {
                        // the sender is asserting ownership
                        simulationBlocked = false;
                    } else {
                        // the sender is trying to steal ownership from another simulator
                        // so we apply the rules for ownership change:
                        // (1) higher priority wins
                        // (2) equal priority wins if ownership filter has expired except...
                        uint8_t oldPriority = entity->getSimulationPriority();
                        uint8_t newPriority = properties.getSimulationOwner().getPriority();
                        if (newPriority > oldPriority ||
                             (newPriority == oldPriority && properties.getSimulationOwner().hasExpired())) {
                            simulationBlocked = false;
                        }
                    }
                } else {
                    // the entire update is suspect --> ignore it
                    return false;
                }
            } else {
                simulationBlocked = senderID != entity->getSimulatorID();
            }
            if (simulationBlocked) {
                // squash ownership and physics-related changes.
                properties.setSimulationOwnerChanged(false);
                properties.setPositionChanged(false);
                properties.setRotationChanged(false);
                properties.setVelocityChanged(false);
                properties.setAngularVelocityChanged(false);
                properties.setAccelerationChanged(false);

                if (wantTerseEditLogging()) {
                    qCDebug(entities) << senderNode->getUUID() << "physical edits suppressed";
                }
            }
        }
        // else client accepts what the server says

        QString entityScriptBefore = entity->getScript();
        quint64 entityScriptTimestampBefore = entity->getScriptTimestamp();
        QString collisionSoundURLBefore = entity->getCollisionSoundURL();
        uint32_t preFlags = entity->getDirtyFlags();
        UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, properties);
        recurseTreeWithOperator(&theOperator);
        _isDirty = true;

        uint32_t newFlags = entity->getDirtyFlags() & ~preFlags;
        if (newFlags) {
            if (_simulation) {
                if (newFlags & DIRTY_SIMULATION_FLAGS) {
                    _simulation->changeEntity(entity);
                }
            } else {
                // normally the _simulation clears ALL updateFlags, but since there is none we do it explicitly
                entity->clearDirtyFlags();
            }
        }

        QString entityScriptAfter = entity->getScript();
        quint64 entityScriptTimestampAfter = entity->getScriptTimestamp();
        bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter;
        if (entityScriptBefore != entityScriptAfter || reload) {
            emitEntityScriptChanging(entity->getEntityItemID(), reload); // the entity script has changed
        }
        maybeNotifyNewCollisionSoundURL(collisionSoundURLBefore, entity->getCollisionSoundURL());
     }

    // TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG).
    containingElement = getContainingElement(entity->getEntityItemID());
    if (!containingElement) {
        qCDebug(entities) << "UNEXPECTED!!!! after updateEntity() we no longer have a containing element??? entityID="
                << entity->getEntityItemID();
        return false;
    }

    return true;
}
Beispiel #6
0
bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& origProperties, 
                                         EntityTreeElement* containingElement, const SharedNodePointer& senderNode) {
    EntityItemProperties properties = origProperties;

    bool allowLockChange;
    QUuid senderID;
    if (senderNode.isNull()) {
        auto nodeList = DependencyManager::get<NodeList>();
        allowLockChange = nodeList->getThisNodeCanAdjustLocks();
        senderID = nodeList->getSessionUUID();
    } else {
        allowLockChange = senderNode->getCanAdjustLocks();
        senderID = senderNode->getUUID();
    }

    if (!allowLockChange && (entity->getLocked() != properties.getLocked())) {
        qCDebug(entities) << "Refusing disallowed lock adjustment.";
        return false;
    }

    // enforce support for locked entities. If an entity is currently locked, then the only
    // property we allow you to change is the locked property.
    if (entity->getLocked()) {
        if (properties.lockedChanged()) {
            bool wantsLocked = properties.getLocked();
            if (!wantsLocked) {
                EntityItemProperties tempProperties;
                tempProperties.setLocked(wantsLocked);
                UpdateEntityOperator theOperator(this, containingElement, entity, tempProperties);
                recurseTreeWithOperator(&theOperator);
                _isDirty = true;
            }
        }
    } else {
        if (getIsServer()) {
            bool simulationBlocked = !entity->getSimulatorID().isNull();
            if (properties.simulatorIDChanged()) {
                QUuid submittedID = properties.getSimulatorID();
                // a legit interface will only submit their own ID or NULL:
                if (submittedID.isNull()) {
                    if (entity->getSimulatorID() == senderID) {
                        // We only allow the simulation owner to clear their own simulationID's.
                        simulationBlocked = false;
                    }
                    // else: We assume the sender really did believe it was the simulation owner when it sent
                } else if (submittedID == senderID) {
                    // the sender is trying to take or continue ownership
                    if (entity->getSimulatorID().isNull() || entity->getSimulatorID() == senderID) {
                        simulationBlocked = false;
                    } else {
                        // the sender is trying to steal ownership from another simulator
                        // so we apply the ownership change filter
                        if (usecTimestampNow() - entity->getSimulatorIDChangedTime() > SIMULATOR_CHANGE_LOCKOUT_PERIOD) {
                            simulationBlocked = false;
                        }
                    }
                } else {
                    // the entire update is suspect --> ignore it
                    return false;
                }
            }
            if (simulationBlocked) {
                // squash the physics-related changes.
                properties.setSimulatorIDChanged(false);
                properties.setPositionChanged(false);
                properties.setRotationChanged(false);
            }
        }
        // else client accepts what the server says

        QString entityScriptBefore = entity->getScript();
        QString collisionSoundURLBefore = entity->getCollisionSoundURL();
        uint32_t preFlags = entity->getDirtyFlags();
        UpdateEntityOperator theOperator(this, containingElement, entity, properties);
        recurseTreeWithOperator(&theOperator);
        _isDirty = true;

        uint32_t newFlags = entity->getDirtyFlags() & ~preFlags;
        if (newFlags) {
            if (_simulation) {
                if (newFlags & DIRTY_SIMULATION_FLAGS) {
                    _simulation->lock();
                    _simulation->changeEntity(entity);
                    _simulation->unlock();
                }
            } else {
                // normally the _simulation clears ALL updateFlags, but since there is none we do it explicitly
                entity->clearDirtyFlags();
            }
        }
        
        QString entityScriptAfter = entity->getScript();
        if (entityScriptBefore != entityScriptAfter) {
            emitEntityScriptChanging(entity->getEntityItemID()); // the entity script has changed
        }
        maybeNotifyNewCollisionSoundURL(collisionSoundURLBefore, entity->getCollisionSoundURL());
     }
    
    // TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG).
    containingElement = getContainingElement(entity->getEntityItemID());
    if (!containingElement) {
        qCDebug(entities) << "UNEXPECTED!!!! after updateEntity() we no longer have a containing element??? entityID=" 
                << entity->getEntityItemID();
        return false;
    }
    
    return true;
}