diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 1f11faa648..09ab25ad91 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2255,8 +2255,8 @@ void DomainServer::respondToPathQuery(const QByteArray& receivedPacket, const Hi qDebug() << "Sending a viewpoint response for path query" << pathQuery << "-" << viewpointUTF8; // send off the packet - see if we can associate this outbound data to a particular node - SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket); - nodeList->writeUnverifiedDatagram(pathResponsePacket, matchingNode, senderSockAddr); + // TODO: does this senderSockAddr always work for a punched DS client? + nodeList->writeUnverifiedDatagram(pathResponsePacket, senderSockAddr); } } diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index f477be0718..51adb7ba1a 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -25,7 +25,7 @@ DatagramProcessor::DatagramProcessor(QObject* parent) : QObject(parent) { - + } void DatagramProcessor::processDatagrams() { @@ -35,23 +35,23 @@ void DatagramProcessor::processDatagrams() { if (_isShuttingDown) { return; // bail early... we're shutting down. } - + HifiSockAddr senderSockAddr; - + static QByteArray incomingPacket; - + Application* application = Application::getInstance(); auto nodeList = DependencyManager::get(); - + while (DependencyManager::get()->getNodeSocket().hasPendingDatagrams()) { incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->readDatagram(incomingPacket, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); - + _inPacketCount++; _inByteCount += incomingPacket.size(); - + if (nodeList->packetVersionAndHashMatch(incomingPacket)) { - + PacketType incomingType = packetTypeForPacket(incomingPacket); // only process this packet if we have a match on the packet version switch (incomingType) { @@ -72,14 +72,14 @@ void DatagramProcessor::processDatagrams() { Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } - + // update having heard from the audio-mixer and record the bytes received SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket); - + if (audioMixer) { audioMixer->setLastHeardMicrostamp(usecTimestampNow()); } - + break; } case PacketTypeEntityAddResponse: @@ -94,7 +94,7 @@ void DatagramProcessor::processDatagrams() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _octreeProcessor.queueReceivedPacket()"); SharedNodePointer matchedNode = DependencyManager::get()->sendingNodeForPacket(incomingPacket); - + if (matchedNode) { // add this packet to our list of octree packets and process them on the octree data processing application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket); @@ -107,10 +107,10 @@ void DatagramProcessor::processDatagrams() { case PacketTypeAvatarBillboard: { // update having heard from the avatar-mixer and record the bytes received SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket); - + if (avatarMixer) { avatarMixer->setLastHeardMicrostamp(usecTimestampNow()); - + QMetaObject::invokeMethod(DependencyManager::get().data(), "processAvatarMixerDatagram", Q_ARG(const QByteArray&, incomingPacket), Q_ARG(const QWeakPointer&, avatarMixer)); @@ -135,20 +135,20 @@ void DatagramProcessor::processDatagrams() { case PacketTypeNoisyMute: case PacketTypeMuteEnvironment: { bool mute = !DependencyManager::get()->isMuted(); - + if (incomingType == PacketTypeMuteEnvironment) { glm::vec3 position; float radius; - + int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment); memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3)); memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float)); float distance = glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), position); - + mute = mute && (distance < radius); } - + if (mute) { DependencyManager::get()->toggleMute(); if (incomingType == PacketTypeMuteEnvironment) { diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 0dd621a7b7..48d9655e43 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -125,7 +125,6 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { // wasn't an address - lookup the place name // we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path()); - } } @@ -253,10 +252,13 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" << returnedPath; } + } else { + // we didn't override the path or get one back - ask the DS for the viewpoint of its index path + // which we will jump to if it exists + emit pathChangeRequired(INDEX_PATH); } } - } else { qCDebug(networking) << "Received an address manager API response with no domain key. Cannot parse."; qCDebug(networking) << locationMap; diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 08d5cc59a8..5831d62603 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -24,6 +24,7 @@ const QString HIFI_URL_SCHEME = "hifi"; const QString DEFAULT_HIFI_ADDRESS = "hifi://entry"; +const QString INDEX_PATH = "/"; typedef const glm::vec3& (*PositionGetter)(); typedef glm::quat (*OrientationGetter)(); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 4f9585cceb..f7d460d7dd 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -36,19 +36,19 @@ DomainHandler::DomainHandler(QObject* parent) : _settingsObject(), _failedSettingsRequests(0) { - + } void DomainHandler::clearConnectionInfo() { _uuid = QUuid(); - + _icePeer = NetworkPeer(); - + if (requiresICE()) { // if we connected to this domain with ICE, re-set the socket so we reconnect through the ice-server - _sockAddr.setAddress(QHostAddress::Null); + _sockAddr.clear(); } - + setIsConnected(false); } @@ -65,12 +65,15 @@ void DomainHandler::softReset() { void DomainHandler::hardReset() { softReset(); - + qCDebug(networking) << "Hard reset in NodeList DomainHandler."; _iceDomainID = QUuid(); _iceServerSockAddr = HifiSockAddr(); _hostname = QString(); - _sockAddr.setAddress(QHostAddress::Null); + _sockAddr.clear(); + + // clear any pending path we may have wanted to ask the previous DS about + _pendingPath.clear(); } void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) { @@ -80,7 +83,7 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos // change the sockAddr _sockAddr = sockAddr; } - + // some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification _hostname = hostname; } @@ -93,29 +96,29 @@ void DomainHandler::setUUID(const QUuid& uuid) { } void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) { - + if (hostname != _hostname || _sockAddr.getPort() != port) { // re-set the domain info so that auth information is reloaded hardReset(); - + if (hostname != _hostname) { // set the new hostname _hostname = hostname; - + qCDebug(networking) << "Updated domain hostname to" << _hostname; - + // 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.", _hostname.toLocal8Bit().constData()); QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&))); - + UserActivityLogger::getInstance().changedDomain(_hostname); emit hostnameChanged(_hostname); } - + if (_sockAddr.getPort() != port) { qCDebug(networking) << "Updated domain port to" << port; } - + // grab the port by reading the string after the colon _sockAddr.setPort(port); } @@ -125,16 +128,16 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, if (id != _uuid) { // re-set the domain info to connect to new domain hardReset(); - + _iceDomainID = id; - + HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr; replaceableSockAddr->~HifiSockAddr(); replaceableSockAddr = new (replaceableSockAddr) HifiSockAddr(iceServerHostname, ICE_SERVER_DEFAULT_PORT); - + // refresh our ICE client UUID to something new _iceClientID = QUuid::createUuid(); - + qCDebug(networking) << "ICE required to connect to domain via ice server at" << iceServerHostname; } } @@ -142,23 +145,29 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, void DomainHandler::activateICELocalSocket() { _sockAddr = _icePeer.getLocalSocket(); _hostname = _sockAddr.getAddress().toString(); + emit completedSocketDiscovery(); } void DomainHandler::activateICEPublicSocket() { _sockAddr = _icePeer.getPublicSocket(); _hostname = _sockAddr.getAddress().toString(); + emit completedSocketDiscovery(); } void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { for (int i = 0; i < hostInfo.addresses().size(); i++) { if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) { _sockAddr.setAddress(hostInfo.addresses()[i]); + qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(), _sockAddr.getAddress().toString().toLocal8Bit().constData()); + + emit completedSocketDiscovery(); + return; - } + } } - + // if we got here then we failed to lookup the address qCDebug(networking, "Failed domain server lookup"); } @@ -166,10 +175,10 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { void DomainHandler::setIsConnected(bool isConnected) { if (_isConnected != isConnected) { _isConnected = isConnected; - + if (_isConnected) { emit connectedToDomain(_hostname); - + // we've connected to new domain - time to ask it for global settings requestDomainSettings(); } else { @@ -195,9 +204,9 @@ void DomainHandler::requestDomainSettings() { settingsJSONURL.setPath("/settings.json"); Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get()->getOwnerType()); settingsJSONURL.setQuery(QString("type=%1").arg(assignmentType)); - + qCDebug(networking) << "Requesting domain-server settings at" << settingsJSONURL.toString(); - + QNetworkRequest settingsRequest(settingsJSONURL); settingsRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = NetworkAccessManager::getInstance().get(settingsRequest); @@ -210,23 +219,23 @@ const int MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS = 5; void DomainHandler::settingsRequestFinished() { QNetworkReply* settingsReply = reinterpret_cast(sender()); - + int replyCode = settingsReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - + if (settingsReply->error() == QNetworkReply::NoError && replyCode != 301 && replyCode != 302) { // parse the JSON to a QJsonObject and save it _settingsObject = QJsonDocument::fromJson(settingsReply->readAll()).object(); - + qCDebug(networking) << "Received domain settings."; emit settingsReceived(_settingsObject); - + // reset failed settings requests to 0, we got them _failedSettingsRequests = 0; } else { // error grabbing the settings - in some cases this means we are stuck // so we should retry until we get it qCDebug(networking) << "Error getting domain settings -" << settingsReply->errorString() << "- retrying"; - + if (++_failedSettingsRequests >= MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS) { qCDebug(networking) << "Failed to retreive domain-server settings" << MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS << "times. Re-setting connection to domain."; @@ -235,7 +244,7 @@ void DomainHandler::settingsRequestFinished() { emit settingsReceiveFail(); } else { requestDomainSettings(); - } + } } settingsReply->deleteLater(); } @@ -243,30 +252,30 @@ void DomainHandler::settingsRequestFinished() { void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) { // figure out the port that the DS wants us to use for us to talk to them with DTLS int numBytesPacketHeader = numBytesForPacketHeader(dtlsRequirementPacket); - + unsigned short dtlsPort = 0; memcpy(&dtlsPort, dtlsRequirementPacket.data() + numBytesPacketHeader, sizeof(dtlsPort)); - + qCDebug(networking) << "domain-server DTLS port changed to" << dtlsPort << "- Enabling DTLS."; - + _sockAddr.setPort(dtlsPort); - + // initializeDTLSSession(); } void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) { QDataStream iceResponseStream(icePacket); iceResponseStream.skipRawData(numBytesForPacketHeader(icePacket)); - + NetworkPeer packetPeer; iceResponseStream >> packetPeer; - + if (packetPeer.getUUID() != _iceDomainID) { qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection."; } else { qCDebug(networking) << "Received network peer object for domain -" << packetPeer; _icePeer = packetPeer; - + emit requestICEConnectionAttempt(); } } diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 35ef7d8e2c..80a211405b 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -31,67 +31,79 @@ class DomainHandler : public QObject { Q_OBJECT public: DomainHandler(QObject* parent = 0); - + void clearConnectionInfo(); void clearSettings(); - + const QUuid& getUUID() const { return _uuid; } void setUUID(const QUuid& uuid); - + const QString& getHostname() const { return _hostname; } - + const QHostAddress& getIP() const { return _sockAddr.getAddress(); } void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); } - + const HifiSockAddr& getSockAddr() { return _sockAddr; } void setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname); - + unsigned short getPort() const { return _sockAddr.getPort(); } void setPort(quint16 port) { _sockAddr.setPort(port); } - + const QUuid& getAssignmentUUID() const { return _assignmentUUID; } void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } - + const QUuid& getICEDomainID() const { return _iceDomainID; } - + const QUuid& getICEClientID() const { return _iceClientID; } - + bool requiresICE() const { return !_iceServerSockAddr.isNull(); } const HifiSockAddr& getICEServerSockAddr() const { return _iceServerSockAddr; } NetworkPeer& getICEPeer() { return _icePeer; } void activateICELocalSocket(); void activateICEPublicSocket(); - + bool isConnected() const { return _isConnected; } void setIsConnected(bool isConnected); - + bool hasSettings() const { return !_settingsObject.isEmpty(); } void requestDomainSettings(); const QJsonObject& getSettingsObject() const { return _settingsObject; } - + void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket); void processICEResponsePacket(const QByteArray& icePacket); - + + void setPendingPath(const QString& pendingPath) { _pendingPath = pendingPath; } + const QString& getPendingPath() { return _pendingPath; } + void clearPendingPath() { _pendingPath.clear(); } + + bool isSocketKnown() const { return !_sockAddr.getAddress().isNull(); } + void softReset(); public slots: void setHostnameAndPort(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT); void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id); - + private slots: void completedHostnameLookup(const QHostInfo& hostInfo); void settingsRequestFinished(); signals: void hostnameChanged(const QString& hostname); + + // NOTE: the emission of completedSocketDiscovery does not mean a connection to DS is established + // It means that, either from DNS lookup or ICE, we think we have a socket we can talk to DS on + void completedSocketDiscovery(); + void connectedToDomain(const QString& hostname); void disconnectedFromDomain(); + void requestICEConnectionAttempt(); - + void settingsReceived(const QJsonObject& domainSettingsObject); void settingsReceiveFail(); - + private: void hardReset(); - + QUuid _uuid; QString _hostname; HifiSockAddr _sockAddr; @@ -103,6 +115,7 @@ private: bool _isConnected; QJsonObject _settingsObject; int _failedSettingsRequests; + QString _pendingPath; }; #endif // hifi_DomainHandler_h diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 4d3944012e..9151e51af2 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -29,26 +29,27 @@ public: HifiSockAddr(const HifiSockAddr& otherSockAddr); HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false); HifiSockAddr(const sockaddr* sockaddr); - + bool isNull() const { return _address.isNull() && _port == 0; } + void clear() { _address = QHostAddress::Null; _port = 0;} HifiSockAddr& operator=(const HifiSockAddr& rhsSockAddr); void swap(HifiSockAddr& otherSockAddr); - + bool operator==(const HifiSockAddr& rhsSockAddr) const; bool operator!=(const HifiSockAddr& rhsSockAddr) const { return !(*this == rhsSockAddr); } - + const QHostAddress& getAddress() const { return _address; } QHostAddress* getAddressPointer() { return &_address; } void setAddress(const QHostAddress& address) { _address = address; } - + quint16 getPort() const { return _port; } quint16* getPortPointer() { return &_port; } void setPort(quint16 port) { _port = port; } - + static int packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr); static int unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr); - + friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr); friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr); friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index f56a710980..dcfb006b2b 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -54,7 +54,12 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID, &_domainHandler, &DomainHandler::setIceServerHostnameAndID); - connect(addressManager.data(), &AddressManager::pathChangeRequired, this, &NodeList::sendDSPathQuery); + // handle a request for a path change from the AddressManager + connect(addressManager.data(), &AddressManager::pathChangeRequired, this, &NodeList::handleDSPathQuery); + + // in case we don't know how to talk to DS when a path change is requested + // fire off any pending DS path query when we get socket information + connect(&_domainHandler, &DomainHandler::completedSocketDiscovery, this, &NodeList::sendPendingDSPathQuery); // clear our NodeList when the domain changes connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset); @@ -401,9 +406,34 @@ void NodeList::sendDomainServerCheckIn() { } } +void NodeList::handleDSPathQuery(const QString& newPath) { + 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 { + // otherwise we make it pending so that it can be sent once a connection is established + _domainHandler.setPendingPath(newPath); + } +} + +void NodeList::sendPendingDSPathQuery() { + + QString pendingPath = _domainHandler.getPendingPath(); + + if (!pendingPath.isEmpty()) { + qCDebug(networking) << "Attemping 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(); + } +} + void NodeList::sendDSPathQuery(const QString& newPath) { // only send a path query if we know who our DS is or is going to be - if (!_domainHandler.getSockAddr().isNull()) { + if (_domainHandler.isSocketKnown()) { // construct the path query packet QByteArray pathQueryPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerPathQuery); @@ -420,12 +450,13 @@ void NodeList::sendDSPathQuery(const QString& newPath) { // append the path itself to the query packet pathQueryPacket.append(pathQueryUTF8); - qDebug() << "Sending a path query packet for path" << newPath << "to domain-server at" << _domainHandler.getSockAddr(); + qCDebug(networking) << "Sending a path query packet for path" << newPath << "to domain-server at" + << _domainHandler.getSockAddr(); // send off the path query writeUnverifiedDatagram(pathQueryPacket, _domainHandler.getSockAddr()); } else { - qDebug() << "Path" << newPath << "would make PacketTypeDomainServerPathQuery packet > MAX_PACKET_SIZE." << + qCDebug(networking) << "Path" << newPath << "would make PacketTypeDomainServerPathQuery packet > MAX_PACKET_SIZE." << "Will not send query."; } } @@ -462,9 +493,10 @@ void NodeList::handleDSPathQueryResponse(const QByteArray& packet) { // Hand it off to the AddressManager so it can handle it as a relative viewpoint if (DependencyManager::get()->goToViewpoint(viewpoint)) { - qDebug() << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery; + qCDebug(networking) << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery; } else { - qDebug() << "Could not go to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery; + qCDebug(networking) << "Could not go to viewpoint" << viewpoint + << "which was the lookup result for path" << pathQuery; } } } diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 2b755823a6..39b1e3e2d2 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -71,9 +71,11 @@ public slots: void reset(); void sendDomainServerCheckIn(); void pingInactiveNodes(); - void sendDSPathQuery(const QString& newPath); + void handleDSPathQuery(const QString& newPath); signals: void limitOfSilentDomainCheckInsReached(); +private slots: + void sendPendingDSPathQuery(); private: NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); @@ -92,6 +94,8 @@ private: void handleDSPathQueryResponse(const QByteArray& packet); + void sendDSPathQuery(const QString& newPath); + NodeType_t _ownerType; NodeSet _nodeTypesOfInterest; DomainHandler _domainHandler;