Merge pull request #6383 from birarda/goodbye-packet

send a hopeful disconnect packet from ACs and Interface
This commit is contained in:
Seth Alves 2015-11-16 11:28:48 -08:00
commit 36af315e5d
16 changed files with 169 additions and 141 deletions

View file

@ -272,6 +272,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
packetReceiver.registerMessageListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket");
// NodeList won't be available to the settings manager when it is created, so call registerListener here
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
@ -1826,3 +1827,24 @@ void DomainServer::processPathQueryPacket(QSharedPointer<NLPacket> packet) {
}
}
}
void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<NLPacket> packet) {
// This packet has been matched to a source node and they're asking not to be in the domain anymore
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
const QUuid& nodeUUID = packet->getSourceID();
qDebug() << "Received a disconnect request from node with UUID" << nodeUUID;
if (limitedNodeList->killNodeWithUUID(nodeUUID)) {
static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID);
removedNodePacket->reset();
removedNodePacket->write(nodeUUID.toRfc4122());
// broadcast out the DomainServerRemovedNode message
limitedNodeList->eachNode([&limitedNodeList](const SharedNodePointer& otherNode){
limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode);
});
}
}

View file

@ -60,7 +60,8 @@ public slots:
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processNodeJSONStatsPacket(QSharedPointer<NLPacketList> packetList, SharedNodePointer sendingNode);
void processPathQueryPacket(QSharedPointer<NLPacket> packet);
void processNodeDisconnectRequestPacket(QSharedPointer<NLPacket> packet);
private slots:
void aboutToQuit();

View file

@ -55,8 +55,7 @@ bool IceServer::packetVersionMatch(const udt::Packet& packet) {
if (headerVersion == versionForPacketType(headerType)) {
return true;
} else {
qDebug() << "Packet version mismatch for packet" << headerType
<< "(" << nameForPacketType(headerType) << ") from" << packet.getSenderSockAddr();
qDebug() << "Packet version mismatch for packet" << headerType << " from" << packet.getSenderSockAddr();
return false;
}

View file

@ -804,8 +804,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
void Application::aboutToQuit() {
emit beforeAboutToQuit();
getActiveDisplayPlugin()->deactivate();
_aboutToQuit = true;
cleanupBeforeQuit();
}
@ -831,8 +834,14 @@ void Application::cleanupBeforeQuit() {
_entities.clear(); // this will allow entity scripts to properly shutdown
auto nodeList = DependencyManager::get<NodeList>();
// 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
DependencyManager::get<NodeList>()->getPacketReceiver().setShouldDropPackets(true);
nodeList->getPacketReceiver().setShouldDropPackets(true);
_entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
@ -852,9 +861,6 @@ void Application::cleanupBeforeQuit() {
saveSettings();
_window->saveGeometry();
// let the avatar mixer know we're out
MyAvatar::sendKillAvatar();
// stop the AudioClient
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"stop", Qt::BlockingQueuedConnection);

View file

@ -1024,11 +1024,6 @@ int MyAvatar::parseDataFromBuffer(const QByteArray& buffer) {
return buffer.size();
}
void MyAvatar::sendKillAvatar() {
auto killPacket = NLPacket::create(PacketType::KillAvatar, 0);
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::AvatarMixer);
}
void MyAvatar::updateLookAtTargetAvatar() {
//
// Look at the avatar whose eyes are closest to the ray in direction of my avatar's head

View file

@ -162,8 +162,6 @@ public:
eyeContactTarget getEyeContactTarget();
static void sendKillAvatar();
Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; }
Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); }
Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); }

View file

@ -46,7 +46,13 @@ DomainHandler::DomainHandler(QObject* parent) :
connect(this, &DomainHandler::completedSocketDiscovery, &_icePeer, &NetworkPeer::stopPingTimer);
}
void DomainHandler::clearConnectionInfo() {
void DomainHandler::disconnect() {
// if we're currently connected to a domain, send a disconnect packet on our way out
if (_isConnected) {
sendDisconnectPacket();
}
// clear member variables that hold the connection state to a domain
_uuid = QUuid();
_connectionToken = QUuid();
@ -60,6 +66,18 @@ void DomainHandler::clearConnectionInfo() {
setIsConnected(false);
}
void DomainHandler::sendDisconnectPacket() {
// The DomainDisconnect packet is not verified - we're relying on the eventual addition of DTLS to the
// domain-server connection to stop greifing here
// construct the disconnect packet once (an empty packet but sourced with our current session UUID)
static auto disconnectPacket = NLPacket::create(PacketType::DomainDisconnectRequest, 0);
// send the disconnect packet to the current domain server
auto nodeList = DependencyManager::get<NodeList>();
nodeList->sendUnreliablePacket(*disconnectPacket, _sockAddr);
}
void DomainHandler::clearSettings() {
_settingsObject = QJsonObject();
_failedSettingsRequests = 0;
@ -67,7 +85,7 @@ void DomainHandler::clearSettings() {
void DomainHandler::softReset() {
qCDebug(networking) << "Resetting current domain connection information.";
clearConnectionInfo();
disconnect();
clearSettings();
}

View file

@ -35,7 +35,7 @@ class DomainHandler : public QObject {
public:
DomainHandler(QObject* parent = 0);
void clearConnectionInfo();
void disconnect();
void clearSettings();
const QUuid& getUUID() const { return _uuid; }
@ -113,6 +113,7 @@ signals:
void settingsReceiveFail();
private:
void sendDisconnectPacket();
void hardReset();
QUuid _uuid;

View file

@ -441,7 +441,7 @@ void LimitedNodeList::reset() {
_nodeSocket.clearConnections();
}
void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
QReadLocker readLocker(&_nodeMutex);
NodeHash::iterator it = _nodeHash.find(nodeUUID);
@ -456,7 +456,10 @@ void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
}
handleNodeKill(matchingNode);
return true;
}
return false;
}
void LimitedNodeList::processKillNode(NLPacket& packet) {

View file

@ -230,7 +230,7 @@ public slots:
virtual void sendSTUNRequest();
void sendPingPackets();
void killNodeWithUUID(const QUuid& nodeUUID);
bool killNodeWithUUID(const QUuid& nodeUUID);
signals:
void dataSent(quint8 channelType, int bytes);

View file

@ -103,6 +103,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket");
packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket");
packetReceiver.registerListener(PacketType::DomainServerPathResponse, this, "processDomainServerPathResponse");
packetReceiver.registerListener(PacketType::DomainServerRemovedNode, this, "processDomainServerRemovedNode");
}
qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& destination) {
@ -218,6 +219,10 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes)
}
void NodeList::sendDomainServerCheckIn() {
if (_isShuttingDown) {
qCDebug(networking) << "Refusing to send a domain-server check in while shutting down.";
}
if (_publicSockAddr.isNull()) {
// we don't know our public socket and we need to send it to the domain server
qCDebug(networking) << "Waiting for inital public socket from STUN. Will not send domain-server check in.";
@ -513,6 +518,13 @@ void NodeList::processDomainServerAddedNode(QSharedPointer<NLPacket> packet) {
parseNodeFromPacketStream(packetStream);
}
void NodeList::processDomainServerRemovedNode(QSharedPointer<NLPacket> packet) {
// read the UUID from the packet, remove it if it exists
QUuid nodeUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
qDebug() << "Received packet from domain-server to remove node with UUID" << uuidStringWithoutCurlyBraces(nodeUUID);
killNodeWithUUID(nodeUUID);
}
void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
// setup variables to read into from QDataStream
qint8 nodeType;

View file

@ -66,6 +66,8 @@ public:
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
void sendAssignment(Assignment& assignment);
void setIsShuttingDown(bool isShuttingDown) { _isShuttingDown = isShuttingDown; }
public slots:
void reset();
@ -74,6 +76,7 @@ public slots:
void processDomainServerList(QSharedPointer<NLPacket> packet);
void processDomainServerAddedNode(QSharedPointer<NLPacket> packet);
void processDomainServerRemovedNode(QSharedPointer<NLPacket> packet);
void processDomainServerPathResponse(QSharedPointer<NLPacket> packet);
void processDomainServerConnectionTokenPacket(QSharedPointer<NLPacket> packet);
@ -114,6 +117,7 @@ private:
DomainHandler _domainHandler;
int _numNoReplyDomainCheckIns;
HifiSockAddr _assignmentServerSocket;
bool _isShuttingDown { false };
};
#endif // hifi_NodeList_h

View file

@ -33,14 +33,19 @@ void ThreadedAssignment::setFinished(bool isFinished) {
if (_isFinished) {
qDebug() << "ThreadedAssignment::setFinished(true) called - finishing up.";
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver();
// we should de-register immediately for any of our packets
packetReceiver.unregisterListener(this);
// we should also tell the packet receiver to drop packets while we're cleaning up
packetReceiver.setShouldDropPackets(true);
// send a disconnect packet to the domain
nodeList->getDomainHandler().disconnect();
if (_domainServerTimer) {
// stop the domain-server check in timer by calling deleteLater so it gets cleaned up on NL thread

View file

@ -14,11 +14,13 @@
#include <math.h>
#include <QtCore/QDebug>
#include <QtCore/QMetaEnum>
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
<< PacketType::NodeJsonStats << PacketType::EntityQuery
<< PacketType::OctreeDataNack << PacketType::EntityEditNack
<< PacketType::DomainListRequest << PacketType::StopNode;
<< PacketType::DomainListRequest << PacketType::StopNode
<< PacketType::DomainDisconnectRequest;
const QSet<PacketType> NON_SOURCED_PACKETS = QSet<PacketType>()
<< PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment
@ -29,7 +31,8 @@ const QSet<PacketType> NON_SOURCED_PACKETS = QSet<PacketType>()
<< PacketType::DomainSettingsRequest << PacketType::DomainSettings
<< PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat
<< PacketType::ICEPing << PacketType::ICEPingReply
<< PacketType::AssignmentClientStatus << PacketType::StopNode;
<< PacketType::AssignmentClientStatus << PacketType::StopNode
<< PacketType::DomainServerRemovedNode;
const QSet<PacketType> RELIABLE_PACKETS = QSet<PacketType>();
@ -46,66 +49,17 @@ PacketVersion versionForPacketType(PacketType packetType) {
}
}
#define PACKET_TYPE_NAME_LOOKUP(x) case x: return QString(#x);
QString nameForPacketType(PacketType packetType) {
switch (packetType) {
PACKET_TYPE_NAME_LOOKUP(PacketType::Unknown);
PACKET_TYPE_NAME_LOOKUP(PacketType::StunResponse);
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainList);
PACKET_TYPE_NAME_LOOKUP(PacketType::Ping);
PACKET_TYPE_NAME_LOOKUP(PacketType::PingReply);
PACKET_TYPE_NAME_LOOKUP(PacketType::KillAvatar);
PACKET_TYPE_NAME_LOOKUP(PacketType::AvatarData);
PACKET_TYPE_NAME_LOOKUP(PacketType::InjectAudio);
PACKET_TYPE_NAME_LOOKUP(PacketType::MixedAudio);
PACKET_TYPE_NAME_LOOKUP(PacketType::MicrophoneAudioNoEcho);
PACKET_TYPE_NAME_LOOKUP(PacketType::MicrophoneAudioWithEcho);
PACKET_TYPE_NAME_LOOKUP(PacketType::BulkAvatarData);
PACKET_TYPE_NAME_LOOKUP(PacketType::SilentAudioFrame);
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainListRequest);
PACKET_TYPE_NAME_LOOKUP(PacketType::RequestAssignment);
PACKET_TYPE_NAME_LOOKUP(PacketType::CreateAssignment);
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainConnectionDenied);
PACKET_TYPE_NAME_LOOKUP(PacketType::MuteEnvironment);
PACKET_TYPE_NAME_LOOKUP(PacketType::AudioStreamStats);
PACKET_TYPE_NAME_LOOKUP(PacketType::OctreeStats);
PACKET_TYPE_NAME_LOOKUP(PacketType::Jurisdiction);
PACKET_TYPE_NAME_LOOKUP(PacketType::JurisdictionRequest);
PACKET_TYPE_NAME_LOOKUP(PacketType::AvatarIdentity);
PACKET_TYPE_NAME_LOOKUP(PacketType::AvatarBillboard);
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainConnectRequest);
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerRequireDTLS);
PACKET_TYPE_NAME_LOOKUP(PacketType::NodeJsonStats);
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityQuery);
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityData);
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityErase);
PACKET_TYPE_NAME_LOOKUP(PacketType::OctreeDataNack);
PACKET_TYPE_NAME_LOOKUP(PacketType::StopNode);
PACKET_TYPE_NAME_LOOKUP(PacketType::AudioEnvironment);
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityEditNack);
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEServerHeartbeat);
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerAddedNode);
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEServerQuery);
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEServerPeerInformation);
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEPing);
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEPingReply);
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityAdd);
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityEdit);
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerConnectionToken);
default:
return QString("Type: ") + QString::number((int)packetType);
}
return QString("unexpected");
}
uint qHash(const PacketType& key, uint seed) {
// seems odd that Qt couldn't figure out this cast itself, but this fixes a compile error after switch to
// strongly typed enum for PacketType
// seems odd that Qt couldn't figure out this cast itself, but this fixes a compile error after switch
// to strongly typed enum for PacketType
return qHash((quint8) key, seed);
}
QDebug operator<<(QDebug debug, const PacketType& type) {
debug.nospace() << (uint8_t) type << " (" << qPrintable(nameForPacketType(type)) << ")";
QMetaObject metaObject = PacketTypeEnum::staticMetaObject;
QMetaEnum metaEnum = metaObject.enumerator(metaObject.enumeratorOffset());
QString typeName = metaEnum.valueToKey((int) type);
debug.nospace().noquote() << (uint8_t) type << " (" << typeName << ")";
return debug.space();
}

View file

@ -18,70 +18,81 @@
#include <map>
#include <QtCore/QCryptographicHash>
#include <QtCore/QObject>
#include <QtCore/QSet>
#include <QtCore/QUuid>
// If adding a new packet packetType, you can replace one marked usable or add at the end.
// If you want the name of the packet packetType to be available for debugging or logging, update nameForPacketType() as well
// This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
enum class PacketType : uint8_t {
Unknown,
StunResponse,
DomainList,
Ping,
PingReply,
KillAvatar,
AvatarData,
InjectAudio,
MixedAudio,
MicrophoneAudioNoEcho,
MicrophoneAudioWithEcho,
BulkAvatarData,
SilentAudioFrame,
DomainListRequest,
RequestAssignment,
CreateAssignment,
DomainConnectionDenied,
MuteEnvironment,
AudioStreamStats,
DomainServerPathQuery,
DomainServerPathResponse,
DomainServerAddedNode,
ICEServerPeerInformation,
ICEServerQuery,
OctreeStats,
Jurisdiction,
JurisdictionRequest,
AssignmentClientStatus,
NoisyMute,
AvatarIdentity,
AvatarBillboard,
DomainConnectRequest,
DomainServerRequireDTLS,
NodeJsonStats,
OctreeDataNack,
StopNode,
AudioEnvironment,
EntityEditNack,
ICEServerHeartbeat,
ICEPing,
ICEPingReply,
EntityData,
EntityQuery,
EntityAdd,
EntityErase,
EntityEdit,
DomainServerConnectionToken,
DomainSettingsRequest,
DomainSettings,
AssetGet,
AssetGetReply,
AssetUpload,
AssetUploadReply,
AssetGetInfo,
AssetGetInfoReply
// The enums are inside this PacketTypeEnum for run-time conversion of enum value to string via
// Q_ENUMS, without requiring a macro that is called for each enum value.
class PacketTypeEnum {
Q_GADGET
Q_ENUMS(Value)
public:
// If adding a new packet packetType, you can replace one marked usable or add at the end.
// This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
enum class Value : uint8_t {
Unknown,
StunResponse,
DomainList,
Ping,
PingReply,
KillAvatar,
AvatarData,
InjectAudio,
MixedAudio,
MicrophoneAudioNoEcho,
MicrophoneAudioWithEcho,
BulkAvatarData,
SilentAudioFrame,
DomainListRequest,
RequestAssignment,
CreateAssignment,
DomainConnectionDenied,
MuteEnvironment,
AudioStreamStats,
DomainServerPathQuery,
DomainServerPathResponse,
DomainServerAddedNode,
ICEServerPeerInformation,
ICEServerQuery,
OctreeStats,
Jurisdiction,
JurisdictionRequest,
AssignmentClientStatus,
NoisyMute,
AvatarIdentity,
AvatarBillboard,
DomainConnectRequest,
DomainServerRequireDTLS,
NodeJsonStats,
OctreeDataNack,
StopNode,
AudioEnvironment,
EntityEditNack,
ICEServerHeartbeat,
ICEPing,
ICEPingReply,
EntityData,
EntityQuery,
EntityAdd,
EntityErase,
EntityEdit,
DomainServerConnectionToken,
DomainSettingsRequest,
DomainSettings,
AssetGet,
AssetGetReply,
AssetUpload,
AssetUploadReply,
AssetGetInfo,
AssetGetInfoReply,
DomainDisconnectRequest,
DomainServerRemovedNode
};
};
using PacketType = PacketTypeEnum::Value;
const int NUM_BYTES_MD5_HASH = 16;
typedef char PacketVersion;
@ -90,7 +101,6 @@ extern const QSet<PacketType> NON_VERIFIED_PACKETS;
extern const QSet<PacketType> NON_SOURCED_PACKETS;
extern const QSet<PacketType> RELIABLE_PACKETS;
QString nameForPacketType(PacketType packetType);
PacketVersion versionForPacketType(PacketType packetType);
uint qHash(const PacketType& key, uint seed);

View file

@ -1890,8 +1890,8 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr
versionForPacketType(expectedDataPacketType()), gotVersion);
}
} else {
qCDebug(octree) << "SVO file type mismatch. Expected: " << nameForPacketType(expectedType)
<< " Got: " << nameForPacketType(gotType);
qCDebug(octree) << "SVO file type mismatch. Expected: " << expectedType
<< " Got: " << gotType;
}
} else {