mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-11 05:43:04 +02:00
Merge pull request #10255 from ZappoMan/retrySkeletonURL
Fix two cases for avatars stuck in T-Pose
This commit is contained in:
commit
df7cb09fe4
22 changed files with 145 additions and 137 deletions
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -231,7 +231,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
|||
ImmediateSessionDisplayNameUpdates,
|
||||
VariableAvatarData,
|
||||
AvatarAsChildFixes,
|
||||
StickAndBallDefaultAvatar
|
||||
StickAndBallDefaultAvatar,
|
||||
IdentityPacketsIncludeUpdateTime
|
||||
};
|
||||
|
||||
enum class DomainConnectRequestVersion : PacketVersion {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue