diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 33af7da1ae..97c8854c86 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -170,20 +170,24 @@ Item { objectName: "loader" asynchronous: false - width: parent.width height: parent.height + // Hook up callback for clara.io download from the marketplace. + Connections { + id: eventBridgeConnection + target: null + onWebEventReceived: { + if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") { + ApplicationInterface.addAssetToWorldFromURL(message.slice(18)); + } + } + } + onLoaded: { if (loader.item.hasOwnProperty("eventBridge")) { loader.item.eventBridge = eventBridge; - - // Hook up callback for clara.io download from the marketplace. - eventBridge.webEventReceived.connect(function (event) { - if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") { - ApplicationInterface.addAssetToWorldFromURL(event.slice(18)); - } - }); + eventBridgeConnection.target = eventBridge } if (loader.item.hasOwnProperty("sendToScript")) { loader.item.sendToScript.connect(tabletRoot.sendToScript); diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index 470fd4a830..ee8dbbff59 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -90,16 +90,21 @@ Windows.ScrollingWindow { anchors.left: parent.left anchors.top: parent.top + // Hook up callback for clara.io download from the marketplace. + Connections { + id: eventBridgeConnection + target: null + onWebEventReceived: { + if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") { + ApplicationInterface.addAssetToWorldFromURL(message.slice(18)); + } + } + } + onLoaded: { if (loader.item.hasOwnProperty("eventBridge")) { loader.item.eventBridge = eventBridge; - - // Hook up callback for clara.io download from the marketplace. - eventBridge.webEventReceived.connect(function (event) { - if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") { - ApplicationInterface.addAssetToWorldFromURL(event.slice(18)); - } - }); + eventBridgeConnection.target = eventBridge } if (loader.item.hasOwnProperty("sendToScript")) { loader.item.sendToScript.connect(tabletRoot.sendToScript); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8d20cbe715..673813b0db 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1459,6 +1459,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo updateSystemTabletMode(); connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged); + + qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID()); } void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { @@ -1681,6 +1683,10 @@ Application::~Application() { _physicsEngine->setCharacterController(nullptr); + // the _shapeManager should have zero references + _shapeManager.collectGarbage(); + assert(_shapeManager.getNumShapes() == 0); + // shutdown render engine _main3DScene = nullptr; _renderEngine = nullptr; @@ -4492,12 +4498,13 @@ void Application::update(float deltaTime) { getEntities()->getTree()->withWriteLock([&] { PerformanceTimer perfTimer("handleOutgoingChanges"); - const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates(); - _entitySimulation->handleDeactivatedMotionStates(deactivations); const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); _entitySimulation->handleChangedMotionStates(outgoingChanges); avatarManager->handleChangedMotionStates(outgoingChanges); + + const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates(); + _entitySimulation->handleDeactivatedMotionStates(deactivations); }); if (!_aboutToQuit) { diff --git a/libraries/controllers/src/controllers/InputRecorder.cpp b/libraries/controllers/src/controllers/InputRecorder.cpp index dbc2e4df52..54d1aaf131 100644 --- a/libraries/controllers/src/controllers/InputRecorder.cpp +++ b/libraries/controllers/src/controllers/InputRecorder.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -27,7 +28,7 @@ QString SAVE_DIRECTORY = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + BuildInfo::MODIFIED_ORGANIZATION + "/" + BuildInfo::INTERFACE_NAME + "/hifi-input-recordings/"; QString FILE_PREFIX_NAME = "input-recording-"; -QString COMPRESS_EXTENSION = "json.gz"; +QString COMPRESS_EXTENSION = ".json.gz"; namespace controller { QJsonObject poseToJsonObject(const Pose pose) { @@ -93,23 +94,26 @@ namespace controller { } - void exportToFile(const QJsonObject& object) { + void exportToFile(const QJsonObject& object, const QString& fileName) { if (!QDir(SAVE_DIRECTORY).exists()) { QDir().mkdir(SAVE_DIRECTORY); } - - QString timeStamp = QDateTime::currentDateTime().toString(Qt::ISODate); - timeStamp.replace(":", "-"); - QString fileName = SAVE_DIRECTORY + FILE_PREFIX_NAME + timeStamp + COMPRESS_EXTENSION; - qDebug() << fileName; + QFile saveFile (fileName); if (!saveFile.open(QIODevice::WriteOnly)) { qWarning() << "could not open file: " << fileName; return; } QJsonDocument saveData(object); - QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact)); - saveFile.write(compressedData); + QByteArray jsonData = saveData.toJson(QJsonDocument::Indented); + QByteArray jsonDataForFile; + + if (!gzip(jsonData, jsonDataForFile, -1)) { + qCritical("unable to gzip while saving to json."); + return; + } + + saveFile.write(jsonDataForFile); saveFile.close(); } @@ -121,8 +125,16 @@ namespace controller { status = false; return object; } - QByteArray compressedData = qUncompress(openFile.readAll()); - QJsonDocument jsonDoc = QJsonDocument::fromJson(compressedData); + QByteArray compressedData = openFile.readAll(); + QByteArray jsonData; + + if (!gunzip(compressedData, jsonData)) { + qCritical() << "json file not in gzip format: " << file; + status = false; + return object; + } + + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); object = jsonDoc.object(); status = true; openFile.close(); @@ -153,7 +165,7 @@ namespace controller { QJsonObject InputRecorder::recordDataToJson() { QJsonObject data; data["frameCount"] = _framesRecorded; - data["version"] = "1.0"; + data["version"] = "0.0"; QJsonArray actionArrayList; QJsonArray poseArrayList; @@ -187,7 +199,10 @@ namespace controller { void InputRecorder::saveRecording() { QJsonObject jsonData = recordDataToJson(); - exportToFile(jsonData); + QString timeStamp = QDateTime::currentDateTime().toString(Qt::ISODate); + timeStamp.replace(":", "-"); + QString fileName = SAVE_DIRECTORY + FILE_PREFIX_NAME + timeStamp + COMPRESS_EXTENSION; + exportToFile(jsonData, fileName); } void InputRecorder::loadRecording(const QString& path) { @@ -202,10 +217,12 @@ namespace controller { QString filePath = urlPath.toLocalFile(); QFileInfo info(filePath); QString extension = info.suffix(); + if (extension != "gz") { qWarning() << "can not load file with exentsion of " << extension; return; } + bool success = false; QJsonObject data = openFile(filePath, success); auto keyValue = data.find("version"); @@ -233,34 +250,7 @@ namespace controller { _poseStateList.push_back(_currentFramePoses); _currentFramePoses.clear(); } - } else if (success) { - //convert recording to new reacording standard and rewrite file - auto userInputMapper = DependencyManager::get(); - _framesRecorded = data["frameCount"].toInt(); - QJsonArray actionArrayList = data["actionList"].toArray(); - QJsonArray poseArrayList = data["poseList"].toArray(); - - for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) { - QJsonArray actionState = actionArrayList[actionIndex].toArray(); - for (int index = 0; index < actionState.size(); index++) { - QString actionName = userInputMapper->getActionName(Action(index)); - _currentFrameActions[actionName] = actionState[index].toDouble(); - } - _actionStateList.push_back(_currentFrameActions); - _currentFrameActions.clear(); - } - - for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) { - QJsonArray poseState = poseArrayList[poseIndex].toArray(); - for (int index = 0; index < poseState.size(); index++) { - QString actionName = userInputMapper->getActionName(Action(index)); - _currentFramePoses[actionName] = jsonObjectToPose(poseState[index].toObject()); - } - _poseStateList.push_back(_currentFramePoses); - _currentFramePoses.clear(); - } - } - + } _loading = false; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 570081d1f1..79f4325ae6 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -329,6 +329,16 @@ QString UserInputMapper::getActionName(Action action) const { return QString(); } +QString UserInputMapper::getStandardPoseName(uint16_t pose) { + Locker locker(_lock); + for (auto posePair : getStandardInputs()) { + if (posePair.first.channel == pose && posePair.first.getType() == ChannelType::POSE) { + return posePair.second; + } + } + return QString(); +} + QVector UserInputMapper::getActionNames() const { Locker locker(_lock); QVector result; diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 05a286cc10..0c8bb51008 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -80,6 +80,7 @@ namespace controller { QVector getAllActions() const; QString getActionName(Action action) const; + QString getStandardPoseName(uint16_t pose); float getActionState(Action action) const { return _actionStates[toInt(action)]; } Pose getPoseState(Action action) const; int findAction(const QString& actionName) const; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp index eb834a0bf2..ef9f04402b 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp @@ -36,9 +36,6 @@ void ActionEndpoint::apply(const Pose& value, const Pointer& source) { auto userInputMapper = DependencyManager::get(); QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); inputRecorder->setActionState(actionName, _currentPose); - if (inputRecorder->isPlayingback()) { - _currentPose = inputRecorder->getPoseState(actionName); - } if (!_currentPose.isValid()) { return; diff --git a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h index dfa728d2b6..2006809fed 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h @@ -12,6 +12,11 @@ #include "../Endpoint.h" +#include + +#include "../../InputRecorder.h" +#include "../../UserInputMapper.h" + namespace controller { class StandardEndpoint : public VirtualEndpoint { @@ -40,6 +45,12 @@ public: virtual Pose pose() override { _read = true; + InputRecorder* inputRecorder = InputRecorder::getInstance(); + if (inputRecorder->isPlayingback()) { + auto userInputMapper = DependencyManager::get(); + QString actionName = userInputMapper->getStandardPoseName(_input.getChannel()); + return inputRecorder->getPoseState(actionName); + } return VirtualEndpoint::pose(); } diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 6266ad0f89..c6fffbfdbd 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -201,6 +201,13 @@ void AccountManager::setAuthURL(const QUrl& authURL) { } } +void AccountManager::setSessionID(const QUuid& sessionID) { + if (_sessionID != sessionID) { + qCDebug(networking) << "Metaverse session ID changed to" << uuidStringWithoutCurlyBraces(sessionID); + _sessionID = sessionID; + } +} + void AccountManager::sendRequest(const QString& path, AccountManagerAuth::Type authType, QNetworkAccessManager::Operation operation, diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index dd2216957f..9a456ca7e8 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -90,7 +90,7 @@ public: static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply); QUuid getSessionID() const { return _sessionID; } - void setSessionID(const QUuid& sessionID) { _sessionID = sessionID; } + void setSessionID(const QUuid& sessionID); void setTemporaryDomain(const QUuid& domainID, const QString& key); const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index ff69363570..61f2071c5f 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -56,7 +56,7 @@ void UserActivityLoggerScriptingInterface::palAction(QString action, QString tar } void UserActivityLoggerScriptingInterface::palOpened(float secondsOpened) { - doLogAction("pal_opened", { + doLogAction("pal_opened", { { "seconds_opened", secondsOpened } }); } @@ -71,6 +71,14 @@ void UserActivityLoggerScriptingInterface::makeUserConnection(QString otherID, b doLogAction("makeUserConnection", payload); } +void UserActivityLoggerScriptingInterface::bubbleToggled(bool newValue) { + doLogAction(newValue ? "bubbleOn" : "bubbleOff"); +} + +void UserActivityLoggerScriptingInterface::bubbleActivated() { + doLogAction("bubbleActivated"); +} + void UserActivityLoggerScriptingInterface::logAction(QString action, QVariantMap details) { doLogAction(action, QJsonObject::fromVariantMap(details)); } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index b141e930f2..885f637a62 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -30,6 +30,8 @@ public: Q_INVOKABLE void palAction(QString action, QString target); Q_INVOKABLE void palOpened(float secondsOpen); Q_INVOKABLE void makeUserConnection(QString otherUser, bool success, QString details = ""); + Q_INVOKABLE void bubbleToggled(bool newValue); + Q_INVOKABLE void bubbleActivated(); Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{}); private: void doLogAction(QString action, QJsonObject details = {}); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index fdd290bfca..adb7bbddde 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -114,6 +114,7 @@ void EntityMotionState::handleDeactivation() { // virtual void EntityMotionState::handleEasyChanges(uint32_t& flags) { + assert(_entity); assert(entityTreeIsLocked()); updateServerPhysicsVariables(); ObjectMotionState::handleEasyChanges(flags); @@ -170,6 +171,7 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { // virtual bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { + assert(_entity); updateServerPhysicsVariables(); return ObjectMotionState::handleHardAndEasyChanges(flags, engine); } diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index a877887840..b11e21366e 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -202,6 +202,7 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) { } void ObjectMotionState::handleEasyChanges(uint32_t& flags) { + assert(_body && _shape); if (flags & Simulation::DIRTY_POSITION) { btTransform worldTrans = _body->getWorldTransform(); btVector3 newPosition = glmToBullet(getObjectPosition()); @@ -282,6 +283,7 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) { } bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { + assert(_body && _shape); if (flags & Simulation::DIRTY_SHAPE) { // make sure the new shape is valid if (!isReadyToComputeShape()) { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 1e582ea854..ccdc0e0041 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -79,7 +79,7 @@ public: static ShapeManager* getShapeManager(); ObjectMotionState(const btCollisionShape* shape); - ~ObjectMotionState(); + virtual ~ObjectMotionState(); virtual void handleEasyChanges(uint32_t& flags); virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine); diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 2e69ff987c..fe507ed1ee 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -130,7 +130,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() { } // then remove the objects (aka MotionStates) from physics - _physicsEngine->removeObjects(_physicalObjects); + _physicsEngine->removeSetOfObjects(_physicalObjects); // delete the MotionStates // TODO: after we invert the entities/physics lib dependencies we will let EntityItem delete diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 3a02e95e7c..1d61e53fbb 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -207,7 +207,7 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { } // Same as above, but takes a Set instead of a Vector. Should only be called during teardown. -void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) { +void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) { _contactMap.clear(); for (auto object : objects) { btRigidBody* body = object->getRigidBody(); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index e9b29a43a4..c029d14a42 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -53,7 +53,7 @@ public: uint32_t getNumSubsteps(); void removeObjects(const VectorOfMotionStates& objects); - void removeObjects(const SetOfMotionStates& objects); // only called during teardown + void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown void addObjects(const VectorOfMotionStates& objects); VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects); diff --git a/scripts/system/bubble.js b/scripts/system/bubble.js index 8d103c93de..c2a2f7af40 100644 --- a/scripts/system/bubble.js +++ b/scripts/system/bubble.js @@ -10,7 +10,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */ +/* global Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation, UserActivityLogger */ (function () { // BEGIN LOCAL_SCOPE var button; @@ -76,6 +76,7 @@ // Called from the C++ scripting interface to show the bubble overlay function enteredIgnoreRadius() { createOverlays(); + UserActivityLogger.bubbleActivated(); } // Used to set the state of the bubble HUD button @@ -139,10 +140,14 @@ } // When the space bubble is toggled... - function onBubbleToggled() { - var bubbleActive = Users.getIgnoreRadiusEnabled(); - writeButtonProperties(bubbleActive); - if (bubbleActive) { + // NOTE: the c++ calls this with just the first param -- we added a second + // just for not logging the initial state of the bubble when we startup. + function onBubbleToggled(enabled, doNotLog) { + writeButtonProperties(enabled); + if (doNotLog !== true) { + UserActivityLogger.bubbleToggled(enabled); + } + if (enabled) { createOverlays(); } else { hideOverlays(); @@ -163,7 +168,7 @@ sortOrder: 4 }); - onBubbleToggled(); + onBubbleToggled(Users.getIgnoreRadiusEnabled(), true); // pass in true so we don't log this initial one in the UserActivity table button.clicked.connect(Users.toggleIgnoreRadius); Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index f39165f3df..6a1ede88c6 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2100,7 +2100,13 @@ function selectParticleEntity(entityID) { } entityListTool.webView.webEventReceived.connect(function (data) { - data = JSON.parse(data); + try { + data = JSON.parse(data); + } catch(e) { + print("edit.js: Error parsing JSON: " + e.name + " data " + data) + return; + } + if (data.type === 'parent') { parentSelectedEntities(); } else if(data.type === 'unparent') { diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 3b6d32ec1c..64a05fcebf 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -109,7 +109,13 @@ EntityListTool = function(opts) { }; webView.webEventReceived.connect(function(data) { - data = JSON.parse(data); + try { + data = JSON.parse(data); + } catch(e) { + print("entityList.js: Error parsing JSON: " + e.name + " data " + data) + return; + } + if (data.type == "selectionUpdate") { var ids = data.entityIds; var entityIDs = []; diff --git a/scripts/system/libraries/gridTool.js b/scripts/system/libraries/gridTool.js index 0290674a0f..de9596e9be 100644 --- a/scripts/system/libraries/gridTool.js +++ b/scripts/system/libraries/gridTool.js @@ -238,7 +238,13 @@ GridTool = function(opts) { }); webView.webEventReceived.connect(function(data) { - data = JSON.parse(data); + try { + data = JSON.parse(data); + } catch(e) { + print("gridTool.js: Error parsing JSON: " + e.name + " data " + data) + return; + } + if (data.type == "init") { horizontalGrid.emitUpdate(); } else if (data.type == "update") {