mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Checkpoint trait flow control
This commit is contained in:
parent
c9e6d2711d
commit
2e457e2212
2 changed files with 64 additions and 29 deletions
|
@ -19,9 +19,7 @@
|
||||||
|
|
||||||
#include "AvatarMixerSlave.h"
|
#include "AvatarMixerSlave.h"
|
||||||
|
|
||||||
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) :
|
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID, nodeLocalID) {
|
||||||
NodeData(nodeID, nodeLocalID)
|
|
||||||
{
|
|
||||||
// in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID
|
// in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID
|
||||||
_avatar->setID(nodeID);
|
_avatar->setID(nodeID);
|
||||||
}
|
}
|
||||||
|
@ -82,12 +80,11 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
|
||||||
}
|
}
|
||||||
|
|
||||||
int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
||||||
|
|
||||||
// pull the sequence number from the data first
|
// pull the sequence number from the data first
|
||||||
uint16_t sequenceNumber;
|
uint16_t sequenceNumber;
|
||||||
|
|
||||||
message.readPrimitive(&sequenceNumber);
|
message.readPrimitive(&sequenceNumber);
|
||||||
|
|
||||||
if (sequenceNumber < _lastReceivedSequenceNumber && _lastReceivedSequenceNumber != UINT16_MAX) {
|
if (sequenceNumber < _lastReceivedSequenceNumber && _lastReceivedSequenceNumber != UINT16_MAX) {
|
||||||
incrementNumOutOfOrderSends();
|
incrementNumOutOfOrderSends();
|
||||||
}
|
}
|
||||||
|
@ -98,7 +95,8 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||||
const SlaveSharedData& slaveSharedData, Node& sendingNode) {
|
const SlaveSharedData& slaveSharedData,
|
||||||
|
Node& sendingNode) {
|
||||||
// pull the trait version from the message
|
// pull the trait version from the message
|
||||||
AvatarTraits::TraitVersion packetTraitVersion;
|
AvatarTraits::TraitVersion packetTraitVersion;
|
||||||
message.readPrimitive(&packetTraitVersion);
|
message.readPrimitive(&packetTraitVersion);
|
||||||
|
@ -137,7 +135,7 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||||
AvatarTraits::TraitInstanceID instanceID = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
AvatarTraits::TraitInstanceID instanceID = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
if (message.getBytesLeftToRead() == 0) {
|
if (message.getBytesLeftToRead() == 0) {
|
||||||
qWarning () << "Received an instanced trait with no size from" << message.getSenderSockAddr();
|
qWarning() << "Received an instanced trait with no size from" << message.getSenderSockAddr();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +143,8 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||||
message.readPrimitive(&traitSize);
|
message.readPrimitive(&traitSize);
|
||||||
|
|
||||||
if (traitSize < -1 || traitSize > message.getBytesLeftToRead()) {
|
if (traitSize < -1 || traitSize > message.getBytesLeftToRead()) {
|
||||||
qWarning() << "Refusing to process instanced trait of size" << traitSize << "from" << message.getSenderSockAddr();
|
qWarning() << "Refusing to process instanced trait of size" << traitSize << "from"
|
||||||
|
<< message.getSenderSockAddr();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +170,8 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||||
message.seek(message.getPosition() + traitSize);
|
message.seek(message.getPosition() + traitSize);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Refusing to process traits packet with instanced trait of unprocessable type from" << message.getSenderSockAddr();
|
qWarning() << "Refusing to process traits packet with instanced trait of unprocessable type from"
|
||||||
|
<< message.getSenderSockAddr();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,42 +183,56 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::processBulkAvatarTraitsAckMessage(ReceivedMessage& message) {
|
void AvatarMixerClientData::processBulkAvatarTraitsAckMessage(ReceivedMessage& message) {
|
||||||
|
|
||||||
// Look up the avatar/trait data associated with this ack and update the 'last ack' list
|
// Look up the avatar/trait data associated with this ack and update the 'last ack' list
|
||||||
// with it.
|
// with it.
|
||||||
AvatarTraits::TraitMessageSequence seq;
|
AvatarTraits::TraitMessageSequence seq;
|
||||||
message.readPrimitive(&seq);
|
message.readPrimitive(&seq);
|
||||||
auto& sentAvatarTraitVersions = _pendingTraitVersions.find(seq);
|
auto sentAvatarTraitVersions = _pendingTraitVersions.find(seq);
|
||||||
if (sentAvatarTraitVersions != _pendingTraitVersions.end()) {
|
if (sentAvatarTraitVersions != _pendingTraitVersions.end()) {
|
||||||
// Note, this is not a simple move of the pending traits
|
// Note, this is not a simple move of the pending traits
|
||||||
// to the acked traits. Instead, it's a copy where existing
|
// to the acked traits. Instead, it's a copy where existing
|
||||||
// trait versions in the acked hash are retained for traits not
|
// trait versions in the acked hash are retained for traits not
|
||||||
// included in the pending hash
|
// included in the pending hash
|
||||||
for (auto& nodeTraitVersions : sentAvatarTraitVersions->second) {
|
for (auto& nodeTraitVersions : sentAvatarTraitVersions->second) {
|
||||||
auto& nodeId = nodeTraitVersions.first;
|
auto& nodeId = nodeTraitVersions.first;
|
||||||
auto& versions = nodeTraitVersions.second;
|
auto& versions = nodeTraitVersions.second;
|
||||||
auto simpleReceivedIt = versions.simpleCBegin();
|
auto simpleReceivedIt = versions.simpleCBegin();
|
||||||
while (simpleReceivedIt != versions.simpleCEnd()) {
|
while (simpleReceivedIt != versions.simpleCEnd()) {
|
||||||
auto traitType = static_cast<AvatarTraits::TraitType>(std::distance(versions.simpleCBegin(),
|
auto traitType = static_cast<AvatarTraits::TraitType>(std::distance(versions.simpleCBegin(), simpleReceivedIt));
|
||||||
simpleReceivedIt));
|
|
||||||
_ackedTraitVersions[nodeId][traitType] = *simpleReceivedIt;
|
_ackedTraitVersions[nodeId][traitType] = *simpleReceivedIt;
|
||||||
simpleReceivedIt++;
|
simpleReceivedIt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enumerate the sent instanced trait versions
|
||||||
|
auto instancedSentIt = versions.instancedCBegin();
|
||||||
|
while (instancedSentIt != versions.instancedCEnd()) {
|
||||||
|
auto traitType = instancedSentIt->traitType;
|
||||||
|
// get or create the sent trait versions for this trait type
|
||||||
|
auto& sentIDValuePairs = versions.getInstanceIDValuePairs(traitType);
|
||||||
|
|
||||||
|
// enumerate each sent instance
|
||||||
|
for (auto& sentInstance : instancedSentIt->instances) {
|
||||||
|
auto instanceID = sentInstance.id;
|
||||||
|
const auto sentVersion = sentInstance.value;
|
||||||
|
_ackedTraitVersions[nodeId].instanceInsert(traitType, instanceID, sentVersion);
|
||||||
|
}
|
||||||
|
instancedSentIt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_pendingTraitVersions.erase(sentAvatarTraitVersions);
|
_pendingTraitVersions.erase(sentAvatarTraitVersions);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// This can happen either the BulkAvatarTraits was sent with no simple traits,
|
// This can happen either the BulkAvatarTraits was sent with no simple traits,
|
||||||
// or if the avatar mixer restarts while there are pending
|
// or if the avatar mixer restarts while there are pending
|
||||||
// BulkAvatarTraits messages in-flight.
|
// BulkAvatarTraits messages in-flight.
|
||||||
if (seq > getTraitsMessageSequence()) {
|
if (seq > getTraitsMessageSequence()) {
|
||||||
qWarning() << "Received BulkAvatarTraitsAck with future seq (potential avatar mixer restart) " << seq << " from " << message.getSenderSockAddr();
|
qWarning() << "Received BulkAvatarTraitsAck with future seq (potential avatar mixer restart) " << seq << " from "
|
||||||
|
<< message.getSenderSockAddr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData,
|
||||||
void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedData &slaveSharedData, Node& sendingNode,
|
Node& sendingNode,
|
||||||
AvatarTraits::TraitVersion traitVersion) {
|
AvatarTraits::TraitVersion traitVersion) {
|
||||||
const auto& whitelist = slaveSharedData.skeletonURLWhitelist;
|
const auto& whitelist = slaveSharedData.skeletonURLWhitelist;
|
||||||
|
|
||||||
|
@ -244,8 +258,8 @@ void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedDa
|
||||||
// make sure we're not unecessarily overriding the default avatar with the default avatar
|
// make sure we're not unecessarily overriding the default avatar with the default avatar
|
||||||
if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData.skeletonReplacementURL) {
|
if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData.skeletonReplacementURL) {
|
||||||
// we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change
|
// we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change
|
||||||
qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL()
|
qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL() << "to replacement"
|
||||||
<< "to replacement" << slaveSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID();
|
<< slaveSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID();
|
||||||
_avatar->setSkeletonModelURL(slaveSharedData.skeletonReplacementURL);
|
_avatar->setSkeletonModelURL(slaveSharedData.skeletonReplacementURL);
|
||||||
|
|
||||||
auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true);
|
auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true);
|
||||||
|
@ -327,7 +341,7 @@ void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
|
||||||
_currentViewFrustums.clear();
|
_currentViewFrustums.clear();
|
||||||
|
|
||||||
auto sourceBuffer = reinterpret_cast<const unsigned char*>(message.constData());
|
auto sourceBuffer = reinterpret_cast<const unsigned char*>(message.constData());
|
||||||
|
|
||||||
uint8_t numFrustums = 0;
|
uint8_t numFrustums = 0;
|
||||||
memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums));
|
memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums));
|
||||||
sourceBuffer += sizeof(numFrustums);
|
sourceBuffer += sizeof(numFrustums);
|
||||||
|
@ -342,9 +356,7 @@ void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
|
||||||
|
|
||||||
bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) {
|
bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) {
|
||||||
return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums),
|
return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums),
|
||||||
[&](const ConicalViewFrustum& viewFrustum) {
|
[&](const ConicalViewFrustum& viewFrustum) { return viewFrustum.intersects(otherAvatarBox); });
|
||||||
return viewFrustum.intersects(otherAvatarBox);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
||||||
|
@ -355,14 +367,15 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
||||||
jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends;
|
jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends;
|
||||||
|
|
||||||
jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps();
|
jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps();
|
||||||
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar->getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar->getAverageBytesReceivedPerSecond() / (float)BYTES_PER_KILOBIT;
|
||||||
|
|
||||||
jsonObject["av_data_receive_rate"] = _avatar->getReceiveRate();
|
jsonObject["av_data_receive_rate"] = _avatar->getReceiveRate();
|
||||||
jsonObject["recent_other_av_in_view"] = _recentOtherAvatarsInView;
|
jsonObject["recent_other_av_in_view"] = _recentOtherAvatarsInView;
|
||||||
jsonObject["recent_other_av_out_of_view"] = _recentOtherAvatarsOutOfView;
|
jsonObject["recent_other_av_out_of_view"] = _recentOtherAvatarsOutOfView;
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const {
|
AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherAvatarTraitsSendPoint(
|
||||||
|
Node::LocalID otherAvatar) const {
|
||||||
auto it = _lastSentTraitsTimestamps.find(otherAvatar);
|
auto it = _lastSentTraitsTimestamps.find(otherAvatar);
|
||||||
|
|
||||||
if (it != _lastSentTraitsTimestamps.end()) {
|
if (it != _lastSentTraitsTimestamps.end()) {
|
||||||
|
|
|
@ -150,6 +150,7 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
||||||
|
|
||||||
// get or create the sent trait versions for this trait type
|
// get or create the sent trait versions for this trait type
|
||||||
auto& sentIDValuePairs = lastSentVersions.getInstanceIDValuePairs(traitType);
|
auto& sentIDValuePairs = lastSentVersions.getInstanceIDValuePairs(traitType);
|
||||||
|
auto& ackIDValuePairs = lastAckedVersions.getInstanceIDValuePairs(traitType);
|
||||||
|
|
||||||
// enumerate each received instance
|
// enumerate each received instance
|
||||||
for (auto& receivedInstance : instancedReceivedIt->instances) {
|
for (auto& receivedInstance : instancedReceivedIt->instances) {
|
||||||
|
@ -167,7 +168,16 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
||||||
{
|
{
|
||||||
return sentInstance.id == instanceID;
|
return sentInstance.id == instanceID;
|
||||||
});
|
});
|
||||||
|
// look for existing acked version for this instance
|
||||||
|
auto ackedInstanceIt = std::find_if(ackIDValuePairs.begin(), ackIDValuePairs.end(),
|
||||||
|
[instanceID](auto& ackInstance) { return ackInstance.id == instanceID; });
|
||||||
|
|
||||||
|
// if we have a sent version, then we must have an acked instance of the same trait with the same
|
||||||
|
// version to go on, otherwise we drop the received trait
|
||||||
|
if (sentInstanceIt != sentIDValuePairs.end() &&
|
||||||
|
(ackedInstanceIt == ackIDValuePairs.end() || sentInstanceIt->value != ackedInstanceIt->value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!isDeleted && (sentInstanceIt == sentIDValuePairs.end() || receivedVersion > sentInstanceIt->value)) {
|
if (!isDeleted && (sentInstanceIt == sentIDValuePairs.end() || receivedVersion > sentInstanceIt->value)) {
|
||||||
addTraitsNodeHeader(listeningNodeData, sendingNodeData, traitsPacketList, bytesWritten);
|
addTraitsNodeHeader(listeningNodeData, sendingNodeData, traitsPacketList, bytesWritten);
|
||||||
|
|
||||||
|
@ -179,6 +189,12 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
||||||
} else {
|
} else {
|
||||||
sentIDValuePairs.emplace_back(instanceID, receivedVersion);
|
sentIDValuePairs.emplace_back(instanceID, receivedVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& pendingTraitVersions =
|
||||||
|
listeningNodeData->getPendingTraitVersions(listeningNodeData->getTraitsMessageSequence(),
|
||||||
|
otherNodeLocalID);
|
||||||
|
pendingTraitVersions.instanceInsert(traitType, instanceID, receivedVersion);
|
||||||
|
|
||||||
} else if (isDeleted && sentInstanceIt != sentIDValuePairs.end() && absoluteReceivedVersion > sentInstanceIt->value) {
|
} else if (isDeleted && sentInstanceIt != sentIDValuePairs.end() && absoluteReceivedVersion > sentInstanceIt->value) {
|
||||||
addTraitsNodeHeader(listeningNodeData, sendingNodeData, traitsPacketList, bytesWritten);
|
addTraitsNodeHeader(listeningNodeData, sendingNodeData, traitsPacketList, bytesWritten);
|
||||||
|
|
||||||
|
@ -187,6 +203,12 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
||||||
|
|
||||||
// update the last sent version for this trait instance to the absolute value of the deleted version
|
// update the last sent version for this trait instance to the absolute value of the deleted version
|
||||||
sentInstanceIt->value = absoluteReceivedVersion;
|
sentInstanceIt->value = absoluteReceivedVersion;
|
||||||
|
|
||||||
|
auto& pendingTraitVersions =
|
||||||
|
listeningNodeData->getPendingTraitVersions(listeningNodeData->getTraitsMessageSequence(),
|
||||||
|
otherNodeLocalID);
|
||||||
|
pendingTraitVersions.instanceInsert(traitType, instanceID, absoluteReceivedVersion);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue