diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 063bf24de8..1f56118177 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -93,7 +94,7 @@ void Agent::handleJurisdictionPacket(QSharedPointer packet, SharedNode DependencyManager::get()->getJurisdictionListener()-> queueReceivedPacket(packet, senderNode); } -} +} void Agent::handleAudioPacket(QSharedPointer packet) { _receivedAudioStream.parseData(*packet); @@ -109,11 +110,21 @@ const int PING_INTERVAL = 1000; void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); + // Setup MessagesClient + auto messagesClient = DependencyManager::set(); + QThread* messagesThread = new QThread; + messagesThread->setObjectName("Messages Client Thread"); + messagesClient->moveToThread(messagesThread); + connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); + messagesThread->start(); + + auto nodeList = DependencyManager::get(); nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer << NodeType::EntityServer + << NodeType::MessagesMixer ); _pingTimer = new QTimer(this); diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index cacc523ebd..c4cd6821ef 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -17,6 +17,7 @@ #include "avatars/AvatarMixer.h" #include "entities/EntityServer.h" #include "assets/AssetServer.h" +#include "messages/MessagesMixer.h" ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) { @@ -36,6 +37,8 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) { return new EntityServer(packet); case Assignment::AssetServerType: return new AssetServer(packet); + case Assignment::MessagesMixerType: + return new MessagesMixer(packet); default: return NULL; } diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 5754a9e057..2fafaa6731 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -112,7 +112,6 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN quint64 deletePacketSentAt = usecTimestampNow(); EntityTreePointer tree = std::static_pointer_cast(_tree); auto recentlyDeleted = tree->getRecentlyDeletedEntityIDs(); - bool hasMoreToSend = true; packetsSent = 0; diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp new file mode 100644 index 0000000000..21e3fdc4c5 --- /dev/null +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -0,0 +1,155 @@ +// +// MessagesMixer.cpp +// assignment-client/src/messages +// +// Created by Brad hefta-Gaub on 11/16/2015. +// 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 "MessagesMixer.h" + +const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer"; + +MessagesMixer::MessagesMixer(NLPacket& packet) : + ThreadedAssignment(packet) +{ + // make sure we hear about node kills so we can tell the other nodes + connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled); + + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); + packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessages"); + packetReceiver.registerMessageListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe"); + packetReceiver.registerMessageListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe"); +} + +MessagesMixer::~MessagesMixer() { +} + +void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { + // FIXME - remove the node from the subscription maps +} + +void MessagesMixer::handleMessages(QSharedPointer packetList, SharedNodePointer senderNode) { + Q_ASSERT(packetList->getType() == PacketType::MessagesData); + + QByteArray packetData = packetList->getMessage(); + QBuffer packet{ &packetData }; + packet.open(QIODevice::ReadOnly); + + quint16 channelLength; + packet.read(reinterpret_cast(&channelLength), sizeof(channelLength)); + auto channelData = packet.read(channelLength); + QString channel = QString::fromUtf8(channelData); + + quint16 messageLength; + packet.read(reinterpret_cast(&messageLength), sizeof(messageLength)); + auto messageData = packet.read(messageLength); + QString message = QString::fromUtf8(messageData); + + auto nodeList = DependencyManager::get(); + + nodeList->eachMatchingNode( + [&](const SharedNodePointer& node)->bool { + + return node->getType() == NodeType::Agent && node->getActiveSocket() && + _channelSubscribers[channel].contains(node->getUUID()); + }, + [&](const SharedNodePointer& node) { + + auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); + + auto channelUtf8 = channel.toUtf8(); + quint16 channelLength = channelUtf8.length(); + packetList->writePrimitive(channelLength); + packetList->write(channelUtf8); + + auto messageUtf8 = message.toUtf8(); + quint16 messageLength = messageUtf8.length(); + packetList->writePrimitive(messageLength); + packetList->write(messageUtf8); + + nodeList->sendPacketList(std::move(packetList), *node); + }); +} + +void MessagesMixer::handleMessagesSubscribe(QSharedPointer packetList, SharedNodePointer senderNode) { + Q_ASSERT(packetList->getType() == PacketType::MessagesSubscribe); + QString channel = QString::fromUtf8(packetList->getMessage()); + qDebug() << "Node [" << senderNode->getUUID() << "] subscribed to channel:" << channel; + _channelSubscribers[channel] << senderNode->getUUID(); +} + +void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer packetList, SharedNodePointer senderNode) { + Q_ASSERT(packetList->getType() == PacketType::MessagesUnsubscribe); + QString channel = QString::fromUtf8(packetList->getMessage()); + qDebug() << "Node [" << senderNode->getUUID() << "] unsubscribed from channel:" << channel; + + if (_channelSubscribers.contains(channel)) { + _channelSubscribers[channel].remove(senderNode->getUUID()); + } +} + +// FIXME - make these stats relevant +void MessagesMixer::sendStatsPacket() { + QJsonObject statsObject; + QJsonObject messagesObject; + auto nodeList = DependencyManager::get(); + // add stats for each listerner + nodeList->eachNode([&](const SharedNodePointer& node) { + QJsonObject messagesStats; + + // add the key to ask the domain-server for a username replacement, if it has it + messagesStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); + messagesStats["outbound_kbps"] = node->getOutboundBandwidth(); + messagesStats["inbound_kbps"] = node->getInboundBandwidth(); + + messagesObject[uuidStringWithoutCurlyBraces(node->getUUID())] = messagesStats; + }); + + statsObject["messages"] = messagesObject; + ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); +} + +void MessagesMixer::run() { + ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); + + auto nodeList = DependencyManager::get(); + nodeList->addNodeTypeToInterestSet(NodeType::Agent); + + // wait until we have the domain-server settings, otherwise we bail + DomainHandler& domainHandler = nodeList->getDomainHandler(); + + qDebug() << "Waiting for domain settings from domain-server."; + + // block until we get the settingsRequestComplete signal + QEventLoop loop; + connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); + connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); + domainHandler.requestDomainSettings(); + loop.exec(); + + if (domainHandler.getSettingsObject().isEmpty()) { + qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; + setFinished(true); + return; + } + + // parse the settings to pull out the values we need + parseDomainServerSettings(domainHandler.getSettingsObject()); +} + +void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { + // TODO - if we want options, parse them here... + const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; +} diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h new file mode 100644 index 0000000000..12667bcc1b --- /dev/null +++ b/assignment-client/src/messages/MessagesMixer.h @@ -0,0 +1,43 @@ +// +// MessagesMixer.h +// assignment-client/src/messages +// +// Created by Brad hefta-Gaub on 11/16/2015. +// Copyright 2015 High Fidelity, Inc. +// +// The avatar mixer receives head, hand and positional data from all connected +// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms. +// +// 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_MessagesMixer_h +#define hifi_MessagesMixer_h + +#include + +/// Handles assignments of type MessagesMixer - distribution of avatar data to various clients +class MessagesMixer : public ThreadedAssignment { + Q_OBJECT +public: + MessagesMixer(NLPacket& packet); + ~MessagesMixer(); + +public slots: + void run(); + void nodeKilled(SharedNodePointer killedNode); + void sendStatsPacket(); + +private slots: + void handleMessages(QSharedPointer packetList, SharedNodePointer senderNode); + void handleMessagesSubscribe(QSharedPointer packetList, SharedNodePointer senderNode); + void handleMessagesUnsubscribe(QSharedPointer packetList, SharedNodePointer senderNode); + +private: + void parseDomainServerSettings(const QJsonObject& domainSettings); + + QHash> _channelSubscribers; +}; + +#endif // hifi_MessagesMixer_h diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index d360ab4802..55f0fb2d2b 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -48,7 +48,8 @@ QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer << NodeType::EntityServer - << NodeType::AssetServer; + << NodeType::AssetServer + << NodeType::MessagesMixer; void DomainGatekeeper::processConnectRequestPacket(QSharedPointer packet) { if (packet->getPayloadSize() == 0) { @@ -66,7 +67,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer pack } static const NodeSet VALID_NODE_TYPES { - NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent + NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer }; if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) { diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 127c121cf3..d7bcec2431 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -554,7 +554,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet(static_cast(defaultedType) + 1)) { if (!excludedTypes.contains(defaultedType) - && defaultedType != Assignment::UNUSED_0 && defaultedType != Assignment::UNUSED_1 && defaultedType != Assignment::AgentType) { diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index f650089486..88fc6a6cad 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -131,7 +131,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion); } -QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString &keyPath) { +QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath); if (foundValue) { diff --git a/examples/example/messagesExample.js b/examples/example/messagesExample.js new file mode 100644 index 0000000000..11827f019f --- /dev/null +++ b/examples/example/messagesExample.js @@ -0,0 +1,43 @@ +var totalTime = 0; +var unsubscribedForTime = 0; +var subscribedForTime = 0; +var subscribed = false; +var SWITCH_SUBSCRIPTION_TIME = 10; +Script.update.connect(function (deltaTime) { + var channel = "example"; + totalTime += deltaTime; + if (!subscribed) { + unsubscribedForTime += deltaTime; + } else { + subscribedForTime += deltaTime; + } + + if (totalTime > 5) { + + // if we've been unsubscribed for SWITCH_SUBSCRIPTION_TIME seconds, subscribe + if (!subscribed && unsubscribedForTime > SWITCH_SUBSCRIPTION_TIME) { + print("---- subscribing ----"); + Messages.subscribe(channel); + subscribed = true; + subscribedForTime = 0; + } + + // if we've been subscribed for SWITCH_SUBSCRIPTION_TIME seconds, unsubscribe + if (subscribed && subscribedForTime > SWITCH_SUBSCRIPTION_TIME) { + print("---- unsubscribing ----"); + Messages.unsubscribe(channel); + subscribed = false; + unsubscribedForTime = 0; + } + + // Even if not subscribed, still publish + var message = "update() deltaTime:" + deltaTime; + //print(message); + Messages.sendMessage(channel, message); + } +}); + + +Messages.messageReceived.connect(function (channel, message) { + print("message received on channel:" + channel + ", message:" + message); +}); \ No newline at end of file diff --git a/examples/example/messagesReceiverExample.js b/examples/example/messagesReceiverExample.js new file mode 100644 index 0000000000..31020a4c8a --- /dev/null +++ b/examples/example/messagesReceiverExample.js @@ -0,0 +1,22 @@ +var totalTime = 0; +var subscribed = false; +var WAIT_FOR_SUBSCRIPTION_TIME = 10; +function myUpdate(deltaTime) { + var channel = "example"; + totalTime += deltaTime; + + if (totalTime > WAIT_FOR_SUBSCRIPTION_TIME && !subscribed) { + + print("---- subscribing ----"); + Messages.subscribe(channel); + subscribed = true; + Script.update.disconnect(myUpdate); + } +} + +Script.update.connect(myUpdate); + +Messages.messageReceived.connect(function (channel, message) { + print("message received on channel:" + channel + ", message:" + message); +}); + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 96b8ab74a8..c77bb9a114 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -70,6 +70,7 @@ #include #include #include +#include #include #include #include @@ -339,6 +340,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); return true; @@ -484,6 +486,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init); assetThread->start(); + // Setup MessagesClient + auto messagesClient = DependencyManager::get(); + QThread* messagesThread = new QThread; + messagesThread->setObjectName("Messages Client Thread"); + messagesClient->moveToThread(messagesThread); + connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); + messagesThread->start(); + const DomainHandler& domainHandler = nodeList->getDomainHandler(); connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); @@ -550,7 +560,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // tell the NodeList instance who to tell the domain server we care about nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer - << NodeType::EntityServer << NodeType::AssetServer); + << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer); // connect to the packet sent signal of the _entityEditSender connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index b8cf8ab4f1..e8452583fc 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -91,7 +91,6 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { } if (isMine) { - MyAvatar* myAvatar = static_cast(_owningAvatar); auto player = DependencyManager::get(); // Only use face trackers when not playing back a recording. if (!player->isPlaying()) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 83c8cdfcf5..87f0e631f2 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -247,7 +247,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { return; // only simulate for own avatar } - MyAvatar* myAvatar = static_cast(_owningAvatar); auto player = DependencyManager::get(); if (player->isPlaying()) { return; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index a506fe217c..50bfd995f2 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -909,13 +909,8 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { // we don't have an audioPacket yet - set that up now _audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho); } + // FIXME either discard stereo in the recording or record a stereo flag - const int numNetworkBytes = _isStereoInput - ? AudioConstants::NETWORK_FRAME_BYTES_STEREO - : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - const int numNetworkSamples = _isStereoInput - ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO - : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; auto nodeList = DependencyManager::get(); SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 66c6bf2a2c..5fdedeafb9 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -30,6 +30,8 @@ Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) { return Assignment::EntityServerType; case NodeType::AssetServer: return Assignment::AssetServerType; + case NodeType::MessagesMixer: + return Assignment::MessagesMixerType; default: return Assignment::AllTypes; } @@ -131,6 +133,8 @@ const char* Assignment::getTypeName() const { return "asset-server"; case Assignment::EntityServerType: return "entity-server"; + case Assignment::MessagesMixerType: + return "messages-mixer"; default: return "unknown"; } diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index ee3d9cb5fd..9639411eec 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -30,7 +30,7 @@ public: AvatarMixerType = 1, AgentType = 2, AssetServerType = 3, - UNUSED_0 = 4, + MessagesMixerType = 4, UNUSED_1 = 5, EntityServerType = 6, AllTypes = 7 diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp new file mode 100644 index 0000000000..ac2bf55033 --- /dev/null +++ b/libraries/networking/src/MessagesClient.cpp @@ -0,0 +1,108 @@ +// +// MessagesClient.cpp +// libraries/networking/src +// +// Created by Brad hefta-Gaub on 11/16/2015. +// 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 "MessagesClient.h" + +#include + +#include +#include + +#include "NetworkLogging.h" +#include "NodeList.h" +#include "PacketReceiver.h" + +MessagesClient::MessagesClient() { + setCustomDeleter([](Dependency* dependency){ + static_cast(dependency)->deleteLater(); + }); + auto nodeList = DependencyManager::get(); + auto& packetReceiver = nodeList->getPacketReceiver(); + packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessagesPacket"); + connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &MessagesClient::handleNodeKilled); +} + +void MessagesClient::init() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection); + } +} + +void MessagesClient::handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode) { + QByteArray packetData = packetList->getMessage(); + QBuffer packet{ &packetData }; + packet.open(QIODevice::ReadOnly); + + quint16 channelLength; + packet.read(reinterpret_cast(&channelLength), sizeof(channelLength)); + auto channelData = packet.read(channelLength); + QString channel = QString::fromUtf8(channelData); + + quint16 messageLength; + packet.read(reinterpret_cast(&messageLength), sizeof(messageLength)); + auto messageData = packet.read(messageLength); + QString message = QString::fromUtf8(messageData); + + emit messageReceived(channel, message); +} + +void MessagesClient::sendMessage(const QString& channel, const QString& message) { + auto nodeList = DependencyManager::get(); + SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); + + if (messagesMixer) { + auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); + + auto channelUtf8 = channel.toUtf8(); + quint16 channelLength = channelUtf8.length(); + packetList->writePrimitive(channelLength); + packetList->write(channelUtf8); + + auto messageUtf8 = message.toUtf8(); + quint16 messageLength = messageUtf8.length(); + packetList->writePrimitive(messageLength); + packetList->write(messageUtf8); + + nodeList->sendPacketList(std::move(packetList), *messagesMixer); + } +} + +// FIXME - we should keep track of the channels we are subscribed to locally, and +// in the event that they mixer goes away and/or comes back we should automatically +// resubscribe to those channels +void MessagesClient::subscribe(const QString& channel) { + auto nodeList = DependencyManager::get(); + SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); + + if (messagesMixer) { + auto packetList = NLPacketList::create(PacketType::MessagesSubscribe, QByteArray(), true, true); + packetList->write(channel.toUtf8()); + nodeList->sendPacketList(std::move(packetList), *messagesMixer); + } +} + +void MessagesClient::unsubscribe(const QString& channel) { + auto nodeList = DependencyManager::get(); + SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); + + if (messagesMixer) { + auto packetList = NLPacketList::create(PacketType::MessagesUnsubscribe, QByteArray(), true, true); + packetList->write(channel.toUtf8()); + nodeList->sendPacketList(std::move(packetList), *messagesMixer); + } +} + +void MessagesClient::handleNodeKilled(SharedNodePointer node) { + if (node->getType() != NodeType::MessagesMixer) { + return; + } + // FIXME - do we need to do any special bookkeeping for when the messages mixer is no longer available +} diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h new file mode 100644 index 0000000000..13e908e129 --- /dev/null +++ b/libraries/networking/src/MessagesClient.h @@ -0,0 +1,43 @@ +// +// MessagesClient.h +// libraries/networking/src +// +// Created by Brad hefta-Gaub on 11/16/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#ifndef hifi_MessagesClient_h +#define hifi_MessagesClient_h + +#include + +#include + +#include "LimitedNodeList.h" +#include "NLPacket.h" +#include "Node.h" + +class MessagesClient : public QObject, public Dependency { + Q_OBJECT +public: + MessagesClient(); + + Q_INVOKABLE void init(); + + Q_INVOKABLE void sendMessage(const QString& channel, const QString& message); + Q_INVOKABLE void subscribe(const QString& channel); + Q_INVOKABLE void unsubscribe(const QString& channel); + +signals: + void messageReceived(const QString& channel, const QString& message); + +private slots: + void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); + void handleNodeKilled(SharedNodePointer node); +}; + +#endif diff --git a/libraries/networking/src/NetworkLogging.cpp b/libraries/networking/src/NetworkLogging.cpp index 28b209960e..01abd6ae19 100644 --- a/libraries/networking/src/NetworkLogging.cpp +++ b/libraries/networking/src/NetworkLogging.cpp @@ -13,3 +13,4 @@ Q_LOGGING_CATEGORY(networking, "hifi.networking") Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client") +Q_LOGGING_CATEGORY(messages_client, "hifi.networking.messages_client") diff --git a/libraries/networking/src/NetworkLogging.h b/libraries/networking/src/NetworkLogging.h index 838bbb57d2..37ebc1933d 100644 --- a/libraries/networking/src/NetworkLogging.h +++ b/libraries/networking/src/NetworkLogging.h @@ -16,5 +16,6 @@ Q_DECLARE_LOGGING_CATEGORY(networking) Q_DECLARE_LOGGING_CATEGORY(asset_client) +Q_DECLARE_LOGGING_CATEGORY(messages_client) #endif // hifi_NetworkLogging_h diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 5fea670dd0..243dca78e2 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -32,6 +32,7 @@ void NodeType::init() { TypeNameHash.insert(NodeType::Agent, "Agent"); TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer"); TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer"); + TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer"); TypeNameHash.insert(NodeType::AssetServer, "Asset Server"); TypeNameHash.insert(NodeType::Unassigned, "Unassigned"); } diff --git a/libraries/networking/src/NodeType.h b/libraries/networking/src/NodeType.h index e680f218db..d4377f4610 100644 --- a/libraries/networking/src/NodeType.h +++ b/libraries/networking/src/NodeType.h @@ -23,6 +23,7 @@ namespace NodeType { const NodeType_t AudioMixer = 'M'; const NodeType_t AvatarMixer = 'W'; const NodeType_t AssetServer = 'A'; + const NodeType_t MessagesMixer = 'm'; const NodeType_t Unassigned = 1; void init(); diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 9d25724f6c..07f25fee5f 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -97,7 +97,7 @@ void PacketReceiver::registerDirectListenerForTypes(PacketTypeList types, bool PacketReceiver::registerMessageListener(PacketType type, QObject* listener, const char* slot) { Q_ASSERT_X(listener, "PacketReceiver::registerMessageListener", "No object to register"); Q_ASSERT_X(slot, "PacketReceiver::registerMessageListener", "No slot to register"); - + QMetaMethod matchingMethod = matchingMethodForListener(type, listener, slot); if (matchingMethod.isValid()) { @@ -110,8 +110,12 @@ bool PacketReceiver::registerMessageListener(PacketType type, QObject* listener, // add the mapping _packetListListenerMap[type] = ObjectMethodPair(QPointer(listener), matchingMethod); + + qCDebug(networking) << "Registering a packet listener for packet list type" << type; + return true; } else { + qCWarning(networking) << "FAILED to Register a packet listener for packet list type" << type; return false; } } @@ -352,7 +356,7 @@ void PacketReceiver::handleVerifiedPacketList(std::unique_ptr p } } else if (it == _packetListListenerMap.end()) { - qCWarning(networking) << "No listener found for packet type" << nlPacketList->getType(); + qCWarning(networking) << "No listener found for packet list type" << nlPacketList->getType(); // insert a dummy listener so we don't print this again _packetListListenerMap.insert(nlPacketList->getType(), { nullptr, QMetaMethod() }); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index da061d8fdf..e0a847dcc6 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -87,7 +87,10 @@ public: AssetGetInfo, AssetGetInfoReply, DomainDisconnectRequest, - DomainServerRemovedNode + DomainServerRemovedNode, + MessagesData, + MessagesSubscribe, + MessagesUnsubscribe }; }; diff --git a/libraries/recording/src/recording/impl/BufferClip.h b/libraries/recording/src/recording/impl/BufferClip.h index af8a64716b..1ea79f3df2 100644 --- a/libraries/recording/src/recording/impl/BufferClip.h +++ b/libraries/recording/src/recording/impl/BufferClip.h @@ -26,7 +26,8 @@ public: private: virtual FrameConstPointer readFrame(size_t index) const override; QString _name { QUuid().toString() }; - mutable size_t _frameIndex { 0 }; + + //mutable size_t _frameIndex { 0 }; // FIXME - not in use }; } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0f62bf8cd5..c17b091643 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -366,6 +367,7 @@ void ScriptEngine::init() { registerGlobalObject("Vec3", &_vec3Library); registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("AnimationCache", DependencyManager::get().data()); + registerGlobalObject("Messages", DependencyManager::get().data()); qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); diff --git a/libraries/shared/src/shared/JSONHelpers.cpp b/libraries/shared/src/shared/JSONHelpers.cpp index 52ece73490..c0a8820d95 100644 --- a/libraries/shared/src/shared/JSONHelpers.cpp +++ b/libraries/shared/src/shared/JSONHelpers.cpp @@ -27,7 +27,6 @@ QJsonValue glmToJson(const T& t) { template T glmFromJson(const QJsonValue& json) { - static const T DEFAULT_VALUE = T(); T result; if (json.isArray()) { QJsonArray array = json.toArray();