Merge branch 'master' of github.com:highfidelity/hifi into head-controller

This commit is contained in:
Seth Alves 2017-06-02 10:07:50 -07:00
commit d91b3700a7
22 changed files with 145 additions and 76 deletions

View file

@ -170,20 +170,24 @@ Item {
objectName: "loader" objectName: "loader"
asynchronous: false asynchronous: false
width: parent.width width: parent.width
height: parent.height 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: { onLoaded: {
if (loader.item.hasOwnProperty("eventBridge")) { if (loader.item.hasOwnProperty("eventBridge")) {
loader.item.eventBridge = eventBridge; loader.item.eventBridge = eventBridge;
eventBridgeConnection.target = 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));
}
});
} }
if (loader.item.hasOwnProperty("sendToScript")) { if (loader.item.hasOwnProperty("sendToScript")) {
loader.item.sendToScript.connect(tabletRoot.sendToScript); loader.item.sendToScript.connect(tabletRoot.sendToScript);

View file

@ -90,16 +90,21 @@ Windows.ScrollingWindow {
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top 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: { onLoaded: {
if (loader.item.hasOwnProperty("eventBridge")) { if (loader.item.hasOwnProperty("eventBridge")) {
loader.item.eventBridge = eventBridge; loader.item.eventBridge = eventBridge;
eventBridgeConnection.target = 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));
}
});
} }
if (loader.item.hasOwnProperty("sendToScript")) { if (loader.item.hasOwnProperty("sendToScript")) {
loader.item.sendToScript.connect(tabletRoot.sendToScript); loader.item.sendToScript.connect(tabletRoot.sendToScript);

View file

@ -1459,6 +1459,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
updateSystemTabletMode(); updateSystemTabletMode();
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged); 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) { void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
@ -1681,6 +1683,10 @@ Application::~Application() {
_physicsEngine->setCharacterController(nullptr); _physicsEngine->setCharacterController(nullptr);
// the _shapeManager should have zero references
_shapeManager.collectGarbage();
assert(_shapeManager.getNumShapes() == 0);
// shutdown render engine // shutdown render engine
_main3DScene = nullptr; _main3DScene = nullptr;
_renderEngine = nullptr; _renderEngine = nullptr;
@ -4492,12 +4498,13 @@ void Application::update(float deltaTime) {
getEntities()->getTree()->withWriteLock([&] { getEntities()->getTree()->withWriteLock([&] {
PerformanceTimer perfTimer("handleOutgoingChanges"); PerformanceTimer perfTimer("handleOutgoingChanges");
const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
_entitySimulation->handleDeactivatedMotionStates(deactivations);
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
_entitySimulation->handleChangedMotionStates(outgoingChanges); _entitySimulation->handleChangedMotionStates(outgoingChanges);
avatarManager->handleChangedMotionStates(outgoingChanges); avatarManager->handleChangedMotionStates(outgoingChanges);
const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
_entitySimulation->handleDeactivatedMotionStates(deactivations);
}); });
if (!_aboutToQuit) { if (!_aboutToQuit) {

View file

@ -19,6 +19,7 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <PathUtils.h> #include <PathUtils.h>
#include <QUrl> #include <QUrl>
#include <Gzip.h>
#include <BuildInfo.h> #include <BuildInfo.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
@ -27,7 +28,7 @@
QString SAVE_DIRECTORY = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + BuildInfo::MODIFIED_ORGANIZATION + "/" + BuildInfo::INTERFACE_NAME + "/hifi-input-recordings/"; QString SAVE_DIRECTORY = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + BuildInfo::MODIFIED_ORGANIZATION + "/" + BuildInfo::INTERFACE_NAME + "/hifi-input-recordings/";
QString FILE_PREFIX_NAME = "input-recording-"; QString FILE_PREFIX_NAME = "input-recording-";
QString COMPRESS_EXTENSION = "json.gz"; QString COMPRESS_EXTENSION = ".json.gz";
namespace controller { namespace controller {
QJsonObject poseToJsonObject(const Pose pose) { 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()) { if (!QDir(SAVE_DIRECTORY).exists()) {
QDir().mkdir(SAVE_DIRECTORY); 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); QFile saveFile (fileName);
if (!saveFile.open(QIODevice::WriteOnly)) { if (!saveFile.open(QIODevice::WriteOnly)) {
qWarning() << "could not open file: " << fileName; qWarning() << "could not open file: " << fileName;
return; return;
} }
QJsonDocument saveData(object); QJsonDocument saveData(object);
QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact)); QByteArray jsonData = saveData.toJson(QJsonDocument::Indented);
saveFile.write(compressedData); QByteArray jsonDataForFile;
if (!gzip(jsonData, jsonDataForFile, -1)) {
qCritical("unable to gzip while saving to json.");
return;
}
saveFile.write(jsonDataForFile);
saveFile.close(); saveFile.close();
} }
@ -121,8 +125,16 @@ namespace controller {
status = false; status = false;
return object; return object;
} }
QByteArray compressedData = qUncompress(openFile.readAll()); QByteArray compressedData = openFile.readAll();
QJsonDocument jsonDoc = QJsonDocument::fromJson(compressedData); 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(); object = jsonDoc.object();
status = true; status = true;
openFile.close(); openFile.close();
@ -153,7 +165,7 @@ namespace controller {
QJsonObject InputRecorder::recordDataToJson() { QJsonObject InputRecorder::recordDataToJson() {
QJsonObject data; QJsonObject data;
data["frameCount"] = _framesRecorded; data["frameCount"] = _framesRecorded;
data["version"] = "1.0"; data["version"] = "0.0";
QJsonArray actionArrayList; QJsonArray actionArrayList;
QJsonArray poseArrayList; QJsonArray poseArrayList;
@ -187,7 +199,10 @@ namespace controller {
void InputRecorder::saveRecording() { void InputRecorder::saveRecording() {
QJsonObject jsonData = recordDataToJson(); 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) { void InputRecorder::loadRecording(const QString& path) {
@ -202,10 +217,12 @@ namespace controller {
QString filePath = urlPath.toLocalFile(); QString filePath = urlPath.toLocalFile();
QFileInfo info(filePath); QFileInfo info(filePath);
QString extension = info.suffix(); QString extension = info.suffix();
if (extension != "gz") { if (extension != "gz") {
qWarning() << "can not load file with exentsion of " << extension; qWarning() << "can not load file with exentsion of " << extension;
return; return;
} }
bool success = false; bool success = false;
QJsonObject data = openFile(filePath, success); QJsonObject data = openFile(filePath, success);
auto keyValue = data.find("version"); auto keyValue = data.find("version");
@ -233,34 +250,7 @@ namespace controller {
_poseStateList.push_back(_currentFramePoses); _poseStateList.push_back(_currentFramePoses);
_currentFramePoses.clear(); _currentFramePoses.clear();
} }
} else if (success) { }
//convert recording to new reacording standard and rewrite file
auto userInputMapper = DependencyManager::get<UserInputMapper>();
_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; _loading = false;
} }

View file

@ -329,6 +329,16 @@ QString UserInputMapper::getActionName(Action action) const {
return QString(); 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<QString> UserInputMapper::getActionNames() const { QVector<QString> UserInputMapper::getActionNames() const {
Locker locker(_lock); Locker locker(_lock);
QVector<QString> result; QVector<QString> result;

View file

@ -80,6 +80,7 @@ namespace controller {
QVector<Action> getAllActions() const; QVector<Action> getAllActions() const;
QString getActionName(Action action) const; QString getActionName(Action action) const;
QString getStandardPoseName(uint16_t pose);
float getActionState(Action action) const { return _actionStates[toInt(action)]; } float getActionState(Action action) const { return _actionStates[toInt(action)]; }
Pose getPoseState(Action action) const; Pose getPoseState(Action action) const;
int findAction(const QString& actionName) const; int findAction(const QString& actionName) const;

View file

@ -36,9 +36,6 @@ void ActionEndpoint::apply(const Pose& value, const Pointer& source) {
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); QString actionName = userInputMapper->getActionName(Action(_input.getChannel()));
inputRecorder->setActionState(actionName, _currentPose); inputRecorder->setActionState(actionName, _currentPose);
if (inputRecorder->isPlayingback()) {
_currentPose = inputRecorder->getPoseState(actionName);
}
if (!_currentPose.isValid()) { if (!_currentPose.isValid()) {
return; return;

View file

@ -12,6 +12,11 @@
#include "../Endpoint.h" #include "../Endpoint.h"
#include <DependencyManager.h>
#include "../../InputRecorder.h"
#include "../../UserInputMapper.h"
namespace controller { namespace controller {
class StandardEndpoint : public VirtualEndpoint { class StandardEndpoint : public VirtualEndpoint {
@ -40,6 +45,12 @@ public:
virtual Pose pose() override { virtual Pose pose() override {
_read = true; _read = true;
InputRecorder* inputRecorder = InputRecorder::getInstance();
if (inputRecorder->isPlayingback()) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
QString actionName = userInputMapper->getStandardPoseName(_input.getChannel());
return inputRecorder->getPoseState(actionName);
}
return VirtualEndpoint::pose(); return VirtualEndpoint::pose();
} }

View file

@ -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, void AccountManager::sendRequest(const QString& path,
AccountManagerAuth::Type authType, AccountManagerAuth::Type authType,
QNetworkAccessManager::Operation operation, QNetworkAccessManager::Operation operation,

View file

@ -90,7 +90,7 @@ public:
static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply); static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply);
QUuid getSessionID() const { return _sessionID; } 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); void setTemporaryDomain(const QUuid& domainID, const QString& key);
const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); } const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); }

View file

@ -56,7 +56,7 @@ void UserActivityLoggerScriptingInterface::palAction(QString action, QString tar
} }
void UserActivityLoggerScriptingInterface::palOpened(float secondsOpened) { void UserActivityLoggerScriptingInterface::palOpened(float secondsOpened) {
doLogAction("pal_opened", { doLogAction("pal_opened", {
{ "seconds_opened", secondsOpened } { "seconds_opened", secondsOpened }
}); });
} }
@ -71,6 +71,14 @@ void UserActivityLoggerScriptingInterface::makeUserConnection(QString otherID, b
doLogAction("makeUserConnection", payload); doLogAction("makeUserConnection", payload);
} }
void UserActivityLoggerScriptingInterface::bubbleToggled(bool newValue) {
doLogAction(newValue ? "bubbleOn" : "bubbleOff");
}
void UserActivityLoggerScriptingInterface::bubbleActivated() {
doLogAction("bubbleActivated");
}
void UserActivityLoggerScriptingInterface::logAction(QString action, QVariantMap details) { void UserActivityLoggerScriptingInterface::logAction(QString action, QVariantMap details) {
doLogAction(action, QJsonObject::fromVariantMap(details)); doLogAction(action, QJsonObject::fromVariantMap(details));
} }

View file

@ -30,6 +30,8 @@ public:
Q_INVOKABLE void palAction(QString action, QString target); Q_INVOKABLE void palAction(QString action, QString target);
Q_INVOKABLE void palOpened(float secondsOpen); Q_INVOKABLE void palOpened(float secondsOpen);
Q_INVOKABLE void makeUserConnection(QString otherUser, bool success, QString details = ""); 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{}); Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{});
private: private:
void doLogAction(QString action, QJsonObject details = {}); void doLogAction(QString action, QJsonObject details = {});

View file

@ -114,6 +114,7 @@ void EntityMotionState::handleDeactivation() {
// virtual // virtual
void EntityMotionState::handleEasyChanges(uint32_t& flags) { void EntityMotionState::handleEasyChanges(uint32_t& flags) {
assert(_entity);
assert(entityTreeIsLocked()); assert(entityTreeIsLocked());
updateServerPhysicsVariables(); updateServerPhysicsVariables();
ObjectMotionState::handleEasyChanges(flags); ObjectMotionState::handleEasyChanges(flags);
@ -170,6 +171,7 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
// virtual // virtual
bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
assert(_entity);
updateServerPhysicsVariables(); updateServerPhysicsVariables();
return ObjectMotionState::handleHardAndEasyChanges(flags, engine); return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
} }

View file

@ -202,6 +202,7 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) {
} }
void ObjectMotionState::handleEasyChanges(uint32_t& flags) { void ObjectMotionState::handleEasyChanges(uint32_t& flags) {
assert(_body && _shape);
if (flags & Simulation::DIRTY_POSITION) { if (flags & Simulation::DIRTY_POSITION) {
btTransform worldTrans = _body->getWorldTransform(); btTransform worldTrans = _body->getWorldTransform();
btVector3 newPosition = glmToBullet(getObjectPosition()); btVector3 newPosition = glmToBullet(getObjectPosition());
@ -282,6 +283,7 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) {
} }
bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
assert(_body && _shape);
if (flags & Simulation::DIRTY_SHAPE) { if (flags & Simulation::DIRTY_SHAPE) {
// make sure the new shape is valid // make sure the new shape is valid
if (!isReadyToComputeShape()) { if (!isReadyToComputeShape()) {

View file

@ -79,7 +79,7 @@ public:
static ShapeManager* getShapeManager(); static ShapeManager* getShapeManager();
ObjectMotionState(const btCollisionShape* shape); ObjectMotionState(const btCollisionShape* shape);
~ObjectMotionState(); virtual ~ObjectMotionState();
virtual void handleEasyChanges(uint32_t& flags); virtual void handleEasyChanges(uint32_t& flags);
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine); virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);

View file

@ -130,7 +130,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
} }
// then remove the objects (aka MotionStates) from physics // then remove the objects (aka MotionStates) from physics
_physicsEngine->removeObjects(_physicalObjects); _physicsEngine->removeSetOfObjects(_physicalObjects);
// delete the MotionStates // delete the MotionStates
// TODO: after we invert the entities/physics lib dependencies we will let EntityItem delete // TODO: after we invert the entities/physics lib dependencies we will let EntityItem delete

View file

@ -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. // 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(); _contactMap.clear();
for (auto object : objects) { for (auto object : objects) {
btRigidBody* body = object->getRigidBody(); btRigidBody* body = object->getRigidBody();

View file

@ -53,7 +53,7 @@ public:
uint32_t getNumSubsteps(); uint32_t getNumSubsteps();
void removeObjects(const VectorOfMotionStates& objects); 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); void addObjects(const VectorOfMotionStates& objects);
VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects); VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects);

View file

@ -10,7 +10,7 @@
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // 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 (function () { // BEGIN LOCAL_SCOPE
var button; var button;
@ -76,6 +76,7 @@
// Called from the C++ scripting interface to show the bubble overlay // Called from the C++ scripting interface to show the bubble overlay
function enteredIgnoreRadius() { function enteredIgnoreRadius() {
createOverlays(); createOverlays();
UserActivityLogger.bubbleActivated();
} }
// Used to set the state of the bubble HUD button // Used to set the state of the bubble HUD button
@ -139,10 +140,14 @@
} }
// When the space bubble is toggled... // When the space bubble is toggled...
function onBubbleToggled() { // NOTE: the c++ calls this with just the first param -- we added a second
var bubbleActive = Users.getIgnoreRadiusEnabled(); // just for not logging the initial state of the bubble when we startup.
writeButtonProperties(bubbleActive); function onBubbleToggled(enabled, doNotLog) {
if (bubbleActive) { writeButtonProperties(enabled);
if (doNotLog !== true) {
UserActivityLogger.bubbleToggled(enabled);
}
if (enabled) {
createOverlays(); createOverlays();
} else { } else {
hideOverlays(); hideOverlays();
@ -163,7 +168,7 @@
sortOrder: 4 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); button.clicked.connect(Users.toggleIgnoreRadius);
Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled); Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled);

View file

@ -2100,7 +2100,13 @@ function selectParticleEntity(entityID) {
} }
entityListTool.webView.webEventReceived.connect(function (data) { 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') { if (data.type === 'parent') {
parentSelectedEntities(); parentSelectedEntities();
} else if(data.type === 'unparent') { } else if(data.type === 'unparent') {

View file

@ -109,7 +109,13 @@ EntityListTool = function(opts) {
}; };
webView.webEventReceived.connect(function(data) { 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") { if (data.type == "selectionUpdate") {
var ids = data.entityIds; var ids = data.entityIds;
var entityIDs = []; var entityIDs = [];

View file

@ -238,7 +238,13 @@ GridTool = function(opts) {
}); });
webView.webEventReceived.connect(function(data) { 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") { if (data.type == "init") {
horizontalGrid.emitUpdate(); horizontalGrid.emitUpdate();
} else if (data.type == "update") { } else if (data.type == "update") {