From de97af5c022e5c8217c20d1a5d63cd10e052d399 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 24 Apr 2019 17:42:51 -0700 Subject: [PATCH] Minimal working version for avatar's client Also merge of Wayne's proof-of-concept --- assignment-client/src/avatars/AvatarMixer.cpp | 8 +- assignment-client/src/avatars/MixerAvatar.cpp | 58 ++++--- assignment-client/src/avatars/MixerAvatar.h | 15 +- .../resources/images/AvatarTheftBanner.png | Bin 0 -> 49298 bytes interface/resources/qml/AvatarTheft.qml | 70 +++++++++ interface/resources/qml/AvatarTheftBanner.qml | 67 ++++++++ .../resources/qml/AvatarTheftSettings.qml | 139 +++++++++++++++++ interface/src/Application.cpp | 1 + interface/src/avatar/AvatarManager.cpp | 8 + .../developer/tests/avatarTheftPrototype.js | 144 ++++++++++++++++++ 10 files changed, 481 insertions(+), 29 deletions(-) create mode 100644 interface/resources/images/AvatarTheftBanner.png create mode 100644 interface/resources/qml/AvatarTheft.qml create mode 100644 interface/resources/qml/AvatarTheftBanner.qml create mode 100644 interface/resources/qml/AvatarTheftSettings.qml create mode 100644 scripts/developer/tests/avatarTheftPrototype.js diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b804e4a20f..96e225c7e7 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -368,9 +368,10 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { return; } - bool sendIdentity = false; + MixerAvatar& avatar = nodeData->getAvatar(); + bool sendIdentity = avatar.needsIdentityUpdate(); if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) { - AvatarData& avatar = nodeData->getAvatar(); + MixerAvatar& avatar = nodeData->getAvatar(); const QString& existingBaseDisplayName = nodeData->getAvatar().getSessionDisplayName(); if (!existingBaseDisplayName.isEmpty()) { SessionDisplayName existingDisplayName { existingBaseDisplayName }; @@ -415,10 +416,11 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar. // since this packet includes a change to either the skeleton model URL or the display name // it needs a new sequence number - nodeData->getAvatar().pushIdentitySequenceNumber(); + avatar.pushIdentitySequenceNumber(); // tell node whose name changed about its new session display name or avatar. sendIdentityPacket(nodeData, node); + avatar.clearIdentityUpdate(); } } diff --git a/assignment-client/src/avatars/MixerAvatar.cpp b/assignment-client/src/avatars/MixerAvatar.cpp index 3f57bbe3e9..9ad4a0cfd3 100644 --- a/assignment-client/src/avatars/MixerAvatar.cpp +++ b/assignment-client/src/avatars/MixerAvatar.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -78,6 +79,7 @@ void MixerAvatar::fstRequestComplete() { } else { _avatarFSTContents = fstRequest->getData(); _verifyState = kReceivedFST; + _pendingEvent = true; } _avatarRequest->deleteLater(); _avatarRequest = nullptr; @@ -156,12 +158,31 @@ QByteArray MixerAvatar::canonicalJson(const QString fstFile) { return jsonDocCertifiedItems.toJson(QJsonDocument::Compact); } -void MixerAvatar::processCertifyEvents() { +void MixerAvatar::ownerRequestComplete() { QMutexLocker certifyLocker(&_avatarCertifyLock); - if (_verifyState != kReceivedFST && _verifyState != kOwnerResponse && _verifyState != kChallengeResponse && _verifyState != kRequestingOwner) { + QNetworkReply* networkReply = static_cast(QObject::sender()); + + if (networkReply->error() == QNetworkReply::NoError) { + _dynamicMarketResponse = networkReply->readAll(); + _verifyState = kOwnerResponse; + _pendingEvent = true; + } else { + auto jsonData = QJsonDocument::fromJson(networkReply->readAll())["data"]; + if (!jsonData.isUndefined() && !jsonData.toObject()["message"].isUndefined()) { + qCDebug(avatars) << "Owner lookup failed for" << getDisplayName() << ":" + << jsonData.toObject()["message"].toString(); + _verifyState = kError; + } + } + networkReply->deleteLater(); +} + +void MixerAvatar::processCertifyEvents() { + if (!_pendingEvent) { return; } + QMutexLocker certifyLocker(&_avatarCertifyLock); switch (_verifyState) { case kReceivedFST: @@ -185,24 +206,10 @@ void MixerAvatar::processCertifyEvents() { request["certificate_id"] = _certificateIdFromFST; _verifyState = kRequestingOwner; QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - //networkReply->setParent(this); - connect(networkReply, &QNetworkReply::readyRead, [this, networkReply]() { - QMutexLocker certifyLocker(&_avatarCertifyLock); - if (networkReply->error() == QNetworkReply::NoError) { - _dynamicMarketResponse = networkReply->readAll(); - _verifyState = kOwnerResponse; - } else { - auto jsonData = QJsonDocument::fromJson(networkReply->readAll())["data"]; - if (!jsonData.isUndefined() && !jsonData.toObject()["message"].isUndefined()) { - qCDebug(avatars) << "Owner lookup failed for" << getDisplayName() << ":" - << jsonData.toObject()["message"].toString(); - _verifyState = kError; - } - } - networkReply->deleteLater(); - }); + connect(networkReply, &QNetworkReply::finished, this, &MixerAvatar::ownerRequestComplete); } else { - _verifyState = kVerificationFailedPending; + _verifyState = kVerificationFailed; + _pendingEvent = false; qCDebug(avatars) << "Avatar" << getDisplayName() << "FAILED static certification"; } break; @@ -244,6 +251,7 @@ void MixerAvatar::processCertifyEvents() { "message:" << responseJson["message"].toString(); _verifyState = kError; } + _pendingEvent = false; break; } @@ -266,19 +274,19 @@ void MixerAvatar::processCertifyEvents() { bool challengeResult = EntityItemProperties::verifySignature(_ownerPublicKey, _challengeNonceHash, QByteArray::fromBase64(signedNonce)); - _verifyState = challengeResult ? kVerificationSucceeded : kVerificationFailedPending; - if (_verifyState == kVerificationFailedPending) { + _verifyState = challengeResult ? kVerificationSucceeded : kVerificationFailed; + _needsIdentityUpdate = true; + if (_verifyState == kVerificationFailed) { qCDebug(avatars) << "Dynamic verification FAILED for " << getDisplayName() << getSessionUUID(); } else { qCDebug(avatars) << "Dynamic verification SUCCEEDED for " << getDisplayName() << getSessionUUID(); } - + _pendingEvent = false; break; } case kRequestingOwner: - { - certifyLocker.unlock(); + { // Qt networking done on this thread: QCoreApplication::processEvents(); break; } @@ -307,6 +315,7 @@ void MixerAvatar::sendOwnerChallenge() { _challengeTimeout.setInterval(CHALLENGE_TIMEOUT_MS); _challengeTimeout.connect(&_challengeTimeout, &QTimer::timeout, [this]() { _verifyState = kVerificationFailed; + _needsIdentityUpdate = true; }); } @@ -318,5 +327,6 @@ void MixerAvatar::handleChallengeResponse(ReceivedMessage * response) { _challengeTimeout.stop(); _challengeResponse = response->readAll(); _verifyState = kChallengeResponse; + _pendingEvent = true; } } diff --git a/assignment-client/src/avatars/MixerAvatar.h b/assignment-client/src/avatars/MixerAvatar.h index 8979d5c9ad..5a81001ea9 100644 --- a/assignment-client/src/avatars/MixerAvatar.h +++ b/assignment-client/src/avatars/MixerAvatar.h @@ -25,7 +25,15 @@ public: void setNeedsHeroCheck(bool needsHeroCheck = true) { _needsHeroCheck = needsHeroCheck; } void fetchAvatarFST(); - virtual bool isCertifyFailed() const override { return _verifyState == kVerificationFailed || _verifyState == kVerificationFailedPending; } + virtual bool isCertifyFailed() const override { return _verifyState == kVerificationFailed; } + bool needsIdentityUpdate() const { return _needsIdentityUpdate; } + void clearIdentityUpdate() { _needsIdentityUpdate = false; } + + + //bool isPendingCertifyFailed() const { return _verifyState == kVerificationFailedPending; } + //void advanceCertifyFailed() { + // if (isPendingCertifyFailed()) { _verifyState = kVerificationFailed; } + //} void processCertifyEvents(); void handleChallengeResponse(ReceivedMessage * response); @@ -34,10 +42,11 @@ private: // Avatar certification/verification: enum VerifyState { kNoncertified, kRequestingFST, kReceivedFST, kStaticValidation, kRequestingOwner, kOwnerResponse, - kChallengeClient, kChallengeResponse, kVerified, kVerificationFailedPending, kVerificationFailed, + kChallengeClient, kChallengeResponse, kVerified, kVerificationFailed, kVerificationSucceeded, kError }; Q_ENUM(VerifyState); VerifyState _verifyState { kNoncertified }; + std::atomic _pendingEvent { false }; QMutex _avatarCertifyLock; ResourceRequest* _avatarRequest { nullptr }; QString _marketplaceIdFromURL; @@ -51,6 +60,7 @@ private: QByteArray _challengeNonceHash; QByteArray _challengeResponse; QTimer _challengeTimeout; + bool _needsIdentityUpdate { false }; bool generateFSTHash(); bool validateFSTHash(const QString& publicKey); @@ -59,6 +69,7 @@ private: private slots: void fstRequestComplete(); + void ownerRequestComplete(); }; using MixerAvatarSharedPointer = std::shared_ptr; diff --git a/interface/resources/images/AvatarTheftBanner.png b/interface/resources/images/AvatarTheftBanner.png new file mode 100644 index 0000000000000000000000000000000000000000..3dc76999e084f73842d4d9d53c2600599bda2bbe GIT binary patch literal 49298 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 literal 0 HcmV?d00001 diff --git a/interface/resources/qml/AvatarTheft.qml b/interface/resources/qml/AvatarTheft.qml new file mode 100644 index 0000000000..5c67e64589 --- /dev/null +++ b/interface/resources/qml/AvatarTheft.qml @@ -0,0 +1,70 @@ +import QtQuick 2.7 +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit + +Rectangle { + color: "black" + height: 480 + width: 720 + + readonly property string avatarTheftEntityName: "hifi-avatar-theft-banner"; + + HifiStylesUit.RalewayRegular { + id: displayMessage; + text: "The avatar you're using is registered to another user."; + size: 20; + color: "white"; + anchors.top: parent.top; + anchors.topMargin: 0.1 * parent.height; + anchors.left: parent.left; + anchors.leftMargin: (parent.width - width) / 2 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + HifiStylesUit.ShortcutText { + id: gotoShortcut; + anchors.top: parent.top; + anchors.topMargin: 0.4 * parent.height; + anchors.left: parent.left; + anchors.leftMargin: (parent.width - width) / 2 + font.family: "Raleway" + font.pixelSize: 20 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + linkColor: hifi.colors.blueAccent + text: "Click here to change your avatar and dismiss this banner." + onLinkActivated: { + AddressManager.handleLookupString("hifi://BodyMart"); + } + } + + HifiStylesUit.RalewayRegular { + id: contactText; + text: "If you own this avatar, please contact" + size: 20; + anchors.bottom: parent.bottom + anchors.bottomMargin: 0.15 * parent.height + anchors.left: parent.left; + anchors.leftMargin: (parent.width - width) / 2 + } + + HifiStylesUit.ShortcutText { + id: gotoShortcut; + anchors.top: contactText.bottom; + anchors.left: parent.left; + anchors.leftMargin: (parent.width - width) / 2 + font.family: "Raleway" + font.pixelSize: 20 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + linkColor: hifi.colors.blueAccent + text: "Click here to change your avatar and dismiss this banner." + onLinkActivated: { + HiFiAbout.openUrl("mailto:support@highfidelity.com"); + } + } +} diff --git a/interface/resources/qml/AvatarTheftBanner.qml b/interface/resources/qml/AvatarTheftBanner.qml new file mode 100644 index 0000000000..e2c437487e --- /dev/null +++ b/interface/resources/qml/AvatarTheftBanner.qml @@ -0,0 +1,67 @@ +import QtQuick 2.7 +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit + +Rectangle { + color: "black" + + HifiStylesUit.HifiConstants { id: hifi; } + + HifiStylesUit.RalewayRegular { + id: displayMessage; + text: "The avatar you're using is registered to another user."; + size: 20; + color: "white"; + anchors.top: parent.top; + anchors.topMargin: 0.1 * parent.height; + anchors.left: parent.left; + anchors.leftMargin: (parent.width - width) / 2 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + HifiStylesUit.ShortcutText { + id: gotoShortcut; + anchors.top: parent.top; + anchors.topMargin: 0.4 * parent.height; + anchors.left: parent.left; + anchors.leftMargin: (parent.width - width) / 2 + font.family: "Raleway" + font.pixelSize: 20 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + linkColor: hifi.colors.blueAccent + text: "Click here to change your avatar and dismiss this banner." + onLinkActivated: { + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + tablet.loadQMLSource("hifi/AvatarApp.qml"); + } + } + + HifiStylesUit.RalewayRegular { + id: contactText; + text: "If you own this avatar, please contact" + size: 20; + color: "white" + anchors.bottom: parent.bottom + anchors.bottomMargin: 0.15 * parent.height + anchors.left: parent.left; + anchors.leftMargin: (parent.width - width) / 2 + } + + HifiStylesUit.ShortcutText { + id: email; + anchors.top: contactText.bottom; + anchors.left: parent.left; + anchors.leftMargin: (parent.width - width) / 2 + font.family: "Raleway" + font.pixelSize: 20 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + linkColor: hifi.colors.blueAccent + text: "support@highfidelity.com." + } +} diff --git a/interface/resources/qml/AvatarTheftSettings.qml b/interface/resources/qml/AvatarTheftSettings.qml new file mode 100644 index 0000000000..849e611af2 --- /dev/null +++ b/interface/resources/qml/AvatarTheftSettings.qml @@ -0,0 +1,139 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 + +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit + +Rectangle { + id: root; + + HifiConstants { id: hifi; } + + color: hifi.colors.baseGray; + + signal sendToScript(var message); + function emitSendToScript(message) { + sendToScript(message); + } + + function fromScript(message) { + } + + RalewayRegular { + id: title; + color: hifi.colors.white; + text: qsTr("Avatar Theft Entity position") + size: 20 + font.bold: true + anchors { + top: parent.top + left: parent.left + leftMargin: (parent.width - width) / 2 + } + } + + HifiControlsUit.Slider { + id: xSlider + anchors { + top: title.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + label: "X OFFSET: " + value.toFixed(2); + maximumValue: 1.0 + minimumValue: -1.0 + stepSize: 0.05 + value: 0.0 + width: 300 + onValueChanged: { + emitSendToScript({ + "method": "reposition", + "x": value + }); + } + } + + HifiControlsUit.Slider { + id: ySlider + anchors { + top: xSlider.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + label: "Y OFFSET: " + value.toFixed(2); + maximumValue: 1.0 + minimumValue: -1.0 + stepSize: 0.05 + value: 0.0 + width: 300 + onValueChanged: { + emitSendToScript({ + "method": "reposition", + "y": value + }); + } + } + + HifiControlsUit.Slider { + id: zSlider + anchors { + top: ySlider.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + label: "Z OFFSET: " + value.toFixed(2); + maximumValue: 0.0 + minimumValue: -2.0 + stepSize: 0.05 + value: -1.0 + width: 300 + onValueChanged: { + emitSendToScript({ + "method": "reposition", + "z": value + }); + } + } + + HifiControlsUit.Button { + id: setVisibleButton; + text: setVisible ? "SET INVISIBLE" : "SET VISIBLE"; + width: 300; + property bool setVisible: true; + anchors { + top: zSlider.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + onClicked: { + setVisible = !setVisible; + emitSendToScript({ + "method": "setVisible", + "visible": setVisible + }); + } + } + + HifiControlsUit.Button { + id: printButton; + text: "PRINT POSITIONS"; + width: 300; + anchors { + top: setVisibleButton.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + onClicked: { + emitSendToScript({ + "method": "print", + }); + } + } +} + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7ba45da1fd..55a6fc6672 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3368,6 +3368,7 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona surfaceContext->setContextProperty("KeyboardScriptingInterface", DependencyManager::get().data()); if (setAdditionalContextProperties) { + qDebug() << "setting additional context properties!"; auto tabletScriptingInterface = DependencyManager::get(); auto flags = tabletScriptingInterface->getFlags(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 8274259922..390500dd2a 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -46,6 +46,7 @@ #include "MyAvatar.h" #include "DebugDraw.h" #include "SceneScriptingInterface.h" +#include "ui/AvatarCertifyBanner.h" // 50 times per second - target is 45hz, but this helps account for any small deviations // in the update loop - this also results in ~30hz when in desktop mode which is essentially @@ -176,6 +177,13 @@ void AvatarManager::updateMyAvatar(float deltaTime) { _lastSendAvatarDataTime = now; _myAvatarSendRate.increment(); } + + static AvatarCertifyBanner theftBanner; + if (_myAvatar->isCertifyFailed()) { + theftBanner.show(_myAvatar->getSessionUUID(), _myAvatar->getJointIndex("_CAMERA_MATRIX")); + } else { + theftBanner.clear(); + } } diff --git a/scripts/developer/tests/avatarTheftPrototype.js b/scripts/developer/tests/avatarTheftPrototype.js new file mode 100644 index 0000000000..0ee5795a17 --- /dev/null +++ b/scripts/developer/tests/avatarTheftPrototype.js @@ -0,0 +1,144 @@ +"use strict"; + +(function() { + + var appUi = Script.require("appUi"); + var ui; + + var AVATAR_THEFT_BANNER_IMAGE = Script.resourcesPath() + "images/AvatarTheftBanner.png"; + var AVATAR_THEFT_SETTINGS_QML = Script.resourcesPath() + "qml/AvatarTheftSettings.qml"; + var button; + var theftBanner = null; + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var onAvatarBanner = false; + var THEFT_BANNER_DIMENSIONS = {x: 1.0, y: 1.0, z: 0.3}; + + function createEntities() { + //if (HMD.active) { + var render = tablet.tabletShown ? "world" : "front"; + var pos = tablet.tabletShown ? { x: 0.0, y: 0.0, z: -1.8 } : { x: 0.0, y: 0.0, z: -0.7 }; + var dimensionMultiplier = tablet.tabletShown ? 2.6 : 1; + var props = { + type: "Image", + imageURL: AVATAR_THEFT_BANNER_IMAGE, + name: "hifi-avatar-theft-banner", + parentID: MyAvatar.SELF_ID, + parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX"), + localPosition: pos, + dimensions: {x: THEFT_BANNER_DIMENSIONS.x * dimensionMultiplier, y: THEFT_BANNER_DIMENSIONS.y * dimensionMultiplier}, + renderLayer: render, + userData: { + grabbable: false + }, + visible: true + }; +/* var props = {*/ + //type: "Web", + //name: "hifi-avatar-theft-banner", + //sourceUrl: AVATAR_THEFT_BANNER_QML, + //parentID: MyAvatar.SELF_ID, + //parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX"), + //localPosition: { x: 0.0, y: 0.0, z: -1.0 }, + //dimensions: THEFT_BANNER_DIMENSIONS, + //drawInFront: true, + //userData: { + //grabbable: false + //}, + //visible: true + /*};*/ + if (theftBanner) { + Entities.deleteEntity(theftBanner); + } + theftBanner = Entities.addEntity(props, "local"); + Window.copyToClipboard(theftBanner); + console.log("created entity"); + //} else { + //} + } + + function fromQml(message) { + if (message.method === "reposition") { + var theftBannerLocalPosition = Entities.getEntityProperties(theftBanner).localPosition; + var newTheftBannerLocalPosition; + if (message.x !== undefined) { + newTheftBannerLocalPosition = { x: -((THEFT_BANNER_DIMENSIONS.x) / 2) + message.x, y: theftBannerLocalPosition.y, z: theftBannerLocalPosition.z }; + } else if (message.y !== undefined) { + newtheftBannerLocalPosition = { x: theftBannerLocalPosition.x, y: message.y, z: theftBannerLocalPosition.z }; + } else if (message.z !== undefined) { + newtheftBannerLocalPosition = { x: theftBannerLocalPosition.x, y: theftBannerLocalPosition.y, z: message.z }; + } + var theftBannerProps = { + localPosition: newtheftBannerLocalPosition + }; + + Entities.editEntity(theftBanner, theftBannerProps); + } else if (message.method === "setVisible") { + if (message.visible !== undefined) { + var props = { + visible: message.visible + }; + Entities.editEntity(theftBannerEntity, props); + } + } else if (message.method === "print") { + // prints the local position into the hifi log. + var theftBannerLocalPosition = Entities.getEntityProperties(theftBannerEntity).localPosition; + console.log("theft banner local position is at " + JSON.stringify(theftBannerLocalPosition)); + } + }; + + var cleanup = function () { + if (theftBanner) { + Entities.deleteEntity(theftBanner); + } + }; + + function setup() { + ui = new appUi({ + buttonName: "THEFT", + home: AVATAR_THEFT_SETTINGS_QML, + onMessage: fromQml, + onOpened: createEntities, + onClosed: cleanup, + normalButton: "icons/tablet-icons/edit-i.svg", + activeButton: "icons/tablet-icons/edit-a.svg", + }); + }; + + setup(); + + Entities.mousePressOnEntity.connect(function (entityID, event) { + if (entityID === theftBanner && theftBanner){ + tablet.loadQMLSource(Script.resourcesPath() + "qml/hifi/AvatarApp.qml"); + } + }) + + tablet.isTabletShownChanged.connect(function () { + if (theftBanner) { + if (tablet.tabletShown) { + Entities.editEntity(theftBanner, { + dimensions: THEFT_BANNER_DIMENSIONS, + localPosition: { x: 0.0, y: 0.0, z: -0.7 }, + renderLayer: "world" + }); + } else { + Entities.editEntity(theftBanner, { + localPosition: { x: 0.0, y: 0.0, z: -1.8 }, + dimensions: {x: THEFT_BANNER_DIMENSIONS.x * 2.6, y: THEFT_BANNER_DIMENSIONS.y * 2.6}, + renderLayer: "front" + }); + } + console.log(Entities.getEntityProperties(theftBanner).renderLayer); + } + }) + + Script.scriptEnding.connect(cleanup); + + location.hostChanged.connect(function (){ + if (theftBanner) { + Entities.editEntity(theftBanner, { + visible: false + }); + } + }) + +}());