diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 570db05871..72e9751294 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -368,11 +368,11 @@ void DomainServer::setupNodeListAndAssignments() { const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port"; QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION); - unsigned short domainServerPort = (unsigned short) localPortValue.toUInt(); + int domainServerPort = localPortValue.toInt(); QVariantMap& settingsMap = _settingsManager.getSettingsMap(); - unsigned short domainServerDTLSPort = 0; + int domainServerDTLSPort = INVALID_PORT; if (_isUsingDTLS) { domainServerDTLSPort = DEFAULT_DOMAIN_SERVER_DTLS_PORT; diff --git a/interface/resources/qml/hifi/dialogs/NetworkingPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/NetworkingPreferencesDialog.qml new file mode 100644 index 0000000000..652d3347fc --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/NetworkingPreferencesDialog.qml @@ -0,0 +1,19 @@ +import QtQuick 2.5 +import Qt.labs.settings 1.0 + +import "../../dialogs" + +PreferencesDialog { + id: root + objectName: "NetworkingPreferencesDialog" + title: "Networking Settings" + showCategories: ["Networking"] + property var settings: Settings { + category: root.objectName + property alias x: root.x + property alias y: root.y + property alias width: root.width + property alias height: root.height + } +} + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8620b384ec..904a6c5b65 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -400,12 +400,10 @@ static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; bool setupEssentials(int& argc, char** argv) { - unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast<const char**>(argv); const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); - if (portStr) { - listenPort = atoi(portStr); - } + const int listenPort = portStr ? atoi(portStr) : INVALID_PORT; + // Set build version QCoreApplication::setApplicationVersion(BuildInfo::VERSION); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 50dc748461..d3caa4a092 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -522,6 +522,11 @@ Menu::Menu() { // Developer > Network >>> MenuWrapper* networkMenu = developerMenu->addMenu("Network"); + action = addActionToQMenuAndActionHash(networkMenu, MenuOption::Networking); + connect(action, &QAction::triggered, [] { + DependencyManager::get<OffscreenUi>()->toggle(QUrl("hifi/dialogs/NetworkingPreferencesDialog.qml"), + "NetworkingPreferencesDialog"); + }); addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index ee00644746..b25603caeb 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -129,6 +129,7 @@ namespace MenuOption { const QString MuteEnvironment = "Mute Environment"; const QString MuteFaceTracking = "Mute Face Tracking"; const QString NamesAboveHeads = "Names Above Heads"; + const QString Networking = "Networking..."; const QString NoFaceTracking = "None"; const QString OctreeStats = "Entity Statistics"; const QString OnePointCalibration = "1 Point Calibration"; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 7fdafc9bda..0ba057a5e3 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -332,4 +332,19 @@ void setupPreferences() { preferences->addPreference(preference); } } + { + static const QString RENDER("Networking"); + + auto nodelist = DependencyManager::get<NodeList>(); + { + static const int MIN_PORT_NUMBER { 0 }; + static const int MAX_PORT_NUMBER { 65535 }; + auto getter = [nodelist] { return static_cast<int>(nodelist->getSocketLocalPort()); }; + auto setter = [nodelist](int preset) { nodelist->setSocketLocalPort(static_cast<quint16>(preset)); }; + auto preference = new IntSpinnerPreference(RENDER, "Listening Port", getter, setter); + preference->setMin(MIN_PORT_NUMBER); + preference->setMax(MAX_PORT_NUMBER); + preferences->addPreference(preference); + } + } } diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 6d9de2dbc1..7fad66d608 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -18,6 +18,7 @@ #include <QtCore/QDataStream> #include <QtCore/QDebug> #include <QtCore/QJsonDocument> +#include <QtCore/QThread> #include <QtCore/QUrl> #include <QtNetwork/QHostInfo> @@ -25,6 +26,7 @@ #include <LogHandler.h> #include <NumericalConstants.h> +#include <SettingHandle.h> #include <SharedUtil.h> #include <UUID.h> @@ -34,12 +36,14 @@ #include "NetworkLogging.h" #include "udt/Packet.h" +static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0); + const std::set<NodeType_t> SOLO_NODE_TYPES = { NodeType::AvatarMixer, NodeType::AudioMixer }; -LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) : +LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) : _sessionUUID(), _nodeHash(), _nodeMutex(QReadWriteLock::Recursive), @@ -62,11 +66,11 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short } qRegisterMetaType<ConnectionStep>("ConnectionStep"); - - _nodeSocket.bind(QHostAddress::AnyIPv4, socketListenPort); + auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get(); + _nodeSocket.bind(QHostAddress::AnyIPv4, port); qCDebug(networking) << "NodeList socket is listening on" << _nodeSocket.localPort(); - if (dtlsListenPort > 0) { + if (dtlsListenPort != INVALID_PORT) { // only create the DTLS socket during constructor if a custom port is passed _dtlsSocket = new QUdpSocket(this); @@ -157,6 +161,18 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) { } } +void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setSocketLocalPort", Qt::QueuedConnection, + Q_ARG(quint16, socketLocalPort)); + return; + } + if (_nodeSocket.localPort() != socketLocalPort) { + _nodeSocket.rebind(socketLocalPort); + LIMITED_NODELIST_LOCAL_PORT.set(socketLocalPort); + } +} + QUdpSocket& LimitedNodeList::getDTLSSocket() { if (!_dtlsSocket) { // DTLS socket getter called but no DTLS socket exists, create it now @@ -606,6 +622,12 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t }); } + // Signal when a socket changes, so we can start the hole punch over. + auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambda to hold a strong ref + connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [=] { + emit nodeSocketUpdated(weakPtr); + }); + return newNodePointer; } } diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 49a3a155a2..cd343a5232 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -45,6 +45,8 @@ #include "udt/Socket.h" #include "UUIDHasher.h" +const int INVALID_PORT = -1; + const quint64 NODE_SILENCE_THRESHOLD_MSECS = 5 * 1000; extern const std::set<NodeType_t> SOLO_NODE_TYPES; @@ -113,6 +115,8 @@ public: bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); } + Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort); + QUdpSocket& getDTLSSocket(); PacketReceiver& getPacketReceiver() { return *_packetReceiver; } @@ -250,6 +254,7 @@ signals: void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID); void nodeAdded(SharedNodePointer); + void nodeSocketUpdated(SharedNodePointer); void nodeKilled(SharedNodePointer); void nodeActivated(SharedNodePointer); @@ -267,9 +272,9 @@ protected slots: void errorTestingLocalSocket(); protected: - LimitedNodeList(unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); - LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton - void operator=(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton + LimitedNodeList(int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT); + LimitedNodeList(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton + void operator=(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode, const HifiSockAddr& overridenSockAddr); diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index da2eced05c..2ebee1b71f 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -63,6 +63,7 @@ void NetworkPeer::setPublicSocket(const HifiSockAddr& publicSocket) { if (!wasOldSocketNull) { qCDebug(networking) << "Public socket change for node" << *this; + emit socketUpdated(); } } } @@ -82,6 +83,7 @@ void NetworkPeer::setLocalSocket(const HifiSockAddr& localSocket) { if (!wasOldSocketNull) { qCDebug(networking) << "Local socket change for node" << *this; + emit socketUpdated(); } } } @@ -101,6 +103,7 @@ void NetworkPeer::setSymmetricSocket(const HifiSockAddr& symmetricSocket) { if (!wasOldSocketNull) { qCDebug(networking) << "Symmetric socket change for node" << *this; + emit socketUpdated(); } } } diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index 8298a2dad4..7185ffef29 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -81,9 +81,12 @@ public: public slots: void startPingTimer(); void stopPingTimer(); + signals: void pingTimerTimeout(); void socketActivated(const HifiSockAddr& sockAddr); + void socketUpdated(); + protected: void setActiveSocket(HifiSockAddr* discoveredSocket); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 3a07ea8b54..593a79b311 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -33,7 +33,7 @@ const int KEEPALIVE_PING_INTERVAL_MS = 1000; -NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) : +NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) : LimitedNodeList(socketListenPort, dtlsListenPort), _ownerType(newOwnerType), _nodeTypesOfInterest(), @@ -93,6 +93,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // anytime we get a new node we will want to attempt to punch to it connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch); + connect(this, &LimitedNodeList::nodeSocketUpdated, this, &NodeList::startNodeHolePunch); // anytime we get a new node we may need to re-send our set of ignored node IDs to it connect(this, &LimitedNodeList::nodeActivated, this, &NodeList::maybeSendIgnoreSetToNode); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index f3cd5bed0d..f08c0dbe45 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -116,10 +116,10 @@ private slots: void maybeSendIgnoreSetToNode(SharedNodePointer node); private: - NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile - NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); - NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton - void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton + NodeList() : LimitedNodeList(INVALID_PORT, INVALID_PORT) { assert(false); } // Not implemented, needed for DependencyManager templates compile + NodeList(char ownerType, int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT); + NodeList(NodeList const&) = delete; // Don't implement, needed to avoid copies of singleton + void operator=(NodeList const&) = delete; // Don't implement, needed to avoid copies of singleton void processDomainServerAuthRequest(const QByteArray& packet); void requestAuthForDomainServer(); diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index a39100f8d1..37ededa55c 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -63,10 +63,12 @@ void Socket::bind(const QHostAddress& address, quint16 port) { } void Socket::rebind() { - quint16 oldPort = _udpSocket.localPort(); + rebind(_udpSocket.localPort()); +} +void Socket::rebind(quint16 localPort) { _udpSocket.close(); - bind(QHostAddress::AnyIPv4, oldPort); + bind(QHostAddress::AnyIPv4, localPort); } void Socket::setSystemBufferSizes() { diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index 6b8ccf1fa8..bc4393d4bd 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -61,8 +61,9 @@ public: qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr); void bind(const QHostAddress& address, quint16 port = 0); + void rebind(quint16 port); void rebind(); - + void setPacketFilterOperator(PacketFilterOperator filterOperator) { _packetFilterOperator = filterOperator; } void setPacketHandler(PacketHandler handler) { _packetHandler = handler; } void setMessageHandler(MessageHandler handler) { _messageHandler = handler; } diff --git a/libraries/shared/src/Preferences.h b/libraries/shared/src/Preferences.h index abb3f5afbd..f1915a9d6a 100644 --- a/libraries/shared/src/Preferences.h +++ b/libraries/shared/src/Preferences.h @@ -189,6 +189,38 @@ protected: float _step { 0.1f }; }; + +class IntPreference : public TypedPreference<int> { + Q_OBJECT + Q_PROPERTY(float value READ getValue WRITE setValue NOTIFY valueChanged) + Q_PROPERTY(float min READ getMin CONSTANT) + Q_PROPERTY(float max READ getMax CONSTANT) + Q_PROPERTY(float step READ getStep CONSTANT) + +public: + IntPreference(const QString& category, const QString& name, Getter getter, Setter setter) + : TypedPreference(category, name, getter, setter) { } + + float getMin() const { return _min; } + void setMin(float min) { _min = min; }; + + float getMax() const { return _max; } + void setMax(float max) { _max = max; }; + + float getStep() const { return _step; } + void setStep(float step) { _step = step; }; + +signals: + void valueChanged(); + +protected: + void emitValueChanged() override { emit valueChanged(); } + + int _min { std::numeric_limits<int>::min() }; + int _max { std::numeric_limits<int>::max() }; + int _step { 1 }; +}; + class StringPreference : public TypedPreference<QString> { Q_OBJECT Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged) @@ -222,6 +254,15 @@ public: Type getType() override { return Spinner; } }; +class IntSpinnerPreference : public IntPreference { + Q_OBJECT +public: + IntSpinnerPreference(const QString& category, const QString& name, Getter getter, Setter setter) + : IntPreference(category, name, getter, setter) { } + + Type getType() override { return Spinner; } +}; + class EditPreference : public StringPreference { Q_OBJECT Q_PROPERTY(QString placeholderText READ getPlaceholderText CONSTANT) diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index c6cca74c69..7fa36136d2 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -503,7 +503,7 @@ public: DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>(); DependencyManager::set<AddressManager>(); - DependencyManager::set<NodeList>(NodeType::Agent, 0); + DependencyManager::set<NodeList>(NodeType::Agent); DependencyManager::set<DeferredLightingEffect>(); DependencyManager::set<ResourceCacheSharedItems>(); DependencyManager::set<TextureCache>(); diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index fd6885c381..04bce72757 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -295,7 +295,7 @@ public: DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); //DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>(); DependencyManager::set<AddressManager>(); - DependencyManager::set<NodeList>(NodeType::Agent, 0); + DependencyManager::set<NodeList>(NodeType::Agent); DependencyManager::set<DeferredLightingEffect>(); DependencyManager::set<ResourceCacheSharedItems>(); DependencyManager::set<TextureCache>();