mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #4995 from birarda/domain-dtls
speed up initial connection to domains
This commit is contained in:
commit
b0f6826d17
28 changed files with 1295 additions and 778 deletions
|
@ -65,9 +65,13 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_settingsManager(),
|
||||
_iceServerSocket(ICE_SERVER_DEFAULT_HOSTNAME, ICE_SERVER_DEFAULT_PORT)
|
||||
{
|
||||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||
|
||||
LogUtils::init();
|
||||
Setting::init();
|
||||
|
||||
connect(this, &QCoreApplication::aboutToQuit, this, &DomainServer::aboutToQuit);
|
||||
|
||||
setOrganizationName("High Fidelity");
|
||||
setOrganizationDomain("highfidelity.io");
|
||||
setApplicationName("domain-server");
|
||||
|
@ -106,6 +110,11 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::aboutToQuit() {
|
||||
// clear the log handler so that Qt doesn't call the destructor on LogHandler
|
||||
qInstallMessageHandler(0);
|
||||
}
|
||||
|
||||
void DomainServer::restart() {
|
||||
qDebug() << "domain-server is restarting.";
|
||||
|
||||
|
@ -354,37 +363,23 @@ bool DomainServer::optionallySetupAssignmentPayment() {
|
|||
void DomainServer::setupAutomaticNetworking() {
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
const int STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS = 10 * 1000;
|
||||
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
|
||||
|
||||
// setup our timer to check our IP via stun every X seconds
|
||||
QTimer* dynamicIPTimer = new QTimer(this);
|
||||
connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN);
|
||||
|
||||
_automaticNetworkingSetting =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
|
||||
|
||||
if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
|
||||
dynamicIPTimer->start(STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS);
|
||||
|
||||
// setup a timer to heartbeat with the ice-server every so often
|
||||
QTimer* iceHeartbeatTimer = new QTimer(this);
|
||||
connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates);
|
||||
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
|
||||
|
||||
// call our sendHeartbeatToIceServer immediately anytime a local or public socket changes
|
||||
connect(nodeList.data(), &LimitedNodeList::localSockAddrChanged,
|
||||
this, &DomainServer::sendHeartbeatToIceServer);
|
||||
connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged,
|
||||
this, &DomainServer::sendHeartbeatToIceServer);
|
||||
|
||||
// attempt to update our public socket now, this will send a heartbeat once we get public socket
|
||||
requestCurrentPublicSocketViaSTUN();
|
||||
|
||||
// in case the STUN lookup is still happening we should re-request a public socket once we get that address
|
||||
connect(&nodeList->getSTUNSockAddr(), &HifiSockAddr::lookupCompleted,
|
||||
this, &DomainServer::requestCurrentPublicSocketViaSTUN);
|
||||
// we need this DS to know what our public IP is - start trying to figure that out now
|
||||
nodeList->startSTUNPublicSocketUpdate();
|
||||
|
||||
// setup a timer to heartbeat with the ice-server every so often
|
||||
QTimer* iceHeartbeatTimer = new QTimer(this);
|
||||
connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToIceServer);
|
||||
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
|
||||
}
|
||||
|
||||
if (!didSetupAccountManagerWithAccessToken()) {
|
||||
|
@ -404,14 +399,12 @@ void DomainServer::setupAutomaticNetworking() {
|
|||
<< uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
|
||||
|
||||
if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
|
||||
dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS);
|
||||
|
||||
// send public socket changes to the data server so nodes can find us at our new IP
|
||||
// send any public socket changes to the data server so nodes can find us at our new IP
|
||||
connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged,
|
||||
this, &DomainServer::performIPAddressUpdate);
|
||||
|
||||
// attempt to update our sockets now
|
||||
requestCurrentPublicSocketViaSTUN();
|
||||
// have the LNL enable public socket updating via STUN
|
||||
nodeList->startSTUNPublicSocketUpdate();
|
||||
} else {
|
||||
// send our heartbeat to data server so it knows what our network settings are
|
||||
sendHeartbeatToDataServer();
|
||||
|
@ -574,7 +567,6 @@ const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
|||
<< NodeType::AvatarMixer << NodeType::EntityServer;
|
||||
|
||||
void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
|
||||
|
||||
NodeType_t nodeType;
|
||||
HifiSockAddr publicSockAddr, localSockAddr;
|
||||
|
||||
|
@ -640,9 +632,17 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
|
||||
QUuid nodeUUID;
|
||||
|
||||
if (_connectingICEPeers.contains(packetUUID) || _connectedICEPeers.contains(packetUUID)) {
|
||||
HifiSockAddr discoveredSocket = senderSockAddr;
|
||||
SharedNetworkPeer connectedPeer = _icePeers.value(packetUUID);
|
||||
|
||||
if (connectedPeer) {
|
||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||
nodeUUID = packetUUID;
|
||||
|
||||
if (connectedPeer->getActiveSocket()) {
|
||||
// set their discovered socket to whatever the activated socket on the network peer object was
|
||||
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||
}
|
||||
} else {
|
||||
// we got a packetUUID we didn't recognize, just add the node
|
||||
nodeUUID = QUuid::createUuid();
|
||||
|
@ -670,6 +670,10 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeType,
|
||||
publicSockAddr, localSockAddr,
|
||||
canAdjustLocks, canRez);
|
||||
|
||||
// So that we can send messages to this node at will - we need to activate the correct socket on this node now
|
||||
newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket);
|
||||
|
||||
// when the newNode is created the linked data is also created
|
||||
// if this was a static assignment set the UUID, set the sendingSockAddr
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||
|
@ -699,10 +703,12 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
|
||||
// reply back to the user with a PacketTypeDomainList
|
||||
sendDomainListToNode(newNode, senderSockAddr, nodeInterestList.toSet());
|
||||
|
||||
// send out this node to our other connected nodes
|
||||
broadcastNewNode(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int DomainServer::countConnectedUsers() {
|
||||
unsigned int result = 0;
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
@ -715,9 +721,9 @@ unsigned int DomainServer::countConnectedUsers() {
|
|||
}
|
||||
|
||||
|
||||
bool DomainServer::verifyUsersKey (const QString& username,
|
||||
const QByteArray& usernameSignature,
|
||||
QString& reasonReturn) {
|
||||
bool DomainServer::verifyUsersKey(const QString& username,
|
||||
const QByteArray& usernameSignature,
|
||||
QString& reasonReturn) {
|
||||
// it's possible this user can be allowed to connect, but we need to check their username signature
|
||||
|
||||
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
||||
|
@ -912,26 +918,8 @@ int DomainServer::parseNodeDataFromByteArray(QDataStream& packetStream, NodeType
|
|||
return packetStream.device()->pos();
|
||||
}
|
||||
|
||||
NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numPreceedingBytes);
|
||||
|
||||
quint8 numInterestTypes = 0;
|
||||
packetStream >> numInterestTypes;
|
||||
|
||||
quint8 nodeType;
|
||||
NodeSet nodeInterestSet;
|
||||
|
||||
for (int i = 0; i < numInterestTypes; i++) {
|
||||
packetStream >> nodeType;
|
||||
nodeInterestSet.insert((NodeType_t) nodeType);
|
||||
}
|
||||
|
||||
return nodeInterestSet;
|
||||
}
|
||||
|
||||
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr,
|
||||
const NodeSet& nodeInterestList) {
|
||||
const NodeSet& nodeInterestSet) {
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
QByteArray broadcastPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainList);
|
||||
|
||||
|
@ -945,14 +933,10 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
// if we've established a connection via ICE with this peer, use that socket
|
||||
// otherwise just try to reply back to them on their sending socket (although that may not work)
|
||||
HifiSockAddr destinationSockAddr = _connectedICEPeers.value(node->getUUID());
|
||||
if (destinationSockAddr.isNull()) {
|
||||
destinationSockAddr = senderSockAddr;
|
||||
}
|
||||
// store the nodeInterestSet on this DomainServerNodeData, in case it has changed
|
||||
nodeData->setNodeInterestSet(nodeInterestSet);
|
||||
|
||||
if (nodeInterestList.size() > 0) {
|
||||
if (nodeInterestSet.size() > 0) {
|
||||
|
||||
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
||||
int dataMTU = MAX_PACKET_SIZE;
|
||||
|
@ -964,27 +948,13 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
QByteArray nodeByteArray;
|
||||
QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append);
|
||||
|
||||
if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) {
|
||||
if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) {
|
||||
|
||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||
nodeDataStream << *otherNode.data();
|
||||
|
||||
// pack the secret that these two nodes will use to communicate with each other
|
||||
QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID());
|
||||
if (secretUUID.isNull()) {
|
||||
// generate a new secret UUID these two nodes can use
|
||||
secretUUID = QUuid::createUuid();
|
||||
|
||||
// set that on the current Node's sessionSecretHash
|
||||
nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID);
|
||||
|
||||
// set it on the other Node's sessionSecretHash
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())
|
||||
->getSessionSecretHash().insert(node->getUUID(), secretUUID);
|
||||
|
||||
}
|
||||
|
||||
nodeDataStream << secretUUID;
|
||||
nodeDataStream << connectionSecretForNodes(node, otherNode);
|
||||
|
||||
if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) {
|
||||
// we need to break here and start a new packet
|
||||
|
@ -1005,7 +975,62 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
}
|
||||
|
||||
// always write the last broadcastPacket
|
||||
limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr);
|
||||
limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node);
|
||||
}
|
||||
|
||||
QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
|
||||
DomainServerNodeData* nodeAData = dynamic_cast<DomainServerNodeData*>(nodeA->getLinkedData());
|
||||
DomainServerNodeData* nodeBData = dynamic_cast<DomainServerNodeData*>(nodeB->getLinkedData());
|
||||
|
||||
if (nodeAData && nodeBData) {
|
||||
QUuid& secretUUID = nodeAData->getSessionSecretHash()[nodeB->getUUID()];
|
||||
|
||||
if (secretUUID.isNull()) {
|
||||
// generate a new secret UUID these two nodes can use
|
||||
secretUUID = QUuid::createUuid();
|
||||
|
||||
// set it on the other Node's sessionSecretHash
|
||||
reinterpret_cast<DomainServerNodeData*>(nodeBData)->getSessionSecretHash().insert(nodeA->getUUID(), secretUUID);
|
||||
}
|
||||
|
||||
return secretUUID;
|
||||
}
|
||||
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
// setup the add packet for this new node
|
||||
QByteArray addNodePacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerAddedNode);
|
||||
QDataStream addNodeStream(&addNodePacket, QIODevice::Append);
|
||||
|
||||
addNodeStream << *addedNode.data();
|
||||
|
||||
int connectionSecretIndex = addNodePacket.size();
|
||||
|
||||
limitedNodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
if (node->getLinkedData() && node->getActiveSocket() && node != addedNode) {
|
||||
// is the added Node in this node's interest list?
|
||||
DomainServerNodeData* nodeData = dynamic_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
return nodeData->getNodeInterestSet().contains(addedNode->getType());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122();
|
||||
|
||||
// replace the bytes at the end of the packet for the connection secret between these nodes
|
||||
addNodePacket.replace(connectionSecretIndex, NUM_BYTES_RFC4122_UUID, rfcConnectionSecret);
|
||||
|
||||
// send off this packet to the node
|
||||
limitedNodeList->writeUnverifiedDatagram(addNodePacket, node);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void DomainServer::readAvailableDatagrams() {
|
||||
|
@ -1216,10 +1241,6 @@ void DomainServer::transactionJSONCallback(const QJsonObject& data) {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestCurrentPublicSocketViaSTUN() {
|
||||
DependencyManager::get<LimitedNodeList>()->sendSTUNRequest();
|
||||
}
|
||||
|
||||
QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) {
|
||||
const QString SOCKET_NETWORK_ADDRESS_KEY = "network_address";
|
||||
const QString SOCKET_PORT_KEY = "port";
|
||||
|
@ -1284,82 +1305,84 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) {
|
|||
domainUpdateJSON.toUtf8());
|
||||
}
|
||||
|
||||
// todo: have data-web respond with ice-server hostname to use
|
||||
|
||||
void DomainServer::performICEUpdates() {
|
||||
sendHeartbeatToIceServer();
|
||||
sendICEPingPackets();
|
||||
}
|
||||
// TODO: have data-web respond with ice-server hostname to use
|
||||
|
||||
void DomainServer::sendHeartbeatToIceServer() {
|
||||
DependencyManager::get<LimitedNodeList>()->sendHeartbeatToIceServer(_iceServerSocket);
|
||||
}
|
||||
|
||||
void DomainServer::sendICEPingPackets() {
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
const int NUM_PEER_PINGS_BEFORE_DELETE = 2000 / UDP_PUNCH_PING_INTERVAL_MS;
|
||||
|
||||
QHash<QUuid, NetworkPeer>::iterator peer = _connectingICEPeers.begin();
|
||||
void DomainServer::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) {
|
||||
|
||||
while (peer != _connectingICEPeers.end()) {
|
||||
if (peer->getConnectionAttempts() > 0 && peer->getConnectionAttempts() % NUM_PEER_PINGS_BEFORE_DELETE == 0) {
|
||||
// we've reached the maximum number of ping attempts
|
||||
qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID();
|
||||
qDebug() << "Removing from list of connecting peers.";
|
||||
|
||||
if (peer->getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {
|
||||
// we've already tried to connect to this peer enough times
|
||||
// remove it from our list - if it wants to re-connect it'll come back through ice-server
|
||||
peer = _connectingICEPeers.erase(peer);
|
||||
} else {
|
||||
// send ping packets to this peer's interfaces
|
||||
qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID"
|
||||
<< peer->getUUID();
|
||||
_icePeers.remove(peer->getUUID());
|
||||
} else {
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
QByteArray localPingPacket = nodeList->constructPingPacket(PingType::Local, false);
|
||||
nodeList->writeUnverifiedDatagram(localPingPacket, peer->getLocalSocket());
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
QByteArray localPingPacket = nodeList->constructPingPacket(PingType::Local, false);
|
||||
nodeList->writeUnverifiedDatagram(localPingPacket, peer->getLocalSocket());
|
||||
|
||||
QByteArray publicPingPacket = nodeList->constructPingPacket(PingType::Public, false);
|
||||
nodeList->writeUnverifiedDatagram(publicPingPacket, peer->getPublicSocket());
|
||||
QByteArray publicPingPacket = nodeList->constructPingPacket(PingType::Public, false);
|
||||
nodeList->writeUnverifiedDatagram(publicPingPacket, peer->getPublicSocket());
|
||||
|
||||
peer->incrementConnectionAttempts();
|
||||
peer->incrementConnectionAttempts();
|
||||
}
|
||||
}
|
||||
|
||||
// go to next peer in hash
|
||||
++peer;
|
||||
void DomainServer::handlePeerPingTimeout() {
|
||||
NetworkPeer* senderPeer = qobject_cast<NetworkPeer*>(sender());
|
||||
|
||||
if (senderPeer) {
|
||||
SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID());
|
||||
|
||||
if (sharedPeer && !sharedPeer->getActiveSocket()) {
|
||||
pingPunchForConnectingPeer(sharedPeer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processICEHeartbeatResponse(const QByteArray& packet) {
|
||||
void DomainServer::processICEPeerInformation(const QByteArray& packet) {
|
||||
// loop through the packet and pull out network peers
|
||||
// any peer we don't have we add to the hash, otherwise we update
|
||||
QDataStream iceResponseStream(packet);
|
||||
iceResponseStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
NetworkPeer receivedPeer;
|
||||
NetworkPeer* receivedPeer = new NetworkPeer;
|
||||
iceResponseStream >> *receivedPeer;
|
||||
|
||||
while (!iceResponseStream.atEnd()) {
|
||||
iceResponseStream >> receivedPeer;
|
||||
if (!_icePeers.contains(receivedPeer->getUUID())) {
|
||||
qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer;
|
||||
SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer);
|
||||
_icePeers[receivedPeer->getUUID()] = newPeer;
|
||||
|
||||
if (!_connectedICEPeers.contains(receivedPeer.getUUID())) {
|
||||
if (!_connectingICEPeers.contains(receivedPeer.getUUID())) {
|
||||
qDebug() << "New peer requesting connection being added to hash -" << receivedPeer;
|
||||
}
|
||||
// make sure we know when we should ping this peer
|
||||
connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainServer::handlePeerPingTimeout);
|
||||
|
||||
_connectingICEPeers[receivedPeer.getUUID()] = receivedPeer;
|
||||
}
|
||||
// immediately ping the new peer, and start a timer to continue pinging it until we connect to it
|
||||
newPeer->startPingTimer();
|
||||
|
||||
qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID"
|
||||
<< newPeer->getUUID();
|
||||
|
||||
pingPunchForConnectingPeer(newPeer);
|
||||
} else {
|
||||
delete receivedPeer;
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(packet);
|
||||
NetworkPeer sendingPeer = _connectingICEPeers.take(nodeUUID);
|
||||
SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID);
|
||||
|
||||
if (!sendingPeer.isNull()) {
|
||||
if (sendingPeer) {
|
||||
// we had this NetworkPeer in our connecting list - add the right sock addr to our connected list
|
||||
if (senderSockAddr == sendingPeer.getLocalSocket()) {
|
||||
qDebug() << "Activating local socket for communication with network peer -" << sendingPeer;
|
||||
_connectedICEPeers.insert(nodeUUID, sendingPeer.getLocalSocket());
|
||||
} else if (senderSockAddr == sendingPeer.getPublicSocket()) {
|
||||
qDebug() << "Activating public socket for communication with network peer -" << sendingPeer;
|
||||
_connectedICEPeers.insert(nodeUUID, sendingPeer.getPublicSocket());
|
||||
}
|
||||
sendingPeer->activateMatchingOrNewSymmetricSocket(senderSockAddr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1427,8 +1450,8 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS
|
|||
processICEPingReply(receivedPacket, senderSockAddr);
|
||||
break;
|
||||
}
|
||||
case PacketTypeIceServerHeartbeatResponse:
|
||||
processICEHeartbeatResponse(receivedPacket);
|
||||
case PacketTypeIceServerPeerInformation:
|
||||
processICEPeerInformation(receivedPacket);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -2087,9 +2110,8 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
|
|||
|
||||
void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||
|
||||
// remove this node from the connecting / connected ICE lists (if they exist)
|
||||
_connectingICEPeers.remove(node->getUUID());
|
||||
_connectedICEPeers.remove(node->getUUID());
|
||||
// if this peer connected via ICE then remove them from our ICE peers hash
|
||||
_icePeers.remove(node->getUUID());
|
||||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
|
|
|
@ -57,17 +57,17 @@ public slots:
|
|||
void restart();
|
||||
|
||||
private slots:
|
||||
void aboutToQuit();
|
||||
|
||||
void loginFailed();
|
||||
void readAvailableDatagrams();
|
||||
void setupPendingAssignmentCredits();
|
||||
void sendPendingTransactionsToServer();
|
||||
|
||||
void requestCurrentPublicSocketViaSTUN();
|
||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||
void performICEUpdates();
|
||||
void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); }
|
||||
void sendHeartbeatToIceServer();
|
||||
void sendICEPingPackets();
|
||||
void handlePeerPingTimeout();
|
||||
private:
|
||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||
bool optionallySetupOAuth();
|
||||
|
@ -80,7 +80,9 @@ private:
|
|||
void setupAutomaticNetworking();
|
||||
void sendHeartbeatToDataServer(const QString& networkAddress);
|
||||
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||
void processICEHeartbeatResponse(const QByteArray& packet);
|
||||
void processICEPeerInformation(const QByteArray& packet);
|
||||
|
||||
void pingPunchForConnectingPeer(const SharedNetworkPeer& peer);
|
||||
|
||||
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
|
@ -98,9 +100,11 @@ private:
|
|||
HifiSockAddr& publicSockAddr,
|
||||
HifiSockAddr& localSockAddr,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
|
||||
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
|
||||
const NodeSet& nodeInterestList);
|
||||
const NodeSet& nodeInterestSet);
|
||||
|
||||
QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB);
|
||||
void broadcastNewNode(const SharedNodePointer& node);
|
||||
|
||||
void parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes);
|
||||
void addStaticAssignmentToAssignmentHash(Assignment* newAssignment);
|
||||
|
@ -151,8 +155,7 @@ private:
|
|||
|
||||
QHash<QString, QByteArray> _userPublicKeys;
|
||||
|
||||
QHash<QUuid, NetworkPeer> _connectingICEPeers;
|
||||
QHash<QUuid, HifiSockAddr> _connectedICEPeers;
|
||||
QHash<QUuid, SharedNetworkPeer> _icePeers;
|
||||
|
||||
QString _automaticNetworkingSetting;
|
||||
|
||||
|
|
|
@ -12,44 +12,47 @@
|
|||
#ifndef hifi_DomainServerNodeData_h
|
||||
#define hifi_DomainServerNodeData_h
|
||||
|
||||
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <HifiSockAddr.h>
|
||||
#include <NodeData.h>
|
||||
#include <NodeType.h>
|
||||
|
||||
class DomainServerNodeData : public NodeData {
|
||||
public:
|
||||
DomainServerNodeData();
|
||||
int parseData(const QByteArray& packet) { return 0; }
|
||||
|
||||
|
||||
const QJsonObject& getStatsJSONObject() const { return _statsJSONObject; }
|
||||
|
||||
|
||||
void parseJSONStatsPacket(const QByteArray& statsPacket);
|
||||
|
||||
|
||||
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
||||
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
||||
|
||||
|
||||
void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; }
|
||||
const QUuid& getWalletUUID() const { return _walletUUID; }
|
||||
|
||||
|
||||
void setUsername(const QString& username) { _username = username; }
|
||||
const QString& getUsername() const { return _username; }
|
||||
|
||||
|
||||
QElapsedTimer& getPaymentIntervalTimer() { return _paymentIntervalTimer; }
|
||||
|
||||
|
||||
void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; }
|
||||
const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; }
|
||||
|
||||
|
||||
void setIsAuthenticated(bool isAuthenticated) { _isAuthenticated = isAuthenticated; }
|
||||
bool isAuthenticated() const { return _isAuthenticated; }
|
||||
|
||||
|
||||
QHash<QUuid, QUuid>& getSessionSecretHash() { return _sessionSecretHash; }
|
||||
|
||||
const NodeSet& getNodeInterestSet() const { return _nodeInterestSet; }
|
||||
void setNodeInterestSet(const NodeSet& nodeInterestSet) { _nodeInterestSet = nodeInterestSet; }
|
||||
private:
|
||||
QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject);
|
||||
|
||||
|
||||
QHash<QUuid, QUuid> _sessionSecretHash;
|
||||
QUuid _assignmentUUID;
|
||||
QUuid _walletUUID;
|
||||
|
@ -58,6 +61,7 @@ private:
|
|||
QJsonObject _statsJSONObject;
|
||||
HifiSockAddr _sendingSockAddr;
|
||||
bool _isAuthenticated;
|
||||
NodeSet _nodeInterestSet;
|
||||
};
|
||||
|
||||
#endif // hifi_DomainServerNodeData_h
|
||||
|
|
|
@ -26,18 +26,16 @@ int main(int argc, char* argv[]) {
|
|||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
||||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||
|
||||
|
||||
int currentExitCode = 0;
|
||||
|
||||
|
||||
// use a do-while to handle domain-server restart
|
||||
do {
|
||||
DomainServer domainServer(argc, argv);
|
||||
currentExitCode = domainServer.exec();
|
||||
} while (currentExitCode == DomainServer::EXIT_CODE_REBOOT);
|
||||
|
||||
|
||||
|
||||
|
||||
return currentExitCode;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,133 +33,121 @@ IceServer::IceServer(int argc, char* argv[]) :
|
|||
qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT;
|
||||
qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT;
|
||||
_serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
|
||||
|
||||
|
||||
// call our process datagrams slot when the UDP socket has packets ready
|
||||
connect(&_serverSocket, &QUdpSocket::readyRead, this, &IceServer::processDatagrams);
|
||||
|
||||
|
||||
// setup our timer to clear inactive peers
|
||||
QTimer* inactivePeerTimer = new QTimer(this);
|
||||
connect(inactivePeerTimer, &QTimer::timeout, this, &IceServer::clearInactivePeers);
|
||||
inactivePeerTimer->start(CLEAR_INACTIVE_PEERS_INTERVAL_MSECS);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void IceServer::processDatagrams() {
|
||||
HifiSockAddr sendingSockAddr;
|
||||
QByteArray incomingPacket;
|
||||
|
||||
|
||||
while (_serverSocket.hasPendingDatagrams()) {
|
||||
incomingPacket.resize(_serverSocket.pendingDatagramSize());
|
||||
|
||||
|
||||
_serverSocket.readDatagram(incomingPacket.data(), incomingPacket.size(),
|
||||
sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer());
|
||||
|
||||
|
||||
if (packetTypeForPacket(incomingPacket) == PacketTypeIceServerHeartbeat) {
|
||||
|
||||
PacketType packetType = packetTypeForPacket(incomingPacket);
|
||||
|
||||
if (packetType == PacketTypeIceServerHeartbeat) {
|
||||
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(incomingPacket);
|
||||
|
||||
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
|
||||
peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr);
|
||||
} else if (packetType == PacketTypeIceServerQuery) {
|
||||
|
||||
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
|
||||
QUuid senderUUID = uuidFromPacketHeader(incomingPacket);
|
||||
|
||||
|
||||
// pull the public and private sock addrs for this peer
|
||||
HifiSockAddr publicSocket, localSocket;
|
||||
|
||||
|
||||
QDataStream hearbeatStream(incomingPacket);
|
||||
hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket));
|
||||
|
||||
|
||||
hearbeatStream >> publicSocket >> localSocket;
|
||||
|
||||
// make sure we have this sender in our peer hash
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID);
|
||||
|
||||
if (!matchingPeer) {
|
||||
// if we don't have this sender we need to create them now
|
||||
matchingPeer = SharedNetworkPeer(new NetworkPeer(senderUUID, publicSocket, localSocket));
|
||||
_activePeers.insert(senderUUID, matchingPeer);
|
||||
|
||||
qDebug() << "Added a new network peer" << *matchingPeer;
|
||||
} else {
|
||||
// we already had the peer so just potentially update their sockets
|
||||
matchingPeer->setPublicSocket(publicSocket);
|
||||
matchingPeer->setLocalSocket(localSocket);
|
||||
|
||||
qDebug() << "Matched hearbeat to existing network peer" << *matchingPeer;
|
||||
}
|
||||
|
||||
// update our last heard microstamp for this network peer to now
|
||||
matchingPeer->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
|
||||
// check if this node also included a UUID that they would like to connect to
|
||||
QUuid connectRequestID;
|
||||
hearbeatStream >> connectRequestID;
|
||||
|
||||
// get the peers asking for connections with this peer
|
||||
QSet<QUuid>& requestingConnections = _currentConnections[senderUUID];
|
||||
|
||||
if (!connectRequestID.isNull()) {
|
||||
qDebug() << "Peer wants to connect to peer with ID" << uuidStringWithoutCurlyBraces(connectRequestID);
|
||||
|
||||
// ensure this peer is in the set of current connections for the peer with ID it wants to connect with
|
||||
_currentConnections[connectRequestID].insert(senderUUID);
|
||||
|
||||
// add the ID of the node they have said they would like to connect to
|
||||
requestingConnections.insert(connectRequestID);
|
||||
}
|
||||
|
||||
if (requestingConnections.size() > 0) {
|
||||
// send a heartbeart response based on the set of connections
|
||||
qDebug() << "Sending a heartbeat response to" << senderUUID << "who has" << requestingConnections.size()
|
||||
<< "potential connections";
|
||||
sendHeartbeatResponse(sendingSockAddr, requestingConnections);
|
||||
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
|
||||
|
||||
if (matchingPeer) {
|
||||
// we have the peer they want to connect to - send them pack the information for that peer
|
||||
sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr);
|
||||
|
||||
// we also need to send them to the active peer they are hoping to connect to
|
||||
// create a dummy peer object we can pass to sendPeerInformationPacket
|
||||
|
||||
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
|
||||
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IceServer::sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, QSet<QUuid>& connections) {
|
||||
QSet<QUuid>::iterator peerID = connections.begin();
|
||||
|
||||
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(const QByteArray& incomingPacket) {
|
||||
QUuid senderUUID = uuidFromPacketHeader(incomingPacket);
|
||||
|
||||
// pull the public and private sock addrs for this peer
|
||||
HifiSockAddr publicSocket, localSocket;
|
||||
|
||||
QDataStream hearbeatStream(incomingPacket);
|
||||
hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket));
|
||||
|
||||
hearbeatStream >> publicSocket >> localSocket;
|
||||
|
||||
// make sure we have this sender in our peer hash
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID);
|
||||
|
||||
if (!matchingPeer) {
|
||||
// if we don't have this sender we need to create them now
|
||||
matchingPeer = SharedNetworkPeer(new NetworkPeer(senderUUID, publicSocket, localSocket));
|
||||
_activePeers.insert(senderUUID, matchingPeer);
|
||||
|
||||
qDebug() << "Added a new network peer" << *matchingPeer;
|
||||
} else {
|
||||
// we already had the peer so just potentially update their sockets
|
||||
matchingPeer->setPublicSocket(publicSocket);
|
||||
matchingPeer->setLocalSocket(localSocket);
|
||||
}
|
||||
|
||||
// update our last heard microstamp for this network peer to now
|
||||
matchingPeer->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
return matchingPeer;
|
||||
}
|
||||
|
||||
void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) {
|
||||
QByteArray outgoingPacket(MAX_PACKET_SIZE, 0);
|
||||
int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerPeerInformation, _id);
|
||||
int numHeaderBytes = currentPacketSize;
|
||||
|
||||
// go through the connections, sending packets containing connection information for those nodes
|
||||
while (peerID != connections.end()) {
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(*peerID);
|
||||
// if this node is inactive we remove it from the set
|
||||
if (!matchingPeer) {
|
||||
peerID = connections.erase(peerID);
|
||||
} else {
|
||||
// get the byte array for this peer
|
||||
QByteArray peerBytes = matchingPeer->toByteArray();
|
||||
|
||||
if (currentPacketSize + peerBytes.size() > MAX_PACKET_SIZE) {
|
||||
// write the current packet
|
||||
_serverSocket.writeDatagram(outgoingPacket.data(), currentPacketSize,
|
||||
destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
|
||||
// reset the packet size to our number of header bytes
|
||||
currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
}
|
||||
|
||||
// append the current peer bytes
|
||||
outgoingPacket.insert(currentPacketSize, peerBytes);
|
||||
currentPacketSize += peerBytes.size();
|
||||
|
||||
++peerID;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPacketSize > numHeaderBytes) {
|
||||
// write the last packet, if there is data in it
|
||||
_serverSocket.writeDatagram(outgoingPacket.data(), currentPacketSize,
|
||||
destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
}
|
||||
|
||||
// get the byte array for this peer
|
||||
QByteArray peerBytes = peer.toByteArray();
|
||||
outgoingPacket.replace(numHeaderBytes, peerBytes.size(), peerBytes);
|
||||
|
||||
currentPacketSize += peerBytes.size();
|
||||
|
||||
// write the current packet
|
||||
_serverSocket.writeDatagram(outgoingPacket.data(), outgoingPacket.size(),
|
||||
destinationSockAddr->getAddress(), destinationSockAddr->getPort());
|
||||
}
|
||||
|
||||
void IceServer::clearInactivePeers() {
|
||||
NetworkPeerHash::iterator peerItem = _activePeers.begin();
|
||||
|
||||
|
||||
while (peerItem != _activePeers.end()) {
|
||||
SharedNetworkPeer peer = peerItem.value();
|
||||
|
||||
|
||||
if ((usecTimestampNow() - peer->getLastHeardMicrostamp()) > (PEER_SILENCE_THRESHOLD_MSECS * 1000)) {
|
||||
qDebug() << "Removing peer from memory for inactivity -" << *peer;
|
||||
peerItem = _activePeers.erase(peerItem);
|
||||
|
@ -171,11 +159,9 @@ void IceServer::clearInactivePeers() {
|
|||
}
|
||||
|
||||
bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {
|
||||
//
|
||||
// We need an HTTP handler in order to monitor the health of the ice server
|
||||
// The correct functioning of the ICE server will be determined by its HTTP availability,
|
||||
//
|
||||
|
||||
|
||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
|
||||
if (url.path() == "/status") {
|
||||
connection->respond(HTTPConnection::StatusCode200, QByteArray::number(_activePeers.size()));
|
||||
|
|
|
@ -31,14 +31,14 @@ private slots:
|
|||
void processDatagrams();
|
||||
void clearInactivePeers();
|
||||
private:
|
||||
|
||||
void sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, QSet<QUuid>& connections);
|
||||
|
||||
|
||||
SharedNetworkPeer addOrUpdateHeartbeatingPeer(const QByteArray& incomingPacket);
|
||||
void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr);
|
||||
|
||||
QUuid _id;
|
||||
QUdpSocket _serverSocket;
|
||||
NetworkPeerHash _activePeers;
|
||||
QHash<QUuid, QSet<QUuid> > _currentConnections;
|
||||
HTTPManager _httpManager;
|
||||
};
|
||||
|
||||
#endif // hifi_IceServer_h
|
||||
#endif // hifi_IceServer_h
|
||||
|
|
|
@ -71,14 +71,14 @@ Menu::Menu() {
|
|||
|
||||
{
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Login);
|
||||
|
||||
|
||||
// connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item
|
||||
connect(&accountManager, &AccountManager::profileChanged,
|
||||
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
|
||||
connect(&accountManager, &AccountManager::logoutComplete,
|
||||
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
|
||||
}
|
||||
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O,
|
||||
qApp, SLOT(loadDialog()));
|
||||
|
@ -92,7 +92,7 @@ Menu::Menu() {
|
|||
|
||||
addDisabledActionAndSeparator(fileMenu, "Location");
|
||||
qApp->getBookmarks()->setupMenus(this, fileMenu);
|
||||
|
||||
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::AddressBar,
|
||||
Qt::CTRL | Qt::Key_L,
|
||||
|
@ -148,8 +148,8 @@ Menu::Menu() {
|
|||
SLOT(setEnabled(bool)));
|
||||
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
|
||||
#endif
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
|
||||
0, // QML Qt::Key_Backslash,
|
||||
dialogsManager.data(), SLOT(showIRCLink()));
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0,
|
||||
|
@ -175,7 +175,7 @@ Menu::Menu() {
|
|||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToNoOne);
|
||||
|
||||
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
|
||||
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
|
||||
discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged);
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ Menu::Menu() {
|
|||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp,
|
||||
SLOT(resetSensors()));
|
||||
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()));
|
||||
|
||||
|
@ -245,17 +245,17 @@ Menu::Menu() {
|
|||
qApp,
|
||||
SLOT(setFullscreen(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson,
|
||||
0, // QML Qt::Key_P,
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson,
|
||||
0, // QML Qt::Key_P,
|
||||
true, qApp, SLOT(cameraMenuChanged()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
|
||||
0, //QML Qt::SHIFT | Qt::Key_H,
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
|
||||
0, //QML Qt::SHIFT | Qt::Key_H,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
|
||||
0, // QML Qt::Key_H,
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
|
||||
0, // QML Qt::Key_H,
|
||||
false, qApp, SLOT(cameraMenuChanged()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
|
||||
#ifdef Q_OS_MAC
|
||||
Qt::META | Qt::Key_H,
|
||||
#else
|
||||
|
@ -285,8 +285,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()));
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()));
|
||||
|
@ -297,8 +297,8 @@ Menu::Menu() {
|
|||
MenuWrapper* developerMenu = addMenu("Developer");
|
||||
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
|
||||
0, // QML Qt::SHIFT | Qt::Key_A,
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
|
||||
0, // QML Qt::SHIFT | Qt::Key_A,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges);
|
||||
|
@ -317,7 +317,7 @@ Menu::Menu() {
|
|||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight7, 0, false));
|
||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
|
||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
|
||||
|
||||
|
||||
MenuWrapper* shadowMenu = renderOptionsMenu->addMenu("Shadows");
|
||||
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
|
||||
|
@ -351,14 +351,14 @@ Menu::Menu() {
|
|||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
|
||||
0, // QML Qt::Key_Asterisk,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true,
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true,
|
||||
DependencyManager::get<GlowEffect>().data(), SLOT(toggleGlowEffect(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||
0, // QML Qt::SHIFT | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(lodTools()));
|
||||
|
||||
|
@ -384,7 +384,7 @@ Menu::Menu() {
|
|||
faceTrackerGroup->addAction(faceshiftFaceTracker);
|
||||
#endif
|
||||
#ifdef HAVE_DDE
|
||||
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera,
|
||||
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera,
|
||||
0, true,
|
||||
qApp, SLOT(setActiveFaceTracker()));
|
||||
faceTrackerGroup->addAction(ddeFaceTracker);
|
||||
|
@ -404,13 +404,13 @@ Menu::Menu() {
|
|||
#endif
|
||||
#if defined(HAVE_FACESHIFT) || defined(HAVE_DDE)
|
||||
faceTrackingMenu->addSeparator();
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking,
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_F, true, // DDE face tracking is on by default
|
||||
qApp, SLOT(toggleFaceTrackerMute()));
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, true);
|
||||
#endif
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
|
||||
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
|
||||
|
||||
|
@ -426,7 +426,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
||||
|
||||
|
||||
MenuWrapper* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
|
||||
#ifdef __APPLE__
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||
|
@ -472,7 +472,11 @@ Menu::Menu() {
|
|||
addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0,
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0,
|
||||
dialogsManager.data(), SLOT(showDomainConnectionDialog()));
|
||||
|
||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing and Stats");
|
||||
|
||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
||||
|
@ -510,7 +514,7 @@ Menu::Menu() {
|
|||
0,
|
||||
audioIO.data(),
|
||||
SLOT(sendMuteEnvironmentPacket()));
|
||||
|
||||
|
||||
auto scope = DependencyManager::get<AudioScope>();
|
||||
|
||||
MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
|
||||
|
@ -548,7 +552,7 @@ Menu::Menu() {
|
|||
audioScopeFramesGroup->addAction(twentyFrames);
|
||||
audioScopeFramesGroup->addAction(fiftyFrames);
|
||||
}
|
||||
|
||||
|
||||
auto statsRenderer = DependencyManager::get<AudioIOStatsRenderer>();
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStats,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_A,
|
||||
|
@ -767,7 +771,7 @@ QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) {
|
|||
} else {
|
||||
menuActions = actions();
|
||||
}
|
||||
|
||||
|
||||
foreach (QAction* menuAction, menuActions) {
|
||||
QString actionText = menuAction->text();
|
||||
if (menuName == menuAction->text()) {
|
||||
|
@ -868,14 +872,14 @@ MenuWrapper* Menu::addMenu(const QString& menuName) {
|
|||
}
|
||||
addTo = menu;
|
||||
}
|
||||
|
||||
|
||||
QMenuBar::repaint();
|
||||
return menu;
|
||||
}
|
||||
|
||||
void Menu::removeMenu(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
QString finalMenuPart;
|
||||
|
@ -885,14 +889,14 @@ void Menu::removeMenu(const QString& menuName) {
|
|||
} else {
|
||||
QMenuBar::removeAction(action);
|
||||
}
|
||||
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
}
|
||||
|
||||
bool Menu::menuExists(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
return true;
|
||||
|
@ -937,7 +941,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
|
|||
if (!properties.shortcutKeySequence.isEmpty()) {
|
||||
shortcut = new QShortcut(properties.shortcutKeySequence, this);
|
||||
}
|
||||
|
||||
|
||||
// check for positioning requests
|
||||
int requestedPosition = properties.position;
|
||||
if (requestedPosition == UNSPECIFIED_POSITION && !properties.beforeItem.isEmpty()) {
|
||||
|
@ -951,7 +955,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
|
|||
requestedPosition = afterPosition + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QAction* menuItemAction = NULL;
|
||||
if (properties.isSeparator) {
|
||||
addDisabledActionAndSeparator(menuObj, properties.menuItemName, requestedPosition);
|
||||
|
|
|
@ -58,15 +58,15 @@ class Menu : public QMenuBar {
|
|||
Q_OBJECT
|
||||
public:
|
||||
static Menu* getInstance();
|
||||
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
|
||||
MenuWrapper* getMenu(const QString& menuName);
|
||||
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
|
@ -80,9 +80,9 @@ public:
|
|||
const QKeySequence& shortcut = 0,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
|
||||
void removeAction(MenuWrapper* menu, const QString& actionName);
|
||||
|
||||
|
||||
public slots:
|
||||
MenuWrapper* addMenu(const QString& menuName);
|
||||
void removeMenu(const QString& menuName);
|
||||
|
@ -94,21 +94,21 @@ public slots:
|
|||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
bool isOptionChecked(const QString& menuOption) const;
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
|
||||
private:
|
||||
static Menu* _instance;
|
||||
Menu();
|
||||
|
||||
|
||||
typedef void(*settingsAction)(Settings&, QAction&);
|
||||
static void loadAction(Settings& settings, QAction& action);
|
||||
static void saveAction(Settings& settings, QAction& action);
|
||||
void scanMenuBar(settingsAction modifySetting);
|
||||
void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings);
|
||||
|
||||
|
||||
/// helper method to have separators with labels that are also compatible with OS X
|
||||
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
|
||||
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
|
@ -116,15 +116,15 @@ private:
|
|||
const QObject* receiver = NULL,
|
||||
const char* member = NULL,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
|
||||
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
|
||||
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
|
||||
MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart);
|
||||
|
||||
|
||||
QAction* getMenuAction(const QString& menuName);
|
||||
int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem);
|
||||
int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition);
|
||||
|
||||
|
||||
QHash<QString, QAction*> _actionHash;
|
||||
};
|
||||
|
||||
|
@ -262,6 +262,7 @@ namespace MenuOption {
|
|||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptEditor = "Script Editor...";
|
||||
const QString ScriptedMotorControl = "Enable Scripted Motor Control";
|
||||
const QString ShowDSConnectTable = "Show Domain Connection Timing";
|
||||
const QString ShowBordersEntityNodes = "Show Entity Nodes";
|
||||
const QString ShowIKConstraints = "Show IK Constraints";
|
||||
const QString SimpleShadows = "Simple";
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "BandwidthDialog.h"
|
||||
#include "CachesSizeDialog.h"
|
||||
#include "DiskCacheEditor.h"
|
||||
#include "DomainConnectionDialog.h"
|
||||
#include "HMDToolsDialog.h"
|
||||
#include "LodToolsDialog.h"
|
||||
#include "LoginDialog.h"
|
||||
|
@ -52,7 +53,7 @@ void DialogsManager::showLoginDialog() {
|
|||
void DialogsManager::octreeStatsDetails() {
|
||||
if (!_octreeStatsDialog) {
|
||||
_octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats());
|
||||
|
||||
|
||||
if (_hmdToolsDialog) {
|
||||
_hmdToolsDialog->watchWindow(_octreeStatsDialog->windowHandle());
|
||||
}
|
||||
|
@ -65,7 +66,7 @@ void DialogsManager::octreeStatsDetails() {
|
|||
void DialogsManager::cachesSizeDialog() {
|
||||
if (!_cachesSizeDialog) {
|
||||
maybeCreateDialog(_cachesSizeDialog);
|
||||
|
||||
|
||||
connect(_cachesSizeDialog, SIGNAL(closed()), _cachesSizeDialog, SLOT(deleteLater()));
|
||||
_cachesSizeDialog->show();
|
||||
}
|
||||
|
@ -112,11 +113,11 @@ void DialogsManager::bandwidthDetails() {
|
|||
if (! _bandwidthDialog) {
|
||||
_bandwidthDialog = new BandwidthDialog(qApp->getWindow());
|
||||
connect(_bandwidthDialog, SIGNAL(closed()), _bandwidthDialog, SLOT(deleteLater()));
|
||||
|
||||
|
||||
if (_hmdToolsDialog) {
|
||||
_hmdToolsDialog->watchWindow(_bandwidthDialog->windowHandle());
|
||||
}
|
||||
|
||||
|
||||
_bandwidthDialog->show();
|
||||
}
|
||||
_bandwidthDialog->raise();
|
||||
|
@ -125,7 +126,7 @@ void DialogsManager::bandwidthDetails() {
|
|||
void DialogsManager::lodTools() {
|
||||
if (!_lodToolsDialog) {
|
||||
maybeCreateDialog(_lodToolsDialog);
|
||||
|
||||
|
||||
connect(_lodToolsDialog, SIGNAL(closed()), _lodToolsDialog, SLOT(deleteLater()));
|
||||
_lodToolsDialog->show();
|
||||
}
|
||||
|
@ -172,7 +173,20 @@ void DialogsManager::showIRCLink() {
|
|||
_ircInfoBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
_ircInfoBox->show();
|
||||
}
|
||||
|
||||
|
||||
_ircInfoBox->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::showDomainConnectionDialog() {
|
||||
// if the dialog already exists we delete it so the connection data is refreshed
|
||||
if (_domainConnectionDialog) {
|
||||
_domainConnectionDialog->close();
|
||||
_domainConnectionDialog->deleteLater();
|
||||
_domainConnectionDialog = NULL;
|
||||
}
|
||||
|
||||
maybeCreateDialog(_domainConnectionDialog);
|
||||
|
||||
_domainConnectionDialog->show();
|
||||
_domainConnectionDialog->raise();
|
||||
}
|
||||
|
|
|
@ -34,18 +34,19 @@ class PreferencesDialog;
|
|||
class ScriptEditorWindow;
|
||||
class QMessageBox;
|
||||
class AvatarAppearanceDialog;
|
||||
class DomainConnectionDialog;
|
||||
|
||||
class DialogsManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
|
||||
public:
|
||||
QPointer<BandwidthDialog> getBandwidthDialog() const { return _bandwidthDialog; }
|
||||
QPointer<HMDToolsDialog> getHMDToolsDialog() const { return _hmdToolsDialog; }
|
||||
QPointer<LodToolsDialog> getLodToolsDialog() const { return _lodToolsDialog; }
|
||||
QPointer<OctreeStatsDialog> getOctreeStatsDialog() const { return _octreeStatsDialog; }
|
||||
QPointer<PreferencesDialog> getPreferencesDialog() const { return _preferencesDialog; }
|
||||
|
||||
|
||||
public slots:
|
||||
void toggleAddressBar();
|
||||
void toggleDiskCacheEditor();
|
||||
|
@ -62,14 +63,15 @@ public slots:
|
|||
void showScriptEditor();
|
||||
void showIRCLink();
|
||||
void changeAvatarAppearance();
|
||||
void showDomainConnectionDialog();
|
||||
|
||||
private slots:
|
||||
void toggleToolWindow();
|
||||
void hmdToolsClosed();
|
||||
|
||||
|
||||
private:
|
||||
DialogsManager() {}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void maybeCreateDialog(QPointer<T>& member) {
|
||||
if (!member) {
|
||||
|
@ -77,13 +79,13 @@ private:
|
|||
Q_CHECK_PTR(parent);
|
||||
member = new T(parent);
|
||||
Q_CHECK_PTR(member);
|
||||
|
||||
|
||||
if (_hmdToolsDialog && member->windowHandle()) {
|
||||
_hmdToolsDialog->watchWindow(member->windowHandle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QPointer<AddressBarDialog> _addressBarDialog;
|
||||
QPointer<AnimationsDialog> _animationsDialog;
|
||||
QPointer<AttachmentsDialog> _attachmentsDialog;
|
||||
|
@ -98,6 +100,7 @@ private:
|
|||
QPointer<PreferencesDialog> _preferencesDialog;
|
||||
QPointer<ScriptEditorWindow> _scriptEditor;
|
||||
QPointer<AvatarAppearanceDialog> _avatarAppearanceDialog;
|
||||
QPointer<DomainConnectionDialog> _domainConnectionDialog;
|
||||
};
|
||||
|
||||
#endif // hifi_DialogsManager_h
|
||||
#endif // hifi_DialogsManager_h
|
||||
|
|
96
interface/src/ui/DomainConnectionDialog.cpp
Normal file
96
interface/src/ui/DomainConnectionDialog.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// DomainConnectionDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stephen Birarda on 05/26/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QMetaEnum>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QHeaderView>
|
||||
#include <QtWidgets/QTableWidget>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "DomainConnectionDialog.h"
|
||||
|
||||
DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) :
|
||||
QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint)
|
||||
{
|
||||
setWindowTitle("Domain Connection Timing");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
// setup a QTableWidget so we can populate it with our values
|
||||
QTableWidget* timeTable = new QTableWidget;
|
||||
|
||||
timeTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
|
||||
const QStringList TABLE_HEADERS = QStringList() << "Name" << "Timestamp (ms)" << "Delta (ms)" << "Time elapsed (ms)";
|
||||
|
||||
timeTable->setColumnCount(TABLE_HEADERS.size());
|
||||
|
||||
// ask the NodeList for the current values for connection times
|
||||
QMap<quint64, LimitedNodeList::ConnectionStep> times = DependencyManager::get<NodeList>()->getLastConnectionTimes();
|
||||
|
||||
timeTable->setRowCount(times.size());
|
||||
|
||||
timeTable->setHorizontalHeaderLabels(TABLE_HEADERS);
|
||||
|
||||
// setup our data with the values from the NodeList
|
||||
quint64 firstStepTime = times.firstKey() / USECS_PER_MSEC;
|
||||
quint64 lastStepTime = firstStepTime;
|
||||
|
||||
int thisRow = 0;
|
||||
|
||||
const QMetaObject &nodeListMeta = NodeList::staticMetaObject;
|
||||
QMetaEnum stepEnum = nodeListMeta.enumerator(nodeListMeta.indexOfEnumerator("ConnectionStep"));
|
||||
|
||||
foreach(quint64 timestamp, times.keys()) {
|
||||
// When did this step occur, how long since the last step, how long since the start?
|
||||
quint64 stepTime = timestamp / USECS_PER_MSEC;
|
||||
quint64 delta = (stepTime - lastStepTime);
|
||||
quint64 elapsed = stepTime - firstStepTime;
|
||||
|
||||
lastStepTime = stepTime;
|
||||
|
||||
// setup the columns for this row in the table
|
||||
int stepIndex = (int) times.value(timestamp);
|
||||
|
||||
timeTable->setItem(thisRow, 0, new QTableWidgetItem(stepEnum.valueToKey(stepIndex)));
|
||||
timeTable->setItem(thisRow, 1, new QTableWidgetItem(QString::number(stepTime)));
|
||||
timeTable->setItem(thisRow, 2, new QTableWidgetItem(QString::number(delta)));
|
||||
timeTable->setItem(thisRow, 3, new QTableWidgetItem(QString::number(elapsed)));
|
||||
|
||||
++thisRow;
|
||||
}
|
||||
|
||||
// setup a horizontal box layout
|
||||
QHBoxLayout* hBoxLayout = new QHBoxLayout;
|
||||
hBoxLayout->addWidget(timeTable);
|
||||
hBoxLayout->setSizeConstraint(QLayout::SetMinimumSize);
|
||||
|
||||
// resize the table columns
|
||||
timeTable->resizeColumnsToContents();
|
||||
|
||||
// figure out the size of the table so we can size the dialog
|
||||
int tableWidth = timeTable->verticalHeader()->sizeHint().width();
|
||||
for (int i = 0; i < timeTable->columnCount(); i++) {
|
||||
tableWidth += timeTable->columnWidth(i);
|
||||
}
|
||||
|
||||
int tableHeight = timeTable->horizontalHeader()->sizeHint().height();
|
||||
for (int i = 0; i < timeTable->rowCount(); i++) {
|
||||
tableHeight += timeTable->rowHeight(i);
|
||||
}
|
||||
|
||||
// set the minimum size of the table to whatever we got
|
||||
timeTable->setMinimumSize(tableWidth, tableHeight);
|
||||
|
||||
setLayout(hBoxLayout);
|
||||
adjustSize();
|
||||
}
|
25
interface/src/ui/DomainConnectionDialog.h
Normal file
25
interface/src/ui/DomainConnectionDialog.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// DomainConnectionDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stephen Birarda on 05/26/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DomainConnectionDialog_h
|
||||
#define hifi_DomainConnectionDialog_h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
class DomainConnectionDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DomainConnectionDialog(QWidget* parent);
|
||||
};
|
||||
|
||||
#endif // hifi_DomainConnectionDialog_h
|
|
@ -116,7 +116,7 @@ AudioClient::AudioClient() :
|
|||
{
|
||||
// clear the array of locally injected samples
|
||||
memset(_localProceduralSamples, 0, AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL);
|
||||
|
||||
|
||||
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
|
||||
this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
|
||||
|
||||
|
@ -127,7 +127,7 @@ AudioClient::AudioClient() :
|
|||
QTimer* updateTimer = new QTimer(this);
|
||||
connect(updateTimer, &QTimer::timeout, this, &AudioClient::checkDevices);
|
||||
updateTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
|
||||
|
||||
|
||||
// create GVerb filter
|
||||
_gverb = createGverbFilter();
|
||||
configureGverbFilter(_gverb);
|
||||
|
@ -135,7 +135,7 @@ AudioClient::AudioClient() :
|
|||
|
||||
AudioClient::~AudioClient() {
|
||||
stop();
|
||||
|
||||
|
||||
if (_gverb) {
|
||||
gverb_free(_gverb);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ void AudioClient::reset() {
|
|||
_toneSource.reset();
|
||||
_sourceGain.reset();
|
||||
_inputGain.reset();
|
||||
|
||||
|
||||
gverb_flush(_gverb);
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ int numDestinationSamplesRequired(const QAudioFormat& sourceFormat, const QAudio
|
|||
int numSourceSamples) {
|
||||
float ratio = (float) destinationFormat.channelCount() / sourceFormat.channelCount();
|
||||
ratio *= (float) destinationFormat.sampleRate() / sourceFormat.sampleRate();
|
||||
|
||||
|
||||
return (numSourceSamples * ratio) + 0.5f;
|
||||
}
|
||||
|
||||
|
@ -287,7 +287,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
|||
}
|
||||
|
||||
qCDebug(audioclient) << "DEBUG [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
|
||||
|
||||
|
||||
return getNamedAudioDeviceForMode(mode, deviceName);
|
||||
#endif
|
||||
|
||||
|
@ -302,7 +302,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
|||
if (!audioDevice.isFormatSupported(desiredAudioFormat)) {
|
||||
qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat;
|
||||
qCDebug(audioclient, "The desired audio format is not supported by this device");
|
||||
|
||||
|
||||
if (desiredAudioFormat.channelCount() == 1) {
|
||||
adjustedAudioFormat = desiredAudioFormat;
|
||||
adjustedAudioFormat.setChannelCount(2);
|
||||
|
@ -313,17 +313,17 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
|||
adjustedAudioFormat.setChannelCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int FORTY_FOUR = 44100;
|
||||
|
||||
|
||||
adjustedAudioFormat = desiredAudioFormat;
|
||||
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
adjustedAudioFormat.setSampleRate(FORTY_FOUR);
|
||||
#else
|
||||
|
||||
|
||||
const int HALF_FORTY_FOUR = FORTY_FOUR / 2;
|
||||
|
||||
|
||||
if (audioDevice.supportedSampleRates().contains(AudioConstants::SAMPLE_RATE * 2)) {
|
||||
// use 48, which is a sample downsample, upsample
|
||||
adjustedAudioFormat.setSampleRate(AudioConstants::SAMPLE_RATE * 2);
|
||||
|
@ -335,7 +335,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
|||
adjustedAudioFormat.setSampleRate(FORTY_FOUR);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (adjustedAudioFormat != desiredAudioFormat) {
|
||||
// return the nearest in case it needs 2 channels
|
||||
adjustedAudioFormat = audioDevice.nearestFormat(adjustedAudioFormat);
|
||||
|
@ -357,18 +357,18 @@ bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationS
|
|||
for (uint i = 0; i < numSourceSamples; i += 2) {
|
||||
destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 2);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
} else if (sourceAudioFormat.channelCount() == 1 && destinationAudioFormat.channelCount() == 2) {
|
||||
|
||||
|
||||
// loop through the mono input audio and repeat each sample twice
|
||||
for (uint i = 0; i < numSourceSamples; ++i) {
|
||||
destinationSamples[i * 2] = destinationSamples[(i * 2) + 1] = sourceSamples[i];
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -376,7 +376,7 @@ soxr_error_t possibleResampling(soxr_t resampler,
|
|||
const int16_t* sourceSamples, int16_t* destinationSamples,
|
||||
unsigned int numSourceSamples, unsigned int numDestinationSamples,
|
||||
const QAudioFormat& sourceAudioFormat, const QAudioFormat& destinationAudioFormat) {
|
||||
|
||||
|
||||
if (numSourceSamples > 0) {
|
||||
if (!resampler) {
|
||||
if (!sampleChannelConversion(sourceSamples, destinationSamples, numSourceSamples,
|
||||
|
@ -384,41 +384,41 @@ soxr_error_t possibleResampling(soxr_t resampler,
|
|||
// no conversion, we can copy the samples directly across
|
||||
memcpy(destinationSamples, sourceSamples, numSourceSamples * sizeof(int16_t));
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
soxr_error_t resampleError = 0;
|
||||
|
||||
|
||||
if (sourceAudioFormat.channelCount() != destinationAudioFormat.channelCount()) {
|
||||
float channelCountRatio = (float) destinationAudioFormat.channelCount() / sourceAudioFormat.channelCount();
|
||||
|
||||
|
||||
int numChannelCoversionSamples = (int) (numSourceSamples * channelCountRatio);
|
||||
int16_t* channelConversionSamples = new int16_t[numChannelCoversionSamples];
|
||||
|
||||
|
||||
sampleChannelConversion(sourceSamples, channelConversionSamples,
|
||||
numSourceSamples,
|
||||
sourceAudioFormat, destinationAudioFormat);
|
||||
|
||||
|
||||
resampleError = soxr_process(resampler,
|
||||
channelConversionSamples, numChannelCoversionSamples, NULL,
|
||||
destinationSamples, numDestinationSamples, NULL);
|
||||
|
||||
|
||||
delete[] channelConversionSamples;
|
||||
} else {
|
||||
|
||||
|
||||
unsigned int numAdjustedSourceSamples = numSourceSamples;
|
||||
unsigned int numAdjustedDestinationSamples = numDestinationSamples;
|
||||
|
||||
|
||||
if (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2) {
|
||||
numAdjustedSourceSamples /= 2;
|
||||
numAdjustedDestinationSamples /= 2;
|
||||
}
|
||||
|
||||
|
||||
resampleError = soxr_process(resampler,
|
||||
sourceSamples, numAdjustedSourceSamples, NULL,
|
||||
destinationSamples, numAdjustedDestinationSamples, NULL);
|
||||
}
|
||||
|
||||
|
||||
return resampleError;
|
||||
}
|
||||
} else {
|
||||
|
@ -429,30 +429,30 @@ soxr_error_t possibleResampling(soxr_t resampler,
|
|||
soxr_t soxrResamplerFromInputFormatToOutputFormat(const QAudioFormat& sourceAudioFormat,
|
||||
const QAudioFormat& destinationAudioFormat) {
|
||||
soxr_error_t soxrError;
|
||||
|
||||
|
||||
// setup soxr_io_spec_t for input and output
|
||||
soxr_io_spec_t inputToNetworkSpec = soxr_io_spec(soxrDataTypeFromQAudioFormat(sourceAudioFormat),
|
||||
soxrDataTypeFromQAudioFormat(destinationAudioFormat));
|
||||
|
||||
|
||||
// setup soxr_quality_spec_t for quality options
|
||||
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
|
||||
|
||||
|
||||
int channelCount = (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2)
|
||||
? 2 : 1;
|
||||
|
||||
|
||||
soxr_t newResampler = soxr_create(sourceAudioFormat.sampleRate(),
|
||||
destinationAudioFormat.sampleRate(),
|
||||
channelCount,
|
||||
&soxrError, &inputToNetworkSpec, &qualitySpec, 0);
|
||||
|
||||
|
||||
if (soxrError) {
|
||||
qCDebug(audioclient) << "There was an error setting up the soxr resampler -" << "soxr error code was " << soxrError;
|
||||
|
||||
|
||||
soxr_delete(newResampler);
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
return newResampler;
|
||||
}
|
||||
|
||||
|
@ -476,7 +476,7 @@ void AudioClient::start() {
|
|||
QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);
|
||||
qCDebug(audioclient) << "The default audio output device is" << outputDeviceInfo.deviceName();
|
||||
bool outputFormatSupported = switchOutputToAudioDevice(outputDeviceInfo);
|
||||
|
||||
|
||||
if (!inputFormatSupported) {
|
||||
qCDebug(audioclient) << "Unable to set up audio input because of a problem with input format.";
|
||||
qCDebug(audioclient) << "The closest format available is" << inputDeviceInfo.nearestFormat(_desiredInputFormat);
|
||||
|
@ -503,11 +503,11 @@ void AudioClient::stop() {
|
|||
_sourceGain.finalize();
|
||||
_noiseSource.finalize();
|
||||
_toneSource.finalize();
|
||||
|
||||
|
||||
// "switch" to invalid devices in order to shut down the state
|
||||
switchInputToAudioDevice(QAudioDeviceInfo());
|
||||
switchOutputToAudioDevice(QAudioDeviceInfo());
|
||||
|
||||
|
||||
if (_loopbackResampler) {
|
||||
soxr_delete(_loopbackResampler);
|
||||
_loopbackResampler = NULL;
|
||||
|
@ -543,7 +543,7 @@ ty_gverb* AudioClient::createGverbFilter() {
|
|||
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
|
||||
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
|
||||
_reverbOptions->getTailLevel());
|
||||
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
@ -560,7 +560,7 @@ void AudioClient::configureGverbFilter(ty_gverb* filter) {
|
|||
void AudioClient::updateGverbOptions() {
|
||||
bool reverbChanged = false;
|
||||
if (_receivedAudioStream.hasReverb()) {
|
||||
|
||||
|
||||
if (_zoneReverbOptions.getReverbTime() != _receivedAudioStream.getRevebTime()) {
|
||||
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
|
||||
reverbChanged = true;
|
||||
|
@ -569,7 +569,7 @@ void AudioClient::updateGverbOptions() {
|
|||
_zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
|
||||
// Not part of actual filter config, no need to set reverbChanged to true
|
||||
}
|
||||
|
||||
|
||||
if (_reverbOptions != &_zoneReverbOptions) {
|
||||
_reverbOptions = &_zoneReverbOptions;
|
||||
reverbChanged = true;
|
||||
|
@ -578,7 +578,7 @@ void AudioClient::updateGverbOptions() {
|
|||
_reverbOptions = &_scriptReverbOptions;
|
||||
reverbChanged = true;
|
||||
}
|
||||
|
||||
|
||||
if (reverbChanged) {
|
||||
gverb_free(_gverb);
|
||||
_gverb = createGverbFilter();
|
||||
|
@ -588,7 +588,7 @@ void AudioClient::updateGverbOptions() {
|
|||
|
||||
void AudioClient::setReverb(bool reverb) {
|
||||
_reverb = reverb;
|
||||
|
||||
|
||||
if (!_reverb) {
|
||||
gverb_flush(_gverb);
|
||||
}
|
||||
|
@ -620,7 +620,7 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reve
|
|||
QAudioFormat& audioFormat, bool noEcho) {
|
||||
float wetFraction = DB_CO(_reverbOptions->getWetLevel());
|
||||
float dryFraction = 1.0f - wetFraction;
|
||||
|
||||
|
||||
float lValue,rValue;
|
||||
for (int sample = 0; sample < numSamples; sample += audioFormat.channelCount()) {
|
||||
// Run GVerb
|
||||
|
@ -634,7 +634,7 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reve
|
|||
int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction),
|
||||
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
|
||||
samplesData[j] = (int16_t)lResult;
|
||||
|
||||
|
||||
if (noEcho) {
|
||||
reverbAlone[j] = (int16_t)lValue * wetFraction;
|
||||
}
|
||||
|
@ -643,7 +643,7 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reve
|
|||
int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction),
|
||||
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
|
||||
samplesData[j] = (int16_t)rResult;
|
||||
|
||||
|
||||
if (noEcho) {
|
||||
reverbAlone[j] = (int16_t)rValue * wetFraction;
|
||||
}
|
||||
|
@ -660,53 +660,53 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
|||
if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if this person wants local loopback add that to the locally injected audio
|
||||
// if there is reverb apply it to local audio and substract the origin samples
|
||||
|
||||
|
||||
if (!_loopbackOutputDevice && _loopbackAudioOutput) {
|
||||
// we didn't have the loopback output device going so set that up now
|
||||
_loopbackOutputDevice = _loopbackAudioOutput->start();
|
||||
|
||||
|
||||
if (!_loopbackOutputDevice) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// do we need to setup a resampler?
|
||||
if (_inputFormat.sampleRate() != _outputFormat.sampleRate() && !_loopbackResampler) {
|
||||
qCDebug(audioclient) << "Attemping to create a resampler for input format to output format for audio loopback.";
|
||||
_loopbackResampler = soxrResamplerFromInputFormatToOutputFormat(_inputFormat, _outputFormat);
|
||||
|
||||
|
||||
if (!_loopbackResampler) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static QByteArray reverbAlone; // Intermediary for local reverb with no echo
|
||||
static QByteArray loopBackByteArray;
|
||||
|
||||
|
||||
int numInputSamples = inputByteArray.size() / sizeof(int16_t);
|
||||
int numLoopbackSamples = numDestinationSamplesRequired(_inputFormat, _outputFormat, numInputSamples);
|
||||
|
||||
|
||||
reverbAlone.resize(numInputSamples * sizeof(int16_t));
|
||||
loopBackByteArray.resize(numLoopbackSamples * sizeof(int16_t));
|
||||
|
||||
|
||||
int16_t* inputSamples = reinterpret_cast<int16_t*>(inputByteArray.data());
|
||||
int16_t* reverbAloneSamples = reinterpret_cast<int16_t*>(reverbAlone.data());
|
||||
int16_t* loopbackSamples = reinterpret_cast<int16_t*>(loopBackByteArray.data());
|
||||
|
||||
|
||||
if (hasReverb) {
|
||||
updateGverbOptions();
|
||||
addReverb(_gverb, inputSamples, reverbAloneSamples, numInputSamples,
|
||||
_inputFormat, !_shouldEchoLocally);
|
||||
}
|
||||
|
||||
|
||||
possibleResampling(_loopbackResampler,
|
||||
(_shouldEchoLocally) ? inputSamples : reverbAloneSamples, loopbackSamples,
|
||||
numInputSamples, numLoopbackSamples,
|
||||
_inputFormat, _outputFormat);
|
||||
|
||||
|
||||
_loopbackOutputDevice->write(loopBackByteArray);
|
||||
}
|
||||
|
||||
|
@ -726,7 +726,7 @@ void AudioClient::handleAudioInput() {
|
|||
int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio);
|
||||
|
||||
QByteArray inputByteArray = _inputDevice->readAll();
|
||||
|
||||
|
||||
// Add audio source injection if enabled
|
||||
if (!_muted && _audioSourceInjectEnabled) {
|
||||
int16_t* inputFrameData = (int16_t*)inputByteArray.data();
|
||||
|
@ -745,11 +745,11 @@ void AudioClient::handleAudioInput() {
|
|||
_sourceGain.render(_inputFrameBuffer); // post gain
|
||||
_inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/);
|
||||
}
|
||||
|
||||
|
||||
handleLocalEchoAndReverb(inputByteArray);
|
||||
|
||||
_inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size());
|
||||
|
||||
|
||||
float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC));
|
||||
_stats.updateInputMsecsRead(audioInputMsecsRead);
|
||||
|
||||
|
@ -763,50 +763,50 @@ void AudioClient::handleAudioInput() {
|
|||
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
||||
|
||||
if (!_muted) {
|
||||
|
||||
|
||||
// zero out the monoAudioSamples array and the locally injected audio
|
||||
memset(networkAudioSamples, 0, numNetworkBytes);
|
||||
|
||||
|
||||
// Increment the time since the last clip
|
||||
if (_timeSinceLastClip >= 0.0f) {
|
||||
_timeSinceLastClip += (float) numNetworkSamples / (float) AudioConstants::SAMPLE_RATE;
|
||||
}
|
||||
|
||||
|
||||
int16_t* inputAudioSamples = new int16_t[inputSamplesRequired];
|
||||
_inputRingBuffer.readSamples(inputAudioSamples, inputSamplesRequired);
|
||||
|
||||
|
||||
possibleResampling(_inputToNetworkResampler,
|
||||
inputAudioSamples, networkAudioSamples,
|
||||
inputSamplesRequired, numNetworkSamples,
|
||||
_inputFormat, _desiredInputFormat);
|
||||
|
||||
|
||||
delete[] inputAudioSamples;
|
||||
|
||||
|
||||
// only impose the noise gate and perform tone injection if we are sending mono audio
|
||||
if (!_isStereoInput && !_audioSourceInjectEnabled && _isNoiseGateEnabled) {
|
||||
_inputGate.gateSamples(networkAudioSamples, numNetworkSamples);
|
||||
|
||||
|
||||
// if we performed the noise gate we can get values from it instead of enumerating the samples again
|
||||
_lastInputLoudness = _inputGate.getLastLoudness();
|
||||
|
||||
|
||||
if (_inputGate.clippedInLastFrame()) {
|
||||
_timeSinceLastClip = 0.0f;
|
||||
}
|
||||
} else {
|
||||
float loudness = 0.0f;
|
||||
|
||||
|
||||
for (int i = 0; i < numNetworkSamples; i++) {
|
||||
int thisSample = std::abs(networkAudioSamples[i]);
|
||||
loudness += (float) thisSample;
|
||||
|
||||
|
||||
if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) {
|
||||
_timeSinceLastClip = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_lastInputLoudness = fabs(loudness / numNetworkSamples);
|
||||
}
|
||||
|
||||
|
||||
emit inputReceived(QByteArray(reinterpret_cast<const char*>(networkAudioSamples),
|
||||
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * sizeof(AudioConstants::AudioSample)));
|
||||
|
||||
|
@ -814,13 +814,13 @@ void AudioClient::handleAudioInput() {
|
|||
// our input loudness is 0, since we're muted
|
||||
_lastInputLoudness = 0;
|
||||
_timeSinceLastClip = 0.0f;
|
||||
|
||||
|
||||
_inputRingBuffer.shiftReadPosition(inputSamplesRequired);
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
|
||||
|
||||
if (audioMixer && audioMixer->getActiveSocket()) {
|
||||
glm::vec3 headPosition = _positionGetter();
|
||||
glm::quat headOrientation = _orientationGetter();
|
||||
|
@ -852,7 +852,7 @@ void AudioClient::handleAudioInput() {
|
|||
// memcpy the three float positions
|
||||
memcpy(currentPacketPtr, &headPosition, sizeof(headPosition));
|
||||
currentPacketPtr += (sizeof(headPosition));
|
||||
|
||||
|
||||
// memcpy our orientation
|
||||
memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation));
|
||||
currentPacketPtr += sizeof(headOrientation);
|
||||
|
@ -864,7 +864,7 @@ void AudioClient::handleAudioInput() {
|
|||
// memcpy the three float positions
|
||||
memcpy(currentPacketPtr, &headPosition, sizeof(headPosition));
|
||||
currentPacketPtr += (sizeof(headPosition));
|
||||
|
||||
|
||||
// memcpy our orientation
|
||||
memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation));
|
||||
currentPacketPtr += sizeof(headOrientation);
|
||||
|
@ -875,6 +875,8 @@ void AudioClient::handleAudioInput() {
|
|||
|
||||
_stats.sentPacket();
|
||||
|
||||
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket);
|
||||
|
||||
int packetBytes = currentPacketPtr - audioDataPacket;
|
||||
nodeList->writeDatagram(audioDataPacket, packetBytes, audioMixer);
|
||||
_outgoingAvatarAudioSequenceNumber++;
|
||||
|
@ -890,7 +892,7 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr
|
|||
outputBuffer.resize(numDeviceOutputSamples * sizeof(int16_t));
|
||||
|
||||
const int16_t* receivedSamples = reinterpret_cast<const int16_t*>(inputBuffer.data());
|
||||
|
||||
|
||||
// copy the packet from the RB to the output
|
||||
possibleResampling(_networkToOutputResampler, receivedSamples,
|
||||
reinterpret_cast<int16_t*>(outputBuffer.data()),
|
||||
|
@ -900,20 +902,20 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr
|
|||
|
||||
void AudioClient::sendMuteEnvironmentPacket() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
||||
QByteArray mutePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeMuteEnvironment);
|
||||
int headerSize = mutePacket.size();
|
||||
|
||||
|
||||
const float MUTE_RADIUS = 50;
|
||||
|
||||
|
||||
glm::vec3 currentSourcePosition = _positionGetter();
|
||||
mutePacket.resize(mutePacket.size() + sizeof(glm::vec3) + sizeof(float));
|
||||
memcpy(mutePacket.data() + headerSize, ¤tSourcePosition, sizeof(glm::vec3));
|
||||
memcpy(mutePacket.data() + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float));
|
||||
|
||||
|
||||
// grab our audio mixer from the NodeList, if it exists
|
||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
|
||||
|
||||
if (audioMixer) {
|
||||
// send off this mute packet
|
||||
nodeList->writeDatagram(mutePacket, audioMixer);
|
||||
|
@ -921,6 +923,8 @@ void AudioClient::sendMuteEnvironmentPacket() {
|
|||
}
|
||||
|
||||
void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) {
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
|
||||
|
||||
if (_audioOutput) {
|
||||
// Audio output must exist and be correctly set up if we're going to process received audio
|
||||
_receivedAudioStream.parseData(audioByteArray);
|
||||
|
@ -930,11 +934,11 @@ void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) {
|
|||
void AudioClient::parseAudioEnvironmentData(const QByteArray &packet) {
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||
const char* dataAt = packet.constData() + numBytesPacketHeader;
|
||||
|
||||
|
||||
char bitset;
|
||||
memcpy(&bitset, dataAt, sizeof(char));
|
||||
dataAt += sizeof(char);
|
||||
|
||||
|
||||
bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT);;
|
||||
if (hasReverb) {
|
||||
float reverbTime, wetLevel;
|
||||
|
@ -956,13 +960,13 @@ void AudioClient::toggleMute() {
|
|||
void AudioClient::setIsStereoInput(bool isStereoInput) {
|
||||
if (isStereoInput != _isStereoInput) {
|
||||
_isStereoInput = isStereoInput;
|
||||
|
||||
|
||||
if (_isStereoInput) {
|
||||
_desiredInputFormat.setChannelCount(2);
|
||||
} else {
|
||||
_desiredInputFormat.setChannelCount(1);
|
||||
}
|
||||
|
||||
|
||||
// change in channel count for desired input format, restart the input device
|
||||
switchInputToAudioDevice(_inputAudioDeviceName);
|
||||
}
|
||||
|
@ -976,7 +980,7 @@ void AudioClient::selectAudioSourcePinkNoise() {
|
|||
_noiseSourceEnabled = true;
|
||||
_toneSourceEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
void AudioClient::selectAudioSourceSine440() {
|
||||
_toneSourceEnabled = true;
|
||||
_noiseSourceEnabled = false;
|
||||
|
@ -986,24 +990,24 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) {
|
|||
if (injector->getLocalBuffer()) {
|
||||
QAudioFormat localFormat = _desiredOutputFormat;
|
||||
localFormat.setChannelCount(isStereo ? 2 : 1);
|
||||
|
||||
|
||||
QAudioOutput* localOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName),
|
||||
localFormat,
|
||||
injector->getLocalBuffer());
|
||||
|
||||
|
||||
// move the localOutput to the same thread as the local injector buffer
|
||||
localOutput->moveToThread(injector->getLocalBuffer()->thread());
|
||||
|
||||
|
||||
// have it be stopped when that local buffer is about to close
|
||||
connect(injector->getLocalBuffer(), &AudioInjectorLocalBuffer::bufferEmpty, localOutput, &QAudioOutput::stop);
|
||||
connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop);
|
||||
|
||||
|
||||
qCDebug(audioclient) << "Starting QAudioOutput for local injector" << localOutput;
|
||||
|
||||
|
||||
localOutput->start(injector->getLocalBuffer());
|
||||
return localOutput->state() == QAudio::ActiveState;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1015,7 +1019,7 @@ void AudioClient::outputFormatChanged() {
|
|||
|
||||
bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
|
||||
bool supportedFormat = false;
|
||||
|
||||
|
||||
// cleanup any previously initialized device
|
||||
if (_audioInput) {
|
||||
// The call to stop() causes _inputDevice to be destructed.
|
||||
|
@ -1030,7 +1034,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
|
|||
|
||||
_inputAudioDeviceName = "";
|
||||
}
|
||||
|
||||
|
||||
if (_inputToNetworkResampler) {
|
||||
// if we were using an input to network resampler, delete it here
|
||||
soxr_delete(_inputToNetworkResampler);
|
||||
|
@ -1040,36 +1044,36 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
|
|||
if (!inputDeviceInfo.isNull()) {
|
||||
qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available.";
|
||||
_inputAudioDeviceName = inputDeviceInfo.deviceName().trimmed();
|
||||
|
||||
|
||||
if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) {
|
||||
qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat;
|
||||
|
||||
|
||||
// we've got the best we can get for input
|
||||
// if required, setup a soxr resampler for this input to our desired network format
|
||||
if (_inputFormat != _desiredInputFormat
|
||||
&& _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) {
|
||||
qCDebug(audioclient) << "Attemping to create a soxr resampler for input format to network format.";
|
||||
_inputToNetworkResampler = soxrResamplerFromInputFormatToOutputFormat(_inputFormat, _desiredInputFormat);
|
||||
|
||||
|
||||
if (!_inputToNetworkResampler) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qCDebug(audioclient) << "No resampling required for audio input to match desired network format.";
|
||||
}
|
||||
|
||||
|
||||
// if the user wants stereo but this device can't provide then bail
|
||||
if (!_isStereoInput || _inputFormat.channelCount() == 2) {
|
||||
if (!_isStereoInput || _inputFormat.channelCount() == 2) {
|
||||
_audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this);
|
||||
_numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat);
|
||||
_audioInput->setBufferSize(_numInputCallbackBytes);
|
||||
|
||||
|
||||
// how do we want to handle input working, but output not working?
|
||||
int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes);
|
||||
_inputRingBuffer.resizeForFrameSize(numFrameSamples);
|
||||
|
||||
|
||||
_inputDevice = _audioInput->start();
|
||||
|
||||
|
||||
if (_inputDevice) {
|
||||
connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput()));
|
||||
supportedFormat = true;
|
||||
|
@ -1079,7 +1083,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return supportedFormat;
|
||||
}
|
||||
|
||||
|
@ -1117,7 +1121,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
// cleanup any previously initialized device
|
||||
if (_audioOutput) {
|
||||
_audioOutput->stop();
|
||||
|
||||
|
||||
delete _audioOutput;
|
||||
_audioOutput = NULL;
|
||||
|
||||
|
@ -1125,13 +1129,13 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
delete _loopbackAudioOutput;
|
||||
_loopbackAudioOutput = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (_networkToOutputResampler) {
|
||||
// if we were using an input to network resampler, delete it here
|
||||
soxr_delete(_networkToOutputResampler);
|
||||
_networkToOutputResampler = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (_loopbackResampler) {
|
||||
// if we were using an input to output resample, delete it here
|
||||
soxr_delete(_loopbackResampler);
|
||||
|
@ -1141,24 +1145,24 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
if (!outputDeviceInfo.isNull()) {
|
||||
qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available.";
|
||||
_outputAudioDeviceName = outputDeviceInfo.deviceName().trimmed();
|
||||
|
||||
|
||||
if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) {
|
||||
qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat;
|
||||
|
||||
|
||||
// we've got the best we can get for input
|
||||
// if required, setup a soxr resampler for this input to our desired network format
|
||||
if (_desiredOutputFormat != _outputFormat
|
||||
&& _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) {
|
||||
qCDebug(audioclient) << "Attemping to create a resampler for network format to output format.";
|
||||
_networkToOutputResampler = soxrResamplerFromInputFormatToOutputFormat(_desiredOutputFormat, _outputFormat);
|
||||
|
||||
|
||||
if (!_networkToOutputResampler) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qCDebug(audioclient) << "No resampling required for network output to match actual output format.";
|
||||
}
|
||||
|
||||
|
||||
outputFormatChanged();
|
||||
|
||||
// setup our general output device for audio-mixer audio
|
||||
|
@ -1168,20 +1172,20 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify);
|
||||
|
||||
qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / sizeof(int16_t) / (float)_outputFrameSize;
|
||||
|
||||
|
||||
_audioOutputIODevice.start();
|
||||
_audioOutput->start(&_audioOutputIODevice);
|
||||
|
||||
// setup a loopback audio output device
|
||||
_loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
||||
|
||||
|
||||
|
||||
_timeSinceLastReceived.start();
|
||||
|
||||
supportedFormat = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return supportedFormat;
|
||||
}
|
||||
|
||||
|
@ -1202,7 +1206,7 @@ void AudioClient::setOutputBufferSize(int numFrames) {
|
|||
|
||||
// The following constant is operating system dependent due to differences in
|
||||
// the way input audio is handled. The audio input buffer size is inversely
|
||||
// proportional to the accelerator ratio.
|
||||
// proportional to the accelerator ratio.
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 0.1f;
|
||||
|
@ -1226,7 +1230,7 @@ int AudioClient::calculateNumberOfInputCallbackBytes(const QAudioFormat& format)
|
|||
}
|
||||
|
||||
float AudioClient::calculateDeviceToNetworkInputRatio() const {
|
||||
float inputToNetworkInputRatio = (int)((_numInputCallbackBytes
|
||||
float inputToNetworkInputRatio = (int)((_numInputCallbackBytes
|
||||
* CALLBACK_ACCELERATOR_RATIO
|
||||
/ AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL) + 0.5f);
|
||||
|
||||
|
@ -1257,7 +1261,7 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
int samplesRequested = maxSize / sizeof(int16_t);
|
||||
int samplesPopped;
|
||||
int bytesWritten;
|
||||
|
||||
|
||||
if ((samplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) {
|
||||
AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput();
|
||||
lastPopOutput.readSamples((int16_t*)data, samplesPopped);
|
||||
|
|
|
@ -106,6 +106,8 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
|||
|
||||
qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString();
|
||||
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::LookupAddress);
|
||||
|
||||
// there are 4 possible lookup strings
|
||||
|
||||
// 1. global place name (name of domain or place) - example: sanfrancisco
|
||||
|
@ -202,6 +204,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
|
|||
const QString DOMAIN_NETWORK_PORT_KEY = "network_port";
|
||||
const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address";
|
||||
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
|
||||
|
||||
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
|
||||
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
|
||||
|
||||
|
@ -432,6 +436,8 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
|
|||
|
||||
qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port;
|
||||
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
|
||||
|
||||
emit possibleDomainChangeRequired(hostname, port);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,18 +31,19 @@ DomainHandler::DomainHandler(QObject* parent) :
|
|||
_iceDomainID(),
|
||||
_iceClientID(),
|
||||
_iceServerSockAddr(),
|
||||
_icePeer(),
|
||||
_icePeer(this),
|
||||
_isConnected(false),
|
||||
_settingsObject(),
|
||||
_failedSettingsRequests(0)
|
||||
{
|
||||
|
||||
// if we get a socket that make sure our NetworkPeer ping timer stops
|
||||
connect(this, &DomainHandler::completedSocketDiscovery, &_icePeer, &NetworkPeer::stopPingTimer);
|
||||
}
|
||||
|
||||
void DomainHandler::clearConnectionInfo() {
|
||||
_uuid = QUuid();
|
||||
|
||||
_icePeer = NetworkPeer();
|
||||
_icePeer.reset();
|
||||
|
||||
if (requiresICE()) {
|
||||
// if we connected to this domain with ICE, re-set the socket so we reconnect through the ice-server
|
||||
|
@ -84,6 +85,10 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos
|
|||
_sockAddr = sockAddr;
|
||||
}
|
||||
|
||||
if (!_sockAddr.isNull()) {
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket);
|
||||
}
|
||||
|
||||
// some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification
|
||||
_hostname = hostname;
|
||||
}
|
||||
|
@ -111,6 +116,8 @@ void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) {
|
|||
qCDebug(networking, "Looking up DS hostname %s.", _hostname.toLocal8Bit().constData());
|
||||
QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&)));
|
||||
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainHostname);
|
||||
|
||||
UserActivityLogger::getInstance().changedDomain(_hostname);
|
||||
emit hostnameChanged(_hostname);
|
||||
}
|
||||
|
@ -135,6 +142,18 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
|
|||
replaceableSockAddr->~HifiSockAddr();
|
||||
replaceableSockAddr = new (replaceableSockAddr) HifiSockAddr(iceServerHostname, ICE_SERVER_DEFAULT_PORT);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetICEServerHostname);
|
||||
|
||||
if (_iceServerSockAddr.getAddress().isNull()) {
|
||||
// connect to lookup completed for ice-server socket so we can request a heartbeat once hostname is looked up
|
||||
connect(&_iceServerSockAddr, &HifiSockAddr::lookupCompleted, this, &DomainHandler::completedIceServerHostnameLookup);
|
||||
} else {
|
||||
completedIceServerHostnameLookup();
|
||||
}
|
||||
|
||||
|
||||
// refresh our ICE client UUID to something new
|
||||
_iceClientID = QUuid::createUuid();
|
||||
|
||||
|
@ -143,12 +162,14 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
|
|||
}
|
||||
|
||||
void DomainHandler::activateICELocalSocket() {
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket);
|
||||
_sockAddr = _icePeer.getLocalSocket();
|
||||
_hostname = _sockAddr.getAddress().toString();
|
||||
emit completedSocketDiscovery();
|
||||
}
|
||||
|
||||
void DomainHandler::activateICEPublicSocket() {
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket);
|
||||
_sockAddr = _icePeer.getPublicSocket();
|
||||
_hostname = _sockAddr.getAddress().toString();
|
||||
emit completedSocketDiscovery();
|
||||
|
@ -159,6 +180,8 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
|
|||
if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
_sockAddr.setAddress(hostInfo.addresses()[i]);
|
||||
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket);
|
||||
|
||||
qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(),
|
||||
_sockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
|
||||
|
@ -172,6 +195,15 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
|
|||
qCDebug(networking, "Failed domain server lookup");
|
||||
}
|
||||
|
||||
void DomainHandler::completedIceServerHostnameLookup() {
|
||||
qDebug() << "ICE server socket is at" << _iceServerSockAddr;
|
||||
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetICEServerSocket);
|
||||
|
||||
// emit our signal so we can send a heartbeat to ice-server immediately
|
||||
emit iceSocketAndIDReceived();
|
||||
}
|
||||
|
||||
void DomainHandler::setIsConnected(bool isConnected) {
|
||||
if (_isConnected != isConnected) {
|
||||
_isConnected = isConnected;
|
||||
|
@ -267,15 +299,20 @@ void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) {
|
|||
QDataStream iceResponseStream(icePacket);
|
||||
iceResponseStream.skipRawData(numBytesForPacketHeader(icePacket));
|
||||
|
||||
NetworkPeer packetPeer;
|
||||
iceResponseStream >> packetPeer;
|
||||
iceResponseStream >> _icePeer;
|
||||
|
||||
if (packetPeer.getUUID() != _iceDomainID) {
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSPeerInformation);
|
||||
|
||||
if (_icePeer.getUUID() != _iceDomainID) {
|
||||
qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection.";
|
||||
_icePeer.reset();
|
||||
} else {
|
||||
qCDebug(networking) << "Received network peer object for domain -" << packetPeer;
|
||||
_icePeer = packetPeer;
|
||||
qCDebug(networking) << "Received network peer object for domain -" << _icePeer;
|
||||
|
||||
emit requestICEConnectionAttempt();
|
||||
// ask the peer object to start its ping timer
|
||||
_icePeer.startPingTimer();
|
||||
|
||||
// emit our signal so the NodeList knows to send a ping immediately
|
||||
emit icePeerSocketsReceived();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,9 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void completedHostnameLookup(const QHostInfo& hostInfo);
|
||||
void completedIceServerHostnameLookup();
|
||||
void settingsRequestFinished();
|
||||
|
||||
signals:
|
||||
void hostnameChanged(const QString& hostname);
|
||||
|
||||
|
@ -96,7 +98,8 @@ signals:
|
|||
void connectedToDomain(const QString& hostname);
|
||||
void disconnectedFromDomain();
|
||||
|
||||
void requestICEConnectionAttempt();
|
||||
void iceSocketAndIDReceived();
|
||||
void icePeerSocketsReceived();
|
||||
|
||||
void settingsReceived(const QJsonObject& domainSettingsObject);
|
||||
void settingsReceiveFail();
|
||||
|
|
|
@ -61,6 +61,8 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
|
|||
firstCall = false;
|
||||
}
|
||||
|
||||
qRegisterMetaType<ConnectionStep>("ConnectionStep");
|
||||
|
||||
_nodeSocket.bind(QHostAddress::AnyIPv4, socketListenPort);
|
||||
qCDebug(networking) << "NodeList socket is listening on" << _nodeSocket.localPort();
|
||||
|
||||
|
@ -470,7 +472,8 @@ void LimitedNodeList::handleNodeKill(const SharedNodePointer& node) {
|
|||
|
||||
SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||
bool canAdjustLocks, bool canRez) {
|
||||
bool canAdjustLocks, bool canRez,
|
||||
const QUuid& connectionSecret) {
|
||||
NodeHash::const_iterator it = _nodeHash.find(uuid);
|
||||
|
||||
if (it != _nodeHash.end()) {
|
||||
|
@ -480,11 +483,17 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
matchingNode->setLocalSocket(localSocket);
|
||||
matchingNode->setCanAdjustLocks(canAdjustLocks);
|
||||
matchingNode->setCanRez(canRez);
|
||||
matchingNode->setConnectionSecret(connectionSecret);
|
||||
|
||||
return matchingNode;
|
||||
} else {
|
||||
// we didn't have this node, so add them
|
||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks, canRez);
|
||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks, canRez, connectionSecret, this);
|
||||
|
||||
if (nodeType == NodeType::AudioMixer) {
|
||||
LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer);
|
||||
}
|
||||
|
||||
SharedNodePointer newNodePointer(newNode);
|
||||
|
||||
_nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer));
|
||||
|
@ -596,6 +605,22 @@ const int NUM_BYTES_STUN_HEADER = 20;
|
|||
|
||||
void LimitedNodeList::sendSTUNRequest() {
|
||||
|
||||
static quint64 lastTimeStamp = usecTimestampNow();
|
||||
lastTimeStamp = usecTimestampNow();
|
||||
|
||||
const int NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL = 10;
|
||||
|
||||
if (!_hasCompletedInitialSTUN) {
|
||||
qCDebug(networking) << "Sending intial stun request to" << STUN_SERVER_HOSTNAME;
|
||||
|
||||
if (_numInitialSTUNRequests > NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL) {
|
||||
// we're still trying to do our initial STUN we're over the fail threshold
|
||||
stopInitialSTUNUpdate(false);
|
||||
}
|
||||
|
||||
++_numInitialSTUNRequests;
|
||||
}
|
||||
|
||||
unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER];
|
||||
|
||||
int packetIndex = 0;
|
||||
|
@ -620,6 +645,8 @@ void LimitedNodeList::sendSTUNRequest() {
|
|||
QUuid randomUUID = QUuid::createUuid();
|
||||
memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES);
|
||||
|
||||
flagTimeForConnectionStep(ConnectionStep::SendSTUNRequest);
|
||||
|
||||
_nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket),
|
||||
_stunSockAddr.getAddress(), _stunSockAddr.getPort());
|
||||
}
|
||||
|
@ -652,8 +679,6 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
|
|||
const int NUM_BYTES_FAMILY_ALIGN = 1;
|
||||
const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8;
|
||||
|
||||
|
||||
|
||||
int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
|
||||
|
||||
uint8_t addressFamily = 0;
|
||||
|
@ -685,7 +710,14 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
|
|||
_publicSockAddr.getAddress().toString().toLocal8Bit().constData(),
|
||||
_publicSockAddr.getPort());
|
||||
|
||||
if (!_hasCompletedInitialSTUN) {
|
||||
// if we're here we have definitely completed our initial STUN sequence
|
||||
stopInitialSTUNUpdate(true);
|
||||
}
|
||||
|
||||
emit publicSockAddrChanged(_publicSockAddr);
|
||||
|
||||
flagTimeForConnectionStep(ConnectionStep::SetPublicSocketFromSTUN);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -707,6 +739,65 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void LimitedNodeList::startSTUNPublicSocketUpdate() {
|
||||
assert(!_initialSTUNTimer);
|
||||
|
||||
if (!_initialSTUNTimer) {
|
||||
// if we don't know the STUN IP yet we need to have ourselves be called once it is known
|
||||
if (_stunSockAddr.getAddress().isNull()) {
|
||||
connect(&_stunSockAddr, &HifiSockAddr::lookupCompleted, this, &LimitedNodeList::startSTUNPublicSocketUpdate);
|
||||
} else {
|
||||
// setup our initial STUN timer here so we can quickly find out our public IP address
|
||||
_initialSTUNTimer = new QTimer(this);
|
||||
|
||||
connect(_initialSTUNTimer.data(), &QTimer::timeout, this, &LimitedNodeList::sendSTUNRequest);
|
||||
|
||||
const int STUN_INITIAL_UPDATE_INTERVAL_MSECS = 250;
|
||||
_initialSTUNTimer->start(STUN_INITIAL_UPDATE_INTERVAL_MSECS);
|
||||
|
||||
// send an initial STUN request right away
|
||||
sendSTUNRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LimitedNodeList::stopInitialSTUNUpdate(bool success) {
|
||||
_hasCompletedInitialSTUN = true;
|
||||
|
||||
if (!success) {
|
||||
// if we're here this was the last failed STUN request
|
||||
// use our DS as our stun server
|
||||
qCDebug(networking, "Failed to lookup public address via STUN server at %s:%hu.",
|
||||
STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
|
||||
qCDebug(networking) << "LimitedNodeList public socket will be set with local port and null QHostAddress.";
|
||||
|
||||
// reset the public address and port to a null address
|
||||
_publicSockAddr = HifiSockAddr(QHostAddress(), _nodeSocket.localPort());
|
||||
|
||||
// we have changed the publicSockAddr, so emit our signal
|
||||
emit publicSockAddrChanged(_publicSockAddr);
|
||||
|
||||
flagTimeForConnectionStep(ConnectionStep::SetPublicSocketFromSTUN);
|
||||
}
|
||||
|
||||
assert(_initialSTUNTimer);
|
||||
|
||||
// stop our initial fast timer
|
||||
if (_initialSTUNTimer) {
|
||||
_initialSTUNTimer->stop();
|
||||
_initialSTUNTimer->deleteLater();
|
||||
}
|
||||
|
||||
// We now setup a timer here to fire every so often to check that our IP address has not changed.
|
||||
// Or, if we failed - if will check if we can eventually get a public socket
|
||||
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
|
||||
|
||||
QTimer* stunOccasionalTimer = new QTimer(this);
|
||||
connect(stunOccasionalTimer, &QTimer::timeout, this, &LimitedNodeList::sendSTUNRequest);
|
||||
|
||||
stunOccasionalTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS);
|
||||
}
|
||||
|
||||
void LimitedNodeList::updateLocalSockAddr() {
|
||||
HifiSockAddr newSockAddr(getLocalAddress(), _nodeSocket.localPort());
|
||||
if (newSockAddr != _localSockAddr) {
|
||||
|
@ -723,23 +814,30 @@ void LimitedNodeList::updateLocalSockAddr() {
|
|||
}
|
||||
}
|
||||
|
||||
void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr,
|
||||
QUuid headerID, const QUuid& connectionRequestID) {
|
||||
void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr) {
|
||||
sendPacketToIceServer(PacketTypeIceServerHeartbeat, iceServerSockAddr, _sessionUUID);
|
||||
}
|
||||
|
||||
if (headerID.isNull()) {
|
||||
headerID = _sessionUUID;
|
||||
}
|
||||
void LimitedNodeList::sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID,
|
||||
const QUuid& peerID) {
|
||||
sendPacketToIceServer(PacketTypeIceServerQuery, iceServerSockAddr, clientID, peerID);
|
||||
}
|
||||
|
||||
QByteArray iceRequestByteArray = byteArrayWithUUIDPopulatedHeader(PacketTypeIceServerHeartbeat, headerID);
|
||||
void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr,
|
||||
const QUuid& headerID, const QUuid& peerID) {
|
||||
|
||||
QByteArray iceRequestByteArray = byteArrayWithUUIDPopulatedHeader(packetType, headerID);
|
||||
QDataStream iceDataStream(&iceRequestByteArray, QIODevice::Append);
|
||||
|
||||
iceDataStream << _publicSockAddr << _localSockAddr;
|
||||
|
||||
if (!connectionRequestID.isNull()) {
|
||||
iceDataStream << connectionRequestID;
|
||||
if (packetType == PacketTypeIceServerQuery) {
|
||||
assert(!peerID.isNull());
|
||||
|
||||
iceDataStream << peerID;
|
||||
|
||||
qCDebug(networking) << "Sending packet to ICE server to request connection info for peer with ID"
|
||||
<< uuidStringWithoutCurlyBraces(connectionRequestID);
|
||||
<< uuidStringWithoutCurlyBraces(peerID);
|
||||
}
|
||||
|
||||
writeUnverifiedDatagram(iceRequestByteArray, iceServerSockAddr);
|
||||
|
@ -774,3 +872,41 @@ bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quin
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep) {
|
||||
QMetaObject::invokeMethod(this, "flagTimeForConnectionStep",
|
||||
Q_ARG(ConnectionStep, connectionStep),
|
||||
Q_ARG(quint64, usecTimestampNow()));
|
||||
}
|
||||
|
||||
void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp) {
|
||||
|
||||
if (connectionStep == ConnectionStep::LookupAddress) {
|
||||
QWriteLocker writeLock(&_connectionTimeLock);
|
||||
|
||||
// we clear the current times if the user just fired off a lookup
|
||||
_lastConnectionTimes.clear();
|
||||
_areConnectionTimesComplete = false;
|
||||
|
||||
_lastConnectionTimes[timestamp] = connectionStep;
|
||||
} else if (!_areConnectionTimesComplete) {
|
||||
QWriteLocker writeLock(&_connectionTimeLock);
|
||||
|
||||
|
||||
// anything > than sending the first DS check should not come before the DS check in, so we drop those
|
||||
// this handles the case where you lookup an address and get packets in the existing domain before changing domains
|
||||
if (connectionStep > LimitedNodeList::ConnectionStep::SendDSCheckIn
|
||||
&& (_lastConnectionTimes.key(ConnectionStep::SendDSCheckIn) == 0
|
||||
|| timestamp <= _lastConnectionTimes.key(ConnectionStep::SendDSCheckIn))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is no time for existing step add a timestamp on the first call for each ConnectionStep
|
||||
_lastConnectionTimes[timestamp] = connectionStep;
|
||||
|
||||
// if this is a received audio packet we consider our connection times complete
|
||||
if (connectionStep == ConnectionStep::ReceiveFirstAudioPacket) {
|
||||
_areConnectionTimesComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,13 +21,14 @@
|
|||
#include <unistd.h> // not on windows, not needed for mac or windows
|
||||
#endif
|
||||
|
||||
#include <qelapsedtimer.h>
|
||||
#include <qreadwritelock.h>
|
||||
#include <qset.h>
|
||||
#include <qsharedpointer.h>
|
||||
#include <QtNetwork/qudpsocket.h>
|
||||
#include <QtNetwork/qhostaddress.h>
|
||||
#include <QSharedMemory>
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QReadWriteLock>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QSharedMemory>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
#include <QtNetwork/QHostAddress>
|
||||
|
||||
#include <tbb/concurrent_unordered_map.h>
|
||||
|
||||
|
@ -59,8 +60,6 @@ const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username";
|
|||
|
||||
class HifiSockAddr;
|
||||
|
||||
typedef QSet<NodeType_t> NodeSet;
|
||||
|
||||
typedef QSharedPointer<Node> SharedNodePointer;
|
||||
Q_DECLARE_METATYPE(SharedNodePointer)
|
||||
|
||||
|
@ -79,8 +78,31 @@ namespace PingType {
|
|||
class LimitedNodeList : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
|
||||
enum ConnectionStep {
|
||||
LookupAddress = 1,
|
||||
HandleAddress,
|
||||
SendSTUNRequest,
|
||||
SetPublicSocketFromSTUN,
|
||||
SetICEServerHostname,
|
||||
SetICEServerSocket,
|
||||
SendICEServerQuery,
|
||||
ReceiveDSPeerInformation,
|
||||
SendPingsToDS,
|
||||
SetDomainHostname,
|
||||
SetDomainSocket,
|
||||
SendDSCheckIn,
|
||||
ReceiveDSList,
|
||||
AddedAudioMixer,
|
||||
SendAudioPing,
|
||||
SetAudioMixerSocket,
|
||||
SendAudioPacket,
|
||||
ReceiveFirstAudioPacket
|
||||
};
|
||||
|
||||
Q_ENUMS(ConnectionStep);
|
||||
|
||||
const QUuid& getSessionUUID() const { return _sessionUUID; }
|
||||
void setSessionUUID(const QUuid& sessionUUID);
|
||||
|
||||
|
@ -128,7 +150,10 @@ public:
|
|||
|
||||
SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||
bool canAdjustLocks, bool canRez);
|
||||
bool canAdjustLocks, bool canRez,
|
||||
const QUuid& connectionSecret = QUuid());
|
||||
|
||||
bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; }
|
||||
|
||||
const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; }
|
||||
const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; }
|
||||
|
@ -149,11 +174,10 @@ public:
|
|||
const QUuid& packetHeaderID = QUuid());
|
||||
QByteArray constructPingReplyPacket(const QByteArray& pingPacket, const QUuid& packetHeaderID = QUuid());
|
||||
|
||||
virtual void sendSTUNRequest();
|
||||
virtual bool processSTUNResponse(const QByteArray& packet);
|
||||
|
||||
void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr,
|
||||
QUuid headerID = QUuid(), const QUuid& connectRequestID = QUuid());
|
||||
void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr);
|
||||
void sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID);
|
||||
|
||||
template<typename NodeLambda>
|
||||
void eachNode(NodeLambda functor) {
|
||||
|
@ -202,6 +226,11 @@ public:
|
|||
void putLocalPortIntoSharedMemory(const QString key, QObject* parent, quint16 localPort);
|
||||
bool getLocalServerPortFromSharedMemory(const QString key, quint16& localPort);
|
||||
|
||||
const QMap<quint64, ConnectionStep> getLastConnectionTimes() const
|
||||
{ QReadLocker readLock(&_connectionTimeLock); return _lastConnectionTimes; }
|
||||
void flagTimeForConnectionStep(ConnectionStep connectionStep);
|
||||
|
||||
|
||||
public slots:
|
||||
void reset();
|
||||
void eraseAllNodes();
|
||||
|
@ -210,6 +239,9 @@ public slots:
|
|||
|
||||
void updateLocalSockAddr();
|
||||
|
||||
void startSTUNPublicSocketUpdate();
|
||||
virtual void sendSTUNRequest();
|
||||
|
||||
void killNodeWithUUID(const QUuid& nodeUUID);
|
||||
signals:
|
||||
void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID);
|
||||
|
@ -240,6 +272,11 @@ protected:
|
|||
|
||||
void handleNodeKill(const SharedNodePointer& node);
|
||||
|
||||
void stopInitialSTUNUpdate(bool success);
|
||||
|
||||
void sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& headerID,
|
||||
const QUuid& peerRequestID = QUuid());
|
||||
|
||||
QUuid _sessionUUID;
|
||||
NodeHash _nodeHash;
|
||||
QReadWriteLock _nodeMutex;
|
||||
|
@ -259,6 +296,16 @@ protected:
|
|||
|
||||
std::unordered_map<QUuid, PacketTypeSequenceMap, UUIDHasher> _packetSequenceNumbers;
|
||||
|
||||
QPointer<QTimer> _initialSTUNTimer;
|
||||
int _numInitialSTUNRequests = 0;
|
||||
bool _hasCompletedInitialSTUN = false;
|
||||
quint64 _firstSTUNTime = 0;
|
||||
quint64 _publicSocketUpdateTime = 0;
|
||||
|
||||
mutable QReadWriteLock _connectionTimeLock { };
|
||||
QMap<quint64, ConnectionStep> _lastConnectionTimes;
|
||||
bool _areConnectionTimesComplete = false;
|
||||
|
||||
template<typename IteratorLambda>
|
||||
void eachNodeHashIterator(IteratorLambda functor) {
|
||||
QWriteLocker writeLock(&_nodeMutex);
|
||||
|
@ -268,7 +315,8 @@ protected:
|
|||
functor(it);
|
||||
}
|
||||
}
|
||||
|
||||
private slots:
|
||||
void flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp);
|
||||
};
|
||||
|
||||
#endif // hifi_LimitedNodeList_h
|
||||
|
|
|
@ -9,61 +9,153 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <qdatetime.h>
|
||||
#include "NetworkPeer.h"
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "NetworkPeer.h"
|
||||
#include "NetworkLogging.h"
|
||||
|
||||
#include "BandwidthRecorder.h"
|
||||
|
||||
NetworkPeer::NetworkPeer() :
|
||||
NetworkPeer::NetworkPeer(QObject* parent) :
|
||||
QObject(parent),
|
||||
_uuid(),
|
||||
_publicSocket(),
|
||||
_localSocket(),
|
||||
_symmetricSocket(),
|
||||
_activeSocket(NULL),
|
||||
_wakeTimestamp(QDateTime::currentMSecsSinceEpoch()),
|
||||
_lastHeardMicrostamp(usecTimestampNow()),
|
||||
_connectionAttempts(0)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
NetworkPeer::NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) :
|
||||
NetworkPeer::NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, QObject* parent) :
|
||||
QObject(parent),
|
||||
_uuid(uuid),
|
||||
_publicSocket(publicSocket),
|
||||
_localSocket(localSocket),
|
||||
_symmetricSocket(),
|
||||
_activeSocket(NULL),
|
||||
_wakeTimestamp(QDateTime::currentMSecsSinceEpoch()),
|
||||
_lastHeardMicrostamp(usecTimestampNow()),
|
||||
_connectionAttempts(0)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
NetworkPeer::NetworkPeer(const NetworkPeer& otherPeer) : QObject() {
|
||||
_uuid = otherPeer._uuid;
|
||||
_publicSocket = otherPeer._publicSocket;
|
||||
_localSocket = otherPeer._localSocket;
|
||||
|
||||
_wakeTimestamp = otherPeer._wakeTimestamp;
|
||||
_lastHeardMicrostamp = otherPeer._lastHeardMicrostamp;
|
||||
_connectionAttempts = otherPeer._connectionAttempts;
|
||||
void NetworkPeer::setPublicSocket(const HifiSockAddr& publicSocket) {
|
||||
if (publicSocket != _publicSocket) {
|
||||
if (_activeSocket == &_publicSocket) {
|
||||
// if the active socket was the public socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (!_publicSocket.isNull()) {
|
||||
qCDebug(networking) << "Public socket change for node" << *this;
|
||||
}
|
||||
|
||||
_publicSocket = publicSocket;
|
||||
}
|
||||
}
|
||||
|
||||
NetworkPeer& NetworkPeer::operator=(const NetworkPeer& otherPeer) {
|
||||
NetworkPeer temp(otherPeer);
|
||||
swap(temp);
|
||||
return *this;
|
||||
void NetworkPeer::setLocalSocket(const HifiSockAddr& localSocket) {
|
||||
if (localSocket != _localSocket) {
|
||||
if (_activeSocket == &_localSocket) {
|
||||
// if the active socket was the local socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (!_localSocket.isNull()) {
|
||||
qCDebug(networking) << "Local socket change for node" << *this;
|
||||
}
|
||||
|
||||
_localSocket = localSocket;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPeer::swap(NetworkPeer& otherPeer) {
|
||||
using std::swap;
|
||||
|
||||
swap(_uuid, otherPeer._uuid);
|
||||
swap(_publicSocket, otherPeer._publicSocket);
|
||||
swap(_localSocket, otherPeer._localSocket);
|
||||
swap(_wakeTimestamp, otherPeer._wakeTimestamp);
|
||||
swap(_lastHeardMicrostamp, otherPeer._lastHeardMicrostamp);
|
||||
swap(_connectionAttempts, otherPeer._connectionAttempts);
|
||||
void NetworkPeer::setSymmetricSocket(const HifiSockAddr& symmetricSocket) {
|
||||
if (symmetricSocket != _symmetricSocket) {
|
||||
if (_activeSocket == &_symmetricSocket) {
|
||||
// if the active socket was the symmetric socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (!_symmetricSocket.isNull()) {
|
||||
qCDebug(networking) << "Symmetric socket change for node" << *this;
|
||||
}
|
||||
|
||||
_symmetricSocket = symmetricSocket;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPeer::setActiveSocket(HifiSockAddr* discoveredSocket) {
|
||||
_activeSocket = discoveredSocket;
|
||||
|
||||
// we have an active socket, stop our ping timer
|
||||
stopPingTimer();
|
||||
|
||||
// we're now considered connected to this peer - reset the number of connection attemps
|
||||
resetConnectionAttempts();
|
||||
}
|
||||
|
||||
void NetworkPeer::activateLocalSocket() {
|
||||
if (_activeSocket != &_localSocket) {
|
||||
qCDebug(networking) << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
setActiveSocket(&_localSocket);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPeer::activatePublicSocket() {
|
||||
if (_activeSocket != &_publicSocket) {
|
||||
qCDebug(networking) << "Activating public socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
setActiveSocket(&_publicSocket);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPeer::activateSymmetricSocket() {
|
||||
if (_activeSocket != &_symmetricSocket) {
|
||||
qCDebug(networking) << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
setActiveSocket(&_symmetricSocket);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPeer::activateMatchingOrNewSymmetricSocket(const HifiSockAddr& matchableSockAddr) {
|
||||
if (matchableSockAddr == _publicSocket) {
|
||||
activatePublicSocket();
|
||||
} else if (matchableSockAddr == _localSocket) {
|
||||
activateLocalSocket();
|
||||
} else {
|
||||
// set the Node's symmetric socket to the passed socket
|
||||
setSymmetricSocket(matchableSockAddr);
|
||||
activateSymmetricSocket();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPeer::softReset() {
|
||||
// a soft reset should clear the sockets and reset the number of connection attempts
|
||||
_localSocket.clear();
|
||||
_publicSocket.clear();
|
||||
_symmetricSocket.clear();
|
||||
_activeSocket = NULL;
|
||||
|
||||
// stop our ping timer since we don't have sockets to ping anymore anyways
|
||||
stopPingTimer();
|
||||
|
||||
_connectionAttempts = 0;
|
||||
}
|
||||
|
||||
void NetworkPeer::reset() {
|
||||
softReset();
|
||||
|
||||
_uuid = QUuid();
|
||||
_wakeTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
_lastHeardMicrostamp = usecTimestampNow();
|
||||
}
|
||||
|
||||
QByteArray NetworkPeer::toByteArray() const {
|
||||
|
@ -71,15 +163,33 @@ QByteArray NetworkPeer::toByteArray() const {
|
|||
|
||||
QDataStream peerStream(&peerByteArray, QIODevice::Append);
|
||||
peerStream << *this;
|
||||
|
||||
|
||||
return peerByteArray;
|
||||
}
|
||||
|
||||
void NetworkPeer::startPingTimer() {
|
||||
if (!_pingTimer) {
|
||||
_pingTimer = new QTimer(this);
|
||||
|
||||
connect(_pingTimer, &QTimer::timeout, this, &NetworkPeer::pingTimerTimeout);
|
||||
|
||||
_pingTimer->start(UDP_PUNCH_PING_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPeer::stopPingTimer() {
|
||||
if (_pingTimer) {
|
||||
_pingTimer->stop();
|
||||
_pingTimer->deleteLater();
|
||||
_pingTimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& out, const NetworkPeer& peer) {
|
||||
out << peer._uuid;
|
||||
out << peer._publicSocket;
|
||||
out << peer._localSocket;
|
||||
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -87,7 +197,7 @@ QDataStream& operator>>(QDataStream& in, NetworkPeer& peer) {
|
|||
in >> peer._uuid;
|
||||
in >> peer._publicSocket;
|
||||
in >> peer._localSocket;
|
||||
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
#ifndef hifi_NetworkPeer_h
|
||||
#define hifi_NetworkPeer_h
|
||||
|
||||
#include <qobject.h>
|
||||
#include <quuid.h>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
|
@ -22,59 +23,80 @@ const int ICE_SERVER_DEFAULT_PORT = 7337;
|
|||
const int ICE_HEARBEAT_INTERVAL_MSECS = 2 * 1000;
|
||||
const int MAX_ICE_CONNECTION_ATTEMPTS = 5;
|
||||
|
||||
const int UDP_PUNCH_PING_INTERVAL_MS = 25;
|
||||
|
||||
class NetworkPeer : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
NetworkPeer();
|
||||
NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
|
||||
|
||||
// privatize copy and assignment operator to disallow peer copying
|
||||
NetworkPeer(const NetworkPeer &otherPeer);
|
||||
NetworkPeer& operator=(const NetworkPeer& otherPeer);
|
||||
|
||||
NetworkPeer(QObject* parent = 0);
|
||||
NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, QObject* parent = 0);
|
||||
|
||||
bool isNull() const { return _uuid.isNull(); }
|
||||
|
||||
bool hasSockets() const { return !_localSocket.isNull() && !_publicSocket.isNull(); }
|
||||
|
||||
const QUuid& getUUID() const { return _uuid; }
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
|
||||
|
||||
void softReset();
|
||||
void reset();
|
||||
|
||||
|
||||
const HifiSockAddr& getPublicSocket() const { return _publicSocket; }
|
||||
virtual void setPublicSocket(const HifiSockAddr& publicSocket) { _publicSocket = publicSocket; }
|
||||
const HifiSockAddr& getLocalSocket() const { return _localSocket; }
|
||||
virtual void setLocalSocket(const HifiSockAddr& localSocket) { _localSocket = localSocket; }
|
||||
|
||||
const HifiSockAddr& getSymmetricSocket() const { return _symmetricSocket; }
|
||||
|
||||
void setPublicSocket(const HifiSockAddr& publicSocket);
|
||||
void setLocalSocket(const HifiSockAddr& localSocket);
|
||||
void setSymmetricSocket(const HifiSockAddr& symmetricSocket);
|
||||
|
||||
const HifiSockAddr* getActiveSocket() const { return _activeSocket; }
|
||||
|
||||
void activatePublicSocket();
|
||||
void activateLocalSocket();
|
||||
void activateSymmetricSocket();
|
||||
|
||||
void activateMatchingOrNewSymmetricSocket(const HifiSockAddr& matchableSockAddr);
|
||||
|
||||
quint64 getWakeTimestamp() const { return _wakeTimestamp; }
|
||||
void setWakeTimestamp(quint64 wakeTimestamp) { _wakeTimestamp = wakeTimestamp; }
|
||||
|
||||
|
||||
quint64 getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
|
||||
void setLastHeardMicrostamp(quint64 lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
|
||||
|
||||
|
||||
QByteArray toByteArray() const;
|
||||
|
||||
|
||||
int getConnectionAttempts() const { return _connectionAttempts; }
|
||||
void incrementConnectionAttempts() { ++_connectionAttempts; }
|
||||
void resetConnectionAttemps() { _connectionAttempts = 0; }
|
||||
void resetConnectionAttempts() { _connectionAttempts = 0; }
|
||||
|
||||
void recordBytesSent(int count);
|
||||
void recordBytesReceived(int count);
|
||||
|
||||
float getOutboundBandwidth(); // in kbps
|
||||
float getInboundBandwidth(); // in kbps
|
||||
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& out, const NetworkPeer& peer);
|
||||
friend QDataStream& operator>>(QDataStream& in, NetworkPeer& peer);
|
||||
public slots:
|
||||
void startPingTimer();
|
||||
void stopPingTimer();
|
||||
signals:
|
||||
void pingTimerTimeout();
|
||||
protected:
|
||||
void setActiveSocket(HifiSockAddr* discoveredSocket);
|
||||
|
||||
QUuid _uuid;
|
||||
|
||||
|
||||
HifiSockAddr _publicSocket;
|
||||
HifiSockAddr _localSocket;
|
||||
|
||||
HifiSockAddr _symmetricSocket;
|
||||
HifiSockAddr* _activeSocket;
|
||||
|
||||
quint64 _wakeTimestamp;
|
||||
quint64 _lastHeardMicrostamp;
|
||||
|
||||
|
||||
QTimer* _pingTimer = NULL;
|
||||
|
||||
int _connectionAttempts;
|
||||
private:
|
||||
void swap(NetworkPeer& otherPeer);
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, const NetworkPeer &peer);
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include "Node.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "NetworkLogging.h"
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDebug>
|
||||
|
@ -42,12 +41,11 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
|
|||
}
|
||||
|
||||
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
|
||||
const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez) :
|
||||
NetworkPeer(uuid, publicSocket, localSocket),
|
||||
const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez, const QUuid& connectionSecret,
|
||||
QObject* parent) :
|
||||
NetworkPeer(uuid, publicSocket, localSocket, parent),
|
||||
_type(type),
|
||||
_activeSocket(NULL),
|
||||
_symmetricSocket(),
|
||||
_connectionSecret(),
|
||||
_connectionSecret(connectionSecret),
|
||||
_linkedData(NULL),
|
||||
_isAlive(true),
|
||||
_pingMs(-1), // "Uninitialized"
|
||||
|
@ -57,7 +55,7 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
|
|||
_canAdjustLocks(canAdjustLocks),
|
||||
_canRez(canRez)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
Node::~Node() {
|
||||
|
@ -69,68 +67,8 @@ void Node::updateClockSkewUsec(int clockSkewSample) {
|
|||
_clockSkewUsec = (int)_clockSkewMovingPercentile.getValueAtPercentile();
|
||||
}
|
||||
|
||||
void Node::setPublicSocket(const HifiSockAddr& publicSocket) {
|
||||
if (publicSocket != _publicSocket) {
|
||||
if (_activeSocket == &_publicSocket) {
|
||||
// if the active socket was the public socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (!_publicSocket.isNull()) {
|
||||
qCDebug(networking) << "Public socket change for node" << *this;
|
||||
}
|
||||
|
||||
_publicSocket = publicSocket;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::setLocalSocket(const HifiSockAddr& localSocket) {
|
||||
if (localSocket != _localSocket) {
|
||||
if (_activeSocket == &_localSocket) {
|
||||
// if the active socket was the local socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (!_localSocket.isNull()) {
|
||||
qCDebug(networking) << "Local socket change for node" << *this;
|
||||
}
|
||||
|
||||
_localSocket = localSocket;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::setSymmetricSocket(const HifiSockAddr& symmetricSocket) {
|
||||
if (symmetricSocket != _symmetricSocket) {
|
||||
if (_activeSocket == &_symmetricSocket) {
|
||||
// if the active socket was the symmetric socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (!_symmetricSocket.isNull()) {
|
||||
qCDebug(networking) << "Symmetric socket change for node" << *this;
|
||||
}
|
||||
|
||||
_symmetricSocket = symmetricSocket;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::activateLocalSocket() {
|
||||
qCDebug(networking) << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
_activeSocket = &_localSocket;
|
||||
}
|
||||
|
||||
void Node::activatePublicSocket() {
|
||||
qCDebug(networking) << "Activating public socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
_activeSocket = &_publicSocket;
|
||||
}
|
||||
|
||||
void Node::activateSymmetricSocket() {
|
||||
qCDebug(networking) << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
_activeSocket = &_symmetricSocket;
|
||||
}
|
||||
|
||||
PacketSequenceNumber Node::getLastSequenceNumberForPacketType(PacketType packetType) const {
|
||||
auto typeMatch = _lastSequenceNumbers.find(packetType);
|
||||
auto typeMatch = _lastSequenceNumbers.find(packetType);
|
||||
if (typeMatch != _lastSequenceNumbers.end()) {
|
||||
return typeMatch->second;
|
||||
} else {
|
||||
|
@ -145,7 +83,7 @@ QDataStream& operator<<(QDataStream& out, const Node& node) {
|
|||
out << node._localSocket;
|
||||
out << node._canAdjustLocks;
|
||||
out << node._canRez;
|
||||
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -156,7 +94,7 @@ QDataStream& operator>>(QDataStream& in, Node& node) {
|
|||
in >> node._localSocket;
|
||||
in >> node._canAdjustLocks;
|
||||
in >> node._canRez;
|
||||
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,30 +23,18 @@
|
|||
#include "HifiSockAddr.h"
|
||||
#include "NetworkPeer.h"
|
||||
#include "NodeData.h"
|
||||
#include "NodeType.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "SimpleMovingAverage.h"
|
||||
#include "MovingPercentile.h"
|
||||
|
||||
typedef quint8 NodeType_t;
|
||||
|
||||
namespace NodeType {
|
||||
const NodeType_t DomainServer = 'D';
|
||||
const NodeType_t EntityServer = 'o'; // was ModelServer
|
||||
const NodeType_t EnvironmentServer = 'E';
|
||||
const NodeType_t Agent = 'I';
|
||||
const NodeType_t AudioMixer = 'M';
|
||||
const NodeType_t AvatarMixer = 'W';
|
||||
const NodeType_t Unassigned = 1;
|
||||
|
||||
void init();
|
||||
const QString& getNodeTypeName(NodeType_t nodeType);
|
||||
}
|
||||
|
||||
class Node : public NetworkPeer {
|
||||
Q_OBJECT
|
||||
public:
|
||||
public:
|
||||
Node(const QUuid& uuid, NodeType_t type,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez);
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||
bool canAdjustLocks, bool canRez, const QUuid& connectionSecret = QUuid(),
|
||||
QObject* parent = 0);
|
||||
~Node();
|
||||
|
||||
bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; }
|
||||
|
@ -54,7 +42,7 @@ public:
|
|||
|
||||
char getType() const { return _type; }
|
||||
void setType(char type) { _type = type; }
|
||||
|
||||
|
||||
const QUuid& getConnectionSecret() const { return _connectionSecret; }
|
||||
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
|
||||
|
||||
|
@ -70,28 +58,17 @@ public:
|
|||
int getClockSkewUsec() const { return _clockSkewUsec; }
|
||||
void updateClockSkewUsec(int clockSkewSample);
|
||||
QMutex& getMutex() { return _mutex; }
|
||||
|
||||
virtual void setPublicSocket(const HifiSockAddr& publicSocket);
|
||||
virtual void setLocalSocket(const HifiSockAddr& localSocket);
|
||||
const HifiSockAddr& getSymmetricSocket() const { return _symmetricSocket; }
|
||||
virtual void setSymmetricSocket(const HifiSockAddr& symmetricSocket);
|
||||
|
||||
const HifiSockAddr* getActiveSocket() const { return _activeSocket; }
|
||||
|
||||
void setCanAdjustLocks(bool canAdjustLocks) { _canAdjustLocks = canAdjustLocks; }
|
||||
bool getCanAdjustLocks() { return _canAdjustLocks; }
|
||||
|
||||
void setCanRez(bool canRez) { _canRez = canRez; }
|
||||
bool getCanRez() { return _canRez; }
|
||||
|
||||
void activatePublicSocket();
|
||||
void activateLocalSocket();
|
||||
void activateSymmetricSocket();
|
||||
|
||||
void setLastSequenceNumberForPacketType(PacketSequenceNumber sequenceNumber, PacketType packetType)
|
||||
{ _lastSequenceNumbers[packetType] = sequenceNumber; }
|
||||
PacketSequenceNumber getLastSequenceNumberForPacketType(PacketType packetType) const;
|
||||
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& out, const Node& node);
|
||||
friend QDataStream& operator>>(QDataStream& in, Node& node);
|
||||
|
||||
|
@ -101,10 +78,7 @@ private:
|
|||
Node& operator=(Node otherNode);
|
||||
|
||||
NodeType_t _type;
|
||||
|
||||
HifiSockAddr* _activeSocket;
|
||||
HifiSockAddr _symmetricSocket;
|
||||
|
||||
|
||||
QUuid _connectionSecret;
|
||||
NodeData* _linkedData;
|
||||
bool _isAlive;
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QMetaEnum>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include <LogHandler.h>
|
||||
|
@ -34,9 +36,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
|
|||
_nodeTypesOfInterest(),
|
||||
_domainHandler(this),
|
||||
_numNoReplyDomainCheckIns(0),
|
||||
_assignmentServerSocket(),
|
||||
_hasCompletedInitialSTUNFailure(false),
|
||||
_stunRequestsSinceSuccess(0)
|
||||
_assignmentServerSocket()
|
||||
{
|
||||
static bool firstCall = true;
|
||||
if (firstCall) {
|
||||
|
@ -61,17 +61,35 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
|
|||
// fire off any pending DS path query when we get socket information
|
||||
connect(&_domainHandler, &DomainHandler::completedSocketDiscovery, this, &NodeList::sendPendingDSPathQuery);
|
||||
|
||||
// send a domain server check in immediately once the DS socket is known
|
||||
connect(&_domainHandler, &DomainHandler::completedSocketDiscovery, this, &NodeList::sendDomainServerCheckIn);
|
||||
|
||||
// send a domain server check in immediately if there is a public socket change
|
||||
connect(this, &LimitedNodeList::publicSockAddrChanged, this, &NodeList::sendDomainServerCheckIn);
|
||||
|
||||
// clear our NodeList when the domain changes
|
||||
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset);
|
||||
|
||||
// handle ICE signal from DS so connection is attempted immediately
|
||||
connect(&_domainHandler, &DomainHandler::requestICEConnectionAttempt, this, &NodeList::handleICEConnectionToDomainServer);
|
||||
// send an ICE heartbeat as soon as we get ice server information
|
||||
connect(&_domainHandler, &DomainHandler::iceSocketAndIDReceived, this, &NodeList::handleICEConnectionToDomainServer);
|
||||
|
||||
// handle ping timeout from DomainHandler to establish a connection with auto networked domain-server
|
||||
connect(&_domainHandler.getICEPeer(), &NetworkPeer::pingTimerTimeout, this, &NodeList::pingPunchForDomainServer);
|
||||
|
||||
// send a ping punch immediately
|
||||
connect(&_domainHandler, &DomainHandler::icePeerSocketsReceived, this, &NodeList::pingPunchForDomainServer);
|
||||
|
||||
// clear out NodeList when login is finished
|
||||
connect(&AccountManager::getInstance(), &AccountManager::loginComplete , this, &NodeList::reset);
|
||||
|
||||
// clear our NodeList when logout is requested
|
||||
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
|
||||
|
||||
// anytime we get a new node we will want to attempt to punch to it
|
||||
connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
|
||||
|
||||
// we definitely want STUN to update our public socket, so call the LNL to kick that off
|
||||
startSTUNPublicSocketUpdate();
|
||||
}
|
||||
|
||||
qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& destination) {
|
||||
|
@ -155,12 +173,18 @@ void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer&
|
|||
}
|
||||
|
||||
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
|
||||
switch (packetTypeForPacket(packet)) {
|
||||
case PacketTypeDomainList: {
|
||||
PacketType packetType = packetTypeForPacket(packet);
|
||||
switch (packetType) {
|
||||
case PacketTypeDomainList:
|
||||
case PacketTypeDomainServerAddedNode: {
|
||||
if (!_domainHandler.getSockAddr().isNull()) {
|
||||
// only process a list from domain-server if we're talking to a domain
|
||||
// only process a packet from domain-server if we're talking to a domain
|
||||
// TODO: how do we make sure this is actually the domain we want the list from (DTLS probably)
|
||||
processDomainServerList(packet);
|
||||
if (packetType == PacketTypeDomainList) {
|
||||
processDomainServerList(packet);
|
||||
} else if (packetType == PacketTypeDomainServerAddedNode) {
|
||||
processDomainServerAddedNode(packet);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -168,8 +192,10 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
|||
_domainHandler.parseDTLSRequirementPacket(packet);
|
||||
break;
|
||||
}
|
||||
case PacketTypeIceServerHeartbeatResponse: {
|
||||
_domainHandler.processICEResponsePacket(packet);
|
||||
case PacketTypeIceServerPeerInformation: {
|
||||
if (!_domainHandler.getICEPeer().hasSockets()) {
|
||||
_domainHandler.processICEResponsePacket(packet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PacketTypePing: {
|
||||
|
@ -216,15 +242,18 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
|||
case PacketTypeUnverifiedPingReply: {
|
||||
qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr;
|
||||
|
||||
// for now we're unsafely assuming this came back from the domain
|
||||
if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) {
|
||||
qCDebug(networking) << "Connecting to domain using local socket";
|
||||
_domainHandler.activateICELocalSocket();
|
||||
} else if (senderSockAddr == _domainHandler.getICEPeer().getPublicSocket()) {
|
||||
qCDebug(networking) << "Conecting to domain using public socket";
|
||||
_domainHandler.activateICEPublicSocket();
|
||||
} else {
|
||||
qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect.";
|
||||
if (_domainHandler.getIP().isNull()) {
|
||||
// for now we're unsafely assuming this came back from the domain
|
||||
if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) {
|
||||
qCDebug(networking) << "Connecting to domain using local socket";
|
||||
_domainHandler.activateICELocalSocket();
|
||||
} else if (senderSockAddr == _domainHandler.getICEPeer().getPublicSocket()) {
|
||||
qCDebug(networking) << "Conecting to domain using public socket";
|
||||
_domainHandler.activateICEPublicSocket();
|
||||
} else {
|
||||
qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect.";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
case PacketTypeStunResponse: {
|
||||
|
@ -270,57 +299,14 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes)
|
|||
_nodeTypesOfInterest.unite(setOfNodeTypes);
|
||||
}
|
||||
|
||||
|
||||
const unsigned int NUM_STUN_REQUESTS_BEFORE_FALLBACK = 5;
|
||||
|
||||
void NodeList::sendSTUNRequest() {
|
||||
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
qCDebug(networking) << "Sending intial stun request to" << STUN_SERVER_HOSTNAME;
|
||||
}
|
||||
|
||||
LimitedNodeList::sendSTUNRequest();
|
||||
|
||||
_stunRequestsSinceSuccess++;
|
||||
|
||||
if (_stunRequestsSinceSuccess >= NUM_STUN_REQUESTS_BEFORE_FALLBACK) {
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
// if we're here this was the last failed STUN request
|
||||
// use our DS as our stun server
|
||||
qCDebug(networking, "Failed to lookup public address via STUN server at %s:%hu. Using DS for STUN.",
|
||||
STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
}
|
||||
|
||||
// reset the public address and port
|
||||
// use 0 so the DS knows to act as out STUN server
|
||||
_publicSockAddr = HifiSockAddr(QHostAddress(), _nodeSocket.localPort());
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeList::processSTUNResponse(const QByteArray& packet) {
|
||||
if (LimitedNodeList::processSTUNResponse(packet)) {
|
||||
// reset the number of failed STUN requests since last success
|
||||
_stunRequestsSinceSuccess = 0;
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::sendDomainServerCheckIn() {
|
||||
if (_publicSockAddr.isNull() && !_hasCompletedInitialSTUNFailure) {
|
||||
if (_publicSockAddr.isNull()) {
|
||||
// we don't know our public socket and we need to send it to the domain server
|
||||
// send a STUN request to figure it out
|
||||
sendSTUNRequest();
|
||||
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()
|
||||
|
@ -365,7 +351,6 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
// pack our data to send to the domain-server
|
||||
packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();
|
||||
|
||||
|
||||
// if this is a connect request, and we can present a username signature, send it along
|
||||
if (!_domainHandler.isConnected()) {
|
||||
DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo();
|
||||
|
@ -379,18 +364,12 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
}
|
||||
}
|
||||
|
||||
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn);
|
||||
|
||||
if (!isUsingDTLS) {
|
||||
writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr());
|
||||
}
|
||||
|
||||
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
||||
static unsigned int numDomainCheckins = 0;
|
||||
|
||||
// send a STUN request every Nth domain server check in so we update our public socket, if required
|
||||
if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) {
|
||||
sendSTUNRequest();
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -499,17 +478,40 @@ void NodeList::handleDSPathQueryResponse(const QByteArray& packet) {
|
|||
}
|
||||
|
||||
void NodeList::handleICEConnectionToDomainServer() {
|
||||
if (_domainHandler.getICEPeer().isNull()
|
||||
|| _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {
|
||||
// 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().resetConnectionAttemps();
|
||||
_domainHandler.getICEPeer().resetConnectionAttempts();
|
||||
|
||||
LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(),
|
||||
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendICEServerQuery);
|
||||
|
||||
LimitedNodeList::sendPeerQueryToIceServer(_domainHandler.getICEServerSockAddr(),
|
||||
_domainHandler.getICEClientID(),
|
||||
_domainHandler.getICEDomainID());
|
||||
} else {
|
||||
qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID"
|
||||
<< uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
_domainHandler.getICEPeer().softReset();
|
||||
handleICEConnectionToDomainServer();
|
||||
}
|
||||
}
|
||||
|
||||
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendPingsToDS);
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID());
|
||||
|
@ -526,6 +528,8 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
|
|||
// this is a packet from the domain server, reset the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList);
|
||||
|
||||
// if this was the first domain-server list from this domain, we've now connected
|
||||
if (!_domainHandler.isConnected()) {
|
||||
_domainHandler.setUUID(uuidFromPacketHeader(packet));
|
||||
|
@ -551,36 +555,45 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
|
|||
setThisNodeCanRez(thisNodeCanRez);
|
||||
|
||||
// pull each node in the packet
|
||||
while(packetStream.device()->pos() < packet.size()) {
|
||||
// setup variables to read into from QDataStream
|
||||
qint8 nodeType;
|
||||
QUuid nodeUUID, connectionUUID;
|
||||
HifiSockAddr nodePublicSocket, nodeLocalSocket;
|
||||
bool canAdjustLocks;
|
||||
bool canRez;
|
||||
|
||||
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> canAdjustLocks >> canRez;
|
||||
|
||||
// if the public socket address is 0 then it's reachable at the same IP
|
||||
// as the domain server
|
||||
if (nodePublicSocket.getAddress().isNull()) {
|
||||
nodePublicSocket.setAddress(_domainHandler.getIP());
|
||||
}
|
||||
|
||||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
|
||||
nodeLocalSocket, canAdjustLocks, canRez);
|
||||
|
||||
packetStream >> connectionUUID;
|
||||
node->setConnectionSecret(connectionUUID);
|
||||
while (packetStream.device()->pos() < packet.size()) {
|
||||
parseNodeFromPacketStream(packetStream);
|
||||
}
|
||||
|
||||
// ping inactive nodes in conjunction with receipt of list from domain-server
|
||||
// this makes it happen every second and also pings any newly added nodes
|
||||
pingInactiveNodes();
|
||||
|
||||
return readNodes;
|
||||
}
|
||||
|
||||
void NodeList::processDomainServerAddedNode(const QByteArray& packet) {
|
||||
// setup a QDataStream, skip the header
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
// use our shared method to pull out the new node
|
||||
parseNodeFromPacketStream(packetStream);
|
||||
}
|
||||
|
||||
void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
||||
// setup variables to read into from QDataStream
|
||||
qint8 nodeType;
|
||||
QUuid nodeUUID, connectionUUID;
|
||||
HifiSockAddr nodePublicSocket, nodeLocalSocket;
|
||||
bool canAdjustLocks;
|
||||
bool canRez;
|
||||
|
||||
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> canAdjustLocks >> canRez;
|
||||
|
||||
// if the public socket address is 0 then it's reachable at the same IP
|
||||
// as the domain server
|
||||
if (nodePublicSocket.getAddress().isNull()) {
|
||||
nodePublicSocket.setAddress(_domainHandler.getIP());
|
||||
}
|
||||
|
||||
packetStream >> connectionUUID;
|
||||
|
||||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
|
||||
nodeLocalSocket, canAdjustLocks, canRez,
|
||||
connectionUUID);
|
||||
}
|
||||
|
||||
void NodeList::sendAssignment(Assignment& assignment) {
|
||||
|
||||
PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
|
||||
|
@ -596,6 +609,16 @@ void NodeList::sendAssignment(Assignment& assignment) {
|
|||
}
|
||||
|
||||
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
|
||||
QByteArray localPingPacket = constructPingPacket(PingType::Local);
|
||||
|
@ -608,15 +631,30 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
|
|||
QByteArray symmetricPingPacket = constructPingPacket(PingType::Symmetric);
|
||||
writeDatagram(symmetricPingPacket, node, node->getSymmetricSocket());
|
||||
}
|
||||
|
||||
node->incrementConnectionAttempts();
|
||||
}
|
||||
|
||||
void NodeList::pingInactiveNodes() {
|
||||
eachNode([this](const SharedNodePointer& node){
|
||||
if (!node->getActiveSocket()) {
|
||||
// we don't have an active link to this node, ping it to set that up
|
||||
pingPunchForInactiveNode(node);
|
||||
void NodeList::startNodeHolePunch(const SharedNodePointer& node) {
|
||||
// connect to the correct signal on this node so we know when to ping it
|
||||
connect(node.data(), &Node::pingTimerTimeout, this, &NodeList::handleNodePingTimeout);
|
||||
|
||||
// start the ping timer for this node
|
||||
node->startPingTimer();
|
||||
|
||||
// ping this node immediately
|
||||
pingPunchForInactiveNode(node);
|
||||
}
|
||||
|
||||
void NodeList::handleNodePingTimeout() {
|
||||
Node* senderNode = qobject_cast<Node*>(sender());
|
||||
if (senderNode) {
|
||||
SharedNodePointer sharedNode = nodeWithUUID(senderNode->getUUID());
|
||||
|
||||
if (sharedNode && !sharedNode->getActiveSocket()) {
|
||||
pingPunchForInactiveNode(sharedNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode) {
|
||||
|
@ -636,4 +674,8 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con
|
|||
} else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) {
|
||||
sendingNode->activateSymmetricSocket();
|
||||
}
|
||||
|
||||
if (sendingNode->getType() == NodeType::AudioMixer) {
|
||||
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetAudioMixerSocket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,32 +61,29 @@ public:
|
|||
|
||||
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
|
||||
|
||||
int processDomainServerList(const QByteArray& packet);
|
||||
|
||||
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
|
||||
void sendAssignment(Assignment& assignment);
|
||||
|
||||
void pingPunchForInactiveNode(const SharedNodePointer& node);
|
||||
public slots:
|
||||
void reset();
|
||||
void sendDomainServerCheckIn();
|
||||
void pingInactiveNodes();
|
||||
void handleDSPathQuery(const QString& newPath);
|
||||
signals:
|
||||
void limitOfSilentDomainCheckInsReached();
|
||||
private slots:
|
||||
void sendPendingDSPathQuery();
|
||||
void handleICEConnectionToDomainServer();
|
||||
|
||||
void startNodeHolePunch(const SharedNodePointer& node);
|
||||
void handleNodePingTimeout();
|
||||
|
||||
void pingPunchForDomainServer();
|
||||
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);
|
||||
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
|
||||
void sendSTUNRequest();
|
||||
bool processSTUNResponse(const QByteArray& packet);
|
||||
|
||||
void handleICEConnectionToDomainServer();
|
||||
|
||||
void processDomainServerAuthRequest(const QByteArray& packet);
|
||||
void requestAuthForDomainServer();
|
||||
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||
|
@ -96,13 +93,17 @@ private:
|
|||
|
||||
void sendDSPathQuery(const QString& newPath);
|
||||
|
||||
int processDomainServerList(const QByteArray& packet);
|
||||
void processDomainServerAddedNode(const QByteArray& packet);
|
||||
void parseNodeFromPacketStream(QDataStream& packetStream);
|
||||
|
||||
void pingPunchForInactiveNode(const SharedNodePointer& node);
|
||||
|
||||
NodeType_t _ownerType;
|
||||
NodeSet _nodeTypesOfInterest;
|
||||
DomainHandler _domainHandler;
|
||||
int _numNoReplyDomainCheckIns;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
bool _hasCompletedInitialSTUNFailure;
|
||||
unsigned int _stunRequestsSinceSuccess;
|
||||
|
||||
friend class Application;
|
||||
};
|
||||
|
|
34
libraries/networking/src/NodeType.h
Normal file
34
libraries/networking/src/NodeType.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// NodeType.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 05/29/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_NodeType_h
|
||||
#define hifi_NodeType_h
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef quint8 NodeType_t;
|
||||
|
||||
namespace NodeType {
|
||||
const NodeType_t DomainServer = 'D';
|
||||
const NodeType_t EntityServer = 'o'; // was ModelServer
|
||||
const NodeType_t EnvironmentServer = 'E';
|
||||
const NodeType_t Agent = 'I';
|
||||
const NodeType_t AudioMixer = 'M';
|
||||
const NodeType_t AvatarMixer = 'W';
|
||||
const NodeType_t Unassigned = 1;
|
||||
|
||||
void init();
|
||||
const QString& getNodeTypeName(NodeType_t nodeType);
|
||||
}
|
||||
|
||||
typedef QSet<NodeType_t> NodeSet;
|
||||
|
||||
#endif // hifi_NodeType_h
|
|
@ -78,6 +78,9 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return 2;
|
||||
case PacketTypeAudioStreamStats:
|
||||
return 1;
|
||||
case PacketTypeIceServerHeartbeat:
|
||||
case PacketTypeIceServerQuery:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -125,7 +128,9 @@ QString nameForPacketType(PacketType packetType) {
|
|||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityEditNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeSignedTransactionPayment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeat);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeatResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainServerAddedNode);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerQuery);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerPeerInformation);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPing);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPingReply);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAdd);
|
||||
|
@ -149,33 +154,33 @@ int populatePacketHeaderWithUUID(QByteArray& packet, PacketType packetType, cons
|
|||
if (packet.size() < numBytesForPacketHeaderGivenPacketType(packetType)) {
|
||||
packet.resize(numBytesForPacketHeaderGivenPacketType(packetType));
|
||||
}
|
||||
|
||||
|
||||
return populatePacketHeaderWithUUID(packet.data(), packetType, connectionUUID);
|
||||
}
|
||||
|
||||
int populatePacketHeaderWithUUID(char* packet, PacketType packetType, const QUuid& connectionUUID) {
|
||||
int numTypeBytes = packArithmeticallyCodedValue(packetType, packet);
|
||||
packet[numTypeBytes] = versionForPacketType(packetType);
|
||||
|
||||
|
||||
char* position = packet + numTypeBytes + sizeof(PacketVersion);
|
||||
|
||||
|
||||
QByteArray rfcUUID = connectionUUID.toRfc4122();
|
||||
memcpy(position, rfcUUID.constData(), NUM_BYTES_RFC4122_UUID);
|
||||
position += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
|
||||
if (!NON_VERIFIED_PACKETS.contains(packetType)) {
|
||||
// pack 16 bytes of zeros where the md5 hash will be placed once data is packed
|
||||
memset(position, 0, NUM_BYTES_MD5_HASH);
|
||||
position += NUM_BYTES_MD5_HASH;
|
||||
}
|
||||
|
||||
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
// Pack zeros for the number of bytes that the sequence number requires.
|
||||
// The LimitedNodeList will handle packing in the sequence number when sending out the packet.
|
||||
memset(position, 0, sizeof(PacketSequenceNumber));
|
||||
position += sizeof(PacketSequenceNumber);
|
||||
}
|
||||
|
||||
|
||||
// return the number of bytes written for pointer pushing
|
||||
return position - packet;
|
||||
}
|
||||
|
@ -235,13 +240,13 @@ PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketTy
|
|||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
|
||||
PacketSequenceNumber result = DEFAULT_SEQUENCE_NUMBER;
|
||||
|
||||
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
memcpy(&result, packet.data() + sequenceNumberOffsetForPacketType(packetType), sizeof(PacketSequenceNumber));
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -249,7 +254,7 @@ void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID, Packet
|
|||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
|
||||
packet.replace(hashOffsetForPacketType(packetType), NUM_BYTES_MD5_HASH,
|
||||
hashForPacketAndConnectionUUID(packet, connectionUUID));
|
||||
}
|
||||
|
@ -258,7 +263,7 @@ void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequ
|
|||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
|
||||
packet.replace(sequenceNumberOffsetForPacketType(packetType),
|
||||
sizeof(PacketSequenceNumber), reinterpret_cast<char*>(&sequenceNumber), sizeof(PacketSequenceNumber));
|
||||
}
|
||||
|
@ -268,7 +273,7 @@ void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& conne
|
|||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
|
||||
replaceHashInPacket(packet, connectionUUID, packetType);
|
||||
replaceSequenceNumberInPacket(packet, sequenceNumber, packetType);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
// NOTE: if adding a new packet packetType, you can replace one marked usable or add at the end
|
||||
// NOTE: if you want the name of the packet packetType to be available for debugging or logging, update nameForPacketType() as well
|
||||
|
||||
enum PacketType {
|
||||
PacketTypeUnknown, // 0
|
||||
PacketTypeStunResponse,
|
||||
|
@ -49,9 +50,9 @@ enum PacketType {
|
|||
PacketTypeDataServerConfirm, // 20
|
||||
PacketTypeDomainServerPathQuery,
|
||||
PacketTypeDomainServerPathResponse,
|
||||
UNUSED_3,
|
||||
UNUSED_4,
|
||||
UNUSED_5, // 25
|
||||
PacketTypeDomainServerAddedNode,
|
||||
PacketTypeIceServerPeerInformation,
|
||||
PacketTypeIceServerQuery, // 25
|
||||
PacketTypeOctreeStats,
|
||||
PacketTypeJurisdiction,
|
||||
PacketTypeJurisdictionRequest,
|
||||
|
@ -77,7 +78,6 @@ enum PacketType {
|
|||
PacketTypeEntityEditNack,
|
||||
PacketTypeSignedTransactionPayment,
|
||||
PacketTypeIceServerHeartbeat, // 50
|
||||
PacketTypeIceServerHeartbeatResponse,
|
||||
PacketTypeUnverifiedPing,
|
||||
PacketTypeUnverifiedPingReply,
|
||||
PacketTypeParticleEntitiesFix
|
||||
|
@ -91,14 +91,16 @@ const PacketSequenceNumber DEFAULT_SEQUENCE_NUMBER = 0;
|
|||
typedef std::map<PacketType, PacketSequenceNumber> PacketTypeSequenceMap;
|
||||
|
||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
|
||||
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied
|
||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
||||
<< PacketTypeNodeJsonStats << PacketTypeEntityQuery
|
||||
<< PacketTypeOctreeDataNack << PacketTypeEntityEditNack
|
||||
<< PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse
|
||||
<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode
|
||||
<< PacketTypeDomainServerPathQuery << PacketTypeDomainServerPathResponse;
|
||||
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
|
||||
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied
|
||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
||||
<< PacketTypeNodeJsonStats << PacketTypeEntityQuery
|
||||
<< PacketTypeOctreeDataNack << PacketTypeEntityEditNack
|
||||
<< PacketTypeIceServerHeartbeat << PacketTypeIceServerPeerInformation
|
||||
<< PacketTypeIceServerQuery << PacketTypeUnverifiedPing
|
||||
<< PacketTypeUnverifiedPingReply << PacketTypeStopNode
|
||||
<< PacketTypeDomainServerPathQuery << PacketTypeDomainServerPathResponse
|
||||
<< PacketTypeDomainServerAddedNode;
|
||||
|
||||
const QSet<PacketType> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeAvatarData;
|
||||
|
|
|
@ -46,7 +46,6 @@ protected:
|
|||
|
||||
private slots:
|
||||
void checkInWithDomainServerOrExit();
|
||||
|
||||
};
|
||||
|
||||
typedef QSharedPointer<ThreadedAssignment> SharedAssignmentPointer;
|
||||
|
|
Loading…
Reference in a new issue