Example #1
0
void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
    if (node->getType() == NodeType::AudioMixer) {
        flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPing);
    }

    // every second we're trying to ping this node and we're not getting anywhere - debug that out
    const int NUM_DEBUG_CONNECTION_ATTEMPTS = 1000 / (UDP_PUNCH_PING_INTERVAL_MS);

    if (node->getConnectionAttempts() > 0 && node->getConnectionAttempts() % NUM_DEBUG_CONNECTION_ATTEMPTS == 0) {
        qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last second.";
    }

    // send the ping packet to the local and public sockets for this node
    auto localPingPacket = constructPingPacket(PingType::Local);
    sendPacket(std::move(localPingPacket), *node, node->getLocalSocket());

    auto publicPingPacket = constructPingPacket(PingType::Public);
    sendPacket(std::move(publicPingPacket), *node, node->getPublicSocket());

    if (!node->getSymmetricSocket().isNull()) {
        auto symmetricPingPacket = constructPingPacket(PingType::Symmetric);
        sendPacket(std::move(symmetricPingPacket), *node, node->getSymmetricSocket());
    }

    node->incrementConnectionAttempts();
}
Example #2
0
void NodeList::pingPunchForDomainServer() {
    // make sure if we're here that we actually still need to ping the domain-server
    if (_domainHandler.getIP().isNull() && _domainHandler.getICEPeer().hasSockets()) {

        // check if we've hit the number of pings we'll send to the DS before we consider it a fail
        const int NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET = 2000 / UDP_PUNCH_PING_INTERVAL_MS;

        if (_domainHandler.getICEPeer().getConnectionAttempts() == 0) {
            qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID"
                << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
        } else {
            if (_domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) {
                // if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat
                qCDebug(networking) << "No ping replies received from domain-server with ID"
                    << uuidStringWithoutCurlyBraces(_domainHandler.getICEClientID()) << "-" << "re-sending ICE query.";

                _domainHandler.getICEPeer().softReset();
                handleICEConnectionToDomainServer();

                return;
            }
        }

        flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendPingsToDS);

        // send the ping packet to the local and public sockets for this node
        auto localPingPacket = constructICEPingPacket(PingType::Local, _sessionUUID);
        sendPacket(std::move(localPingPacket), _domainHandler.getICEPeer().getLocalSocket());

        auto publicPingPacket = constructICEPingPacket(PingType::Public, _sessionUUID);
        sendPacket(std::move(publicPingPacket), _domainHandler.getICEPeer().getPublicSocket());

        _domainHandler.getICEPeer().incrementConnectionAttempts();
    }
}
Example #3
0
void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber,
                                             const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale,
                                             PacketType packetType, QString codecName) {
    static std::mutex _mutex;
    using Locker = std::unique_lock<std::mutex>;
    auto nodeList = DependencyManager::get<NodeList>();
    SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
    if (audioMixer && audioMixer->getActiveSocket()) {
        Locker lock(_mutex);
        auto audioPacket = NLPacket::create(packetType);

        // FIXME - this is not a good way to determine stereoness with codecs.... 
        quint8 isStereo = bytes == AudioConstants::NETWORK_FRAME_BYTES_STEREO ? 1 : 0;

        // write sequence number
        auto sequence = sequenceNumber++;
        audioPacket->writePrimitive(sequence);

        // write the codec
        audioPacket->writeString(codecName);

        if (packetType == PacketType::SilentAudioFrame) {
            // pack num silent samples
            quint16 numSilentSamples = isStereo ?
                AudioConstants::NETWORK_FRAME_SAMPLES_STEREO :
                AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
            audioPacket->writePrimitive(numSilentSamples);
        } else {
            // set the mono/stereo byte
            audioPacket->writePrimitive(isStereo);
        }

        // pack the three float positions
        audioPacket->writePrimitive(transform.getTranslation());
        // pack the orientation
        audioPacket->writePrimitive(transform.getRotation());

        audioPacket->writePrimitive(avatarBoundingBoxCorner);
        audioPacket->writePrimitive(avatarBoundingBoxScale);


        if (audioPacket->getType() != PacketType::SilentAudioFrame) {
            // audio samples have already been packed (written to networkAudioSamples)
            int leadingBytes = audioPacket->getPayloadSize();
            audioPacket->setPayloadSize(leadingBytes + bytes);
            memcpy(audioPacket->getPayload() + leadingBytes, audioData, bytes);
        }
        nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket);
        nodeList->sendUnreliablePacket(*audioPacket, *audioMixer);
    }
}
Example #4
0
void NodeList::handleICEConnectionToDomainServer() {
    // if we're still waiting to get sockets we want to ping for the domain-server
    // then send another heartbeat now
    if (!_domainHandler.getICEPeer().hasSockets()) {

        _domainHandler.getICEPeer().resetConnectionAttempts();

        flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendICEServerQuery);

        LimitedNodeList::sendPeerQueryToIceServer(_domainHandler.getICEServerSockAddr(),
                                                  _domainHandler.getICEClientID(),
                                                  _domainHandler.getICEDomainID());
    }
}
Example #5
0
void NodeList::activateSocketFromNodeCommunication(ReceivedMessage& message, const SharedNodePointer& sendingNode) {
    // deconstruct this ping packet to see if it is a public or local reply
    QDataStream packetStream(message.getMessage());

    quint8 pingType;
    packetStream >> pingType;

    // if this is a local or public ping then we can activate a socket
    // we do nothing with agnostic pings, those are simply for timing
    if (pingType == PingType::Local && sendingNode->getActiveSocket() != &sendingNode->getLocalSocket()) {
        sendingNode->activateLocalSocket();
    } else if (pingType == PingType::Public && !sendingNode->getActiveSocket()) {
        sendingNode->activatePublicSocket();
    } else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) {
        sendingNode->activateSymmetricSocket();
    }

    if (sendingNode->getType() == NodeType::AudioMixer) {
       flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetAudioMixerSocket);
    }
}
void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, const Transform& transform, PacketType packetType) {
    static std::mutex _mutex;
    using Locker = std::unique_lock<std::mutex>;
    auto nodeList = DependencyManager::get<NodeList>();
    SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
    if (audioMixer && audioMixer->getActiveSocket()) {
        Locker lock(_mutex);
        static std::unique_ptr<NLPacket> audioPacket = NLPacket::create(PacketType::Unknown);
        quint8 isStereo = bytes == AudioConstants::NETWORK_FRAME_BYTES_STEREO ? 1 : 0;
        audioPacket->setType(packetType);
        // reset the audio packet so we can start writing
        audioPacket->reset();
        // write sequence number
        audioPacket->writePrimitive(sequenceNumber++);
        if (audioPacket->getType() == PacketType::SilentAudioFrame) {
            // pack num silent samples
            quint16 numSilentSamples = isStereo ?
                AudioConstants::NETWORK_FRAME_SAMPLES_STEREO :
                AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
            audioPacket->writePrimitive(numSilentSamples);
        } else {
            // set the mono/stereo byte
            audioPacket->writePrimitive(isStereo);
        }

        // pack the three float positions
        audioPacket->writePrimitive(transform.getTranslation());
        // pack the orientation
        audioPacket->writePrimitive(transform.getRotation());

        if (audioPacket->getType() != PacketType::SilentAudioFrame) {
            // audio samples have already been packed (written to networkAudioSamples)
            audioPacket->setPayloadSize(audioPacket->getPayloadSize() + bytes);
            static const int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8);
            memcpy(audioPacket->getPayload() + leadingBytes, audioData, bytes);
        }
        nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket);
        nodeList->sendUnreliablePacket(*audioPacket, *audioMixer);
    }
}
Example #7
0
void NodeList::sendDomainServerCheckIn() {
    if (_isShuttingDown) {
        qCDebug(networking) << "Refusing to send a domain-server check in while shutting down.";
        return;
    }
    
    if (_publicSockAddr.isNull()) {
        // we don't know our public socket and we need to send it to the domain server
        qCDebug(networking) << "Waiting for inital public socket from STUN. Will not send domain-server check in.";
    } else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) {
        qCDebug(networking) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in.";
        handleICEConnectionToDomainServer();
    } else if (!_domainHandler.getIP().isNull()) {
        bool isUsingDTLS = false;

        PacketType domainPacketType = !_domainHandler.isConnected()
            ? PacketType::DomainConnectRequest : PacketType::DomainListRequest;

        if (!_domainHandler.isConnected()) {
            qCDebug(networking) << "Sending connect request to domain-server at" << _domainHandler.getHostname();

            // is this our localhost domain-server?
            // if so we need to make sure we have an up-to-date local port in case it restarted

            if (_domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost
                || _domainHandler.getHostname() == "localhost") {

                quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
                getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, domainPort);
                qCDebug(networking) << "Local domain-server port read from shared memory (or default) is" << domainPort;
                _domainHandler.setPort(domainPort);
            }

        }

        // check if we're missing a keypair we need to verify ourselves with the domain-server
        auto& accountManager = AccountManager::getInstance();
        const QUuid& connectionToken = _domainHandler.getConnectionToken();

        // we assume that we're on the same box as the DS if it has the same local address and
        // it didn't present us with a connection token to use for username signature
        bool localhostDomain = _domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost
            || (_domainHandler.getSockAddr().getAddress() == _localSockAddr.getAddress() && connectionToken.isNull());

        bool requiresUsernameSignature = !_domainHandler.isConnected() && !connectionToken.isNull() && !localhostDomain;

        if (requiresUsernameSignature && !accountManager.getAccountInfo().hasPrivateKey()) {
            qWarning() << "A keypair is required to present a username signature to the domain-server"
                << "but no keypair is present. Waiting for keypair generation to complete.";
            accountManager.generateNewUserKeypair();

            // don't send the check in packet - wait for the keypair first
            return;
        }

        auto domainPacket = NLPacket::create(domainPacketType);
        
        QDataStream packetStream(domainPacket.get());

        if (domainPacketType == PacketType::DomainConnectRequest) {
            QUuid connectUUID;

            if (!_domainHandler.getAssignmentUUID().isNull()) {
                // this is a connect request and we're an assigned node
                // so set our packetUUID as the assignment UUID
                connectUUID = _domainHandler.getAssignmentUUID();
            } else if (_domainHandler.requiresICE()) {
                // this is a connect request and we're an interface client
                // that used ice to discover the DS
                // so send our ICE client UUID with the connect request
                connectUUID = _domainHandler.getICEClientID();
            }

            // pack the connect UUID for this connect request
            packetStream << connectUUID;
        }

        // pack our data to send to the domain-server
        packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();

        if (!_domainHandler.isConnected()) {
            DataServerAccountInfo& accountInfo = accountManager.getAccountInfo();
            packetStream << accountInfo.getUsername();

            // if this is a connect request, and we can present a username signature, send it along
            if (requiresUsernameSignature && accountManager.getAccountInfo().hasPrivateKey()) {
                const QByteArray& usernameSignature = accountManager.getAccountInfo().getUsernameSignature(connectionToken);
                packetStream << usernameSignature;
            }
        }

        flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn);

        if (!isUsingDTLS) {
            sendPacket(std::move(domainPacket), _domainHandler.getSockAddr());
        }

        if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
            // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
            // so emit our signal that says that
            emit limitOfSilentDomainCheckInsReached();
        }

        // increment the count of un-replied check-ins
        _numNoReplyDomainCheckIns++;
    }
}