예제 #1
0
파일: NodeList.cpp 프로젝트: VR-cdrone/hifi
qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) {
    QByteArray statsPacket = byteArrayWithPopulatedHeader(PacketTypeNodeJsonStats);
    QDataStream statsPacketStream(&statsPacket, QIODevice::Append);
    
    statsPacketStream << statsObject.toVariantMap();
    
    return writeUnverifiedDatagram(statsPacket, _domainHandler.getSockAddr());
}
예제 #2
0
파일: NodeList.cpp 프로젝트: VR-cdrone/hifi
void NodeList::handleICEConnectionToDomainServer() {
    if (_domainHandler.getICEPeer().isNull()
        || _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {
        
        _domainHandler.getICEPeer().resetConnectionAttemps();
        
        LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(),
                                                  _domainHandler.getICEClientID(),
                                                  _domainHandler.getICEDomainID());
    } else {
        qDebug() << "Sending ping packets to establish connectivity with domain-server with ID"
            << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
        
        // send the ping packet to the local and public sockets for this nodfe
        QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID());
        writeUnverifiedDatagram(localPingPacket, _domainHandler.getICEPeer().getLocalSocket());
        
        QByteArray publicPingPacket = constructPingPacket(PingType::Public, false, _domainHandler.getICEClientID());
        writeUnverifiedDatagram(publicPingPacket, _domainHandler.getICEPeer().getPublicSocket());
        
        _domainHandler.getICEPeer().incrementConnectionAttempts();
    }
}
예제 #3
0
qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram,
                                      const SharedNodePointer& destinationNode,
                                      const HifiSockAddr& overridenSockAddr) {
    if (destinationNode) {
        PacketType packetType = packetTypeForPacket(datagram);

        if (NON_VERIFIED_PACKETS.contains(packetType)) {
            return writeUnverifiedDatagram(datagram, destinationNode, overridenSockAddr);
        }

        // if we don't have an overridden address, assume they want to send to the node's active socket
        const HifiSockAddr* destinationSockAddr = &overridenSockAddr;
        if (overridenSockAddr.isNull()) {
            if (destinationNode->getActiveSocket()) {
                // use the node's active socket as the destination socket
                destinationSockAddr = destinationNode->getActiveSocket();
            } else {
                // we don't have a socket to send to, return 0
                return 0;
            }
        }

        QByteArray datagramCopy = datagram;

        // if we're here and the connection secret is null, debug out - this could be a problem
        if (destinationNode->getConnectionSecret().isNull()) {
            qDebug() << "LimitedNodeList::writeDatagram called for verified datagram with null connection secret for"
                     << "destination node" << destinationNode->getUUID() << " - this is either not secure or will cause"
                     << "this packet to be unverifiable on the receiving side.";
        }

        // perform replacement of hash and optionally also sequence number in the header
        if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
            PacketSequenceNumber sequenceNumber = getNextSequenceNumberForPacket(destinationNode->getUUID(), packetType);
            replaceHashAndSequenceNumberInPacket(datagramCopy, destinationNode->getConnectionSecret(),
                                                 sequenceNumber, packetType);
        } else {
            replaceHashInPacket(datagramCopy, destinationNode->getConnectionSecret(), packetType);
        }

        emit dataSent(destinationNode->getType(), datagram.size());
        auto bytesWritten = writeDatagram(datagramCopy, *destinationSockAddr);
        // Keep track of per-destination-node bandwidth
        destinationNode->recordBytesSent(bytesWritten);
        return bytesWritten;
    }

    // didn't have a destinationNode to send to, return 0
    return 0;
}
예제 #4
0
파일: NodeList.cpp 프로젝트: VR-cdrone/hifi
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
    switch (packetTypeForPacket(packet)) {
        case PacketTypeDomainList: {
            processDomainServerList(packet);
            break;
        }
        case PacketTypeDomainServerRequireDTLS: {
            _domainHandler.parseDTLSRequirementPacket(packet);
            break;
        }
        case PacketTypeIceServerHeartbeatResponse: {
            _domainHandler.processICEResponsePacket(packet);
            break;
        }
        case PacketTypePing: {
            // send back a reply
            SharedNodePointer matchingNode = sendingNodeForPacket(packet);
            if (matchingNode) {
                matchingNode->setLastHeardMicrostamp(usecTimestampNow());
                QByteArray replyPacket = constructPingReplyPacket(packet);
                writeDatagram(replyPacket, matchingNode, senderSockAddr);
                
                // If we don't have a symmetric socket for this node and this socket doesn't match
                // what we have for public and local then set it as the symmetric.
                // This allows a server on a reachable port to communicate with nodes on symmetric NATs
                if (matchingNode->getSymmetricSocket().isNull()) {
                    if (senderSockAddr != matchingNode->getLocalSocket() && senderSockAddr != matchingNode->getPublicSocket()) {
                        matchingNode->setSymmetricSocket(senderSockAddr);
                    }
                }
            }
            
            break;
        }
        case PacketTypePingReply: {
            SharedNodePointer sendingNode = sendingNodeForPacket(packet);
            
            if (sendingNode) {
                sendingNode->setLastHeardMicrostamp(usecTimestampNow());
                
                // activate the appropriate socket for this node, if not yet updated
                activateSocketFromNodeCommunication(packet, sendingNode);
                
                // set the ping time for this node for stat collection
                timePingReply(packet, sendingNode);
            }
            
            break;
        }
        case PacketTypeUnverifiedPing: {
            // send back a reply
            QByteArray replyPacket = constructPingReplyPacket(packet, _domainHandler.getICEClientID());
            writeUnverifiedDatagram(replyPacket, senderSockAddr);
            break;
        }
        case PacketTypeUnverifiedPingReply: {
            qDebug() << "Received reply from domain-server on" << senderSockAddr;
            
            // for now we're unsafely assuming this came back from the domain
            if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) {
                qDebug() << "Connecting to domain using local socket";
                _domainHandler.activateICELocalSocket();
            } else if (senderSockAddr == _domainHandler.getICEPeer().getPublicSocket()) {
                qDebug() << "Conecting to domain using public socket";
                _domainHandler.activateICEPublicSocket();
            } else {
                qDebug() << "Reply does not match either local or public socket for domain. Will not connect.";
            }
        }
        case PacketTypeStunResponse: {
            // a STUN packet begins with 00, we've checked the second zero with packetVersionMatch
            // pass it along so it can be processed into our public address and port
            processSTUNResponse(packet);
            break;
        }
        default:
            LimitedNodeList::processNodeData(senderSockAddr, packet);
            break;
    }
}
예제 #5
0
qint64 LimitedNodeList::writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
        const HifiSockAddr& overridenSockAddr) {
    return writeUnverifiedDatagram(QByteArray(data, size), destinationNode, overridenSockAddr);
}
int OctreeInboundPacketProcessor::sendNackPackets() {
    int packetsSent = 0;

    if (_shuttingDown) {
        qDebug() << "OctreeInboundPacketProcessor::sendNackPackets() while shutting down... ignore";
        return packetsSent;
    }

    char packet[MAX_PACKET_SIZE];
    
    NodeToSenderStatsMapIterator i = _singleSenderStats.begin();
    while (i != _singleSenderStats.end()) {

        QUuid nodeUUID = i.key();
        SingleSenderStats nodeStats = i.value();

        // check if this node is still alive.  Remove its stats if it's dead.
        if (!isAlive(nodeUUID)) {
            i = _singleSenderStats.erase(i);
            continue;
        }

        // if there are packets from _node that are waiting to be processed,
        // don't send a NACK since the missing packets may be among those waiting packets.
        if (hasPacketsToProcessFrom(nodeUUID)) {
            i++;
            continue;
        }

        const SharedNodePointer& destinationNode = DependencyManager::get<NodeList>()->nodeWithUUID(nodeUUID);

        // retrieve sequence number stats of node, prune its missing set
        SequenceNumberStats& sequenceNumberStats = nodeStats.getIncomingEditSequenceNumberStats();
        sequenceNumberStats.pruneMissingSet();
        
        // construct nack packet(s) for this node
        const QSet<unsigned short int>& missingSequenceNumbers = sequenceNumberStats.getMissingSet();
        int numSequenceNumbersAvailable = missingSequenceNumbers.size();
        QSet<unsigned short int>::const_iterator missingSequenceNumberIterator = missingSequenceNumbers.constBegin();
        while (numSequenceNumbersAvailable > 0) {

            char* dataAt = packet;
            int bytesRemaining = MAX_PACKET_SIZE;
            
            auto nodeList = DependencyManager::get<NodeList>();

            // pack header
            int numBytesPacketHeader = nodeList->populatePacketHeader(packet, _myServer->getMyEditNackType());
            dataAt += numBytesPacketHeader;
            bytesRemaining -= numBytesPacketHeader;

            // calculate and pack the number of sequence numbers to nack
            int numSequenceNumbersRoomFor = (bytesRemaining - sizeof(uint16_t)) / sizeof(unsigned short int);
            uint16_t numSequenceNumbers = std::min(numSequenceNumbersAvailable, numSequenceNumbersRoomFor);
            uint16_t* numSequenceNumbersAt = (uint16_t*)dataAt;
            *numSequenceNumbersAt = numSequenceNumbers;
            dataAt += sizeof(uint16_t);

            // pack sequence numbers to nack
            for (uint16_t i = 0; i < numSequenceNumbers; i++) {
                unsigned short int* sequenceNumberAt = (unsigned short int*)dataAt;
                *sequenceNumberAt = *missingSequenceNumberIterator;
                dataAt += sizeof(unsigned short int);

                missingSequenceNumberIterator++;
            }
            numSequenceNumbersAvailable -= numSequenceNumbers;

            // send it
            nodeList->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode);
            packetsSent++;
            
            qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID;
        }
        i++;
    }
    return packetsSent;
}
예제 #7
0
void OctreeHeadlessViewer::queryOctree() {
    char serverType = getMyNodeType();
    PacketType packetType = getMyQueryMessageType();
    NodeToJurisdictionMap& jurisdictions = *_jurisdictionListener->getJurisdictions();

    bool wantExtraDebugging = false;

    if (wantExtraDebugging) {
        qCDebug(octree) << "OctreeHeadlessViewer::queryOctree() _jurisdictionListener=" << _jurisdictionListener;
        qCDebug(octree) << "---------------";
        qCDebug(octree) << "_jurisdictionListener=" << _jurisdictionListener;
        qCDebug(octree) << "Jurisdictions...";
        jurisdictions.lockForRead();
        for (NodeToJurisdictionMapIterator i = jurisdictions.begin(); i != jurisdictions.end(); ++i) {
            qCDebug(octree) << i.key() << ": " << &i.value();
        }
        jurisdictions.unlock();
        qCDebug(octree) << "---------------";
    }

    // These will be the same for all servers, so we can set them up once and then reuse for each server we send to.
    _octreeQuery.setWantLowResMoving(true);
    _octreeQuery.setWantColor(true);
    _octreeQuery.setWantDelta(true);
    _octreeQuery.setWantOcclusionCulling(false);
    _octreeQuery.setWantCompression(true); // TODO: should be on by default

    _octreeQuery.setCameraPosition(_viewFrustum.getPosition());
    _octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
    _octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
    _octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio());
    _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
    _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
    _octreeQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition());
    _octreeQuery.setOctreeSizeScale(getVoxelSizeScale());
    _octreeQuery.setBoundaryLevelAdjust(getBoundaryLevelAdjust());

    unsigned char queryPacket[MAX_PACKET_SIZE];

    // Iterate all of the nodes, and get a count of how many voxel servers we have...
    int totalServers = 0;
    int inViewServers = 0;
    int unknownJurisdictionServers = 0;
    
    DependencyManager::get<NodeList>()->eachNode([&](const SharedNodePointer& node){
        // only send to the NodeTypes that are serverType
        if (node->getActiveSocket() && node->getType() == serverType) {
            totalServers++;
            
            // get the server bounds for this server
            QUuid nodeUUID = node->getUUID();
            
            // if we haven't heard from this voxel server, go ahead and send it a query, so we
            // can get the jurisdiction...
            jurisdictions.lockForRead();
            if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
                jurisdictions.unlock();
                unknownJurisdictionServers++;
            } else {
                const JurisdictionMap& map = (jurisdictions)[nodeUUID];
                
                unsigned char* rootCode = map.getRootOctalCode();
                
                if (rootCode) {
                    VoxelPositionSize rootDetails;
                    voxelDetailsForCode(rootCode, rootDetails);
                    jurisdictions.unlock();
                    AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
                    
                    ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
                    
                    if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
                        inViewServers++;
                    }
                } else {
                    jurisdictions.unlock();
                }
            }
        }
    });

    if (wantExtraDebugging) {
        qCDebug(octree, "Servers: total %d, in view %d, unknown jurisdiction %d",
            totalServers, inViewServers, unknownJurisdictionServers);
    }

    int perServerPPS = 0;
    const int SMALL_BUDGET = 10;
    int perUnknownServer = SMALL_BUDGET;
    int totalPPS = getMaxPacketsPerSecond();

    // determine PPS based on number of servers
    if (inViewServers >= 1) {
        // set our preferred PPS to be exactly evenly divided among all of the voxel servers... and allocate 1 PPS
        // for each unknown jurisdiction server
        perServerPPS = (totalPPS / inViewServers) - (unknownJurisdictionServers * perUnknownServer);
    } else {
        if (unknownJurisdictionServers > 0) {
            perUnknownServer = (totalPPS / unknownJurisdictionServers);
        }
    }

    if (wantExtraDebugging) {
        qCDebug(octree, "perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer);
    }

    auto nodeList = DependencyManager::get<NodeList>();
    nodeList->eachNode([&](const SharedNodePointer& node){
        // only send to the NodeTypes that are serverType
        if (node->getActiveSocket() && node->getType() == serverType) {
            
            // get the server bounds for this server
            QUuid nodeUUID = node->getUUID();
            
            bool inView = false;
            bool unknownView = false;
            
            // if we haven't heard from this voxel server, go ahead and send it a query, so we
            // can get the jurisdiction...
            jurisdictions.lockForRead();
            if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
                jurisdictions.unlock();
                unknownView = true; // assume it's in view
                if (wantExtraDebugging) {
                    qCDebug(octree) << "no known jurisdiction for node " << *node << ", assume it's visible.";
                }
            } else {
                const JurisdictionMap& map = (jurisdictions)[nodeUUID];
                
                unsigned char* rootCode = map.getRootOctalCode();
                
                if (rootCode) {
                    VoxelPositionSize rootDetails;
                    voxelDetailsForCode(rootCode, rootDetails);
                    jurisdictions.unlock();
                    AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
                    
                    ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
                    if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
                        inView = true;
                    } else {
                        inView = false;
                    }
                } else {
                    jurisdictions.unlock();
                    if (wantExtraDebugging) {
                        qCDebug(octree) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
                    }
                }
            }
            
            if (inView) {
                _octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS);
                if (wantExtraDebugging) {
                    qCDebug(octree) << "inView for node " << *node << ", give it budget of " << perServerPPS;
                }
            } else if (unknownView) {
                if (wantExtraDebugging) {
                    qCDebug(octree) << "no known jurisdiction for node " << *node << ", give it budget of "
                    << perUnknownServer << " to send us jurisdiction.";
                }
                
                // set the query's position/orientation to be degenerate in a manner that will get the scene quickly
                // If there's only one server, then don't do this, and just let the normal voxel query pass through
                // as expected... this way, we will actually get a valid scene if there is one to be seen
                if (totalServers > 1) {
                    _octreeQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1));
                    const glm::quat OFF_IN_NEGATIVE_SPACE = glm::quat(-0.5, 0, -0.5, 1.0);
                    _octreeQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE);
                    _octreeQuery.setCameraNearClip(0.1f);
                    _octreeQuery.setCameraFarClip(0.1f);
                    if (wantExtraDebugging) {
                        qCDebug(octree) << "Using 'minimal' camera position for node" << *node;
                    }
                } else {
                    if (wantExtraDebugging) {
                        qCDebug(octree) << "Using regular camera position for node" << *node;
                    }
                }
                _octreeQuery.setMaxQueryPacketsPerSecond(perUnknownServer);
            } else {
                _octreeQuery.setMaxQueryPacketsPerSecond(0);
            }
            // set up the packet for sending...
            unsigned char* endOfQueryPacket = queryPacket;
            
            // insert packet type/version and node UUID
            endOfQueryPacket += nodeList->populatePacketHeader(reinterpret_cast<char*>(endOfQueryPacket), packetType);
            
            // encode the query data...
            endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket);
            
            int packetLength = endOfQueryPacket - queryPacket;
            
            // make sure we still have an active socket
            nodeList->writeUnverifiedDatagram(reinterpret_cast<const char*>(queryPacket), packetLength, node);
        }
    });
}