mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
cbc505e71a
37 changed files with 1225 additions and 543 deletions
|
@ -58,6 +58,7 @@ endforeach()
|
|||
# targets on all platforms
|
||||
add_subdirectory(assignment-client)
|
||||
add_subdirectory(domain-server)
|
||||
add_subdirectory(ice-server)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(tools)
|
||||
|
|
|
@ -12,7 +12,28 @@
|
|||
"name": "id",
|
||||
"label": "Domain ID",
|
||||
"help": "This is your High Fidelity domain ID. If you do not want your domain to be registered in the High Fidelity metaverse you can leave this blank."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "automatic_networking",
|
||||
"label": "Automatic Networking",
|
||||
"help": "This defines how other nodes in the High Fidelity metaverse will be able to reach your domain-server.<br/>If you don't want to deal with any network settings, use full automatic networking.",
|
||||
"default": "disabled",
|
||||
"type": "select",
|
||||
"options": [
|
||||
{
|
||||
"value": "full",
|
||||
"label": "Full: update both the IP address and port to reach my server"
|
||||
},
|
||||
{
|
||||
"value": "ip",
|
||||
"label": "IP Only: update just my IP address, I will open the port manually"
|
||||
},
|
||||
{
|
||||
"value": "disabled",
|
||||
"label": "None: use the network information I have entered for this domain at data.highfidelity.io"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -53,11 +74,11 @@
|
|||
},
|
||||
{
|
||||
"name": "attenuation_per_doubling_in_distance",
|
||||
"label": "Attenuattion per doubling in distance",
|
||||
"label": "Attenuation per doubling in distance",
|
||||
"help": "Factor between 0.0 and 1.0 (0.0: No attenuation, 1.0: extreme attenuation)",
|
||||
"placeholder": "0.18",
|
||||
"default": "0.18",
|
||||
"advanced": true
|
||||
"advanced": false
|
||||
},
|
||||
{
|
||||
"name": "dynamic_jitter_buffer",
|
||||
|
|
|
@ -64,7 +64,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
qRegisterMetaType<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||
|
||||
|
||||
if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) {
|
||||
// we either read a certificate and private key or were not passed one
|
||||
// and completed login or did not need to
|
||||
|
@ -74,8 +74,8 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
loadExistingSessionsFromSettings();
|
||||
|
||||
// check if we have the flag that enables dynamic IP
|
||||
setupDynamicIPAddressUpdating();
|
||||
// setup automatic networking settings with data server
|
||||
setupAutomaticNetworking();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,6 +156,16 @@ bool DomainServer::optionallySetupOAuth() {
|
|||
|
||||
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
||||
_oauthProviderURL = QUrl(settingsMap.value(OAUTH_PROVIDER_URL_OPTION).toString());
|
||||
|
||||
// if we don't have an oauth provider URL then we default to the default node auth url
|
||||
if (_oauthProviderURL.isEmpty()) {
|
||||
_oauthProviderURL = DEFAULT_NODE_AUTH_URL;
|
||||
}
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
accountManager.disableSettingsFilePersistence();
|
||||
accountManager.setAuthURL(_oauthProviderURL);
|
||||
|
||||
_oauthClientID = settingsMap.value(OAUTH_CLIENT_ID_OPTION).toString();
|
||||
_oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV);
|
||||
_hostname = settingsMap.value(REDIRECT_HOSTNAME_OPTION).toString();
|
||||
|
@ -230,41 +240,37 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
|||
addStaticAssignmentsToQueue();
|
||||
}
|
||||
|
||||
const QString HIFI_USERNAME_ENV_KEY = "DOMAIN_SERVER_USERNAME";
|
||||
const QString HIFI_PASSWORD_ENV_KEY = "DOMAIN_SERVER_PASSWORD";
|
||||
|
||||
bool DomainServer::hasOAuthProviderAndAuthInformation() {
|
||||
bool DomainServer::didSetupAccountManagerWithAccessToken() {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
if (accountManager.hasValidAccessToken()) {
|
||||
// we already gave the account manager a valid access token
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_oauthProviderURL.isEmpty()) {
|
||||
// check for an access-token in our settings, can optionally be overidden by env value
|
||||
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
|
||||
const QString ENV_ACCESS_TOKEN_KEY = "DOMAIN_SERVER_ACCESS_TOKEN";
|
||||
|
||||
static bool hasAttemptedAuthWithOAuthProvider = false;
|
||||
QString accessToken = QProcessEnvironment::systemEnvironment().value(ENV_ACCESS_TOKEN_KEY);
|
||||
|
||||
if (!hasAttemptedAuthWithOAuthProvider) {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
accountManager.setAuthURL(_oauthProviderURL);
|
||||
if (accessToken.isEmpty()) {
|
||||
const QVariant* accessTokenVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ACCESS_TOKEN_KEY_PATH);
|
||||
|
||||
if (!accountManager.hasValidAccessToken()) {
|
||||
// we don't have a valid access token so we need to get one
|
||||
// check if we have a username and password set via env
|
||||
QString username = QProcessEnvironment::systemEnvironment().value(HIFI_USERNAME_ENV_KEY);
|
||||
QString password = QProcessEnvironment::systemEnvironment().value(HIFI_PASSWORD_ENV_KEY);
|
||||
|
||||
if (!username.isEmpty() && !password.isEmpty()) {
|
||||
|
||||
accountManager.requestAccessToken(username, password);
|
||||
|
||||
// connect to loginFailed signal from AccountManager so we can quit if that is the case
|
||||
connect(&accountManager, &AccountManager::loginFailed, this, &DomainServer::loginFailed);
|
||||
} else {
|
||||
qDebug() << "Missing access-token or username and password combination. domain-server will now quit.";
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
return false;
|
||||
}
|
||||
if (accessTokenVariant && accessTokenVariant->canConvert(QMetaType::QString)) {
|
||||
accessToken = accessTokenVariant->toString();
|
||||
} else {
|
||||
qDebug() << "A domain-server feature that requires authentication is enabled but no access token is present."
|
||||
<< "Set an access token via the web interface, in your user or master config"
|
||||
<< "at keypath metaverse.access_token or in your ENV at key DOMAIN_SERVER_ACCESS_TOKEN";
|
||||
return false;
|
||||
}
|
||||
|
||||
hasAttemptedAuthWithOAuthProvider = true;
|
||||
}
|
||||
|
||||
// give this access token to the AccountManager
|
||||
accountManager.setAccessTokenForCurrentAuthURL(accessToken);
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
@ -282,7 +288,7 @@ bool DomainServer::optionallySetupAssignmentPayment() {
|
|||
|
||||
if (settingsMap.contains(PAY_FOR_ASSIGNMENTS_OPTION) &&
|
||||
settingsMap.value(PAY_FOR_ASSIGNMENTS_OPTION).toBool() &&
|
||||
hasOAuthProviderAndAuthInformation()) {
|
||||
didSetupAccountManagerWithAccessToken()) {
|
||||
|
||||
qDebug() << "Assignments will be paid for via" << qPrintable(_oauthProviderURL.toString());
|
||||
|
||||
|
@ -304,49 +310,71 @@ bool DomainServer::optionallySetupAssignmentPayment() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void DomainServer::setupDynamicIPAddressUpdating() {
|
||||
const QString ENABLE_DYNAMIC_IP_UPDATING_OPTION = "update-ip";
|
||||
const QString FULL_AUTOMATIC_NETWORKING_VALUE = "full";
|
||||
const QString IP_ONLY_AUTOMATIC_NETWORKING_VALUE = "ip";
|
||||
const QString DISABLED_AUTOMATIC_NETWORKING_VALUE = "disabled";
|
||||
|
||||
void DomainServer::setupAutomaticNetworking() {
|
||||
const QString METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH = "metaverse.automatic_networking";
|
||||
|
||||
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
||||
if (!didSetupAccountManagerWithAccessToken()) {
|
||||
qDebug() << "Cannot setup domain-server automatic networking without an access token.";
|
||||
qDebug() << "Please add an access token to your config file or via the web interface.";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (settingsMap.contains(ENABLE_DYNAMIC_IP_UPDATING_OPTION) &&
|
||||
settingsMap.value(ENABLE_DYNAMIC_IP_UPDATING_OPTION).toBool() &&
|
||||
hasOAuthProviderAndAuthInformation()) {
|
||||
QString automaticNetworkValue =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
|
||||
|
||||
if (automaticNetworkValue == IP_ONLY_AUTOMATIC_NETWORKING_VALUE ||
|
||||
automaticNetworkValue == FULL_AUTOMATIC_NETWORKING_VALUE) {
|
||||
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
const QUuid& domainID = nodeList->getSessionUUID();
|
||||
|
||||
if (!domainID.isNull()) {
|
||||
qDebug() << "domain-server IP address will be updated for domain with ID"
|
||||
qDebug() << "domain-server" << automaticNetworkValue << "automatic networking enabled for ID"
|
||||
<< uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
|
||||
|
||||
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
|
||||
const int STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS = 10 * 1000;
|
||||
|
||||
// setup our timer to check our IP via stun every 30 seconds
|
||||
// setup our timer to check our IP via stun every X seconds
|
||||
QTimer* dynamicIPTimer = new QTimer(this);
|
||||
connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentIPAddressViaSTUN);
|
||||
dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS);
|
||||
connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN);
|
||||
|
||||
// send public socket changes to the data server so nodes can find us at our new IP
|
||||
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendNewPublicSocketToDataServer);
|
||||
|
||||
if (!AccountManager::getInstance().hasValidAccessToken()) {
|
||||
// we don't have an access token to talk to data-web yet, so
|
||||
// check our IP address as soon as we get an AccountManager access token
|
||||
connect(&AccountManager::getInstance(), &AccountManager::loginComplete,
|
||||
this, &DomainServer::requestCurrentIPAddressViaSTUN);
|
||||
if (automaticNetworkValue == 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
|
||||
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::performIPAddressUpdate);
|
||||
} else {
|
||||
// access token good to go, attempt to update our IP now
|
||||
requestCurrentIPAddressViaSTUN();
|
||||
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 sendHeartbeaToIceServer immediately anytime a public address changes
|
||||
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer);
|
||||
|
||||
// tell the data server which type of automatic networking we are using
|
||||
updateNetworkingInfoWithDataServer(automaticNetworkValue);
|
||||
}
|
||||
|
||||
} else {
|
||||
qDebug() << "Cannot enable dynamic domain-server IP address updating without a domain ID."
|
||||
<< "Please add an id to your config.json or pass it with the command line argument --id.";
|
||||
qDebug() << "Failed dynamic IP address update setup. domain-server will now quit.";
|
||||
// attempt to update our sockets now
|
||||
requestCurrentPublicSocketViaSTUN();
|
||||
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
} else {
|
||||
qDebug() << "Cannot enable domain-server automatic networking without a domain ID."
|
||||
<< "Please add an ID to your config file or via the web interface.";
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
updateNetworkingInfoWithDataServer(automaticNetworkValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -556,9 +584,17 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType))
|
||||
|| (isAssignment && matchingQueuedAssignment)) {
|
||||
// this was either not a static assignment or it was and we had a matching one in the queue
|
||||
|
||||
QUuid nodeUUID;
|
||||
|
||||
// create a new session UUID for this node
|
||||
QUuid nodeUUID = QUuid::createUuid();
|
||||
if (_connectingICEPeers.contains(packetUUID) || _connectedICEPeers.contains(packetUUID)) {
|
||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||
nodeUUID = packetUUID;
|
||||
} else {
|
||||
// we got a packetUUID we didn't recognize, just add the node
|
||||
nodeUUID = QUuid::createUuid();
|
||||
}
|
||||
|
||||
|
||||
SharedNodePointer newNode = LimitedNodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType,
|
||||
publicSockAddr, localSockAddr);
|
||||
|
@ -675,6 +711,13 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (nodeInterestList.size() > 0) {
|
||||
|
||||
|
@ -912,18 +955,45 @@ void DomainServer::transactionJSONCallback(const QJsonObject& data) {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestCurrentIPAddressViaSTUN() {
|
||||
void DomainServer::requestCurrentPublicSocketViaSTUN() {
|
||||
LimitedNodeList::getInstance()->sendSTUNRequest();
|
||||
}
|
||||
|
||||
void DomainServer::sendNewPublicSocketToDataServer(const HifiSockAddr& newPublicSockAddr) {
|
||||
QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) {
|
||||
const QString SOCKET_NETWORK_ADDRESS_KEY = "network_address";
|
||||
const QString SOCKET_PORT_KEY = "port";
|
||||
|
||||
QJsonObject socketObject;
|
||||
socketObject[SOCKET_NETWORK_ADDRESS_KEY] = socket.getAddress().toString();
|
||||
socketObject[SOCKET_PORT_KEY] = socket.getPort();
|
||||
|
||||
return socketObject;
|
||||
}
|
||||
|
||||
const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
||||
|
||||
void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) {
|
||||
updateNetworkingInfoWithDataServer(IP_ONLY_AUTOMATIC_NETWORKING_VALUE, newPublicSockAddr.getAddress().toString());
|
||||
}
|
||||
|
||||
void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress) {
|
||||
const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
|
||||
const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID();
|
||||
|
||||
// setup the domain object to send to the data server
|
||||
const QString DOMAIN_JSON_OBJECT = "{\"domain\":{\"network_address\":\"%1\"}}";
|
||||
const QString PUBLIC_NETWORK_ADDRESS_KEY = "network_address";
|
||||
const QString AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
||||
|
||||
QString domainUpdateJSON = DOMAIN_JSON_OBJECT.arg(newPublicSockAddr.getAddress().toString());
|
||||
QJsonObject domainObject;
|
||||
if (!networkAddress.isEmpty()) {
|
||||
domainObject[PUBLIC_NETWORK_ADDRESS_KEY] = networkAddress;
|
||||
}
|
||||
|
||||
qDebug() << "Updating automatic networking setting in domain-server to" << newSetting;
|
||||
|
||||
domainObject[AUTOMATIC_NETWORKING_KEY] = newSetting;
|
||||
|
||||
QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson()));
|
||||
|
||||
AccountManager::getInstance().authenticatedRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
|
||||
QNetworkAccessManager::PutOperation,
|
||||
|
@ -931,6 +1001,86 @@ void DomainServer::sendNewPublicSocketToDataServer(const HifiSockAddr& newPublic
|
|||
domainUpdateJSON.toUtf8());
|
||||
}
|
||||
|
||||
// todo: have data-web respond with ice-server hostname to use
|
||||
|
||||
void DomainServer::performICEUpdates() {
|
||||
sendHearbeatToIceServer();
|
||||
sendICEPingPackets();
|
||||
}
|
||||
|
||||
void DomainServer::sendHearbeatToIceServer() {
|
||||
const HifiSockAddr ICE_SERVER_SOCK_ADDR = HifiSockAddr("ice.highfidelity.io", ICE_SERVER_DEFAULT_PORT);
|
||||
LimitedNodeList::getInstance()->sendHeartbeatToIceServer(ICE_SERVER_SOCK_ADDR);
|
||||
}
|
||||
|
||||
void DomainServer::sendICEPingPackets() {
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
|
||||
QHash<QUuid, NetworkPeer>::iterator peer = _connectingICEPeers.begin();
|
||||
|
||||
while (peer != _connectingICEPeers.end()) {
|
||||
|
||||
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();
|
||||
|
||||
// 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());
|
||||
|
||||
peer->incrementConnectionAttempts();
|
||||
|
||||
// go to next peer in hash
|
||||
++peer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processICEHeartbeatResponse(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;
|
||||
|
||||
while (!iceResponseStream.atEnd()) {
|
||||
iceResponseStream >> receivedPeer;
|
||||
|
||||
if (!_connectedICEPeers.contains(receivedPeer.getUUID())) {
|
||||
if (!_connectingICEPeers.contains(receivedPeer.getUUID())) {
|
||||
qDebug() << "New peer requesting connection being added to hash -" << receivedPeer;
|
||||
}
|
||||
|
||||
_connectingICEPeers[receivedPeer.getUUID()] = receivedPeer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(packet);
|
||||
NetworkPeer sendingPeer = _connectingICEPeers.take(nodeUUID);
|
||||
|
||||
if (!sendingPeer.isNull()) {
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
|
||||
|
@ -972,6 +1122,19 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS
|
|||
case PacketTypeStunResponse:
|
||||
nodeList->processSTUNResponse(receivedPacket);
|
||||
break;
|
||||
case PacketTypeUnverifiedPing: {
|
||||
QByteArray pingReplyPacket = nodeList->constructPingReplyPacket(receivedPacket);
|
||||
nodeList->writeUnverifiedDatagram(pingReplyPacket, senderSockAddr);
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketTypeUnverifiedPingReply: {
|
||||
processICEPingReply(receivedPacket, senderSockAddr);
|
||||
break;
|
||||
}
|
||||
case PacketTypeIceServerHeartbeatResponse:
|
||||
processICEHeartbeatResponse(receivedPacket);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1676,6 +1839,10 @@ 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());
|
||||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
|
|
|
@ -62,15 +62,22 @@ private slots:
|
|||
void setupPendingAssignmentCredits();
|
||||
void sendPendingTransactionsToServer();
|
||||
|
||||
void requestCurrentIPAddressViaSTUN();
|
||||
void sendNewPublicSocketToDataServer(const HifiSockAddr& newPublicSockAddr);
|
||||
void requestCurrentPublicSocketViaSTUN();
|
||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||
void performICEUpdates();
|
||||
void sendHearbeatToIceServer();
|
||||
void sendICEPingPackets();
|
||||
private:
|
||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||
bool optionallySetupOAuth();
|
||||
bool optionallyReadX509KeyAndCertificate();
|
||||
bool hasOAuthProviderAndAuthInformation();
|
||||
bool didSetupAccountManagerWithAccessToken();
|
||||
bool optionallySetupAssignmentPayment();
|
||||
void setupDynamicIPAddressUpdating();
|
||||
|
||||
void setupAutomaticNetworking();
|
||||
void updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress = QString());
|
||||
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||
void processICEHeartbeatResponse(const QByteArray& packet);
|
||||
|
||||
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
|
@ -130,7 +137,13 @@ private:
|
|||
QSet<QUuid> _webAuthenticationStateSet;
|
||||
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
|
||||
|
||||
HifiSockAddr _localSockAddr;
|
||||
|
||||
QHash<QUuid, NetworkPeer> _connectingICEPeers;
|
||||
QHash<QUuid, HifiSockAddr> _connectedICEPeers;
|
||||
|
||||
DomainServerSettingsManager _settingsManager;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_DomainServer_h
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
|
||||
const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json";
|
||||
|
||||
const QString DESCRIPTION_SETTINGS_KEY = "settings";
|
||||
const QString SETTING_DEFAULT_KEY = "default";
|
||||
const QString DESCRIPTION_NAME_KEY = "name";
|
||||
|
||||
DomainServerSettingsManager::DomainServerSettingsManager() :
|
||||
_descriptionArray(),
|
||||
_configMap()
|
||||
|
@ -63,6 +67,36 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
}
|
||||
}
|
||||
|
||||
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString &keyPath) {
|
||||
const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath);
|
||||
|
||||
if (foundValue) {
|
||||
return *foundValue;
|
||||
} else {
|
||||
int dotIndex = keyPath.indexOf('.');
|
||||
|
||||
QString groupKey = keyPath.mid(0, dotIndex);
|
||||
QString settingKey = keyPath.mid(dotIndex + 1);
|
||||
|
||||
foreach(const QVariant& group, _descriptionArray.toVariantList()) {
|
||||
QVariantMap groupMap = group.toMap();
|
||||
|
||||
if (groupMap[DESCRIPTION_NAME_KEY].toString() == groupKey) {
|
||||
foreach(const QVariant& setting, groupMap[DESCRIPTION_SETTINGS_KEY].toList()) {
|
||||
QVariantMap settingMap = setting.toMap();
|
||||
if (settingMap[DESCRIPTION_NAME_KEY].toString() == settingKey) {
|
||||
return settingMap[SETTING_DEFAULT_KEY];
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
const QString SETTINGS_PATH = "/settings.json";
|
||||
|
||||
bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) {
|
||||
|
@ -127,10 +161,6 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
|
|||
return false;
|
||||
}
|
||||
|
||||
const QString DESCRIPTION_SETTINGS_KEY = "settings";
|
||||
const QString SETTING_DEFAULT_KEY = "default";
|
||||
const QString DESCRIPTION_NAME_KEY = "name";
|
||||
|
||||
QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& typeValue, bool isAuthenticated) {
|
||||
QJsonObject responseObject;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||
|
||||
void setupConfigMap(const QStringList& argumentList);
|
||||
QVariant valueOrDefaultValueForKeyPath(const QString& keyPath);
|
||||
|
||||
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
|
||||
private:
|
||||
|
|
9
ice-server/CMakeLists.txt
Normal file
9
ice-server/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
set(TARGET_NAME ice-server)
|
||||
|
||||
# setup the project and link required Qt modules
|
||||
setup_hifi_project(Network)
|
||||
|
||||
# link the shared hifi libraries
|
||||
link_hifi_libraries(networking shared)
|
||||
|
||||
link_shared_dependencies()
|
164
ice-server/src/IceServer.cpp
Normal file
164
ice-server/src/IceServer.cpp
Normal file
|
@ -0,0 +1,164 @@
|
|||
//
|
||||
// IceServer.cpp
|
||||
// ice-server/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-10-01.
|
||||
// Copyright 2014 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 <QTimer>
|
||||
|
||||
#include <LimitedNodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "IceServer.h"
|
||||
|
||||
const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000;
|
||||
const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000;
|
||||
|
||||
IceServer::IceServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_id(QUuid::createUuid()),
|
||||
_serverSocket(),
|
||||
_activePeers()
|
||||
{
|
||||
// start the ice-server socket
|
||||
qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IceServer::sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, QSet<QUuid>& connections) {
|
||||
QSet<QUuid>::iterator peerID = connections.begin();
|
||||
|
||||
QByteArray outgoingPacket(MAX_PACKET_SIZE, 0);
|
||||
int currentPacketSize = populatePacketHeader(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
|
||||
// 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 = populatePacketHeader(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
}
|
||||
|
||||
// append the current peer bytes
|
||||
outgoingPacket.insert(currentPacketSize, peerBytes);
|
||||
currentPacketSize += peerBytes.size();
|
||||
|
||||
++peerID;
|
||||
}
|
||||
}
|
||||
|
||||
// write the last packet
|
||||
_serverSocket.writeDatagram(outgoingPacket.data(), currentPacketSize,
|
||||
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);
|
||||
} else {
|
||||
// we didn't kill this peer, push the iterator forwards
|
||||
++peerItem;
|
||||
}
|
||||
}
|
||||
}
|
39
ice-server/src/IceServer.h
Normal file
39
ice-server/src/IceServer.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// IceServer.h
|
||||
// ice-server/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-10-01.
|
||||
// Copyright 2014 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_IceServer_h
|
||||
#define hifi_IceServer_h
|
||||
|
||||
#include <qcoreapplication.h>
|
||||
#include <qsharedpointer.h>
|
||||
#include <qudpsocket.h>
|
||||
|
||||
#include <NetworkPeer.h>
|
||||
|
||||
typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash;
|
||||
|
||||
class IceServer : public QCoreApplication {
|
||||
public:
|
||||
IceServer(int argc, char* argv[]);
|
||||
private slots:
|
||||
void processDatagrams();
|
||||
void clearInactivePeers();
|
||||
private:
|
||||
|
||||
void sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, QSet<QUuid>& connections);
|
||||
|
||||
QUuid _id;
|
||||
QUdpSocket _serverSocket;
|
||||
NetworkPeerHash _activePeers;
|
||||
QHash<QUuid, QSet<QUuid> > _currentConnections;
|
||||
};
|
||||
|
||||
#endif // hifi_IceServer_h
|
27
ice-server/src/main.cpp
Normal file
27
ice-server/src/main.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// main.cpp
|
||||
// ice-server/src
|
||||
//
|
||||
// Created by Stephen Birarda on 10/01/12.
|
||||
// Copyright 2014 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/QCoreApplication>
|
||||
|
||||
#include <Logging.h>
|
||||
|
||||
#include "IceServer.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
||||
qInstallMessageHandler(Logging::verboseMessageHandler);
|
||||
|
||||
IceServer iceServer(argc, argv);
|
||||
return iceServer.exec();
|
||||
}
|
|
@ -299,9 +299,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
AddressManager& addressManager = AddressManager::getInstance();
|
||||
|
||||
// connect to the domainChangeRequired signal on AddressManager
|
||||
connect(&addressManager, &AddressManager::possibleDomainChangeRequired,
|
||||
// use our MyAvatar position and quat for address manager path
|
||||
addressManager.setPositionGetter(getPositionForPath);
|
||||
addressManager.setOrientationGetter(getOrientationForPath);
|
||||
|
||||
// handle domain change signals from AddressManager
|
||||
connect(&addressManager, &AddressManager::possibleDomainChangeRequiredToHostname,
|
||||
this, &Application::changeDomainHostname);
|
||||
|
||||
connect(&addressManager, &AddressManager::possibleDomainChangeRequiredViaICEForID,
|
||||
&domainHandler, &DomainHandler::setIceServerHostnameAndID);
|
||||
|
||||
_settings = new QSettings(this);
|
||||
_numChangedSettings = 0;
|
||||
|
@ -1792,16 +1799,25 @@ void Application::init() {
|
|||
|
||||
Menu::getInstance()->loadSettings();
|
||||
_audio.setReceivedAudioStreamSettings(Menu::getInstance()->getReceivedAudioStreamSettings());
|
||||
|
||||
qDebug() << "Loaded settings";
|
||||
|
||||
// when --url in command line, teleport to location
|
||||
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
|
||||
int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY);
|
||||
if (urlIndex != -1) {
|
||||
AddressManager::getInstance().handleLookupString(arguments().value(urlIndex + 1));
|
||||
} else {
|
||||
// check if we have a URL in settings to load to jump back to
|
||||
// we load this separate from the other settings so we don't double lookup a URL
|
||||
QSettings* interfaceSettings = lockSettings();
|
||||
QUrl addressURL = interfaceSettings->value(SETTINGS_ADDRESS_KEY).toUrl();
|
||||
|
||||
AddressManager::getInstance().handleLookupString(addressURL.toString());
|
||||
|
||||
unlockSettings();
|
||||
}
|
||||
|
||||
qDebug() << "Loaded settings";
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseEnabled)) {
|
||||
// on OS X we only setup sixense if the user wants it on - this allows running without the hid_init crash
|
||||
|
@ -3424,7 +3440,7 @@ void Application::updateWindowTitle(){
|
|||
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED) ";
|
||||
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
|
||||
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
||||
+ nodeList->getDomainHandler().getHostname() + connectionStatus + buildVersion;
|
||||
+ AddressManager::getInstance().getCurrentDomain() + connectionStatus + buildVersion;
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
if (accountManager.getAccountInfo().hasBalance()) {
|
||||
|
@ -3455,9 +3471,7 @@ void Application::updateLocationInServer() {
|
|||
|
||||
QJsonObject locationObject;
|
||||
|
||||
QString pathString = AddressManager::pathForPositionAndOrientation(_myAvatar->getPosition(),
|
||||
true,
|
||||
_myAvatar->getOrientation());
|
||||
QString pathString = AddressManager::getInstance().currentPath();
|
||||
|
||||
const QString LOCATION_KEY_IN_ROOT = "location";
|
||||
const QString PATH_KEY_IN_LOCATION = "path";
|
||||
|
|
|
@ -140,6 +140,8 @@ class Application : public QApplication {
|
|||
public:
|
||||
static Application* getInstance() { return static_cast<Application*>(QCoreApplication::instance()); }
|
||||
static QString& resourcesPath();
|
||||
static const glm::vec3& getPositionForPath() { return getInstance()->_myAvatar->getPosition(); }
|
||||
static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); }
|
||||
|
||||
Application(int& argc, char** argv, QElapsedTimer &startup_time);
|
||||
~Application();
|
||||
|
|
|
@ -754,7 +754,6 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
scanMenuBar(&loadAction, settings);
|
||||
Application::getInstance()->getAvatar()->loadData(settings);
|
||||
Application::getInstance()->updateWindowTitle();
|
||||
NodeList::getInstance()->loadData(settings);
|
||||
|
||||
// notify that a settings has changed
|
||||
connect(&NodeList::getInstance()->getDomainHandler(), &DomainHandler::hostnameChanged, this, &Menu::bumpSettings);
|
||||
|
@ -815,7 +814,8 @@ void Menu::saveSettings(QSettings* settings) {
|
|||
|
||||
scanMenuBar(&saveAction, settings);
|
||||
Application::getInstance()->getAvatar()->saveData(settings);
|
||||
NodeList::getInstance()->saveData(settings);
|
||||
|
||||
settings->setValue(SETTINGS_ADDRESS_KEY, AddressManager::getInstance().currentAddress());
|
||||
|
||||
if (lockedSettings) {
|
||||
Application::getInstance()->unlockSettings();
|
||||
|
|
|
@ -51,6 +51,8 @@ const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
|
|||
const float MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER = 0.1f;
|
||||
const float MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER = 15.0f;
|
||||
|
||||
const QString SETTINGS_ADDRESS_KEY = "address";
|
||||
|
||||
enum FrustumDrawMode {
|
||||
FRUSTUM_DRAW_MODE_ALL,
|
||||
FRUSTUM_DRAW_MODE_VECTORS,
|
||||
|
|
|
@ -1551,6 +1551,11 @@ public:
|
|||
AxisIndex(int x = -1, int y = -1, int z = -1) : x(x), y(y), z(z) { }
|
||||
};
|
||||
|
||||
static glm::vec3 safeNormalize(const glm::vec3& vector) {
|
||||
float length = glm::length(vector);
|
||||
return (length > 0.0f) ? (vector / length) : vector;
|
||||
}
|
||||
|
||||
int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
||||
if (!info.isLeaf) {
|
||||
return DEFAULT_ORDER;
|
||||
|
@ -1879,7 +1884,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
}
|
||||
}
|
||||
glm::vec3 normal = glm::normalize(axisNormals[0] + axisNormals[1] + axisNormals[2]);
|
||||
glm::vec3 normal = safeNormalize(axisNormals[0] + axisNormals[1] + axisNormals[2]);
|
||||
center /= crossingCount;
|
||||
|
||||
// use a sequence of Givens rotations to perform a QR decomposition
|
||||
|
@ -1967,12 +1972,12 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
vertices.append(point);
|
||||
|
||||
} else {
|
||||
axisNormals[0] = glm::normalize(axisNormals[0]);
|
||||
axisNormals[1] = glm::normalize(axisNormals[1]);
|
||||
axisNormals[2] = glm::normalize(axisNormals[2]);
|
||||
glm::vec3 normalXY(glm::normalize(axisNormals[0] + axisNormals[1]));
|
||||
glm::vec3 normalXZ(glm::normalize(axisNormals[0] + axisNormals[2]));
|
||||
glm::vec3 normalYZ(glm::normalize(axisNormals[1] + axisNormals[2]));
|
||||
axisNormals[0] = safeNormalize(axisNormals[0]);
|
||||
axisNormals[1] = safeNormalize(axisNormals[1]);
|
||||
axisNormals[2] = safeNormalize(axisNormals[2]);
|
||||
glm::vec3 normalXY(safeNormalize(axisNormals[0] + axisNormals[1]));
|
||||
glm::vec3 normalXZ(safeNormalize(axisNormals[0] + axisNormals[2]));
|
||||
glm::vec3 normalYZ(safeNormalize(axisNormals[1] + axisNormals[2]));
|
||||
if (glm::dot(axisNormals[0], normalXY) > CREASE_COS_NORMAL &&
|
||||
glm::dot(axisNormals[1], normalXY) > CREASE_COS_NORMAL) {
|
||||
point.setNormal(normalXY);
|
||||
|
|
|
@ -740,16 +740,8 @@ AnimationDetails MyAvatar::getAnimationDetails(const QString& url) {
|
|||
void MyAvatar::saveData(QSettings* settings) {
|
||||
settings->beginGroup("Avatar");
|
||||
|
||||
settings->setValue("bodyYaw", _bodyYaw);
|
||||
settings->setValue("bodyPitch", _bodyPitch);
|
||||
settings->setValue("bodyRoll", _bodyRoll);
|
||||
|
||||
settings->setValue("headPitch", getHead()->getBasePitch());
|
||||
|
||||
settings->setValue("position_x", _position.x);
|
||||
settings->setValue("position_y", _position.y);
|
||||
settings->setValue("position_z", _position.z);
|
||||
|
||||
settings->setValue("pupilDilation", getHead()->getPupilDilation());
|
||||
|
||||
settings->setValue("leanScale", _leanScale);
|
||||
|
@ -800,19 +792,8 @@ void MyAvatar::saveData(QSettings* settings) {
|
|||
void MyAvatar::loadData(QSettings* settings) {
|
||||
settings->beginGroup("Avatar");
|
||||
|
||||
// in case settings is corrupt or missing loadSetting() will check for NaN
|
||||
_bodyYaw = loadSetting(settings, "bodyYaw", 0.0f);
|
||||
_bodyPitch = loadSetting(settings, "bodyPitch", 0.0f);
|
||||
_bodyRoll = loadSetting(settings, "bodyRoll", 0.0f);
|
||||
|
||||
getHead()->setBasePitch(loadSetting(settings, "headPitch", 0.0f));
|
||||
|
||||
glm::vec3 newPosition;
|
||||
newPosition.x = loadSetting(settings, "position_x", START_LOCATION.x);
|
||||
newPosition.y = loadSetting(settings, "position_y", START_LOCATION.y);
|
||||
newPosition.z = loadSetting(settings, "position_z", START_LOCATION.z);
|
||||
slamPosition(newPosition);
|
||||
|
||||
getHead()->setPupilDilation(loadSetting(settings, "pupilDilation", 0.0f));
|
||||
|
||||
_leanScale = loadSetting(settings, "leanScale", 0.05f);
|
||||
|
|
|
@ -101,7 +101,7 @@ void SixenseManager::initialize() {
|
|||
_sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME);
|
||||
#else
|
||||
const QString SIXENSE_LIBRARY_NAME = "libsixense_x64";
|
||||
QString frameworkSixenseLibrary = QCoreApplication::applicationDirPath() + "../Frameworks/"
|
||||
QString frameworkSixenseLibrary = QCoreApplication::applicationDirPath() + "/../Frameworks/"
|
||||
+ SIXENSE_LIBRARY_NAME;
|
||||
|
||||
_sixenseLibrary = new QLibrary(frameworkSixenseLibrary);
|
||||
|
|
|
@ -29,9 +29,7 @@ QString LocationScriptingInterface::getHref() {
|
|||
}
|
||||
|
||||
QString LocationScriptingInterface::getPathname() {
|
||||
MyAvatar* applicationAvatar = Application::getInstance()->getAvatar();
|
||||
return AddressManager::pathForPositionAndOrientation(applicationAvatar->getPosition(),
|
||||
true, applicationAvatar->getOrientation());
|
||||
return AddressManager::getInstance().currentPath();
|
||||
}
|
||||
|
||||
QString LocationScriptingInterface::getHostname() {
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
JENKINS_URL = 'https://jenkins.below92.com/'
|
||||
GITHUB_HOOK_URL = 'https://github.com/worklist/hifi/'
|
||||
GIT_REPO_URL = 'git@github.com:worklist/hifi.git'
|
||||
HIPCHAT_ROOM = 'High Fidelity'
|
||||
|
||||
def hifiJob(String targetName, Boolean deploy) {
|
||||
job {
|
||||
name "hifi-${targetName}"
|
||||
logRotator(7, -1, -1, -1)
|
||||
|
||||
scm {
|
||||
git(GIT_REPO_URL, 'master') { node ->
|
||||
node << {
|
||||
includedRegions "${targetName}/.*\nlibraries/.*"
|
||||
useShallowClone true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure { project ->
|
||||
project / 'properties' << {
|
||||
'com.coravy.hudson.plugins.github.GithubProjectProperty' {
|
||||
projectUrl GITHUB_HOOK_URL
|
||||
}
|
||||
|
||||
'jenkins.plugins.hipchat.HipChatNotifier_-HipChatJobProperty' {
|
||||
room HIPCHAT_ROOM
|
||||
}
|
||||
|
||||
'hudson.plugins.buildblocker.BuildBlockerProperty' {
|
||||
useBuildBlocker true
|
||||
blockingJobs 'hifi--seed'
|
||||
}
|
||||
}
|
||||
|
||||
project / 'triggers' << 'com.cloudbees.jenkins.GitHubPushTrigger' {
|
||||
spec ''
|
||||
}
|
||||
}
|
||||
|
||||
configure cmakeBuild(targetName, 'make install')
|
||||
|
||||
if (deploy) {
|
||||
publishers {
|
||||
publishScp("${ARTIFACT_DESTINATION}") {
|
||||
entry("**/build/${targetName}/${targetName}", "deploy/${targetName}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure { project ->
|
||||
|
||||
project / 'publishers' << {
|
||||
if (deploy) {
|
||||
'hudson.plugins.postbuildtask.PostbuildTask' {
|
||||
'tasks' {
|
||||
'hudson.plugins.postbuildtask.TaskProperties' {
|
||||
logTexts {
|
||||
'hudson.plugins.postbuildtask.LogProperties' {
|
||||
logText '.'
|
||||
operator 'AND'
|
||||
}
|
||||
}
|
||||
EscalateStatus true
|
||||
RunIfJobSuccessful true
|
||||
script "curl -d 'action=deploy&role=highfidelity-live&revision=${targetName}' https://${ARTIFACT_DESTINATION}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
'jenkins.plugins.hipchat.HipChatNotifier' {
|
||||
jenkinsUrl JENKINS_URL
|
||||
authToken "${HIPCHAT_AUTH_TOKEN}"
|
||||
room HIPCHAT_ROOM
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Closure cmakeBuild(srcDir, instCommand) {
|
||||
return { project ->
|
||||
project / 'builders' / 'hudson.plugins.cmake.CmakeBuilder' {
|
||||
sourceDir '.'
|
||||
buildDir 'build'
|
||||
installDir ''
|
||||
buildType 'RelWithDebInfo'
|
||||
generator 'Unix Makefiles'
|
||||
makeCommand "make ${srcDir}"
|
||||
installCommand instCommand
|
||||
preloadScript ''
|
||||
cmakeArgs ''
|
||||
projectCmakePath '/usr/local/bin/cmake'
|
||||
cleanBuild 'false'
|
||||
cleanInstallDir 'false'
|
||||
builderImpl ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def targets = [
|
||||
'animation-server':true,
|
||||
'assignment-server':true,
|
||||
'assignment-client':true,
|
||||
'domain-server':true,
|
||||
'eve':true,
|
||||
'pairing-server':true,
|
||||
'space-server':true,
|
||||
'voxel-server':true,
|
||||
]
|
||||
|
||||
/* setup all of the target jobs to use the above template */
|
||||
for (target in targets) {
|
||||
queue hifiJob(target.key, target.value)
|
||||
}
|
||||
|
||||
/* setup the OS X interface builds */
|
||||
interfaceOSXJob = hifiJob('interface', false)
|
||||
interfaceOSXJob.with {
|
||||
name 'hifi-interface-osx'
|
||||
|
||||
scm {
|
||||
git(GIT_REPO_URL, 'stable') { node ->
|
||||
node << {
|
||||
includedRegions "interface/.*\nlibraries/.*"
|
||||
useShallowClone true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure { project ->
|
||||
project << {
|
||||
assignedNode 'interface-mini'
|
||||
canRoam false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queue interfaceOSXJob
|
||||
|
||||
/* setup the parametrized build job for builds from jenkins */
|
||||
parameterizedJob = hifiJob('$TARGET', true)
|
||||
parameterizedJob.with {
|
||||
name 'hifi-branch-deploy'
|
||||
parameters {
|
||||
stringParam('GITHUB_USER', '', "Specifies the name of the GitHub user that we're building from.")
|
||||
stringParam('GIT_BRANCH', '', "Specifies the specific branch to build and deploy.")
|
||||
stringParam('HOSTNAME', 'devel.highfidelity.io', "Specifies the hostname to deploy against.")
|
||||
stringParam('TARGET', '', "What server to build specifically")
|
||||
}
|
||||
scm {
|
||||
git('git@github.com:/$GITHUB_USER/hifi.git', '$GIT_BRANCH') { node ->
|
||||
node << {
|
||||
wipeOutWorkspace true
|
||||
useShallowClone true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
configure { project ->
|
||||
def curlCommand = 'curl -d action=hifidevgrid -d "hostname=$HOSTNAME" ' +
|
||||
'-d "github_user=$GITHUB_USER" -d "build_branch=$GIT_BRANCH" ' +
|
||||
"-d \"revision=\$TARGET\" https://${ARTIFACT_DESTINATION}"
|
||||
|
||||
(project / publishers / 'hudson.plugins.postbuildtask.PostbuildTask' /
|
||||
tasks / 'hudson.plugins.postbuildtask.TaskProperties' / script).setValue(curlCommand)
|
||||
}
|
||||
}
|
||||
|
||||
doxygenJob = hifiJob('docs', false)
|
||||
doxygenJob.with {
|
||||
scm {
|
||||
git(GIT_REPO_URL, 'master') { node ->
|
||||
node << {
|
||||
useShallowClone true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure { project ->
|
||||
(project / builders).setValue('')
|
||||
}
|
||||
|
||||
steps {
|
||||
shell('doxygen')
|
||||
}
|
||||
}
|
||||
|
||||
queue doxygenJob
|
|
@ -59,7 +59,8 @@ JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, co
|
|||
AccountManager::AccountManager() :
|
||||
_authURL(),
|
||||
_pendingCallbackMap(),
|
||||
_accountInfo()
|
||||
_accountInfo(),
|
||||
_shouldPersistToSettingsFile(true)
|
||||
{
|
||||
qRegisterMetaType<OAuthAccessToken>("OAuthAccessToken");
|
||||
qRegisterMetaTypeStreamOperators<OAuthAccessToken>("OAuthAccessToken");
|
||||
|
@ -83,14 +84,18 @@ void AccountManager::logout() {
|
|||
|
||||
emit balanceChanged(0);
|
||||
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
|
||||
settings.remove(keyURLString);
|
||||
|
||||
qDebug() << "Removed account info for" << _authURL << "from in-memory accounts and .ini file";
|
||||
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
|
||||
settings.remove(keyURLString);
|
||||
|
||||
qDebug() << "Removed account info for" << _authURL << "from in-memory accounts and .ini file";
|
||||
} else {
|
||||
qDebug() << "Cleared data server account info in account manager.";
|
||||
}
|
||||
|
||||
emit logoutComplete();
|
||||
// the username has changed to blank
|
||||
|
@ -116,28 +121,29 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
|
|||
if (_authURL != authURL) {
|
||||
_authURL = authURL;
|
||||
|
||||
qDebug() << "URL for node authentication has been changed to" << qPrintable(_authURL.toString());
|
||||
qDebug() << "Re-setting authentication flow.";
|
||||
|
||||
// check if there are existing access tokens to load from settings
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
foreach(const QString& key, settings.allKeys()) {
|
||||
// take a key copy to perform the double slash replacement
|
||||
QString keyCopy(key);
|
||||
QUrl keyURL(keyCopy.replace("slashslash", "//"));
|
||||
|
||||
if (keyURL == _authURL) {
|
||||
// pull out the stored access token and store it in memory
|
||||
_accountInfo = settings.value(key).value<DataServerAccountInfo>();
|
||||
qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString());
|
||||
|
||||
// profile info isn't guaranteed to be saved too
|
||||
if (_accountInfo.hasProfile()) {
|
||||
emit profileChanged();
|
||||
} else {
|
||||
requestProfile();
|
||||
qDebug() << "AccountManager URL for authenticated requests has been changed to" << qPrintable(_authURL.toString());
|
||||
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
// check if there are existing access tokens to load from settings
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
foreach(const QString& key, settings.allKeys()) {
|
||||
// take a key copy to perform the double slash replacement
|
||||
QString keyCopy(key);
|
||||
QUrl keyURL(keyCopy.replace("slashslash", "//"));
|
||||
|
||||
if (keyURL == _authURL) {
|
||||
// pull out the stored access token and store it in memory
|
||||
_accountInfo = settings.value(key).value<DataServerAccountInfo>();
|
||||
qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString());
|
||||
|
||||
// profile info isn't guaranteed to be saved too
|
||||
if (_accountInfo.hasProfile()) {
|
||||
emit profileChanged();
|
||||
} else {
|
||||
requestProfile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,8 +320,9 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
|
|||
}
|
||||
|
||||
bool AccountManager::hasValidAccessToken() {
|
||||
|
||||
|
||||
if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) {
|
||||
|
||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||
qDebug() << "An access token is required for requests to" << qPrintable(_authURL.toString());
|
||||
}
|
||||
|
@ -337,6 +344,19 @@ bool AccountManager::checkAndSignalForAccessToken() {
|
|||
return hasToken;
|
||||
}
|
||||
|
||||
void AccountManager::setAccessTokenForCurrentAuthURL(const QString& accessToken) {
|
||||
// clear our current DataServerAccountInfo
|
||||
_accountInfo = DataServerAccountInfo();
|
||||
|
||||
// start the new account info with a new OAuthAccessToken
|
||||
OAuthAccessToken newOAuthToken;
|
||||
newOAuthToken.token = accessToken;
|
||||
|
||||
qDebug() << "Setting new account manager access token to" << accessToken;
|
||||
|
||||
_accountInfo.setAccessToken(newOAuthToken);
|
||||
}
|
||||
|
||||
void AccountManager::requestAccessToken(const QString& login, const QString& password) {
|
||||
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
@ -387,12 +407,14 @@ void AccountManager::requestAccessTokenFinished() {
|
|||
_accountInfo.setAccessTokenFromJSON(rootObject);
|
||||
|
||||
emit loginComplete(rootURL);
|
||||
|
||||
// store this access token into the local settings
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
// store this access token into the local settings
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
}
|
||||
|
||||
requestProfile();
|
||||
}
|
||||
|
@ -436,13 +458,16 @@ void AccountManager::requestProfileFinished() {
|
|||
// the username has changed to whatever came back
|
||||
emit usernameChanged(_accountInfo.getUsername());
|
||||
|
||||
// store the whole profile into the local settings
|
||||
QUrl rootURL = profileReply->url();
|
||||
rootURL.setPath("");
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
// store the whole profile into the local settings
|
||||
QUrl rootURL = profileReply->url();
|
||||
rootURL.setPath("");
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: error handling
|
||||
qDebug() << "Error in response for profile";
|
||||
|
|
|
@ -59,10 +59,13 @@ public:
|
|||
const QUrl& getAuthURL() const { return _authURL; }
|
||||
void setAuthURL(const QUrl& authURL);
|
||||
bool hasAuthEndpoint() { return !_authURL.isEmpty(); }
|
||||
|
||||
void disableSettingsFilePersistence() { _shouldPersistToSettingsFile = false; }
|
||||
|
||||
bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); }
|
||||
bool hasValidAccessToken();
|
||||
Q_INVOKABLE bool checkAndSignalForAccessToken();
|
||||
void setAccessTokenForCurrentAuthURL(const QString& accessToken);
|
||||
|
||||
void requestAccessToken(const QString& login, const QString& password);
|
||||
void requestProfile();
|
||||
|
@ -107,6 +110,7 @@ private:
|
|||
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
|
||||
|
||||
DataServerAccountInfo _accountInfo;
|
||||
bool _shouldPersistToSettingsFile;
|
||||
};
|
||||
|
||||
#endif // hifi_AccountManager_h
|
||||
|
|
|
@ -22,17 +22,46 @@ AddressManager& AddressManager::getInstance() {
|
|||
return sharedInstance;
|
||||
}
|
||||
|
||||
QString AddressManager::pathForPositionAndOrientation(const glm::vec3& position, bool hasOrientation,
|
||||
const glm::quat& orientation) {
|
||||
AddressManager::AddressManager() :
|
||||
_currentDomain(),
|
||||
_positionGetter(NULL),
|
||||
_orientationGetter(NULL)
|
||||
{
|
||||
|
||||
QString pathString = "/" + createByteArray(position);
|
||||
}
|
||||
|
||||
const QUrl AddressManager::currentAddress() {
|
||||
QUrl hifiURL;
|
||||
|
||||
if (hasOrientation) {
|
||||
QString orientationString = createByteArray(orientation);
|
||||
pathString += "/" + orientationString;
|
||||
hifiURL.setScheme(HIFI_URL_SCHEME);
|
||||
hifiURL.setHost(_currentDomain);
|
||||
hifiURL.setPath(currentPath());
|
||||
|
||||
return hifiURL;
|
||||
}
|
||||
|
||||
const QString AddressManager::currentPath(bool withOrientation) const {
|
||||
|
||||
if (_positionGetter) {
|
||||
QString pathString = "/" + createByteArray(_positionGetter());
|
||||
|
||||
if (withOrientation) {
|
||||
if (_orientationGetter) {
|
||||
QString orientationString = createByteArray(_orientationGetter());
|
||||
pathString += "/" + orientationString;
|
||||
} else {
|
||||
qDebug() << "Cannot add orientation to path without a getter for position."
|
||||
<< "Call AdressManager::setOrientationGetter to pass a function that will return a glm::quat";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return pathString;
|
||||
} else {
|
||||
qDebug() << "Cannot create address path without a getter for position."
|
||||
<< "Call AdressManager::setPositionGetter to pass a function that will return a const glm::vec3&";
|
||||
return QString();
|
||||
}
|
||||
|
||||
return pathString;
|
||||
}
|
||||
|
||||
const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
|
||||
|
@ -118,9 +147,26 @@ void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) {
|
|||
QJsonObject domainObject = dataObject[ADDRESS_API_DOMAIN_KEY].toObject();
|
||||
|
||||
const QString DOMAIN_NETWORK_ADDRESS_KEY = "network_address";
|
||||
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
|
||||
const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address";
|
||||
|
||||
emit possibleDomainChangeRequired(domainHostname);
|
||||
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
|
||||
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
|
||||
|
||||
emit possibleDomainChangeRequiredToHostname(domainHostname);
|
||||
} else {
|
||||
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
|
||||
|
||||
const QString DOMAIN_ID_KEY = "id";
|
||||
QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
|
||||
QUuid domainID(domainIDString);
|
||||
|
||||
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
|
||||
}
|
||||
|
||||
// set our current domain to the name that came back
|
||||
const QString DOMAIN_NAME_KEY = "name";
|
||||
|
||||
_currentDomain = domainObject[DOMAIN_NAME_KEY].toString();
|
||||
|
||||
// take the path that came back
|
||||
const QString LOCATION_KEY = "location";
|
||||
|
@ -132,7 +178,7 @@ void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) {
|
|||
} else if (domainObject.contains(LOCATION_KEY)) {
|
||||
returnedPath = domainObject[LOCATION_KEY].toObject()[LOCATION_PATH_KEY].toString();
|
||||
}
|
||||
|
||||
|
||||
bool shouldFaceViewpoint = dataObject.contains(ADDRESS_API_ONLINE_KEY);
|
||||
|
||||
if (!returnedPath.isEmpty()) {
|
||||
|
@ -182,16 +228,25 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
|||
QRegExp hostnameRegex(HOSTNAME_REGEX_STRING, Qt::CaseInsensitive);
|
||||
|
||||
if (hostnameRegex.indexIn(lookupString) != -1) {
|
||||
emit possibleDomainChangeRequired(hostnameRegex.cap(0));
|
||||
QString domainHostname = hostnameRegex.cap(0);
|
||||
|
||||
emit possibleDomainChangeRequiredToHostname(domainHostname);
|
||||
emit lookupResultsFinished();
|
||||
|
||||
_currentDomain = domainHostname;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QRegExp ipAddressRegex(IP_ADDRESS_REGEX_STRING);
|
||||
|
||||
if (ipAddressRegex.indexIn(lookupString) != -1) {
|
||||
emit possibleDomainChangeRequired(ipAddressRegex.cap(0));
|
||||
QString domainIPString = ipAddressRegex.cap(0);
|
||||
|
||||
emit possibleDomainChangeRequiredToHostname(domainIPString);
|
||||
emit lookupResultsFinished();
|
||||
|
||||
_currentDomain = domainIPString;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,15 +21,24 @@
|
|||
|
||||
static const QString HIFI_URL_SCHEME = "hifi";
|
||||
|
||||
typedef const glm::vec3& (*PositionGetter)();
|
||||
typedef glm::quat (*OrientationGetter)();
|
||||
|
||||
class AddressManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static AddressManager& getInstance();
|
||||
|
||||
static QString pathForPositionAndOrientation(const glm::vec3& position, bool hasOrientation = false,
|
||||
const glm::quat& orientation = glm::quat());
|
||||
const QUrl currentAddress();
|
||||
const QString currentPath(bool withOrientation = true) const;
|
||||
|
||||
const QString& getCurrentDomain() const { return _currentDomain; }
|
||||
|
||||
void attemptPlaceNameLookup(const QString& lookupString);
|
||||
|
||||
void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
|
||||
void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
|
||||
|
||||
public slots:
|
||||
void handleLookupString(const QString& lookupString);
|
||||
|
||||
|
@ -40,11 +49,14 @@ signals:
|
|||
void lookupResultsFinished();
|
||||
void lookupResultIsOffline();
|
||||
void lookupResultIsNotFound();
|
||||
void possibleDomainChangeRequired(const QString& newHostname);
|
||||
void possibleDomainChangeRequiredToHostname(const QString& newHostname);
|
||||
void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID);
|
||||
void locationChangeRequired(const glm::vec3& newPosition,
|
||||
bool hasOrientationChange, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation);
|
||||
private:
|
||||
AddressManager();
|
||||
|
||||
const JSONCallbackParameters& apiCallbackParameters();
|
||||
|
||||
bool handleUrl(const QUrl& lookupUrl);
|
||||
|
@ -52,6 +64,10 @@ private:
|
|||
bool handleNetworkAddress(const QString& lookupString);
|
||||
bool handleRelativeViewpoint(const QString& pathSubsection, bool shouldFace = false);
|
||||
bool handleUsername(const QString& lookupString);
|
||||
|
||||
QString _currentDomain;
|
||||
PositionGetter _positionGetter;
|
||||
OrientationGetter _orientationGetter;
|
||||
};
|
||||
|
||||
#endif // hifi_AddressManager_h
|
|
@ -27,6 +27,7 @@ public:
|
|||
DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo);
|
||||
|
||||
const OAuthAccessToken& getAccessToken() const { return _accessToken; }
|
||||
void setAccessToken(const OAuthAccessToken& accessToken) { _accessToken = accessToken; }
|
||||
void setAccessTokenFromJSON(const QJsonObject& jsonObject);
|
||||
|
||||
const QString& getUsername() const { return _username; }
|
||||
|
|
|
@ -25,6 +25,9 @@ DomainHandler::DomainHandler(QObject* parent) :
|
|||
_uuid(),
|
||||
_sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
||||
_assignmentUUID(),
|
||||
_iceClientID(),
|
||||
_iceServerSockAddr(),
|
||||
_icePeer(),
|
||||
_isConnected(false),
|
||||
_handshakeTimer(NULL),
|
||||
_settingsObject(),
|
||||
|
@ -35,7 +38,12 @@ DomainHandler::DomainHandler(QObject* parent) :
|
|||
|
||||
void DomainHandler::clearConnectionInfo() {
|
||||
_uuid = QUuid();
|
||||
|
||||
_iceServerSockAddr = HifiSockAddr();
|
||||
_icePeer = NetworkPeer();
|
||||
|
||||
_isConnected = false;
|
||||
|
||||
emit disconnectedFromDomain();
|
||||
|
||||
if (_handshakeTimer) {
|
||||
|
@ -123,6 +131,31 @@ void DomainHandler::setHostname(const QString& hostname) {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id) {
|
||||
if (id != _uuid) {
|
||||
// re-set the domain info to connect to new domain
|
||||
hardReset();
|
||||
|
||||
setUUID(id);
|
||||
_iceServerSockAddr = HifiSockAddr(iceServerHostname, ICE_SERVER_DEFAULT_PORT);
|
||||
|
||||
// refresh our ICE client UUID to something new
|
||||
_iceClientID = QUuid::createUuid();
|
||||
|
||||
qDebug() << "ICE required to connect to domain via ice server at" << iceServerHostname;
|
||||
}
|
||||
}
|
||||
|
||||
void DomainHandler::activateICELocalSocket() {
|
||||
_sockAddr = _icePeer.getLocalSocket();
|
||||
_hostname = _sockAddr.getAddress().toString();
|
||||
}
|
||||
|
||||
void DomainHandler::activateICEPublicSocket() {
|
||||
_sockAddr = _icePeer.getPublicSocket();
|
||||
_hostname = _sockAddr.getAddress().toString();
|
||||
}
|
||||
|
||||
void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
|
||||
for (int i = 0; i < hostInfo.addresses().size(); i++) {
|
||||
if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
|
@ -152,21 +185,29 @@ void DomainHandler::setIsConnected(bool isConnected) {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainHandler::requestDomainSettings() const {
|
||||
if (_settingsObject.isEmpty()) {
|
||||
// setup the URL required to grab settings JSON
|
||||
QUrl settingsJSONURL;
|
||||
settingsJSONURL.setScheme("http");
|
||||
settingsJSONURL.setHost(_hostname);
|
||||
settingsJSONURL.setPort(DOMAIN_SERVER_HTTP_PORT);
|
||||
settingsJSONURL.setPath("/settings.json");
|
||||
Assignment::Type assignmentType = Assignment::typeForNodeType(NodeList::getInstance()->getOwnerType());
|
||||
settingsJSONURL.setQuery(QString("type=%1").arg(assignmentType));
|
||||
|
||||
qDebug() << "Requesting domain-server settings at" << settingsJSONURL.toString();
|
||||
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(settingsJSONURL));
|
||||
connect(reply, &QNetworkReply::finished, this, &DomainHandler::settingsRequestFinished);
|
||||
void DomainHandler::requestDomainSettings() {
|
||||
NodeType_t owningNodeType = NodeList::getInstance()->getOwnerType();
|
||||
if (owningNodeType == NodeType::Agent) {
|
||||
// for now the agent nodes don't need any settings - this allows local assignment-clients
|
||||
// to connect to a domain that is using automatic networking (since we don't have TCP hole punch yet)
|
||||
_settingsObject = QJsonObject();
|
||||
emit settingsReceived(_settingsObject);
|
||||
} else {
|
||||
if (_settingsObject.isEmpty()) {
|
||||
// setup the URL required to grab settings JSON
|
||||
QUrl settingsJSONURL;
|
||||
settingsJSONURL.setScheme("http");
|
||||
settingsJSONURL.setHost(_hostname);
|
||||
settingsJSONURL.setPort(DOMAIN_SERVER_HTTP_PORT);
|
||||
settingsJSONURL.setPath("/settings.json");
|
||||
Assignment::Type assignmentType = Assignment::typeForNodeType(NodeList::getInstance()->getOwnerType());
|
||||
settingsJSONURL.setQuery(QString("type=%1").arg(assignmentType));
|
||||
|
||||
qDebug() << "Requesting domain-server settings at" << settingsJSONURL.toString();
|
||||
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(settingsJSONURL));
|
||||
connect(reply, &QNetworkReply::finished, this, &DomainHandler::settingsRequestFinished);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,3 +257,20 @@ void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirement
|
|||
|
||||
// initializeDTLSSession();
|
||||
}
|
||||
|
||||
void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) {
|
||||
QDataStream iceResponseStream(icePacket);
|
||||
iceResponseStream.skipRawData(numBytesForPacketHeader(icePacket));
|
||||
|
||||
NetworkPeer packetPeer;
|
||||
iceResponseStream >> packetPeer;
|
||||
|
||||
if (packetPeer.getUUID() != _uuid) {
|
||||
qDebug() << "Received a network peer with ID that does not match current domain. Will not attempt connection.";
|
||||
} else {
|
||||
qDebug() << "Received network peer object for domain -" << packetPeer;
|
||||
_icePeer = packetPeer;
|
||||
|
||||
emit requestICEConnectionAttempt();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
#include "NetworkPeer.h"
|
||||
|
||||
const QString DEFAULT_DOMAIN_HOSTNAME = "sandbox.highfidelity.io";
|
||||
|
||||
|
@ -54,18 +55,28 @@ public:
|
|||
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
||||
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
||||
|
||||
const QUuid& getICEClientID() const { return _iceClientID; }
|
||||
|
||||
bool requiresICE() const { return !_iceServerSockAddr.isNull(); }
|
||||
const HifiSockAddr& getICEServerSockAddr() const { return _iceServerSockAddr; }
|
||||
NetworkPeer& getICEPeer() { return _icePeer; }
|
||||
void activateICELocalSocket();
|
||||
void activateICEPublicSocket();
|
||||
|
||||
bool isConnected() const { return _isConnected; }
|
||||
void setIsConnected(bool isConnected);
|
||||
|
||||
bool hasSettings() const { return !_settingsObject.isEmpty(); }
|
||||
void requestDomainSettings() const;
|
||||
void requestDomainSettings();
|
||||
const QJsonObject& getSettingsObject() const { return _settingsObject; }
|
||||
|
||||
void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket);
|
||||
void processICEResponsePacket(const QByteArray& icePacket);
|
||||
|
||||
void softReset();
|
||||
public slots:
|
||||
void setHostname(const QString& hostname);
|
||||
void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id);
|
||||
|
||||
private slots:
|
||||
void completedHostnameLookup(const QHostInfo& hostInfo);
|
||||
|
@ -74,6 +85,7 @@ signals:
|
|||
void hostnameChanged(const QString& hostname);
|
||||
void connectedToDomain(const QString& hostname);
|
||||
void disconnectedFromDomain();
|
||||
void requestICEConnectionAttempt();
|
||||
|
||||
void settingsReceived(const QJsonObject& domainSettingsObject);
|
||||
void settingsReceiveFail();
|
||||
|
@ -85,6 +97,9 @@ private:
|
|||
QString _hostname;
|
||||
HifiSockAddr _sockAddr;
|
||||
QUuid _assignmentUUID;
|
||||
QUuid _iceClientID;
|
||||
HifiSockAddr _iceServerSockAddr;
|
||||
NetworkPeer _icePeer;
|
||||
bool _isConnected;
|
||||
QTimer* _handshakeTimer;
|
||||
QJsonObject _settingsObject;
|
||||
|
|
|
@ -459,6 +459,39 @@ unsigned LimitedNodeList::broadcastToNodes(const QByteArray& packet, const NodeS
|
|||
return n;
|
||||
}
|
||||
|
||||
QByteArray LimitedNodeList::constructPingPacket(PingType_t pingType, bool isVerified, const QUuid& packetHeaderID) {
|
||||
QByteArray pingPacket = byteArrayWithPopulatedHeader(isVerified ? PacketTypePing : PacketTypeUnverifiedPing,
|
||||
packetHeaderID);
|
||||
|
||||
QDataStream packetStream(&pingPacket, QIODevice::Append);
|
||||
|
||||
packetStream << pingType;
|
||||
packetStream << usecTimestampNow();
|
||||
|
||||
return pingPacket;
|
||||
}
|
||||
|
||||
QByteArray LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacket, const QUuid& packetHeaderID) {
|
||||
QDataStream pingPacketStream(pingPacket);
|
||||
pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
|
||||
|
||||
PingType_t typeFromOriginalPing;
|
||||
pingPacketStream >> typeFromOriginalPing;
|
||||
|
||||
quint64 timeFromOriginalPing;
|
||||
pingPacketStream >> timeFromOriginalPing;
|
||||
|
||||
PacketType replyType = (packetTypeForPacket(pingPacket) == PacketTypePing)
|
||||
? PacketTypePingReply : PacketTypeUnverifiedPingReply;
|
||||
|
||||
QByteArray replyPacket = byteArrayWithPopulatedHeader(replyType, packetHeaderID);
|
||||
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
||||
|
||||
packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow();
|
||||
|
||||
return replyPacket;
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::soloNodeOfType(char nodeType) {
|
||||
|
||||
if (memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
|
||||
|
@ -618,3 +651,25 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr,
|
||||
QUuid headerID, const QUuid& connectionRequestID) {
|
||||
|
||||
if (headerID.isNull()) {
|
||||
headerID = _sessionUUID;
|
||||
}
|
||||
|
||||
QByteArray iceRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeIceServerHeartbeat, headerID);
|
||||
QDataStream iceDataStream(&iceRequestByteArray, QIODevice::Append);
|
||||
|
||||
iceDataStream << _publicSockAddr << HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort());
|
||||
|
||||
if (!connectionRequestID.isNull()) {
|
||||
iceDataStream << connectionRequestID;
|
||||
|
||||
qDebug() << "Sending packet to ICE server to request connection info for peer with ID"
|
||||
<< uuidStringWithoutCurlyBraces(connectionRequestID);
|
||||
}
|
||||
|
||||
writeUnverifiedDatagram(iceRequestByteArray, iceServerSockAddr);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,14 @@ typedef QSharedPointer<Node> SharedNodePointer;
|
|||
typedef QHash<QUuid, SharedNodePointer> NodeHash;
|
||||
Q_DECLARE_METATYPE(SharedNodePointer)
|
||||
|
||||
typedef quint8 PingType_t;
|
||||
namespace PingType {
|
||||
const PingType_t Agnostic = 0;
|
||||
const PingType_t Local = 1;
|
||||
const PingType_t Public = 2;
|
||||
const PingType_t Symmetric = 3;
|
||||
}
|
||||
|
||||
class LimitedNodeList : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -104,8 +112,15 @@ public:
|
|||
void getPacketStats(float &packetsPerSecond, float &bytesPerSecond);
|
||||
void resetPacketStats();
|
||||
|
||||
QByteArray constructPingPacket(PingType_t pingType = PingType::Agnostic, bool isVerified = true,
|
||||
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());
|
||||
public slots:
|
||||
void reset();
|
||||
void eraseAllNodes();
|
||||
|
|
99
libraries/networking/src/NetworkPeer.cpp
Normal file
99
libraries/networking/src/NetworkPeer.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// NetworkPeer.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-10-02.
|
||||
// Copyright 2014 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 <qdatetime.h>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "NetworkPeer.h"
|
||||
|
||||
NetworkPeer::NetworkPeer() :
|
||||
_uuid(),
|
||||
_publicSocket(),
|
||||
_localSocket(),
|
||||
_wakeTimestamp(QDateTime::currentMSecsSinceEpoch()),
|
||||
_lastHeardMicrostamp(usecTimestampNow()),
|
||||
_connectionAttempts(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NetworkPeer::NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) :
|
||||
_uuid(uuid),
|
||||
_publicSocket(publicSocket),
|
||||
_localSocket(localSocket),
|
||||
_wakeTimestamp(QDateTime::currentMSecsSinceEpoch()),
|
||||
_lastHeardMicrostamp(usecTimestampNow()),
|
||||
_connectionAttempts(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NetworkPeer::NetworkPeer(const NetworkPeer& otherPeer) {
|
||||
|
||||
_uuid = otherPeer._uuid;
|
||||
_publicSocket = otherPeer._publicSocket;
|
||||
_localSocket = otherPeer._localSocket;
|
||||
|
||||
_wakeTimestamp = otherPeer._wakeTimestamp;
|
||||
_lastHeardMicrostamp = otherPeer._lastHeardMicrostamp;
|
||||
_connectionAttempts = otherPeer._connectionAttempts;
|
||||
}
|
||||
|
||||
NetworkPeer& NetworkPeer::operator=(const NetworkPeer& otherPeer) {
|
||||
NetworkPeer temp(otherPeer);
|
||||
swap(temp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
QByteArray NetworkPeer::toByteArray() const {
|
||||
QByteArray peerByteArray;
|
||||
|
||||
QDataStream peerStream(&peerByteArray, QIODevice::Append);
|
||||
peerStream << *this;
|
||||
|
||||
return peerByteArray;
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& out, const NetworkPeer& peer) {
|
||||
out << peer._uuid;
|
||||
out << peer._publicSocket;
|
||||
out << peer._localSocket;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& in, NetworkPeer& peer) {
|
||||
in >> peer._uuid;
|
||||
in >> peer._publicSocket;
|
||||
in >> peer._localSocket;
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const NetworkPeer &peer) {
|
||||
debug << uuidStringWithoutCurlyBraces(peer.getUUID())
|
||||
<< "- public:" << peer.getPublicSocket()
|
||||
<< "- local:" << peer.getLocalSocket();
|
||||
return debug;
|
||||
}
|
77
libraries/networking/src/NetworkPeer.h
Normal file
77
libraries/networking/src/NetworkPeer.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// NetworkPeer.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-10-02.
|
||||
// Copyright 2014 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_NetworkPeer_h
|
||||
#define hifi_NetworkPeer_h
|
||||
|
||||
#include <qobject.h>
|
||||
#include <quuid.h>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
const QString ICE_SERVER_HOSTNAME = "localhost";
|
||||
const int ICE_SERVER_DEFAULT_PORT = 7337;
|
||||
const int ICE_HEARBEAT_INTERVAL_MSECS = 2 * 1000;
|
||||
const int MAX_ICE_CONNECTION_ATTEMPTS = 5;
|
||||
|
||||
class NetworkPeer : public QObject {
|
||||
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);
|
||||
|
||||
bool isNull() const { return _uuid.isNull(); }
|
||||
|
||||
const QUuid& getUUID() const { return _uuid; }
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
|
||||
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; }
|
||||
|
||||
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; }
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& out, const NetworkPeer& peer);
|
||||
friend QDataStream& operator>>(QDataStream& in, NetworkPeer& peer);
|
||||
protected:
|
||||
QUuid _uuid;
|
||||
|
||||
HifiSockAddr _publicSocket;
|
||||
HifiSockAddr _localSocket;
|
||||
|
||||
quint64 _wakeTimestamp;
|
||||
quint64 _lastHeardMicrostamp;
|
||||
|
||||
int _connectionAttempts;
|
||||
private:
|
||||
void swap(NetworkPeer& otherPeer);
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, const NetworkPeer &peer);
|
||||
typedef QSharedPointer<NetworkPeer> SharedNetworkPeer;
|
||||
|
||||
#endif // hifi_NetworkPeer_h
|
|
@ -12,11 +12,12 @@
|
|||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <UUID.h>
|
||||
|
||||
#include "Node.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
const QString UNKNOWN_NodeType_t_NAME = "Unknown";
|
||||
|
@ -44,14 +45,10 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
|
|||
}
|
||||
|
||||
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) :
|
||||
NetworkPeer(uuid, publicSocket, localSocket),
|
||||
_type(type),
|
||||
_uuid(uuid),
|
||||
_wakeTimestamp(QDateTime::currentMSecsSinceEpoch()),
|
||||
_lastHeardMicrostamp(usecTimestampNow()),
|
||||
_publicSocket(publicSocket),
|
||||
_localSocket(localSocket),
|
||||
_symmetricSocket(),
|
||||
_activeSocket(NULL),
|
||||
_symmetricSocket(),
|
||||
_connectionSecret(),
|
||||
_bytesReceivedMovingAverage(NULL),
|
||||
_linkedData(NULL),
|
||||
|
@ -68,48 +65,6 @@ Node::~Node() {
|
|||
delete _bytesReceivedMovingAverage;
|
||||
}
|
||||
|
||||
void Node::setPublicSocket(const HifiSockAddr& publicSocket) {
|
||||
if (_activeSocket == &_publicSocket) {
|
||||
// if the active socket was the public socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
_publicSocket = publicSocket;
|
||||
}
|
||||
|
||||
void Node::setLocalSocket(const HifiSockAddr& localSocket) {
|
||||
if (_activeSocket == &_localSocket) {
|
||||
// if the active socket was the local socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
_localSocket = localSocket;
|
||||
}
|
||||
|
||||
void Node::setSymmetricSocket(const HifiSockAddr& symmetricSocket) {
|
||||
if (_activeSocket == &_symmetricSocket) {
|
||||
// if the active socket was the symmetric socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
_symmetricSocket = symmetricSocket;
|
||||
}
|
||||
|
||||
void Node::activateLocalSocket() {
|
||||
qDebug() << "Activating local socket for node" << *this;
|
||||
_activeSocket = &_localSocket;
|
||||
}
|
||||
|
||||
void Node::activatePublicSocket() {
|
||||
qDebug() << "Activating public socket for node" << *this;
|
||||
_activeSocket = &_publicSocket;
|
||||
}
|
||||
|
||||
void Node::activateSymmetricSocket() {
|
||||
qDebug() << "Activating symmetric socket for node" << *this;
|
||||
_activeSocket = &_symmetricSocket;
|
||||
}
|
||||
|
||||
void Node::recordBytesReceived(int bytesReceived) {
|
||||
if (!_bytesReceivedMovingAverage) {
|
||||
_bytesReceivedMovingAverage = new SimpleMovingAverage(100);
|
||||
|
@ -139,6 +94,48 @@ void Node::updateClockSkewUsec(int clockSkewSample) {
|
|||
_clockSkewUsec = (int)_clockSkewMovingPercentile.getValueAtPercentile();
|
||||
}
|
||||
|
||||
void Node::setPublicSocket(const HifiSockAddr& publicSocket) {
|
||||
if (_activeSocket == &_publicSocket) {
|
||||
// if the active socket was the public socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
_publicSocket = publicSocket;
|
||||
}
|
||||
|
||||
void Node::setLocalSocket(const HifiSockAddr& localSocket) {
|
||||
if (_activeSocket == &_localSocket) {
|
||||
// if the active socket was the local socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
_localSocket = localSocket;
|
||||
}
|
||||
|
||||
void Node::setSymmetricSocket(const HifiSockAddr& symmetricSocket) {
|
||||
if (_activeSocket == &_symmetricSocket) {
|
||||
// if the active socket was the symmetric socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
_symmetricSocket = symmetricSocket;
|
||||
}
|
||||
|
||||
void Node::activateLocalSocket() {
|
||||
qDebug() << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
_activeSocket = &_localSocket;
|
||||
}
|
||||
|
||||
void Node::activatePublicSocket() {
|
||||
qDebug() << "Activating public socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
_activeSocket = &_publicSocket;
|
||||
}
|
||||
|
||||
void Node::activateSymmetricSocket() {
|
||||
qDebug() << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
|
||||
_activeSocket = &_symmetricSocket;
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& out, const Node& node) {
|
||||
out << node._type;
|
||||
out << node._uuid;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QMutex>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
#include "NetworkPeer.h"
|
||||
#include "NodeData.h"
|
||||
#include "SimpleMovingAverage.h"
|
||||
#include "MovingPercentile.h"
|
||||
|
@ -44,7 +45,7 @@ namespace NodeType {
|
|||
const QString& getNodeTypeName(NodeType_t nodeType);
|
||||
}
|
||||
|
||||
class Node : public QObject {
|
||||
class Node : public NetworkPeer {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
|
||||
|
@ -55,28 +56,6 @@ public:
|
|||
|
||||
char getType() const { return _type; }
|
||||
void setType(char type) { _type = type; }
|
||||
|
||||
const QUuid& getUUID() const { return _uuid; }
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
|
||||
quint64 getWakeTimestamp() const { return _wakeTimestamp; }
|
||||
void setWakeTimestamp(quint64 wakeTimestamp) { _wakeTimestamp = wakeTimestamp; }
|
||||
|
||||
quint64 getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
|
||||
void setLastHeardMicrostamp(quint64 lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
|
||||
|
||||
const HifiSockAddr& getPublicSocket() const { return _publicSocket; }
|
||||
void setPublicSocket(const HifiSockAddr& publicSocket);
|
||||
const HifiSockAddr& getLocalSocket() const { return _localSocket; }
|
||||
void setLocalSocket(const HifiSockAddr& localSocket);
|
||||
const HifiSockAddr& getSymmetricSocket() const { return _symmetricSocket; }
|
||||
void setSymmetricSocket(const HifiSockAddr& symmetricSocket);
|
||||
|
||||
const HifiSockAddr* getActiveSocket() const { return _activeSocket; }
|
||||
|
||||
void activatePublicSocket();
|
||||
void activateLocalSocket();
|
||||
void activateSymmetricSocket();
|
||||
|
||||
const QUuid& getConnectionSecret() const { return _connectionSecret; }
|
||||
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
|
||||
|
@ -98,6 +77,17 @@ public:
|
|||
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 activatePublicSocket();
|
||||
void activateLocalSocket();
|
||||
void activateSymmetricSocket();
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& out, const Node& node);
|
||||
friend QDataStream& operator>>(QDataStream& in, Node& node);
|
||||
|
||||
|
@ -107,13 +97,10 @@ private:
|
|||
Node& operator=(Node otherNode);
|
||||
|
||||
NodeType_t _type;
|
||||
QUuid _uuid;
|
||||
quint64 _wakeTimestamp;
|
||||
quint64 _lastHeardMicrostamp;
|
||||
HifiSockAddr _publicSocket;
|
||||
HifiSockAddr _localSocket;
|
||||
HifiSockAddr _symmetricSocket;
|
||||
|
||||
HifiSockAddr* _activeSocket;
|
||||
HifiSockAddr _symmetricSocket;
|
||||
|
||||
QUuid _connectionSecret;
|
||||
SimpleMovingAverage* _bytesReceivedMovingAverage;
|
||||
NodeData* _linkedData;
|
||||
|
|
|
@ -64,6 +64,9 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
|
|||
// clear our NodeList when the domain changes
|
||||
connect(&_domainHandler, &DomainHandler::hostnameChanged, this, &NodeList::reset);
|
||||
|
||||
// handle ICE signal from DS so connection is attempted immediately
|
||||
connect(&_domainHandler, &DomainHandler::requestICEConnectionAttempt, this, &NodeList::handleICEConnectionToDomainServer);
|
||||
|
||||
// clear our NodeList when logout is requested
|
||||
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
|
||||
}
|
||||
|
@ -123,6 +126,10 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
|||
_domainHandler.parseDTLSRequirementPacket(packet);
|
||||
break;
|
||||
}
|
||||
case PacketTypeIceServerHeartbeatResponse: {
|
||||
_domainHandler.processICEResponsePacket(packet);
|
||||
break;
|
||||
}
|
||||
case PacketTypePing: {
|
||||
// send back a reply
|
||||
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
|
||||
|
@ -158,6 +165,26 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
|||
|
||||
break;
|
||||
}
|
||||
case PacketTypeUnverifiedPing: {
|
||||
// send back a reply
|
||||
QByteArray replyPacket = constructPingReplyPacket(packet, _domainHandler.getICEClientID());
|
||||
writeUnverifiedDatagram(replyPacket, senderSockAddr);
|
||||
break;
|
||||
}
|
||||
case PacketTypeUnverifiedPingReply: {
|
||||
qDebug() << "Received reply from domain-server on" << senderSockAddr;
|
||||
|
||||
// for now we're unsafely assuming this came back from the domain
|
||||
if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) {
|
||||
qDebug() << "Connecting to domain using local socket";
|
||||
_domainHandler.activateICELocalSocket();
|
||||
} else if (senderSockAddr == _domainHandler.getICEPeer().getPublicSocket()) {
|
||||
qDebug() << "Conecting to domain using public socket";
|
||||
_domainHandler.activateICEPublicSocket();
|
||||
} else {
|
||||
qDebug() << "Reply does not match either local or public socket for domain. Will not connect.";
|
||||
}
|
||||
}
|
||||
case PacketTypeStunResponse: {
|
||||
// a STUN packet begins with 00, we've checked the second zero with packetVersionMatch
|
||||
// pass it along so it can be processed into our public address and port
|
||||
|
@ -242,8 +269,10 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
// 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();
|
||||
} else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) {
|
||||
handleICEConnectionToDomainServer();
|
||||
} else if (!_domainHandler.getIP().isNull()) {
|
||||
|
||||
|
||||
bool isUsingDTLS = false;
|
||||
|
||||
PacketType domainPacketType = !_domainHandler.isConnected()
|
||||
|
@ -256,10 +285,17 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
// construct the DS check in packet
|
||||
QUuid packetUUID = _sessionUUID;
|
||||
|
||||
if (!_domainHandler.getAssignmentUUID().isNull() && domainPacketType == PacketTypeDomainConnectRequest) {
|
||||
// this is a connect request and we're an assigned node
|
||||
// so set our packetUUID as the assignment UUID
|
||||
packetUUID = _domainHandler.getAssignmentUUID();
|
||||
if (domainPacketType == PacketTypeDomainConnectRequest) {
|
||||
if (!_domainHandler.getAssignmentUUID().isNull()) {
|
||||
// this is a connect request and we're an assigned node
|
||||
// so set our packetUUID as the assignment UUID
|
||||
packetUUID = _domainHandler.getAssignmentUUID();
|
||||
} else if (_domainHandler.requiresICE()) {
|
||||
// this is a connect request and we're an interface client
|
||||
// that used ice to discover the DS
|
||||
// so send our ICE client UUID with the connect request
|
||||
packetUUID = _domainHandler.getICEClientID();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID);
|
||||
|
@ -267,8 +303,8 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
|
||||
// pack our data to send to the domain-server
|
||||
packetStream << _ownerType << _publicSockAddr
|
||||
<< HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort())
|
||||
<< (quint8) _nodeTypesOfInterest.size();
|
||||
<< HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort())
|
||||
<< (quint8) _nodeTypesOfInterest.size();
|
||||
|
||||
// copy over the bytes for node types of interest, if required
|
||||
foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) {
|
||||
|
@ -298,6 +334,30 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
}
|
||||
}
|
||||
|
||||
void NodeList::handleICEConnectionToDomainServer() {
|
||||
if (_domainHandler.getICEPeer().isNull()
|
||||
|| _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {
|
||||
|
||||
_domainHandler.getICEPeer().resetConnectionAttemps();
|
||||
|
||||
LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(),
|
||||
_domainHandler.getICEClientID(),
|
||||
_domainHandler.getUUID());
|
||||
} else {
|
||||
qDebug() << "Sending ping packets to establish connectivity with domain-server with ID"
|
||||
<< uuidStringWithoutCurlyBraces(_domainHandler.getUUID());
|
||||
|
||||
// send the ping packet to the local and public sockets for this nodfe
|
||||
QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID());
|
||||
writeUnverifiedDatagram(localPingPacket, _domainHandler.getICEPeer().getLocalSocket());
|
||||
|
||||
QByteArray publicPingPacket = constructPingPacket(PingType::Public, false, _domainHandler.getICEClientID());
|
||||
writeUnverifiedDatagram(publicPingPacket, _domainHandler.getICEPeer().getPublicSocket());
|
||||
|
||||
_domainHandler.getICEPeer().incrementConnectionAttempts();
|
||||
}
|
||||
}
|
||||
|
||||
int NodeList::processDomainServerList(const QByteArray& packet) {
|
||||
// this is a packet from the domain server, reset the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
@ -369,35 +429,6 @@ void NodeList::sendAssignment(Assignment& assignment) {
|
|||
_nodeSocket.writeDatagram(packet, assignmentServerSocket->getAddress(), assignmentServerSocket->getPort());
|
||||
}
|
||||
|
||||
QByteArray NodeList::constructPingPacket(PingType_t pingType) {
|
||||
QByteArray pingPacket = byteArrayWithPopulatedHeader(PacketTypePing);
|
||||
|
||||
QDataStream packetStream(&pingPacket, QIODevice::Append);
|
||||
|
||||
packetStream << pingType;
|
||||
packetStream << usecTimestampNow();
|
||||
|
||||
return pingPacket;
|
||||
}
|
||||
|
||||
QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
|
||||
QDataStream pingPacketStream(pingPacket);
|
||||
pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
|
||||
|
||||
PingType_t typeFromOriginalPing;
|
||||
pingPacketStream >> typeFromOriginalPing;
|
||||
|
||||
quint64 timeFromOriginalPing;
|
||||
pingPacketStream >> timeFromOriginalPing;
|
||||
|
||||
QByteArray replyPacket = byteArrayWithPopulatedHeader(PacketTypePingReply);
|
||||
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
||||
|
||||
packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow();
|
||||
|
||||
return replyPacket;
|
||||
}
|
||||
|
||||
void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
|
@ -440,34 +471,3 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con
|
|||
sendingNode->activateSymmetricSocket();
|
||||
}
|
||||
}
|
||||
|
||||
const QString QSETTINGS_GROUP_NAME = "NodeList";
|
||||
const QString DOMAIN_SERVER_SETTING_KEY = "domainServerHostname";
|
||||
|
||||
void NodeList::loadData(QSettings *settings) {
|
||||
settings->beginGroup(DOMAIN_SERVER_SETTING_KEY);
|
||||
|
||||
QString domainServerHostname = settings->value(DOMAIN_SERVER_SETTING_KEY).toString();
|
||||
|
||||
if (domainServerHostname.size() > 0) {
|
||||
_domainHandler.setHostname(domainServerHostname);
|
||||
} else {
|
||||
_domainHandler.setHostname(DEFAULT_DOMAIN_HOSTNAME);
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void NodeList::saveData(QSettings* settings) {
|
||||
settings->beginGroup(DOMAIN_SERVER_SETTING_KEY);
|
||||
|
||||
if (_domainHandler.getHostname() != DEFAULT_DOMAIN_HOSTNAME) {
|
||||
// the user is using a different hostname, store it
|
||||
settings->setValue(DOMAIN_SERVER_SETTING_KEY, QVariant(_domainHandler.getHostname()));
|
||||
} else {
|
||||
// the user has switched back to default, remove the current setting
|
||||
settings->remove(DOMAIN_SERVER_SETTING_KEY);
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
|
|
@ -37,14 +37,6 @@ const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
|
|||
|
||||
class Assignment;
|
||||
|
||||
typedef quint8 PingType_t;
|
||||
namespace PingType {
|
||||
const PingType_t Agnostic = 0;
|
||||
const PingType_t Local = 1;
|
||||
const PingType_t Public = 2;
|
||||
const PingType_t Symmetric = 3;
|
||||
}
|
||||
|
||||
class NodeList : public LimitedNodeList {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -69,14 +61,8 @@ public:
|
|||
|
||||
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
|
||||
void sendAssignment(Assignment& assignment);
|
||||
|
||||
QByteArray constructPingPacket(PingType_t pingType = PingType::Agnostic);
|
||||
QByteArray constructPingReplyPacket(const QByteArray& pingPacket);
|
||||
|
||||
void pingPunchForInactiveNode(const SharedNodePointer& node);
|
||||
|
||||
void loadData(QSettings* settings);
|
||||
void saveData(QSettings* settings);
|
||||
public slots:
|
||||
void reset();
|
||||
void sendDomainServerCheckIn();
|
||||
|
@ -91,6 +77,8 @@ private:
|
|||
void sendSTUNRequest();
|
||||
bool processSTUNResponse(const QByteArray& packet);
|
||||
|
||||
void handleICEConnectionToDomainServer();
|
||||
|
||||
void processDomainServerAuthRequest(const QByteArray& packet);
|
||||
void requestAuthForDomainServer();
|
||||
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
OAuthAccessToken::OAuthAccessToken() :
|
||||
token(),
|
||||
refreshToken(),
|
||||
expiryTimestamp(0),
|
||||
expiryTimestamp(-1),
|
||||
tokenType()
|
||||
{
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
|
||||
QByteArray authorizationHeaderValue() const { return QString("Bearer %1").arg(token).toUtf8(); }
|
||||
|
||||
bool isExpired() const { return expiryTimestamp <= QDateTime::currentMSecsSinceEpoch(); }
|
||||
bool isExpired() const { return expiryTimestamp != -1 && expiryTimestamp <= QDateTime::currentMSecsSinceEpoch(); }
|
||||
|
||||
QString token;
|
||||
QString refreshToken;
|
||||
|
|
|
@ -70,7 +70,11 @@ enum PacketType {
|
|||
PacketTypeVoxelEditNack,
|
||||
PacketTypeParticleEditNack,
|
||||
PacketTypeEntityEditNack, // 48
|
||||
PacketTypeSignedTransactionPayment
|
||||
PacketTypeSignedTransactionPayment,
|
||||
PacketTypeIceServerHeartbeat,
|
||||
PacketTypeIceServerHeartbeatResponse,
|
||||
PacketTypeUnverifiedPing,
|
||||
PacketTypeUnverifiedPingReply
|
||||
};
|
||||
|
||||
typedef char PacketVersion;
|
||||
|
@ -80,7 +84,9 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
|||
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest
|
||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
||||
<< PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeEntityQuery
|
||||
<< PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeParticleEditNack << PacketTypeEntityEditNack;
|
||||
<< PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeParticleEditNack << PacketTypeEntityEditNack
|
||||
<< PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse
|
||||
<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply;
|
||||
|
||||
const int NUM_BYTES_MD5_HASH = 16;
|
||||
const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
|
||||
|
|
Loading…
Reference in a new issue