Merge branch 'master' of https://github.com/highfidelity/hifi into commerce_inspectionCertRedesign

This commit is contained in:
Zach Fox 2018-02-01 13:15:38 -08:00
commit d4f9287e3f
38 changed files with 322 additions and 31156 deletions

View file

@ -453,7 +453,7 @@ void EntityServer::domainSettingsRequestFailed() {
void EntityServer::startDynamicDomainVerification() {
qCDebug(entities) << "Starting Dynamic Domain Verification...";
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainId().remove(QRegExp("\\{|\\}"));
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());

View file

@ -16,6 +16,7 @@ CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(q
}
WalletScriptingInterface::WalletScriptingInterface() {
}
void WalletScriptingInterface::refreshWalletStatus() {
@ -26,4 +27,19 @@ void WalletScriptingInterface::refreshWalletStatus() {
void WalletScriptingInterface::setWalletStatus(const uint& status) {
_walletStatus = status;
emit DependencyManager::get<Wallet>()->walletStatusResult(status);
}
void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUuid& entityID) {
QSharedPointer<ContextOverlayInterface> contextOverlayInterface = DependencyManager::get<ContextOverlayInterface>();
EntityItemProperties entityProperties = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID,
contextOverlayInterface->getEntityPropertyFlags());
if (entityProperties.getClientOnly()) {
if (!entityID.isNull() && entityProperties.getCertificateID().length() > 0) {
contextOverlayInterface->requestOwnershipVerification(entityID);
} else {
qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is null or not a certified item";
}
} else {
qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity";
}
}

View file

@ -21,6 +21,7 @@
#include <OffscreenUi.h>
#include "Application.h"
#include "commerce/Wallet.h"
#include "ui/overlays/ContextOverlayInterface.h"
class CheckoutProxy : public QmlWrapper {
Q_OBJECT
@ -39,6 +40,7 @@ public:
Q_INVOKABLE void refreshWalletStatus();
Q_INVOKABLE uint getWalletStatus() { return _walletStatus; }
Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID);
// setWalletStatus() should never be made Q_INVOKABLE. If it were,
// scripts could cause the Wallet to incorrectly report its status.
void setWalletStatus(const uint& status);
@ -46,6 +48,8 @@ public:
signals:
void walletStatusChanged();
void walletNotSetup();
void ownershipVerificationSuccess(const QUuid& entityID);
void ownershipVerificationFailed(const QUuid& entityID);
private:
uint _walletStatus;

View file

@ -38,7 +38,7 @@ const QString FATAL_TEXT = "[FATAL]";
const QString SUPPRESS_TEXT = "[SUPPRESS]";
const QString UNKNOWN_TEXT = "[UNKNOWN]";
LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLogDialog(parent) {
LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLogDialog(parent), _windowGeometry("logDialogGeometry", QRect()) {
_logger = logger;
setWindowTitle("Log");
@ -155,6 +155,11 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog
_clearFilterButton->show();
connect(_clearFilterButton, &QPushButton::clicked, this, &LogDialog::handleClearFilterButton);
handleClearFilterButton();
auto windowGeometry = _windowGeometry.get();
if (windowGeometry.isValid()) {
setGeometry(windowGeometry);
}
}
void LogDialog::resizeEvent(QResizeEvent* event) {
@ -173,6 +178,11 @@ void LogDialog::resizeEvent(QResizeEvent* event) {
ELEMENT_HEIGHT);
}
void LogDialog::closeEvent(QCloseEvent* event) {
BaseLogDialog::closeEvent(event);
_windowGeometry.set(geometry());
}
void LogDialog::handleRevealButton() {
_logger->locateLog();
}

View file

@ -13,6 +13,7 @@
#define hifi_LogDialog_h
#include "BaseLogDialog.h"
#include <SettingHandle.h>
class QCheckBox;
class QPushButton;
@ -44,6 +45,8 @@ private slots:
protected:
void resizeEvent(QResizeEvent* event) override;
void closeEvent(QCloseEvent* event) override;
QString getCurrentLog() override;
void printLogFile();
@ -62,6 +65,7 @@ private:
QString _filterSelection;
AbstractLoggerInterface* _logger;
Setting::Handle<QRect> _windowGeometry;
};
#endif // hifi_LogDialog_h

View file

@ -266,6 +266,93 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI
}
}
void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID) {
setLastInspectedEntity(entityID);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
auto nodeList = DependencyManager::get<NodeList>();
if (entityProperties.getClientOnly()) {
if (entityProperties.verifyStaticCertificateProperties()) {
SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer);
if (entityServer) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
QJsonObject request;
request["certificate_id"] = entityProperties.getCertificateID();
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = NULL;
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, [=]() {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
if (networkReply->error() == QNetworkReply::NoError) {
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
qCDebug(entities) << "invalid_reason not empty";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
qCDebug(entities) << "'transfer_status' is 'failed'";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
qCDebug(entities) << "'transfer_status' is 'pending'";
} else {
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
QByteArray certID = entityProperties.getCertificateID().toUtf8();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
int certIDByteArraySize = certID.length();
int textByteArraySize = text.length();
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(textByteArraySize);
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(text);
challengeOwnershipPacket->write(nodeToChallengeByteArray);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
return;
} else {
startChallengeOwnershipTimer();
}
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
"More info:" << networkReply->readAll();
}
networkReply->deleteLater();
});
} else {
qCWarning(context_overlay) << "Couldn't get Entity Server!";
}
} else {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
}
}
}
static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml";
void ContextOverlayInterface::openInspectionCertificate() {
// lets open the tablet to the inspection certificate QML
@ -275,87 +362,7 @@ void ContextOverlayInterface::openInspectionCertificate() {
_hmdScriptingInterface->openTablet();
setLastInspectedEntity(_currentEntityWithContextOverlay);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
auto nodeList = DependencyManager::get<NodeList>();
if (entityProperties.getClientOnly()) {
if (entityProperties.verifyStaticCertificateProperties()) {
SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer);
if (entityServer) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
QJsonObject request;
request["certificate_id"] = entityProperties.getCertificateID();
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = NULL;
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, [=]() {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
if (networkReply->error() == QNetworkReply::NoError) {
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
qCDebug(entities) << "invalid_reason not empty";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
qCDebug(entities) << "'transfer_status' is 'failed'";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
qCDebug(entities) << "'transfer_status' is 'pending'";
} else {
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
QByteArray certID = entityProperties.getCertificateID().toUtf8();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
int certIDByteArraySize = certID.length();
int textByteArraySize = text.length();
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(textByteArraySize);
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(text);
challengeOwnershipPacket->write(nodeToChallengeByteArray);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
return;
} else {
startChallengeOwnershipTimer();
}
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
"More info:" << networkReply->readAll();
}
networkReply->deleteLater();
});
} else {
qCWarning(context_overlay) << "Couldn't get Entity Server!";
}
} else {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
}
}
requestOwnershipVerification(_lastInspectedEntity);
}
}
@ -397,6 +404,7 @@ void ContextOverlayInterface::startChallengeOwnershipTimer() {
connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() {
qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity;
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
});
_challengeOwnershipTimeoutTimer.start(5000);
@ -421,7 +429,9 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer
if (verificationSuccess) {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationSuccess(_lastInspectedEntity);
} else {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
}
}

View file

@ -26,6 +26,7 @@
#include "ui/overlays/Overlays.h"
#include "scripting/HMDScriptingInterface.h"
#include "scripting/SelectionScriptingInterface.h"
#include "scripting/WalletScriptingInterface.h"
#include "EntityTree.h"
#include "ContextOverlayLogging.h"
@ -33,12 +34,13 @@
/**jsdoc
* @namespace ContextOverlay
*/
class ContextOverlayInterface : public QObject, public Dependency {
class ContextOverlayInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay)
Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled)
Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode)
QSharedPointer<EntityScriptingInterface> _entityScriptingInterface;
EntityPropertyFlags _entityPropertyFlags;
QSharedPointer<HMDScriptingInterface> _hmdScriptingInterface;
@ -47,9 +49,7 @@ class ContextOverlayInterface : public QObject, public Dependency {
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
std::shared_ptr<Image3DOverlay> _contextOverlay { nullptr };
public:
ContextOverlayInterface();
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; }
void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; }
@ -57,6 +57,8 @@ public:
bool getEnabled() { return _enabled; }
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; }
void requestOwnershipVerification(const QUuid& entityID);
EntityPropertyFlags getEntityPropertyFlags() { return _entityPropertyFlags; }
signals:
void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay);
@ -80,8 +82,7 @@ private:
enum {
MAX_SELECTION_COUNT = 16
};
bool _verboseLogging { true };
bool _verboseLogging{ true };
bool _enabled { true };
EntityItemID _currentEntityWithContextOverlay{};
EntityItemID _lastInspectedEntity{};

View file

@ -76,7 +76,9 @@ Web3DOverlay::Web3DOverlay() {
//need to be intialized before Tablet 1st open
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(_url);
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
}
@ -199,11 +201,6 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags);
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
// in Qt 5.10.0 there is already an "Audio" object in the QML context
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
_webSurface->getSurfaceContext()->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());

View file

@ -192,7 +192,11 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
_nonMirroredIndices.clear();
_mirrorMap.reserve(_jointsSize);
for (int i = 0; i < _jointsSize; i++) {
if (_joints[i].name.endsWith("tEye")) {
if (_joints[i].name != "Hips" && _joints[i].name != "Spine" &&
_joints[i].name != "Spine1" && _joints[i].name != "Spine2" &&
_joints[i].name != "Neck" && _joints[i].name != "Head" &&
!((_joints[i].name.startsWith("Left") || _joints[i].name.startsWith("Right")) &&
_joints[i].name != "LeftEye" && _joints[i].name != "RightEye")) {
// HACK: we don't want to mirror some joints so we remember their indices
// so we can restore them after a future mirror operation
_nonMirroredIndices.push_back(i);

View file

@ -51,9 +51,9 @@ void EntityScriptServerLogClient::enableToEntityServerScriptLog(bool enable) {
if (_subscribed != enable) {
if (enable) {
emit receivedNewLogLines("====================== Subscribded to the Entity Script Server's log ======================");
emit receivedNewLogLines("====================== Subscribed to the Entity Script Server's log ======================");
} else {
emit receivedNewLogLines("==================== Unsubscribded from the Entity Script Server's log ====================");
emit receivedNewLogLines("==================== Unsubscribed from the Entity Script Server's log ====================");
}
}
_subscribed = enable;

View file

@ -1455,18 +1455,22 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
QSet<QString> remainingModels;
for (QHash<QString, FBXModel>::const_iterator model = models.constBegin(); model != models.constEnd(); model++) {
// models with clusters must be parented to the cluster top
foreach (const QString& deformerID, _connectionChildMap.values(model.key())) {
foreach (const QString& clusterID, _connectionChildMap.values(deformerID)) {
if (!clusters.contains(clusterID)) {
continue;
// Unless the model is a root node.
bool isARootNode = !modelIDs.contains(_connectionParentMap.value(model.key()));
if (!isARootNode) {
foreach(const QString& deformerID, _connectionChildMap.values(model.key())) {
foreach(const QString& clusterID, _connectionChildMap.values(deformerID)) {
if (!clusters.contains(clusterID)) {
continue;
}
QString topID = getTopModelID(_connectionParentMap, models, _connectionChildMap.value(clusterID), url);
_connectionChildMap.remove(_connectionParentMap.take(model.key()), model.key());
_connectionParentMap.insert(model.key(), topID);
goto outerBreak;
}
QString topID = getTopModelID(_connectionParentMap, models, _connectionChildMap.value(clusterID), url);
_connectionChildMap.remove(_connectionParentMap.take(model.key()), model.key());
_connectionParentMap.insert(model.key(), topID);
goto outerBreak;
}
outerBreak: ;
}
outerBreak:
// make sure the parent is in the child map
QString parent = _connectionParentMap.value(model.key());
@ -1477,11 +1481,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
}
while (!remainingModels.isEmpty()) {
QString first = *remainingModels.constBegin();
foreach (const QString& id, remainingModels) {
if (id < first) {
first = id;
}
}
QString topID = getTopModelID(_connectionParentMap, models, first, url);
appendModelIDs(_connectionParentMap.value(topID), _connectionChildMap, models, remainingModels, modelIDs, true);
}

View file

@ -23,25 +23,31 @@
#include <shared/GlobalAppProperties.h>
#include <GLMHelpers.h>
#include "GLLogging.h"
#include "Config.h"
#ifdef Q_OS_WIN
#if defined(DEBUG) || defined(USE_GLES)
static bool enableDebugLogger = true;
#else
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL");
static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
#endif
#endif
#include "Config.h"
#include "GLHelpers.h"
using namespace gl;
bool Context::enableDebugLogger() {
#if defined(DEBUG) || defined(USE_GLES)
static bool enableDebugLogger = true;
#else
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL");
static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
#endif
static std::once_flag once;
std::call_once(once, [&] {
// If the previous run crashed, force GL debug logging on
if (qApp->property(hifi::properties::CRASHED).toBool()) {
enableDebugLogger = true;
}
});
return enableDebugLogger;
}
std::atomic<size_t> Context::_totalSwapchainMemoryUsage { 0 };
size_t Context::getSwapchainMemoryUsage() { return _totalSwapchainMemoryUsage.load(); }
@ -245,10 +251,6 @@ void Context::create() {
// Create a temporary context to initialize glew
static std::once_flag once;
std::call_once(once, [&] {
// If the previous run crashed, force GL debug logging on
if (qApp->property(hifi::properties::CRASHED).toBool()) {
enableDebugLogger = true;
}
auto hdc = GetDC(hwnd);
setupPixelFormatSimple(hdc);
auto glrc = wglCreateContext(hdc);
@ -328,7 +330,7 @@ void Context::create() {
contextAttribs.push_back(WGL_CONTEXT_CORE_PROFILE_BIT_ARB);
#endif
contextAttribs.push_back(WGL_CONTEXT_FLAGS_ARB);
if (enableDebugLogger) {
if (enableDebugLogger()) {
contextAttribs.push_back(WGL_CONTEXT_DEBUG_BIT_ARB);
} else {
contextAttribs.push_back(0);
@ -350,7 +352,7 @@ void Context::create() {
if (!makeCurrent()) {
throw std::runtime_error("Could not make context current");
}
if (enableDebugLogger) {
if (enableDebugLogger()) {
glDebugMessageCallback(debugMessageCallback, NULL);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
}

View file

@ -47,6 +47,7 @@ namespace gl {
Context(const Context& other);
public:
static bool enableDebugLogger();
Context();
Context(QWindow* window);
void release();

View file

@ -18,6 +18,7 @@
#include <QtCore/QDebug>
#include <QtGui/QOffscreenSurface>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLDebugLogger>
#include "Context.h"
#include "GLHelpers.h"
@ -68,9 +69,32 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
}
#endif
if (gl::Context::enableDebugLogger()) {
_context->makeCurrent(_offscreenSurface);
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
connect(logger, &QOpenGLDebugLogger::messageLogged, this, &OffscreenGLCanvas::onMessageLogged);
logger->initialize();
logger->enableMessages();
logger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
_context->doneCurrent();
}
return true;
}
void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage) {
auto severity = debugMessage.severity();
switch (severity) {
case QOpenGLDebugMessage::NotificationSeverity:
case QOpenGLDebugMessage::LowSeverity:
return;
default:
break;
}
qDebug(glLogging) << debugMessage;
return;
}
bool OffscreenGLCanvas::makeCurrent() {
bool result = _context->makeCurrent(_offscreenSurface);
std::call_once(_reportOnce, []{

View file

@ -17,7 +17,7 @@
class QOpenGLContext;
class QOffscreenSurface;
class QOpenGLDebugLogger;
class QOpenGLDebugMessage;
class OffscreenGLCanvas : public QObject {
public:
@ -32,6 +32,9 @@ public:
}
QObject* getContextObject();
private slots:
void onMessageLogged(const QOpenGLDebugMessage &debugMessage);
protected:
std::once_flag _reportOnce;
QOpenGLContext* _context{ nullptr };

View file

@ -776,7 +776,7 @@ void AddressManager::copyPath() {
QApplication::clipboard()->setText(currentPath());
}
QString AddressManager::getDomainId() const {
QString AddressManager::getDomainID() const {
return DependencyManager::get<NodeList>()->getDomainHandler().getUUID().toString();
}

View file

@ -35,9 +35,11 @@ const QString GET_PLACE = "/api/v1/places/%1";
* The location API provides facilities related to your current location in the metaverse.
*
* @namespace location
* @property {Uuid} domainId - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not
* @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not
* connected to the domain.
* <em>Read-only.</em>
* @property {Uuid} domainId - Synonym for <code>domainId</code>. <em>Read-only.</em> <strong>Deprecated:</strong> This property
* is deprecated and will soon be removed.
* @property {string} hostname - The name of the domain for your current metaverse address (e.g., <code>"AvatarIsland"</code>,
* <code>localhost</code>, or an IP address).
* <em>Read-only.</em>
@ -66,7 +68,8 @@ class AddressManager : public QObject, public Dependency {
Q_PROPERTY(QString hostname READ getHost)
Q_PROPERTY(QString pathname READ currentPath)
Q_PROPERTY(QString placename READ getPlaceName)
Q_PROPERTY(QString domainId READ getDomainId)
Q_PROPERTY(QString domainID READ getDomainID)
Q_PROPERTY(QString domainId READ getDomainID)
public:
/**jsdoc
@ -164,7 +167,7 @@ public:
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
const QString& getPlaceName() const { return _shareablePlaceName.isEmpty() ? _placeName : _shareablePlaceName; }
QString getDomainId() const;
QString getDomainID() const;
const QString& getHost() const { return _host; }

View file

@ -99,7 +99,9 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie
// Diffuse from ambient
diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lowNormalCurvature.xyz).xyz;
specular = vec3(0.0);
// Scattering ambient specular is the same as non scattering for now
// TODO: we should use the same specular answer as for direct lighting
}
<@endif@>

View file

@ -227,7 +227,7 @@ bool QmlWindowClass::isVisible() {
glm::vec2 QmlWindowClass::getPosition() {
if (QThread::currentThread() != thread()) {
vec2 result;
BLOCKING_INVOKE_METHOD(this, "getPosition", Q_RETURN_ARG(vec2, result));
BLOCKING_INVOKE_METHOD(this, "getPosition", Q_RETURN_ARG(glm::vec2, result));
return result;
}
@ -241,7 +241,7 @@ glm::vec2 QmlWindowClass::getPosition() {
void QmlWindowClass::setPosition(const glm::vec2& position) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setPosition", Q_ARG(vec2, position));
QMetaObject::invokeMethod(this, "setPosition", Q_ARG(const glm::vec2&, position));
return;
}
@ -262,7 +262,7 @@ glm::vec2 toGlm(const QSizeF& size) {
glm::vec2 QmlWindowClass::getSize() {
if (QThread::currentThread() != thread()) {
vec2 result;
BLOCKING_INVOKE_METHOD(this, "getSize", Q_RETURN_ARG(vec2, result));
BLOCKING_INVOKE_METHOD(this, "getSize", Q_RETURN_ARG(glm::vec2, result));
return result;
}
@ -275,7 +275,7 @@ glm::vec2 QmlWindowClass::getSize() {
void QmlWindowClass::setSize(const glm::vec2& size) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setSize", Q_ARG(vec2, size));
QMetaObject::invokeMethod(this, "setSize", Q_ARG(const glm::vec2&, size));
return;
}

View file

@ -543,6 +543,7 @@ void OffscreenQmlSurface::cleanup() {
void OffscreenQmlSurface::render() {
#if !defined(DISABLE_QML)
if (nsightActive()) {
return;
}
@ -569,14 +570,18 @@ void OffscreenQmlSurface::render() {
{
// If the most recent texture was unused, we can directly recycle it
if (_latestTextureAndFence.first) {
offscreenTextures.releaseTexture(_latestTextureAndFence);
_latestTextureAndFence = { 0, 0 };
}
_latestTextureAndFence = { texture, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) };
auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// Fence will be used in another thread / context, so a flush is required
glFlush();
{
Lock lock(_latestTextureAndFenceMutex);
if (_latestTextureAndFence.first) {
offscreenTextures.releaseTexture(_latestTextureAndFence);
_latestTextureAndFence = { 0, 0 };
}
_latestTextureAndFence = { texture, fence};
}
}
_quickWindow->resetOpenGLState();
@ -588,13 +593,21 @@ void OffscreenQmlSurface::render() {
bool OffscreenQmlSurface::fetchTexture(TextureAndFence& textureAndFence) {
textureAndFence = { 0, 0 };
// Lock free early check
if (0 == _latestTextureAndFence.first) {
return false;
}
// Ensure writes to the latest texture are complete before before returning it for reading
textureAndFence = _latestTextureAndFence;
_latestTextureAndFence = { 0, 0 };
{
Lock lock(_latestTextureAndFenceMutex);
// Double check inside the lock
if (0 == _latestTextureAndFence.first) {
return false;
}
textureAndFence = _latestTextureAndFence;
_latestTextureAndFence = { 0, 0 };
}
return true;
}
@ -813,10 +826,13 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) {
// Release hold on the textures of the old size
if (uvec2() != _size) {
// If the most recent texture was unused, we can directly recycle it
if (_latestTextureAndFence.first) {
offscreenTextures.releaseTexture(_latestTextureAndFence);
_latestTextureAndFence = { 0, 0 };
{
Lock lock(_latestTextureAndFenceMutex);
// If the most recent texture was unused, we can directly recycle it
if (_latestTextureAndFence.first) {
offscreenTextures.releaseTexture(_latestTextureAndFence);
_latestTextureAndFence = { 0, 0 };
}
}
offscreenTextures.releaseSize(_size);
}

View file

@ -167,6 +167,9 @@ public slots:
bool handlePointerEvent(const PointerEvent& event, class QTouchDevice& device, bool release = false);
private:
using Mutex = std::mutex;
using Lock = std::unique_lock<std::mutex>;
QQuickWindow* _quickWindow { nullptr };
QQmlContext* _qmlContext { nullptr };
QQuickItem* _rootItem { nullptr };
@ -188,6 +191,7 @@ private:
#endif
// Texture management
Mutex _latestTextureAndFenceMutex;
TextureAndFence _latestTextureAndFence { 0, 0 };
bool _render { false };

View file

@ -1 +0,0 @@
/node_modules

View file

@ -1 +0,0 @@
web: node app.js

View file

@ -1,5 +0,0 @@
This gameserver sets up a server with websockets that listen for messages from interface regarding when users shoot rats, and updates a real-time game board with that information. This is just a first pass, and the plan is to abstract this to work with any kind of game content creators wish to make with High Fidelity.
To enter the game: Run pistol.js and shoot at rats.
For every rat you kill, you get a point.
You're score will be displayed at https://desolate-bastion-1742.herokuapp.com/

View file

@ -1,76 +0,0 @@
'use strict';
/**
* Module dependencies.
*/
var express = require('express');
var http = require('http');
var _ = require('underscore');
var shortid = require('shortid');
var app = express();
var server = http.createServer(app);
var WebSocketServer = require('websocket').server;
var wsServer = new WebSocketServer({
httpServer: server
});
var users = [];
var connections = [];
wsServer.on('request', function(request) {
console.log("SOMEONE JOINED");
var connection = request.accept(null, request.origin);
connections.push(connection);
connection.on('message', function(data) {
var userData = JSON.parse(data.utf8Data);
var user = _.find(users, function(user) {
return user.username === userData.username;
});
if (user) {
// This user already exists, so just update score
users[users.indexOf(user)].score = userData.score;
} else {
users.push({
id: shortid.generate(),
username: userData.username,
score: userData.score
});
}
connections.forEach(function(aConnection) {
aConnection.sendUTF(JSON.stringify({
users: users
}));
})
});
});
app.get('/users', function(req, res) {
res.send({
users: users
});
});
/* Configuration */
app.set('views', __dirname + '/views');
app.use(express.static(__dirname + '/public'));
app.set('port', (process.env.PORT || 5000));
if (process.env.NODE_ENV === 'development') {
app.use(express.errorHandler({
dumpExceptions: true,
showStack: true
}));
}
/* Start server */
server.listen(app.get('port'), function() {
console.log('Express server listening on port %d in %s mode', app.get('port'), app.get('env'));
});
module.exports = app;

View file

@ -1,87 +0,0 @@
'use strict';
var React = require('react');
var _ = require('underscore')
var $ = require('jquery');
var UserList = React.createClass({
render: function(){
var sortedUsers = _.sortBy(this.props.data.users, function(users){
//Show higher scorers at top of board
return 1 - users.score;
});
var users = sortedUsers.map(function(user) {
return (
<User username = {user.username} score = {user.score} key = {user.id}></User>
)
});
return (
<div>{users}</div>
)
}
});
var GameBoard = React.createClass({
loadDataFromServer: function(data) {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: {users: []}};
},
componentDidMount: function() {
this.loadDataFromServer();
//set up web socket
var path = window.location.hostname + ":" + window.location.port;
console.log("LOCATION ", path)
var socketClient = new WebSocket("wss://" + path);
var self = this;
socketClient.onopen = function() {
console.log("CONNECTED");
socketClient.onmessage = function(data) {
console.log("ON MESSAGE");
self.setState({data: JSON.parse(data.data)});
};
};
},
render: function() {
return (
<div>
<div className = "gameTitle">Kill All The Rats!</div>
<div className = "boardHeader">
<div className="username">PLAYER</div>
<div className="score" > SCORE </div>
</div>
<UserList data ={this.state.data}/>
</div>
);
}
});
var User = React.createClass({
render: function() {
return (
<div className = "entry">
<div className="username"> {this.props.username} </div>
<div className="score" > {this.props.score} </div>
</div>
);
}
})
React.render(
<GameBoard url = "/users" />,
document.getElementById('app')
);

View file

@ -1,15 +0,0 @@
var gulp = require('gulp');
var exec = require('child_process').exec;
gulp.task('build', function() {
exec('npm run build', function(msg){
console.log(msg);
});
});
gulp.task('watch', function() {
gulp.watch('client/*.jsx', ['build']);
});
gulp.task('default', ['build', 'watch'])

View file

@ -1,21 +0,0 @@
{
"name": "KillAllTheRats",
"version": "0.6.9",
"scripts": {
"build": "browserify ./client/app.jsx -t babelify --outfile ./public/js/app.js",
"start": "node app.js"
},
"dependencies": {
"express": "^4.13.1",
"gulp": "^3.9.0",
"jquery": "^2.1.4",
"react": "^0.13.3",
"shortid": "^2.2.4",
"underscore": "^1.8.3",
"websocket": "^1.0.22"
},
"devDependencies": {
"babelify": "^6.1.3",
"browserify": "^10.2.6"
}
}

View file

@ -1,36 +0,0 @@
body {
font-family: Impact;
background-color: #009DC0 ;
font-size: 60px;
}
.gameTitle {
color: #D61010;
}
.entry{
width:100%;
height:50px;
border:1px solid #A9D1E1;
color: white;
margin-right:10px;
padding: 10px;
float:left;
font-size: 40px;
}
.boardHeader{
width:100%;
height:50px;
border:5px solid #A9D1E1;
color: white;
margin-right:10px;
padding: 10px;
float:left;
font-size: 40px;
}
.username{
font-weight: bold;
float: left;
margin-right: 50%;
}

View file

@ -1,12 +0,0 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="css/style.css">
<title>Kill The Rats!</title>
</head>
<body>
<div id="app"></div>
<script src="js/app.js"></script>
</body>
</html>

View file

@ -1,31 +0,0 @@
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
var scriptURL = Script.resolvePath("pistolScriptSpawner.js");
var modelURL = "http://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx";
var pistolSpawnerEntity = Entities.addEntity({
type: 'Box',
position: center,
dimensions: {x: 0.38, y: 1.9, z: 3.02},
script: scriptURL,
visible: false,
collisionless: true
});
var pistol = Entities.addEntity({
type: 'Model',
modelURL: modelURL,
position: center,
dimensions: {x: 0.38, y: 1.9, z: 3.02},
script: scriptURL,
color: {red: 200, green: 0, blue: 20},
collisionless: true
});
function cleanup() {
Entities.deleteEntity(pistolSpawnerEntity);
Entities.deleteEntity(pistol);
}
// Script.update.connect(update);
Script.scriptEnding.connect(cleanup);

View file

@ -22,14 +22,37 @@ if (scripts.length >= 2) {
// Set up the qml ui
var qml = Script.resolvePath('debugWindow.qml');
var HMD_DEBUG_WINDOW_GEOMETRY_KEY = 'hmdDebugWindowGeometry';
var hmdDebugWindowGeometryValue = Settings.getValue(HMD_DEBUG_WINDOW_GEOMETRY_KEY)
var windowWidth = 400;
var windowHeight = 900;
var hasPosition = false;
var windowX = 0;
var windowY = 0;
if (hmdDebugWindowGeometryValue !== '') {
var geometry = JSON.parse(hmdDebugWindowGeometryValue);
windowWidth = geometry.width
windowHeight = geometry.height
windowX = geometry.x
windowY = geometry.y
hasPosition = true;
}
var window = new OverlayWindow({
title: 'Debug Window',
source: qml,
width: 400, height: 900,
width: windowWidth, height: windowHeight,
});
window.setPosition(25, 50);
window.closed.connect(function() { Script.stop(); });
if (hasPosition) {
window.setPosition(windowX, windowY);
}
window.closed.connect(function () { Script.stop(); });
var getFormattedDate = function() {
var date = new Date();
@ -65,6 +88,14 @@ ScriptDiscoveryService.clearDebugWindow.connect(function() {
});
Script.scriptEnding.connect(function () {
var geometry = JSON.stringify({
x: window.position.x,
y: window.position.y,
width: window.size.x,
height: window.size.y
})
Settings.setValue(HMD_DEBUG_WINDOW_GEOMETRY_KEY, geometry);
window.close();
})

View file

@ -10,7 +10,7 @@
getControllerJointIndex, enableDispatcherModule, disableDispatcherModule,
Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions,
Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable,
cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE
cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE, Uuid
*/
Script.include("/~/system/libraries/Xform.js");
@ -269,6 +269,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
this.grabEntityProps = null;
this.shouldSendStart = false;
this.equipedWithSecondary = false;
this.handHasBeenRightsideUp = false;
this.parameters = makeDispatcherModuleParameters(
300,
@ -486,15 +487,17 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
var grabbedProperties = Entities.getEntityProperties(this.targetEntityID);
// if an object is "equipped" and has a predefined offset, use it.
var offsets = getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand);
if (offsets) {
this.offsetPosition = offsets[0];
this.offsetRotation = offsets[1];
} else {
var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand";
if (this.grabbedHotspot.joints[handJointName]) {
this.offsetPosition = this.grabbedHotspot.joints[handJointName][0];
this.offsetRotation = this.grabbedHotspot.joints[handJointName][1];
if (this.grabbedHotspot) {
var offsets = getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand);
if (offsets) {
this.offsetPosition = offsets[0];
this.offsetRotation = offsets[1];
} else {
var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand";
if (this.grabbedHotspot.joints[handJointName]) {
this.offsetPosition = this.grabbedHotspot.joints[handJointName][0];
this.offsetRotation = this.grabbedHotspot.joints[handJointName][1];
}
}
}
@ -549,7 +552,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
// 100 ms seems to be sufficient time to force the check even occur after the object has been initialized.
Script.setTimeout(grabEquipCheck, 100);
}
};
this.endEquipEntity = function () {
@ -624,7 +626,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
this.grabbedHotspot = potentialEquipHotspot;
this.targetEntityID = this.grabbedHotspot.entityID;
this.startEquipEntity(controllerData);
this.messageGrabEnity = false;
this.messageGrabEntity = false;
this.equipedWithSecondary = this.secondarySmoothedSqueezed();
return makeRunningValues(true, [potentialEquipHotspot.entityID], []);
} else {
@ -640,6 +642,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
this.isReady = function (controllerData, deltaTime) {
var timestamp = Date.now();
this.updateInputs(controllerData);
this.handHasBeenRightsideUp = false;
return this.checkNearbyHotspots(controllerData, deltaTime, timestamp);
};
@ -671,7 +674,14 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
return makeRunningValues(false, [], []);
}
var dropDetected = this.dropGestureProcess(deltaTime);
var handIsUpsideDown = this.dropGestureProcess(deltaTime);
var dropDetected = false;
if (this.handHasBeenRightsideUp) {
dropDetected = handIsUpsideDown;
}
if (!handIsUpsideDown) {
this.handHasBeenRightsideUp = true;
}
if (this.triggerSmoothedReleased() || this.secondaryReleased()) {
if (this.shouldSendStart) {
@ -692,7 +702,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
}
// highlight the grabbed hotspot when the dropGesture is detected.
if (dropDetected) {
if (dropDetected && this.grabbedHotspot) {
equipHotspotBuddy.updateHotspot(this.grabbedHotspot, timestamp);
equipHotspotBuddy.highlightHotspot(this.grabbedHotspot);
}

View file

@ -493,7 +493,7 @@ function populateNearbyUserList(selectData, oldAudioData) {
data.push(avatarPalDatum);
print('PAL data:', JSON.stringify(avatarPalDatum));
});
getConnectionData(false, location.domainId); // Even admins don't get relationship data in requestUsernameFromID (which is still needed for admin status, which comes from domain).
getConnectionData(false, location.domainID); // Even admins don't get relationship data in requestUsernameFromID (which is still needed for admin status, which comes from domain).
conserveResources = Object.keys(avatarsOfInterest).length > 20;
sendToQml({ method: 'nearbyUsers', params: data });
if (selectData) {

View file

@ -35,4 +35,5 @@
button.editProperties({isActive: true});
}
Script.scriptEnding.connect(cleanup);
}());

View file

@ -337,7 +337,7 @@ function fillImageDataFromPrevious() {
containsGif: previousAnimatedSnapPath !== "",
processingGif: false,
shouldUpload: false,
canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"),
canBlast: location.domainID === Settings.getValue("previousSnapshotDomainID"),
isLoggedIn: isLoggedIn
};
imageData = [];
@ -416,7 +416,7 @@ function snapshotUploaded(isError, reply) {
}
isUploadingPrintableStill = false;
}
var href, domainId;
var href, domainID;
function takeSnapshot() {
tablet.emitScriptEvent(JSON.stringify({
type: "snapshot",
@ -443,11 +443,11 @@ function takeSnapshot() {
MyAvatar.setClearOverlayWhenMoving(false);
// We will record snapshots based on the starting location. That could change, e.g., when recording a .gif.
// Even the domainId could change (e.g., if the user falls into a teleporter while recording).
// Even the domainID could change (e.g., if the user falls into a teleporter while recording).
href = location.href;
Settings.setValue("previousSnapshotHref", href);
domainId = location.domainId;
Settings.setValue("previousSnapshotDomainID", domainId);
domainID = location.domainID;
Settings.setValue("previousSnapshotDomainID", domainID);
maybeDeleteSnapshotStories();
@ -548,7 +548,7 @@ function stillSnapshotTaken(pathStillSnapshot, notify) {
}
HMD.openTablet();
isDomainOpen(domainId, function (canShare) {
isDomainOpen(domainID, function (canShare) {
snapshotOptions = {
containsGif: false,
processingGif: false,
@ -594,7 +594,7 @@ function processingGifStarted(pathStillSnapshot) {
}
HMD.openTablet();
isDomainOpen(domainId, function (canShare) {
isDomainOpen(domainID, function (canShare) {
snapshotOptions = {
containsGif: true,
processingGif: true,
@ -622,13 +622,13 @@ function processingGifCompleted(pathAnimatedSnapshot) {
Settings.setValue("previousAnimatedSnapPath", pathAnimatedSnapshot);
isDomainOpen(domainId, function (canShare) {
isDomainOpen(domainID, function (canShare) {
snapshotOptions = {
containsGif: true,
processingGif: false,
canShare: canShare,
isLoggedIn: isLoggedIn,
canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"),
canBlast: location.domainID === Settings.getValue("previousSnapshotDomainID"),
};
imageData = [{ localPath: pathAnimatedSnapshot, href: href }];
tablet.emitScriptEvent(JSON.stringify({

View file

@ -23,7 +23,7 @@ var BALL_DENSITY = 1000;
var ACTION_DISTANCE = 0.35;
var ACTION_TIMESCALE = 0.035;
var MAX_DISTANCE_MULTIPLIER = 4;
var STICK_SCRIPT_URL = Script.resolvePath("./entity_scripts/tetherballStick.js?v=" + Date.now());
var STICK_SCRIPT_URL = Script.resolvePath("./entity_scripts/tetherballStick.js");
var STICK_MODEL_URL = "http://hifi-content.s3.amazonaws.com/caitlyn/production/raveStick/newRaveStick2.fbx";
var COLLISION_SOUND_URL = "http://public.highfidelity.io/sounds/Footsteps/FootstepW3Left-12db.wav";