Merge branch 'master' of https://github.com/highfidelity/hifi into 20550-installOnMac

This commit is contained in:
NissimHadar 2019-01-17 12:19:04 -08:00
commit d6f2a0ebe3
21 changed files with 295 additions and 96 deletions

View file

@ -177,8 +177,9 @@ Rectangle {
repeat: true repeat: true
onTriggered: { onTriggered: {
var currentWearable = getCurrentWearable(); var currentWearable = getCurrentWearable();
var soft = currentWearable ? currentWearable.relayParentJoints : false; var hasSoft = currentWearable && currentWearable.relayParentJoints !== undefined;
var softEnabled = currentWearable ? entityHasAvatarJoints(currentWearable.id) : false; var soft = hasSoft ? currentWearable.relayParentJoints : false;
var softEnabled = hasSoft ? entityHasAvatarJoints(currentWearable.id) : false;
isSoft.set(soft); isSoft.set(soft);
isSoft.enabled = softEnabled; isSoft.enabled = softEnabled;
} }
@ -511,7 +512,7 @@ Rectangle {
function set(value) { function set(value) {
notify = false; notify = false;
checked = value checked = value;
notify = true; notify = true;
} }

View file

@ -6757,7 +6757,7 @@ void Application::updateWindowTitle() const {
DependencyManager::get< MessagesClient >()->sendLocalMessage("Toolbar-DomainChanged", ""); DependencyManager::get< MessagesClient >()->sendLocalMessage("Toolbar-DomainChanged", "");
} }
void Application::clearDomainOctreeDetails() { void Application::clearDomainOctreeDetails(bool clearAll) {
// before we delete all entities get MyAvatar's AvatarEntityData ready // before we delete all entities get MyAvatar's AvatarEntityData ready
getMyAvatar()->prepareAvatarEntityDataForReload(); getMyAvatar()->prepareAvatarEntityDataForReload();
@ -6776,7 +6776,7 @@ void Application::clearDomainOctreeDetails() {
}); });
// reset the model renderer // reset the model renderer
getEntities()->clear(); clearAll ? getEntities()->clear() : getEntities()->clearNonLocalEntities();
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage(); auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
@ -6814,7 +6814,7 @@ void Application::goToErrorDomainURL(QUrl errorDomainURL) {
void Application::resettingDomain() { void Application::resettingDomain() {
_notifiedPacketVersionMismatchThisDomain = false; _notifiedPacketVersionMismatchThisDomain = false;
clearDomainOctreeDetails(); clearDomainOctreeDetails(false);
} }
void Application::nodeAdded(SharedNodePointer node) const { void Application::nodeAdded(SharedNodePointer node) const {
@ -6900,7 +6900,7 @@ void Application::nodeKilled(SharedNodePointer node) {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled"); QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled");
} else if (node->getType() == NodeType::EntityServer) { } else if (node->getType() == NodeType::EntityServer) {
// we lost an entity server, clear all of the domain octree details // we lost an entity server, clear all of the domain octree details
clearDomainOctreeDetails(); clearDomainOctreeDetails(false);
} else if (node->getType() == NodeType::AssetServer) { } else if (node->getType() == NodeType::AssetServer) {
// asset server going away - check if we have the asset browser showing // asset server going away - check if we have the asset browser showing

View file

@ -469,7 +469,7 @@ private slots:
void onDesktopRootItemCreated(QQuickItem* qmlContext); void onDesktopRootItemCreated(QQuickItem* qmlContext);
void onDesktopRootContextCreated(QQmlContext* qmlContext); void onDesktopRootContextCreated(QQmlContext* qmlContext);
void showDesktop(); void showDesktop();
void clearDomainOctreeDetails(); void clearDomainOctreeDetails(bool clearAll = true);
void onAboutToQuit(); void onAboutToQuit();
void onPresent(quint32 frameCount); void onPresent(quint32 frameCount);

View file

@ -55,7 +55,7 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
QVariantMap asMap = variantProperties.toMap(); QVariantMap asMap = variantProperties.toMap();
QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine);
EntityItemProperties entityProperties; EntityItemProperties entityProperties;
EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, entityProperties); EntityItemPropertiesFromScriptValueIgnoreReadOnly(scriptProperties, entityProperties);
entityProperties.setParentID(myNodeID); entityProperties.setParentID(myNodeID);
entityProperties.setEntityHostType(entity::HostType::AVATAR); entityProperties.setEntityHostType(entity::HostType::AVATAR);
@ -151,8 +151,29 @@ void AvatarBookmarks::removeBookmark(const QString& bookmarkName) {
void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
myAvatar->removeWearableAvatarEntities(); auto currentAvatarEntities = myAvatar->getAvatarEntityData();
addAvatarEntities(avatarEntities); std::set<QUuid> newAvatarEntities;
// Update or add all the new avatar entities
for (auto& avatarEntityVariant : avatarEntities) {
auto avatarEntityVariantMap = avatarEntityVariant.toMap();
auto idItr = avatarEntityVariantMap.find("id");
if (idItr != avatarEntityVariantMap.end()) {
auto propertiesItr = avatarEntityVariantMap.find("properties");
if (propertiesItr != avatarEntityVariantMap.end()) {
EntityItemID id = idItr.value().toUuid();
newAvatarEntities.insert(id);
myAvatar->updateAvatarEntity(id, QJsonDocument::fromVariant(propertiesItr.value()).toBinaryData());
}
}
}
// Remove any old entities not in the new list
for (auto& avatarEntityID : currentAvatarEntities.keys()) {
if (newAvatarEntities.find(avatarEntityID) == newAvatarEntities.end()) {
myAvatar->removeWornAvatarEntity(avatarEntityID);
}
}
} }
void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { void AvatarBookmarks::loadBookmark(const QString& bookmarkName) {
@ -176,7 +197,7 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>(); auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
myAvatar->removeWearableAvatarEntities(); myAvatar->clearWornAvatarEntities();
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
myAvatar->useFullAvatarURL(avatarUrl); myAvatar->useFullAvatarURL(avatarUrl);
qCDebug(interfaceapp) << "Avatar On"; qCDebug(interfaceapp) << "Avatar On";

View file

@ -1610,10 +1610,12 @@ void MyAvatar::handleChangedAvatarEntityData() {
skip = true; skip = true;
} }
}); });
sanitizeAvatarEntityProperties(properties); if (!skip) {
entityTree->withWriteLock([&] { sanitizeAvatarEntityProperties(properties);
entityTree->updateEntity(id, properties); entityTree->withWriteLock([&] {
}); entityTree->updateEntity(id, properties);
});
}
} }
// DELETE cached blobs // DELETE cached blobs
@ -1810,19 +1812,27 @@ void MyAvatar::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) {
void MyAvatar::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) { void MyAvatar::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) {
// NOTE: this is an invokable Script call // NOTE: this is an invokable Script call
// TODO: we should handle the case where entityData is corrupt or invalid bool changed = false;
// BEFORE we store into _cachedAvatarEntityBlobs
_needToSaveAvatarEntitySettings = true;
_avatarEntitiesLock.withWriteLock([&] { _avatarEntitiesLock.withWriteLock([&] {
AvatarEntityMap::iterator itr = _cachedAvatarEntityBlobs.find(entityID); auto data = QJsonDocument::fromBinaryData(entityData);
if (itr != _cachedAvatarEntityBlobs.end()) { if (data.isEmpty() || data.isNull() || !data.isObject() || data.object().isEmpty()) {
_entitiesToUpdate.push_back(entityID); qDebug() << "ERROR! Trying to update with invalid avatar entity data. Skipping." << data;
itr.value() = entityData;
} else { } else {
_entitiesToAdd.push_back(entityID); auto itr = _cachedAvatarEntityBlobs.find(entityID);
_cachedAvatarEntityBlobs.insert(entityID, entityData); if (itr == _cachedAvatarEntityBlobs.end()) {
_entitiesToAdd.push_back(entityID);
_cachedAvatarEntityBlobs.insert(entityID, entityData);
changed = true;
} else {
_entitiesToUpdate.push_back(entityID);
itr.value() = entityData;
changed = true;
}
} }
}); });
if (changed) {
_needToSaveAvatarEntitySettings = true;
}
} }
void MyAvatar::avatarEntityDataToJson(QJsonObject& root) const { void MyAvatar::avatarEntityDataToJson(QJsonObject& root) const {
@ -2362,50 +2372,36 @@ bool isWearableEntity(const EntityItemPointer& entity) {
|| entity->getParentID() == AVATAR_SELF_ID); || entity->getParentID() == AVATAR_SELF_ID);
} }
void MyAvatar::clearAvatarEntities() { void MyAvatar::removeWornAvatarEntity(const EntityItemID& entityID) {
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>(); auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
QList<QUuid> avatarEntityIDs;
_avatarEntitiesLock.withReadLock([&] {
avatarEntityIDs = _packedAvatarEntityData.keys();
});
for (const auto& entityID : avatarEntityIDs) {
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);
}
}
void MyAvatar::removeWearableAvatarEntities() {
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
if (entityTree) { if (entityTree) {
QList<QUuid> avatarEntityIDs; auto entity = entityTree->findEntityByID(entityID);
_avatarEntitiesLock.withReadLock([&] { if (entity && isWearableEntity(entity)) {
avatarEntityIDs = _packedAvatarEntityData.keys(); entityTree->withWriteLock([&entityID, &entityTree] {
}); // remove this entity first from the entity tree
for (const auto& entityID : avatarEntityIDs) { entityTree->deleteEntity(entityID, true, true);
auto entity = entityTree->findEntityByID(entityID); });
if (entity && isWearableEntity(entity)) {
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 // remove the avatar entity from our internal list
// (but indicate it doesn't need to be pulled from the tree) // (but indicate it doesn't need to be pulled from the tree)
clearAvatarEntity(entityID, false); clearAvatarEntity(entityID, false);
}
} }
} }
} }
void MyAvatar::clearWornAvatarEntities() {
QList<QUuid> avatarEntityIDs;
_avatarEntitiesLock.withReadLock([&] {
avatarEntityIDs = _packedAvatarEntityData.keys();
});
for (auto entityID : avatarEntityIDs) {
removeWornAvatarEntity(entityID);
}
}
QVariantList MyAvatar::getAvatarEntitiesVariant() { QVariantList MyAvatar::getAvatarEntitiesVariant() {
// NOTE: this method is NOT efficient // NOTE: this method is NOT efficient
QVariantList avatarEntitiesData; QVariantList avatarEntitiesData;
@ -2428,6 +2424,7 @@ QVariantList MyAvatar::getAvatarEntitiesVariant() {
desiredProperties += PROP_LOCAL_ROTATION; desiredProperties += PROP_LOCAL_ROTATION;
EntityItemProperties entityProperties = entity->getProperties(desiredProperties); EntityItemProperties entityProperties = entity->getProperties(desiredProperties);
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(_myScriptEngine, entityProperties); QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(_myScriptEngine, entityProperties);
avatarEntityData["id"] = entityID;
avatarEntityData["properties"] = scriptProperties.toVariant(); avatarEntityData["properties"] = scriptProperties.toVariant();
avatarEntitiesData.append(QVariant(avatarEntityData)); avatarEntitiesData.append(QVariant(avatarEntityData));
} }
@ -2806,8 +2803,8 @@ void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData)
newEntitiesProperties.push_back(properties); newEntitiesProperties.push_back(properties);
} }
// clear any existing avatar entities // clear any existing wearables
clearAvatarEntities(); clearWornAvatarEntities();
for (auto& properties : newEntitiesProperties) { for (auto& properties : newEntitiesProperties) {
DependencyManager::get<EntityScriptingInterface>()->addEntity(properties, true); DependencyManager::get<EntityScriptingInterface>()->addEntity(properties, true);

View file

@ -970,8 +970,8 @@ public:
* @returns {object[]} * @returns {object[]}
*/ */
Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); Q_INVOKABLE QVariantList getAvatarEntitiesVariant();
void clearAvatarEntities(); void removeWornAvatarEntity(const EntityItemID& entityID);
void removeWearableAvatarEntities(); void clearWornAvatarEntities();
/**jsdoc /**jsdoc
* Check whether your avatar is flying or not. * Check whether your avatar is flying or not.

View file

@ -2744,23 +2744,28 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) {
} }
void AvatarData::storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& data) { void AvatarData::storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& data) {
bool changed = false;
_avatarEntitiesLock.withWriteLock([&] { _avatarEntitiesLock.withWriteLock([&] {
PackedAvatarEntityMap::iterator itr = _packedAvatarEntityData.find(entityID); auto itr = _packedAvatarEntityData.find(entityID);
if (itr == _packedAvatarEntityData.end()) { if (itr == _packedAvatarEntityData.end()) {
if (_packedAvatarEntityData.size() < MAX_NUM_AVATAR_ENTITIES) { if (_packedAvatarEntityData.size() < MAX_NUM_AVATAR_ENTITIES) {
_packedAvatarEntityData.insert(entityID, data); _packedAvatarEntityData.insert(entityID, data);
changed = true;
} }
} else { } else {
itr.value() = data; itr.value() = data;
changed = true;
} }
}); });
_avatarEntityDataChanged = true; if (changed) {
_avatarEntityDataChanged = true;
if (_clientTraitsHandler) { if (_clientTraitsHandler) {
// we have a client traits handler, so we need to mark this instanced trait as changed // we have a client traits handler, so we need to mark this instanced trait as changed
// so that changes will be sent next frame // so that changes will be sent next frame
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID); _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID);
}
} }
} }

View file

@ -197,9 +197,57 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() {
}); });
} }
void EntityTreeRenderer::stopNonLocalEntityScripts() {
leaveNonLocalEntities();
// unload and stop the engine
if (_entitiesScriptEngine) {
QList<EntityItemID> entitiesWithEntityScripts = _entitiesScriptEngine->getListOfEntityScriptIDs();
foreach (const EntityItemID& entityID, entitiesWithEntityScripts) {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
if (entityItem) {
if (!entityItem->isLocalEntity()) {
_entitiesScriptEngine->unloadEntityScript(entityID, true);
}
}
}
}
}
void EntityTreeRenderer::clearNonLocalEntities() {
stopNonLocalEntityScripts();
std::unordered_map<EntityItemID, EntityRendererPointer> savedEntities;
// remove all entities from the scene
_space->clear();
auto scene = _viewState->getMain3DScene();
if (scene) {
render::Transaction transaction;
for (const auto& entry : _entitiesInScene) {
const auto& renderer = entry.second;
const EntityItemPointer& entityItem = renderer->getEntity();
if (!entityItem->isLocalEntity()) {
renderer->removeFromScene(scene, transaction);
} else {
savedEntities[entry.first] = entry.second;
}
}
scene->enqueueTransaction(transaction);
} else {
qCWarning(entitiesrenderer) << "EntitityTreeRenderer::clear(), Unexpected null scene, possibly during application shutdown";
}
_renderablesToUpdate = savedEntities;
_entitiesInScene = savedEntities;
_layeredZones.clearNonLocalLayeredZones();
OctreeProcessor::clearNonLocalEntities();
}
void EntityTreeRenderer::clear() { void EntityTreeRenderer::clear() {
leaveAllEntities(); leaveAllEntities();
// unload and stop the engine // unload and stop the engine
if (_entitiesScriptEngine) { if (_entitiesScriptEngine) {
// do this here (instead of in deleter) to avoid marshalling unload signals back to this thread // do this here (instead of in deleter) to avoid marshalling unload signals back to this thread
@ -211,8 +259,8 @@ void EntityTreeRenderer::clear() {
if (_wantScripts && !_shuttingDown) { if (_wantScripts && !_shuttingDown) {
resetEntitiesScriptEngine(); resetEntitiesScriptEngine();
} }
// remove all entities from the scene // remove all entities from the scene
_space->clear(); _space->clear();
auto scene = _viewState->getMain3DScene(); auto scene = _viewState->getMain3DScene();
if (scene) { if (scene) {
@ -507,8 +555,7 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityIt
auto entityTree = std::static_pointer_cast<EntityTree>(_tree); auto entityTree = std::static_pointer_cast<EntityTree>(_tree);
// FIXME - if EntityTree had a findEntitiesContainingPoint() this could theoretically be a little faster // FIXME - if EntityTree had a findEntitiesContainingPoint() this could theoretically be a little faster
entityTree->evalEntitiesInSphere(_avatarPosition, radius, entityTree->evalEntitiesInSphere(_avatarPosition, radius, PickFilter(), entityIDs);
PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)), entityIDs);
LayeredZones oldLayeredZones(std::move(_layeredZones)); LayeredZones oldLayeredZones(std::move(_layeredZones));
_layeredZones.clear(); _layeredZones.clear();
@ -614,6 +661,26 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
return didUpdate; return didUpdate;
} }
void EntityTreeRenderer::leaveNonLocalEntities() {
if (_tree && !_shuttingDown) {
QVector<EntityItemID> currentLocalEntitiesInside;
foreach (const EntityItemID& entityID, _currentEntitiesInside) {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
if (!entityItem->isLocalEntity()) {
emit leaveEntity(entityID);
if (_entitiesScriptEngine) {
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
}
} else {
currentLocalEntitiesInside.push_back(entityID);
}
}
_currentEntitiesInside = currentLocalEntitiesInside;
forceRecheckEntities();
}
}
void EntityTreeRenderer::leaveAllEntities() { void EntityTreeRenderer::leaveAllEntities() {
if (_tree && !_shuttingDown) { if (_tree && !_shuttingDown) {
@ -1136,6 +1203,29 @@ EntityTreeRenderer::LayeredZones::LayeredZones(LayeredZones&& other) {
} }
} }
void EntityTreeRenderer::LayeredZones::clearNonLocalLayeredZones() {
std::set<LayeredZone> localLayeredZones;
std::map<QUuid, iterator> newMap;
for (auto iter = begin(); iter != end(); iter++) {
LayeredZone layeredZone = *iter;
if (layeredZone.zone->isLocalEntity()) {
bool success;
iterator it;
std::tie(it, success) = localLayeredZones.insert(layeredZone);
if (success) {
newMap.emplace(layeredZone.id, it);
}
}
}
std::set<LayeredZone>::operator=(localLayeredZones);
_map = newMap;
_skyboxLayer = empty() ? end() : begin();
}
void EntityTreeRenderer::LayeredZones::clear() { void EntityTreeRenderer::LayeredZones::clear() {
std::set<LayeredZone>::clear(); std::set<LayeredZone>::clear();
_map.clear(); _map.clear();

View file

@ -86,6 +86,7 @@ public:
virtual void init() override; virtual void init() override;
/// clears the tree /// clears the tree
virtual void clearNonLocalEntities() override;
virtual void clear() override; virtual void clear() override;
/// reloads the entity scripts, calling unload and preload /// reloads the entity scripts, calling unload and preload
@ -161,6 +162,7 @@ private:
bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr); bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);
bool applyLayeredZones(); bool applyLayeredZones();
void stopNonLocalEntityScripts();
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false); void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false);
@ -169,6 +171,7 @@ private:
QScriptValueList createEntityArgs(const EntityItemID& entityID); QScriptValueList createEntityArgs(const EntityItemID& entityID);
bool checkEnterLeaveEntities(); bool checkEnterLeaveEntities();
void leaveNonLocalEntities();
void leaveAllEntities(); void leaveAllEntities();
void forceRecheckEntities(); void forceRecheckEntities();
@ -219,6 +222,7 @@ private:
LayeredZones& operator=(LayeredZones&&) = delete; LayeredZones& operator=(LayeredZones&&) = delete;
void clear(); void clear();
void clearNonLocalLayeredZones();
std::pair<iterator, bool> insert(const LayeredZone& layer); std::pair<iterator, bool> insert(const LayeredZone& layer);
void update(std::shared_ptr<ZoneEntityItem> zone); void update(std::shared_ptr<ZoneEntityItem> zone);
bool contains(const LayeredZones& other); bool contains(const LayeredZones& other);

View file

@ -4616,14 +4616,14 @@ bool EntityItemProperties::blobToProperties(QScriptEngine& scriptEngine, const Q
// DANGER: this method is NOT efficient. // DANGER: this method is NOT efficient.
// begin recipe for converting unfortunately-formatted-binary-blob to EntityItemProperties // begin recipe for converting unfortunately-formatted-binary-blob to EntityItemProperties
QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(blob); QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(blob);
if (!jsonProperties.isObject()) { if (jsonProperties.isEmpty() || jsonProperties.isNull() || !jsonProperties.isObject() || jsonProperties.object().isEmpty()) {
qCDebug(entities) << "bad avatarEntityData json" << QString(blob.toHex()); qCDebug(entities) << "bad avatarEntityData json" << QString(blob.toHex());
return false; return false;
} }
QVariant variant = jsonProperties.toVariant(); QVariant variant = jsonProperties.toVariant();
QVariantMap variantMap = variant.toMap(); QVariantMap variantMap = variant.toMap();
QScriptValue scriptValue = variantMapToScriptValue(variantMap, scriptEngine); QScriptValue scriptValue = variantMapToScriptValue(variantMap, scriptEngine);
EntityItemPropertiesFromScriptValueHonorReadOnly(scriptValue, properties); EntityItemPropertiesFromScriptValueIgnoreReadOnly(scriptValue, properties);
// end recipe // end recipe
return true; return true;
} }

View file

@ -986,7 +986,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
shouldSendDeleteToServer = false; shouldSendDeleteToServer = false;
} else { } else {
// only delete local entities, server entities will round trip through the server filters // only delete local entities, server entities will round trip through the server filters
if (entity->isAvatarEntity() || _entityTree->isServerlessMode()) { if (!entity->isDomainEntity() || _entityTree->isServerlessMode()) {
shouldSendDeleteToServer = false; shouldSendDeleteToServer = false;
_entityTree->deleteEntity(entityID); _entityTree->deleteEntity(entityID);

View file

@ -70,6 +70,49 @@ OctreeElementPointer EntityTree::createNewElement(unsigned char* octalCode) {
return std::static_pointer_cast<OctreeElement>(newElement); return std::static_pointer_cast<OctreeElement>(newElement);
} }
void EntityTree::eraseNonLocalEntities() {
emit clearingEntities();
if (_simulation) {
// This will clear all entities host types including local entities, because local entities
// are not in the physics simulation
_simulation->clearEntities();
}
_staleProxies.clear();
QHash<EntityItemID, EntityItemPointer> localMap;
localMap.swap(_entityMap);
QHash<EntityItemID, EntityItemPointer> savedEntities;
this->withWriteLock([&] {
foreach(EntityItemPointer entity, localMap) {
EntityTreeElementPointer element = entity->getElement();
if (element) {
element->cleanupNonLocalEntities();
}
if (entity->isLocalEntity()) {
savedEntities[entity->getEntityItemID()] = entity;
}
}
});
localMap.clear();
_entityMap = savedEntities;
resetClientEditStats();
clearDeletedEntities();
{
QWriteLocker locker(&_needsParentFixupLock);
QVector<EntityItemWeakPointer> localEntitiesNeedsParentFixup;
foreach (EntityItemWeakPointer entityItem, _needsParentFixup) {
if (!entityItem.expired() && entityItem.lock()->isLocalEntity()) {
localEntitiesNeedsParentFixup.push_back(entityItem);
}
}
_needsParentFixup = localEntitiesNeedsParentFixup;
}
}
void EntityTree::eraseAllOctreeElements(bool createNewRoot) { void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
emit clearingEntities(); emit clearingEntities();

View file

@ -74,6 +74,8 @@ public:
return std::static_pointer_cast<EntityTreeElement>(_rootElement); return std::static_pointer_cast<EntityTreeElement>(_rootElement);
} }
virtual void eraseNonLocalEntities() override;
virtual void eraseAllOctreeElements(bool createNewRoot = true) override; virtual void eraseAllOctreeElements(bool createNewRoot = true) override;
virtual void readBitstreamToTree(const unsigned char* bitstream, virtual void readBitstreamToTree(const unsigned char* bitstream,

View file

@ -683,6 +683,23 @@ EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemI
return foundEntity; return foundEntity;
} }
void EntityTreeElement::cleanupNonLocalEntities() {
withWriteLock([&] {
EntityItems savedEntities;
foreach(EntityItemPointer entity, _entityItems) {
if (!entity->isLocalEntity()) {
entity->preDelete();
entity->_element = NULL;
} else {
savedEntities.push_back(entity);
}
}
_entityItems = savedEntities;
});
bumpChangedContent();
}
void EntityTreeElement::cleanupEntities() { void EntityTreeElement::cleanupEntities() {
withWriteLock([&] { withWriteLock([&] {
foreach(EntityItemPointer entity, _entityItems) { foreach(EntityItemPointer entity, _entityItems) {

View file

@ -189,6 +189,7 @@ public:
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const; EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const;
void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities); void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities);
void cleanupNonLocalEntities();
void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities
bool removeEntityItem(EntityItemPointer entity, bool deletion = false); bool removeEntityItem(EntityItemPointer entity, bool deletion = false);

View file

@ -149,6 +149,7 @@ public:
OctreeElementPointer getRoot() { return _rootElement; } OctreeElementPointer getRoot() { return _rootElement; }
virtual void eraseNonLocalEntities() { _isDirty = true; };
virtual void eraseAllOctreeElements(bool createNewRoot = true); virtual void eraseAllOctreeElements(bool createNewRoot = true);
virtual void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args); virtual void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args);

View file

@ -51,15 +51,15 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showTimingDetails, "OctreeProcessor::processDatagram()", showTimingDetails); PerformanceWarning warn(showTimingDetails, "OctreeProcessor::processDatagram()", showTimingDetails);
if (message.getType() == getExpectedPacketType()) { if (message.getType() == getExpectedPacketType()) {
PerformanceWarning warn(showTimingDetails, "OctreeProcessor::processDatagram expected PacketType", showTimingDetails); PerformanceWarning warn(showTimingDetails, "OctreeProcessor::processDatagram expected PacketType", showTimingDetails);
// if we are getting inbound packets, then our tree is also viewing, and we should remember that fact. // if we are getting inbound packets, then our tree is also viewing, and we should remember that fact.
_tree->setIsViewing(true); _tree->setIsViewing(true);
OCTREE_PACKET_FLAGS flags; OCTREE_PACKET_FLAGS flags;
message.readPrimitive(&flags); message.readPrimitive(&flags);
OCTREE_PACKET_SEQUENCE sequence; OCTREE_PACKET_SEQUENCE sequence;
message.readPrimitive(&sequence); message.readPrimitive(&sequence);
@ -68,7 +68,7 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT); bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT);
bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT); bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
qint64 clockSkew = sourceNode ? sourceNode->getClockSkewUsec() : 0; qint64 clockSkew = sourceNode ? sourceNode->getClockSkewUsec() : 0;
qint64 flightTime = arrivedAt - sentAt + clockSkew; qint64 flightTime = arrivedAt - sentAt + clockSkew;
@ -79,27 +79,27 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
qCDebug(octree) << "OctreeProcessor::processDatagram() ... " qCDebug(octree) << "OctreeProcessor::processDatagram() ... "
"Got Packet Section color:" << packetIsColored << "Got Packet Section color:" << packetIsColored <<
"compressed:" << packetIsCompressed << "compressed:" << packetIsCompressed <<
"sequence: " << sequence << "sequence: " << sequence <<
"flight: " << flightTime << " usec" << "flight: " << flightTime << " usec" <<
"size:" << message.getSize() << "size:" << message.getSize() <<
"data:" << message.getBytesLeftToRead(); "data:" << message.getBytesLeftToRead();
} }
_packetsInLastWindow++; _packetsInLastWindow++;
int elementsPerPacket = 0; int elementsPerPacket = 0;
int entitiesPerPacket = 0; int entitiesPerPacket = 0;
quint64 totalWaitingForLock = 0; quint64 totalWaitingForLock = 0;
quint64 totalUncompress = 0; quint64 totalUncompress = 0;
quint64 totalReadBitsteam = 0; quint64 totalReadBitsteam = 0;
const QUuid& sourceUUID = sourceNode->getUUID(); const QUuid& sourceUUID = sourceNode->getUUID();
int subsection = 1; int subsection = 1;
bool error = false; bool error = false;
while (message.getBytesLeftToRead() > 0 && !error) { while (message.getBytesLeftToRead() > 0 && !error) {
if (packetIsCompressed) { if (packetIsCompressed) {
if (message.getBytesLeftToRead() > (qint64) sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE)) { if (message.getBytesLeftToRead() > (qint64) sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE)) {
@ -111,7 +111,7 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
} else { } else {
sectionLength = message.getBytesLeftToRead(); sectionLength = message.getBytesLeftToRead();
} }
if (sectionLength) { if (sectionLength) {
// ask the VoxelTree to read the bitstream into the tree // ask the VoxelTree to read the bitstream into the tree
ReadBitstreamToTreeParams args(WANT_EXISTS_BITS, NULL, ReadBitstreamToTreeParams args(WANT_EXISTS_BITS, NULL,
@ -149,7 +149,7 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
qCDebug(octree) << "OctreeProcessor::processDatagram() ******* END _tree->readBitstreamToTree()..."; qCDebug(octree) << "OctreeProcessor::processDatagram() ******* END _tree->readBitstreamToTree()...";
} }
}); });
// seek forwards in packet // seek forwards in packet
message.seek(message.getPosition() + sectionLength); message.seek(message.getPosition() + sectionLength);
@ -172,13 +172,13 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
_waitLockPerPacket.updateAverage(totalWaitingForLock); _waitLockPerPacket.updateAverage(totalWaitingForLock);
_uncompressPerPacket.updateAverage(totalUncompress); _uncompressPerPacket.updateAverage(totalUncompress);
_readBitstreamPerPacket.updateAverage(totalReadBitsteam); _readBitstreamPerPacket.updateAverage(totalReadBitsteam);
quint64 now = usecTimestampNow(); quint64 now = usecTimestampNow();
if (_lastWindowAt == 0) { if (_lastWindowAt == 0) {
_lastWindowAt = now; _lastWindowAt = now;
} }
quint64 sinceLastWindow = now - _lastWindowAt; quint64 sinceLastWindow = now - _lastWindowAt;
if (sinceLastWindow > USECS_PER_SECOND) { if (sinceLastWindow > USECS_PER_SECOND) {
float packetsPerSecondInWindow = (float)_packetsInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND); float packetsPerSecondInWindow = (float)_packetsInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND);
float elementsPerSecondInWindow = (float)_elementsInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND); float elementsPerSecondInWindow = (float)_elementsInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND);
@ -197,6 +197,14 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
} }
} }
void OctreeProcessor::clearNonLocalEntities() {
if (_tree) {
_tree->withWriteLock([&] {
_tree->eraseNonLocalEntities();
});
}
}
void OctreeProcessor::clear() { void OctreeProcessor::clear() {
if (_tree) { if (_tree) {
_tree->withWriteLock([&] { _tree->withWriteLock([&] {

View file

@ -43,6 +43,7 @@ public:
virtual void init(); virtual void init();
/// clears the tree /// clears the tree
virtual void clearNonLocalEntities();
virtual void clear(); virtual void clear();
float getAverageElementsPerPacket() const { return _elementsPerPacket.getAverage(); } float getAverageElementsPerPacket() const { return _elementsPerPacket.getAverage(); }

View file

@ -2407,6 +2407,11 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR
} }
} }
QList<EntityItemID> ScriptEngine::getListOfEntityScriptIDs() {
QReadLocker locker{ &_entityScriptsLock };
return _entityScripts.keys();
}
void ScriptEngine::unloadAllEntityScripts() { void ScriptEngine::unloadAllEntityScripts() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING

View file

@ -145,6 +145,9 @@ public:
QString getFilename() const; QString getFilename() const;
QList<EntityItemID> getListOfEntityScriptIDs();
/**jsdoc /**jsdoc
* Stop the current script. * Stop the current script.
* @function Script.stop * @function Script.stop

View file

@ -70,9 +70,9 @@ function getMyAvatarSettings() {
} }
} }
function updateAvatarWearables(avatar, callback) { function updateAvatarWearables(avatar, callback, wearablesOverride) {
executeLater(function() { executeLater(function() {
var wearables = getMyAvatarWearables(); var wearables = wearablesOverride ? wearablesOverride : getMyAvatarWearables();
avatar[ENTRY_AVATAR_ENTITIES] = wearables; avatar[ENTRY_AVATAR_ENTITIES] = wearables;
sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables}) sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables})
@ -210,7 +210,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
break; break;
case 'adjustWearable': case 'adjustWearable':
if(message.properties.localRotationAngles) { if(message.properties.localRotationAngles) {
message.properties.localRotation = Quat.fromVec3Degrees(message.properties.localRotationAngles) message.properties.localRotation = Quat.fromVec3Degrees(message.properties.localRotationAngles);
} }
Entities.editEntity(message.entityID, message.properties); Entities.editEntity(message.entityID, message.properties);
@ -235,7 +235,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
// revert changes using snapshot of wearables // revert changes using snapshot of wearables
if(currentAvatarWearablesBackup !== null) { if(currentAvatarWearablesBackup !== null) {
AvatarBookmarks.updateAvatarEntities(currentAvatarWearablesBackup); AvatarBookmarks.updateAvatarEntities(currentAvatarWearablesBackup);
updateAvatarWearables(currentAvatar); updateAvatarWearables(currentAvatar, null, currentAvatarWearablesBackup);
} }
} else { } else {
sendToQml({'method' : 'updateAvatarInBookmarks'}); sendToQml({'method' : 'updateAvatarInBookmarks'});