mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 09:08:37 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into 20550-installOnWindows
This commit is contained in:
commit
9a8b33eb96
23 changed files with 324 additions and 96 deletions
|
@ -504,7 +504,7 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre
|
||||||
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
||||||
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
|
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
|
||||||
|
|
||||||
float gain = 1.0f;
|
float gain = masterListenerGain;
|
||||||
if (!isSoloing) {
|
if (!isSoloing) {
|
||||||
gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho);
|
gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||||
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
||||||
packetReceiver.registerListener(PacketType::AvatarIdentityRequest, this, "handleAvatarIdentityRequestPacket");
|
packetReceiver.registerListener(PacketType::AvatarIdentityRequest, this, "handleAvatarIdentityRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket");
|
packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket");
|
||||||
|
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket");
|
||||||
|
|
||||||
packetReceiver.registerListenerForTypes({
|
packetReceiver.registerListenerForTypes({
|
||||||
PacketType::ReplicatedAvatarIdentity,
|
PacketType::ReplicatedAvatarIdentity,
|
||||||
|
@ -767,6 +768,9 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
|
|
||||||
float averageOverBudgetAvatars = averageNodes ? aggregateStats.overBudgetAvatars / averageNodes : 0.0f;
|
float averageOverBudgetAvatars = averageNodes ? aggregateStats.overBudgetAvatars / averageNodes : 0.0f;
|
||||||
slavesAggregatObject["sent_3_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars);
|
slavesAggregatObject["sent_3_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars);
|
||||||
|
slavesAggregatObject["sent_4_averageDataBytes"] = TIGHT_LOOP_STAT(aggregateStats.numDataBytesSent);
|
||||||
|
slavesAggregatObject["sent_5_averageTraitsBytes"] = TIGHT_LOOP_STAT(aggregateStats.numTraitsBytesSent);
|
||||||
|
slavesAggregatObject["sent_6_averageIdentityBytes"] = TIGHT_LOOP_STAT(aggregateStats.numIdentityBytesSent);
|
||||||
|
|
||||||
slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime);
|
slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime);
|
||||||
slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime);
|
slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime);
|
||||||
|
@ -775,7 +779,7 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
slavesAggregatObject["timing_5_packetSending"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.packetSendingElapsedTime);
|
slavesAggregatObject["timing_5_packetSending"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.packetSendingElapsedTime);
|
||||||
slavesAggregatObject["timing_6_jobElapsedTime"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.jobElapsedTime);
|
slavesAggregatObject["timing_6_jobElapsedTime"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.jobElapsedTime);
|
||||||
|
|
||||||
statsObject["slaves_aggregate"] = slavesAggregatObject;
|
statsObject["slaves_aggregate (per frame)"] = slavesAggregatObject;
|
||||||
|
|
||||||
_handleViewFrustumPacketElapsedTime = 0;
|
_handleViewFrustumPacketElapsedTime = 0;
|
||||||
_handleAvatarIdentityPacketElapsedTime = 0;
|
_handleAvatarIdentityPacketElapsedTime = 0;
|
||||||
|
@ -800,7 +804,8 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
// add the key to ask the domain-server for a username replacement, if it has it
|
// add the key to ask the domain-server for a username replacement, if it has it
|
||||||
avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
|
avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
|
||||||
|
|
||||||
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundKbps();
|
float outboundAvatarDataKbps = node->getOutboundKbps();
|
||||||
|
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = outboundAvatarDataKbps;
|
||||||
avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundKbps();
|
avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundKbps();
|
||||||
|
|
||||||
AvatarMixerClientData* clientData = static_cast<AvatarMixerClientData*>(node->getLinkedData());
|
AvatarMixerClientData* clientData = static_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||||
|
@ -811,7 +816,7 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
|
|
||||||
// add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only
|
// add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only
|
||||||
avatarStats["delta_full_vs_avatar_data_kbps"] =
|
avatarStats["delta_full_vs_avatar_data_kbps"] =
|
||||||
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble();
|
(double)outboundAvatarDataKbps - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,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);
|
||||||
}
|
}
|
||||||
|
@ -68,6 +67,9 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
|
||||||
case PacketType::SetAvatarTraits:
|
case PacketType::SetAvatarTraits:
|
||||||
processSetTraitsMessage(*packet, slaveSharedData, *node);
|
processSetTraitsMessage(*packet, slaveSharedData, *node);
|
||||||
break;
|
break;
|
||||||
|
case PacketType::BulkAvatarTraitsAck:
|
||||||
|
processBulkAvatarTraitsAckMessage(*packet);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -79,7 +81,6 @@ 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;
|
||||||
|
|
||||||
|
@ -95,7 +96,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);
|
||||||
|
@ -142,7 +144,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +172,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,7 +184,61 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedData &slaveSharedData, Node& sendingNode,
|
void AvatarMixerClientData::processBulkAvatarTraitsAckMessage(ReceivedMessage& message) {
|
||||||
|
// Avatar Traits flow control marks each outgoing avatar traits packet with a
|
||||||
|
// sequence number. The mixer caches the traits sent in the traits packet.
|
||||||
|
// Until an ack with the sequence number comes back, all updates to _traits
|
||||||
|
// in that packet_ are ignored. Updates to traits not in that packet will
|
||||||
|
// be sent.
|
||||||
|
|
||||||
|
// Look up the avatar/trait data associated with this ack and update the 'last ack' list
|
||||||
|
// with it.
|
||||||
|
AvatarTraits::TraitMessageSequence seq;
|
||||||
|
message.readPrimitive(&seq);
|
||||||
|
auto sentAvatarTraitVersions = _perNodePendingTraitVersions.find(seq);
|
||||||
|
if (sentAvatarTraitVersions != _perNodePendingTraitVersions.end()) {
|
||||||
|
for (auto& perNodeTraitVersions : sentAvatarTraitVersions->second) {
|
||||||
|
auto& nodeId = perNodeTraitVersions.first;
|
||||||
|
auto& traitVersions = perNodeTraitVersions.second;
|
||||||
|
// For each trait that was sent in the traits packet,
|
||||||
|
// update the 'acked' trait version. Traits not
|
||||||
|
// sent in the traits packet keep their version.
|
||||||
|
|
||||||
|
// process simple traits
|
||||||
|
auto simpleReceivedIt = traitVersions.simpleCBegin();
|
||||||
|
while (simpleReceivedIt != traitVersions.simpleCEnd()) {
|
||||||
|
auto traitType = static_cast<AvatarTraits::TraitType>(std::distance(traitVersions.simpleCBegin(), simpleReceivedIt));
|
||||||
|
_perNodeAckedTraitVersions[nodeId][traitType] = *simpleReceivedIt;
|
||||||
|
simpleReceivedIt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process instanced traits
|
||||||
|
auto instancedSentIt = traitVersions.instancedCBegin();
|
||||||
|
while (instancedSentIt != traitVersions.instancedCEnd()) {
|
||||||
|
auto traitType = instancedSentIt->traitType;
|
||||||
|
|
||||||
|
for (auto& sentInstance : instancedSentIt->instances) {
|
||||||
|
auto instanceID = sentInstance.id;
|
||||||
|
const auto sentVersion = sentInstance.value;
|
||||||
|
_perNodeAckedTraitVersions[nodeId].instanceInsert(traitType, instanceID, sentVersion);
|
||||||
|
}
|
||||||
|
instancedSentIt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_perNodePendingTraitVersions.erase(sentAvatarTraitVersions);
|
||||||
|
} else {
|
||||||
|
// This can happen either the BulkAvatarTraits was sent with no simple traits,
|
||||||
|
// or if the avatar mixer restarts while there are pending
|
||||||
|
// BulkAvatarTraits messages in-flight.
|
||||||
|
if (seq > getTraitsMessageSequence()) {
|
||||||
|
qWarning() << "Received BulkAvatarTraitsAck with future seq (potential avatar mixer restart) " << seq << " from "
|
||||||
|
<< message.getSenderSockAddr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData,
|
||||||
|
Node& sendingNode,
|
||||||
AvatarTraits::TraitVersion traitVersion) {
|
AvatarTraits::TraitVersion traitVersion) {
|
||||||
const auto& whitelist = slaveSharedData.skeletonURLWhitelist;
|
const auto& whitelist = slaveSharedData.skeletonURLWhitelist;
|
||||||
|
|
||||||
|
@ -282,7 +340,11 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(const QUuid& other) {
|
||||||
|
|
||||||
void AvatarMixerClientData::resetSentTraitData(Node::LocalID nodeLocalID) {
|
void AvatarMixerClientData::resetSentTraitData(Node::LocalID nodeLocalID) {
|
||||||
_lastSentTraitsTimestamps[nodeLocalID] = TraitsCheckTimestamp();
|
_lastSentTraitsTimestamps[nodeLocalID] = TraitsCheckTimestamp();
|
||||||
_sentTraitVersions[nodeLocalID].reset();
|
_perNodeSentTraitVersions[nodeLocalID].reset();
|
||||||
|
_perNodeAckedTraitVersions[nodeLocalID].reset();
|
||||||
|
for (auto && pendingTraitVersions : _perNodePendingTraitVersions) {
|
||||||
|
pendingTraitVersions.second[nodeLocalID].reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
|
void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
|
||||||
|
@ -317,6 +379,7 @@ 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[OUTBOUND_AVATAR_TRAITS_STATS_KEY] = getOutboundAvatarTraitsKbps();
|
||||||
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();
|
||||||
|
@ -338,5 +401,5 @@ void AvatarMixerClientData::cleanupKilledNode(const QUuid&, Node::LocalID nodeLo
|
||||||
removeLastBroadcastSequenceNumber(nodeLocalID);
|
removeLastBroadcastSequenceNumber(nodeLocalID);
|
||||||
removeLastBroadcastTime(nodeLocalID);
|
removeLastBroadcastTime(nodeLocalID);
|
||||||
_lastSentTraitsTimestamps.erase(nodeLocalID);
|
_lastSentTraitsTimestamps.erase(nodeLocalID);
|
||||||
_sentTraitVersions.erase(nodeLocalID);
|
_perNodeSentTraitVersions.erase(nodeLocalID);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <shared/ConicalViewFrustum.h>
|
#include <shared/ConicalViewFrustum.h>
|
||||||
|
|
||||||
const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps";
|
const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps";
|
||||||
|
const QString OUTBOUND_AVATAR_TRAITS_STATS_KEY = "outbound_av_traits_kbps";
|
||||||
const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
|
const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
|
||||||
|
|
||||||
struct SlaveSharedData;
|
struct SlaveSharedData;
|
||||||
|
@ -42,6 +43,7 @@ public:
|
||||||
AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID);
|
AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID);
|
||||||
virtual ~AvatarMixerClientData() {}
|
virtual ~AvatarMixerClientData() {}
|
||||||
using HRCTime = p_high_resolution_clock::time_point;
|
using HRCTime = p_high_resolution_clock::time_point;
|
||||||
|
using PerNodeTraitVersions = std::unordered_map<Node::LocalID, AvatarTraits::TraitVersions>;
|
||||||
|
|
||||||
int parseData(ReceivedMessage& message) override;
|
int parseData(ReceivedMessage& message) override;
|
||||||
AvatarData& getAvatar() { return *_avatar; }
|
AvatarData& getAvatar() { return *_avatar; }
|
||||||
|
@ -85,10 +87,15 @@ public:
|
||||||
void incrementNumFramesSinceFRDAdjustment() { ++_numFramesSinceAdjustment; }
|
void incrementNumFramesSinceFRDAdjustment() { ++_numFramesSinceAdjustment; }
|
||||||
void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; }
|
void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; }
|
||||||
|
|
||||||
void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); }
|
void recordSentAvatarData(int numDataBytes, int numTraitsBytes = 0) {
|
||||||
|
_avgOtherAvatarDataRate.updateAverage(numDataBytes);
|
||||||
|
_avgOtherAvatarTraitsRate.updateAverage(numTraitsBytes);
|
||||||
|
}
|
||||||
|
|
||||||
float getOutboundAvatarDataKbps() const
|
float getOutboundAvatarDataKbps() const
|
||||||
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
|
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
|
||||||
|
float getOutboundAvatarTraitsKbps() const
|
||||||
|
{ return _avgOtherAvatarTraitsRate.getAverageSampleValuePerSecond() / BYTES_PER_KILOBIT; }
|
||||||
|
|
||||||
void loadJSONStats(QJsonObject& jsonObject) const;
|
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||||
|
|
||||||
|
@ -124,6 +131,7 @@ public:
|
||||||
int processPackets(const SlaveSharedData& slaveSharedData); // returns number of packets processed
|
int processPackets(const SlaveSharedData& slaveSharedData); // returns number of packets processed
|
||||||
|
|
||||||
void processSetTraitsMessage(ReceivedMessage& message, const SlaveSharedData& slaveSharedData, Node& sendingNode);
|
void processSetTraitsMessage(ReceivedMessage& message, const SlaveSharedData& slaveSharedData, Node& sendingNode);
|
||||||
|
void processBulkAvatarTraitsAckMessage(ReceivedMessage& message);
|
||||||
void checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData, Node& sendingNode,
|
void checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData, Node& sendingNode,
|
||||||
AvatarTraits::TraitVersion traitVersion);
|
AvatarTraits::TraitVersion traitVersion);
|
||||||
|
|
||||||
|
@ -138,7 +146,14 @@ public:
|
||||||
void setLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar, TraitsCheckTimestamp sendPoint)
|
void setLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar, TraitsCheckTimestamp sendPoint)
|
||||||
{ _lastSentTraitsTimestamps[otherAvatar] = sendPoint; }
|
{ _lastSentTraitsTimestamps[otherAvatar] = sendPoint; }
|
||||||
|
|
||||||
AvatarTraits::TraitVersions& getLastSentTraitVersions(Node::LocalID otherAvatar) { return _sentTraitVersions[otherAvatar]; }
|
AvatarTraits::TraitMessageSequence getTraitsMessageSequence() const { return _currentTraitsMessageSequence; }
|
||||||
|
AvatarTraits::TraitMessageSequence nextTraitsMessageSequence() { return ++_currentTraitsMessageSequence; }
|
||||||
|
AvatarTraits::TraitVersions& getPendingTraitVersions(AvatarTraits::TraitMessageSequence seq, Node::LocalID otherId) {
|
||||||
|
return _perNodePendingTraitVersions[seq][otherId];
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarTraits::TraitVersions& getLastSentTraitVersions(Node::LocalID otherAvatar) { return _perNodeSentTraitVersions[otherAvatar]; }
|
||||||
|
AvatarTraits::TraitVersions& getLastAckedTraitVersions(Node::LocalID otherAvatar) { return _perNodeAckedTraitVersions[otherAvatar]; }
|
||||||
|
|
||||||
void resetSentTraitData(Node::LocalID nodeID);
|
void resetSentTraitData(Node::LocalID nodeID);
|
||||||
|
|
||||||
|
@ -171,6 +186,7 @@ private:
|
||||||
int _numOutOfOrderSends = 0;
|
int _numOutOfOrderSends = 0;
|
||||||
|
|
||||||
SimpleMovingAverage _avgOtherAvatarDataRate;
|
SimpleMovingAverage _avgOtherAvatarDataRate;
|
||||||
|
SimpleMovingAverage _avgOtherAvatarTraitsRate;
|
||||||
std::vector<QUuid> _radiusIgnoredOthers;
|
std::vector<QUuid> _radiusIgnoredOthers;
|
||||||
ConicalViewFrustums _currentViewFrustums;
|
ConicalViewFrustums _currentViewFrustums;
|
||||||
|
|
||||||
|
@ -183,8 +199,27 @@ private:
|
||||||
AvatarTraits::TraitVersions _lastReceivedTraitVersions;
|
AvatarTraits::TraitVersions _lastReceivedTraitVersions;
|
||||||
TraitsCheckTimestamp _lastReceivedTraitsChange;
|
TraitsCheckTimestamp _lastReceivedTraitsChange;
|
||||||
|
|
||||||
|
AvatarTraits::TraitMessageSequence _currentTraitsMessageSequence{ 0 };
|
||||||
|
|
||||||
|
// Cache of trait versions sent in a given packet (indexed by sequence number)
|
||||||
|
// When an ack is received, the sequence number in the ack is used to look up
|
||||||
|
// the sent trait versions and they are copied to _perNodeAckedTraitVersions.
|
||||||
|
// We remember the data in _perNodePendingTraitVersions instead of requiring
|
||||||
|
// the client to return all of the versions for each trait it received in a given packet,
|
||||||
|
// reducing the size of the ack packet.
|
||||||
|
std::unordered_map<AvatarTraits::TraitMessageSequence, PerNodeTraitVersions> _perNodePendingTraitVersions;
|
||||||
|
|
||||||
|
// Versions of traits that have been acked, which will be compared to incoming
|
||||||
|
// trait updates. Incoming updates going to a given node will be ignored if
|
||||||
|
// the ack for the previous packet (containing those versions) has not been
|
||||||
|
// received.
|
||||||
|
PerNodeTraitVersions _perNodeAckedTraitVersions;
|
||||||
|
|
||||||
std::unordered_map<Node::LocalID, TraitsCheckTimestamp> _lastSentTraitsTimestamps;
|
std::unordered_map<Node::LocalID, TraitsCheckTimestamp> _lastSentTraitsTimestamps;
|
||||||
std::unordered_map<Node::LocalID, AvatarTraits::TraitVersions> _sentTraitVersions;
|
|
||||||
|
// cache of traits sent to a node which are compared to incoming traits to
|
||||||
|
// prevent sending traits that have already been sent.
|
||||||
|
PerNodeTraitVersions _perNodeSentTraitVersions;
|
||||||
|
|
||||||
std::atomic_bool _isIgnoreRadiusEnabled { false };
|
std::atomic_bool _isIgnoreRadiusEnabled { false };
|
||||||
};
|
};
|
||||||
|
|
|
@ -73,17 +73,39 @@ int AvatarMixerSlave::sendIdentityPacket(NLPacketList& packetList, const AvatarM
|
||||||
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray();
|
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray();
|
||||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
|
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
|
||||||
packetList.write(individualData);
|
packetList.write(individualData);
|
||||||
_stats.numIdentityPackets++;
|
_stats.numIdentityPacketsSent++;
|
||||||
|
_stats.numIdentityBytesSent += individualData.size();
|
||||||
return individualData.size();
|
return individualData.size();
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qint64 AvatarMixerSlave::addTraitsNodeHeader(AvatarMixerClientData* listeningNodeData,
|
||||||
|
const AvatarMixerClientData* sendingNodeData,
|
||||||
|
NLPacketList& traitsPacketList,
|
||||||
|
qint64 bytesWritten) {
|
||||||
|
if (bytesWritten == 0) {
|
||||||
|
if (traitsPacketList.getNumPackets() == 0) {
|
||||||
|
// This is the beginning of the traits packet, write out the sequence number.
|
||||||
|
bytesWritten += traitsPacketList.writePrimitive(listeningNodeData->nextTraitsMessageSequence());
|
||||||
|
}
|
||||||
|
// This is the beginning of the traits for a node, write out the node id
|
||||||
|
bytesWritten += traitsPacketList.write(sendingNodeData->getNodeID().toRfc4122());
|
||||||
|
}
|
||||||
|
return bytesWritten;
|
||||||
|
}
|
||||||
|
|
||||||
qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData,
|
qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData,
|
||||||
const AvatarMixerClientData* sendingNodeData,
|
const AvatarMixerClientData* sendingNodeData,
|
||||||
NLPacketList& traitsPacketList) {
|
NLPacketList& traitsPacketList) {
|
||||||
|
|
||||||
|
// Avatar Traits flow control marks each outgoing avatar traits packet with a
|
||||||
|
// sequence number. The mixer caches the traits sent in the traits packet.
|
||||||
|
// Until an ack with the sequence number comes back, all updates to _traits
|
||||||
|
// in that packet_ are ignored. Updates to traits not in that packet will
|
||||||
|
// be sent.
|
||||||
|
|
||||||
auto otherNodeLocalID = sendingNodeData->getNodeLocalID();
|
auto otherNodeLocalID = sendingNodeData->getNodeLocalID();
|
||||||
|
|
||||||
// Perform a simple check with two server clock time points
|
// Perform a simple check with two server clock time points
|
||||||
|
@ -96,29 +118,32 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
||||||
if (timeOfLastTraitsChange > timeOfLastTraitsSent) {
|
if (timeOfLastTraitsChange > timeOfLastTraitsSent) {
|
||||||
// there is definitely new traits data to send
|
// there is definitely new traits data to send
|
||||||
|
|
||||||
// add the avatar ID to mark the beginning of traits for this avatar
|
|
||||||
bytesWritten += traitsPacketList.write(sendingNodeData->getNodeID().toRfc4122());
|
|
||||||
|
|
||||||
auto sendingAvatar = sendingNodeData->getAvatarSharedPointer();
|
auto sendingAvatar = sendingNodeData->getAvatarSharedPointer();
|
||||||
|
|
||||||
// compare trait versions so we can see what exactly needs to go out
|
// compare trait versions so we can see what exactly needs to go out
|
||||||
auto& lastSentVersions = listeningNodeData->getLastSentTraitVersions(otherNodeLocalID);
|
auto& lastSentVersions = listeningNodeData->getLastSentTraitVersions(otherNodeLocalID);
|
||||||
|
auto& lastAckedVersions = listeningNodeData->getLastAckedTraitVersions(otherNodeLocalID);
|
||||||
const auto& lastReceivedVersions = sendingNodeData->getLastReceivedTraitVersions();
|
const auto& lastReceivedVersions = sendingNodeData->getLastReceivedTraitVersions();
|
||||||
|
|
||||||
auto simpleReceivedIt = lastReceivedVersions.simpleCBegin();
|
auto simpleReceivedIt = lastReceivedVersions.simpleCBegin();
|
||||||
while (simpleReceivedIt != lastReceivedVersions.simpleCEnd()) {
|
while (simpleReceivedIt != lastReceivedVersions.simpleCEnd()) {
|
||||||
auto traitType = static_cast<AvatarTraits::TraitType>(std::distance(lastReceivedVersions.simpleCBegin(),
|
auto traitType = static_cast<AvatarTraits::TraitType>(std::distance(lastReceivedVersions.simpleCBegin(),
|
||||||
simpleReceivedIt));
|
simpleReceivedIt));
|
||||||
|
|
||||||
auto lastReceivedVersion = *simpleReceivedIt;
|
auto lastReceivedVersion = *simpleReceivedIt;
|
||||||
auto& lastSentVersionRef = lastSentVersions[traitType];
|
auto& lastSentVersionRef = lastSentVersions[traitType];
|
||||||
|
auto& lastAckedVersionRef = lastAckedVersions[traitType];
|
||||||
|
|
||||||
if (lastReceivedVersions[traitType] > lastSentVersionRef) {
|
// hold sending more traits until we've been acked that the last one we sent was received
|
||||||
|
if (lastSentVersionRef == lastAckedVersionRef && lastReceivedVersions[traitType] > lastSentVersionRef) {
|
||||||
|
bytesWritten += addTraitsNodeHeader(listeningNodeData, sendingNodeData, traitsPacketList, bytesWritten);
|
||||||
// there is an update to this trait, add it to the traits packet
|
// there is an update to this trait, add it to the traits packet
|
||||||
bytesWritten += sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion);
|
bytesWritten += sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion);
|
||||||
|
|
||||||
// update the last sent version
|
// update the last sent version
|
||||||
lastSentVersionRef = lastReceivedVersion;
|
lastSentVersionRef = lastReceivedVersion;
|
||||||
|
// Remember which versions we sent in this particular packet
|
||||||
|
// so we can verify when it's acked.
|
||||||
|
auto& pendingTraitVersions = listeningNodeData->getPendingTraitVersions(listeningNodeData->getTraitsMessageSequence(), otherNodeLocalID);
|
||||||
|
pendingTraitVersions[traitType] = lastReceivedVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
++simpleReceivedIt;
|
++simpleReceivedIt;
|
||||||
|
@ -131,6 +156,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) {
|
||||||
|
@ -148,8 +174,18 @@ 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)) {
|
||||||
|
bytesWritten += addTraitsNodeHeader(listeningNodeData, sendingNodeData, traitsPacketList, bytesWritten);
|
||||||
|
|
||||||
// this instance version exists and has never been sent or is newer so we need to send it
|
// this instance version exists and has never been sent or is newer so we need to send it
|
||||||
bytesWritten += sendingAvatar->packTraitInstance(traitType, instanceID, traitsPacketList, receivedVersion);
|
bytesWritten += sendingAvatar->packTraitInstance(traitType, instanceID, traitsPacketList, receivedVersion);
|
||||||
|
@ -159,22 +195,35 @@ 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) {
|
||||||
|
bytesWritten += addTraitsNodeHeader(listeningNodeData, sendingNodeData, traitsPacketList, bytesWritten);
|
||||||
|
|
||||||
// this instance version was deleted and we haven't sent the delete to this client yet
|
// this instance version was deleted and we haven't sent the delete to this client yet
|
||||||
bytesWritten += AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion);
|
bytesWritten += AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion);
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++instancedReceivedIt;
|
++instancedReceivedIt;
|
||||||
}
|
}
|
||||||
|
if (bytesWritten) {
|
||||||
// write a null trait type to mark the end of trait data for this avatar
|
// write a null trait type to mark the end of trait data for this avatar
|
||||||
bytesWritten += traitsPacketList.writePrimitive(AvatarTraits::NullTrait);
|
bytesWritten += traitsPacketList.writePrimitive(AvatarTraits::NullTrait);
|
||||||
|
}
|
||||||
// since we send all traits for this other avatar, update the time of last traits sent
|
// since we send all traits for this other avatar, update the time of last traits sent
|
||||||
// to match the time of last traits change
|
// to match the time of last traits change
|
||||||
listeningNodeData->setLastOtherAvatarTraitsSendPoint(otherNodeLocalID, timeOfLastTraitsChange);
|
listeningNodeData->setLastOtherAvatarTraitsSendPoint(otherNodeLocalID, timeOfLastTraitsChange);
|
||||||
|
@ -191,7 +240,8 @@ int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const
|
||||||
auto identityPacket = NLPacketList::create(PacketType::ReplicatedAvatarIdentity, QByteArray(), true, true);
|
auto identityPacket = NLPacketList::create(PacketType::ReplicatedAvatarIdentity, QByteArray(), true, true);
|
||||||
identityPacket->write(individualData);
|
identityPacket->write(individualData);
|
||||||
DependencyManager::get<NodeList>()->sendPacketList(std::move(identityPacket), destinationNode);
|
DependencyManager::get<NodeList>()->sendPacketList(std::move(identityPacket), destinationNode);
|
||||||
_stats.numIdentityPackets++;
|
_stats.numIdentityPacketsSent++;
|
||||||
|
_stats.numIdentityBytesSent += individualData.size();
|
||||||
return individualData.size();
|
return individualData.size();
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -419,6 +469,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
|
|
||||||
int remainingAvatars = (int)sortedAvatars.size();
|
int remainingAvatars = (int)sortedAvatars.size();
|
||||||
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
|
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
|
||||||
|
|
||||||
auto avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
|
auto avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
|
||||||
const int avatarPacketCapacity = avatarPacket->getPayloadCapacity();
|
const int avatarPacketCapacity = avatarPacket->getPayloadCapacity();
|
||||||
int avatarSpaceAvailable = avatarPacketCapacity;
|
int avatarSpaceAvailable = avatarPacketCapacity;
|
||||||
|
@ -539,17 +590,16 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
++numPacketsSent;
|
++numPacketsSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stats.numPacketsSent += numPacketsSent;
|
_stats.numDataPacketsSent += numPacketsSent;
|
||||||
_stats.numBytesSent += numAvatarDataBytes;
|
_stats.numDataBytesSent += numAvatarDataBytes;
|
||||||
|
|
||||||
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
|
||||||
nodeData->recordSentAvatarData(numAvatarDataBytes);
|
|
||||||
|
|
||||||
// close the current traits packet list
|
// close the current traits packet list
|
||||||
traitsPacketList->closeCurrentPacket();
|
traitsPacketList->closeCurrentPacket();
|
||||||
|
|
||||||
if (traitsPacketList->getNumPackets() >= 1) {
|
if (traitsPacketList->getNumPackets() >= 1) {
|
||||||
// send the traits packet list
|
// send the traits packet list
|
||||||
|
_stats.numTraitsBytesSent += traitBytesSent;
|
||||||
|
_stats.numTraitsPacketsSent += (int) traitsPacketList->getNumPackets();
|
||||||
nodeList->sendPacketList(std::move(traitsPacketList), *destinationNode);
|
nodeList->sendPacketList(std::move(traitsPacketList), *destinationNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,6 +609,10 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
||||||
nodeList->sendPacketList(std::move(identityPacketList), *destinationNode);
|
nodeList->sendPacketList(std::move(identityPacketList), *destinationNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
||||||
|
nodeData->recordSentAvatarData(numAvatarDataBytes, traitBytesSent);
|
||||||
|
|
||||||
|
|
||||||
// record the number of avatars held back this frame
|
// record the number of avatars held back this frame
|
||||||
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
|
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
|
||||||
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
|
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
|
||||||
|
@ -685,8 +739,8 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin
|
||||||
// close the current packet so that we're always sending something
|
// close the current packet so that we're always sending something
|
||||||
avatarPacketList->closeCurrentPacket(true);
|
avatarPacketList->closeCurrentPacket(true);
|
||||||
|
|
||||||
_stats.numPacketsSent += (int)avatarPacketList->getNumPackets();
|
_stats.numDataPacketsSent += (int)avatarPacketList->getNumPackets();
|
||||||
_stats.numBytesSent += numAvatarDataBytes;
|
_stats.numDataBytesSent += numAvatarDataBytes;
|
||||||
|
|
||||||
// send the replicated bulk avatar data
|
// send the replicated bulk avatar data
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
|
@ -24,9 +24,12 @@ public:
|
||||||
|
|
||||||
int nodesBroadcastedTo { 0 };
|
int nodesBroadcastedTo { 0 };
|
||||||
int downstreamMixersBroadcastedTo { 0 };
|
int downstreamMixersBroadcastedTo { 0 };
|
||||||
int numPacketsSent { 0 };
|
int numDataBytesSent { 0 };
|
||||||
int numBytesSent { 0 };
|
int numTraitsBytesSent { 0 };
|
||||||
int numIdentityPackets { 0 };
|
int numIdentityBytesSent { 0 };
|
||||||
|
int numDataPacketsSent { 0 };
|
||||||
|
int numTraitsPacketsSent { 0 };
|
||||||
|
int numIdentityPacketsSent { 0 };
|
||||||
int numOthersIncluded { 0 };
|
int numOthersIncluded { 0 };
|
||||||
int overBudgetAvatars { 0 };
|
int overBudgetAvatars { 0 };
|
||||||
|
|
||||||
|
@ -45,9 +48,13 @@ public:
|
||||||
// sending job stats
|
// sending job stats
|
||||||
nodesBroadcastedTo = 0;
|
nodesBroadcastedTo = 0;
|
||||||
downstreamMixersBroadcastedTo = 0;
|
downstreamMixersBroadcastedTo = 0;
|
||||||
numPacketsSent = 0;
|
|
||||||
numBytesSent = 0;
|
numDataBytesSent = 0;
|
||||||
numIdentityPackets = 0;
|
numTraitsBytesSent = 0;
|
||||||
|
numIdentityBytesSent = 0;
|
||||||
|
numDataPacketsSent = 0;
|
||||||
|
numTraitsPacketsSent = 0;
|
||||||
|
numIdentityPacketsSent = 0;
|
||||||
numOthersIncluded = 0;
|
numOthersIncluded = 0;
|
||||||
overBudgetAvatars = 0;
|
overBudgetAvatars = 0;
|
||||||
|
|
||||||
|
@ -65,9 +72,12 @@ public:
|
||||||
|
|
||||||
nodesBroadcastedTo += rhs.nodesBroadcastedTo;
|
nodesBroadcastedTo += rhs.nodesBroadcastedTo;
|
||||||
downstreamMixersBroadcastedTo += rhs.downstreamMixersBroadcastedTo;
|
downstreamMixersBroadcastedTo += rhs.downstreamMixersBroadcastedTo;
|
||||||
numPacketsSent += rhs.numPacketsSent;
|
numDataBytesSent += rhs.numDataBytesSent;
|
||||||
numBytesSent += rhs.numBytesSent;
|
numTraitsBytesSent += rhs.numTraitsBytesSent;
|
||||||
numIdentityPackets += rhs.numIdentityPackets;
|
numIdentityBytesSent += rhs.numIdentityBytesSent;
|
||||||
|
numDataPacketsSent += rhs.numDataPacketsSent;
|
||||||
|
numTraitsPacketsSent += rhs.numTraitsPacketsSent;
|
||||||
|
numIdentityPacketsSent += rhs.numIdentityPacketsSent;
|
||||||
numOthersIncluded += rhs.numOthersIncluded;
|
numOthersIncluded += rhs.numOthersIncluded;
|
||||||
overBudgetAvatars += rhs.overBudgetAvatars;
|
overBudgetAvatars += rhs.overBudgetAvatars;
|
||||||
|
|
||||||
|
@ -104,6 +114,11 @@ private:
|
||||||
int sendIdentityPacket(NLPacketList& packet, const AvatarMixerClientData* nodeData, const Node& destinationNode);
|
int sendIdentityPacket(NLPacketList& packet, const AvatarMixerClientData* nodeData, const Node& destinationNode);
|
||||||
int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode);
|
int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode);
|
||||||
|
|
||||||
|
qint64 addTraitsNodeHeader(AvatarMixerClientData* listeningNodeData,
|
||||||
|
const AvatarMixerClientData* sendingNodeData,
|
||||||
|
NLPacketList& traitsPacketList,
|
||||||
|
qint64 bytesWritten);
|
||||||
|
|
||||||
qint64 addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData,
|
qint64 addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData,
|
||||||
const AvatarMixerClientData* sendingNodeData,
|
const AvatarMixerClientData* sendingNodeData,
|
||||||
NLPacketList& traitsPacketList);
|
NLPacketList& traitsPacketList);
|
||||||
|
|
|
@ -2060,7 +2060,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
properties["avatar_ping"] = avatarMixerNode ? avatarMixerNode->getPingMs() : -1;
|
properties["avatar_ping"] = avatarMixerNode ? avatarMixerNode->getPingMs() : -1;
|
||||||
properties["asset_ping"] = assetServerNode ? assetServerNode->getPingMs() : -1;
|
properties["asset_ping"] = assetServerNode ? assetServerNode->getPingMs() : -1;
|
||||||
properties["messages_ping"] = messagesMixerNode ? messagesMixerNode->getPingMs() : -1;
|
properties["messages_ping"] = messagesMixerNode ? messagesMixerNode->getPingMs() : -1;
|
||||||
properties["atp_in_kbps"] = messagesMixerNode ? assetServerNode->getInboundKbps() : 0.0f;
|
properties["atp_in_kbps"] = assetServerNode ? assetServerNode->getInboundKbps() : 0.0f;
|
||||||
|
|
||||||
auto loadingRequests = ResourceCache::getLoadingRequests();
|
auto loadingRequests = ResourceCache::getLoadingRequests();
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,6 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
|
||||||
entityProperties.setParentID(myNodeID);
|
entityProperties.setParentID(myNodeID);
|
||||||
entityProperties.setEntityHostType(entity::HostType::AVATAR);
|
entityProperties.setEntityHostType(entity::HostType::AVATAR);
|
||||||
entityProperties.setOwningAvatarID(myNodeID);
|
entityProperties.setOwningAvatarID(myNodeID);
|
||||||
entityProperties.setSimulationOwner(myNodeID, AVATAR_ENTITY_SIMULATION_PRIORITY);
|
|
||||||
entityProperties.markAllChanged();
|
entityProperties.markAllChanged();
|
||||||
|
|
||||||
EntityItemID id = EntityItemID(QUuid::createUuid());
|
EntityItemID id = EntityItemID(QUuid::createUuid());
|
||||||
|
|
|
@ -27,6 +27,9 @@ Base3DOverlay::Base3DOverlay() :
|
||||||
_drawInFront(false),
|
_drawInFront(false),
|
||||||
_drawHUDLayer(false)
|
_drawHUDLayer(false)
|
||||||
{
|
{
|
||||||
|
// HACK: queryAACube stuff not actually relevant for 3DOverlays, and by setting _queryAACubeSet true here
|
||||||
|
// we can avoid incorrect evaluation for sending updates for entities with 3DOverlays children.
|
||||||
|
_queryAACubeSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
||||||
|
@ -41,6 +44,9 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
||||||
_isVisibleInSecondaryCamera(base3DOverlay->_isVisibleInSecondaryCamera)
|
_isVisibleInSecondaryCamera(base3DOverlay->_isVisibleInSecondaryCamera)
|
||||||
{
|
{
|
||||||
setTransform(base3DOverlay->getTransform());
|
setTransform(base3DOverlay->getTransform());
|
||||||
|
// HACK: queryAACube stuff not actually relevant for 3DOverlays, and by setting _queryAACubeSet true here
|
||||||
|
// we can avoid incorrect evaluation for sending updates for entities with 3DOverlays children.
|
||||||
|
_queryAACubeSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties, bool scalesWithParent) {
|
QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties, bool scalesWithParent) {
|
||||||
|
@ -209,6 +215,7 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
||||||
transaction.updateItem(itemID);
|
transaction.updateItem(itemID);
|
||||||
scene->enqueueTransaction(transaction);
|
scene->enqueueTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
_queryAACubeSet = true; // HACK: just in case some SpatiallyNestable code accidentally set it false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ public:
|
||||||
Base3DOverlay(const Base3DOverlay* base3DOverlay);
|
Base3DOverlay(const Base3DOverlay* base3DOverlay);
|
||||||
|
|
||||||
void setVisible(bool visible) override;
|
void setVisible(bool visible) override;
|
||||||
|
bool queryAACubeNeedsUpdate() const override { return false; } // HACK: queryAACube not relevant for Overlays
|
||||||
|
|
||||||
virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); }
|
virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); }
|
||||||
void setOverlayID(OverlayID overlayID) override { setID(overlayID); }
|
void setOverlayID(OverlayID overlayID) override { setID(overlayID); }
|
||||||
|
|
|
@ -394,10 +394,6 @@ void Avatar::updateAvatarEntities() {
|
||||||
properties.setEntityHostType(entity::HostType::AVATAR);
|
properties.setEntityHostType(entity::HostType::AVATAR);
|
||||||
properties.setOwningAvatarID(getID());
|
properties.setOwningAvatarID(getID());
|
||||||
|
|
||||||
// there's no entity-server to tell us we're the simulation owner, so always set the
|
|
||||||
// simulationOwner to the owningAvatarID and a high priority.
|
|
||||||
properties.setSimulationOwner(getID(), AVATAR_ENTITY_SIMULATION_PRIORITY);
|
|
||||||
|
|
||||||
if (properties.getParentID() == AVATAR_SELF_ID) {
|
if (properties.getParentID() == AVATAR_SELF_ID) {
|
||||||
properties.setParentID(getID());
|
properties.setParentID(getID());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1987,9 +1987,9 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr
|
||||||
qint64 bytesWritten = 0;
|
qint64 bytesWritten = 0;
|
||||||
|
|
||||||
if (traitType == AvatarTraits::AvatarEntity) {
|
if (traitType == AvatarTraits::AvatarEntity) {
|
||||||
packAvatarEntityTraitInstance(traitType, traitInstanceID, destination, traitVersion);
|
bytesWritten += packAvatarEntityTraitInstance(traitType, traitInstanceID, destination, traitVersion);
|
||||||
} else if (traitType == AvatarTraits::Grab) {
|
} else if (traitType == AvatarTraits::Grab) {
|
||||||
packGrabTraitInstance(traitType, traitInstanceID, destination, traitVersion);
|
bytesWritten += packGrabTraitInstance(traitType, traitInstanceID, destination, traitVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytesWritten;
|
return bytesWritten;
|
||||||
|
|
|
@ -328,6 +328,19 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||||
|
AvatarTraits::TraitMessageSequence seq;
|
||||||
|
|
||||||
|
message->readPrimitive(&seq);
|
||||||
|
|
||||||
|
auto traitsAckPacket = NLPacket::create(PacketType::BulkAvatarTraitsAck, sizeof(AvatarTraits::TraitMessageSequence), true);
|
||||||
|
traitsAckPacket->writePrimitive(seq);
|
||||||
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
||||||
|
if (!avatarMixer.isNull()) {
|
||||||
|
// we have a mixer to send to, acknowledge that we received these
|
||||||
|
// traits.
|
||||||
|
nodeList->sendPacket(std::move(traitsAckPacket), *avatarMixer);
|
||||||
|
}
|
||||||
|
|
||||||
while (message->getBytesLeftToRead()) {
|
while (message->getBytesLeftToRead()) {
|
||||||
// read the avatar ID to figure out which avatar this is for
|
// read the avatar ID to figure out which avatar this is for
|
||||||
|
|
|
@ -42,6 +42,10 @@ namespace AvatarTraits {
|
||||||
const TraitWireSize DELETED_TRAIT_SIZE = -1;
|
const TraitWireSize DELETED_TRAIT_SIZE = -1;
|
||||||
const TraitWireSize MAXIMUM_TRAIT_SIZE = INT16_MAX;
|
const TraitWireSize MAXIMUM_TRAIT_SIZE = INT16_MAX;
|
||||||
|
|
||||||
|
using TraitMessageSequence = int64_t;
|
||||||
|
const TraitMessageSequence FIRST_TRAIT_SEQUENCE = 0;
|
||||||
|
const TraitMessageSequence MAX_TRAIT_SEQUENCE = INT64_MAX;
|
||||||
|
|
||||||
inline qint64 packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination,
|
inline qint64 packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination,
|
||||||
TraitVersion traitVersion = NULL_TRAIT_VERSION) {
|
TraitVersion traitVersion = NULL_TRAIT_VERSION) {
|
||||||
qint64 bytesWritten = 0;
|
qint64 bytesWritten = 0;
|
||||||
|
|
|
@ -157,7 +157,7 @@ public:
|
||||||
DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64, UNKNOWN_CREATED_TIME);
|
DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64, UNKNOWN_CREATED_TIME);
|
||||||
DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY);
|
DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY);
|
||||||
DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST_TYPE, EntityHostType, entityHostType, entity::HostType, entity::HostType::DOMAIN);
|
DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST_TYPE, EntityHostType, entityHostType, entity::HostType, entity::HostType::DOMAIN);
|
||||||
DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID);
|
DEFINE_PROPERTY_REF_WITH_SETTER(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID);
|
||||||
DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID);
|
DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID);
|
||||||
DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1);
|
DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1);
|
||||||
DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
|
DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
|
||||||
|
@ -499,6 +499,16 @@ void EntityPropertyInfoFromScriptValue(const QScriptValue& object, EntityPropert
|
||||||
inline void EntityItemProperties::setPosition(const glm::vec3& value)
|
inline void EntityItemProperties::setPosition(const glm::vec3& value)
|
||||||
{ _position = glm::clamp(value, (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); _positionChanged = true; }
|
{ _position = glm::clamp(value, (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); _positionChanged = true; }
|
||||||
|
|
||||||
|
inline void EntityItemProperties::setOwningAvatarID(const QUuid& id) {
|
||||||
|
_owningAvatarID = id;
|
||||||
|
if (!_owningAvatarID.isNull()) {
|
||||||
|
// for AvatarEntities there's no entity-server to tell us we're the simulation owner,
|
||||||
|
// so always set the simulationOwner to the owningAvatarID and a high priority.
|
||||||
|
setSimulationOwner(_owningAvatarID, AVATAR_ENTITY_SIMULATION_PRIORITY);
|
||||||
|
}
|
||||||
|
_owningAvatarIDChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f);
|
QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f);
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
||||||
|
|
|
@ -96,7 +96,7 @@ const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
||||||
// When poking objects with scripts an observer will bid at SCRIPT_EDIT priority.
|
// When poking objects with scripts an observer will bid at SCRIPT_EDIT priority.
|
||||||
const uint8_t SCRIPT_GRAB_SIMULATION_PRIORITY = 128;
|
const uint8_t SCRIPT_GRAB_SIMULATION_PRIORITY = 128;
|
||||||
const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1;
|
const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1;
|
||||||
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY + 1;
|
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = 255;
|
||||||
|
|
||||||
// PERSONAL priority (needs a better name) is the level at which a simulation observer owns its own avatar
|
// PERSONAL priority (needs a better name) is the level at which a simulation observer owns its own avatar
|
||||||
// which really just means: things that collide with it will be bid at a priority level one lower
|
// which really just means: things that collide with it will be bid at a priority level one lower
|
||||||
|
|
|
@ -97,6 +97,9 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
return 22;
|
return 22;
|
||||||
case PacketType::EntityQueryInitialResultsComplete:
|
case PacketType::EntityQueryInitialResultsComplete:
|
||||||
return static_cast<PacketVersion>(EntityVersion::ParticleSpin);
|
return static_cast<PacketVersion>(EntityVersion::ParticleSpin);
|
||||||
|
case PacketType::BulkAvatarTraitsAck:
|
||||||
|
case PacketType::BulkAvatarTraits:
|
||||||
|
return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarTraitsAck);
|
||||||
default:
|
default:
|
||||||
return 22;
|
return 22;
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ public:
|
||||||
EntityQueryInitialResultsComplete,
|
EntityQueryInitialResultsComplete,
|
||||||
BulkAvatarTraits,
|
BulkAvatarTraits,
|
||||||
AudioSoloRequest,
|
AudioSoloRequest,
|
||||||
|
BulkAvatarTraitsAck,
|
||||||
NUM_PACKET_TYPE
|
NUM_PACKET_TYPE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -310,7 +310,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
||||||
FarGrabJointsRedux,
|
FarGrabJointsRedux,
|
||||||
JointTransScaled,
|
JointTransScaled,
|
||||||
GrabTraits,
|
GrabTraits,
|
||||||
CollisionFlag
|
CollisionFlag,
|
||||||
|
AvatarTraitsAck
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DomainConnectRequestVersion : PacketVersion {
|
enum class DomainConnectRequestVersion : PacketVersion {
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
using namespace udt;
|
using namespace udt;
|
||||||
|
|
||||||
PacketQueue::PacketQueue(MessageNumber messageNumber) : _currentMessageNumber(messageNumber) {
|
PacketQueue::PacketQueue(MessageNumber messageNumber) : _currentMessageNumber(messageNumber) {
|
||||||
_channels.emplace_back(new std::list<PacketPointer>());
|
_channels.emplace_front(new std::list<PacketPointer>());
|
||||||
|
_currentChannel = _channels.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageNumber PacketQueue::getNextMessageNumber() {
|
MessageNumber PacketQueue::getNextMessageNumber() {
|
||||||
|
@ -27,21 +28,28 @@ MessageNumber PacketQueue::getNextMessageNumber() {
|
||||||
|
|
||||||
bool PacketQueue::isEmpty() const {
|
bool PacketQueue::isEmpty() const {
|
||||||
LockGuard locker(_packetsLock);
|
LockGuard locker(_packetsLock);
|
||||||
|
|
||||||
// Only the main channel and it is empty
|
// Only the main channel and it is empty
|
||||||
return (_channels.size() == 1) && _channels.front()->empty();
|
return _channels.size() == 1 && _channels.front()->empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketQueue::PacketPointer PacketQueue::takePacket() {
|
PacketQueue::PacketPointer PacketQueue::takePacket() {
|
||||||
LockGuard locker(_packetsLock);
|
LockGuard locker(_packetsLock);
|
||||||
|
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return PacketPointer();
|
return PacketPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find next non empty channel
|
// handle the case where we are looking at the first channel and it is empty
|
||||||
if (_channels[nextIndex()]->empty()) {
|
if (_currentChannel == _channels.begin() && (*_currentChannel)->empty()) {
|
||||||
nextIndex();
|
++_currentChannel;
|
||||||
}
|
}
|
||||||
auto& channel = _channels[_currentIndex];
|
|
||||||
|
// at this point the current channel should always not be at the end and should also not be empty
|
||||||
|
Q_ASSERT(_currentChannel != _channels.end());
|
||||||
|
|
||||||
|
auto& channel = *_currentChannel;
|
||||||
|
|
||||||
Q_ASSERT(!channel->empty());
|
Q_ASSERT(!channel->empty());
|
||||||
|
|
||||||
// Take front packet
|
// Take front packet
|
||||||
|
@ -49,20 +57,28 @@ PacketQueue::PacketPointer PacketQueue::takePacket() {
|
||||||
channel->pop_front();
|
channel->pop_front();
|
||||||
|
|
||||||
// Remove now empty channel (Don't remove the main channel)
|
// Remove now empty channel (Don't remove the main channel)
|
||||||
if (channel->empty() && _currentIndex != 0) {
|
if (channel->empty() && _currentChannel != _channels.begin()) {
|
||||||
channel->swap(*_channels.back());
|
// erase the current channel and slide the iterator to the next channel
|
||||||
_channels.pop_back();
|
_currentChannel = _channels.erase(_currentChannel);
|
||||||
--_currentIndex;
|
} else {
|
||||||
|
++_currentChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// push forward our number of channels taken from
|
||||||
|
++_channelsVisitedCount;
|
||||||
|
|
||||||
|
// check if we need to restart back at the front channel (main)
|
||||||
|
// to respect our capped number of channels considered concurrently
|
||||||
|
static const int MAX_CHANNELS_SENT_CONCURRENTLY = 16;
|
||||||
|
|
||||||
|
if (_currentChannel == _channels.end() || _channelsVisitedCount >= MAX_CHANNELS_SENT_CONCURRENTLY) {
|
||||||
|
_channelsVisitedCount = 0;
|
||||||
|
_currentChannel = _channels.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int PacketQueue::nextIndex() {
|
|
||||||
_currentIndex = (_currentIndex + 1) % _channels.size();
|
|
||||||
return _currentIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketQueue::queuePacket(PacketPointer packet) {
|
void PacketQueue::queuePacket(PacketPointer packet) {
|
||||||
LockGuard locker(_packetsLock);
|
LockGuard locker(_packetsLock);
|
||||||
_channels.front()->push_back(std::move(packet));
|
_channels.front()->push_back(std::move(packet));
|
||||||
|
|
|
@ -30,8 +30,9 @@ class PacketQueue {
|
||||||
using LockGuard = std::lock_guard<Mutex>;
|
using LockGuard = std::lock_guard<Mutex>;
|
||||||
using PacketPointer = std::unique_ptr<Packet>;
|
using PacketPointer = std::unique_ptr<Packet>;
|
||||||
using PacketListPointer = std::unique_ptr<PacketList>;
|
using PacketListPointer = std::unique_ptr<PacketList>;
|
||||||
using Channel = std::unique_ptr<std::list<PacketPointer>>;
|
using RawChannel = std::list<PacketPointer>;
|
||||||
using Channels = std::vector<Channel>;
|
using Channel = std::unique_ptr<RawChannel>;
|
||||||
|
using Channels = std::list<Channel>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PacketQueue(MessageNumber messageNumber = 0);
|
PacketQueue(MessageNumber messageNumber = 0);
|
||||||
|
@ -47,13 +48,14 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MessageNumber getNextMessageNumber();
|
MessageNumber getNextMessageNumber();
|
||||||
unsigned int nextIndex();
|
|
||||||
|
|
||||||
MessageNumber _currentMessageNumber { 0 };
|
MessageNumber _currentMessageNumber { 0 };
|
||||||
|
|
||||||
mutable Mutex _packetsLock; // Protects the packets to be sent.
|
mutable Mutex _packetsLock; // Protects the packets to be sent.
|
||||||
Channels _channels; // One channel per packet list + Main channel
|
Channels _channels; // One channel per packet list + Main channel
|
||||||
unsigned int _currentIndex { 0 };
|
|
||||||
|
Channels::iterator _currentChannel;
|
||||||
|
unsigned int _channelsVisitedCount { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,9 @@ private:
|
||||||
Mutex2& _mutex2;
|
Mutex2& _mutex2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const microseconds SendQueue::MAXIMUM_ESTIMATED_TIMEOUT = seconds(5);
|
||||||
|
const microseconds SendQueue::MINIMUM_ESTIMATED_TIMEOUT = milliseconds(10);
|
||||||
|
|
||||||
std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destination, SequenceNumber currentSequenceNumber,
|
std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destination, SequenceNumber currentSequenceNumber,
|
||||||
MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK) {
|
MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK) {
|
||||||
Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*");
|
Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*");
|
||||||
|
@ -507,12 +510,8 @@ bool SendQueue::isInactive(bool attemptedToSendPacket) {
|
||||||
|
|
||||||
auto estimatedTimeout = std::chrono::microseconds(_estimatedTimeout);
|
auto estimatedTimeout = std::chrono::microseconds(_estimatedTimeout);
|
||||||
|
|
||||||
// cap our maximum estimated timeout to the already unreasonable 5 seconds
|
// Clamp timeout beween 10 ms and 5 s
|
||||||
const auto MAXIMUM_ESTIMATED_TIMEOUT = std::chrono::seconds(5);
|
estimatedTimeout = std::min(MAXIMUM_ESTIMATED_TIMEOUT, std::max(MINIMUM_ESTIMATED_TIMEOUT, estimatedTimeout));
|
||||||
|
|
||||||
if (estimatedTimeout > MAXIMUM_ESTIMATED_TIMEOUT) {
|
|
||||||
estimatedTimeout = MAXIMUM_ESTIMATED_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use our condition_variable_any to wait
|
// use our condition_variable_any to wait
|
||||||
auto cvStatus = _emptyCondition.wait_for(locker, estimatedTimeout);
|
auto cvStatus = _emptyCondition.wait_for(locker, estimatedTimeout);
|
||||||
|
|
|
@ -140,6 +140,9 @@ private:
|
||||||
std::condition_variable_any _emptyCondition;
|
std::condition_variable_any _emptyCondition;
|
||||||
|
|
||||||
std::chrono::high_resolution_clock::time_point _lastPacketSentAt;
|
std::chrono::high_resolution_clock::time_point _lastPacketSentAt;
|
||||||
|
|
||||||
|
static const std::chrono::microseconds MAXIMUM_ESTIMATED_TIMEOUT;
|
||||||
|
static const std::chrono::microseconds MINIMUM_ESTIMATED_TIMEOUT;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,6 +306,8 @@ const btCollisionShape* EntityMotionState::computeNewShape() {
|
||||||
return getShapeManager()->getShape(shapeInfo);
|
return getShapeManager()->getShape(shapeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
||||||
|
|
||||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
// NOTE: this method is only ever called when the entity simulation is locally owned
|
// NOTE: this method is only ever called when the entity simulation is locally owned
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
||||||
|
@ -315,15 +317,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
// TODO: need to be able to detect when logic dictates we *decrease* priority
|
// TODO: need to be able to detect when logic dictates we *decrease* priority
|
||||||
// WIP: print info whenever _bidPriority mismatches what is known to the entity
|
// WIP: print info whenever _bidPriority mismatches what is known to the entity
|
||||||
|
|
||||||
if (_entity->dynamicDataNeedsTransmit()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numSteps = simulationStep - _lastStep;
|
int numSteps = simulationStep - _lastStep;
|
||||||
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||||
|
|
||||||
if (_numInactiveUpdates > 0) {
|
if (_numInactiveUpdates > 0) {
|
||||||
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
|
||||||
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
|
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
|
||||||
// clear local ownership (stop sending updates) and let the server clear itself
|
// clear local ownership (stop sending updates) and let the server clear itself
|
||||||
_entity->clearSimulationOwnership();
|
_entity->clearSimulationOwnership();
|
||||||
|
@ -451,8 +448,13 @@ void EntityMotionState::updateSendVelocities() {
|
||||||
if (!_body->isKinematicObject()) {
|
if (!_body->isKinematicObject()) {
|
||||||
clearObjectVelocities();
|
clearObjectVelocities();
|
||||||
}
|
}
|
||||||
// we pretend we sent the inactive update for this object
|
if (_entity->getEntityHostType() == entity::HostType::AVATAR) {
|
||||||
_numInactiveUpdates = 1;
|
// AvatarEntities only ever need to send one update (their updates are sent over a lossless protocol)
|
||||||
|
// so we set the count to the max to prevent resends
|
||||||
|
_numInactiveUpdates = MAX_NUM_INACTIVE_UPDATES;
|
||||||
|
} else {
|
||||||
|
++_numInactiveUpdates;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
glm::vec3 gravity = _entity->getGravity();
|
glm::vec3 gravity = _entity->getGravity();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue