mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 04:24:47 +02:00
Merge pull request #12571 from Atlante45/fix/es-concurrent-traversal
Fix unprotected tree traversals
This commit is contained in:
commit
7f285c5f90
4 changed files with 65 additions and 34 deletions
|
@ -363,7 +363,9 @@ void EntityServer::nodeAdded(SharedNodePointer node) {
|
||||||
|
|
||||||
void EntityServer::nodeKilled(SharedNodePointer node) {
|
void EntityServer::nodeKilled(SharedNodePointer node) {
|
||||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||||
tree->deleteDescendantsOfAvatar(node->getUUID());
|
tree->withWriteLock([&] {
|
||||||
|
tree->deleteDescendantsOfAvatar(node->getUUID());
|
||||||
|
});
|
||||||
tree->forgetAvatarID(node->getUUID());
|
tree->forgetAvatarID(node->getUUID());
|
||||||
OctreeServer::nodeKilled(node);
|
OctreeServer::nodeKilled(node);
|
||||||
}
|
}
|
||||||
|
@ -451,8 +453,6 @@ void EntityServer::domainSettingsRequestFailed() {
|
||||||
void EntityServer::startDynamicDomainVerification() {
|
void EntityServer::startDynamicDomainVerification() {
|
||||||
qCDebug(entities) << "Starting Dynamic Domain Verification...";
|
qCDebug(entities) << "Starting Dynamic Domain Verification...";
|
||||||
|
|
||||||
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
|
|
||||||
|
|
||||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||||
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());
|
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());
|
||||||
|
|
||||||
|
@ -460,15 +460,19 @@ void EntityServer::startDynamicDomainVerification() {
|
||||||
qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap";
|
qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap";
|
||||||
while (i.hasNext()) {
|
while (i.hasNext()) {
|
||||||
i.next();
|
i.next();
|
||||||
|
const auto& certificateID = i.key();
|
||||||
|
const auto& entityID = i.value();
|
||||||
|
|
||||||
EntityItemPointer entity = tree->findEntityByEntityItemID(i.value());
|
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
|
||||||
|
|
||||||
if (entity) {
|
if (entity) {
|
||||||
if (!entity->getProperties().verifyStaticCertificateProperties()) {
|
if (!entity->getProperties().verifyStaticCertificateProperties()) {
|
||||||
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed"
|
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << entityID << "failed"
|
||||||
<< "static certificate verification.";
|
<< "static certificate verification.";
|
||||||
// Delete the entity if it doesn't pass static certificate verification
|
// Delete the entity if it doesn't pass static certificate verification
|
||||||
tree->deleteEntity(i.value(), true);
|
tree->withWriteLock([&] {
|
||||||
|
tree->deleteEntity(entityID, true);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
QNetworkRequest networkRequest;
|
QNetworkRequest networkRequest;
|
||||||
|
@ -477,39 +481,46 @@ void EntityServer::startDynamicDomainVerification() {
|
||||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
|
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
|
||||||
QJsonObject request;
|
QJsonObject request;
|
||||||
request["certificate_id"] = i.key();
|
request["certificate_id"] = certificateID;
|
||||||
networkRequest.setUrl(requestURL);
|
networkRequest.setUrl(requestURL);
|
||||||
|
|
||||||
QNetworkReply* networkReply = NULL;
|
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||||
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
|
||||||
|
connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] {
|
||||||
|
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||||
|
|
||||||
connect(networkReply, &QNetworkReply::finished, [=]() {
|
|
||||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||||
jsonObject = jsonObject["data"].toObject();
|
jsonObject = jsonObject["data"].toObject();
|
||||||
|
|
||||||
if (networkReply->error() == QNetworkReply::NoError) {
|
if (networkReply->error() == QNetworkReply::NoError) {
|
||||||
|
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
|
||||||
if (jsonObject["domain_id"].toString() != thisDomainID) {
|
if (jsonObject["domain_id"].toString() != thisDomainID) {
|
||||||
|
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
|
||||||
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
|
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
|
||||||
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
||||||
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value();
|
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;
|
||||||
tree->deleteEntity(i.value(), true);
|
tree->withWriteLock([&] {
|
||||||
|
tree->deleteEntity(entityID, true);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << i.value();
|
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value();
|
qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << i.value()
|
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityID
|
||||||
<< "More info:" << jsonObject;
|
<< "More info:" << jsonObject;
|
||||||
tree->deleteEntity(i.value(), true);
|
tree->withWriteLock([&] {
|
||||||
|
tree->deleteEntity(entityID, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
networkReply->deleteLater();
|
networkReply->deleteLater();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCWarning(entities) << "During DDV, an entity with ID" << i.value() << "was NOT found in the Entity Tree!";
|
qCWarning(entities) << "During DDV, an entity with ID" << entityID << "was NOT found in the Entity Tree!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -400,7 +400,9 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
||||||
if (shouldTraverseAndSend(nodeData)) {
|
if (shouldTraverseAndSend(nodeData)) {
|
||||||
quint64 start = usecTimestampNow();
|
quint64 start = usecTimestampNow();
|
||||||
|
|
||||||
traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
|
_myServer->getOctree()->withReadLock([&]{
|
||||||
|
traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
|
||||||
|
});
|
||||||
|
|
||||||
// Here's where we can/should allow the server to send other data...
|
// Here's where we can/should allow the server to send other data...
|
||||||
// send the environment packet
|
// send the environment packet
|
||||||
|
|
|
@ -1155,7 +1155,9 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID)
|
||||||
});
|
});
|
||||||
connect(_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() {
|
connect(_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() {
|
||||||
qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID;
|
qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID;
|
||||||
deleteEntity(entityItemID, true);
|
withWriteLock([&] {
|
||||||
|
deleteEntity(entityItemID, true);
|
||||||
|
});
|
||||||
if (_challengeOwnershipTimeoutTimer) {
|
if (_challengeOwnershipTimeoutTimer) {
|
||||||
_challengeOwnershipTimeoutTimer->stop();
|
_challengeOwnershipTimeoutTimer->stop();
|
||||||
_challengeOwnershipTimeoutTimer->deleteLater();
|
_challengeOwnershipTimeoutTimer->deleteLater();
|
||||||
|
@ -1263,7 +1265,9 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
|
||||||
|
|
||||||
if (text == "") {
|
if (text == "") {
|
||||||
qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity...";
|
qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity...";
|
||||||
deleteEntity(entityItemID, true);
|
withWriteLock([&] {
|
||||||
|
deleteEntity(entityItemID, true);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "Challenging ownership of Cert ID" << certID;
|
qCDebug(entities) << "Challenging ownership of Cert ID" << certID;
|
||||||
// 2. Send the nonce to the rezzing avatar's node
|
// 2. Send the nonce to the rezzing avatar's node
|
||||||
|
@ -1326,8 +1330,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
|
||||||
request["certificate_id"] = certID;
|
request["certificate_id"] = certID;
|
||||||
networkRequest.setUrl(requestURL);
|
networkRequest.setUrl(requestURL);
|
||||||
|
|
||||||
QNetworkReply* networkReply = NULL;
|
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||||
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
|
||||||
|
|
||||||
connect(networkReply, &QNetworkReply::finished, [=]() {
|
connect(networkReply, &QNetworkReply::finished, [=]() {
|
||||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||||
|
@ -1336,14 +1339,20 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
|
||||||
if (networkReply->error() == QNetworkReply::NoError) {
|
if (networkReply->error() == QNetworkReply::NoError) {
|
||||||
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
|
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
|
||||||
qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID;
|
qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID;
|
||||||
deleteEntity(entityItemID, true);
|
withWriteLock([&] {
|
||||||
|
deleteEntity(entityItemID, true);
|
||||||
|
});
|
||||||
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
|
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
|
||||||
qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID;
|
qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID;
|
||||||
deleteEntity(entityItemID, true);
|
withWriteLock([&] {
|
||||||
|
deleteEntity(entityItemID, true);
|
||||||
|
});
|
||||||
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
|
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
|
||||||
if (isRetryingValidation) {
|
if (isRetryingValidation) {
|
||||||
qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID;
|
qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID;
|
||||||
deleteEntity(entityItemID, true);
|
withWriteLock([&] {
|
||||||
|
deleteEntity(entityItemID, true);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
if (thread() != QThread::currentThread()) {
|
if (thread() != QThread::currentThread()) {
|
||||||
QMetaObject::invokeMethod(this, "startPendingTransferStatusTimer",
|
QMetaObject::invokeMethod(this, "startPendingTransferStatusTimer",
|
||||||
|
@ -1366,7 +1375,9 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityItemID
|
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityItemID
|
||||||
<< "More info:" << jsonObject;
|
<< "More info:" << jsonObject;
|
||||||
deleteEntity(entityItemID, true);
|
withWriteLock([&] {
|
||||||
|
deleteEntity(entityItemID, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
networkReply->deleteLater();
|
networkReply->deleteLater();
|
||||||
|
@ -1800,9 +1811,9 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) {
|
||||||
|
|
||||||
void EntityTree::update(bool simulate) {
|
void EntityTree::update(bool simulate) {
|
||||||
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
||||||
fixupNeedsParentFixups();
|
withWriteLock([&] {
|
||||||
if (simulate && _simulation) {
|
fixupNeedsParentFixups();
|
||||||
withWriteLock([&] {
|
if (simulate && _simulation) {
|
||||||
_simulation->updateEntities();
|
_simulation->updateEntities();
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "Deletes");
|
PROFILE_RANGE(simulation_physics, "Deletes");
|
||||||
|
@ -1820,8 +1831,8 @@ void EntityTree::update(bool simulate) {
|
||||||
deleteEntities(idsToDelete, true);
|
deleteEntities(idsToDelete, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
quint64 EntityTree::getAdjustedConsiderSince(quint64 sinceTime) {
|
quint64 EntityTree::getAdjustedConsiderSince(quint64 sinceTime) {
|
||||||
|
@ -2290,7 +2301,9 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer
|
||||||
QScriptEngine scriptEngine;
|
QScriptEngine scriptEngine;
|
||||||
RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues,
|
RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues,
|
||||||
skipThoseWithBadParents, _myAvatar);
|
skipThoseWithBadParents, _myAvatar);
|
||||||
recurseTreeWithOperator(&theOperator);
|
withReadLock([&] {
|
||||||
|
recurseTreeWithOperator(&theOperator);
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,13 @@ void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
|
||||||
// remove ownership and dirty all the tree elements that contain the it
|
// remove ownership and dirty all the tree elements that contain the it
|
||||||
entity->clearSimulationOwnership();
|
entity->clearSimulationOwnership();
|
||||||
entity->markAsChangedOnServer();
|
entity->markAsChangedOnServer();
|
||||||
DirtyOctreeElementOperator op(entity->getElement());
|
if (auto element = entity->getElement()) {
|
||||||
getEntityTree()->recurseTreeWithOperator(&op);
|
auto tree = getEntityTree();
|
||||||
|
tree->withReadLock([&] {
|
||||||
|
DirtyOctreeElementOperator op(element);
|
||||||
|
tree->recurseTreeWithOperator(&op);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
++itemItr;
|
++itemItr;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue