From 59a6726af6a0153f7cb93e58aa3f4588fb6c06ce Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 6 Oct 2017 15:18:39 -0700 Subject: [PATCH] Checkpoint; lotsa changes... --- .../src/entities/EntityServer.cpp | 62 +++++- assignment-client/src/entities/EntityServer.h | 7 + .../qml/hifi/commerce/checkout/Checkout.qml | 2 +- .../qml/hifi/commerce/purchases/Purchases.qml | 10 +- .../qml/hifi/commerce/wallet/Wallet.qml | 2 + interface/src/commerce/Wallet.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 182 +++++++++--------- libraries/entities/src/EntityTree.h | 6 +- scripts/system/commerce/wallet.js | 5 + scripts/system/html/js/marketplacesInject.js | 1 - scripts/system/marketplaces/marketplaces.js | 8 +- 11 files changed, 186 insertions(+), 101 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 1c1e02316a..445af3ac34 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -29,7 +29,8 @@ const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo"; EntityServer::EntityServer(ReceivedMessage& message) : OctreeServer(message), - _entitySimulation(NULL) + _entitySimulation(NULL), + _dynamicDomainVerificationTimer(this) { DependencyManager::set(); DependencyManager::set(); @@ -38,6 +39,9 @@ EntityServer::EntityServer(ReceivedMessage& message) : auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics, PacketType::ChallengeOwnership }, this, "handleEntityPacket"); + + connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification); + _dynamicDomainVerificationTimer.setSingleShot(true); } EntityServer::~EntityServer() { @@ -93,6 +97,8 @@ void EntityServer::beforeRun() { connect(_pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities())); const int PRUNE_DELETED_MODELS_INTERVAL_MSECS = 1 * 1000; // once every second _pruneDeletedEntitiesTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS); + + startDynamicDomainVerification(); } void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) { @@ -410,3 +416,57 @@ QString EntityServer::serverSubclassStats() { return statsString; } + +void EntityServer::startDynamicDomainVerification() { + qCDebug(entities) << "Starting Dynamic Domain Verification..."; + + auto nodeList = DependencyManager::get(); + QString thisDomainID = nodeList->getDomainHandler().getUUID().toString(); + + EntityTreePointer tree = std::static_pointer_cast(_tree); + QHash localMap(tree->getEntityCertificateIDMap()); + + QHashIterator i(localMap); + qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap"; + while (i.hasNext()) { + i.next(); + + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status"); + QJsonObject request; + request["certificate_id"] = i.key(); + networkRequest.setUrl(requestURL); + + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.get(networkRequest); + + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + QJsonDocument doc(jsonObject); + qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); + + // ZRF FIXME!!! + //if (networkReply->error() == QNetworkReply::NoError) { + if (true) { + // ZRF FIXME!!! + //if (jsonObject["location"].toString() != thisDomainID) { + if (false) { + qCDebug(entities) << "invalid_reason not empty, deleting entity" << i.value(); + tree->deleteEntity(i.value(), true); + } + } else { + qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << i.value(); + tree->deleteEntity(i.value(), true); + } + + networkReply->deleteLater(); + }); + } + + int nextInterval = qrand() % ((MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; + qCDebug(entities) << "Restarting Dynamic Domain Verification timer for" << nextInterval / 1000 << "seconds"; + _dynamicDomainVerificationTimer.start(nextInterval); +} diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 26c2f149aa..39d15f726e 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -80,6 +80,13 @@ private: QReadWriteLock _viewerSendingStatsLock; QMap> _viewerSendingStats; + + //static const int MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 45 * 60 * 1000; // 45m + //static const int MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 75 * 60 * 1000; // 1h15m + static const int MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 5 * 1000; // 5s + static const int MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 10 * 1000; // 10s + QTimer _dynamicDomainVerificationTimer; + void startDynamicDomainVerification(); }; #endif // hifi_EntityServer_h diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 09c2f6fa76..2ab8e90e9d 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -125,7 +125,7 @@ Rectangle { id: notSetUpTimer; interval: 200; onTriggered: { - sendToScript({method: 'checkout_walletNotSetUp'}); + sendToScript({method: 'checkout_walletNotSetUp', itemId: itemId}); } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 990fd348c6..42708a80c5 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -78,6 +78,10 @@ Rectangle { onInventoryResult: { purchasesReceived = true; + if (root.pendingInventoryReply) { + inventoryTimer.start(); + } + if (result.status !== 'success') { console.log("Failed to get purchases", result.message); } else { @@ -98,10 +102,6 @@ Rectangle { previousPurchasesModel.append(inventoryResult); buildFilteredPurchasesModel(); - - if (root.pendingInventoryReply) { - inventoryTimer.start(); - } } root.pendingInventoryReply = false; @@ -112,7 +112,7 @@ Rectangle { id: notSetUpTimer; interval: 200; onTriggered: { - sendToScript({method: 'checkout_walletNotSetUp'}); + sendToScript({method: 'purchases_walletNotSetUp'}); } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 0bc444efb5..9beadd3361 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -176,6 +176,8 @@ Rectangle { commerce.getWalletStatus(); } else if (msg.referrer === 'purchases') { sendToScript({method: 'goToPurchases'}); + } else { + sendToScript({method: 'goToMarketplaceItemPage', itemId: msg.referrer}); } } else if (msg.method === 'walletSetup_raiseKeyboard') { root.keyboardRaised = true; diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 34e266b32c..e80475f2cc 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -710,7 +710,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack bool Wallet::verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText) { // I have no idea how to do this yet, so here's some dummy code that may not even work. - decryptedText = QString("fail"); + decryptedText = QString("success"); return true; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 35c47e48c9..8f75a4bdf1 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -147,63 +147,63 @@ int EntityTree::readEntityDataFromBuffer(const unsigned char* data, int bytesLef if (bytesLeftToRead >= (int)(numberOfEntities * expectedBytesPerEntity)) { for (uint16_t i = 0; i < numberOfEntities; i++) { int bytesForThisEntity = 0; - EntityItemID entityItemID = EntityItemID::readEntityItemIDFromBuffer(dataAt, bytesLeftToRead); - EntityItemPointer entity = findEntityByEntityItemID(entityItemID); +EntityItemID entityItemID = EntityItemID::readEntityItemIDFromBuffer(dataAt, bytesLeftToRead); +EntityItemPointer entity = findEntityByEntityItemID(entityItemID); - if (entity) { - QString entityScriptBefore = entity->getScript(); - QUuid parentIDBefore = entity->getParentID(); - QString entityServerScriptsBefore = entity->getServerScripts(); - quint64 entityScriptTimestampBefore = entity->getScriptTimestamp(); +if (entity) { + QString entityScriptBefore = entity->getScript(); + QUuid parentIDBefore = entity->getParentID(); + QString entityServerScriptsBefore = entity->getServerScripts(); + quint64 entityScriptTimestampBefore = entity->getScriptTimestamp(); - bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); - if (entity->getDirtyFlags()) { - entityChanged(entity); - } - _entityMover.addEntityToMoveList(entity, entity->getQueryAACube()); + bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); + if (entity->getDirtyFlags()) { + entityChanged(entity); + } + _entityMover.addEntityToMoveList(entity, entity->getQueryAACube()); - QString entityScriptAfter = entity->getScript(); - QString entityServerScriptsAfter = entity->getServerScripts(); - quint64 entityScriptTimestampAfter = entity->getScriptTimestamp(); - bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; + QString entityScriptAfter = entity->getScript(); + QString entityServerScriptsAfter = entity->getServerScripts(); + quint64 entityScriptTimestampAfter = entity->getScriptTimestamp(); + bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; - // If the script value has changed on us, or it's timestamp has changed to force - // a reload then we want to send out a script changing signal... - if (reload || entityScriptBefore != entityScriptAfter) { - emitEntityScriptChanging(entityItemID, reload); // the entity script has changed - } - if (reload || entityServerScriptsBefore != entityServerScriptsAfter) { - emitEntityServerScriptChanging(entityItemID, reload); // the entity server script has changed - } + // If the script value has changed on us, or it's timestamp has changed to force + // a reload then we want to send out a script changing signal... + if (reload || entityScriptBefore != entityScriptAfter) { + emitEntityScriptChanging(entityItemID, reload); // the entity script has changed + } + if (reload || entityServerScriptsBefore != entityServerScriptsAfter) { + emitEntityServerScriptChanging(entityItemID, reload); // the entity server script has changed + } - QUuid parentIDAfter = entity->getParentID(); - if (parentIDBefore != parentIDAfter) { - addToNeedsParentFixupList(entity); - } - } else { - entity = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args); - if (entity) { - bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); + QUuid parentIDAfter = entity->getParentID(); + if (parentIDBefore != parentIDAfter) { + addToNeedsParentFixupList(entity); + } +} else { + entity = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args); + if (entity) { + bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); - // don't add if we've recently deleted.... - if (!isDeletedEntity(entityItemID)) { - _entitiesToAdd.insert(entityItemID, entity); + // don't add if we've recently deleted.... + if (!isDeletedEntity(entityItemID)) { + _entitiesToAdd.insert(entityItemID, entity); - if (entity->getCreated() == UNKNOWN_CREATED_TIME) { - entity->recordCreationTime(); - } - #ifdef WANT_DEBUG - } else { - qCDebug(entities) << "Received packet for previously deleted entity [" << - entityItemID << "] ignoring. (inside " << __FUNCTION__ << ")"; - #endif - } - } - } - // Move the buffer forward to read more entities - dataAt += bytesForThisEntity; - bytesLeftToRead -= bytesForThisEntity; - bytesRead += bytesForThisEntity; + if (entity->getCreated() == UNKNOWN_CREATED_TIME) { + entity->recordCreationTime(); + } +#ifdef WANT_DEBUG + } else { + qCDebug(entities) << "Received packet for previously deleted entity [" << + entityItemID << "] ignoring. (inside " << __FUNCTION__ << ")"; +#endif + } + } +} +// Move the buffer forward to read more entities +dataAt += bytesForThisEntity; +bytesLeftToRead -= bytesForThisEntity; +bytesRead += bytesForThisEntity; } } } @@ -214,13 +214,13 @@ int EntityTree::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bool EntityTree::handlesEditPacketType(PacketType packetType) const { // we handle these types of "edit" packets switch (packetType) { - case PacketType::EntityAdd: - case PacketType::EntityEdit: - case PacketType::EntityErase: - case PacketType::EntityPhysics: - return true; - default: - return false; + case PacketType::EntityAdd: + case PacketType::EntityEdit: + case PacketType::EntityErase: + case PacketType::EntityPhysics: + return true; + default: + return false; } } @@ -241,6 +241,29 @@ void EntityTree::postAddEntity(EntityItemPointer entity) { // find and hook up any entities with this entity as a (previously) missing parent fixupNeedsParentFixups(); + + if (getIsServer()) { + QString certID(entity->getCertificateID()); + EntityItemID entityItemID = entity->getEntityItemID(); + EntityItemID existingEntityItemID; + + { + QWriteLocker locker(&_entityCertificateIDMapLock); + existingEntityItemID = _entityCertificateIDMap.value(certID); + if (!certID.isEmpty()) { + _entityCertificateIDMap.insert(certID, entityItemID); + qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; + } + } + + // Delete an already-existing entity from the tree if it has the same + // CertificateID as the entity we're trying to add. + if (!existingEntityItemID.isNull()) { + qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" + << existingEntityItemID << ". Deleting existing entity."; + deleteEntity(existingEntityItemID, true); + } + } } bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) { @@ -654,8 +677,16 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) theEntity->die(); if (getIsServer()) { + { + QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); + QString certID = theEntity->getCertificateID(); + if (theEntity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { + _entityCertificateIDMap.remove(certID); + } + } + // set up the deleted entities ID - QWriteLocker locker(&_recentlyDeletedEntitiesLock); + QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock); _recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID()); } else { // on the client side, we also remember that we deleted this entity, we don't care about the time @@ -665,9 +696,6 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) if (_simulation) { _simulation->prepareEntityForDelete(theEntity); } - - QWriteLocker locker(&_entityCertificateIDMapLock); - _entityCertificateIDMap.remove(theEntity->getProperties(PROP_CERTIFICATE_ID).getCertificateID()); } } @@ -1117,8 +1145,8 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) _challengeOwnershipTimeoutTimer->deleteLater(); } }); - _challengeOwnershipTimeoutTimer->setInterval(5000); - _challengeOwnershipTimeoutTimer->start(); + _challengeOwnershipTimeoutTimer->setSingleShot(true); + _challengeOwnershipTimeoutTimer->start(5000); } void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { @@ -1346,7 +1374,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c endCreate = usecTimestampNow(); _totalCreates++; - if (newEntity && isCertified) { + if (newEntity && isCertified && getIsServer()) { if (!newEntity->verifyStaticCertificateProperties()) { qCDebug(entities) << "User" << senderNode->getUUID() << "attempted to add a certified entity with ID" << entityItemID << "which failed" @@ -1357,28 +1385,6 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { QString certID = properties.getCertificateID(); - EntityItemID existingEntityItemID; - - { - QReadLocker locker(&_entityCertificateIDMapLock); - existingEntityItemID = _entityCertificateIDMap.value(certID); - } - - // Delete an already-existing entity from the tree if it has the same - // CertificateID as the entity we're trying to add. - if (!existingEntityItemID.isNull()) { - qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" - << existingEntityItemID << ". Deleting existing entity."; - deleteEntity(existingEntityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), existingEntityItemID); - } - - { - QWriteLocker locker(&_entityCertificateIDMapLock); - _entityCertificateIDMap.insert(certID, entityItemID); - qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; - } // Start owner verification. auto nodeList = DependencyManager::get(); @@ -2178,7 +2184,3 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const { } return entity->getJointNames(); } - -void EntityTree::startDynamicDomainVerification() { - -} diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 85ce6a2ed5..d6a35ca2aa 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -187,6 +187,11 @@ public: return _recentlyDeletedEntityItemIDs; } + QHash getEntityCertificateIDMap() const { + QReadLocker locker(&_entityCertificateIDMapLock); + return _entityCertificateIDMap; + } + void forgetEntitiesDeletedBefore(quint64 sinceTime); int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); @@ -369,7 +374,6 @@ protected: QHash _entitiesToAdd; Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID); - void startDynamicDomainVerification(); }; #endif // hifi_EntityTree_h diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 7553ca4eeb..04b67ec14f 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -16,6 +16,8 @@ (function () { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/accountUtils.js"); + var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; + // Function Name: onButtonClicked() // // Description: @@ -88,6 +90,9 @@ case 'goToPurchases': tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); break; + case 'goToMarketplaceItemPage': + tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); + break; default: print('Unrecognized message from QML:', JSON.stringify(message)); } diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index ded4542c51..41db724c3f 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -334,7 +334,6 @@ $('body').addClass("code-injected"); maybeAddLogInButton(); - maybeAddSetupWalletButton(); changeDropdownMenu(); var purchaseButton = $('#side-info').find('.btn').first(); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 0bee540a9d..deeae0d299 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -289,13 +289,19 @@ openWallet(); break; case 'purchases_walletNotSetUp': - case 'checkout_walletNotSetUp': wireEventBridge(true); tablet.sendToQml({ method: 'updateWalletReferrer', referrer: "purchases" }); openWallet(); + case 'checkout_walletNotSetUp': + wireEventBridge(true); + tablet.sendToQml({ + method: 'updateWalletReferrer', + referrer: message.itemId + }); + openWallet(); break; case 'checkout_cancelClicked': tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.params, MARKETPLACES_INJECT_SCRIPT_URL);