diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index a077efc335..cf0e25a757 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -10,3 +10,6 @@ set_target_properties(vhacd-util PROPERTIES FOLDER "Tools") add_subdirectory(ice-client) set_target_properties(ice-client PROPERTIES FOLDER "Tools") + +add_subdirectory(ac-client) +set_target_properties(ac-client PROPERTIES FOLDER "Tools") diff --git a/tools/ac-client/CMakeLists.txt b/tools/ac-client/CMakeLists.txt new file mode 100644 index 0000000000..9e623b02e9 --- /dev/null +++ b/tools/ac-client/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME ac-client) +setup_hifi_project(Core Widgets) +link_hifi_libraries(shared networking) diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp new file mode 100644 index 0000000000..3ea2322ffd --- /dev/null +++ b/tools/ac-client/src/ACClientApp.cpp @@ -0,0 +1,272 @@ +// +// ACClientApp.cpp +// tools/ac-client/src +// +// Created by Seth Alves on 2016-10-5 +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ACClientApp.h" + +ACClientApp::ACClientApp(int argc, char* argv[]) : + QCoreApplication(argc, argv) +{ + // parse command-line + QCommandLineParser parser; + parser.setApplicationDescription("High Fidelity AC client"); + parser.addHelpOption(); + + const QCommandLineOption helpOption = parser.addHelpOption(); + + const QCommandLineOption verboseOutput("v", "verbose output"); + parser.addOption(verboseOutput); + + const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1"); + parser.addOption(domainAddressOption); + + const QCommandLineOption cacheSTUNOption("s", "cache stun-server response"); + parser.addOption(cacheSTUNOption); + + const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT)); + parser.addOption(listenPortOption); + + + if (!parser.parse(QCoreApplication::arguments())) { + qCritical() << parser.errorText() << endl; + parser.showHelp(); + Q_UNREACHABLE(); + } + + if (parser.isSet(helpOption)) { + parser.showHelp(); + Q_UNREACHABLE(); + } + + _verbose = parser.isSet(verboseOutput); + if (!_verbose) { + QLoggingCategory::setFilterRules("qt.network.ssl.warning=false"); + + const_cast(&networking())->setEnabled(QtDebugMsg, false); + const_cast(&networking())->setEnabled(QtInfoMsg, false); + const_cast(&networking())->setEnabled(QtWarningMsg, false); + + const_cast(&shared())->setEnabled(QtDebugMsg, false); + const_cast(&shared())->setEnabled(QtInfoMsg, false); + const_cast(&shared())->setEnabled(QtWarningMsg, false); + } + + // QString domainServerAddress = "127.0.0.1"; + // if (parser.isSet(domainAddressOption)) { + // // parse the IP and port combination for this target + // QString hostnamePortString = parser.value(domainAddressOption); + + // QHostAddress address { hostnamePortString.left(hostnamePortString.indexOf(':')) }; + // quint16 port { (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt() }; + // if (port == 0) { + // port = DEFAULT_DOMAIN_SERVER_PORT; + // } + + // if (address.isNull()) { + // qCritical() << "Could not parse an IP address and port combination from" << hostnamePortString << "-" << + // "The parsed IP was" << address.toString() << "and the parsed port was" << port; + + // QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + // } else { + // _iceServerAddr = HifiSockAddr(address, port); + // } + + // if (_verbose) { + // qDebug() << "domain-server Address is" << domainServerAddress << "port is" << port; + // } + // } + + QString domainServerAddress = "127.0.0.1:40103"; + if (parser.isSet(domainAddressOption)) { + domainServerAddress = parser.value(domainAddressOption); + } + + if (_verbose) { + qDebug() << "domain-server address is" << domainServerAddress; + } + + int listenPort = INVALID_PORT; + if (parser.isSet(listenPortOption)) { + listenPort = parser.value(listenPortOption).toInt(); + } + + Setting::preInit(); + DependencyManager::registerInheritance(); + // DependencyManager::registerInheritance(); + Setting::init(); + + DependencyManager::set([&]{ return QString("Mozilla/5.0 (HighFidelityACClient)"); }); + DependencyManager::set(); + DependencyManager::set(NodeType::Agent, listenPort); + + + auto nodeList = DependencyManager::get(); + + // start the nodeThread so its event loop is running + QThread* nodeThread = new QThread(this); + nodeThread->setObjectName("NodeList Thread"); + nodeThread->start(); + + // make sure the node thread is given highest priority + nodeThread->setPriority(QThread::TimeCriticalPriority); + + // setup a timer for domain-server check ins + QTimer* domainCheckInTimer = new QTimer(nodeList.data()); + connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn); + domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); + + // put the NodeList and datagram processing on the node thread + nodeList->moveToThread(nodeThread); + + + const DomainHandler& domainHandler = nodeList->getDomainHandler(); + + connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + // connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); + // connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); + // connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); + // connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); + connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ACClientApp::domainConnectionRefused); + + connect(nodeList.data(), &NodeList::nodeAdded, this, &ACClientApp::nodeAdded); + connect(nodeList.data(), &NodeList::nodeKilled, this, &ACClientApp::nodeKilled); + connect(nodeList.data(), &NodeList::nodeActivated, this, &ACClientApp::nodeActivated); + // connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID); + // connect(nodeList.data(), &NodeList::uuidChanged, this, &ACClientApp::setSessionUUID); + // connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ACClientApp::notifyPacketVersionMismatch); + + // // you might think we could just do this in NodeList but we only want this connection for Interface + // connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset); + + // AccountManager _accountManager = new AccountManager(std::bind(&ACClientApp::getUserAgent, qApp)); + + // setState(lookUpStunServer); + + + nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer + << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer); + + + // // send the identity packet for our avatar each second to our avatar mixer + // connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); + // identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); + + + // DependencyManager::get()->loadSettings(domainServerAddress); + + DependencyManager::get()->handleLookupString(domainServerAddress, false); + + QTimer* doTimer = new QTimer(this); + doTimer->setSingleShot(true); + connect(doTimer, &QTimer::timeout, this, &ACClientApp::timedOut); + doTimer->start(4000); +} + +ACClientApp::~ACClientApp() { +} + + +void ACClientApp::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { + qDebug() << "domainConnectionRefused"; +} + +void ACClientApp::domainChanged(const QString& domainHostname) { + if (_verbose) { + qDebug() << "domainChanged"; + } +} + +void ACClientApp::nodeAdded(SharedNodePointer node) { + if (_verbose) { + qDebug() << "node added: " << node->getType(); + } +} + +void ACClientApp::nodeActivated(SharedNodePointer node) { + if (node->getType() == NodeType::EntityServer) { + if (_verbose) { + qDebug() << "saw EntityServer"; + } + _sawEntityServer = true; + } + else if (node->getType() == NodeType::AudioMixer) { + if (_verbose) { + qDebug() << "saw AudioMixer"; + } + _sawAudioMixer = true; + } + else if (node->getType() == NodeType::AvatarMixer) { + if (_verbose) { + qDebug() << "saw AvatarMixer"; + } + _sawAvatarMixer = true; + } + else if (node->getType() == NodeType::AssetServer) { + if (_verbose) { + qDebug() << "saw AssetServer"; + } + _sawAssetServer = true; + } + else if (node->getType() == NodeType::MessagesMixer) { + if (_verbose) { + qDebug() << "saw MessagesMixer"; + } + _sawMessagesMixer = true; + } + + if (_sawEntityServer && _sawAudioMixer && _sawAvatarMixer && _sawAssetServer && _sawMessagesMixer) { + if (_verbose) { + qDebug() << "success"; + } + finish(0); + } +} + +void ACClientApp::nodeKilled(SharedNodePointer node) { + qDebug() << "nodeKilled"; +} + +void ACClientApp::timedOut() { + if (_verbose) { + qDebug() << "timed out: " << _sawEntityServer << _sawAudioMixer << + _sawAvatarMixer << _sawAssetServer << _sawMessagesMixer; + } + finish(1); +} + +void ACClientApp::finish(int exitCode) { + auto nodeList = DependencyManager::get(); + + // send the domain a disconnect packet, force stoppage of domain-server check-ins + nodeList->getDomainHandler().disconnect(); + nodeList->setIsShuttingDown(true); + + // tell the packet receiver we're shutting down, so it can drop packets + nodeList->getPacketReceiver().setShouldDropPackets(true); + + QThread* nodeThread = DependencyManager::get()->thread(); + // remove the NodeList from the DependencyManager + DependencyManager::destroy(); + // ask the node thread to quit and wait until it is done + nodeThread->quit(); + nodeThread->wait(); + + QCoreApplication::exit(exitCode); +} diff --git a/tools/ac-client/src/ACClientApp.h b/tools/ac-client/src/ACClientApp.h new file mode 100644 index 0000000000..36de592401 --- /dev/null +++ b/tools/ac-client/src/ACClientApp.h @@ -0,0 +1,55 @@ +// +// ACClientApp.h +// tools/ac-client/src +// +// Created by Seth Alves on 2016-10-5 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#ifndef hifi_ACClientApp_h +#define hifi_ACClientApp_h + +#include +#include +#include +#include +#include +#include + + +class ACClientApp : public QCoreApplication { + Q_OBJECT +public: + ACClientApp(int argc, char* argv[]); + ~ACClientApp(); + + const int stunFailureExitStatus { 1 }; + const int iceFailureExitStatus { 2 }; + const int domainPingExitStatus { 3 }; + +private slots: + void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo); + void domainChanged(const QString& domainHostname); + void nodeAdded(SharedNodePointer node); + void nodeActivated(SharedNodePointer node); + void nodeKilled(SharedNodePointer node); + +private: + NodeList* _nodeList; + void timedOut(); + void finish(int exitCode); + bool _verbose; + QTimer* _pingDomainTimer { nullptr }; + + bool _sawEntityServer { false }; + bool _sawAudioMixer { false }; + bool _sawAvatarMixer { false }; + bool _sawAssetServer { false }; + bool _sawMessagesMixer { false }; +}; + +#endif //hifi_ACClientApp_h diff --git a/tools/ac-client/src/main.cpp b/tools/ac-client/src/main.cpp new file mode 100644 index 0000000000..918df6413f --- /dev/null +++ b/tools/ac-client/src/main.cpp @@ -0,0 +1,23 @@ +// +// main.cpp +// tools/ice-client/src +// +// Created by Seth Alves on 2016-10-5 +// Copyright 2016 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 +#include +#include +#include + +#include "ACClientApp.h" + +using namespace std; + +int main(int argc, char * argv[]) { + ACClientApp app(argc, argv); + return app.exec(); +}