From 05f19d54f4b7ddabb6039b125d902dc4262b6092 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Jun 2017 11:55:04 +1200 Subject: [PATCH 01/17] Add HMD roll control JavaScript API --- interface/src/avatar/MyAvatar.h | 17 +++++++++++++++++ scripts/developer/hmdRollControlDisable.js | 17 +++++++++++++++++ scripts/developer/hmdRollControlEnable.js | 21 +++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 scripts/developer/hmdRollControlDisable.js create mode 100644 scripts/developer/hmdRollControlEnable.js diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cfe66eb10e..bfc7cd1085 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -132,6 +132,10 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) Q_PROPERTY(bool useAdvancedMovementControls READ useAdvancedMovementControls WRITE setUseAdvancedMovementControls) + Q_PROPERTY(bool hmdRollControlEnabled READ getHMDRollControlEnabled WRITE setHMDRollControlEnabled) + Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) + Q_PROPERTY(float hmdRollControlSpeed READ getHMDRollControlSpeed WRITE setHMDRollControlSpeed) + public: enum DriveKeys { TRANSLATE_X = 0, @@ -337,6 +341,13 @@ public: void setUseAdvancedMovementControls(bool useAdvancedMovementControls) { _useAdvancedMovementControls.set(useAdvancedMovementControls); } + void setHMDRollControlEnabled(bool value) { _hmdRollControlEnabled = value; } + bool getHMDRollControlEnabled() const { return _hmdRollControlEnabled; } + void setHMDRollControlDeadZone(float value) { _hmdRollControlDeadZone = value; } + float getHMDRollControlDeadZone() const { return _hmdRollControlDeadZone; } + void setHMDRollControlSpeed(float value) { _hmdRollControlSpeed = value; } + float getHMDRollControlSpeed() const { return _hmdRollControlSpeed; } + // get/set avatar data void saveData(); void loadData(); @@ -687,6 +698,12 @@ private: bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; + const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg + const float ROLL_CONTROL_SPEED_DEFAULT = 2.5f; // deg/sec/deg + bool _hmdRollControlEnabled { true }; + float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; + float _hmdRollControlSpeed { ROLL_CONTROL_SPEED_DEFAULT }; + // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; diff --git a/scripts/developer/hmdRollControlDisable.js b/scripts/developer/hmdRollControlDisable.js new file mode 100644 index 0000000000..9d87a62264 --- /dev/null +++ b/scripts/developer/hmdRollControlDisable.js @@ -0,0 +1,17 @@ +// +// hmdRollControlDisable.js +// +// Created by David Rowe on 4 Jun 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var hmdRollControlEnabled = false; + +print("HMD roll control: " + hmdRollControlEnabled); + +MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; + +Script.stop(); diff --git a/scripts/developer/hmdRollControlEnable.js b/scripts/developer/hmdRollControlEnable.js new file mode 100644 index 0000000000..d18e099b44 --- /dev/null +++ b/scripts/developer/hmdRollControlEnable.js @@ -0,0 +1,21 @@ +// +// hmdRollControlEnable.js +// +// Created by David Rowe on 4 Jun 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var hmdRollControlEnabled = true; +var hmdRollControlDeadZone = 8.0; // deg +var hmdRollControlSpeed = 1.0; // deg/sec/deg + +print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); + +MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; +MyAvatar.hmdRollControlDeadZone = hmdRollControlDeadZone; +MyAvatar.hmdRollControlSpeed = hmdRollControlSpeed; + +Script.stop(); From 6271b8ee7ca8f344ac49322b82aad985161574e7 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 13 Jun 2017 16:56:18 -0700 Subject: [PATCH 02/17] avatar whitelist --- assignment-client/src/avatars/AvatarMixer.cpp | 55 +++++++++++++++++-- assignment-client/src/avatars/AvatarMixer.h | 7 ++- .../src/avatars/AvatarMixerClientData.h | 3 + .../resources/describe-settings.json | 16 ++++++ libraries/avatars/src/AvatarData.cpp | 3 +- libraries/avatars/src/AvatarData.h | 7 +-- libraries/avatars/src/AvatarHashMap.cpp | 3 +- 7 files changed, 83 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 870149f1bc..b1841eb4e5 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -132,7 +132,7 @@ void AvatarMixer::start() { auto start = usecTimestampNow(); nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) { std::for_each(cbegin, cend, [&](const SharedNodePointer& node) { - manageDisplayName(node); + manageIdentityData(node); ++_sumListeners; }); }, &lockWait, &nodeTransform, &functor); @@ -183,8 +183,9 @@ void AvatarMixer::start() { // NOTE: nodeData->getAvatar() might be side effected, must be called when access to node/nodeData // is guaranteed to not be accessed by other thread -void AvatarMixer::manageDisplayName(const SharedNodePointer& node) { +void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); + bool sendIdentity = false; if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) { AvatarData& avatar = nodeData->getAvatar(); const QString& existingBaseDisplayName = nodeData->getBaseDisplayName(); @@ -210,9 +211,36 @@ void AvatarMixer::manageDisplayName(const SharedNodePointer& node) { soFar.second++; // refcount nodeData->flagIdentityChange(); nodeData->setAvatarSessionDisplayNameMustChange(false); - sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name. + sendIdentity = true; qCDebug(avatars) << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID(); } + if (nodeData && nodeData->getAvatarSkeletonModelUrlMustChange()) { // never true for an empty _avatarWhitelist + nodeData->setAvatarSkeletonModelUrlMustChange(false); + AvatarData& avatar = nodeData->getAvatar(); + static const QUrl emptyURL(""); + QUrl url = avatar.cannonicalSkeletonModelURL(emptyURL); + if (!isAvatarInWhitelist(url)) { + qCDebug(avatars) << "Forbidden avatar" << nodeData->getNodeID() << avatar.getSkeletonModelURL() << "replaced with" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); + avatar.setSkeletonModelURL(_replacementAvatar); + sendIdentity = true; + } + } + if (sendIdentity) { + sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar. + } +} + +bool AvatarMixer::isAvatarInWhitelist(const QUrl& url) { + for (const auto& whiteListedPrefix : _avatarWhitelist) { + auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); + // check if this script URL matches the whitelist domain and, optionally, is beneath the path + if (url.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && + url.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { + return true; + } + } + + return false; } void AvatarMixer::throttle(std::chrono::microseconds duration, int frame) { @@ -402,13 +430,17 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); bool identityChanged = false; bool displayNameChanged = false; - avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); + bool skeletonModelUrlChanged = false; + avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged, skeletonModelUrlChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->flagIdentityChange(); if (displayNameChanged) { nodeData->setAvatarSessionDisplayNameMustChange(true); } + if (skeletonModelUrlChanged && !_avatarWhitelist.isEmpty()) { + nodeData->setAvatarSkeletonModelUrlMustChange(true); + } } } } @@ -764,4 +796,19 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { qCDebug(avatars) << "This domain requires a minimum avatar scale of" << _domainMinimumScale << "and a maximum avatar scale of" << _domainMaximumScale; + const QString AVATAR_WHITELIST_DEFAULT{ "" }; + static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist"; + _avatarWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION].toString(AVATAR_WHITELIST_DEFAULT).split(',', QString::KeepEmptyParts); + + static const QString REPLACEMENT_AVATAR_OPTION = "replacement_avatar"; + _replacementAvatar = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION].toString(REPLACEMENT_AVATAR_DEFAULT); + + if ((_avatarWhitelist.count() == 1) && _avatarWhitelist[0].isEmpty()) { + _avatarWhitelist.clear(); // KeepEmptyParts above will parse "," as ["", ""] (which is ok), but "" as [""] (which is not ok). + } + if (_avatarWhitelist.isEmpty()) { + qCDebug(avatars) << "All avatars are allowed."; + } else { + qCDebug(avatars) << "Avatars other than" << _avatarWhitelist << "will be replaced by" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); + } } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 1925ec1ebd..f8ebe419a9 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -59,7 +59,12 @@ private: void parseDomainServerSettings(const QJsonObject& domainSettings); void sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); - void manageDisplayName(const SharedNodePointer& node); + void manageIdentityData(const SharedNodePointer& node); + bool isAvatarInWhitelist(const QUrl& url); + + const QString REPLACEMENT_AVATAR_DEFAULT{ "" }; + QStringList _avatarWhitelist { }; + QString _replacementAvatar { REPLACEMENT_AVATAR_DEFAULT }; p_high_resolution_clock::time_point _lastFrameTimestamp; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index c905b10251..cf2cdbfc23 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -65,6 +65,8 @@ public: void flagIdentityChange() { _identityChangeTimestamp = usecTimestampNow(); } bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; } void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; } + bool getAvatarSkeletonModelUrlMustChange() const { return _avatarSkeletonModelUrlMustChange; } + void setAvatarSkeletonModelUrlMustChange(bool set = true) { _avatarSkeletonModelUrlMustChange = set; } void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; } void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } @@ -146,6 +148,7 @@ private: uint64_t _identityChangeTimestamp; bool _avatarSessionDisplayNameMustChange{ true }; + bool _avatarSkeletonModelUrlMustChange{ true }; int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index a8c6dd84e7..a16d877f1c 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -866,6 +866,22 @@ "help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.", "placeholder": 3.0, "default": 3.0 + }, + { + "name": "avatar_whitelist", + "label": "Avatars Allowed from:", + "help": "Comma separated list of URLs (with optional paths) that avatar .fst files are allowed from. If someone attempts to use an avatar with a different domain, it will be rejected and the replacement avatar will be used. If left blank, any domain is allowed.", + "placeholder": "", + "default": "", + "advanced": true + }, + { + "name": "replacement_avatar", + "label": "Replacement Avatar for disallowed avatars", + "help": "A URL for an avatar .fst to be used when someone tries to use an avatar that is not allowed. If left blank, the generic defalt avatar is used.", + "placeholder": "", + "default": "", + "advanced": true } ] }, diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4407e12295..7731d53ec3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1504,7 +1504,7 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; } -void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) { +void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged) { if (identity.sequenceId < _identitySequenceId) { qCDebug(avatars) << "Ignoring older identity packet for avatar" << getSessionUUID() @@ -1517,6 +1517,7 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { setSkeletonModelURL(identity.skeletonModelURL); identityChanged = true; + skeletonModelUrlChanged = true; if (_firstSkeletonCheck) { displayNameChanged = true; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 4104615cfe..c64d7a00b8 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -368,6 +368,7 @@ public: virtual ~AvatarData(); static const QUrl& defaultFullAvatarModelUrl(); + QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; virtual bool isMyAvatar() const { return false; } @@ -536,9 +537,8 @@ public: static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut); - // identityChanged returns true if identity has changed, false otherwise. - // displayNameChanged returns true if displayName has changed, false otherwise. - void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged); + // identityChanged returns true if identity has changed, false otherwise. Similarly for displaNameChanged and skeletonModelUrlChange. + void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged); QByteArray identityByteArray() const; @@ -697,7 +697,6 @@ protected: QVector _attachmentData; QString _displayName; QString _sessionDisplayName { }; - QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; QHash _jointIndices; ///< 1-based, since zero is returned for missing keys QStringList _jointNames; ///< in order of depth-first traversal diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 2ccc64fee2..fb954f4731 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -148,8 +148,9 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer auto avatar = newOrExistingAvatar(identity.uuid, sendingNode); bool identityChanged = false; bool displayNameChanged = false; + bool skeletonModelUrlChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. - avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged); + avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged, skeletonModelUrlChanged); } } From be2bcb1c13d4f90739620637d3f859f9c41cfd84 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Jun 2017 12:20:46 +1200 Subject: [PATCH 03/17] Turn with head roll in HMD when walking or flying --- interface/src/avatar/MyAvatar.cpp | 26 ++++++++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 27 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 24a25f314d..bd217a98a2 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1961,6 +1961,32 @@ void MyAvatar::updateOrientation(float deltaTime) { totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI)); } + // Use head/HMD roll to turn while walking or flying. + if (qApp->isHMDMode() && _hmdRollControlEnabled) { + // Turn with head roll. + const float MIN_CONTROL_SPEED = 0.01f; + float speed = glm::length(getVelocity()); + if (speed >= MIN_CONTROL_SPEED) { + // Feather turn when stopping moving. + float speedFactor; + if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) { + _lastDrivenSpeed = speed; + speedFactor = 1.0; + } else { + speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f); + } + + float direction = glm::dot(getVelocity(), getRotation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f; + + float rollAngle = glm::degrees(asin(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT))); + float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f; + rollAngle = fabsf(rollAngle); + + float yawChange = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; + totalBodyYaw += speedFactor * direction * yawChange * deltaTime * _hmdRollControlSpeed; + } + } + // update body orientation by movement inputs glm::quat initialOrientation = getOrientationOutbound(); setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index bfc7cd1085..8039674605 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -703,6 +703,7 @@ private: bool _hmdRollControlEnabled { true }; float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; float _hmdRollControlSpeed { ROLL_CONTROL_SPEED_DEFAULT }; + float _lastDrivenSpeed { 0.0f }; // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; From 395d40f0f9026f4ef3935522b7a2a02f4d3405b0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Jun 2017 12:21:19 +1200 Subject: [PATCH 04/17] Fix script default to match C++ default --- scripts/developer/hmdRollControlEnable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/developer/hmdRollControlEnable.js b/scripts/developer/hmdRollControlEnable.js index d18e099b44..3cdc02a466 100644 --- a/scripts/developer/hmdRollControlEnable.js +++ b/scripts/developer/hmdRollControlEnable.js @@ -10,7 +10,7 @@ var hmdRollControlEnabled = true; var hmdRollControlDeadZone = 8.0; // deg -var hmdRollControlSpeed = 1.0; // deg/sec/deg +var hmdRollControlSpeed = 2.5; // deg/sec/deg print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); From 87f3ce7699031d751c75102010df67a226c167e5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 13 Jun 2017 19:30:01 -0700 Subject: [PATCH 05/17] reset avatar on domain change if the last domain imposed something on us --- interface/src/Application.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9ce6cc9b25..f1bef1a23b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5292,6 +5292,11 @@ void Application::nodeActivated(SharedNodePointer node) { if (node->getType() == NodeType::AvatarMixer) { // new avatar mixer, send off our identity packet on next update loop + // Reset skeletonModelUrl if the last server modified our choice. + static const QUrl empty{}; + if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->cannonicalSkeletonModelURL(empty)) { + getMyAvatar()->resetFullAvatarURL(); + } getMyAvatar()->markIdentityDataChanged(); getMyAvatar()->resetLastSent(); } From e3b4e4aa2045ac14ba7c31350164192a1b193798 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Jun 2017 15:37:36 +1200 Subject: [PATCH 06/17] Disable print statements --- scripts/developer/hmdRollControlDisable.js | 2 +- scripts/developer/hmdRollControlEnable.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/developer/hmdRollControlDisable.js b/scripts/developer/hmdRollControlDisable.js index 9d87a62264..fe8a85e3e5 100644 --- a/scripts/developer/hmdRollControlDisable.js +++ b/scripts/developer/hmdRollControlDisable.js @@ -10,7 +10,7 @@ var hmdRollControlEnabled = false; -print("HMD roll control: " + hmdRollControlEnabled); +//print("HMD roll control: " + hmdRollControlEnabled); MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; diff --git a/scripts/developer/hmdRollControlEnable.js b/scripts/developer/hmdRollControlEnable.js index 3cdc02a466..8af6cca6a5 100644 --- a/scripts/developer/hmdRollControlEnable.js +++ b/scripts/developer/hmdRollControlEnable.js @@ -12,7 +12,7 @@ var hmdRollControlEnabled = true; var hmdRollControlDeadZone = 8.0; // deg var hmdRollControlSpeed = 2.5; // deg/sec/deg -print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); +//print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; MyAvatar.hmdRollControlDeadZone = hmdRollControlDeadZone; From b3091de5369755df9730abaa6e9a361e9e26bf62 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 14 Jun 2017 11:16:55 -0700 Subject: [PATCH 07/17] forgot proper initial value of flag after changing whitelist to be actual list --- assignment-client/src/avatars/AvatarMixerClientData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index cf2cdbfc23..12b0286088 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -148,7 +148,7 @@ private: uint64_t _identityChangeTimestamp; bool _avatarSessionDisplayNameMustChange{ true }; - bool _avatarSkeletonModelUrlMustChange{ true }; + bool _avatarSkeletonModelUrlMustChange{ false }; int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; From 61eb93af357e500123ea6feb4377931f2da252d3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 6 Jun 2017 19:04:12 -0700 Subject: [PATCH 08/17] Fix SharedNodePointer leak --- libraries/networking/src/LimitedNodeList.cpp | 18 +++++++++++------- libraries/networking/src/Node.h | 2 +- .../networking/src/ReceivedPacketProcessor.h | 4 +++- libraries/shared/src/GenericThread.cpp | 2 ++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 93ae941f1e..f9baff0daf 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -584,7 +584,7 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t return matchingNode; } else { // we didn't have this node, so add them - Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, permissions, connectionSecret, this); + Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, permissions, connectionSecret); if (nodeType == NodeType::AudioMixer) { LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer); @@ -617,24 +617,28 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t } // insert the new node and release our read lock - _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); + _nodeHash.emplace(newNode->getUUID(), newNodePointer); readLocker.unlock(); qCDebug(networking) << "Added" << *newNode; + auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambdas to hold a strong ref + emit nodeAdded(newNodePointer); if (newNodePointer->getActiveSocket()) { emit nodeActivated(newNodePointer); } else { - connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [=] { - emit nodeActivated(newNodePointer); - disconnect(newNodePointer.data(), &NetworkPeer::socketActivated, this, 0); + connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [this, weakPtr] { + auto sharedPtr = weakPtr.lock(); + if (sharedPtr) { + emit nodeActivated(sharedPtr); + disconnect(sharedPtr.data(), &NetworkPeer::socketActivated, this, 0); + } }); } // Signal when a socket changes, so we can start the hole punch over. - auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambda to hold a strong ref - connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [=] { + connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [this, weakPtr] { emit nodeSocketUpdated(weakPtr); }); diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 1092fcc7fa..1bb3a0cdc8 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -40,7 +40,7 @@ public: Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, const NodePermissions& permissions, const QUuid& connectionSecret = QUuid(), - QObject* parent = 0); + QObject* parent = nullptr); bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } bool operator!=(const Node& otherNode) const { return !(*this == otherNode); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index dd790a9b3d..4e4a3d1d11 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -20,6 +20,8 @@ class ReceivedPacketProcessor : public GenericThread { Q_OBJECT public: + static const unsigned long MAX_WAIT_TIME { 100 }; + ReceivedPacketProcessor(); /// Add packet from network receive thread to the processing queue. @@ -64,7 +66,7 @@ protected: virtual bool process() override; /// Determines the timeout of the wait when there are no packets to process. Default value means no timeout - virtual unsigned long getMaxWait() const { return ULONG_MAX; } + virtual unsigned long getMaxWait() const { return MAX_WAIT_TIME; } /// Override to do work before the packets processing loop. Default does nothing. virtual void preProcess() { } diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index 00a80a2864..2e126f12c9 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -10,6 +10,7 @@ // #include +#include #include "GenericThread.h" @@ -73,6 +74,7 @@ void GenericThread::threadRoutine() { } while (!_stopThread) { + QCoreApplication::processEvents(); // override this function to do whatever your class actually does, return false to exit thread early if (!process()) { From a15660993df065308a3fd39d391aa37ece315966 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Jun 2017 10:54:22 -0700 Subject: [PATCH 09/17] Fix comment --- libraries/networking/src/ReceivedPacketProcessor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index 4e4a3d1d11..5b54d4f309 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -65,7 +65,7 @@ protected: /// Implements generic processing behavior for this thread. virtual bool process() override; - /// Determines the timeout of the wait when there are no packets to process. Default value means no timeout + /// Determines the timeout of the wait when there are no packets to process. Default value is 100ms to allow for regular event processing. virtual unsigned long getMaxWait() const { return MAX_WAIT_TIME; } /// Override to do work before the packets processing loop. Default does nothing. From 3cc23960217a93cb2403cda36b32dff286a4399d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 Jun 2017 11:26:40 -0700 Subject: [PATCH 10/17] CR --- assignment-client/src/octree/OctreeInboundPacketProcessor.cpp | 2 +- assignment-client/src/octree/OctreeInboundPacketProcessor.h | 2 +- libraries/networking/src/ReceivedPacketProcessor.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index d2fef4dfbd..04409b3b21 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -47,7 +47,7 @@ void OctreeInboundPacketProcessor::resetStats() { _singleSenderStats.clear(); } -unsigned long OctreeInboundPacketProcessor::getMaxWait() const { +uint32_t OctreeInboundPacketProcessor::getMaxWait() const { // calculate time until next sendNackPackets() quint64 nextNackTime = _lastNackTime + TOO_LONG_SINCE_LAST_NACK; quint64 now = usecTimestampNow(); diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.h b/assignment-client/src/octree/OctreeInboundPacketProcessor.h index 4611fcada0..a7fa297d24 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.h +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.h @@ -80,7 +80,7 @@ protected: virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode) override; - virtual unsigned long getMaxWait() const override; + virtual uint32_t getMaxWait() const override; virtual void preProcess() override; virtual void midProcess() override; diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index 5b54d4f309..f71abce1f1 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -20,7 +20,7 @@ class ReceivedPacketProcessor : public GenericThread { Q_OBJECT public: - static const unsigned long MAX_WAIT_TIME { 100 }; + static const uint64_t MAX_WAIT_TIME { 100 }; // Max wait time in ms ReceivedPacketProcessor(); @@ -66,7 +66,7 @@ protected: virtual bool process() override; /// Determines the timeout of the wait when there are no packets to process. Default value is 100ms to allow for regular event processing. - virtual unsigned long getMaxWait() const { return MAX_WAIT_TIME; } + virtual uint32_t getMaxWait() const { return MAX_WAIT_TIME; } /// Override to do work before the packets processing loop. Default does nothing. virtual void preProcess() { } From 5777b5e8050de9dcf22e0a3578df093274cae9f6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 Jun 2017 10:53:36 -0700 Subject: [PATCH 11/17] Create one packet per node --- libraries/octree/src/JurisdictionListener.cpp | 3 +-- libraries/octree/src/JurisdictionSender.cpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index dbbd146f4e..76c5069006 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -35,14 +35,13 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) { } bool JurisdictionListener::queueJurisdictionRequest() { - auto packet = NLPacket::create(PacketType::JurisdictionRequest, 0); - auto nodeList = DependencyManager::get(); int nodeCount = 0; nodeList->eachNode([&](const SharedNodePointer& node) { if (node->getType() == getNodeType() && node->getActiveSocket()) { + auto packet = NLPacket::create(PacketType::JurisdictionRequest, 0); _packetSender.queuePacketForSending(node, std::move(packet)); nodeCount++; } diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp index ed3d59cebc..dfe1a6d872 100644 --- a/libraries/octree/src/JurisdictionSender.cpp +++ b/libraries/octree/src/JurisdictionSender.cpp @@ -41,8 +41,6 @@ bool JurisdictionSender::process() { // call our ReceivedPacketProcessor base class process so we'll get any pending packets if (continueProcessing && (continueProcessing = ReceivedPacketProcessor::process())) { - auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket() - : JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType()); int nodeCount = 0; lockRequestingNodes(); @@ -53,6 +51,8 @@ bool JurisdictionSender::process() { SharedNodePointer node = DependencyManager::get()->nodeWithUUID(nodeUUID); if (node && node->getActiveSocket()) { + auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket() + : JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType()); _packetSender.queuePacketForSending(node, std::move(packet)); nodeCount++; } From 2deb26551f03e7bfc9cdcd022267781a83fcf052 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 14 Jun 2017 11:56:47 -0700 Subject: [PATCH 12/17] typos in comments/help-strings --- domain-server/resources/describe-settings.json | 2 +- libraries/avatars/src/AvatarData.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index a16d877f1c..c5e9b08143 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -878,7 +878,7 @@ { "name": "replacement_avatar", "label": "Replacement Avatar for disallowed avatars", - "help": "A URL for an avatar .fst to be used when someone tries to use an avatar that is not allowed. If left blank, the generic defalt avatar is used.", + "help": "A URL for an avatar .fst to be used when someone tries to use an avatar that is not allowed. If left blank, the generic default avatar is used.", "placeholder": "", "default": "", "advanced": true diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c64d7a00b8..8941d9d95f 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -537,7 +537,7 @@ public: static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut); - // identityChanged returns true if identity has changed, false otherwise. Similarly for displaNameChanged and skeletonModelUrlChange. + // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged); QByteArray identityByteArray() const; From 665870b846250aa136a1cc61dfc509f886102514 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 14 Jun 2017 12:06:29 -0700 Subject: [PATCH 13/17] comment per cr --- assignment-client/src/avatars/AvatarMixer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b1841eb4e5..c8b68a740c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -231,6 +231,9 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { } bool AvatarMixer::isAvatarInWhitelist(const QUrl& url) { + // The avatar is in the whitelist if: + // 1. The avatar's URL's host matches one of the hosts of the URLs in the whitelist AND + // 2. The avatar's URL's path starts with the path of that same URL in the whitelist for (const auto& whiteListedPrefix : _avatarWhitelist) { auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); // check if this script URL matches the whitelist domain and, optionally, is beneath the path From 7a6df850c5777c737bccb1f841fad6076cbb0a99 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Jun 2017 13:22:46 -0700 Subject: [PATCH 14/17] Fix crash on exit when tablet is open --- interface/src/Application.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9ce6cc9b25..56e8c8e2fb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1721,6 +1721,10 @@ void Application::cleanupBeforeQuit() { // Cleanup all overlays after the scripts, as scripts might add more _overlays.cleanupAllOverlays(); + // The cleanup process enqueues the transactions but does not process them. Calling this here will force the actual + // removal of the items. + // See https://highfidelity.fogbugz.com/f/cases/5328 + _main3DScene->processTransactionQueue(); // first stop all timers directly or by invokeMethod // depending on what thread they run in From 55e0082792605ee4713d7bc14ce96d77617466d7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 15 Jun 2017 09:10:08 +1200 Subject: [PATCH 15/17] Code review --- interface/src/avatar/MyAvatar.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bd217a98a2..b2c469f614 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1971,19 +1971,19 @@ void MyAvatar::updateOrientation(float deltaTime) { float speedFactor; if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) { _lastDrivenSpeed = speed; - speedFactor = 1.0; + speedFactor = 1.0f; } else { speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f); } float direction = glm::dot(getVelocity(), getRotation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f; - float rollAngle = glm::degrees(asin(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT))); + float rollAngle = glm::degrees(asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT))); float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f; rollAngle = fabsf(rollAngle); + rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; - float yawChange = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; - totalBodyYaw += speedFactor * direction * yawChange * deltaTime * _hmdRollControlSpeed; + totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlSpeed; } } From 16bebdd40945cf19878abf364f8dc4ad56f02e18 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 15 Jun 2017 09:20:55 +1200 Subject: [PATCH 16/17] Rename identifiers --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/MyAvatar.h | 10 +++++----- scripts/developer/hmdRollControlEnable.js | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b2c469f614..1adcfbd345 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1983,7 +1983,7 @@ void MyAvatar::updateOrientation(float deltaTime) { rollAngle = fabsf(rollAngle); rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; - totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlSpeed; + totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlRate; } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8039674605..f61f24fb11 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -134,7 +134,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hmdRollControlEnabled READ getHMDRollControlEnabled WRITE setHMDRollControlEnabled) Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) - Q_PROPERTY(float hmdRollControlSpeed READ getHMDRollControlSpeed WRITE setHMDRollControlSpeed) + Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate) public: enum DriveKeys { @@ -345,8 +345,8 @@ public: bool getHMDRollControlEnabled() const { return _hmdRollControlEnabled; } void setHMDRollControlDeadZone(float value) { _hmdRollControlDeadZone = value; } float getHMDRollControlDeadZone() const { return _hmdRollControlDeadZone; } - void setHMDRollControlSpeed(float value) { _hmdRollControlSpeed = value; } - float getHMDRollControlSpeed() const { return _hmdRollControlSpeed; } + void setHMDRollControlRate(float value) { _hmdRollControlRate = value; } + float getHMDRollControlRate() const { return _hmdRollControlRate; } // get/set avatar data void saveData(); @@ -699,10 +699,10 @@ private: bool _clearOverlayWhenMoving { true }; const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg - const float ROLL_CONTROL_SPEED_DEFAULT = 2.5f; // deg/sec/deg + const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg bool _hmdRollControlEnabled { true }; float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; - float _hmdRollControlSpeed { ROLL_CONTROL_SPEED_DEFAULT }; + float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT }; float _lastDrivenSpeed { 0.0f }; // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access diff --git a/scripts/developer/hmdRollControlEnable.js b/scripts/developer/hmdRollControlEnable.js index 8af6cca6a5..81318b7ddd 100644 --- a/scripts/developer/hmdRollControlEnable.js +++ b/scripts/developer/hmdRollControlEnable.js @@ -10,12 +10,12 @@ var hmdRollControlEnabled = true; var hmdRollControlDeadZone = 8.0; // deg -var hmdRollControlSpeed = 2.5; // deg/sec/deg +var hmdRollControlRate = 2.5; // deg/sec/deg -//print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); +//print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlRate); MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; MyAvatar.hmdRollControlDeadZone = hmdRollControlDeadZone; -MyAvatar.hmdRollControlSpeed = hmdRollControlSpeed; +MyAvatar.hmdRollControlRate = hmdRollControlRate; Script.stop(); From 9fa21fc79f8dc2ac6330327037b6da2fcd646819 Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 14 Jun 2017 14:45:25 -0700 Subject: [PATCH 17/17] Incomplete sandbox content updates are now removed and reperformed --- server-console/src/main.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 408a17bd56..95fb0d81b2 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -101,6 +101,10 @@ function getApplicationDataDirectory() { return path.join(getRootHifiDataDirectory(), '/Server Console'); } +// Update lock filepath +const UPDATER_LOCK_FILENAME = ".updating"; +const UPDATER_LOCK_FULL_PATH = getRootHifiDataDirectory() + "/" + UPDATER_LOCK_FILENAME; + // Configure log global.log = require('electron-log'); const logFile = getApplicationDataDirectory() + '/log.txt'; @@ -630,11 +634,22 @@ function checkNewContent() { userConfig.save(configPath); } }); + } else if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) { + backupResourceDirectoriesAndRestart(); } } }); } +function removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory) { + if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) { + log.debug('Removing incomplete content update files before copying new update'); + fs.emptyDirSync(dsResourceDirectory); + fs.emptyDirSync(acResourceDirectory); + } else { + fs.ensureFileSync(UPDATER_LOCK_FULL_PATH); + } +} function maybeInstallDefaultContentSet(onComplete) { // Check for existing data @@ -673,7 +688,11 @@ function maybeInstallDefaultContentSet(onComplete) { } log.debug("Found contentPath:" + argv.contentPath); + if (argv.contentPath) { + // check if we're updating a data folder whose update is incomplete + removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory); + fs.copy(argv.contentPath, getRootHifiDataDirectory(), function (err) { if (err) { log.debug('Could not copy home content: ' + err); @@ -682,12 +701,12 @@ function maybeInstallDefaultContentSet(onComplete) { log.debug('Copied home content over to: ' + getRootHifiDataDirectory()); userConfig.set('homeContentLastModified', new Date()); userConfig.save(configPath); + fs.removeSync(UPDATER_LOCK_FULL_PATH); onComplete(); }); return; } - // Show popup var window = new BrowserWindow({ icon: appIcon, @@ -718,6 +737,9 @@ function maybeInstallDefaultContentSet(onComplete) { var aborted = false; + // check if we're updating a data folder whose update is incomplete + removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory); + // Start downloading content set var req = progress(request.get({ url: HOME_CONTENT_URL @@ -763,6 +785,7 @@ function maybeInstallDefaultContentSet(onComplete) { log.debug("Finished unarchiving home content set"); userConfig.set('homeContentLastModified', new Date()); userConfig.save(configPath); + fs.removeSync(UPDATER_LOCK_FULL_PATH); sendStateUpdate('complete'); });