mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 07:23:51 +02:00
Merge branch 'master' into ajt/new-anim-system
This commit is contained in:
commit
a04f356857
71 changed files with 1091 additions and 880 deletions
|
@ -19,6 +19,4 @@ endif (UNIX)
|
||||||
|
|
||||||
include_application_version()
|
include_application_version()
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
|
|
|
@ -33,4 +33,7 @@ macro(LINK_HIFI_LIBRARIES)
|
||||||
list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES ${LINKED_TARGET_DEPENDENCY_INCLUDES})
|
list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES ${LINKED_TARGET_DEPENDENCY_INCLUDES})
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
setup_memory_debugger()
|
||||||
|
|
||||||
endmacro(LINK_HIFI_LIBRARIES)
|
endmacro(LINK_HIFI_LIBRARIES)
|
|
@ -15,6 +15,8 @@ macro(SETUP_HIFI_LIBRARY)
|
||||||
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c")
|
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c")
|
||||||
list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS})
|
list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS})
|
||||||
|
|
||||||
|
setup_memory_debugger()
|
||||||
|
|
||||||
# create a library and set the property so it can be referenced later
|
# create a library and set the property so it can be referenced later
|
||||||
if (${${TARGET_NAME}_SHARED})
|
if (${${TARGET_NAME}_SHARED})
|
||||||
add_library(${TARGET_NAME} SHARED ${LIB_SRCS} ${AUTOMTC_SRC} ${AUTOSCRIBE_SHADER_LIB_SRC} ${QT_RESOURCES_FILE})
|
add_library(${TARGET_NAME} SHARED ${LIB_SRCS} ${AUTOMTC_SRC} ${AUTOSCRIBE_SHADER_LIB_SRC} ${QT_RESOURCES_FILE})
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME domain-server)
|
set(TARGET_NAME domain-server)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
if (UPPER_CMAKE_BUILD_TYPE MATCHES DEBUG AND NOT WIN32)
|
if (UPPER_CMAKE_BUILD_TYPE MATCHES DEBUG AND NOT WIN32)
|
||||||
set(_SHOULD_SYMLINK_RESOURCES TRUE)
|
set(_SHOULD_SYMLINK_RESOURCES TRUE)
|
||||||
else ()
|
else ()
|
||||||
|
|
611
domain-server/src/DomainGatekeeper.cpp
Normal file
611
domain-server/src/DomainGatekeeper.cpp
Normal file
|
@ -0,0 +1,611 @@
|
||||||
|
//
|
||||||
|
// DomainGatekeeper.cpp
|
||||||
|
// domain-server/src
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2015-08-24.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "DomainGatekeeper.h"
|
||||||
|
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/rsa.h>
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
|
||||||
|
#include <AccountManager.h>
|
||||||
|
#include <Assignment.h>
|
||||||
|
#include <JSONBreakableMarshal.h>
|
||||||
|
|
||||||
|
#include "DomainServer.h"
|
||||||
|
#include "DomainServerNodeData.h"
|
||||||
|
|
||||||
|
using SharedAssignmentPointer = QSharedPointer<Assignment>;
|
||||||
|
|
||||||
|
DomainGatekeeper::DomainGatekeeper(DomainServer* server) :
|
||||||
|
_server(server)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainGatekeeper::addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID,
|
||||||
|
const QUuid& walletUUID, const QString& nodeVersion) {
|
||||||
|
_pendingAssignedNodes.emplace(std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(nodeUUID),
|
||||||
|
std::forward_as_tuple(assignmentUUID, walletUUID, nodeVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID) {
|
||||||
|
auto it = _pendingAssignedNodes.find(tempUUID);
|
||||||
|
|
||||||
|
if (it != _pendingAssignedNodes.end()) {
|
||||||
|
return it->second.getAssignmentUUID();
|
||||||
|
} else {
|
||||||
|
return QUuid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||||
|
<< NodeType::AvatarMixer << NodeType::EntityServer;
|
||||||
|
|
||||||
|
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
|
||||||
|
if (packet->getPayloadSize() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream packetStream(packet.data());
|
||||||
|
|
||||||
|
// read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it
|
||||||
|
NodeConnectionData nodeConnection = NodeConnectionData::fromDataStream(packetStream, packet->getSenderSockAddr());
|
||||||
|
|
||||||
|
if (nodeConnection.localSockAddr.isNull() || nodeConnection.publicSockAddr.isNull()) {
|
||||||
|
qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this connect request matches an assignment in the queue
|
||||||
|
auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID);
|
||||||
|
|
||||||
|
SharedNodePointer node;
|
||||||
|
|
||||||
|
if (pendingAssignment != _pendingAssignedNodes.end()) {
|
||||||
|
node = processAssignmentConnectRequest(nodeConnection, pendingAssignment->second);
|
||||||
|
} else if (!STATICALLY_ASSIGNED_NODES.contains(nodeConnection.nodeType)) {
|
||||||
|
QString username;
|
||||||
|
QByteArray usernameSignature;
|
||||||
|
|
||||||
|
if (packet->bytesLeftToRead() > 0) {
|
||||||
|
// read username from packet
|
||||||
|
packetStream >> username;
|
||||||
|
|
||||||
|
if (packet->bytesLeftToRead() > 0) {
|
||||||
|
// read user signature from packet
|
||||||
|
packetStream >> usernameSignature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = processAgentConnectRequest(nodeConnection, username, usernameSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
// set the sending sock addr and node interest set on this node
|
||||||
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
nodeData->setSendingSockAddr(packet->getSenderSockAddr());
|
||||||
|
nodeData->setNodeInterestSet(nodeConnection.interestList.toSet());
|
||||||
|
|
||||||
|
// signal that we just connected a node so the DomainServer can get it a list
|
||||||
|
// and broadcast its presence right away
|
||||||
|
emit connectedNode(node);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Refusing connection from node at" << packet->getSenderSockAddr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||||
|
const PendingAssignedNodeData& pendingAssignment) {
|
||||||
|
|
||||||
|
// make sure this matches an assignment the DS told us we sent out
|
||||||
|
auto it = _pendingAssignedNodes.find(nodeConnection.connectUUID);
|
||||||
|
|
||||||
|
SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer();
|
||||||
|
|
||||||
|
if (it != _pendingAssignedNodes.end()) {
|
||||||
|
// find the matching queued static assignment in DS queue
|
||||||
|
matchingQueuedAssignment = _server->dequeueMatchingAssignment(it->second.getAssignmentUUID(), nodeConnection.nodeType);
|
||||||
|
|
||||||
|
if (matchingQueuedAssignment) {
|
||||||
|
qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID)
|
||||||
|
<< "matches unfulfilled assignment"
|
||||||
|
<< uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID());
|
||||||
|
} else {
|
||||||
|
// this is a node connecting to fulfill an assignment that doesn't exist
|
||||||
|
// don't reply back to them so they cycle back and re-request an assignment
|
||||||
|
qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID);
|
||||||
|
return SharedNodePointer();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "No assignment was deployed with UUID" << uuidStringWithoutCurlyBraces(nodeConnection.connectUUID);
|
||||||
|
return SharedNodePointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the new node
|
||||||
|
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
||||||
|
|
||||||
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||||
|
|
||||||
|
// set assignment related data on the linked data for this node
|
||||||
|
nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID());
|
||||||
|
nodeData->setWalletUUID(it->second.getWalletUUID());
|
||||||
|
nodeData->setNodeVersion(it->second.getNodeVersion());
|
||||||
|
|
||||||
|
// cleanup the PendingAssignedNodeData for this assignment now that it's connecting
|
||||||
|
_pendingAssignedNodes.erase(it);
|
||||||
|
|
||||||
|
// always allow assignment clients to create and destroy entities
|
||||||
|
newNode->setCanAdjustLocks(true);
|
||||||
|
newNode->setCanRez(true);
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
|
||||||
|
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
|
||||||
|
const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
|
||||||
|
|
||||||
|
SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||||
|
const QString& username,
|
||||||
|
const QByteArray& usernameSignature) {
|
||||||
|
|
||||||
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
|
bool isRestrictingAccess =
|
||||||
|
_server->_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
|
||||||
|
|
||||||
|
// check if this user is on our local machine - if this is true they are always allowed to connect
|
||||||
|
QHostAddress senderHostAddress = nodeConnection.senderSockAddr.getAddress();
|
||||||
|
bool isLocalUser =
|
||||||
|
(senderHostAddress == limitedNodeList->getLocalSockAddr().getAddress() || senderHostAddress == QHostAddress::LocalHost);
|
||||||
|
|
||||||
|
// if we're using restricted access and this user is not local make sure we got a user signature
|
||||||
|
if (isRestrictingAccess && !isLocalUser) {
|
||||||
|
if (!username.isEmpty()) {
|
||||||
|
if (usernameSignature.isEmpty()) {
|
||||||
|
// if user didn't include usernameSignature in connect request, send a connectionToken packet
|
||||||
|
sendConnectionTokenPacket(username, nodeConnection.senderSockAddr);
|
||||||
|
|
||||||
|
// ask for their public key right now to make sure we have it
|
||||||
|
requestUserPublicKey(username);
|
||||||
|
|
||||||
|
return SharedNodePointer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verifiedUsername = false;
|
||||||
|
|
||||||
|
// if we do not have a local user we need to subject them to our verification and capacity checks
|
||||||
|
if (!isLocalUser) {
|
||||||
|
|
||||||
|
// check if we need to look at the username signature
|
||||||
|
if (isRestrictingAccess) {
|
||||||
|
if (isVerifiedAllowedUser(username, usernameSignature, nodeConnection.senderSockAddr)) {
|
||||||
|
// we verified the user via their username and signature - set the verifiedUsername
|
||||||
|
// so we don't re-decrypt their sig if we're trying to exempt them from max capacity check (due to
|
||||||
|
// being in the allowed editors list)
|
||||||
|
verifiedUsername = true;
|
||||||
|
} else {
|
||||||
|
// failed to verify user - return a null shared ptr
|
||||||
|
return SharedNodePointer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isWithinMaxCapacity(username, usernameSignature, verifiedUsername, nodeConnection.senderSockAddr)) {
|
||||||
|
// we can't allow this user to connect because we are at max capacity (and they either aren't an allowed editor
|
||||||
|
// or couldn't be verified as one)
|
||||||
|
return SharedNodePointer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this user is in the editors list (or if the editors list is empty) set the user's node's canAdjustLocks to true
|
||||||
|
const QVariant* allowedEditorsVariant =
|
||||||
|
valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
|
||||||
|
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
|
||||||
|
|
||||||
|
// if the allowed editors list is empty then everyone can adjust locks
|
||||||
|
bool canAdjustLocks = allowedEditors.empty();
|
||||||
|
|
||||||
|
if (allowedEditors.contains(username)) {
|
||||||
|
// we have a non-empty allowed editors list - check if this user is verified to be in it
|
||||||
|
if (!verifiedUsername) {
|
||||||
|
if (!verifyUserSignature(username, usernameSignature, HifiSockAddr())) {
|
||||||
|
// failed to verify a user that is in the allowed editors list
|
||||||
|
|
||||||
|
// TODO: fix public key refresh in interface/metaverse and force this check
|
||||||
|
qDebug() << "Could not verify user" << username << "as allowed editor. In the interim this user"
|
||||||
|
<< "will be given edit rights to avoid a thrasing of public key requests and connect requests.";
|
||||||
|
}
|
||||||
|
|
||||||
|
canAdjustLocks = true;
|
||||||
|
} else {
|
||||||
|
// already verified this user and they are in the allowed editors list
|
||||||
|
canAdjustLocks = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if only editors should be able to rez entities
|
||||||
|
const QVariant* editorsAreRezzersVariant =
|
||||||
|
valueForKeyPath(_server->_settingsManager.getSettingsMap(), EDITORS_ARE_REZZERS_KEYPATH);
|
||||||
|
|
||||||
|
bool onlyEditorsAreRezzers = false;
|
||||||
|
if (editorsAreRezzersVariant) {
|
||||||
|
onlyEditorsAreRezzers = editorsAreRezzersVariant->toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canRez = true;
|
||||||
|
if (onlyEditorsAreRezzers) {
|
||||||
|
canRez = canAdjustLocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the new node
|
||||||
|
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
||||||
|
|
||||||
|
// set the edit rights for this user
|
||||||
|
newNode->setCanAdjustLocks(canAdjustLocks);
|
||||||
|
newNode->setCanRez(canRez);
|
||||||
|
|
||||||
|
// grab the linked data for our new node so we can set the username
|
||||||
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||||
|
|
||||||
|
// if we have a username from the connect request, set it on the DomainServerNodeData
|
||||||
|
nodeData->setUsername(username);
|
||||||
|
|
||||||
|
// also add an interpolation to JSONBreakableMarshal so that servers can get username in stats
|
||||||
|
JSONBreakableMarshal::addInterpolationForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
|
||||||
|
uuidStringWithoutCurlyBraces(newNode->getUUID()), username);
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
|
||||||
|
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
|
||||||
|
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
|
||||||
|
|
||||||
|
QUuid nodeUUID;
|
||||||
|
|
||||||
|
if (connectedPeer) {
|
||||||
|
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||||
|
nodeUUID = nodeConnection.connectUUID;
|
||||||
|
|
||||||
|
if (connectedPeer->getActiveSocket()) {
|
||||||
|
// set their discovered socket to whatever the activated socket on the network peer object was
|
||||||
|
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we got a connectUUID we didn't recognize, just add the node with a new UUID
|
||||||
|
nodeUUID = QUuid::createUuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
|
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeConnection.nodeType,
|
||||||
|
nodeConnection.publicSockAddr, nodeConnection.localSockAddr);
|
||||||
|
|
||||||
|
// So that we can send messages to this node at will - we need to activate the correct socket on this node now
|
||||||
|
newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket);
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
||||||
|
const QByteArray& usernameSignature,
|
||||||
|
const HifiSockAddr& senderSockAddr) {
|
||||||
|
|
||||||
|
// it's possible this user can be allowed to connect, but we need to check their username signature
|
||||||
|
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
||||||
|
|
||||||
|
const QUuid& connectionToken = _connectionTokenHash.value(username.toLower());
|
||||||
|
|
||||||
|
if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) {
|
||||||
|
// if we do have a public key for the user, check for a signature match
|
||||||
|
|
||||||
|
const unsigned char* publicKeyData = reinterpret_cast<const unsigned char*>(publicKeyArray.constData());
|
||||||
|
|
||||||
|
// first load up the public key into an RSA struct
|
||||||
|
RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size());
|
||||||
|
|
||||||
|
QByteArray lowercaseUsername = username.toLower().toUtf8();
|
||||||
|
QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
|
||||||
|
QCryptographicHash::Sha256);
|
||||||
|
|
||||||
|
if (rsaPublicKey) {
|
||||||
|
QByteArray decryptedArray(RSA_size(rsaPublicKey), 0);
|
||||||
|
int decryptResult = RSA_verify(NID_sha256,
|
||||||
|
reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
|
||||||
|
usernameWithToken.size(),
|
||||||
|
reinterpret_cast<const unsigned char*>(usernameSignature.constData()),
|
||||||
|
usernameSignature.size(),
|
||||||
|
rsaPublicKey);
|
||||||
|
|
||||||
|
if (decryptResult == 1) {
|
||||||
|
qDebug() << "Username signature matches for" << username << "- allowing connection.";
|
||||||
|
|
||||||
|
// free up the public key and remove connection token before we return
|
||||||
|
RSA_free(rsaPublicKey);
|
||||||
|
_connectionTokenHash.remove(username);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!senderSockAddr.isNull()) {
|
||||||
|
qDebug() << "Error decrypting username signature for " << username << "- denying connection.";
|
||||||
|
sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free up the public key, we don't need it anymore
|
||||||
|
RSA_free(rsaPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// we can't let this user in since we couldn't convert their public key to an RSA key we could use
|
||||||
|
if (!senderSockAddr.isNull()) {
|
||||||
|
qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection.";
|
||||||
|
sendConnectionDeniedPacket("Couldn't convert data to RSA key.", senderSockAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!senderSockAddr.isNull()) {
|
||||||
|
qDebug() << "Insufficient data to decrypt username signature - denying connection.";
|
||||||
|
sendConnectionDeniedPacket("Insufficient data", senderSockAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestUserPublicKey(username); // no joy. maybe next time?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DomainGatekeeper::isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature,
|
||||||
|
const HifiSockAddr& senderSockAddr) {
|
||||||
|
|
||||||
|
if (username.isEmpty()) {
|
||||||
|
qDebug() << "Connect request denied - no username provided.";
|
||||||
|
|
||||||
|
sendConnectionDeniedPacket("No username provided", senderSockAddr);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList allowedUsers =
|
||||||
|
_server->_settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList();
|
||||||
|
|
||||||
|
if (allowedUsers.contains(username, Qt::CaseInsensitive)) {
|
||||||
|
if (!verifyUserSignature(username, usernameSignature, senderSockAddr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "Connect request denied for user" << username << "- not in allowed users list.";
|
||||||
|
sendConnectionDeniedPacket("User not on whitelist.", senderSockAddr);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DomainGatekeeper::isWithinMaxCapacity(const QString& username, const QByteArray& usernameSignature,
|
||||||
|
bool& verifiedUsername,
|
||||||
|
const HifiSockAddr& senderSockAddr) {
|
||||||
|
// find out what our maximum capacity is
|
||||||
|
const QVariant* maximumUserCapacityVariant = valueForKeyPath(_server->_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY);
|
||||||
|
unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0;
|
||||||
|
|
||||||
|
if (maximumUserCapacity > 0) {
|
||||||
|
unsigned int connectedUsers = _server->countConnectedUsers();
|
||||||
|
|
||||||
|
if (connectedUsers >= maximumUserCapacity) {
|
||||||
|
// too many users, deny the new connection unless this user is an allowed editor
|
||||||
|
|
||||||
|
const QVariant* allowedEditorsVariant =
|
||||||
|
valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
|
||||||
|
|
||||||
|
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
|
||||||
|
if (allowedEditors.contains(username)) {
|
||||||
|
if (verifiedUsername || verifyUserSignature(username, usernameSignature, senderSockAddr)) {
|
||||||
|
verifiedUsername = true;
|
||||||
|
qDebug() << "Above maximum capacity -" << connectedUsers << "/" << maximumUserCapacity <<
|
||||||
|
"but user" << username << "is in allowed editors list so will be allowed to connect.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deny connection from this user
|
||||||
|
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection.";
|
||||||
|
sendConnectionDeniedPacket("Too many connected users.", senderSockAddr);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, allowing new connection.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DomainGatekeeper::preloadAllowedUserPublicKeys() {
|
||||||
|
const QVariant* allowedUsersVariant = valueForKeyPath(_server->_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||||
|
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
||||||
|
|
||||||
|
if (allowedUsers.size() > 0) {
|
||||||
|
// in the future we may need to limit how many requests here - for now assume that lists of allowed users are not
|
||||||
|
// going to create > 100 requests
|
||||||
|
foreach(const QString& username, allowedUsers) {
|
||||||
|
requestUserPublicKey(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainGatekeeper::requestUserPublicKey(const QString& username) {
|
||||||
|
// even if we have a public key for them right now, request a new one in case it has just changed
|
||||||
|
JSONCallbackParameters callbackParams;
|
||||||
|
callbackParams.jsonCallbackReceiver = this;
|
||||||
|
callbackParams.jsonCallbackMethod = "publicKeyJSONCallback";
|
||||||
|
|
||||||
|
const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key";
|
||||||
|
|
||||||
|
qDebug() << "Requesting public key for user" << username;
|
||||||
|
|
||||||
|
AccountManager::getInstance().sendRequest(USER_PUBLIC_KEY_PATH.arg(username),
|
||||||
|
AccountManagerAuth::None,
|
||||||
|
QNetworkAccessManager::GetOperation, callbackParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) {
|
||||||
|
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
||||||
|
|
||||||
|
if (jsonObject["status"].toString() == "success") {
|
||||||
|
// figure out which user this is for
|
||||||
|
|
||||||
|
const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key";
|
||||||
|
QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING);
|
||||||
|
|
||||||
|
if (usernameRegex.indexIn(requestReply.url().toString()) != -1) {
|
||||||
|
QString username = usernameRegex.cap(1);
|
||||||
|
|
||||||
|
qDebug() << "Storing a public key for user" << username;
|
||||||
|
|
||||||
|
// pull the public key as a QByteArray from this response
|
||||||
|
const QString JSON_DATA_KEY = "data";
|
||||||
|
const QString JSON_PUBLIC_KEY_KEY = "public_key";
|
||||||
|
|
||||||
|
_userPublicKeys[username] =
|
||||||
|
QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainGatekeeper::sendConnectionDeniedPacket(const QString& reason, const HifiSockAddr& senderSockAddr) {
|
||||||
|
// this is an agent and we've decided we won't let them connect - send them a packet to deny connection
|
||||||
|
QByteArray utfString = reason.toUtf8();
|
||||||
|
quint16 payloadSize = utfString.size();
|
||||||
|
|
||||||
|
// setup the DomainConnectionDenied packet
|
||||||
|
auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, payloadSize + sizeof(payloadSize));
|
||||||
|
|
||||||
|
// pack in the reason the connection was denied (the client displays this)
|
||||||
|
if (payloadSize > 0) {
|
||||||
|
connectionDeniedPacket->writePrimitive(payloadSize);
|
||||||
|
connectionDeniedPacket->write(utfString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the packet off
|
||||||
|
DependencyManager::get<LimitedNodeList>()->sendPacket(std::move(connectionDeniedPacket), senderSockAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainGatekeeper::sendConnectionTokenPacket(const QString& username, const HifiSockAddr& senderSockAddr) {
|
||||||
|
// get the existing connection token or create a new one
|
||||||
|
QUuid& connectionToken = _connectionTokenHash[username.toLower()];
|
||||||
|
|
||||||
|
if (connectionToken.isNull()) {
|
||||||
|
connectionToken = QUuid::createUuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup a static connection token packet
|
||||||
|
static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID);
|
||||||
|
|
||||||
|
// reset the packet before each time we send
|
||||||
|
connectionTokenPacket->reset();
|
||||||
|
|
||||||
|
// write the connection token
|
||||||
|
connectionTokenPacket->write(connectionToken.toRfc4122());
|
||||||
|
|
||||||
|
// send off the packet unreliably
|
||||||
|
DependencyManager::get<LimitedNodeList>()->sendUnreliablePacket(*connectionTokenPacket, senderSockAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int NUM_PEER_PINGS_BEFORE_DELETE = 2000 / UDP_PUNCH_PING_INTERVAL_MS;
|
||||||
|
|
||||||
|
void DomainGatekeeper::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) {
|
||||||
|
|
||||||
|
if (peer->getConnectionAttempts() >= NUM_PEER_PINGS_BEFORE_DELETE) {
|
||||||
|
// we've reached the maximum number of ping attempts
|
||||||
|
qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID();
|
||||||
|
qDebug() << "Removing from list of connecting peers.";
|
||||||
|
|
||||||
|
_icePeers.remove(peer->getUUID());
|
||||||
|
} else {
|
||||||
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
|
// send the ping packet to the local and public sockets for this node
|
||||||
|
auto localPingPacket = limitedNodeList->constructICEPingPacket(PingType::Local, limitedNodeList->getSessionUUID());
|
||||||
|
limitedNodeList->sendPacket(std::move(localPingPacket), peer->getLocalSocket());
|
||||||
|
|
||||||
|
auto publicPingPacket = limitedNodeList->constructICEPingPacket(PingType::Public, limitedNodeList->getSessionUUID());
|
||||||
|
limitedNodeList->sendPacket(std::move(publicPingPacket), peer->getPublicSocket());
|
||||||
|
|
||||||
|
peer->incrementConnectionAttempts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainGatekeeper::handlePeerPingTimeout() {
|
||||||
|
NetworkPeer* senderPeer = qobject_cast<NetworkPeer*>(sender());
|
||||||
|
|
||||||
|
if (senderPeer) {
|
||||||
|
SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID());
|
||||||
|
|
||||||
|
if (sharedPeer && !sharedPeer->getActiveSocket()) {
|
||||||
|
pingPunchForConnectingPeer(sharedPeer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<NLPacket> 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.data());
|
||||||
|
|
||||||
|
NetworkPeer* receivedPeer = new NetworkPeer;
|
||||||
|
iceResponseStream >> *receivedPeer;
|
||||||
|
|
||||||
|
if (!_icePeers.contains(receivedPeer->getUUID())) {
|
||||||
|
qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer;
|
||||||
|
SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer);
|
||||||
|
_icePeers[receivedPeer->getUUID()] = newPeer;
|
||||||
|
|
||||||
|
// make sure we know when we should ping this peer
|
||||||
|
connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainGatekeeper::handlePeerPingTimeout);
|
||||||
|
|
||||||
|
// immediately ping the new peer, and start a timer to continue pinging it until we connect to it
|
||||||
|
newPeer->startPingTimer();
|
||||||
|
|
||||||
|
qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID"
|
||||||
|
<< newPeer->getUUID();
|
||||||
|
|
||||||
|
pingPunchForConnectingPeer(newPeer);
|
||||||
|
} else {
|
||||||
|
delete receivedPeer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainGatekeeper::processICEPingPacket(QSharedPointer<NLPacket> packet) {
|
||||||
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*packet, limitedNodeList->getSessionUUID());
|
||||||
|
|
||||||
|
limitedNodeList->sendPacket(std::move(pingReplyPacket), packet->getSenderSockAddr());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<NLPacket> packet) {
|
||||||
|
QDataStream packetStream(packet.data());
|
||||||
|
|
||||||
|
QUuid nodeUUID;
|
||||||
|
packetStream >> nodeUUID;
|
||||||
|
|
||||||
|
SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID);
|
||||||
|
|
||||||
|
if (sendingPeer) {
|
||||||
|
// we had this NetworkPeer in our connecting list - add the right sock addr to our connected list
|
||||||
|
sendingPeer->activateMatchingOrNewSymmetricSocket(packet->getSenderSockAddr());
|
||||||
|
}
|
||||||
|
}
|
93
domain-server/src/DomainGatekeeper.h
Normal file
93
domain-server/src/DomainGatekeeper.h
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
//
|
||||||
|
// DomainGatekeeper.h
|
||||||
|
// domain-server/src
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2015-08-24.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_DomainGatekeeper_h
|
||||||
|
#define hifi_DomainGatekeeper_h
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtNetwork/QNetworkReply>
|
||||||
|
|
||||||
|
#include <NLPacket.h>
|
||||||
|
#include <Node.h>
|
||||||
|
#include <UUIDHasher.h>
|
||||||
|
|
||||||
|
#include "NodeConnectionData.h"
|
||||||
|
#include "PendingAssignedNodeData.h"
|
||||||
|
|
||||||
|
class DomainServer;
|
||||||
|
|
||||||
|
class DomainGatekeeper : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DomainGatekeeper(DomainServer* server);
|
||||||
|
|
||||||
|
void addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID,
|
||||||
|
const QUuid& walletUUID, const QString& nodeVersion);
|
||||||
|
QUuid assignmentUUIDForPendingAssignment(const QUuid& tempUUID);
|
||||||
|
|
||||||
|
void preloadAllowedUserPublicKeys();
|
||||||
|
|
||||||
|
void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); }
|
||||||
|
public slots:
|
||||||
|
void processConnectRequestPacket(QSharedPointer<NLPacket> packet);
|
||||||
|
void processICEPingPacket(QSharedPointer<NLPacket> packet);
|
||||||
|
void processICEPingReplyPacket(QSharedPointer<NLPacket> packet);
|
||||||
|
void processICEPeerInformationPacket(QSharedPointer<NLPacket> packet);
|
||||||
|
|
||||||
|
void publicKeyJSONCallback(QNetworkReply& requestReply);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void connectedNode(SharedNodePointer node);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handlePeerPingTimeout();
|
||||||
|
private:
|
||||||
|
SharedNodePointer processAssignmentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||||
|
const PendingAssignedNodeData& pendingAssignment);
|
||||||
|
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||||
|
const QString& username,
|
||||||
|
const QByteArray& usernameSignature);
|
||||||
|
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
|
||||||
|
|
||||||
|
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
|
||||||
|
const HifiSockAddr& senderSockAddr);
|
||||||
|
bool isVerifiedAllowedUser(const QString& username, const QByteArray& usernameSignature,
|
||||||
|
const HifiSockAddr& senderSockAddr);
|
||||||
|
bool isWithinMaxCapacity(const QString& username, const QByteArray& usernameSignature,
|
||||||
|
bool& verifiedUsername,
|
||||||
|
const HifiSockAddr& senderSockAddr);
|
||||||
|
|
||||||
|
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
|
||||||
|
const HifiSockAddr& senderSockAddr);
|
||||||
|
|
||||||
|
void sendConnectionTokenPacket(const QString& username, const HifiSockAddr& senderSockAddr);
|
||||||
|
void sendConnectionDeniedPacket(const QString& reason, const HifiSockAddr& senderSockAddr);
|
||||||
|
|
||||||
|
void pingPunchForConnectingPeer(const SharedNetworkPeer& peer);
|
||||||
|
|
||||||
|
void requestUserPublicKey(const QString& username);
|
||||||
|
|
||||||
|
DomainServer* _server;
|
||||||
|
|
||||||
|
std::unordered_map<QUuid, PendingAssignedNodeData> _pendingAssignedNodes;
|
||||||
|
|
||||||
|
QHash<QUuid, SharedNetworkPeer> _icePeers;
|
||||||
|
|
||||||
|
QHash<QString, QUuid> _connectionTokenHash;
|
||||||
|
QHash<QString, QByteArray> _userPublicKeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // hifi_DomainGatekeeper_h
|
|
@ -9,9 +9,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <openssl/err.h>
|
#include "DomainServer.h"
|
||||||
#include <openssl/rsa.h>
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
@ -38,24 +36,19 @@
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
|
|
||||||
#include "DomainServerNodeData.h"
|
#include "DomainServerNodeData.h"
|
||||||
|
#include "NodeConnectionData.h"
|
||||||
#include "DomainServer.h"
|
|
||||||
|
|
||||||
int const DomainServer::EXIT_CODE_REBOOT = 234923;
|
int const DomainServer::EXIT_CODE_REBOOT = 234923;
|
||||||
|
|
||||||
const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io";
|
const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io";
|
||||||
|
|
||||||
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
|
|
||||||
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
|
|
||||||
const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
|
|
||||||
|
|
||||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
QCoreApplication(argc, argv),
|
QCoreApplication(argc, argv),
|
||||||
|
_gatekeeper(this),
|
||||||
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||||
_httpsManager(NULL),
|
_httpsManager(NULL),
|
||||||
_allAssignments(),
|
_allAssignments(),
|
||||||
_unfulfilledAssignments(),
|
_unfulfilledAssignments(),
|
||||||
_pendingAssignedNodes(),
|
|
||||||
_isUsingDTLS(false),
|
_isUsingDTLS(false),
|
||||||
_oauthProviderURL(),
|
_oauthProviderURL(),
|
||||||
_oauthClientID(),
|
_oauthClientID(),
|
||||||
|
@ -94,6 +87,9 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
|
|
||||||
qRegisterMetaType<DomainServerWebSessionData>("DomainServerWebSessionData");
|
qRegisterMetaType<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||||
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("DomainServerWebSessionData");
|
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||||
|
|
||||||
|
// make sure we hear about newly connected nodes from our gatekeeper
|
||||||
|
connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode);
|
||||||
|
|
||||||
if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) {
|
if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) {
|
||||||
// we either read a certificate and private key or were not passed one
|
// we either read a certificate and private key or were not passed one
|
||||||
|
@ -108,7 +104,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
setupAutomaticNetworking();
|
setupAutomaticNetworking();
|
||||||
|
|
||||||
// preload some user public keys so they can connect on first request
|
// preload some user public keys so they can connect on first request
|
||||||
preloadAllowedUserPublicKeys();
|
_gatekeeper.preloadAllowedUserPublicKeys();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,13 +280,13 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
||||||
// register as the packet receiver for the types we want
|
// register as the packet receiver for the types we want
|
||||||
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
||||||
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
|
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
|
||||||
packetReceiver.registerListener(PacketType::DomainConnectRequest, this, "processConnectRequestPacket");
|
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
||||||
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
||||||
packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket");
|
packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket");
|
||||||
packetReceiver.registerListener(PacketType::ICEPingReply, this, "processICEPingReplyPacket");
|
packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket");
|
||||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, this, "processICEPeerInformationPacket");
|
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket");
|
||||||
|
|
||||||
// add whatever static assignments that have been parsed to the queue
|
// add whatever static assignments that have been parsed to the queue
|
||||||
addStaticAssignmentsToQueue();
|
addStaticAssignmentsToQueue();
|
||||||
|
@ -575,217 +571,20 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
|
||||||
<< NodeType::AvatarMixer << NodeType::EntityServer;
|
|
||||||
|
|
||||||
void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
|
|
||||||
NodeType_t nodeType;
|
|
||||||
HifiSockAddr publicSockAddr, localSockAddr;
|
|
||||||
|
|
||||||
if (packet->getPayloadSize() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDataStream packetStream(packet.data());
|
|
||||||
|
|
||||||
QUuid connectUUID;
|
|
||||||
packetStream >> connectUUID;
|
|
||||||
|
|
||||||
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
|
|
||||||
|
|
||||||
parseNodeData(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr);
|
|
||||||
|
|
||||||
if (localSockAddr.isNull() || senderSockAddr.isNull()) {
|
|
||||||
qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if this connect request matches an assignment in the queue
|
|
||||||
bool isAssignment = _pendingAssignedNodes.contains(connectUUID);
|
|
||||||
SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer();
|
|
||||||
PendingAssignedNodeData* pendingAssigneeData = NULL;
|
|
||||||
|
|
||||||
if (isAssignment) {
|
|
||||||
pendingAssigneeData = _pendingAssignedNodes.value(connectUUID);
|
|
||||||
|
|
||||||
if (pendingAssigneeData) {
|
|
||||||
matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(pendingAssigneeData->getAssignmentUUID(), nodeType);
|
|
||||||
|
|
||||||
if (matchingQueuedAssignment) {
|
|
||||||
qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(connectUUID)
|
|
||||||
<< "matches unfulfilled assignment"
|
|
||||||
<< uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID());
|
|
||||||
|
|
||||||
// remove this unique assignment deployment from the hash of pending assigned nodes
|
|
||||||
// cleanup of the PendingAssignedNodeData happens below after the node has been added to the LimitedNodeList
|
|
||||||
_pendingAssignedNodes.remove(connectUUID);
|
|
||||||
} else {
|
|
||||||
// this is a node connecting to fulfill an assignment that doesn't exist
|
|
||||||
// don't reply back to them so they cycle back and re-request an assignment
|
|
||||||
qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(connectUUID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<NodeType_t> nodeInterestList;
|
|
||||||
QString username;
|
|
||||||
QByteArray usernameSignature;
|
|
||||||
|
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
|
||||||
|
|
||||||
packetStream >> nodeInterestList;
|
|
||||||
|
|
||||||
if (packet->bytesLeftToRead() > 0) {
|
|
||||||
// try to verify username
|
|
||||||
packetStream >> username;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isRestrictingAccess =
|
|
||||||
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
|
|
||||||
|
|
||||||
// we always let in a user who is sending a packet from our local socket or from the localhost address
|
|
||||||
bool isLocalUser = (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress() || senderSockAddr.getAddress() == QHostAddress::LocalHost);
|
|
||||||
|
|
||||||
if (isRestrictingAccess && !isLocalUser) {
|
|
||||||
if (!username.isEmpty()) {
|
|
||||||
// if there's a username, try to unpack username signature
|
|
||||||
packetStream >> usernameSignature;
|
|
||||||
|
|
||||||
if (usernameSignature.isEmpty()) {
|
|
||||||
// if user didn't include usernameSignature in connect request, send a connectionToken packet
|
|
||||||
QUuid& connectionToken = _connectionTokenHash[username.toLower()];
|
|
||||||
|
|
||||||
if (connectionToken.isNull()) {
|
|
||||||
connectionToken = QUuid::createUuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID);
|
|
||||||
connectionTokenPacket->reset();
|
|
||||||
connectionTokenPacket->write(connectionToken.toRfc4122());
|
|
||||||
limitedNodeList->sendUnreliablePacket(*connectionTokenPacket, packet->getSenderSockAddr());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString reason;
|
|
||||||
if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr, reason)) {
|
|
||||||
// this is an agent and we've decided we won't let them connect - send them a packet to deny connection
|
|
||||||
QByteArray utfString = reason.toUtf8();
|
|
||||||
quint16 payloadSize = utfString.size();
|
|
||||||
|
|
||||||
auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, payloadSize + sizeof(payloadSize));
|
|
||||||
if (payloadSize > 0) {
|
|
||||||
connectionDeniedPacket->writePrimitive(payloadSize);
|
|
||||||
connectionDeniedPacket->write(utfString);
|
|
||||||
}
|
|
||||||
// tell client it has been refused.
|
|
||||||
limitedNodeList->sendPacket(std::move(connectionDeniedPacket), senderSockAddr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
HifiSockAddr discoveredSocket = senderSockAddr;
|
|
||||||
SharedNetworkPeer connectedPeer = _icePeers.value(connectUUID);
|
|
||||||
|
|
||||||
if (connectedPeer) {
|
|
||||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
|
||||||
nodeUUID = connectUUID;
|
|
||||||
|
|
||||||
if (connectedPeer->getActiveSocket()) {
|
|
||||||
// set their discovered socket to whatever the activated socket on the network peer object was
|
|
||||||
discoveredSocket = *connectedPeer->getActiveSocket();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we got a connectUUID we didn't recognize, just add the node with a new UUID
|
|
||||||
nodeUUID = QUuid::createUuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this user is in the editors list (or if the editors list is empty) set the user's node's canAdjustLocks to true
|
|
||||||
const QVariant* allowedEditorsVariant =
|
|
||||||
valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
|
|
||||||
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
|
|
||||||
bool canAdjustLocks = allowedEditors.isEmpty() || allowedEditors.contains(username);
|
|
||||||
|
|
||||||
const QVariant* editorsAreRezzersVariant =
|
|
||||||
valueForKeyPath(_settingsManager.getSettingsMap(), EDITORS_ARE_REZZERS_KEYPATH);
|
|
||||||
|
|
||||||
bool onlyEditorsAreRezzers = false;
|
|
||||||
if (editorsAreRezzersVariant) {
|
|
||||||
onlyEditorsAreRezzers = editorsAreRezzersVariant->toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canRez = true;
|
|
||||||
if (onlyEditorsAreRezzers) {
|
|
||||||
canRez = canAdjustLocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeType,
|
|
||||||
publicSockAddr, localSockAddr,
|
|
||||||
canAdjustLocks, canRez);
|
|
||||||
|
|
||||||
// So that we can send messages to this node at will - we need to activate the correct socket on this node now
|
|
||||||
newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket);
|
|
||||||
|
|
||||||
// when the newNode is created the linked data is also created
|
|
||||||
// if this was a static assignment set the UUID, set the sendingSockAddr
|
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
|
||||||
|
|
||||||
if (isAssignment) {
|
|
||||||
nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID());
|
|
||||||
nodeData->setWalletUUID(pendingAssigneeData->getWalletUUID());
|
|
||||||
nodeData->setNodeVersion(pendingAssigneeData->getNodeVersion());
|
|
||||||
|
|
||||||
// always allow assignment clients to create and destroy entities
|
|
||||||
newNode->setCanAdjustLocks(true);
|
|
||||||
newNode->setCanRez(true);
|
|
||||||
|
|
||||||
// now that we've pulled the wallet UUID and added the node to our list, delete the pending assignee data
|
|
||||||
delete pendingAssigneeData;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!username.isEmpty()) {
|
|
||||||
// if we have a username from the connect request, set it on the DomainServerNodeData
|
|
||||||
nodeData->setUsername(username);
|
|
||||||
|
|
||||||
// also add an interpolation to JSONBreakableMarshal so that servers can get username in stats
|
|
||||||
JSONBreakableMarshal::addInterpolationForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
|
|
||||||
uuidStringWithoutCurlyBraces(nodeUUID), username);
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeData->setSendingSockAddr(senderSockAddr);
|
|
||||||
|
|
||||||
// reply back to the user with a PacketType::DomainList
|
|
||||||
sendDomainListToNode(newNode, senderSockAddr, nodeInterestList.toSet());
|
|
||||||
|
|
||||||
// send out this node to our other connected nodes
|
|
||||||
broadcastNewNode(newNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
||||||
|
|
||||||
NodeType_t throwawayNodeType;
|
|
||||||
HifiSockAddr nodePublicAddress, nodeLocalAddress;
|
|
||||||
|
|
||||||
QDataStream packetStream(packet.data());
|
QDataStream packetStream(packet.data());
|
||||||
|
NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, packet->getSenderSockAddr(), false);
|
||||||
|
|
||||||
parseNodeData(packetStream, throwawayNodeType, nodePublicAddress, nodeLocalAddress, packet->getSenderSockAddr());
|
// update this node's sockets in case they have changed
|
||||||
|
sendingNode->setPublicSocket(nodeRequestData.publicSockAddr);
|
||||||
|
sendingNode->setLocalSocket(nodeRequestData.localSockAddr);
|
||||||
|
|
||||||
|
// update the NodeInterestSet in case there have been any changes
|
||||||
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
||||||
|
nodeData->setNodeInterestSet(nodeRequestData.interestList.toSet());
|
||||||
|
|
||||||
sendingNode->setPublicSocket(nodePublicAddress);
|
sendDomainListToNode(sendingNode, packet->getSenderSockAddr());
|
||||||
sendingNode->setLocalSocket(nodeLocalAddress);
|
|
||||||
|
|
||||||
QList<NodeType_t> nodeInterestList;
|
|
||||||
packetStream >> nodeInterestList;
|
|
||||||
|
|
||||||
sendDomainListToNode(sendingNode, packet->getSenderSockAddr(), nodeInterestList.toSet());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int DomainServer::countConnectedUsers() {
|
unsigned int DomainServer::countConnectedUsers() {
|
||||||
|
@ -799,156 +598,6 @@ unsigned int DomainServer::countConnectedUsers() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool DomainServer::verifyUserSignature(const QString& username,
|
|
||||||
const QByteArray& usernameSignature,
|
|
||||||
QString& reasonReturn) {
|
|
||||||
|
|
||||||
// it's possible this user can be allowed to connect, but we need to check their username signature
|
|
||||||
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
|
||||||
|
|
||||||
const QUuid& connectionToken = _connectionTokenHash.value(username.toLower());
|
|
||||||
|
|
||||||
if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) {
|
|
||||||
// if we do have a public key for the user, check for a signature match
|
|
||||||
|
|
||||||
const unsigned char* publicKeyData = reinterpret_cast<const unsigned char*>(publicKeyArray.constData());
|
|
||||||
|
|
||||||
// first load up the public key into an RSA struct
|
|
||||||
RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size());
|
|
||||||
|
|
||||||
QByteArray lowercaseUsername = username.toLower().toUtf8();
|
|
||||||
QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
|
|
||||||
QCryptographicHash::Sha256);
|
|
||||||
|
|
||||||
if (rsaPublicKey) {
|
|
||||||
QByteArray decryptedArray(RSA_size(rsaPublicKey), 0);
|
|
||||||
int decryptResult = RSA_verify(NID_sha256,
|
|
||||||
reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
|
|
||||||
usernameWithToken.size(),
|
|
||||||
reinterpret_cast<const unsigned char*>(usernameSignature.constData()),
|
|
||||||
usernameSignature.size(),
|
|
||||||
rsaPublicKey);
|
|
||||||
|
|
||||||
if (decryptResult == 1) {
|
|
||||||
qDebug() << "Username signature matches for" << username << "- allowing connection.";
|
|
||||||
|
|
||||||
// free up the public key and remove connection token before we return
|
|
||||||
RSA_free(rsaPublicKey);
|
|
||||||
_connectionTokenHash.remove(username);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
qDebug() << "Error decrypting username signature for " << username << "- denying connection.";
|
|
||||||
reasonReturn = "Error decrypting username signature.";
|
|
||||||
// free up the public key, we don't need it anymore
|
|
||||||
RSA_free(rsaPublicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// we can't let this user in since we couldn't convert their public key to an RSA key we could use
|
|
||||||
qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection.";
|
|
||||||
reasonReturn = "Couldn't convert data to RSA key.";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "Insufficient data to decrypt username signature - denying connection.";
|
|
||||||
reasonReturn = "Insufficient data";
|
|
||||||
}
|
|
||||||
|
|
||||||
requestUserPublicKey(username); // no joy. maybe next time?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
|
||||||
const QByteArray& usernameSignature,
|
|
||||||
const HifiSockAddr& senderSockAddr,
|
|
||||||
QString& reasonReturn) {
|
|
||||||
|
|
||||||
//TODO: improve flow so these bools aren't declared twice
|
|
||||||
bool isRestrictingAccess =
|
|
||||||
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
|
|
||||||
bool isLocalUser = (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress() || senderSockAddr.getAddress() == QHostAddress::LocalHost);
|
|
||||||
|
|
||||||
if (isRestrictingAccess && !isLocalUser) {
|
|
||||||
QStringList allowedUsers =
|
|
||||||
_settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList();
|
|
||||||
|
|
||||||
if (allowedUsers.contains(username, Qt::CaseInsensitive)) {
|
|
||||||
if (username.isEmpty()) {
|
|
||||||
qDebug() << "Connect request denied - no username provided.";
|
|
||||||
reasonReturn = "No username provided";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!verifyUserSignature(username, usernameSignature, reasonReturn)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "Connect request denied for user" << username << "not in allowed users list.";
|
|
||||||
reasonReturn = "User not on whitelist.";
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// either we aren't restricting users, or this user is in the allowed list
|
|
||||||
// if this user is in the editors list, exempt them from the max-capacity check
|
|
||||||
const QVariant* allowedEditorsVariant =
|
|
||||||
valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
|
|
||||||
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
|
|
||||||
if (allowedEditors.contains(username)) {
|
|
||||||
if (verifyUserSignature(username, usernameSignature, reasonReturn)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we haven't reached max-capacity, let them in.
|
|
||||||
const QVariant* maximumUserCapacityVariant = valueForKeyPath(_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY);
|
|
||||||
unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0;
|
|
||||||
if (maximumUserCapacity > 0) {
|
|
||||||
unsigned int connectedUsers = countConnectedUsers();
|
|
||||||
if (connectedUsers >= maximumUserCapacity) {
|
|
||||||
// too many users, deny the new connection.
|
|
||||||
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection.";
|
|
||||||
reasonReturn = "Too many connected users.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, perhaps allowing new connection.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::preloadAllowedUserPublicKeys() {
|
|
||||||
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH);
|
|
||||||
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
|
||||||
|
|
||||||
if (allowedUsers.size() > 0) {
|
|
||||||
// in the future we may need to limit how many requests here - for now assume that lists of allowed users are not
|
|
||||||
// going to create > 100 requests
|
|
||||||
foreach(const QString& username, allowedUsers) {
|
|
||||||
requestUserPublicKey(username);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::requestUserPublicKey(const QString& username) {
|
|
||||||
// even if we have a public key for them right now, request a new one in case it has just changed
|
|
||||||
JSONCallbackParameters callbackParams;
|
|
||||||
callbackParams.jsonCallbackReceiver = this;
|
|
||||||
callbackParams.jsonCallbackMethod = "publicKeyJSONCallback";
|
|
||||||
|
|
||||||
const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key";
|
|
||||||
|
|
||||||
qDebug() << "Requesting public key for user" << username;
|
|
||||||
|
|
||||||
AccountManager::getInstance().sendRequest(USER_PUBLIC_KEY_PATH.arg(username),
|
|
||||||
AccountManagerAuth::None,
|
|
||||||
QNetworkAccessManager::GetOperation, callbackParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl DomainServer::oauthRedirectURL() {
|
QUrl DomainServer::oauthRedirectURL() {
|
||||||
return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort());
|
return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort());
|
||||||
}
|
}
|
||||||
|
@ -983,30 +632,18 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) {
|
||||||
return authorizationURL;
|
return authorizationURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DomainServer::parseNodeData(QDataStream& packetStream, NodeType_t& nodeType,
|
void DomainServer::handleConnectedNode(SharedNodePointer newNode) {
|
||||||
HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr,
|
|
||||||
const HifiSockAddr& senderSockAddr) {
|
|
||||||
packetStream >> nodeType;
|
|
||||||
packetStream >> publicSockAddr >> localSockAddr;
|
|
||||||
|
|
||||||
if (publicSockAddr.getAddress().isNull()) {
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||||
// this node wants to use us its STUN server
|
|
||||||
// so set the node public address to whatever we perceive the public address to be
|
// reply back to the user with a PacketType::DomainList
|
||||||
|
sendDomainListToNode(newNode, nodeData->getSendingSockAddr());
|
||||||
// if the sender is on our box then leave its public address to 0 so that
|
|
||||||
// other users attempt to reach it on the same address they have for the domain-server
|
// send out this node to our other connected nodes
|
||||||
if (senderSockAddr.getAddress().isLoopback()) {
|
broadcastNewNode(newNode);
|
||||||
publicSockAddr.setAddress(QHostAddress());
|
|
||||||
} else {
|
|
||||||
publicSockAddr.setAddress(senderSockAddr.getAddress());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return packetStream.device()->pos();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr,
|
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr) {
|
||||||
const NodeSet& nodeInterestSet) {
|
|
||||||
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NUM_BYTES_RFC4122_UUID + 2;
|
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NUM_BYTES_RFC4122_UUID + 2;
|
||||||
|
|
||||||
// setup the extended header for the domain list packets
|
// setup the extended header for the domain list packets
|
||||||
|
@ -1029,7 +666,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
|
||||||
// store the nodeInterestSet on this DomainServerNodeData, in case it has changed
|
// store the nodeInterestSet on this DomainServerNodeData, in case it has changed
|
||||||
nodeData->setNodeInterestSet(nodeInterestSet);
|
auto& nodeInterestSet = nodeData->getNodeInterestSet();
|
||||||
|
|
||||||
if (nodeInterestSet.size() > 0) {
|
if (nodeInterestSet.size() > 0) {
|
||||||
|
|
||||||
|
@ -1038,6 +675,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
||||||
// if this authenticated node has any interest types, send back those nodes as well
|
// if this authenticated node has any interest types, send back those nodes as well
|
||||||
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){
|
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){
|
||||||
if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) {
|
if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) {
|
||||||
|
|
||||||
// since we're about to add a node to the packet we start a segment
|
// since we're about to add a node to the packet we start a segment
|
||||||
domainListPackets.startSegment();
|
domainListPackets.startSegment();
|
||||||
|
|
||||||
|
@ -1169,11 +807,10 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packe
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
limitedNodeList->sendUnreliablePacket(*assignmentPacket, packet->getSenderSockAddr());
|
limitedNodeList->sendUnreliablePacket(*assignmentPacket, packet->getSenderSockAddr());
|
||||||
|
|
||||||
// add the information for that deployed assignment to the hash of pending assigned nodes
|
// give the information for that deployed assignment to the gatekeeper so it knows to that that node
|
||||||
PendingAssignedNodeData* pendingNodeData = new PendingAssignedNodeData(assignmentToDeploy->getUUID(),
|
// in when it comes back around
|
||||||
requestAssignment.getWalletUUID(),
|
_gatekeeper.addPendingAssignedNode(uniqueAssignment.getUUID(), assignmentToDeploy->getUUID(),
|
||||||
requestAssignment.getNodeVersion());
|
requestAssignment.getWalletUUID(), requestAssignment.getNodeVersion());
|
||||||
_pendingAssignedNodes.insert(uniqueAssignment.getUUID(), pendingNodeData);
|
|
||||||
} else {
|
} else {
|
||||||
if (requestAssignment.getType() != Assignment::AgentType
|
if (requestAssignment.getType() != Assignment::AgentType
|
||||||
|| noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) {
|
|| noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) {
|
||||||
|
@ -1253,30 +890,6 @@ void DomainServer::sendPendingTransactionsToServer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::publicKeyJSONCallback(QNetworkReply& requestReply) {
|
|
||||||
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
|
||||||
|
|
||||||
if (jsonObject["status"].toString() == "success") {
|
|
||||||
// figure out which user this is for
|
|
||||||
|
|
||||||
const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key";
|
|
||||||
QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING);
|
|
||||||
|
|
||||||
if (usernameRegex.indexIn(requestReply.url().toString()) != -1) {
|
|
||||||
QString username = usernameRegex.cap(1);
|
|
||||||
|
|
||||||
qDebug() << "Storing a public key for user" << username;
|
|
||||||
|
|
||||||
// pull the public key as a QByteArray from this response
|
|
||||||
const QString JSON_DATA_KEY = "data";
|
|
||||||
const QString JSON_PUBLIC_KEY_KEY = "public_key";
|
|
||||||
|
|
||||||
_userPublicKeys[username] =
|
|
||||||
QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::transactionJSONCallback(const QJsonObject& data) {
|
void DomainServer::transactionJSONCallback(const QJsonObject& data) {
|
||||||
// check if this was successful - if so we can remove it from our list of pending
|
// check if this was successful - if so we can remove it from our list of pending
|
||||||
if (data.value("status").toString() == "success") {
|
if (data.value("status").toString() == "success") {
|
||||||
|
@ -1373,91 +986,6 @@ void DomainServer::sendHeartbeatToIceServer() {
|
||||||
DependencyManager::get<LimitedNodeList>()->sendHeartbeatToIceServer(_iceServerSocket);
|
DependencyManager::get<LimitedNodeList>()->sendHeartbeatToIceServer(_iceServerSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int NUM_PEER_PINGS_BEFORE_DELETE = 2000 / UDP_PUNCH_PING_INTERVAL_MS;
|
|
||||||
|
|
||||||
void DomainServer::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) {
|
|
||||||
|
|
||||||
if (peer->getConnectionAttempts() > 0 && peer->getConnectionAttempts() % NUM_PEER_PINGS_BEFORE_DELETE == 0) {
|
|
||||||
// we've reached the maximum number of ping attempts
|
|
||||||
qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID();
|
|
||||||
qDebug() << "Removing from list of connecting peers.";
|
|
||||||
|
|
||||||
_icePeers.remove(peer->getUUID());
|
|
||||||
} else {
|
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
|
||||||
|
|
||||||
// send the ping packet to the local and public sockets for this node
|
|
||||||
auto localPingPacket = limitedNodeList->constructICEPingPacket(PingType::Local, limitedNodeList->getSessionUUID());
|
|
||||||
limitedNodeList->sendPacket(std::move(localPingPacket), peer->getLocalSocket());
|
|
||||||
|
|
||||||
auto publicPingPacket = limitedNodeList->constructICEPingPacket(PingType::Public, limitedNodeList->getSessionUUID());
|
|
||||||
limitedNodeList->sendPacket(std::move(publicPingPacket), peer->getPublicSocket());
|
|
||||||
|
|
||||||
peer->incrementConnectionAttempts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::handlePeerPingTimeout() {
|
|
||||||
NetworkPeer* senderPeer = qobject_cast<NetworkPeer*>(sender());
|
|
||||||
|
|
||||||
if (senderPeer) {
|
|
||||||
SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID());
|
|
||||||
|
|
||||||
if (sharedPeer && !sharedPeer->getActiveSocket()) {
|
|
||||||
pingPunchForConnectingPeer(sharedPeer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::processICEPeerInformationPacket(QSharedPointer<NLPacket> 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.data());
|
|
||||||
|
|
||||||
NetworkPeer* receivedPeer = new NetworkPeer;
|
|
||||||
iceResponseStream >> *receivedPeer;
|
|
||||||
|
|
||||||
if (!_icePeers.contains(receivedPeer->getUUID())) {
|
|
||||||
qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer;
|
|
||||||
SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer);
|
|
||||||
_icePeers[receivedPeer->getUUID()] = newPeer;
|
|
||||||
|
|
||||||
// make sure we know when we should ping this peer
|
|
||||||
connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainServer::handlePeerPingTimeout);
|
|
||||||
|
|
||||||
// immediately ping the new peer, and start a timer to continue pinging it until we connect to it
|
|
||||||
newPeer->startPingTimer();
|
|
||||||
|
|
||||||
qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID"
|
|
||||||
<< newPeer->getUUID();
|
|
||||||
|
|
||||||
pingPunchForConnectingPeer(newPeer);
|
|
||||||
} else {
|
|
||||||
delete receivedPeer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::processICEPingPacket(QSharedPointer<NLPacket> packet) {
|
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
|
||||||
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*packet, limitedNodeList->getSessionUUID());
|
|
||||||
|
|
||||||
limitedNodeList->sendPacket(std::move(pingReplyPacket), packet->getSenderSockAddr());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::processICEPingReplyPacket(QSharedPointer<NLPacket> packet) {
|
|
||||||
QDataStream packetStream(packet.data());
|
|
||||||
|
|
||||||
QUuid nodeUUID;
|
|
||||||
packetStream >> nodeUUID;
|
|
||||||
|
|
||||||
SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID);
|
|
||||||
|
|
||||||
if (sendingPeer) {
|
|
||||||
// we had this NetworkPeer in our connecting list - add the right sock addr to our connected list
|
|
||||||
sendingPeer->activateMatchingOrNewSymmetricSocket(packet->getSenderSockAddr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::processNodeJSONStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
void DomainServer::processNodeJSONStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
||||||
auto nodeData = dynamic_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
auto nodeData = dynamic_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
|
@ -1569,9 +1097,9 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID);
|
SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID);
|
||||||
if (!matchingAssignment) {
|
if (!matchingAssignment) {
|
||||||
// check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment
|
// check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment
|
||||||
PendingAssignedNodeData* pendingData = _pendingAssignedNodes.value(matchingUUID);
|
QUuid assignmentUUID = _gatekeeper.assignmentUUIDForPendingAssignment(matchingUUID);
|
||||||
if (pendingData) {
|
if (!assignmentUUID.isNull()) {
|
||||||
matchingAssignment = _allAssignments.value(pendingData->getAssignmentUUID());
|
matchingAssignment = _allAssignments.value(assignmentUUID);
|
||||||
|
|
||||||
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
|
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
|
||||||
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
|
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
|
||||||
|
@ -1579,7 +1107,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
QUrl scriptURL = url;
|
QUrl scriptURL = url;
|
||||||
scriptURL.setPath(URI_ASSIGNMENT + "/scripts/"
|
scriptURL.setPath(URI_ASSIGNMENT + "/scripts/"
|
||||||
+ uuidStringWithoutCurlyBraces(pendingData->getAssignmentUUID()));
|
+ uuidStringWithoutCurlyBraces(assignmentUUID));
|
||||||
|
|
||||||
// have the HTTPManager serve the appropriate script file
|
// have the HTTPManager serve the appropriate script file
|
||||||
return _httpManager.handleHTTPRequest(connection, scriptURL, true);
|
return _httpManager.handleHTTPRequest(connection, scriptURL, true);
|
||||||
|
@ -2113,7 +1641,7 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
|
||||||
void DomainServer::nodeKilled(SharedNodePointer node) {
|
void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||||
|
|
||||||
// if this peer connected via ICE then remove them from our ICE peers hash
|
// if this peer connected via ICE then remove them from our ICE peers hash
|
||||||
_icePeers.remove(node->getUUID());
|
_gatekeeper.removeICEPeer(node->getUUID());
|
||||||
|
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
|
||||||
|
@ -2141,7 +1669,7 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& assignmentUUID, NodeType_t nodeType) {
|
SharedAssignmentPointer DomainServer::dequeueMatchingAssignment(const QUuid& assignmentUUID, NodeType_t nodeType) {
|
||||||
QQueue<SharedAssignmentPointer>::iterator i = _unfulfilledAssignments.begin();
|
QQueue<SharedAssignmentPointer>::iterator i = _unfulfilledAssignments.begin();
|
||||||
|
|
||||||
while (i != _unfulfilledAssignments.end()) {
|
while (i != _unfulfilledAssignments.end()) {
|
||||||
|
@ -2192,20 +1720,6 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig
|
||||||
return SharedAssignmentPointer();
|
return SharedAssignmentPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment) {
|
|
||||||
QQueue<SharedAssignmentPointer>::iterator potentialMatchingAssignment = _unfulfilledAssignments.begin();
|
|
||||||
while (potentialMatchingAssignment != _unfulfilledAssignments.end()) {
|
|
||||||
if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) {
|
|
||||||
_unfulfilledAssignments.erase(potentialMatchingAssignment);
|
|
||||||
|
|
||||||
// we matched and removed an assignment, bail out
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
++potentialMatchingAssignment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::addStaticAssignmentsToQueue() {
|
void DomainServer::addStaticAssignmentsToQueue() {
|
||||||
|
|
||||||
// if the domain-server has just restarted,
|
// if the domain-server has just restarted,
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <HTTPSConnection.h>
|
#include <HTTPSConnection.h>
|
||||||
#include <LimitedNodeList.h>
|
#include <LimitedNodeList.h>
|
||||||
|
|
||||||
|
#include "DomainGatekeeper.h"
|
||||||
#include "DomainServerSettingsManager.h"
|
#include "DomainServerSettingsManager.h"
|
||||||
#include "DomainServerWebSessionData.h"
|
#include "DomainServerWebSessionData.h"
|
||||||
#include "WalletTransaction.h"
|
#include "WalletTransaction.h"
|
||||||
|
@ -50,19 +51,14 @@ public slots:
|
||||||
/// Called by NodeList to inform us a node has been killed
|
/// Called by NodeList to inform us a node has been killed
|
||||||
void nodeKilled(SharedNodePointer node);
|
void nodeKilled(SharedNodePointer node);
|
||||||
|
|
||||||
void publicKeyJSONCallback(QNetworkReply& requestReply);
|
|
||||||
void transactionJSONCallback(const QJsonObject& data);
|
void transactionJSONCallback(const QJsonObject& data);
|
||||||
|
|
||||||
void restart();
|
void restart();
|
||||||
|
|
||||||
void processRequestAssignmentPacket(QSharedPointer<NLPacket> packet);
|
void processRequestAssignmentPacket(QSharedPointer<NLPacket> packet);
|
||||||
void processConnectRequestPacket(QSharedPointer<NLPacket> packet);
|
|
||||||
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||||
void processNodeJSONStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
void processNodeJSONStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||||
void processPathQueryPacket(QSharedPointer<NLPacket> packet);
|
void processPathQueryPacket(QSharedPointer<NLPacket> packet);
|
||||||
void processICEPingPacket(QSharedPointer<NLPacket> packet);
|
|
||||||
void processICEPingReplyPacket(QSharedPointer<NLPacket> packet);
|
|
||||||
void processICEPeerInformationPacket(QSharedPointer<NLPacket> packet);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void aboutToQuit();
|
void aboutToQuit();
|
||||||
|
@ -74,7 +70,9 @@ private slots:
|
||||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||||
void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); }
|
void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); }
|
||||||
void sendHeartbeatToIceServer();
|
void sendHeartbeatToIceServer();
|
||||||
void handlePeerPingTimeout();
|
|
||||||
|
void handleConnectedNode(SharedNodePointer newNode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||||
bool optionallySetupOAuth();
|
bool optionallySetupOAuth();
|
||||||
|
@ -87,20 +85,9 @@ private:
|
||||||
void setupAutomaticNetworking();
|
void setupAutomaticNetworking();
|
||||||
void sendHeartbeatToDataServer(const QString& networkAddress);
|
void sendHeartbeatToDataServer(const QString& networkAddress);
|
||||||
|
|
||||||
void pingPunchForConnectingPeer(const SharedNetworkPeer& peer);
|
|
||||||
|
|
||||||
unsigned int countConnectedUsers();
|
unsigned int countConnectedUsers();
|
||||||
bool verifyUserSignature (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn);
|
|
||||||
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
|
|
||||||
const HifiSockAddr& senderSockAddr, QString& reasonReturn);
|
|
||||||
|
|
||||||
void preloadAllowedUserPublicKeys();
|
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr);
|
||||||
void requestUserPublicKey(const QString& username);
|
|
||||||
|
|
||||||
int parseNodeData(QDataStream& packetStream, NodeType_t& nodeType,
|
|
||||||
HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, const HifiSockAddr& senderSockAddr);
|
|
||||||
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
|
|
||||||
const NodeSet& nodeInterestSet);
|
|
||||||
|
|
||||||
QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB);
|
QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB);
|
||||||
void broadcastNewNode(const SharedNodePointer& node);
|
void broadcastNewNode(const SharedNodePointer& node);
|
||||||
|
@ -111,12 +98,11 @@ private:
|
||||||
void populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes);
|
void populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes);
|
||||||
void populateStaticScriptedAssignmentsFromSettings();
|
void populateStaticScriptedAssignmentsFromSettings();
|
||||||
|
|
||||||
SharedAssignmentPointer matchingQueuedAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
|
SharedAssignmentPointer dequeueMatchingAssignment(const QUuid& checkInUUID, NodeType_t nodeType);
|
||||||
SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
|
SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
|
||||||
void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment);
|
|
||||||
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
|
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
|
||||||
void addStaticAssignmentsToQueue();
|
void addStaticAssignmentsToQueue();
|
||||||
|
|
||||||
QUrl oauthRedirectURL();
|
QUrl oauthRedirectURL();
|
||||||
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());
|
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());
|
||||||
|
|
||||||
|
@ -131,13 +117,14 @@ private:
|
||||||
|
|
||||||
QJsonObject jsonForSocket(const HifiSockAddr& socket);
|
QJsonObject jsonForSocket(const HifiSockAddr& socket);
|
||||||
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
|
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
|
||||||
|
|
||||||
|
DomainGatekeeper _gatekeeper;
|
||||||
|
|
||||||
HTTPManager _httpManager;
|
HTTPManager _httpManager;
|
||||||
HTTPSManager* _httpsManager;
|
HTTPSManager* _httpsManager;
|
||||||
|
|
||||||
QHash<QUuid, SharedAssignmentPointer> _allAssignments;
|
QHash<QUuid, SharedAssignmentPointer> _allAssignments;
|
||||||
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
||||||
QHash<QUuid, PendingAssignedNodeData*> _pendingAssignedNodes;
|
|
||||||
TransactionHash _pendingAssignmentCredits;
|
TransactionHash _pendingAssignmentCredits;
|
||||||
|
|
||||||
bool _isUsingDTLS;
|
bool _isUsingDTLS;
|
||||||
|
@ -149,18 +136,14 @@ private:
|
||||||
|
|
||||||
QSet<QUuid> _webAuthenticationStateSet;
|
QSet<QUuid> _webAuthenticationStateSet;
|
||||||
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
|
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
|
||||||
|
|
||||||
QHash<QString, QUuid> _connectionTokenHash;
|
|
||||||
|
|
||||||
QHash<QString, QByteArray> _userPublicKeys;
|
|
||||||
|
|
||||||
QHash<QUuid, SharedNetworkPeer> _icePeers;
|
|
||||||
|
|
||||||
QString _automaticNetworkingSetting;
|
QString _automaticNetworkingSetting;
|
||||||
|
|
||||||
DomainServerSettingsManager _settingsManager;
|
DomainServerSettingsManager _settingsManager;
|
||||||
|
|
||||||
HifiSockAddr _iceServerSocket;
|
HifiSockAddr _iceServerSocket;
|
||||||
|
|
||||||
|
friend class DomainGatekeeper;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
42
domain-server/src/NodeConnectionData.cpp
Normal file
42
domain-server/src/NodeConnectionData.cpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// NodeConnectionData.cpp
|
||||||
|
// domain-server/src
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2015-08-24.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "NodeConnectionData.h"
|
||||||
|
|
||||||
|
NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, const HifiSockAddr& senderSockAddr,
|
||||||
|
bool isConnectRequest) {
|
||||||
|
NodeConnectionData newHeader;
|
||||||
|
|
||||||
|
if (isConnectRequest) {
|
||||||
|
dataStream >> newHeader.connectUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStream >> newHeader.nodeType
|
||||||
|
>> newHeader.publicSockAddr >> newHeader.localSockAddr
|
||||||
|
>> newHeader.interestList;
|
||||||
|
|
||||||
|
newHeader.senderSockAddr = senderSockAddr;
|
||||||
|
|
||||||
|
if (newHeader.publicSockAddr.getAddress().isNull()) {
|
||||||
|
// this node wants to use us its STUN server
|
||||||
|
// so set the node public address to whatever we perceive the public address to be
|
||||||
|
|
||||||
|
// if the sender is on our box then leave its public address to 0 so that
|
||||||
|
// other users attempt to reach it on the same address they have for the domain-server
|
||||||
|
if (senderSockAddr.getAddress().isLoopback()) {
|
||||||
|
newHeader.publicSockAddr.setAddress(QHostAddress());
|
||||||
|
} else {
|
||||||
|
newHeader.publicSockAddr.setAddress(senderSockAddr.getAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newHeader;
|
||||||
|
}
|
33
domain-server/src/NodeConnectionData.h
Normal file
33
domain-server/src/NodeConnectionData.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// NodeConnectionData.h
|
||||||
|
// domain-server/src
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2015-08-24.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_NodeConnectionData_h
|
||||||
|
#define hifi_NodeConnectionData_h
|
||||||
|
|
||||||
|
#include <Node.h>
|
||||||
|
|
||||||
|
class NodeConnectionData {
|
||||||
|
public:
|
||||||
|
static NodeConnectionData fromDataStream(QDataStream& dataStream, const HifiSockAddr& senderSockAddr,
|
||||||
|
bool isConnectRequest = true);
|
||||||
|
|
||||||
|
QUuid connectUUID;
|
||||||
|
NodeType_t nodeType;
|
||||||
|
HifiSockAddr publicSockAddr;
|
||||||
|
HifiSockAddr localSockAddr;
|
||||||
|
HifiSockAddr senderSockAddr;
|
||||||
|
QList<NodeType_t> interestList;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // hifi_NodeConnectionData_h
|
|
@ -16,5 +16,5 @@ PendingAssignedNodeData::PendingAssignedNodeData(const QUuid& assignmentUUID, co
|
||||||
_walletUUID(walletUUID),
|
_walletUUID(walletUUID),
|
||||||
_nodeVersion(nodeVersion)
|
_nodeVersion(nodeVersion)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,4 +34,4 @@ private:
|
||||||
QString _nodeVersion;
|
QString _nodeVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_PendingAssignedNodeData_h
|
#endif // hifi_PendingAssignedNodeData_h
|
||||||
|
|
|
@ -88,6 +88,4 @@ if (ANDROID)
|
||||||
|
|
||||||
endif (ANDROID)
|
endif (ANDROID)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME ice-server)
|
set(TARGET_NAME ice-server)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# setup the project and link required Qt modules
|
# setup the project and link required Qt modules
|
||||||
setup_hifi_project(Network)
|
setup_hifi_project(Network)
|
||||||
|
|
||||||
|
|
|
@ -205,6 +205,4 @@ else (APPLE)
|
||||||
endif()
|
endif()
|
||||||
endif (APPLE)
|
endif (APPLE)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
|
|
|
@ -189,6 +189,11 @@ static QTimer* billboardPacketTimer = NULL;
|
||||||
static QTimer* checkFPStimer = NULL;
|
static QTimer* checkFPStimer = NULL;
|
||||||
static QTimer* idleTimer = NULL;
|
static QTimer* idleTimer = NULL;
|
||||||
|
|
||||||
|
static const unsigned int TARGET_SIM_FRAMERATE = 60;
|
||||||
|
static const unsigned int THROTTLED_SIM_FRAMERATE = 15;
|
||||||
|
static const int TARGET_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / TARGET_SIM_FRAMERATE;
|
||||||
|
static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;
|
||||||
|
|
||||||
const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml";
|
const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml";
|
||||||
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
|
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
|
||||||
|
|
||||||
|
@ -347,7 +352,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_trayIcon(new QSystemTrayIcon(_window)),
|
_trayIcon(new QSystemTrayIcon(_window)),
|
||||||
_lastNackTime(usecTimestampNow()),
|
_lastNackTime(usecTimestampNow()),
|
||||||
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
||||||
_isVSyncOn(true),
|
|
||||||
_isThrottleFPSEnabled(true),
|
_isThrottleFPSEnabled(true),
|
||||||
_aboutToQuit(false),
|
_aboutToQuit(false),
|
||||||
_notifiedPacketVersionMismatchThisDomain(false),
|
_notifiedPacketVersionMismatchThisDomain(false),
|
||||||
|
@ -745,8 +749,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
|
connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
|
||||||
|
|
||||||
setVSyncEnabled(); // make sure VSync is set properly at startup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::aboutToQuit() {
|
void Application::aboutToQuit() {
|
||||||
|
@ -920,7 +922,7 @@ void Application::initializeGL() {
|
||||||
// call our idle function whenever we can
|
// call our idle function whenever we can
|
||||||
idleTimer = new QTimer(this);
|
idleTimer = new QTimer(this);
|
||||||
connect(idleTimer, SIGNAL(timeout()), SLOT(idle()));
|
connect(idleTimer, SIGNAL(timeout()), SLOT(idle()));
|
||||||
idleTimer->start(0);
|
idleTimer->start(TARGET_SIM_FRAME_PERIOD_MS);
|
||||||
_idleLoopStdev.reset();
|
_idleLoopStdev.reset();
|
||||||
|
|
||||||
if (_justStarted) {
|
if (_justStarted) {
|
||||||
|
@ -2033,38 +2035,38 @@ void Application::checkFPS() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::idle() {
|
void Application::idle() {
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
|
||||||
static SimpleAverage<float> interIdleDurations;
|
|
||||||
|
|
||||||
static uint64_t lastIdleStart{ 0 };
|
|
||||||
static uint64_t lastIdleEnd{ 0 };
|
|
||||||
uint64_t now = usecTimestampNow();
|
|
||||||
uint64_t idleStartToStartDuration = now - lastIdleStart;
|
|
||||||
|
|
||||||
if (lastIdleStart > 0 && idleStartToStartDuration > 0) {
|
|
||||||
_simsPerSecond.updateAverage((float)USECS_PER_SECOND / (float)idleStartToStartDuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastIdleStart = now;
|
|
||||||
|
|
||||||
if (lastIdleEnd != 0) {
|
|
||||||
interIdleDurations.update(now - lastIdleEnd);
|
|
||||||
static uint64_t lastReportTime = now;
|
|
||||||
if ((now - lastReportTime) >= (USECS_PER_SECOND)) {
|
|
||||||
static QString LOGLINE("Average inter-idle time: %1 us for %2 samples");
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::LogExtraTimings)) {
|
|
||||||
qCDebug(interfaceapp_timing) << LOGLINE.arg((int)interIdleDurations.getAverage()).arg(interIdleDurations.getCount());
|
|
||||||
}
|
|
||||||
interIdleDurations.reset();
|
|
||||||
lastReportTime = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PerformanceTimer perfTimer("idle");
|
|
||||||
if (_aboutToQuit) {
|
if (_aboutToQuit) {
|
||||||
return; // bail early, nothing to do here.
|
return; // bail early, nothing to do here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// depending on whether we're throttling or not.
|
||||||
|
// Once rendering is off on another thread we should be able to have Application::idle run at start(0) in
|
||||||
|
// perpetuity and not expect events to get backed up.
|
||||||
|
bool isThrottled = getActiveDisplayPlugin()->isThrottled();
|
||||||
|
// Only run simulation code if more than the targetFramePeriod have passed since last time we ran
|
||||||
|
// This attempts to lock the simulation at 60 updates per second, regardless of framerate
|
||||||
|
float timeSinceLastUpdateUs = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC;
|
||||||
|
float secondsSinceLastUpdate = timeSinceLastUpdateUs / USECS_PER_SECOND;
|
||||||
|
|
||||||
|
if (isThrottled && (timeSinceLastUpdateUs / USECS_PER_MSEC) < THROTTLED_SIM_FRAME_PERIOD_MS) {
|
||||||
|
return; // bail early, we're throttled and not enough time has elapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastTimeUpdated.start();
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
|
uint64_t now = usecTimestampNow();
|
||||||
|
static uint64_t lastIdleStart{ now };
|
||||||
|
uint64_t idleStartToStartDuration = now - lastIdleStart;
|
||||||
|
if (idleStartToStartDuration != 0) {
|
||||||
|
_simsPerSecond.updateAverage((float)USECS_PER_SECOND / (float)idleStartToStartDuration);
|
||||||
|
}
|
||||||
|
lastIdleStart = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
PerformanceTimer perfTimer("idle");
|
||||||
// Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds
|
// Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds
|
||||||
if (!_keyboardFocusedItem.isInvalidID()) {
|
if (!_keyboardFocusedItem.isInvalidID()) {
|
||||||
const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus
|
const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus
|
||||||
|
@ -2080,69 +2082,42 @@ void Application::idle() {
|
||||||
bool showWarnings = getLogger()->extraDebugging();
|
bool showWarnings = getLogger()->extraDebugging();
|
||||||
PerformanceWarning warn(showWarnings, "idle()");
|
PerformanceWarning warn(showWarnings, "idle()");
|
||||||
|
|
||||||
// Only run simulation code if more than the targetFramePeriod have passed since last time we ran
|
{
|
||||||
double targetFramePeriod = 0.0;
|
PerformanceTimer perfTimer("update");
|
||||||
unsigned int targetFramerate = getRenderTargetFramerate();
|
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
|
||||||
if (targetFramerate > 0) {
|
static const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
||||||
targetFramePeriod = 1000.0 / targetFramerate;
|
update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS));
|
||||||
}
|
}
|
||||||
double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0;
|
{
|
||||||
if (timeSinceLastUpdate > targetFramePeriod) {
|
PerformanceTimer perfTimer("pluginIdle");
|
||||||
_lastTimeUpdated.start();
|
PerformanceWarning warn(showWarnings, "Application::idle()... pluginIdle()");
|
||||||
{
|
getActiveDisplayPlugin()->idle();
|
||||||
PerformanceTimer perfTimer("update");
|
auto inputPlugins = PluginManager::getInstance()->getInputPlugins();
|
||||||
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
|
foreach(auto inputPlugin, inputPlugins) {
|
||||||
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
QString name = inputPlugin->getName();
|
||||||
PROFILE_RANGE(__FUNCTION__ "/idleUpdate");
|
QAction* action = Menu::getInstance()->getActionForOption(name);
|
||||||
update(glm::clamp((float)timeSinceLastUpdate / 1000.0f, 0.0f, BIGGEST_DELTA_TIME_SECS));
|
if (action && action->isChecked()) {
|
||||||
}
|
inputPlugin->idle();
|
||||||
{
|
|
||||||
PerformanceTimer perfTimer("updateGL");
|
|
||||||
PerformanceWarning warn(showWarnings, "Application::idle()... updateGL()");
|
|
||||||
getActiveDisplayPlugin()->idle();
|
|
||||||
auto inputPlugins = PluginManager::getInstance()->getInputPlugins();
|
|
||||||
foreach(auto inputPlugin, inputPlugins) {
|
|
||||||
QString name = inputPlugin->getName();
|
|
||||||
QAction* action = Menu::getInstance()->getActionForOption(name);
|
|
||||||
if (action && action->isChecked()) {
|
|
||||||
inputPlugin->idle();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
}
|
||||||
PerformanceTimer perfTimer("rest");
|
{
|
||||||
PerformanceWarning warn(showWarnings, "Application::idle()... rest of it");
|
PerformanceTimer perfTimer("rest");
|
||||||
_idleLoopStdev.addValue(timeSinceLastUpdate);
|
PerformanceWarning warn(showWarnings, "Application::idle()... rest of it");
|
||||||
|
_idleLoopStdev.addValue(secondsSinceLastUpdate);
|
||||||
|
|
||||||
// Record standard deviation and reset counter if needed
|
// Record standard deviation and reset counter if needed
|
||||||
const int STDEV_SAMPLES = 500;
|
const int STDEV_SAMPLES = 500;
|
||||||
if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) {
|
if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) {
|
||||||
_idleLoopMeasuredJitter = _idleLoopStdev.getStDev();
|
_idleLoopMeasuredJitter = _idleLoopStdev.getStDev();
|
||||||
_idleLoopStdev.reset();
|
_idleLoopStdev.reset();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float secondsSinceLastUpdate = (float)timeSinceLastUpdate / 1000.0f;
|
|
||||||
_overlayConductor.update(secondsSinceLastUpdate);
|
|
||||||
|
|
||||||
// depending on whether we're throttling or not.
|
|
||||||
// Once rendering is off on another thread we should be able to have Application::idle run at start(0) in
|
|
||||||
// perpetuity and not expect events to get backed up.
|
|
||||||
|
|
||||||
bool isThrottled = getActiveDisplayPlugin()->isThrottled();
|
|
||||||
static const int THROTTLED_IDLE_TIMER_DELAY = MSECS_PER_SECOND / 15;
|
|
||||||
static const int IDLE_TIMER_DELAY_MS = 2;
|
|
||||||
int desiredInterval = isThrottled ? THROTTLED_IDLE_TIMER_DELAY : IDLE_TIMER_DELAY_MS;
|
|
||||||
//qDebug() << "isThrottled:" << isThrottled << "desiredInterval:" << desiredInterval;
|
|
||||||
|
|
||||||
if (idleTimer->interval() != desiredInterval) {
|
|
||||||
idleTimer->start(desiredInterval);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_overlayConductor.update(secondsSinceLastUpdate);
|
||||||
|
|
||||||
// check for any requested background downloads.
|
// check for any requested background downloads.
|
||||||
emit checkBackgroundDownloads();
|
emit checkBackgroundDownloads();
|
||||||
lastIdleEnd = usecTimestampNow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Application::getAverageSimsPerSecond() {
|
float Application::getAverageSimsPerSecond() {
|
||||||
|
@ -4481,90 +4456,10 @@ void Application::takeSnapshot() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setVSyncEnabled() {
|
|
||||||
_glWidget->makeCurrent();
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
bool vsyncOn = Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn);
|
|
||||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
|
||||||
wglSwapIntervalEXT(vsyncOn);
|
|
||||||
int swapInterval = wglGetSwapIntervalEXT();
|
|
||||||
qCDebug(interfaceapp, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
|
||||||
} else {
|
|
||||||
qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n");
|
|
||||||
}
|
|
||||||
#elif defined(Q_OS_LINUX)
|
|
||||||
// TODO: write the poper code for linux
|
|
||||||
/*
|
|
||||||
if (glQueryExtension.... ("GLX_EXT_swap_control")) {
|
|
||||||
glxSwapIntervalEXT(vsyncOn);
|
|
||||||
int swapInterval = xglGetSwapIntervalEXT();
|
|
||||||
_isVSyncOn = swapInterval;
|
|
||||||
qCDebug(interfaceapp, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
|
||||||
} else {
|
|
||||||
qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#else
|
|
||||||
qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n");
|
|
||||||
#endif
|
|
||||||
_offscreenContext->makeCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::setThrottleFPSEnabled() {
|
void Application::setThrottleFPSEnabled() {
|
||||||
_isThrottleFPSEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ThrottleFPSIfNotFocus);
|
_isThrottleFPSEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ThrottleFPSIfNotFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::isVSyncOn() const {
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
|
||||||
int swapInterval = wglGetSwapIntervalEXT();
|
|
||||||
return (swapInterval > 0);
|
|
||||||
}
|
|
||||||
#elif defined(Q_OS_LINUX)
|
|
||||||
// TODO: write the poper code for linux
|
|
||||||
/*
|
|
||||||
if (glQueryExtension.... ("GLX_EXT_swap_control")) {
|
|
||||||
int swapInterval = xglGetSwapIntervalEXT();
|
|
||||||
return (swapInterval > 0);
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::isVSyncEditable() const {
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#elif defined(Q_OS_LINUX)
|
|
||||||
// TODO: write the poper code for linux
|
|
||||||
/*
|
|
||||||
if (glQueryExtension.... ("GLX_EXT_swap_control")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int Application::getRenderTargetFramerate() const {
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateUnlimited)) {
|
|
||||||
return 0;
|
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate60)) {
|
|
||||||
return 60;
|
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate50)) {
|
|
||||||
return 50;
|
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate40)) {
|
|
||||||
return 40;
|
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate30)) {
|
|
||||||
return 30;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float Application::getRenderResolutionScale() const {
|
float Application::getRenderResolutionScale() const {
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
|
@ -300,9 +300,6 @@ public:
|
||||||
float getRenderResolutionScale() const;
|
float getRenderResolutionScale() const;
|
||||||
int getRenderAmbientLight() const;
|
int getRenderAmbientLight() const;
|
||||||
|
|
||||||
unsigned int getRenderTargetFramerate() const;
|
|
||||||
bool isVSyncOn() const;
|
|
||||||
bool isVSyncEditable() const;
|
|
||||||
bool isAboutToQuit() const { return _aboutToQuit; }
|
bool isAboutToQuit() const { return _aboutToQuit; }
|
||||||
|
|
||||||
// the isHMDmode is true whenever we use the interface from an HMD and not a standard flat display
|
// the isHMDmode is true whenever we use the interface from an HMD and not a standard flat display
|
||||||
|
@ -411,8 +408,6 @@ public slots:
|
||||||
|
|
||||||
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
||||||
|
|
||||||
void setVSyncEnabled();
|
|
||||||
|
|
||||||
void setThrottleFPSEnabled();
|
void setThrottleFPSEnabled();
|
||||||
bool isThrottleFPSEnabled() { return _isThrottleFPSEnabled; }
|
bool isThrottleFPSEnabled() { return _isThrottleFPSEnabled; }
|
||||||
|
|
||||||
|
@ -626,7 +621,6 @@ private:
|
||||||
quint64 _lastNackTime;
|
quint64 _lastNackTime;
|
||||||
quint64 _lastSendDownstreamAudioStats;
|
quint64 _lastSendDownstreamAudioStats;
|
||||||
|
|
||||||
bool _isVSyncOn;
|
|
||||||
bool _isThrottleFPSEnabled;
|
bool _isThrottleFPSEnabled;
|
||||||
|
|
||||||
bool _aboutToQuit;
|
bool _aboutToQuit;
|
||||||
|
|
|
@ -222,7 +222,7 @@ Menu::Menu() {
|
||||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
||||||
qApp, SLOT(packageModel()));
|
qApp, SLOT(packageModel()));
|
||||||
|
|
||||||
MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH);
|
MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH());
|
||||||
{
|
{
|
||||||
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
|
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
|
||||||
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
|
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
|
||||||
|
@ -333,25 +333,8 @@ Menu::Menu() {
|
||||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
|
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
|
||||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
|
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
|
||||||
|
|
||||||
{
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true,
|
||||||
MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate);
|
|
||||||
QActionGroup* framerateGroup = new QActionGroup(framerateMenu);
|
|
||||||
framerateGroup->setExclusive(true);
|
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true));
|
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false));
|
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false));
|
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false));
|
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false));
|
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
#else
|
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true,
|
|
||||||
qApp, SLOT(setVSyncEnabled()));
|
|
||||||
#endif
|
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true,
|
|
||||||
qApp, SLOT(setThrottleFPSEnabled()));
|
qApp, SLOT(setThrottleFPSEnabled()));
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
||||||
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
|
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
|
||||||
|
|
|
@ -238,13 +238,6 @@ namespace MenuOption {
|
||||||
const QString RenderLookAtTargets = "Show Look-at Targets";
|
const QString RenderLookAtTargets = "Show Look-at Targets";
|
||||||
const QString RenderLookAtVectors = "Show Look-at Vectors";
|
const QString RenderLookAtVectors = "Show Look-at Vectors";
|
||||||
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
|
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
|
||||||
const QString RenderTargetFramerate = "Framerate";
|
|
||||||
const QString RenderTargetFramerateUnlimited = "Unlimited";
|
|
||||||
const QString RenderTargetFramerate60 = "60";
|
|
||||||
const QString RenderTargetFramerate50 = "50";
|
|
||||||
const QString RenderTargetFramerate40 = "40";
|
|
||||||
const QString RenderTargetFramerate30 = "30";
|
|
||||||
const QString RenderTargetFramerateVSyncOn = "V-Sync On";
|
|
||||||
const QString RenderResolution = "Scale Resolution";
|
const QString RenderResolution = "Scale Resolution";
|
||||||
const QString RenderResolutionOne = "1";
|
const QString RenderResolutionOne = "1";
|
||||||
const QString RenderResolutionTwoThird = "2/3";
|
const QString RenderResolutionTwoThird = "2/3";
|
||||||
|
|
|
@ -131,13 +131,20 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
//
|
//
|
||||||
// Thus this should really only be ... else if (_owningAvatar->getHead()->isLookingAtMe()) {...
|
// Thus this should really only be ... else if (_owningAvatar->getHead()->isLookingAtMe()) {...
|
||||||
// However, in the !isLookingAtMe case, the eyes aren't rotating the way they should right now.
|
// However, in the !isLookingAtMe case, the eyes aren't rotating the way they should right now.
|
||||||
// (They latch their looking at me position.) We will revisit that as priorities allow.
|
// We will revisit that as priorities allow, and particularly after the new rig/animation/joints.
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
Head* head = _owningAvatar->getHead();
|
Head* head = _owningAvatar->getHead();
|
||||||
_rig->updateEyeJoints(geometry.leftEyeJointIndex, geometry.rightEyeJointIndex,
|
// If the head is not positioned, updateEyeJoints won't get the math right
|
||||||
getTranslation(), getRotation(),
|
glm::quat headOrientation;
|
||||||
head->getFinalOrientationInWorldFrame(), head->getCorrectedLookAtPosition());
|
_rig->getJointRotation(geometry.headJointIndex, headOrientation);
|
||||||
}
|
glm::vec3 eulers = safeEulerAngles(headOrientation);
|
||||||
|
head->setBasePitch(glm::degrees(-eulers.x));
|
||||||
|
head->setBaseYaw(glm::degrees(eulers.y));
|
||||||
|
head->setBaseRoll(glm::degrees(-eulers.z));
|
||||||
|
_rig->updateEyeJoints(geometry.leftEyeJointIndex, geometry.rightEyeJointIndex,
|
||||||
|
getTranslation(), getRotation(),
|
||||||
|
head->getFinalOrientationInWorldFrame(), head->getCorrectedLookAtPosition());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
|
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
|
||||||
|
|
|
@ -3,6 +3,4 @@ set(TARGET_NAME animation)
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library(Network Script)
|
setup_hifi_library(Network Script)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
link_hifi_libraries(shared gpu model fbx)
|
link_hifi_libraries(shared gpu model fbx)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME audio-client)
|
set(TARGET_NAME audio-client)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library(Network Multimedia)
|
setup_hifi_library(Network Multimedia)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME audio)
|
set(TARGET_NAME audio)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library(Network)
|
setup_hifi_library(Network)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
set(TARGET_NAME auto-updater)
|
set(TARGET_NAME auto-updater)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
setup_hifi_library(Network)
|
setup_hifi_library(Network)
|
||||||
link_hifi_libraries(shared networking)
|
link_hifi_libraries(shared networking)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME avatars)
|
set(TARGET_NAME avatars)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library(Network Script)
|
setup_hifi_library(Network Script)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME display-plugins)
|
set(TARGET_NAME display-plugins)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library(OpenGL)
|
setup_hifi_library(OpenGL)
|
||||||
|
|
||||||
|
|
|
@ -7,21 +7,35 @@
|
||||||
//
|
//
|
||||||
#include "Basic2DWindowOpenGLDisplayPlugin.h"
|
#include "Basic2DWindowOpenGLDisplayPlugin.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
#include <QtWidgets/QAction>
|
||||||
|
|
||||||
#include <plugins/PluginContainer.h>
|
#include <plugins/PluginContainer.h>
|
||||||
#include <QWindow>
|
|
||||||
#include <QGuiApplication>
|
|
||||||
|
|
||||||
const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display");
|
const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display");
|
||||||
|
|
||||||
static const QString FULLSCREEN = "Fullscreen";
|
static const QString FULLSCREEN = "Fullscreen";
|
||||||
|
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
|
||||||
|
static const QString FRAMERATE_UNLIMITED = "Unlimited";
|
||||||
|
static const QString FRAMERATE_60 = "60";
|
||||||
|
static const QString FRAMERATE_50 = "50";
|
||||||
|
static const QString FRAMERATE_40 = "40";
|
||||||
|
static const QString FRAMERATE_30 = "30";
|
||||||
|
static const QString VSYNC_ON = "V-Sync On";
|
||||||
|
|
||||||
const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const {
|
const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<QAction*> _framerateActions;
|
||||||
|
QAction* _vsyncAction{ nullptr };
|
||||||
|
|
||||||
void Basic2DWindowOpenGLDisplayPlugin::activate() {
|
void Basic2DWindowOpenGLDisplayPlugin::activate() {
|
||||||
CONTAINER->addMenu(MENU_PATH);
|
_framerateActions.clear();
|
||||||
CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN,
|
CONTAINER->addMenuItem(MENU_PATH(), FULLSCREEN,
|
||||||
[this](bool clicked) {
|
[this](bool clicked) {
|
||||||
if (clicked) {
|
if (clicked) {
|
||||||
CONTAINER->setFullscreen(getFullscreenTarget());
|
CONTAINER->setFullscreen(getFullscreenTarget());
|
||||||
|
@ -29,18 +43,65 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() {
|
||||||
CONTAINER->unsetFullscreen();
|
CONTAINER->unsetFullscreen();
|
||||||
}
|
}
|
||||||
}, true, false);
|
}, true, false);
|
||||||
|
CONTAINER->addMenu(FRAMERATE);
|
||||||
|
_framerateActions.push_back(
|
||||||
|
CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_UNLIMITED,
|
||||||
|
[this](bool) { updateFramerate(); }, true, true, FRAMERATE));
|
||||||
|
_framerateActions.push_back(
|
||||||
|
CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_60,
|
||||||
|
[this](bool) { updateFramerate(); }, true, false, FRAMERATE));
|
||||||
|
_framerateActions.push_back(
|
||||||
|
CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_50,
|
||||||
|
[this](bool) { updateFramerate(); }, true, false, FRAMERATE));
|
||||||
|
_framerateActions.push_back(
|
||||||
|
CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_40,
|
||||||
|
[this](bool) { updateFramerate(); }, true, false, FRAMERATE));
|
||||||
|
_framerateActions.push_back(
|
||||||
|
CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_30,
|
||||||
|
[this](bool) { updateFramerate(); }, true, false, FRAMERATE));
|
||||||
|
|
||||||
WindowOpenGLDisplayPlugin::activate();
|
WindowOpenGLDisplayPlugin::activate();
|
||||||
|
|
||||||
|
// Vsync detection happens in the parent class activate, so we need to check after that
|
||||||
|
if (_vsyncSupported) {
|
||||||
|
_vsyncAction = CONTAINER->addMenuItem(MENU_PATH(), VSYNC_ON, [this](bool) {}, true, true);
|
||||||
|
} else {
|
||||||
|
_vsyncAction = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFramerate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Basic2DWindowOpenGLDisplayPlugin::deactivate() {
|
void Basic2DWindowOpenGLDisplayPlugin::deactivate() {
|
||||||
WindowOpenGLDisplayPlugin::deactivate();
|
WindowOpenGLDisplayPlugin::deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const {
|
void Basic2DWindowOpenGLDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {
|
||||||
static const int THROTTLED_PAINT_TIMER_DELAY = MSECS_PER_SECOND / 15;
|
if (_vsyncAction) {
|
||||||
static const int PAINT_TIMER_DELAY_MS = 1;
|
bool wantVsync = _vsyncAction->isChecked();
|
||||||
|
bool vsyncEnabed = isVsyncEnabled();
|
||||||
|
if (vsyncEnabed ^ wantVsync) {
|
||||||
|
enableVsync(wantVsync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return isThrottled ? THROTTLED_PAINT_TIMER_DELAY : PAINT_TIMER_DELAY_MS;
|
WindowOpenGLDisplayPlugin::display(sceneTexture, sceneSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const {
|
||||||
|
static const int THROTTLED_PAINT_TIMER_DELAY_MS = MSECS_PER_SECOND / 15;
|
||||||
|
static const int ULIMIITED_PAINT_TIMER_DELAY_MS = 1;
|
||||||
|
int result = ULIMIITED_PAINT_TIMER_DELAY_MS;
|
||||||
|
if (_isThrottled) {
|
||||||
|
result = THROTTLED_PAINT_TIMER_DELAY_MS;
|
||||||
|
}
|
||||||
|
if (0 != _framerateTarget) {
|
||||||
|
result = MSECS_PER_SECOND / _framerateTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "New interval " << result;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const {
|
bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const {
|
||||||
|
@ -49,14 +110,42 @@ bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const {
|
||||||
bool shouldThrottle = (!CONTAINER->isForeground() && CONTAINER->isOptionChecked(ThrottleFPSIfNotFocus));
|
bool shouldThrottle = (!CONTAINER->isForeground() && CONTAINER->isOptionChecked(ThrottleFPSIfNotFocus));
|
||||||
|
|
||||||
if (_isThrottled != shouldThrottle) {
|
if (_isThrottled != shouldThrottle) {
|
||||||
int desiredInterval = getDesiredInterval(shouldThrottle);
|
|
||||||
_timer.start(desiredInterval);
|
|
||||||
_isThrottled = shouldThrottle;
|
_isThrottled = shouldThrottle;
|
||||||
|
_timer.start(getDesiredInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
return shouldThrottle;
|
return shouldThrottle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
|
||||||
|
QAction* checkedFramerate{ nullptr };
|
||||||
|
foreach(auto action, _framerateActions) {
|
||||||
|
if (action->isChecked()) {
|
||||||
|
checkedFramerate = action;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_framerateTarget = 0;
|
||||||
|
if (checkedFramerate) {
|
||||||
|
QString actionText = checkedFramerate->text();
|
||||||
|
if (FRAMERATE_60 == actionText) {
|
||||||
|
_framerateTarget = 60;
|
||||||
|
} else if (FRAMERATE_50 == actionText) {
|
||||||
|
_framerateTarget = 50;
|
||||||
|
} else if (FRAMERATE_40 == actionText) {
|
||||||
|
_framerateTarget = 40;
|
||||||
|
} else if (FRAMERATE_30 == actionText) {
|
||||||
|
_framerateTarget = 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int newInterval = getDesiredInterval();
|
||||||
|
qDebug() << newInterval;
|
||||||
|
_timer.start(getDesiredInterval());
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME target the screen the window is currently on
|
// FIXME target the screen the window is currently on
|
||||||
QScreen* Basic2DWindowOpenGLDisplayPlugin::getFullscreenTarget() {
|
QScreen* Basic2DWindowOpenGLDisplayPlugin::getFullscreenTarget() {
|
||||||
return qApp->primaryScreen();
|
return qApp->primaryScreen();
|
||||||
|
|
|
@ -14,19 +14,23 @@ class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
virtual const QString & getName() const override;
|
||||||
|
|
||||||
virtual void activate() override;
|
virtual void activate() override;
|
||||||
virtual void deactivate() override;
|
virtual void deactivate() override;
|
||||||
|
|
||||||
virtual const QString & getName() const override;
|
virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) override;
|
||||||
|
|
||||||
virtual bool isThrottled() const override;
|
virtual bool isThrottled() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int getDesiredInterval(bool isThrottled) const;
|
int getDesiredInterval() const;
|
||||||
mutable bool _isThrottled = false;
|
mutable bool _isThrottled = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void updateFramerate();
|
||||||
static const QString NAME;
|
static const QString NAME;
|
||||||
QScreen* getFullscreenTarget();
|
QScreen* getFullscreenTarget();
|
||||||
|
uint32_t _framerateTarget{ 0 };
|
||||||
int _fullscreenTarget{ -1 };
|
int _fullscreenTarget{ -1 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,10 @@
|
||||||
#include "oculus/OculusDisplayPlugin.h"
|
#include "oculus/OculusDisplayPlugin.h"
|
||||||
#include "oculus/OculusLegacyDisplayPlugin.h"
|
#include "oculus/OculusLegacyDisplayPlugin.h"
|
||||||
|
|
||||||
const QString DisplayPlugin::MENU_PATH{ "Display" };
|
const QString& DisplayPlugin::MENU_PATH() {
|
||||||
|
static const QString value = "Display";
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
|
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
|
||||||
DisplayPluginList getDisplayPlugins() {
|
DisplayPluginList getDisplayPlugins() {
|
||||||
|
|
|
@ -120,7 +120,7 @@ public:
|
||||||
virtual void resetSensors() {}
|
virtual void resetSensors() {}
|
||||||
virtual float devicePixelRatio() { return 1.0; }
|
virtual float devicePixelRatio() { return 1.0; }
|
||||||
|
|
||||||
static const QString MENU_PATH;
|
static const QString& MENU_PATH();
|
||||||
signals:
|
signals:
|
||||||
void recommendedFramebufferSizeChanged(const QSize & size);
|
void recommendedFramebufferSizeChanged(const QSize & size);
|
||||||
void requestRender();
|
void requestRender();
|
||||||
|
|
|
@ -38,6 +38,11 @@ void OpenGLDisplayPlugin::finishFrame() {
|
||||||
|
|
||||||
void OpenGLDisplayPlugin::customizeContext() {
|
void OpenGLDisplayPlugin::customizeContext() {
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
|
// TODO: write the poper code for linux
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
_vsyncSupported = wglewGetExtension("WGL_EXT_swap_control");
|
||||||
|
#endif
|
||||||
|
|
||||||
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
|
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
|
||||||
Context::Disable(Capability::Blend);
|
Context::Disable(Capability::Blend);
|
||||||
Context::Disable(Capability::DepthTest);
|
Context::Disable(Capability::DepthTest);
|
||||||
|
@ -46,6 +51,8 @@ void OpenGLDisplayPlugin::customizeContext() {
|
||||||
|
|
||||||
_program = loadDefaultShader();
|
_program = loadDefaultShader();
|
||||||
_plane = loadPlane(_program);
|
_plane = loadPlane(_program);
|
||||||
|
|
||||||
|
enableVsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDisplayPlugin::activate() {
|
void OpenGLDisplayPlugin::activate() {
|
||||||
|
@ -114,4 +121,24 @@ void OpenGLDisplayPlugin::display(
|
||||||
void OpenGLDisplayPlugin::drawUnitQuad() {
|
void OpenGLDisplayPlugin::drawUnitQuad() {
|
||||||
_program->Bind();
|
_program->Bind();
|
||||||
_plane->Draw();
|
_plane->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayPlugin::enableVsync(bool enable) {
|
||||||
|
if (!_vsyncSupported) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
wglSwapIntervalEXT(enable ? 1 : 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDisplayPlugin::isVsyncEnabled() {
|
||||||
|
if (!_vsyncSupported) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
return wglGetSwapIntervalEXT() != 0;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
|
@ -38,9 +38,13 @@ protected:
|
||||||
virtual void doneCurrent() = 0;
|
virtual void doneCurrent() = 0;
|
||||||
virtual void swapBuffers() = 0;
|
virtual void swapBuffers() = 0;
|
||||||
|
|
||||||
|
virtual bool isVsyncEnabled();
|
||||||
|
virtual void enableVsync(bool enable = true);
|
||||||
|
|
||||||
mutable QTimer _timer;
|
mutable QTimer _timer;
|
||||||
ProgramPtr _program;
|
ProgramPtr _program;
|
||||||
ShapeWrapperPtr _plane;
|
ShapeWrapperPtr _plane;
|
||||||
|
bool _vsyncSupported{ false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ void StereoDisplayPlugin::activate() {
|
||||||
if (screen == qApp->primaryScreen()) {
|
if (screen == qApp->primaryScreen()) {
|
||||||
checked = true;
|
checked = true;
|
||||||
}
|
}
|
||||||
auto action = CONTAINER->addMenuItem(MENU_PATH, name,
|
auto action = CONTAINER->addMenuItem(MENU_PATH(), name,
|
||||||
[this](bool clicked) { updateScreen(); }, true, checked, "Screens");
|
[this](bool clicked) { updateScreen(); }, true, checked, "Screens");
|
||||||
_screenActions[i] = action;
|
_screenActions[i] = action;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
set(TARGET_NAME embedded-webserver)
|
set(TARGET_NAME embedded-webserver)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library(Network)
|
setup_hifi_library(Network)
|
||||||
|
|
|
@ -26,6 +26,4 @@ find_package(PolyVox REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS})
|
||||||
target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES})
|
target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES})
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
link_hifi_libraries(shared gpu script-engine render render-utils)
|
link_hifi_libraries(shared gpu script-engine render render-utils)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME entities)
|
set(TARGET_NAME entities)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library(Network Script)
|
setup_hifi_library(Network Script)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,4 @@ add_dependency_external_projects(glm)
|
||||||
find_package(GLM REQUIRED)
|
find_package(GLM REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
link_hifi_libraries(shared networking)
|
link_hifi_libraries(shared networking)
|
||||||
|
|
|
@ -7,6 +7,4 @@ add_dependency_external_projects(glm)
|
||||||
find_package(GLM REQUIRED)
|
find_package(GLM REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
link_hifi_libraries(shared gpu model networking octree)
|
link_hifi_libraries(shared gpu model networking octree)
|
||||||
|
|
|
@ -211,13 +211,17 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (tokenizer.nextToken()) {
|
switch (tokenizer.nextToken()) {
|
||||||
case OBJTokenizer::COMMENT_TOKEN:
|
case OBJTokenizer::COMMENT_TOKEN:
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader MTLLIB comment:" << tokenizer.getComment();
|
qCDebug(modelformat) << "OBJ Reader MTLLIB comment:" << tokenizer.getComment();
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case OBJTokenizer::DATUM_TOKEN:
|
case OBJTokenizer::DATUM_TOKEN:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
materials[matName] = currentMaterial;
|
materials[matName] = currentMaterial;
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader Last material shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << currentMaterial.specularColor << " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << currentMaterial.specularTextureFilename;
|
qCDebug(modelformat) << "OBJ Reader Last material shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << currentMaterial.specularColor << " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << currentMaterial.specularTextureFilename;
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QByteArray token = tokenizer.getDatum();
|
QByteArray token = tokenizer.getDatum();
|
||||||
|
@ -229,14 +233,18 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
||||||
matName = tokenizer.getDatum();
|
matName = tokenizer.getDatum();
|
||||||
currentMaterial = materials[matName];
|
currentMaterial = materials[matName];
|
||||||
currentMaterial.diffuseTextureFilename = "test";
|
currentMaterial.diffuseTextureFilename = "test";
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader Starting new material definition " << matName;
|
qCDebug(modelformat) << "OBJ Reader Starting new material definition " << matName;
|
||||||
|
#endif
|
||||||
currentMaterial.diffuseTextureFilename = "";
|
currentMaterial.diffuseTextureFilename = "";
|
||||||
} else if (token == "Ns") {
|
} else if (token == "Ns") {
|
||||||
currentMaterial.shininess = tokenizer.getFloat();
|
currentMaterial.shininess = tokenizer.getFloat();
|
||||||
} else if ((token == "d") || (token == "Tr")) {
|
} else if ((token == "d") || (token == "Tr")) {
|
||||||
currentMaterial.opacity = tokenizer.getFloat();
|
currentMaterial.opacity = tokenizer.getFloat();
|
||||||
} else if (token == "Ka") {
|
} else if (token == "Ka") {
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3();
|
qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3();
|
||||||
|
#endif
|
||||||
} else if (token == "Kd") {
|
} else if (token == "Kd") {
|
||||||
currentMaterial.diffuseColor = tokenizer.getVec3();
|
currentMaterial.diffuseColor = tokenizer.getVec3();
|
||||||
} else if (token == "Ks") {
|
} else if (token == "Ks") {
|
||||||
|
@ -244,7 +252,9 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
||||||
} else if ((token == "map_Kd") || (token == "map_Ks")) {
|
} else if ((token == "map_Kd") || (token == "map_Ks")) {
|
||||||
QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8();
|
QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8();
|
||||||
if (filename.endsWith(".tga")) {
|
if (filename.endsWith(".tga")) {
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << _url;
|
qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << _url;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (isValidTexture(filename)) {
|
if (isValidTexture(filename)) {
|
||||||
|
@ -254,7 +264,9 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
||||||
currentMaterial.specularTextureFilename = filename;
|
currentMaterial.specularTextureFilename = filename;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader WARNING: " << _url << " ignoring missing texture " << filename;
|
qCDebug(modelformat) << "OBJ Reader WARNING: " << _url << " ignoring missing texture " << filename;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,7 +330,6 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
||||||
}
|
}
|
||||||
QByteArray groupName = tokenizer.getDatum();
|
QByteArray groupName = tokenizer.getDatum();
|
||||||
currentGroup = groupName;
|
currentGroup = groupName;
|
||||||
//qCDebug(modelformat) << "new group:" << groupName;
|
|
||||||
} else if (token == "mtllib" && _url) {
|
} else if (token == "mtllib" && _url) {
|
||||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||||
break;
|
break;
|
||||||
|
@ -330,13 +341,17 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
||||||
librariesSeen[libraryName] = true;
|
librariesSeen[libraryName] = true;
|
||||||
// Throw away any path part of libraryName, and merge against original url.
|
// Throw away any path part of libraryName, and merge against original url.
|
||||||
QUrl libraryUrl = _url->resolved(QUrl(libraryName).fileName());
|
QUrl libraryUrl = _url->resolved(QUrl(libraryName).fileName());
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader new library:" << libraryName << " at:" << libraryUrl;
|
qCDebug(modelformat) << "OBJ Reader new library:" << libraryName << " at:" << libraryUrl;
|
||||||
|
#endif
|
||||||
QNetworkReply* netReply = request(libraryUrl, false);
|
QNetworkReply* netReply = request(libraryUrl, false);
|
||||||
if (netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)) {
|
if (netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)) {
|
||||||
parseMaterialLibrary(netReply);
|
parseMaterialLibrary(netReply);
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader " << libraryName << " did not answer. Got "
|
qCDebug(modelformat) << "OBJ Reader " << libraryName << " did not answer. Got "
|
||||||
<< netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
<< netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
netReply->deleteLater();
|
netReply->deleteLater();
|
||||||
} else if (token == "usemtl") {
|
} else if (token == "usemtl") {
|
||||||
|
@ -344,7 +359,9 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
currentMaterialName = tokenizer.getDatum();
|
currentMaterialName = tokenizer.getDatum();
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader new current material:" << currentMaterialName;
|
qCDebug(modelformat) << "OBJ Reader new current material:" << currentMaterialName;
|
||||||
|
#endif
|
||||||
} else if (token == "v") {
|
} else if (token == "v") {
|
||||||
vertices.append(tokenizer.getVec3());
|
vertices.append(tokenizer.getVec3());
|
||||||
} else if (token == "vn") {
|
} else if (token == "vn") {
|
||||||
|
@ -394,7 +411,6 @@ done:
|
||||||
} else {
|
} else {
|
||||||
faceGroups.append(faces); // We're done with this group. Add the faces.
|
faceGroups.append(faces); // We're done with this group. Add the faces.
|
||||||
}
|
}
|
||||||
//qCDebug(modelformat) << "end group:" << meshPart.materialID << " original faces:" << originalFaceCountForDebugging << " triangles:" << faces.count() << " keep going:" << result;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,13 +491,17 @@ FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping,
|
||||||
OBJFace leadFace = faceGroup[0]; // All the faces in the same group will have the same name and material.
|
OBJFace leadFace = faceGroup[0]; // All the faces in the same group will have the same name and material.
|
||||||
QString groupMaterialName = leadFace.materialName;
|
QString groupMaterialName = leadFace.materialName;
|
||||||
if (groupMaterialName.isEmpty() && (leadFace.textureUVIndices.count() > 0)) {
|
if (groupMaterialName.isEmpty() && (leadFace.textureUVIndices.count() > 0)) {
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader WARNING: " << url
|
qCDebug(modelformat) << "OBJ Reader WARNING: " << url
|
||||||
<< " needs a texture that isn't specified. Using default mechanism.";
|
<< " needs a texture that isn't specified. Using default mechanism.";
|
||||||
|
#endif
|
||||||
groupMaterialName = SMART_DEFAULT_MATERIAL_NAME;
|
groupMaterialName = SMART_DEFAULT_MATERIAL_NAME;
|
||||||
} else if (!groupMaterialName.isEmpty() && !materials.contains(groupMaterialName)) {
|
} else if (!groupMaterialName.isEmpty() && !materials.contains(groupMaterialName)) {
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
qCDebug(modelformat) << "OBJ Reader WARNING: " << url
|
qCDebug(modelformat) << "OBJ Reader WARNING: " << url
|
||||||
<< " specifies a material " << groupMaterialName
|
<< " specifies a material " << groupMaterialName
|
||||||
<< " that is not defined. Using default mechanism.";
|
<< " that is not defined. Using default mechanism.";
|
||||||
|
#endif
|
||||||
groupMaterialName = SMART_DEFAULT_MATERIAL_NAME;
|
groupMaterialName = SMART_DEFAULT_MATERIAL_NAME;
|
||||||
}
|
}
|
||||||
if (!groupMaterialName.isEmpty()) {
|
if (!groupMaterialName.isEmpty()) {
|
||||||
|
@ -496,7 +516,6 @@ FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping,
|
||||||
meshPart._material->setGloss(material->shininess);
|
meshPart._material->setGloss(material->shininess);
|
||||||
meshPart._material->setOpacity(material->opacity);
|
meshPart._material->setOpacity(material->opacity);
|
||||||
}
|
}
|
||||||
// qCDebug(modelformat) << "OBJ Reader part:" << meshPartCount << "name:" << leadFace.groupName << "material:" << groupMaterialName << "diffuse:" << meshPart._material->getDiffuse() << "faces:" << faceGroup.count() << "triangle indices will start with:" << mesh.vertices.count();
|
|
||||||
foreach(OBJFace face, faceGroup) {
|
foreach(OBJFace face, faceGroup) {
|
||||||
glm::vec3 v0 = vertices[face.vertexIndices[0]];
|
glm::vec3 v0 = vertices[face.vertexIndices[0]];
|
||||||
glm::vec3 v1 = vertices[face.vertexIndices[1]];
|
glm::vec3 v1 = vertices[face.vertexIndices[1]];
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME gpu)
|
set(TARGET_NAME gpu)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
AUTOSCRIBE_SHADER_LIB(gpu)
|
AUTOSCRIBE_SHADER_LIB(gpu)
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
|
|
|
@ -33,8 +33,6 @@ endif()
|
||||||
#target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
|
#target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
|
||||||
#target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
|
#target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# perform standard include and linking for found externals
|
# perform standard include and linking for found externals
|
||||||
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@ set(TARGET_NAME model)
|
||||||
|
|
||||||
AUTOSCRIBE_SHADER_LIB(gpu model)
|
AUTOSCRIBE_SHADER_LIB(gpu model)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME networking)
|
set(TARGET_NAME networking)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library(Network)
|
setup_hifi_library(Network)
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ public:
|
||||||
|
|
||||||
SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
||||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||||
bool canAdjustLocks, bool canRez,
|
bool canAdjustLocks = false, bool canRez = false,
|
||||||
const QUuid& connectionSecret = QUuid());
|
const QUuid& connectionSecret = QUuid());
|
||||||
|
|
||||||
bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; }
|
bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; }
|
||||||
|
|
|
@ -20,10 +20,17 @@
|
||||||
class UUIDHasher {
|
class UUIDHasher {
|
||||||
public:
|
public:
|
||||||
size_t operator()(const QUuid& uuid) const {
|
size_t operator()(const QUuid& uuid) const {
|
||||||
return uuid.data1 ^ uuid.data2 ^ (uuid.data3 << 16)
|
return qHash(uuid);
|
||||||
^ ((uuid.data4[0] << 24) | (uuid.data4[1] << 16) | (uuid.data4[2] << 8) | uuid.data4[3])
|
|
||||||
^ ((uuid.data4[4] << 24) | (uuid.data4[5] << 16) | (uuid.data4[6] << 8) | uuid.data4[7]);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_UUIDHasher_h
|
namespace std {
|
||||||
|
template <> struct hash<QUuid> {
|
||||||
|
size_t operator()(const QUuid& uuid) const {
|
||||||
|
return qHash(uuid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // hifi_UUIDHasher_h
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME octree)
|
set(TARGET_NAME octree)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,6 @@
|
||||||
#include "JurisdictionMap.h"
|
#include "JurisdictionMap.h"
|
||||||
#include "SentPacketHistory.h"
|
#include "SentPacketHistory.h"
|
||||||
|
|
||||||
namespace std {
|
|
||||||
template <> struct hash<QUuid> {
|
|
||||||
size_t operator()(const QUuid& uuid) const {
|
|
||||||
return qHash(uuid);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Utility for processing, packing, queueing and sending of outbound edit messages.
|
/// Utility for processing, packing, queueing and sending of outbound edit messages.
|
||||||
class OctreeEditPacketSender : public PacketSender {
|
class OctreeEditPacketSender : public PacketSender {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME physics)
|
set(TARGET_NAME physics)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,4 @@ link_hifi_libraries(shared)
|
||||||
add_dependency_external_projects(glm)
|
add_dependency_external_projects(glm)
|
||||||
find_package(GLM REQUIRED)
|
find_package(GLM REQUIRED)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||||
|
|
|
@ -40,6 +40,4 @@ add_dependency_external_projects(oglplus)
|
||||||
find_package(OGLPLUS REQUIRED)
|
find_package(OGLPLUS REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS})
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
link_hifi_libraries(animation fbx shared gpu model render environment)
|
link_hifi_libraries(animation fbx shared gpu model render environment)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME render)
|
set(TARGET_NAME render)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
AUTOSCRIBE_SHADER_LIB(gpu model)
|
AUTOSCRIBE_SHADER_LIB(gpu model)
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME script-engine)
|
set(TARGET_NAME script-engine)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library(Gui Network Script WebSockets Widgets)
|
setup_hifi_library(Gui Network Script WebSockets Widgets)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
set(TARGET_NAME shared)
|
set(TARGET_NAME shared)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
# TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp)
|
# TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp)
|
||||||
setup_hifi_library(Gui Network Script Widgets)
|
setup_hifi_library(Gui Network Script Widgets)
|
||||||
|
|
|
@ -31,6 +31,7 @@ const float METERS_PER_DECIMETER = 0.1f;
|
||||||
const float METERS_PER_CENTIMETER = 0.01f;
|
const float METERS_PER_CENTIMETER = 0.01f;
|
||||||
const float METERS_PER_MILLIMETER = 0.001f;
|
const float METERS_PER_MILLIMETER = 0.001f;
|
||||||
const float MILLIMETERS_PER_METER = 1000.0f;
|
const float MILLIMETERS_PER_METER = 1000.0f;
|
||||||
|
const quint64 NSECS_PER_USEC = 1000;
|
||||||
const quint64 USECS_PER_MSEC = 1000;
|
const quint64 USECS_PER_MSEC = 1000;
|
||||||
const quint64 MSECS_PER_SECOND = 1000;
|
const quint64 MSECS_PER_SECOND = 1000;
|
||||||
const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
|
const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
|
||||||
|
|
|
@ -8,6 +8,4 @@ link_hifi_libraries(render-utils shared)
|
||||||
add_dependency_external_projects(glm)
|
add_dependency_external_projects(glm)
|
||||||
find_package(GLM REQUIRED)
|
find_package(GLM REQUIRED)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||||
|
|
|
@ -35,5 +35,3 @@ set_target_properties("all-tests" PROPERTIES FOLDER "hidden/test-targets")
|
||||||
set_target_properties("all-tests" PROPERTIES
|
set_target_properties("all-tests" PROPERTIES
|
||||||
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
||||||
EXCLUDE_FROM_ALL TRUE)
|
EXCLUDE_FROM_ALL TRUE)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
|
@ -6,6 +6,4 @@ macro (setup_testcase_dependencies)
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
endmacro ()
|
endmacro ()
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
setup_hifi_testcase()
|
setup_hifi_testcase()
|
||||||
|
|
|
@ -6,6 +6,4 @@ macro (SETUP_TESTCASE_DEPENDENCIES)
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
endmacro ()
|
endmacro ()
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
setup_hifi_testcase()
|
setup_hifi_testcase()
|
||||||
|
|
|
@ -9,6 +9,4 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||||
# link in the shared libraries
|
# link in the shared libraries
|
||||||
link_hifi_libraries(entities avatars shared octree gpu model fbx networking animation environment)
|
link_hifi_libraries(entities avatars shared octree gpu model fbx networking animation environment)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
|
|
|
@ -7,6 +7,4 @@ macro (setup_testcase_dependencies)
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
setup_hifi_testcase()
|
setup_hifi_testcase()
|
||||||
|
|
|
@ -7,6 +7,4 @@ macro (setup_testcase_dependencies)
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
endmacro ()
|
endmacro ()
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
setup_hifi_testcase()
|
setup_hifi_testcase()
|
||||||
|
|
|
@ -7,6 +7,4 @@ macro (setup_testcase_dependencies)
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
endmacro ()
|
endmacro ()
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
setup_hifi_testcase(Script Network)
|
setup_hifi_testcase(Script Network)
|
||||||
|
|
|
@ -21,6 +21,4 @@ macro (SETUP_TESTCASE_DEPENDENCIES)
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
endmacro ()
|
endmacro ()
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
setup_hifi_testcase(Script)
|
setup_hifi_testcase(Script)
|
||||||
|
|
|
@ -10,6 +10,4 @@ link_hifi_libraries(render-utils gpu shared)
|
||||||
|
|
||||||
message(${PROJECT_BINARY_DIR})
|
message(${PROJECT_BINARY_DIR})
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
|
|
|
@ -19,6 +19,4 @@ include_directories("${PROJECT_BINARY_DIR}/../../libraries/model/")
|
||||||
|
|
||||||
message(${PROJECT_BINARY_DIR})
|
message(${PROJECT_BINARY_DIR})
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
|
|
|
@ -8,6 +8,4 @@ macro (setup_testcase_dependencies)
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
endmacro ()
|
endmacro ()
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
setup_hifi_testcase()
|
setup_hifi_testcase()
|
||||||
|
|
|
@ -13,6 +13,4 @@ endif()
|
||||||
# link in the shared libraries
|
# link in the shared libraries
|
||||||
link_hifi_libraries(ui render-utils gpu shared)
|
link_hifi_libraries(ui render-utils gpu shared)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
# add the tool directories
|
# add the tool directories
|
||||||
add_subdirectory(mtc)
|
add_subdirectory(mtc)
|
||||||
set_target_properties(mtc PROPERTIES FOLDER "Tools")
|
set_target_properties(mtc PROPERTIES FOLDER "Tools")
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
set(TARGET_NAME mtc)
|
set(TARGET_NAME mtc)
|
||||||
setup_hifi_project()
|
setup_hifi_project()
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
copy_dlls_beside_windows_executable()
|
copy_dlls_beside_windows_executable()
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
set(TARGET_NAME scribe)
|
set(TARGET_NAME scribe)
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
setup_hifi_project()
|
setup_hifi_project()
|
||||||
|
|
|
@ -8,8 +8,6 @@ find_package(VHACD REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${VHACD_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} PUBLIC ${VHACD_INCLUDE_DIRS})
|
||||||
target_link_libraries(${TARGET_NAME} ${VHACD_LIBRARIES})
|
target_link_libraries(${TARGET_NAME} ${VHACD_LIBRARIES})
|
||||||
|
|
||||||
setup_memory_debugger()
|
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
include(FindOpenMP)
|
include(FindOpenMP)
|
||||||
if(OPENMP_FOUND)
|
if(OPENMP_FOUND)
|
||||||
|
|
Loading…
Reference in a new issue