From dc7a9f253cd46895d4b751d9f0940854f8960958 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 5 Oct 2016 13:06:11 -0700 Subject: [PATCH] kill agents in avatar mixer when they stop being avatars --- assignment-client/src/Agent.cpp | 15 +++++++++++++++ libraries/avatars/src/AvatarData.cpp | 11 ++++++++--- libraries/avatars/src/AvatarData.h | 1 + 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index c6a2a3d5e8..c2dba20fb6 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -351,6 +351,21 @@ void Agent::setIsAvatar(bool isAvatar) { _avatarIdentityTimer->stop(); delete _avatarIdentityTimer; _avatarIdentityTimer = nullptr; + + // The avatar mixer never times out a connection (e.g., based on identity or data packets) + // but rather keeps avatars in its list as long as "connected". As a result, clients timeout + // when we stop sending identity, but then get woken up again by the mixer itself, which sends + // identity packets to everyone. Here we explicitly tell the mixer to kill the entry for us. + auto nodeList = DependencyManager::get(); + auto packetList = NLPacketList::create(PacketType::KillAvatar, QByteArray(), true, true); + packetList->write(getSessionUUID().toRfc4122()); + nodeList->eachMatchingNode( + [&](const SharedNodePointer& node)->bool { + return node->getType() == NodeType::AvatarMixer && node->getActiveSocket(); + }, + [&](const SharedNodePointer& node) { + nodeList->sendPacketList(std::move(packetList), *node); + }); } } } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 413180738a..d30b69c5f8 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -976,10 +976,16 @@ void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& ide packetStream >> identityOut.uuid >> identityOut.skeletonModelURL >> identityOut.attachmentData >> identityOut.displayName >> identityOut.avatarEntityData; } +static const QUrl emptyURL(""); +const QUrl& AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) { + // We don't put file urls on the wire, but instead convert to empty. + return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; +} + bool AvatarData::processAvatarIdentity(const Identity& identity) { bool hasIdentityChanged = false; - if (_firstSkeletonCheck || (identity.skeletonModelURL != _skeletonModelURL)) { + if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { setSkeletonModelURL(identity.skeletonModelURL); hasIdentityChanged = true; _firstSkeletonCheck = false; @@ -1010,8 +1016,7 @@ bool AvatarData::processAvatarIdentity(const Identity& identity) { QByteArray AvatarData::identityByteArray() { QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); - QUrl emptyURL(""); - const QUrl& urlToSend = _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; + const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); _avatarEntitiesLock.withReadLock([&] { identityStream << getSessionUUID() << urlToSend << _attachmentData << _displayName << _avatarEntityData; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a5314bf4a8..cb3ef0c40e 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -399,6 +399,7 @@ protected: QUrl _skeletonFBXURL; QVector _attachmentData; QString _displayName; + const QUrl& cannonicalSkeletonModelURL(const QUrl& empty); float _displayNameTargetAlpha; float _displayNameAlpha;