Merge pull request #10255 from ZappoMan/retrySkeletonURL

Fix two cases for avatars stuck in T-Pose
This commit is contained in:
Chris Collins 2017-04-26 21:38:19 -07:00 committed by GitHub
commit df7cb09fe4
22 changed files with 145 additions and 137 deletions

View file

@ -544,7 +544,7 @@ void Agent::setIsAvatar(bool isAvatar) {
connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket); connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
// start the timers // start the timers
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets
// tell the avatarAudioTimer to start ticking // tell the avatarAudioTimer to start ticking
emit startAvatarAudioTimer(); emit startAvatarAudioTimer();

View file

@ -71,15 +71,10 @@ AvatarMixer::~AvatarMixer() {
void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
QByteArray individualData = nodeData->getAvatar().identityByteArray(); QByteArray individualData = nodeData->getAvatar().identityByteArray();
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122());
auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
identityPacket->write(individualData); identityPackets->write(individualData);
DependencyManager::get<NodeList>()->sendPacketList(std::move(identityPackets), *destinationNode);
DependencyManager::get<NodeList>()->sendPacket(std::move(identityPacket), *destinationNode);
++_sumIdentityPackets; ++_sumIdentityPackets;
} }

View file

@ -263,16 +263,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
// make sure we haven't already sent this data from this sender to this receiver // make sure we haven't already sent this data from this sender to this receiver
// or that somehow we haven't sent // or that somehow we haven't sent
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
// don't ignore this avatar if we haven't sent any update for a long while ++numAvatarsHeldBack;
// in an effort to prevent other interfaces from deleting a stale avatar instance shouldIgnore = true;
uint64_t lastBroadcastTime = nodeData->getLastBroadcastTime(avatarNode->getUUID());
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
const uint64_t AVATAR_UPDATE_STALE = AVATAR_UPDATE_TIMEOUT - USECS_PER_SECOND;
if (lastBroadcastTime > otherNodeData->getIdentityChangeTimestamp() &&
lastBroadcastTime + AVATAR_UPDATE_STALE > startIgnoreCalculation) {
++numAvatarsHeldBack;
shouldIgnore = true;
}
} else if (lastSeqFromSender - lastSeqToReceiver > 1) { } else if (lastSeqFromSender - lastSeqToReceiver > 1) {
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening // this is a skip - we still send the packet but capture the presence of the skip so we see it happening
++numAvatarsWithSkippedFrames; ++numAvatarsWithSkippedFrames;
@ -285,7 +277,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
int avatarRank = 0; int avatarRank = 0;
// this is overly conservative, because it includes some avatars we might not consider // this is overly conservative, because it includes some avatars we might not consider
int remainingAvatars = (int)sortedAvatars.size(); int remainingAvatars = (int)sortedAvatars.size();
while (!sortedAvatars.empty()) { while (!sortedAvatars.empty()) {
AvatarPriority sortData = sortedAvatars.top(); AvatarPriority sortData = sortedAvatars.top();

View file

@ -5216,11 +5216,7 @@ void Application::resettingDomain() {
} }
void Application::nodeAdded(SharedNodePointer node) const { void Application::nodeAdded(SharedNodePointer node) const {
if (node->getType() == NodeType::AvatarMixer) { // nothing to do here
// new avatar mixer, send off our identity packet right away
getMyAvatar()->sendIdentityPacket();
getMyAvatar()->resetLastSent();
}
} }
void Application::nodeActivated(SharedNodePointer node) { void Application::nodeActivated(SharedNodePointer node) {
@ -5256,6 +5252,13 @@ void Application::nodeActivated(SharedNodePointer node) {
if (node->getType() == NodeType::AudioMixer) { if (node->getType() == NodeType::AudioMixer) {
DependencyManager::get<AudioClient>()->negotiateAudioFormat(); DependencyManager::get<AudioClient>()->negotiateAudioFormat();
} }
if (node->getType() == NodeType::AvatarMixer) {
// new avatar mixer, send off our identity packet right away
getMyAvatar()->markIdentityDataChanged();
getMyAvatar()->sendIdentityPacket();
getMyAvatar()->resetLastSent();
}
} }
void Application::nodeKilled(SharedNodePointer node) { void Application::nodeKilled(SharedNodePointer node) {

View file

@ -115,8 +115,6 @@ Avatar::Avatar(QThread* thread, RigPointer rig) :
} }
Avatar::~Avatar() { Avatar::~Avatar() {
assert(isDead()); // mark dead before calling the dtor
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>(); auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
if (entityTree) { if (entityTree) {
@ -510,12 +508,13 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
auto avatarPayload = new render::Payload<AvatarData>(self); auto avatarPayload = new render::Payload<AvatarData>(self);
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
_renderItemID = scene->allocateID(); if (_skeletonModel->addToScene(scene, transaction)) {
transaction.resetItem(_renderItemID, avatarPayloadPointer); _renderItemID = scene->allocateID();
_skeletonModel->addToScene(scene, transaction); transaction.resetItem(_renderItemID, avatarPayloadPointer);
for (auto& attachmentModel : _attachmentModels) { for (auto& attachmentModel : _attachmentModels) {
attachmentModel->addToScene(scene, transaction); attachmentModel->addToScene(scene, transaction);
}
} }
} }
@ -1123,11 +1122,20 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
void Avatar::setModelURLFinished(bool success) { void Avatar::setModelURLFinished(bool success) {
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) { if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL; const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
// call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
// we don't redo this every time we receive an identity packet from the avatar with the bad url. _skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) {
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL
Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); << "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts.";
// call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that
// we don't redo this every time we receive an identity packet from the avatar with the bad url.
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL",
Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl()));
} else {
qCWarning(interfaceapp) << "Avatar model: " << _skeletonModelURL
<< "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts()
<< "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS;
}
} }
} }

View file

@ -213,10 +213,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
} }
} }
avatar->animateScaleChanges(deltaTime); avatar->animateScaleChanges(deltaTime);
if (avatar->shouldDie()) {
avatar->die();
removeAvatar(avatar->getID());
}
const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY;
uint64_t now = usecTimestampNow(); uint64_t now = usecTimestampNow();
@ -330,44 +326,12 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() {
return std::make_shared<Avatar>(qApp->thread(), std::make_shared<Rig>()); return std::make_shared<Avatar>(qApp->thread(), std::make_shared<Rig>());
} }
void AvatarManager::processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
PerformanceTimer perfTimer("receiveAvatar");
// enumerate over all of the avatars in this packet
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
while (message->getBytesLeftToRead()) {
AvatarSharedPointer avatarData = parseAvatarData(message, sendingNode);
if (avatarData) {
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
if (avatar->isInScene()) {
if (!_shouldRender) {
// rare transition so we process the transaction immediately
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
avatar->removeFromScene(avatar, scene, transaction);
if (scene) {
scene->enqueueTransaction(transaction);
}
}
} else if (_shouldRender) {
// very rare transition so we process the transaction immediately
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
avatar->addToScene(avatar, scene, transaction);
if (scene) {
scene->enqueueTransaction(transaction);
}
}
}
}
}
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason); AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason);
// removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar // removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar
// class in this context so we can call methods that don't exist at the base class. // class in this context so we can call methods that don't exist at the base class.
Avatar* avatar = static_cast<Avatar*>(removedAvatar.get()); auto avatar = std::static_pointer_cast<Avatar>(removedAvatar);
avatar->die();
AvatarMotionState* motionState = avatar->getMotionState(); AvatarMotionState* motionState = avatar->getMotionState();
if (motionState) { if (motionState) {
@ -403,14 +367,11 @@ void AvatarManager::clearOtherAvatars() {
if (avatar->isInScene()) { if (avatar->isInScene()) {
avatar->removeFromScene(avatar, scene, transaction); avatar->removeFromScene(avatar, scene, transaction);
} }
AvatarMotionState* motionState = avatar->getMotionState(); handleRemovedAvatar(avatar);
if (motionState) { avatarIterator = _avatarHash.erase(avatarIterator);
_motionStatesThatMightUpdate.remove(motionState); } else {
_motionStatesToAddToPhysics.remove(motionState); ++avatarIterator;
_motionStatesToRemoveFromPhysics.push_back(motionState);
}
} }
++avatarIterator;
} }
scene->enqueueTransaction(transaction); scene->enqueueTransaction(transaction);
_myAvatar->clearLookAtTargetAvatar(); _myAvatar->clearLookAtTargetAvatar();

View file

@ -98,9 +98,6 @@ public slots:
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
void updateAvatarRenderStatus(bool shouldRenderAvatars); void updateAvatarRenderStatus(bool shouldRenderAvatars);
protected slots:
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
private: private:
explicit AvatarManager(QObject* parent = 0); explicit AvatarManager(QObject* parent = 0);
explicit AvatarManager(const AvatarManager& other); explicit AvatarManager(const AvatarManager& other);

View file

@ -412,9 +412,7 @@ void MyAvatar::update(float deltaTime) {
Q_ARG(glm::vec3, (getPosition() - halfBoundingBoxDimensions)), Q_ARG(glm::vec3, (getPosition() - halfBoundingBoxDimensions)),
Q_ARG(glm::vec3, (halfBoundingBoxDimensions*2.0f))); Q_ARG(glm::vec3, (halfBoundingBoxDimensions*2.0f)));
uint64_t now = usecTimestampNow(); if (getIdentityDataChanged()) {
if (now > _identityPacketExpiry || _avatarEntityDataLocallyEdited) {
_identityPacketExpiry = now + AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS;
sendIdentityPacket(); sendIdentityPacket();
} }
@ -1258,7 +1256,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
setSkeletonModelURL(fullAvatarURL); setSkeletonModelURL(fullAvatarURL);
UserActivityLogger::getInstance().changedModel("skeleton", urlString); UserActivityLogger::getInstance().changedModel("skeleton", urlString);
} }
_identityPacketExpiry = 0; // triggers an identity packet next update() markIdentityDataChanged();
} }
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) { void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {

View file

@ -701,8 +701,6 @@ private:
std::mutex _holdActionsMutex; std::mutex _holdActionsMutex;
std::vector<AvatarActionHold*> _holdActions; std::vector<AvatarActionHold*> _holdActions;
uint64_t _identityPacketExpiry { 0 };
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f }; float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
float AUDIO_ENERGY_CONSTANT { 0.000001f }; float AUDIO_ENERGY_CONSTANT { 0.000001f };
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f }; float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };

View file

@ -79,6 +79,25 @@ int main(int argc, const char* argv[]) {
instanceMightBeRunning = false; instanceMightBeRunning = false;
} }
QCommandLineParser parser;
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
QCommandLineOption runServerOption("runServer", "Whether to run the server");
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
parser.addOption(checkMinSpecOption);
parser.addOption(runServerOption);
parser.addOption(serverContentPathOption);
parser.addOption(allowMultipleInstancesOption);
parser.parse(arguments);
bool runServer = parser.isSet(runServerOption);
bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption);
QString serverContentPathOptionValue = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString();
bool allowMultipleInstances = parser.isSet(allowMultipleInstancesOption);
if (allowMultipleInstances) {
instanceMightBeRunning = false;
}
if (instanceMightBeRunning) { if (instanceMightBeRunning) {
// Try to connect and send message to existing interface instance // Try to connect and send message to existing interface instance
QLocalSocket socket; QLocalSocket socket;
@ -137,18 +156,6 @@ int main(int argc, const char* argv[]) {
} }
} }
QCommandLineParser parser;
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
QCommandLineOption runServerOption("runServer", "Whether to run the server");
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
parser.addOption(checkMinSpecOption);
parser.addOption(runServerOption);
parser.addOption(serverContentPathOption);
parser.parse(arguments);
bool runServer = parser.isSet(runServerOption);
bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption);
QString serverContentPathOptionValue = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString();
QElapsedTimer startupTime; QElapsedTimer startupTime;
startupTime.start(); startupTime.start();

View file

@ -1473,7 +1473,22 @@ QStringList AvatarData::getJointNames() const {
void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) { void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) {
QDataStream packetStream(data); QDataStream packetStream(data);
packetStream >> identityOut.uuid >> identityOut.skeletonModelURL >> identityOut.attachmentData >> identityOut.displayName >> identityOut.sessionDisplayName >> identityOut.avatarEntityData; packetStream >> identityOut.uuid
>> identityOut.skeletonModelURL
>> identityOut.attachmentData
>> identityOut.displayName
>> identityOut.sessionDisplayName
>> identityOut.avatarEntityData
>> identityOut.updatedAt;
#ifdef WANT_DEBUG
qCDebug(avatars) << __FUNCTION__
<< "identityOut.uuid:" << identityOut.uuid
<< "identityOut.skeletonModelURL:" << identityOut.skeletonModelURL
<< "identityOut.displayName:" << identityOut.displayName
<< "identityOut.sessionDisplayName:" << identityOut.sessionDisplayName;
#endif
} }
static const QUrl emptyURL(""); static const QUrl emptyURL("");
@ -1484,6 +1499,12 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const {
void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) { void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) {
if (identity.updatedAt < _identityUpdatedAt) {
qCDebug(avatars) << "Ignoring late identity packet for avatar " << getSessionUUID()
<< "identity.updatedAt:" << identity.updatedAt << "_identityUpdatedAt:" << _identityUpdatedAt;
return;
}
if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) {
setSkeletonModelURL(identity.skeletonModelURL); setSkeletonModelURL(identity.skeletonModelURL);
identityChanged = true; identityChanged = true;
@ -1513,24 +1534,35 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC
setAvatarEntityData(identity.avatarEntityData); setAvatarEntityData(identity.avatarEntityData);
identityChanged = true; identityChanged = true;
} }
// flag this avatar as non-stale by updating _averageBytesReceived
const int BOGUS_NUM_BYTES = 1; // use the timestamp from this identity, since we want to honor the updated times in "server clock"
_averageBytesReceived.updateAverage(BOGUS_NUM_BYTES); // this will overwrite any changes we made locally to this AvatarData's _identityUpdatedAt
_identityUpdatedAt = identity.updatedAt;
} }
QByteArray AvatarData::identityByteArray() const { QByteArray AvatarData::identityByteArray() const {
QByteArray identityData; QByteArray identityData;
QDataStream identityStream(&identityData, QIODevice::Append); QDataStream identityStream(&identityData, QIODevice::Append);
const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL
_avatarEntitiesLock.withReadLock([&] { _avatarEntitiesLock.withReadLock([&] {
identityStream << getSessionUUID() << urlToSend << _attachmentData << _displayName << getSessionDisplayNameForTransport() << _avatarEntityData; identityStream << getSessionUUID()
<< urlToSend
<< _attachmentData
<< _displayName
<< getSessionDisplayNameForTransport() // depends on _sessionDisplayName
<< _avatarEntityData
<< _identityUpdatedAt;
}); });
return identityData; return identityData;
} }
void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
if (skeletonModelURL.isEmpty()) {
qCDebug(avatars) << __FUNCTION__ << "caller called with empty URL.";
}
const QUrl& expanded = skeletonModelURL.isEmpty() ? AvatarData::defaultFullAvatarModelUrl() : skeletonModelURL; const QUrl& expanded = skeletonModelURL.isEmpty() ? AvatarData::defaultFullAvatarModelUrl() : skeletonModelURL;
if (expanded == _skeletonModelURL) { if (expanded == _skeletonModelURL) {
return; return;
@ -1539,6 +1571,7 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
qCDebug(avatars) << "Changing skeleton model for avatar" << getSessionUUID() << "to" << _skeletonModelURL.toString(); qCDebug(avatars) << "Changing skeleton model for avatar" << getSessionUUID() << "to" << _skeletonModelURL.toString();
updateJointMappings(); updateJointMappings();
markIdentityDataChanged();
} }
void AvatarData::setDisplayName(const QString& displayName) { void AvatarData::setDisplayName(const QString& displayName) {
@ -1548,6 +1581,7 @@ void AvatarData::setDisplayName(const QString& displayName) {
sendIdentityPacket(); sendIdentityPacket();
qCDebug(avatars) << "Changing display name for avatar to" << displayName; qCDebug(avatars) << "Changing display name for avatar to" << displayName;
markIdentityDataChanged();
} }
QVector<AttachmentData> AvatarData::getAttachmentData() const { QVector<AttachmentData> AvatarData::getAttachmentData() const {
@ -1566,6 +1600,7 @@ void AvatarData::setAttachmentData(const QVector<AttachmentData>& attachmentData
return; return;
} }
_attachmentData = attachmentData; _attachmentData = attachmentData;
markIdentityDataChanged();
} }
void AvatarData::attach(const QString& modelURL, const QString& jointName, void AvatarData::attach(const QString& modelURL, const QString& jointName,
@ -1695,7 +1730,6 @@ void AvatarData::sendAvatarDataPacket() {
void AvatarData::sendIdentityPacket() { void AvatarData::sendIdentityPacket() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
QByteArray identityData = identityByteArray(); QByteArray identityData = identityByteArray();
auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
@ -1709,6 +1743,7 @@ void AvatarData::sendIdentityPacket() {
}); });
_avatarEntityDataLocallyEdited = false; _avatarEntityDataLocallyEdited = false;
_identityDataChanged = false;
} }
void AvatarData::updateJointMappings() { void AvatarData::updateJointMappings() {
@ -2245,10 +2280,12 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent
if (_avatarEntityData.size() < MAX_NUM_AVATAR_ENTITIES) { if (_avatarEntityData.size() < MAX_NUM_AVATAR_ENTITIES) {
_avatarEntityData.insert(entityID, entityData); _avatarEntityData.insert(entityID, entityData);
_avatarEntityDataLocallyEdited = true; _avatarEntityDataLocallyEdited = true;
markIdentityDataChanged();
} }
} else { } else {
itr.value() = entityData; itr.value() = entityData;
_avatarEntityDataLocallyEdited = true; _avatarEntityDataLocallyEdited = true;
markIdentityDataChanged();
} }
}); });
} }
@ -2262,6 +2299,7 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID) {
_avatarEntitiesLock.withWriteLock([&] { _avatarEntitiesLock.withWriteLock([&] {
_avatarEntityData.remove(entityID); _avatarEntityData.remove(entityID);
_avatarEntityDataLocallyEdited = true; _avatarEntityDataLocallyEdited = true;
markIdentityDataChanged();
}); });
} }

View file

@ -110,9 +110,7 @@ const char LEFT_HAND_POINTING_FLAG = 1;
const char RIGHT_HAND_POINTING_FLAG = 2; const char RIGHT_HAND_POINTING_FLAG = 2;
const char IS_FINGER_POINTING_FLAG = 4; const char IS_FINGER_POINTING_FLAG = 4;
const qint64 AVATAR_UPDATE_TIMEOUT = 5 * USECS_PER_SECOND; // AvatarData state flags - we store the details about the packet encoding in the first byte,
// AvatarData state flags - we store the details about the packet encoding in the first byte,
// before the "header" structure // before the "header" structure
const char AVATARDATA_FLAGS_MINIMUM = 0; const char AVATARDATA_FLAGS_MINIMUM = 0;
@ -531,6 +529,7 @@ public:
QString displayName; QString displayName;
QString sessionDisplayName; QString sessionDisplayName;
AvatarEntityMap avatarEntityData; AvatarEntityMap avatarEntityData;
quint64 updatedAt;
}; };
static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut); static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut);
@ -547,7 +546,10 @@ public:
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
virtual void setDisplayName(const QString& displayName); virtual void setDisplayName(const QString& displayName);
virtual void setSessionDisplayName(const QString& sessionDisplayName) { _sessionDisplayName = sessionDisplayName; }; virtual void setSessionDisplayName(const QString& sessionDisplayName) {
_sessionDisplayName = sessionDisplayName;
markIdentityDataChanged();
}
Q_INVOKABLE QVector<AttachmentData> getAttachmentData() const; Q_INVOKABLE QVector<AttachmentData> getAttachmentData() const;
Q_INVOKABLE virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData); Q_INVOKABLE virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
@ -565,7 +567,6 @@ public:
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); }
int getAverageBytesReceivedPerSecond() const; int getAverageBytesReceivedPerSecond() const;
int getReceiveRate() const; int getReceiveRate() const;
@ -601,9 +602,6 @@ public:
return _lastSentJointData; return _lastSentJointData;
} }
bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_UPDATE_TIMEOUT; }
static const float OUT_OF_VIEW_PENALTY; static const float OUT_OF_VIEW_PENALTY;
static void sortAvatars( static void sortAvatars(
@ -620,11 +618,15 @@ public:
static float _avatarSortCoefficientCenter; static float _avatarSortCoefficientCenter;
static float _avatarSortCoefficientAge; static float _avatarSortCoefficientAge;
bool getIdentityDataChanged() const { return _identityDataChanged; } // has the identity data changed since the last time sendIdentityPacket() was called
void markIdentityDataChanged() {
_identityDataChanged = true;
_identityUpdatedAt = usecTimestampNow();
}
signals: signals:
void displayNameChanged(); void displayNameChanged();
public slots: public slots:
void sendAvatarDataPacket(); void sendAvatarDataPacket();
void sendIdentityPacket(); void sendIdentityPacket();
@ -779,6 +781,9 @@ protected:
quint64 _audioLoudnessChanged { 0 }; quint64 _audioLoudnessChanged { 0 };
float _audioAverageLoudness { 0.0f }; float _audioAverageLoudness { 0.0f };
bool _identityDataChanged { false };
quint64 _identityUpdatedAt { 0 };
private: private:
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
static QUrl _defaultFullAvatarModelUrl; static QUrl _defaultFullAvatarModelUrl;

View file

@ -57,7 +57,7 @@ public slots:
protected slots: protected slots:
void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID); void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID);
virtual void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode); void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode); void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
void processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode); void processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
void processExitingSpaceBubble(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode); void processExitingSpaceBubble(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);

View file

@ -112,6 +112,8 @@ public:
void setResource(GeometryResource::Pointer resource); void setResource(GeometryResource::Pointer resource);
QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); } QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); }
int getResourceDownloadAttempts() { return _resource ? _resource->getDownloadAttempts() : 0; }
int getResourceDownloadAttemptsRemaining() { return _resource ? _resource->getDownloadAttemptsRemaining() : 0; }
private: private:
void startWatching(); void startWatching();

View file

@ -56,7 +56,6 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
NetworkPeer(uuid, publicSocket, localSocket, parent), NetworkPeer(uuid, publicSocket, localSocket, parent),
_type(type), _type(type),
_connectionSecret(connectionSecret), _connectionSecret(connectionSecret),
_isAlive(true),
_pingMs(-1), // "Uninitialized" _pingMs(-1), // "Uninitialized"
_clockSkewUsec(0), _clockSkewUsec(0),
_mutex(), _mutex(),

View file

@ -54,9 +54,6 @@ public:
NodeData* getLinkedData() const { return _linkedData.get(); } NodeData* getLinkedData() const { return _linkedData.get(); }
void setLinkedData(std::unique_ptr<NodeData> linkedData) { _linkedData = std::move(linkedData); } void setLinkedData(std::unique_ptr<NodeData> linkedData) { _linkedData = std::move(linkedData); }
bool isAlive() const { return _isAlive; }
void setAlive(bool isAlive) { _isAlive = isAlive; }
int getPingMs() const { return _pingMs; } int getPingMs() const { return _pingMs; }
void setPingMs(int pingMs) { _pingMs = pingMs; } void setPingMs(int pingMs) { _pingMs = pingMs; }
@ -92,7 +89,6 @@ private:
QUuid _connectionSecret; QUuid _connectionSecret;
std::unique_ptr<NodeData> _linkedData; std::unique_ptr<NodeData> _linkedData;
bool _isAlive;
int _pingMs; int _pingMs;
qint64 _clockSkewUsec; qint64 _clockSkewUsec;
QMutex _mutex; QMutex _mutex;

View file

@ -26,8 +26,7 @@ ReceivedMessage::ReceivedMessage(const NLPacketList& packetList)
_sourceID(packetList.getSourceID()), _sourceID(packetList.getSourceID()),
_packetType(packetList.getType()), _packetType(packetList.getType()),
_packetVersion(packetList.getVersion()), _packetVersion(packetList.getVersion()),
_senderSockAddr(packetList.getSenderSockAddr()), _senderSockAddr(packetList.getSenderSockAddr())
_isComplete(true)
{ {
} }

View file

@ -627,8 +627,6 @@ void Resource::init() {
} }
} }
const int MAX_ATTEMPTS = 8;
void Resource::attemptRequest() { void Resource::attemptRequest() {
_startedLoading = true; _startedLoading = true;
@ -746,14 +744,17 @@ bool Resource::handleFailedRequest(ResourceRequest::Result result) {
// Fall through to other cases // Fall through to other cases
} }
case ResourceRequest::Result::ServerUnavailable: { case ResourceRequest::Result::ServerUnavailable: {
_attempts++;
_attemptsRemaining--;
qCDebug(networking) << "Retriable error while loading" << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining;
// retry with increasing delays // retry with increasing delays
const int BASE_DELAY_MS = 1000; const int BASE_DELAY_MS = 1000;
if (_attempts++ < MAX_ATTEMPTS) { if (_attempts < MAX_ATTEMPTS) {
auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts);
qCDebug(networking).noquote() << "Server unavailable for" << _url << "- may retry in" << waitTime << "ms" qCDebug(networking).noquote() << "Server unavailable for" << _url << "- may retry in" << waitTime << "ms"
<< "if resource is still needed"; << "if resource is still needed";
QTimer::singleShot(waitTime, this, &Resource::attemptRequest); QTimer::singleShot(waitTime, this, &Resource::attemptRequest);
willRetry = true; willRetry = true;
break; break;
@ -761,9 +762,10 @@ bool Resource::handleFailedRequest(ResourceRequest::Result result) {
// fall through to final failure // fall through to final failure
} }
default: { default: {
qCDebug(networking) << "Error loading " << _url; _attemptsRemaining = 0;
qCDebug(networking) << "Error loading " << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining;
auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError
: QNetworkReply::UnknownNetworkError; : QNetworkReply::UnknownNetworkError;
emit failed(error); emit failed(error);
willRetry = false; willRetry = false;
finishedLoading(false); finishedLoading(false);

View file

@ -395,6 +395,9 @@ public:
const QUrl& getURL() const { return _url; } const QUrl& getURL() const { return _url; }
unsigned int getDownloadAttempts() { return _attempts; }
unsigned int getDownloadAttemptsRemaining() { return _attemptsRemaining; }
signals: signals:
/// Fired when the resource begins downloading. /// Fired when the resource begins downloading.
void loading(); void loading();
@ -483,7 +486,9 @@ private:
int _lruKey{ 0 }; int _lruKey{ 0 };
QTimer* _replyTimer{ nullptr }; QTimer* _replyTimer{ nullptr };
int _attempts{ 0 }; unsigned int _attempts{ 0 };
static const int MAX_ATTEMPTS = 8;
unsigned int _attemptsRemaining { MAX_ATTEMPTS };
bool _isInScript{ false }; bool _isInScript{ false };
}; };

View file

@ -56,7 +56,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::AvatarData: case PacketType::AvatarData:
case PacketType::BulkAvatarData: case PacketType::BulkAvatarData:
case PacketType::KillAvatar: case PacketType::KillAvatar:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::StickAndBallDefaultAvatar); return static_cast<PacketVersion>(AvatarMixerPacketVersion::IdentityPacketsIncludeUpdateTime);
case PacketType::MessagesData: case PacketType::MessagesData:
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData); return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
case PacketType::ICEServerHeartbeat: case PacketType::ICEServerHeartbeat:

View file

@ -231,7 +231,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
ImmediateSessionDisplayNameUpdates, ImmediateSessionDisplayNameUpdates,
VariableAvatarData, VariableAvatarData,
AvatarAsChildFixes, AvatarAsChildFixes,
StickAndBallDefaultAvatar StickAndBallDefaultAvatar,
IdentityPacketsIncludeUpdateTime
}; };
enum class DomainConnectRequestVersion : PacketVersion { enum class DomainConnectRequestVersion : PacketVersion {

View file

@ -252,6 +252,8 @@ public:
void renderDebugMeshBoxes(gpu::Batch& batch); void renderDebugMeshBoxes(gpu::Batch& batch);
int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); }
int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); }
public slots: public slots:
void loadURLFinished(bool success); void loadURLFinished(bool success);