From a21a34a4a46b0c10ee87203f36f057962b846654 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Apr 2017 16:51:24 -0700 Subject: [PATCH 1/3] add entities file replacement to DS and ES --- assignment-client/src/octree/OctreeServer.cpp | 76 ++++++++++++++++--- assignment-client/src/octree/OctreeServer.h | 2 + .../resources/web/content/index.shtml | 41 ++++++++++ .../resources/web/content/js/content.js | 45 +++++++++++ domain-server/resources/web/header.html | 1 + .../web/{settings => }/js/sweetalert.min.js | 0 .../resources/web/settings/index.shtml | 2 +- domain-server/src/DomainServer.cpp | 49 +++++++++++- domain-server/src/DomainServer.h | 4 + .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/octree/src/OctreePersistThread.cpp | 37 +++++++++ libraries/octree/src/OctreePersistThread.h | 2 + 13 files changed, 248 insertions(+), 16 deletions(-) create mode 100644 domain-server/resources/web/content/index.shtml create mode 100644 domain-server/resources/web/content/js/content.js rename domain-server/resources/web/{settings => }/js/sweetalert.min.js (100%) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index f2dbe5d1d2..af5f2c904e 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -11,12 +11,14 @@ #include "OctreeServer.h" +#include #include #include #include #include +#include #include #include #include @@ -924,6 +926,57 @@ void OctreeServer::handleJurisdictionRequestPacket(QSharedPointerqueueReceivedPacket(message, senderNode); } +void OctreeServer::handleOctreeFileReplacement(QSharedPointer message) { + if (!_isFinished && !_isShuttingDown) { + // these messages are only allowed to come from the domain server, so make sure that is the case + auto nodeList = DependencyManager::get(); + if (message->getSenderSockAddr() == nodeList->getDomainHandler().getSockAddr()) { + // it's far cleaner to load up the new content upon server startup + // so here we just store a special file at our persist path + // and then force a stop of the server so that it can pick it up when it relaunches + if (!_persistAbsoluteFilePath.isEmpty()) { + + // before we restart the server and make it try and load this data, let's make sure it is valid + auto compressedOctree = message->getMessage(); + QByteArray jsonOctree; + + // assume we have GZipped content + bool wasCompressed = gunzip(compressedOctree, jsonOctree); + if (!wasCompressed) { + // the source was not compressed, assume we were sent regular JSON data + jsonOctree = compressedOctree; + } + + // check the JSON data to verify it is an object + if (QJsonDocument::fromJson(jsonOctree).isObject()) { + if (!wasCompressed) { + // source was not compressed, we compress it before we write it locally + gzip(jsonOctree, compressedOctree); + } + + // write the compressed octree data to a special file + auto replacementFilePath = _persistAbsoluteFilePath.append(OctreePersistThread::REPLACEMENT_FILE_EXTENSION); + QFile replacementFile(replacementFilePath); + if (replacementFile.open(QIODevice::WriteOnly) && replacementFile.write(compressedOctree) != -1) { + // we've now written our replacement file, time to take the server down so it can + // process it when it comes back up + qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server"; + setFinished(true); + } else { + qWarning() << "Could not write replacement octree data to file - refusing to process"; + } + } else { + qDebug() << "Received replacement octree file that is invalid - refusing to process"; + } + } else { + qDebug() << "Cannot perform octree file replacement since current persist file path is not yet known"; + } + } else { + qDebug() << "Received an octree file replacement that was not from our domain server - refusing to process"; + } + } +} + bool OctreeServer::readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result) { result = false; // assume it doesn't exist bool optionAvailable = false; @@ -1148,6 +1201,7 @@ void OctreeServer::domainSettingsRequestComplete() { packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket"); packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket"); packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket"); + packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacement"); readConfiguration(); @@ -1173,25 +1227,25 @@ void OctreeServer::domainSettingsRequestComplete() { // If persist filename does not exist, let's see if there is one beside the application binary // If there is, let's copy it over to our target persist directory QDir persistPath { _persistFilePath }; - QString persistAbsoluteFilePath = persistPath.absolutePath(); + _persistAbsoluteFilePath = persistPath.absolutePath(); if (persistPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - persistAbsoluteFilePath = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_persistFilePath); + _persistAbsoluteFilePath = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; // force the persist file to end with .json.gz - if (!persistAbsoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) { - persistAbsoluteFilePath += ENTITY_PERSIST_EXTENSION; + if (!_persistAbsoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) { + _persistAbsoluteFilePath += ENTITY_PERSIST_EXTENSION; } else { // make sure the casing of .json.gz is correct - persistAbsoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive); + _persistAbsoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive); } - if (!QFile::exists(persistAbsoluteFilePath)) { + if (!QFile::exists(_persistAbsoluteFilePath)) { qDebug() << "Persist file does not exist, checking for existence of persist file next to application"; static const QString OLD_DEFAULT_PERSIST_FILENAME = "resources/models.json.gz"; @@ -1217,7 +1271,7 @@ void OctreeServer::domainSettingsRequestComplete() { pathToCopyFrom = oldDefaultPersistPath; } - QDir persistFileDirectory { QDir::cleanPath(persistAbsoluteFilePath + "/..") }; + QDir persistFileDirectory { QDir::cleanPath(_persistAbsoluteFilePath + "/..") }; if (!persistFileDirectory.exists()) { qDebug() << "Creating data directory " << persistFileDirectory.absolutePath(); @@ -1225,15 +1279,15 @@ void OctreeServer::domainSettingsRequestComplete() { } if (shouldCopy) { - qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistAbsoluteFilePath; + qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << _persistAbsoluteFilePath; - QFile::copy(pathToCopyFrom, persistAbsoluteFilePath); + QFile::copy(pathToCopyFrom, _persistAbsoluteFilePath); } else { qDebug() << "No existing persist file found"; } } - auto persistFileDirectory = QFileInfo(persistAbsoluteFilePath).absolutePath(); + auto persistFileDirectory = QFileInfo(_persistAbsoluteFilePath).absolutePath(); if (_backupDirectoryPath.isEmpty()) { // Use the persist file's directory to store backups _backupDirectoryPath = persistFileDirectory; @@ -1264,7 +1318,7 @@ void OctreeServer::domainSettingsRequestComplete() { qDebug() << "Backups will be stored in: " << _backupDirectoryPath; // now set up PersistThread - _persistThread = new OctreePersistThread(_tree, persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval, + _persistThread = new OctreePersistThread(_tree, _persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval, _wantBackup, _settings, _debugTimestampNow, _persistAsFileType); _persistThread->initialize(true); } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 3bb327eb06..8db8d845de 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -136,6 +136,7 @@ private slots: void handleOctreeQueryPacket(QSharedPointer message, SharedNodePointer senderNode); void handleOctreeDataNackPacket(QSharedPointer message, SharedNodePointer senderNode); void handleJurisdictionRequestPacket(QSharedPointer message, SharedNodePointer senderNode); + void handleOctreeFileReplacement(QSharedPointer message); void removeSendThread(); protected: @@ -172,6 +173,7 @@ protected: QString _statusHost; QString _persistFilePath; + QString _persistAbsoluteFilePath; QString _persistAsFileType; QString _backupDirectoryPath; int _packetsPerClientPerInterval; diff --git a/domain-server/resources/web/content/index.shtml b/domain-server/resources/web/content/index.shtml new file mode 100644 index 0000000000..1d8d3fe1ab --- /dev/null +++ b/domain-server/resources/web/content/index.shtml @@ -0,0 +1,41 @@ + + +
+
+
+ +
+
+ +
+
+
+
+

Upload Entities File

+
+
+
+

+ Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.
+ Note: Your domain's content will be replaced by the content you upload, but the backup files of your domain's content will not immediately be changed. +

+

+ If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored in C:\Users\[user_name]\AppData\Roaming\High Fidelity\assignment-client. +

+ + +
+ +
+ +
+
+
+
+ + + + + diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js new file mode 100644 index 0000000000..2e6e084164 --- /dev/null +++ b/domain-server/resources/web/content/js/content.js @@ -0,0 +1,45 @@ +$(document).ready(function(){ + + function showSpinnerAlert(title) { + swal({ + title: title, + text: '
', + html: true, + showConfirmButton: false, + allowEscapeKey: false + }); + } + + var frm = $('#upload-form'); + frm.submit(function (ev) { + $.ajax({ + type: frm.attr('method'), + url: frm.attr('action'), + data: new FormData($(this)[0]), + cache: false, + contentType: false, + processData: false, + success: function (data) { + swal({ + title: 'Uploaded', + type: 'success', + text: 'Your Entity Server is restarting to replace its local content with the uploaded file.', + confirmButtonText: 'OK' + }) + }, + error: function (data) { + swal({ + title: '', + type: 'error', + text: 'Your entities file could not be transferred to the Entity Server.
Verify that the file is a .json or .json.gz entities file and try again.', + html: true, + confirmButtonText: 'OK', + }); + } + }); + + ev.preventDefault(); + + showSpinnerAlert("Uploading Entities File"); + }); +}); diff --git a/domain-server/resources/web/header.html b/domain-server/resources/web/header.html index b4eee406f2..965f86b0a1 100644 --- a/domain-server/resources/web/header.html +++ b/domain-server/resources/web/header.html @@ -36,6 +36,7 @@
  • New Assignment
  • +
  • Content
  • Settings
  • diff --git a/domain-server/resources/web/settings/js/sweetalert.min.js b/domain-server/resources/web/js/sweetalert.min.js similarity index 100% rename from domain-server/resources/web/settings/js/sweetalert.min.js rename to domain-server/resources/web/js/sweetalert.min.js diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 3eb7a53726..1812c52dad 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -99,7 +99,7 @@ - + diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 620b11d8ad..233cb02dff 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1633,6 +1633,15 @@ QString pathForAssignmentScript(const QUuid& assignmentUUID) { return directory.absoluteFilePath(uuidStringWithoutCurlyBraces(assignmentUUID)); } +QString DomainServer::pathForRedirect(QString path) const { + // make sure the passed path has a leading slash + if (!path.startsWith('/')) { + path.insert(0, '/'); + } + + return "http://" + _hostname + ":" + QString::number(_httpManager.serverPort()) + path; +} + const QString URI_OAUTH = "/oauth"; bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { const QString JSON_MIME_TYPE = "application/json"; @@ -1640,6 +1649,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString URI_ASSIGNMENT = "/assignment"; const QString URI_NODES = "/nodes"; const QString URI_SETTINGS = "/settings"; + const QString URI_ENTITY_FILE_UPLOAD = "/content/upload"; const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; @@ -1869,6 +1879,25 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // respond with a 200 code for successful upload connection->respond(HTTPConnection::StatusCode200); + return true; + } else if (url.path() == URI_ENTITY_FILE_UPLOAD) { + // this is an entity file upload, ask the HTTPConnection to parse the data + QList formData = connection->parseFormData(); + + Headers redirectHeaders; + + if (formData.size() > 0 && formData[0].second.size() > 0) { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, formData[0].second)); + + // respond with a 200 for success + connection->respond(HTTPConnection::StatusCode200); + } else { + // respond with a 400 for failure + connection->respond(HTTPConnection::StatusCode400); + } + return true; } } else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) { @@ -2159,8 +2188,7 @@ Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileR cookieHeaders.insert("Set-Cookie", cookieString.toUtf8()); // redirect the user back to the homepage so they can present their cookie and be authenticated - QString redirectString = "http://" + _hostname + ":" + QString::number(_httpManager.serverPort()); - cookieHeaders.insert("Location", redirectString.toUtf8()); + cookieHeaders.insert("Location", pathForRedirect().toUtf8()); return cookieHeaders; } @@ -2560,3 +2588,20 @@ void DomainServer::setupGroupCacheRefresh() { _metaverseGroupCacheTimer->start(REFRESH_GROUPS_INTERVAL_MSECS); } } + +void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) { + // enumerate the nodes and find any octree type servers with active sockets + + auto limitedNodeList = DependencyManager::get(); + limitedNodeList->eachMatchingNode([](const SharedNodePointer& node){ + return node->getType() == NodeType::EntityServer && node->getActiveSocket(); + }, [&octreeFile, limitedNodeList](const SharedNodePointer& octreeNode){ + // setup a packet to send to this octree server with the new octree file data + auto octreeFilePacketList = NLPacketList::create(PacketType::OctreeFileReplacement, QByteArray(), true, true); + octreeFilePacketList->write(octreeFile); + + qDebug() << "Sending an octree file replacement of" << octreeFile.size() << "bytes to" << octreeNode; + + limitedNodeList->sendPacketList(std::move(octreeFilePacketList), *octreeNode); + }); +} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 4c5c42acee..63b82cb37d 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -100,6 +100,8 @@ private slots: void handleSuccessfulICEServerAddressUpdate(QNetworkReply& requestReply); void handleFailedICEServerAddressUpdate(QNetworkReply& requestReply); + void handleOctreeFileReplacement(QByteArray octreeFile); + signals: void iceServerChanged(); void userConnected(); @@ -161,6 +163,8 @@ private: void setupGroupCacheRefresh(); + QString pathForRedirect(QString path = QString()) const; + SubnetList _acSubnetWhitelist; DomainGatekeeper _gatekeeper; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index bd30cdd29c..adaa7a848c 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -39,7 +39,7 @@ const QSet NON_SOURCED_PACKETS = QSet() << PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat << PacketType::ICEServerHeartbeatACK << PacketType::ICEPing << PacketType::ICEPingReply << PacketType::ICEServerHeartbeatDenied << PacketType::AssignmentClientStatus << PacketType::StopNode - << PacketType::DomainServerRemovedNode << PacketType::UsernameFromIDReply; + << PacketType::DomainServerRemovedNode << PacketType::UsernameFromIDReply << PacketType::OctreeFileReplacement; PacketVersion versionForPacketType(PacketType packetType) { switch (packetType) { diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index d312427ca7..f803b83887 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -113,7 +113,8 @@ public: EntityPhysics, EntityServerScriptLog, AdjustAvatarSorting, - LAST_PACKET_TYPE = AdjustAvatarSorting + OctreeFileReplacement, + LAST_PACKET_TYPE = OctreeFileReplacement }; }; diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 7034790eaf..b79ce5537f 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -33,6 +33,7 @@ #include "OctreePersistThread.h" const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds +const QString OctreePersistThread::REPLACEMENT_FILE_EXTENSION = ".replace"; OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, int persistInterval, bool wantBackup, const QJsonObject& settings, bool debugTimestampNow, @@ -131,10 +132,46 @@ quint64 OctreePersistThread::getMostRecentBackupTimeInUsecs(const QString& forma return mostRecentBackupInUsecs; } +void OctreePersistThread::possiblyReplaceContent() { + // before we load the normal file, check if there's a pending replacement file + auto replacementFileName = _filename + REPLACEMENT_FILE_EXTENSION; + + QFile replacementFile { replacementFileName }; + if (replacementFile.exists()) { + // we have a replacement file to process + qDebug() << "Replacing models file with" << replacementFileName; + + // first take the current models file and move it to a different filename, appended with the timestamp + QFile currentFile { _filename }; + if (currentFile.exists()) { + static const QString FILENAME_TIMESTAMP_FORMAT = "yyyyMMdd-hhmmss"; + auto backupFileName = _filename + ".backup." + QDateTime::currentDateTime().toString(FILENAME_TIMESTAMP_FORMAT); + + if (currentFile.rename(backupFileName)) { + qDebug() << "Moved previous models file to" << backupFileName; + } else { + qWarning() << "Could not backup previous models file to" << backupFileName << "- removing replacement models file"; + + if (!QFile::remove(replacementFileName)) { + qWarning() << "Could not remove replacement models file from" << replacementFileName + << "- replacement will be re-attempted on next server restart"; + } + } + } + + // rename the replacement file to match what the persist thread is just about to read + if (!replacementFile.rename(_filename)) { + qWarning() << "Could not replace models file with" << replacementFileName << "- starting with empty models file"; + } + } +} + bool OctreePersistThread::process() { if (!_initialLoadComplete) { + possiblyReplaceContent(); + quint64 loadStarted = usecTimestampNow(); qCDebug(octree) << "loading Octrees from file: " << _filename << "..."; diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 927304e862..2441223467 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -32,6 +32,7 @@ public: }; static const int DEFAULT_PERSIST_INTERVAL; + static const QString REPLACEMENT_FILE_EXTENSION; OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, int persistInterval = DEFAULT_PERSIST_INTERVAL, bool wantBackup = false, @@ -60,6 +61,7 @@ protected: bool getMostRecentBackup(const QString& format, QString& mostRecentBackupFileName, QDateTime& mostRecentBackupTime); quint64 getMostRecentBackupTimeInUsecs(const QString& format); void parseSettings(const QJsonObject& settings); + void possiblyReplaceContent(); private: OctreePointer _tree; From 038ac745c8af5fe8ced7d1bd4e3bc093d71fff50 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Apr 2017 17:47:18 -0700 Subject: [PATCH 2/3] clear domain octree details if ES killed --- interface/src/Application.cpp | 38 ++++++++--------------------------- interface/src/Application.h | 1 + 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 39a4b8ee7c..c8e61d87dc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -797,7 +797,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); - connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); + connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &Application::clearDomainAvatars); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this]() { getOverlays().deleteOverlay(getTabletScreenID()); getOverlays().deleteOverlay(getTabletHomeButtonID()); @@ -5196,14 +5196,17 @@ void Application::clearDomainOctreeDetails() { skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT); _recentlyClearedDomain = true; - - DependencyManager::get()->clearOtherAvatars(); + DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); } +void Application::clearDomainAvatars() { + DependencyManager::get()->clearOtherAvatars(); +} + void Application::domainChanged(const QString& domainHostname) { updateWindowTitle(); // disable physics until we have enough information about our new location to not cause craziness. @@ -5273,33 +5276,8 @@ void Application::nodeKilled(SharedNodePointer node) { if (node->getType() == NodeType::AudioMixer) { QMetaObject::invokeMethod(DependencyManager::get().data(), "audioMixerKilled"); } else if (node->getType() == NodeType::EntityServer) { - QUuid nodeUUID = node->getUUID(); - // see if this is the first we've heard of this node... - _entityServerJurisdictions.withReadLock([&] { - if (_entityServerJurisdictions.find(nodeUUID) == _entityServerJurisdictions.end()) { - return; - } - - auto rootCode = _entityServerJurisdictions[nodeUUID].getRootOctalCode(); - VoxelPositionSize rootDetails; - voxelDetailsForCode(rootCode.get(), rootDetails); - - qCDebug(interfaceapp, "model server going away...... v[%f, %f, %f, %f]", - (double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s); - - }); - - // If the model server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server - _entityServerJurisdictions.withWriteLock([&] { - _entityServerJurisdictions.erase(_entityServerJurisdictions.find(nodeUUID)); - }); - - // also clean up scene stats for that server - _octreeServerSceneStats.withWriteLock([&] { - if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { - _octreeServerSceneStats.erase(nodeUUID); - } - }); + // we lost an entity server, clear all of the domain octree details + clearDomainOctreeDetails(); } else if (node->getType() == NodeType::AvatarMixer) { // our avatar mixer has gone away - clear the hash of avatars DependencyManager::get()->clearOtherAvatars(); diff --git a/interface/src/Application.h b/interface/src/Application.h index dff1de2860..041f1f8930 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -409,6 +409,7 @@ public slots: private slots: void showDesktop(); void clearDomainOctreeDetails(); + void clearDomainAvatars(); void aboutToQuit(); void resettingDomain(); From 9bc0609b1e2e6534d510354b234c330cbc72a1a5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Apr 2017 10:17:48 -0700 Subject: [PATCH 3/3] address code review comments --- domain-server/resources/web/content/index.shtml | 11 +++++++---- domain-server/src/DomainServer.cpp | 4 ++-- interface/src/Application.cpp | 2 +- libraries/octree/src/OctreePersistThread.cpp | 3 ++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/domain-server/resources/web/content/index.shtml b/domain-server/resources/web/content/index.shtml index 1d8d3fe1ab..c7eb765878 100644 --- a/domain-server/resources/web/content/index.shtml +++ b/domain-server/resources/web/content/index.shtml @@ -16,20 +16,23 @@

    - Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.
    + Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.
    Note: Your domain's content will be replaced by the content you upload, but the backup files of your domain's content will not immediately be changed.

    - If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored in C:\Users\[user_name]\AppData\Roaming\High Fidelity\assignment-client. + If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:
    +

    C:\Users\[username]\AppData\Roaming\High Fidelity\assignment-client/entities/models.json.gz
    +
    /Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz
    +
    /home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz

    - +
    +
    - diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 233cb02dff..782c54419d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2593,9 +2593,9 @@ void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) { // enumerate the nodes and find any octree type servers with active sockets auto limitedNodeList = DependencyManager::get(); - limitedNodeList->eachMatchingNode([](const SharedNodePointer& node){ + limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) { return node->getType() == NodeType::EntityServer && node->getActiveSocket(); - }, [&octreeFile, limitedNodeList](const SharedNodePointer& octreeNode){ + }, [&octreeFile, limitedNodeList](const SharedNodePointer& octreeNode) { // setup a packet to send to this octree server with the new octree file data auto octreeFilePacketList = NLPacketList::create(PacketType::OctreeFileReplacement, QByteArray(), true, true); octreeFilePacketList->write(octreeFile); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c8e61d87dc..9fa66262cc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5177,7 +5177,6 @@ void Application::clearDomainOctreeDetails() { qCDebug(interfaceapp) << "Clearing domain octree details..."; resetPhysicsReadyInformation(); - getMyAvatar()->setAvatarEntityDataChanged(true); // to recreate worn entities // reset our node to stats and node to jurisdiction maps... since these must be changing... _entityServerJurisdictions.withWriteLock([&] { @@ -5204,6 +5203,7 @@ void Application::clearDomainOctreeDetails() { } void Application::clearDomainAvatars() { + getMyAvatar()->setAvatarEntityDataChanged(true); // to recreate worn entities DependencyManager::get()->clearOtherAvatars(); } diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index b79ce5537f..ea6bd28fc4 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -152,9 +152,10 @@ void OctreePersistThread::possiblyReplaceContent() { } else { qWarning() << "Could not backup previous models file to" << backupFileName << "- removing replacement models file"; - if (!QFile::remove(replacementFileName)) { + if (!replacementFile.remove()) { qWarning() << "Could not remove replacement models file from" << replacementFileName << "- replacement will be re-attempted on next server restart"; + return; } } }