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);
// 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
emit startAvatarAudioTimer();

View file

@ -71,15 +71,10 @@ AvatarMixer::~AvatarMixer() {
void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
QByteArray individualData = nodeData->getAvatar().identityByteArray();
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122());
identityPacket->write(individualData);
DependencyManager::get<NodeList>()->sendPacket(std::move(identityPacket), *destinationNode);
auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
identityPackets->write(individualData);
DependencyManager::get<NodeList>()->sendPacketList(std::move(identityPackets), *destinationNode);
++_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
// or that somehow we haven't sent
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
// don't ignore this avatar if we haven't sent any update for a long while
// in an effort to prevent other interfaces from deleting a stale avatar instance
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;
}
++numAvatarsHeldBack;
shouldIgnore = true;
} 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
++numAvatarsWithSkippedFrames;
@ -285,7 +277,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
int avatarRank = 0;
// 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()) {
AvatarPriority sortData = sortedAvatars.top();

View file

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

View file

@ -115,8 +115,6 @@ Avatar::Avatar(QThread* thread, RigPointer rig) :
}
Avatar::~Avatar() {
assert(isDead()); // mark dead before calling the dtor
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
if (entityTree) {
@ -510,12 +508,13 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
auto avatarPayload = new render::Payload<AvatarData>(self);
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
_renderItemID = scene->allocateID();
transaction.resetItem(_renderItemID, avatarPayloadPointer);
_skeletonModel->addToScene(scene, transaction);
if (_skeletonModel->addToScene(scene, transaction)) {
_renderItemID = scene->allocateID();
transaction.resetItem(_renderItemID, avatarPayloadPointer);
for (auto& attachmentModel : _attachmentModels) {
attachmentModel->addToScene(scene, transaction);
for (auto& attachmentModel : _attachmentModels) {
attachmentModel->addToScene(scene, transaction);
}
}
}
@ -1123,11 +1122,20 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
void Avatar::setModelURLFinished(bool success) {
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL;
// 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()));
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
_skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) {
qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL
<< "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);
if (avatar->shouldDie()) {
avatar->die();
removeAvatar(avatar->getID());
}
const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY;
uint64_t now = usecTimestampNow();
@ -330,44 +326,12 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() {
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) {
AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason);
// 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.
Avatar* avatar = static_cast<Avatar*>(removedAvatar.get());
avatar->die();
auto avatar = std::static_pointer_cast<Avatar>(removedAvatar);
AvatarMotionState* motionState = avatar->getMotionState();
if (motionState) {
@ -403,14 +367,11 @@ void AvatarManager::clearOtherAvatars() {
if (avatar->isInScene()) {
avatar->removeFromScene(avatar, scene, transaction);
}
AvatarMotionState* motionState = avatar->getMotionState();
if (motionState) {
_motionStatesThatMightUpdate.remove(motionState);
_motionStatesToAddToPhysics.remove(motionState);
_motionStatesToRemoveFromPhysics.push_back(motionState);
}
handleRemovedAvatar(avatar);
avatarIterator = _avatarHash.erase(avatarIterator);
} else {
++avatarIterator;
}
++avatarIterator;
}
scene->enqueueTransaction(transaction);
_myAvatar->clearLookAtTargetAvatar();

View file

@ -98,9 +98,6 @@ public slots:
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
void updateAvatarRenderStatus(bool shouldRenderAvatars);
protected slots:
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
private:
explicit AvatarManager(QObject* parent = 0);
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, (halfBoundingBoxDimensions*2.0f)));
uint64_t now = usecTimestampNow();
if (now > _identityPacketExpiry || _avatarEntityDataLocallyEdited) {
_identityPacketExpiry = now + AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS;
if (getIdentityDataChanged()) {
sendIdentityPacket();
}
@ -1258,7 +1256,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
setSkeletonModelURL(fullAvatarURL);
UserActivityLogger::getInstance().changedModel("skeleton", urlString);
}
_identityPacketExpiry = 0; // triggers an identity packet next update()
markIdentityDataChanged();
}
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {

View file

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

View file

@ -79,6 +79,25 @@ int main(int argc, const char* argv[]) {
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) {
// Try to connect and send message to existing interface instance
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;
startupTime.start();

View file

@ -1473,7 +1473,22 @@ QStringList AvatarData::getJointNames() const {
void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) {
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("");
@ -1484,6 +1499,12 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const {
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))) {
setSkeletonModelURL(identity.skeletonModelURL);
identityChanged = true;
@ -1513,24 +1534,35 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC
setAvatarEntityData(identity.avatarEntityData);
identityChanged = true;
}
// flag this avatar as non-stale by updating _averageBytesReceived
const int BOGUS_NUM_BYTES = 1;
_averageBytesReceived.updateAverage(BOGUS_NUM_BYTES);
// use the timestamp from this identity, since we want to honor the updated times in "server clock"
// this will overwrite any changes we made locally to this AvatarData's _identityUpdatedAt
_identityUpdatedAt = identity.updatedAt;
}
QByteArray AvatarData::identityByteArray() const {
QByteArray identityData;
QDataStream identityStream(&identityData, QIODevice::Append);
const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL);
const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL
_avatarEntitiesLock.withReadLock([&] {
identityStream << getSessionUUID() << urlToSend << _attachmentData << _displayName << getSessionDisplayNameForTransport() << _avatarEntityData;
identityStream << getSessionUUID()
<< urlToSend
<< _attachmentData
<< _displayName
<< getSessionDisplayNameForTransport() // depends on _sessionDisplayName
<< _avatarEntityData
<< _identityUpdatedAt;
});
return identityData;
}
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;
if (expanded == _skeletonModelURL) {
return;
@ -1539,6 +1571,7 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
qCDebug(avatars) << "Changing skeleton model for avatar" << getSessionUUID() << "to" << _skeletonModelURL.toString();
updateJointMappings();
markIdentityDataChanged();
}
void AvatarData::setDisplayName(const QString& displayName) {
@ -1548,6 +1581,7 @@ void AvatarData::setDisplayName(const QString& displayName) {
sendIdentityPacket();
qCDebug(avatars) << "Changing display name for avatar to" << displayName;
markIdentityDataChanged();
}
QVector<AttachmentData> AvatarData::getAttachmentData() const {
@ -1566,6 +1600,7 @@ void AvatarData::setAttachmentData(const QVector<AttachmentData>& attachmentData
return;
}
_attachmentData = attachmentData;
markIdentityDataChanged();
}
void AvatarData::attach(const QString& modelURL, const QString& jointName,
@ -1695,7 +1730,6 @@ void AvatarData::sendAvatarDataPacket() {
void AvatarData::sendIdentityPacket() {
auto nodeList = DependencyManager::get<NodeList>();
QByteArray identityData = identityByteArray();
auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
@ -1709,6 +1743,7 @@ void AvatarData::sendIdentityPacket() {
});
_avatarEntityDataLocallyEdited = false;
_identityDataChanged = false;
}
void AvatarData::updateJointMappings() {
@ -2245,10 +2280,12 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent
if (_avatarEntityData.size() < MAX_NUM_AVATAR_ENTITIES) {
_avatarEntityData.insert(entityID, entityData);
_avatarEntityDataLocallyEdited = true;
markIdentityDataChanged();
}
} else {
itr.value() = entityData;
_avatarEntityDataLocallyEdited = true;
markIdentityDataChanged();
}
});
}
@ -2262,6 +2299,7 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID) {
_avatarEntitiesLock.withWriteLock([&] {
_avatarEntityData.remove(entityID);
_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 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
const char AVATARDATA_FLAGS_MINIMUM = 0;
@ -531,6 +529,7 @@ public:
QString displayName;
QString sessionDisplayName;
AvatarEntityMap avatarEntityData;
quint64 updatedAt;
};
static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut);
@ -547,7 +546,10 @@ public:
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
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 virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
@ -565,7 +567,6 @@ public:
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); }
int getAverageBytesReceivedPerSecond() const;
int getReceiveRate() const;
@ -601,9 +602,6 @@ public:
return _lastSentJointData;
}
bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_UPDATE_TIMEOUT; }
static const float OUT_OF_VIEW_PENALTY;
static void sortAvatars(
@ -620,11 +618,15 @@ public:
static float _avatarSortCoefficientCenter;
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:
void displayNameChanged();
public slots:
void sendAvatarDataPacket();
void sendIdentityPacket();
@ -779,6 +781,9 @@ protected:
quint64 _audioLoudnessChanged { 0 };
float _audioAverageLoudness { 0.0f };
bool _identityDataChanged { false };
quint64 _identityUpdatedAt { 0 };
private:
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
static QUrl _defaultFullAvatarModelUrl;

View file

@ -57,7 +57,7 @@ public slots:
protected slots:
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 processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
void processExitingSpaceBubble(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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