diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index 64c0e9a643..417ed9b8de 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -14,8 +14,8 @@ Should you choose not to install Qt5 via a package manager that handles dependen Install qt: ```bash -wget http://debian.highfidelity.com/pool/h/hi/hifi-qt5.10.1_5.10.1_amd64.deb -sudo dpkg -i hifi-qt5.10.1_5.10.1_amd64.deb +wget http://debian.highfidelity.com/pool/h/hi/hifiqt5.10.1_5.10.1_amd64.deb +sudo dpkg -i hifiqt5.10.1_5.10.1_amd64.deb ``` Install build dependencies: diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5758af1129..477df0bdd0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3146,6 +3146,10 @@ void Application::loadServerlessDomain(QUrl domainURL) { tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0); } + std::map namedPaths = tmpTree->getNamedPaths(); + nodeList->getDomainHandler().connectedToServerless(namedPaths); + + _fullSceneReceivedCounter++; } @@ -5903,6 +5907,9 @@ void Application::nodeActivated(SharedNodePointer node) { } getMyAvatar()->markIdentityDataChanged(); getMyAvatar()->resetLastSent(); + + // transmit a "sendAll" packet to the AvatarMixer we just connected to. + getMyAvatar()->sendAvatarDataPacket(true); } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index b25df633c0..428f86f0ab 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -58,6 +58,13 @@ void SkeletonModel::initJointStates() { glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); _rig.initJointStates(geometry, modelOffset); + { + // initialize _jointData with proper values for default joints + QVector defaultJointData; + _rig.copyJointsIntoJointData(defaultJointData); + _owningAvatar->setRawJointData(defaultJointData); + } + // Determine the default eye position for avatar scale = 1.0 int headJointIndex = geometry.headJointIndex; if (0 > headJointIndex || headJointIndex >= _rig.getJointStateCount()) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 08eee947ef..6b974ac9ae 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -559,7 +559,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent const JointData& last = lastSentJointData[i]; if (!data.rotationIsDefaultPose) { - if (sendAll || last.rotationIsDefaultPose || last.rotation != data.rotation) { + bool mustSend = sendAll || last.rotationIsDefaultPose; + if (mustSend || last.rotation != data.rotation) { bool largeEnoughRotation = true; if (cullSmallChanges) { @@ -568,7 +569,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT; } - if (sendAll || !cullSmallChanges || largeEnoughRotation) { + if (mustSend || !cullSmallChanges || largeEnoughRotation) { validity |= (1 << validityBit); #ifdef WANT_DEBUG rotationSentCount++; @@ -608,10 +609,12 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent float maxTranslationDimension = 0.0; for (int i = 0; i < _jointData.size(); i++) { const JointData& data = _jointData[i]; + const JointData& last = lastSentJointData[i]; if (!data.translationIsDefaultPose) { - if (sendAll || lastSentJointData[i].translation != data.translation) { - if (sendAll || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { + bool mustSend = sendAll || last.translationIsDefaultPose; + if (mustSend || last.translation != data.translation) { + if (mustSend || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { validity |= (1 << validityBit); #ifdef WANT_DEBUG translationSentCount++; @@ -669,6 +672,19 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } if (sentJointDataOut) { + + // Mark default poses in lastSentJointData, so when they become non-default we send them. + for (int i = 0; i < _jointData.size(); i++) { + const JointData& data = _jointData[i]; + JointData& local = localSentJointDataOut[i]; + if (data.rotationIsDefaultPose) { + local.rotationIsDefaultPose = true; + } + if (data.translationIsDefaultPose) { + local.translationIsDefaultPose = true; + } + } + // Push new sent joint data to sentJointDataOut sentJointDataOut->swap(localSentJointDataOut); } @@ -1816,13 +1832,13 @@ void AvatarData::setJointMappingsFromNetworkReply() { networkReply->deleteLater(); } -void AvatarData::sendAvatarDataPacket() { +void AvatarData::sendAvatarDataPacket(bool sendAll) { auto nodeList = DependencyManager::get(); // about 2% of the time, we send a full update (meaning, we transmit all the joint data), even if nothing has changed. // this is to guard against a joint moving once, the packet getting lost, and the joint never moving again. - bool cullSmallData = (randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); + bool cullSmallData = !sendAll && (randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); auto dataDetail = cullSmallData ? SendAllData : CullSmallData; QByteArray avatarByteArray = toByteArrayStateful(dataDetail); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e927120b07..7c188019db 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -256,6 +256,11 @@ namespace AvatarDataPacket { SixByteQuat rotation[numValidRotations]; // encodeded and compressed by packOrientationQuatToSixBytes() uint8_t translationValidityBits[ceil(numJoints / 8)]; // one bit per joint, if true then a compressed translation follows. SixByteTrans translation[numValidTranslations]; // encodeded and compressed by packFloatVec3ToSignedTwoByteFixed() + + SixByteQuat leftHandControllerRotation; + SixByteTrans leftHandControllerTranslation; + SixByteQuat rightHandControllerRotation; + SixByteTrans rightHandControllerTranslation; }; */ size_t maxJointDataSize(size_t numJoints); @@ -707,11 +712,11 @@ signals: void sessionUUIDChanged(); public slots: - void sendAvatarDataPacket(); + void sendAvatarDataPacket(bool sendAll = false); void sendIdentityPacket(); void setJointMappingsFromNetworkReply(); - void setSessionUUID(const QUuid& sessionUUID) { + virtual void setSessionUUID(const QUuid& sessionUUID) { if (sessionUUID != getID()) { if (sessionUUID == QUuid()) { setID(AVATAR_SELF_ID); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 9c7b46dc17..c6d42a4a1e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2320,6 +2320,16 @@ bool EntityTree::readFromMap(QVariantMap& map) { _persistDataVersion = map["DataVersion"].toInt(); } + _namedPaths.clear(); + if (map.contains("Paths")) { + QVariantMap namedPathsMap = map["Paths"].toMap(); + for(QVariantMap::const_iterator iter = namedPathsMap.begin(); iter != namedPathsMap.end(); ++iter) { + QString namedPathName = iter.key(); + QString namedPathViewPoint = iter.value().toString(); + _namedPaths[namedPathName] = namedPathViewPoint; + } + } + // map will have a top-level list keyed as "Entities". This will be extracted // and iterated over. Each member of this list is converted to a QVariantMap, then // to a QScriptValue, and then to EntityItemProperties. These properties are used diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 5f69714432..791c030fc8 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -301,6 +301,8 @@ public: static bool addMaterialToOverlay(const QUuid& overlayID, graphics::MaterialLayer material, const std::string& parentMaterialName); static bool removeMaterialFromOverlay(const QUuid& overlayID, graphics::MaterialPointer material, const std::string& parentMaterialName); + std::map getNamedPaths() const { return _namedPaths; } + signals: void deletingEntity(const EntityItemID& entityID); void deletingEntityPointer(EntityItem* entityID); @@ -417,6 +419,8 @@ private: static std::function _removeMaterialFromOverlayOperator; bool _serverlessDomain { false }; + + std::map _namedPaths; }; #endif // hifi_EntityTree_h diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 52253b1cbc..9a96364de2 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -316,7 +316,17 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { _shareablePlaceName.clear(); setDomainInfo(lookupUrl, trigger); emit lookupResultsFinished(); - handlePath(DOMAIN_SPAWNING_POINT, LookupTrigger::Internal, false); + + QString path = DOMAIN_SPAWNING_POINT; + QUrlQuery queryArgs(lookupUrl); + const QString LOCATION_QUERY_KEY = "location"; + if (queryArgs.hasQueryItem(LOCATION_QUERY_KEY)) { + path = queryArgs.queryItemValue(LOCATION_QUERY_KEY); + } else { + path = DEFAULT_NAMED_PATH; + } + + handlePath(path, LookupTrigger::Internal, false); return true; } @@ -433,7 +443,9 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QUrl domainURL; domainURL.setScheme(URL_SCHEME_HIFI); domainURL.setHost(domainHostname); - domainURL.setPort(domainPort); + if (domainPort > 0) { + domainURL.setPort(domainPort); + } emit possibleDomainChangeRequired(domainURL, domainID); } else { QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString(); @@ -604,7 +616,9 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri QUrl domainURL; domainURL.setScheme(URL_SCHEME_HIFI); domainURL.setHost(domainIPString); - domainURL.setPort(domainPort); + if (domainPort > 0) { + domainURL.setPort(domainPort); + } hostChanged = setDomainInfo(domainURL, trigger); return true; @@ -625,7 +639,9 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri QUrl domainURL; domainURL.setScheme(URL_SCHEME_HIFI); domainURL.setHost(domainHostname); - domainURL.setPort(domainPort); + if (domainPort > 0) { + domainURL.setPort(domainPort); + } hostChanged = setDomainInfo(domainURL, trigger); return true; @@ -757,7 +773,9 @@ bool AddressManager::setHost(const QString& host, LookupTrigger trigger, quint16 _domainURL = QUrl(); _domainURL.setScheme(URL_SCHEME_HIFI); _domainURL.setHost(host); - _domainURL.setPort(port); + if (port > 0) { + _domainURL.setPort(port); + } // any host change should clear the shareable place name _shareablePlaceName.clear(); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index c20d6d73be..cd8064c4c0 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -173,15 +173,15 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) { QString previousHost = _domainURL.host(); _domainURL = domainURL; - if (domainURL.scheme() != URL_SCHEME_HIFI) { - setIsConnected(true); - } else if (previousHost != domainURL.host()) { + if (previousHost != domainURL.host()) { qCDebug(networking) << "Updated domain hostname to" << domainURL.host(); if (!domainURL.host().isEmpty()) { - // re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname - qCDebug(networking, "Looking up DS hostname %s.", domainURL.host().toLocal8Bit().constData()); - QHostInfo::lookupHost(domainURL.host(), this, SLOT(completedHostnameLookup(const QHostInfo&))); + if (domainURL.scheme() == URL_SCHEME_HIFI) { + // re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname + qCDebug(networking, "Looking up DS hostname %s.", domainURL.host().toLocal8Bit().constData()); + QHostInfo::lookupHost(domainURL.host(), this, SLOT(completedHostnameLookup(const QHostInfo&))); + } DependencyManager::get()->flagTimeForConnectionStep( LimitedNodeList::ConnectionStep::SetDomainHostname); @@ -250,6 +250,17 @@ void DomainHandler::activateICEPublicSocket() { emit completedSocketDiscovery(); } +QString DomainHandler::getViewPointFromNamedPath(QString namedPath) { + auto lookup = _namedPaths.find(namedPath); + if (lookup != _namedPaths.end()) { + return lookup->second; + } + if (namedPath == DEFAULT_NAMED_PATH) { + return DOMAIN_SPAWNING_POINT; + } + return ""; +} + void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { for (int i = 0; i < hostInfo.addresses().size(); i++) { if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) { @@ -297,6 +308,11 @@ void DomainHandler::setIsConnected(bool isConnected) { } } +void DomainHandler::connectedToServerless(std::map namedPaths) { + _namedPaths = namedPaths; + setIsConnected(true); +} + void DomainHandler::requestDomainSettings() { qCDebug(networking) << "Requesting settings from domain server"; diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index fbc60e2492..7137b8084d 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -76,6 +76,10 @@ public: void setIsConnected(bool isConnected); bool isServerless() const { return _domainURL.scheme() != URL_SCHEME_HIFI; } + void connectedToServerless(std::map namedPaths); + + QString getViewPointFromNamedPath(QString namedPath); + bool hasSettings() const { return !_settingsObject.isEmpty(); } void requestDomainSettings(); const QJsonObject& getSettingsObject() const { return _settingsObject; } @@ -200,9 +204,11 @@ private: int _checkInPacketsSinceLastReply { 0 }; QTimer _apiRefreshTimer; + + std::map _namedPaths; }; const QString DOMAIN_SPAWNING_POINT { "/0, -10, 0" }; - +const QString DEFAULT_NAMED_PATH { "/" }; #endif // hifi_DomainHandler_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index cb0d2e4cd5..13931be2ac 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -413,7 +413,16 @@ void NodeList::sendDomainServerCheckIn() { } void NodeList::handleDSPathQuery(const QString& newPath) { - if (_domainHandler.isSocketKnown()) { + if (_domainHandler.isServerless()) { + if (_domainHandler.isConnected()) { + auto viewpoint = _domainHandler.getViewPointFromNamedPath(newPath); + if (!newPath.isEmpty()) { + DependencyManager::get()->goToViewpointForPath(viewpoint, newPath); + } + } else { + _domainHandler.setPendingPath(newPath); + } + } else if (_domainHandler.isSocketKnown()) { // if we have a DS socket we assume it will get this packet and send if off right away sendDSPathQuery(newPath); } else { @@ -427,10 +436,17 @@ void NodeList::sendPendingDSPathQuery() { QString pendingPath = _domainHandler.getPendingPath(); if (!pendingPath.isEmpty()) { - qCDebug(networking) << "Attempting to send pending query to DS for path" << pendingPath; - // this is a slot triggered if we just established a network link with a DS and want to send a path query - sendDSPathQuery(_domainHandler.getPendingPath()); + if (_domainHandler.isServerless()) { + auto viewpoint = _domainHandler.getViewPointFromNamedPath(pendingPath); + if (!pendingPath.isEmpty()) { + DependencyManager::get()->goToViewpointForPath(viewpoint, pendingPath); + } + } else { + qCDebug(networking) << "Attempting to send pending query to DS for path" << pendingPath; + // this is a slot triggered if we just established a network link with a DS and want to send a path query + sendDSPathQuery(_domainHandler.getPendingPath()); + } // clear whatever the pending path was _domainHandler.clearPendingPath(); @@ -498,7 +514,7 @@ void NodeList::processDomainServerPathResponse(QSharedPointer m QString viewpoint = QString::fromUtf8(message->getRawMessage() + message->getPosition(), numViewpointBytes); // Hand it off to the AddressManager so it can handle it as a relative viewpoint - if (DependencyManager::get()->goToViewpointForPath(viewpoint, pathQuery)) { + if (!pathQuery.isEmpty() && DependencyManager::get()->goToViewpointForPath(viewpoint, pathQuery)) { qCDebug(networking) << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery; } else { qCDebug(networking) << "Could not go to viewpoint" << viewpoint diff --git a/scripts/system/edit.js b/scripts/system/edit.js index fd7a488eb7..467842c712 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -455,6 +455,9 @@ var toolBar = (function () { function initialize() { Script.scriptEnding.connect(cleanup); Window.domainChanged.connect(function () { + if (isActive) { + tablet.gotoHomeScreen(); + } that.setActive(false); that.clearEntityList(); }); diff --git a/tests/baking/src/JSBakerTest.cpp b/tests/baking/src/JSBakerTest.cpp index 082ffb047f..e39ccc6b9a 100644 --- a/tests/baking/src/JSBakerTest.cpp +++ b/tests/baking/src/JSBakerTest.cpp @@ -66,12 +66,14 @@ void JSBakerTest::setTestCases() { _testCases.emplace_back("'abcd1234$%^&[](){}'\na", "'abcd1234$%^&[](){}'\na"); _testCases.emplace_back("\"abcd1234$%^&[](){}\"\na", "\"abcd1234$%^&[](){}\"\na"); _testCases.emplace_back("`abcd1234$%^&[](){}`\na", "`abcd1234$%^&[](){}`a"); + _testCases.emplace_back("\' \';", "\' \';"); + _testCases.emplace_back("\'//single line comment\nvar b=2;\';", "\'//single line comment\nvar b=2;\';"); // Edge Cases //No semicolon to terminate an expression, instead a new line used for termination _testCases.emplace_back("var x=5\nvar y=6;", "var x=5\nvar y=6;"); - + //a + ++b is minified as a+ ++b. _testCases.emplace_back("a + ++b", "a + ++b");