mirror of
https://github.com/lubosz/overte.git
synced 2025-04-13 18:52:13 +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 azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
|
||||
|
||||
float gain = 1.0f;
|
||||
float gain = masterListenerGain;
|
||||
if (!isSoloing) {
|
||||
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::AvatarIdentityRequest, this, "handleAvatarIdentityRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket");
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket");
|
||||
|
||||
packetReceiver.registerListenerForTypes({
|
||||
PacketType::ReplicatedAvatarIdentity,
|
||||
|
@ -767,6 +768,9 @@ void AvatarMixer::sendStatsPacket() {
|
|||
|
||||
float averageOverBudgetAvatars = averageNodes ? aggregateStats.overBudgetAvatars / averageNodes : 0.0f;
|
||||
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_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_6_jobElapsedTime"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.jobElapsedTime);
|
||||
|
||||
statsObject["slaves_aggregate"] = slavesAggregatObject;
|
||||
statsObject["slaves_aggregate (per frame)"] = slavesAggregatObject;
|
||||
|
||||
_handleViewFrustumPacketElapsedTime = 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
|
||||
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();
|
||||
|
||||
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
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,8 @@
|
|||
|
||||
#include "AvatarMixerSlave.h"
|
||||
|
||||
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) :
|
||||
NodeData(nodeID, nodeLocalID)
|
||||
{
|
||||
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) :
|
||||
NodeData(nodeID, nodeLocalID) {
|
||||
// in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID
|
||||
_avatar->setID(nodeID);
|
||||
}
|
||||
|
@ -68,6 +67,9 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
|
|||
case PacketType::SetAvatarTraits:
|
||||
processSetTraitsMessage(*packet, slaveSharedData, *node);
|
||||
break;
|
||||
case PacketType::BulkAvatarTraitsAck:
|
||||
processBulkAvatarTraitsAckMessage(*packet);
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -79,12 +81,11 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
|
|||
}
|
||||
|
||||
int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
||||
|
||||
// pull the sequence number from the data first
|
||||
uint16_t sequenceNumber;
|
||||
|
||||
message.readPrimitive(&sequenceNumber);
|
||||
|
||||
|
||||
if (sequenceNumber < _lastReceivedSequenceNumber && _lastReceivedSequenceNumber != UINT16_MAX) {
|
||||
incrementNumOutOfOrderSends();
|
||||
}
|
||||
|
@ -95,7 +96,8 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
|||
}
|
||||
|
||||
void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||
const SlaveSharedData& slaveSharedData, Node& sendingNode) {
|
||||
const SlaveSharedData& slaveSharedData,
|
||||
Node& sendingNode) {
|
||||
// pull the trait version from the message
|
||||
AvatarTraits::TraitVersion packetTraitVersion;
|
||||
message.readPrimitive(&packetTraitVersion);
|
||||
|
@ -134,7 +136,7 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
AvatarTraits::TraitInstanceID instanceID = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -142,7 +144,8 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
message.readPrimitive(&traitSize);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -169,7 +172,8 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
message.seek(message.getPosition() + traitSize);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
const auto& whitelist = slaveSharedData.skeletonURLWhitelist;
|
||||
|
||||
|
@ -282,14 +340,18 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(const QUuid& other) {
|
|||
|
||||
void AvatarMixerClientData::resetSentTraitData(Node::LocalID nodeLocalID) {
|
||||
_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) {
|
||||
_currentViewFrustums.clear();
|
||||
|
||||
auto sourceBuffer = reinterpret_cast<const unsigned char*>(message.constData());
|
||||
|
||||
|
||||
uint8_t numFrustums = 0;
|
||||
memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums));
|
||||
sourceBuffer += sizeof(numFrustums);
|
||||
|
@ -317,7 +379,8 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
|||
jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends;
|
||||
|
||||
jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps();
|
||||
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar->getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
||||
jsonObject[OUTBOUND_AVATAR_TRAITS_STATS_KEY] = getOutboundAvatarTraitsKbps();
|
||||
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar->getAverageBytesReceivedPerSecond() / (float)BYTES_PER_KILOBIT;
|
||||
|
||||
jsonObject["av_data_receive_rate"] = _avatar->getReceiveRate();
|
||||
jsonObject["recent_other_av_in_view"] = _recentOtherAvatarsInView;
|
||||
|
@ -338,5 +401,5 @@ void AvatarMixerClientData::cleanupKilledNode(const QUuid&, Node::LocalID nodeLo
|
|||
removeLastBroadcastSequenceNumber(nodeLocalID);
|
||||
removeLastBroadcastTime(nodeLocalID);
|
||||
_lastSentTraitsTimestamps.erase(nodeLocalID);
|
||||
_sentTraitVersions.erase(nodeLocalID);
|
||||
_perNodeSentTraitVersions.erase(nodeLocalID);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <shared/ConicalViewFrustum.h>
|
||||
|
||||
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";
|
||||
|
||||
struct SlaveSharedData;
|
||||
|
@ -42,6 +43,7 @@ public:
|
|||
AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID);
|
||||
virtual ~AvatarMixerClientData() {}
|
||||
using HRCTime = p_high_resolution_clock::time_point;
|
||||
using PerNodeTraitVersions = std::unordered_map<Node::LocalID, AvatarTraits::TraitVersions>;
|
||||
|
||||
int parseData(ReceivedMessage& message) override;
|
||||
AvatarData& getAvatar() { return *_avatar; }
|
||||
|
@ -85,10 +87,15 @@ public:
|
|||
void incrementNumFramesSinceFRDAdjustment() { ++_numFramesSinceAdjustment; }
|
||||
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
|
||||
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
|
||||
float getOutboundAvatarTraitsKbps() const
|
||||
{ return _avgOtherAvatarTraitsRate.getAverageSampleValuePerSecond() / BYTES_PER_KILOBIT; }
|
||||
|
||||
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||
|
||||
|
@ -124,6 +131,7 @@ public:
|
|||
int processPackets(const SlaveSharedData& slaveSharedData); // returns number of packets processed
|
||||
|
||||
void processSetTraitsMessage(ReceivedMessage& message, const SlaveSharedData& slaveSharedData, Node& sendingNode);
|
||||
void processBulkAvatarTraitsAckMessage(ReceivedMessage& message);
|
||||
void checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData, Node& sendingNode,
|
||||
AvatarTraits::TraitVersion traitVersion);
|
||||
|
||||
|
@ -138,7 +146,14 @@ public:
|
|||
void setLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar, TraitsCheckTimestamp 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);
|
||||
|
||||
|
@ -171,6 +186,7 @@ private:
|
|||
int _numOutOfOrderSends = 0;
|
||||
|
||||
SimpleMovingAverage _avgOtherAvatarDataRate;
|
||||
SimpleMovingAverage _avgOtherAvatarTraitsRate;
|
||||
std::vector<QUuid> _radiusIgnoredOthers;
|
||||
ConicalViewFrustums _currentViewFrustums;
|
||||
|
||||
|
@ -183,8 +199,27 @@ private:
|
|||
AvatarTraits::TraitVersions _lastReceivedTraitVersions;
|
||||
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, 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 };
|
||||
};
|
||||
|
|
|
@ -73,17 +73,39 @@ int AvatarMixerSlave::sendIdentityPacket(NLPacketList& packetList, const AvatarM
|
|||
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray();
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
|
||||
packetList.write(individualData);
|
||||
_stats.numIdentityPackets++;
|
||||
_stats.numIdentityPacketsSent++;
|
||||
_stats.numIdentityBytesSent += individualData.size();
|
||||
return individualData.size();
|
||||
} else {
|
||||
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,
|
||||
const AvatarMixerClientData* sendingNodeData,
|
||||
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();
|
||||
|
||||
// Perform a simple check with two server clock time points
|
||||
|
@ -96,29 +118,32 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
|||
if (timeOfLastTraitsChange > timeOfLastTraitsSent) {
|
||||
// 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();
|
||||
|
||||
// compare trait versions so we can see what exactly needs to go out
|
||||
auto& lastSentVersions = listeningNodeData->getLastSentTraitVersions(otherNodeLocalID);
|
||||
auto& lastAckedVersions = listeningNodeData->getLastAckedTraitVersions(otherNodeLocalID);
|
||||
const auto& lastReceivedVersions = sendingNodeData->getLastReceivedTraitVersions();
|
||||
|
||||
auto simpleReceivedIt = lastReceivedVersions.simpleCBegin();
|
||||
while (simpleReceivedIt != lastReceivedVersions.simpleCEnd()) {
|
||||
auto traitType = static_cast<AvatarTraits::TraitType>(std::distance(lastReceivedVersions.simpleCBegin(),
|
||||
simpleReceivedIt));
|
||||
|
||||
auto lastReceivedVersion = *simpleReceivedIt;
|
||||
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
|
||||
bytesWritten += sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion);
|
||||
|
||||
// update the last sent version
|
||||
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;
|
||||
|
@ -131,6 +156,7 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
|||
|
||||
// get or create the sent trait versions for this trait type
|
||||
auto& sentIDValuePairs = lastSentVersions.getInstanceIDValuePairs(traitType);
|
||||
auto& ackIDValuePairs = lastAckedVersions.getInstanceIDValuePairs(traitType);
|
||||
|
||||
// enumerate each received instance
|
||||
for (auto& receivedInstance : instancedReceivedIt->instances) {
|
||||
|
@ -148,8 +174,18 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
|||
{
|
||||
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)) {
|
||||
bytesWritten += addTraitsNodeHeader(listeningNodeData, sendingNodeData, traitsPacketList, bytesWritten);
|
||||
|
||||
// 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);
|
||||
|
@ -159,22 +195,35 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
|||
} else {
|
||||
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) {
|
||||
bytesWritten += addTraitsNodeHeader(listeningNodeData, sendingNodeData, traitsPacketList, bytesWritten);
|
||||
|
||||
// this instance version was deleted and we haven't sent the delete to this client yet
|
||||
bytesWritten += AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion);
|
||||
|
||||
// update the last sent version for this trait instance to the absolute value of the deleted version
|
||||
sentInstanceIt->value = absoluteReceivedVersion;
|
||||
|
||||
auto& pendingTraitVersions =
|
||||
listeningNodeData->getPendingTraitVersions(listeningNodeData->getTraitsMessageSequence(),
|
||||
otherNodeLocalID);
|
||||
pendingTraitVersions.instanceInsert(traitType, instanceID, absoluteReceivedVersion);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
++instancedReceivedIt;
|
||||
}
|
||||
|
||||
// write a null trait type to mark the end of trait data for this avatar
|
||||
bytesWritten += traitsPacketList.writePrimitive(AvatarTraits::NullTrait);
|
||||
|
||||
if (bytesWritten) {
|
||||
// write a null trait type to mark the end of trait data for this avatar
|
||||
bytesWritten += traitsPacketList.writePrimitive(AvatarTraits::NullTrait);
|
||||
}
|
||||
// since we send all traits for this other avatar, update the time of last traits sent
|
||||
// to match the time of last traits change
|
||||
listeningNodeData->setLastOtherAvatarTraitsSendPoint(otherNodeLocalID, timeOfLastTraitsChange);
|
||||
|
@ -191,7 +240,8 @@ int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const
|
|||
auto identityPacket = NLPacketList::create(PacketType::ReplicatedAvatarIdentity, QByteArray(), true, true);
|
||||
identityPacket->write(individualData);
|
||||
DependencyManager::get<NodeList>()->sendPacketList(std::move(identityPacket), destinationNode);
|
||||
_stats.numIdentityPackets++;
|
||||
_stats.numIdentityPacketsSent++;
|
||||
_stats.numIdentityBytesSent += individualData.size();
|
||||
return individualData.size();
|
||||
} else {
|
||||
return 0;
|
||||
|
@ -419,6 +469,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
int remainingAvatars = (int)sortedAvatars.size();
|
||||
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
|
||||
|
||||
auto avatarPacket = NLPacket::create(PacketType::BulkAvatarData);
|
||||
const int avatarPacketCapacity = avatarPacket->getPayloadCapacity();
|
||||
int avatarSpaceAvailable = avatarPacketCapacity;
|
||||
|
@ -539,17 +590,16 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
++numPacketsSent;
|
||||
}
|
||||
|
||||
_stats.numPacketsSent += numPacketsSent;
|
||||
_stats.numBytesSent += numAvatarDataBytes;
|
||||
|
||||
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
||||
nodeData->recordSentAvatarData(numAvatarDataBytes);
|
||||
_stats.numDataPacketsSent += numPacketsSent;
|
||||
_stats.numDataBytesSent += numAvatarDataBytes;
|
||||
|
||||
// close the current traits packet list
|
||||
traitsPacketList->closeCurrentPacket();
|
||||
|
||||
if (traitsPacketList->getNumPackets() >= 1) {
|
||||
// send the traits packet list
|
||||
_stats.numTraitsBytesSent += traitBytesSent;
|
||||
_stats.numTraitsPacketsSent += (int) traitsPacketList->getNumPackets();
|
||||
nodeList->sendPacketList(std::move(traitsPacketList), *destinationNode);
|
||||
}
|
||||
|
||||
|
@ -559,6 +609,10 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
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
|
||||
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
|
||||
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
|
||||
|
@ -685,8 +739,8 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin
|
|||
// close the current packet so that we're always sending something
|
||||
avatarPacketList->closeCurrentPacket(true);
|
||||
|
||||
_stats.numPacketsSent += (int)avatarPacketList->getNumPackets();
|
||||
_stats.numBytesSent += numAvatarDataBytes;
|
||||
_stats.numDataPacketsSent += (int)avatarPacketList->getNumPackets();
|
||||
_stats.numDataBytesSent += numAvatarDataBytes;
|
||||
|
||||
// send the replicated bulk avatar data
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
|
|
@ -24,9 +24,12 @@ public:
|
|||
|
||||
int nodesBroadcastedTo { 0 };
|
||||
int downstreamMixersBroadcastedTo { 0 };
|
||||
int numPacketsSent { 0 };
|
||||
int numBytesSent { 0 };
|
||||
int numIdentityPackets { 0 };
|
||||
int numDataBytesSent { 0 };
|
||||
int numTraitsBytesSent { 0 };
|
||||
int numIdentityBytesSent { 0 };
|
||||
int numDataPacketsSent { 0 };
|
||||
int numTraitsPacketsSent { 0 };
|
||||
int numIdentityPacketsSent { 0 };
|
||||
int numOthersIncluded { 0 };
|
||||
int overBudgetAvatars { 0 };
|
||||
|
||||
|
@ -45,9 +48,13 @@ public:
|
|||
// sending job stats
|
||||
nodesBroadcastedTo = 0;
|
||||
downstreamMixersBroadcastedTo = 0;
|
||||
numPacketsSent = 0;
|
||||
numBytesSent = 0;
|
||||
numIdentityPackets = 0;
|
||||
|
||||
numDataBytesSent = 0;
|
||||
numTraitsBytesSent = 0;
|
||||
numIdentityBytesSent = 0;
|
||||
numDataPacketsSent = 0;
|
||||
numTraitsPacketsSent = 0;
|
||||
numIdentityPacketsSent = 0;
|
||||
numOthersIncluded = 0;
|
||||
overBudgetAvatars = 0;
|
||||
|
||||
|
@ -65,9 +72,12 @@ public:
|
|||
|
||||
nodesBroadcastedTo += rhs.nodesBroadcastedTo;
|
||||
downstreamMixersBroadcastedTo += rhs.downstreamMixersBroadcastedTo;
|
||||
numPacketsSent += rhs.numPacketsSent;
|
||||
numBytesSent += rhs.numBytesSent;
|
||||
numIdentityPackets += rhs.numIdentityPackets;
|
||||
numDataBytesSent += rhs.numDataBytesSent;
|
||||
numTraitsBytesSent += rhs.numTraitsBytesSent;
|
||||
numIdentityBytesSent += rhs.numIdentityBytesSent;
|
||||
numDataPacketsSent += rhs.numDataPacketsSent;
|
||||
numTraitsPacketsSent += rhs.numTraitsPacketsSent;
|
||||
numIdentityPacketsSent += rhs.numIdentityPacketsSent;
|
||||
numOthersIncluded += rhs.numOthersIncluded;
|
||||
overBudgetAvatars += rhs.overBudgetAvatars;
|
||||
|
||||
|
@ -104,6 +114,11 @@ private:
|
|||
int sendIdentityPacket(NLPacketList& packet, 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,
|
||||
const AvatarMixerClientData* sendingNodeData,
|
||||
NLPacketList& traitsPacketList);
|
||||
|
|
|
@ -2060,7 +2060,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
properties["avatar_ping"] = avatarMixerNode ? avatarMixerNode->getPingMs() : -1;
|
||||
properties["asset_ping"] = assetServerNode ? assetServerNode->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();
|
||||
|
||||
|
|
|
@ -60,7 +60,6 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
|
|||
entityProperties.setParentID(myNodeID);
|
||||
entityProperties.setEntityHostType(entity::HostType::AVATAR);
|
||||
entityProperties.setOwningAvatarID(myNodeID);
|
||||
entityProperties.setSimulationOwner(myNodeID, AVATAR_ENTITY_SIMULATION_PRIORITY);
|
||||
entityProperties.markAllChanged();
|
||||
|
||||
EntityItemID id = EntityItemID(QUuid::createUuid());
|
||||
|
|
|
@ -27,6 +27,9 @@ Base3DOverlay::Base3DOverlay() :
|
|||
_drawInFront(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) :
|
||||
|
@ -41,6 +44,9 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
|||
_isVisibleInSecondaryCamera(base3DOverlay->_isVisibleInSecondaryCamera)
|
||||
{
|
||||
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) {
|
||||
|
@ -209,6 +215,7 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
transaction.updateItem(itemID);
|
||||
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);
|
||||
|
||||
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()); }
|
||||
void setOverlayID(OverlayID overlayID) override { setID(overlayID); }
|
||||
|
|
|
@ -394,10 +394,6 @@ void Avatar::updateAvatarEntities() {
|
|||
properties.setEntityHostType(entity::HostType::AVATAR);
|
||||
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) {
|
||||
properties.setParentID(getID());
|
||||
}
|
||||
|
|
|
@ -1987,9 +1987,9 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr
|
|||
qint64 bytesWritten = 0;
|
||||
|
||||
if (traitType == AvatarTraits::AvatarEntity) {
|
||||
packAvatarEntityTraitInstance(traitType, traitInstanceID, destination, traitVersion);
|
||||
bytesWritten += packAvatarEntityTraitInstance(traitType, traitInstanceID, destination, traitVersion);
|
||||
} else if (traitType == AvatarTraits::Grab) {
|
||||
packGrabTraitInstance(traitType, traitInstanceID, destination, traitVersion);
|
||||
bytesWritten += packGrabTraitInstance(traitType, traitInstanceID, destination, traitVersion);
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
|
|
|
@ -328,6 +328,19 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
|||
}
|
||||
|
||||
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()) {
|
||||
// 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 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,
|
||||
TraitVersion traitVersion = NULL_TRAIT_VERSION) {
|
||||
qint64 bytesWritten = 0;
|
||||
|
|
|
@ -157,7 +157,7 @@ public:
|
|||
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_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_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1);
|
||||
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)
|
||||
{ _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);
|
||||
|
||||
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.
|
||||
const uint8_t SCRIPT_GRAB_SIMULATION_PRIORITY = 128;
|
||||
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
|
||||
// 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;
|
||||
case PacketType::EntityQueryInitialResultsComplete:
|
||||
return static_cast<PacketVersion>(EntityVersion::ParticleSpin);
|
||||
case PacketType::BulkAvatarTraitsAck:
|
||||
case PacketType::BulkAvatarTraits:
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarTraitsAck);
|
||||
default:
|
||||
return 22;
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ public:
|
|||
EntityQueryInitialResultsComplete,
|
||||
BulkAvatarTraits,
|
||||
AudioSoloRequest,
|
||||
|
||||
BulkAvatarTraitsAck,
|
||||
NUM_PACKET_TYPE
|
||||
};
|
||||
|
||||
|
@ -310,7 +310,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
|||
FarGrabJointsRedux,
|
||||
JointTransScaled,
|
||||
GrabTraits,
|
||||
CollisionFlag
|
||||
CollisionFlag,
|
||||
AvatarTraitsAck
|
||||
};
|
||||
|
||||
enum class DomainConnectRequestVersion : PacketVersion {
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
using namespace udt;
|
||||
|
||||
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() {
|
||||
|
@ -27,21 +28,28 @@ MessageNumber PacketQueue::getNextMessageNumber() {
|
|||
|
||||
bool PacketQueue::isEmpty() const {
|
||||
LockGuard locker(_packetsLock);
|
||||
|
||||
// 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() {
|
||||
LockGuard locker(_packetsLock);
|
||||
|
||||
if (isEmpty()) {
|
||||
return PacketPointer();
|
||||
}
|
||||
|
||||
// Find next non empty channel
|
||||
if (_channels[nextIndex()]->empty()) {
|
||||
nextIndex();
|
||||
// handle the case where we are looking at the first channel and it is empty
|
||||
if (_currentChannel == _channels.begin() && (*_currentChannel)->empty()) {
|
||||
++_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());
|
||||
|
||||
// Take front packet
|
||||
|
@ -49,20 +57,28 @@ PacketQueue::PacketPointer PacketQueue::takePacket() {
|
|||
channel->pop_front();
|
||||
|
||||
// Remove now empty channel (Don't remove the main channel)
|
||||
if (channel->empty() && _currentIndex != 0) {
|
||||
channel->swap(*_channels.back());
|
||||
_channels.pop_back();
|
||||
--_currentIndex;
|
||||
if (channel->empty() && _currentChannel != _channels.begin()) {
|
||||
// erase the current channel and slide the iterator to the next channel
|
||||
_currentChannel = _channels.erase(_currentChannel);
|
||||
} 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;
|
||||
}
|
||||
|
||||
unsigned int PacketQueue::nextIndex() {
|
||||
_currentIndex = (_currentIndex + 1) % _channels.size();
|
||||
return _currentIndex;
|
||||
}
|
||||
|
||||
void PacketQueue::queuePacket(PacketPointer packet) {
|
||||
LockGuard locker(_packetsLock);
|
||||
_channels.front()->push_back(std::move(packet));
|
||||
|
|
|
@ -30,8 +30,9 @@ class PacketQueue {
|
|||
using LockGuard = std::lock_guard<Mutex>;
|
||||
using PacketPointer = std::unique_ptr<Packet>;
|
||||
using PacketListPointer = std::unique_ptr<PacketList>;
|
||||
using Channel = std::unique_ptr<std::list<PacketPointer>>;
|
||||
using Channels = std::vector<Channel>;
|
||||
using RawChannel = std::list<PacketPointer>;
|
||||
using Channel = std::unique_ptr<RawChannel>;
|
||||
using Channels = std::list<Channel>;
|
||||
|
||||
public:
|
||||
PacketQueue(MessageNumber messageNumber = 0);
|
||||
|
@ -47,16 +48,17 @@ public:
|
|||
|
||||
private:
|
||||
MessageNumber getNextMessageNumber();
|
||||
unsigned int nextIndex();
|
||||
|
||||
|
||||
MessageNumber _currentMessageNumber { 0 };
|
||||
|
||||
mutable Mutex _packetsLock; // Protects the packets to be sent.
|
||||
Channels _channels; // One channel per packet list + Main channel
|
||||
unsigned int _currentIndex { 0 };
|
||||
|
||||
Channels::iterator _currentChannel;
|
||||
unsigned int _channelsVisitedCount { 0 };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // hifi_PacketQueue_h
|
||||
#endif // hifi_PacketQueue_h
|
||||
|
|
|
@ -61,6 +61,9 @@ private:
|
|||
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,
|
||||
MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK) {
|
||||
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);
|
||||
|
||||
// cap our maximum estimated timeout to the already unreasonable 5 seconds
|
||||
const auto MAXIMUM_ESTIMATED_TIMEOUT = std::chrono::seconds(5);
|
||||
|
||||
if (estimatedTimeout > MAXIMUM_ESTIMATED_TIMEOUT) {
|
||||
estimatedTimeout = MAXIMUM_ESTIMATED_TIMEOUT;
|
||||
}
|
||||
// Clamp timeout beween 10 ms and 5 s
|
||||
estimatedTimeout = std::min(MAXIMUM_ESTIMATED_TIMEOUT, std::max(MINIMUM_ESTIMATED_TIMEOUT, estimatedTimeout));
|
||||
|
||||
// use our condition_variable_any to wait
|
||||
auto cvStatus = _emptyCondition.wait_for(locker, estimatedTimeout);
|
||||
|
|
|
@ -140,6 +140,9 @@ private:
|
|||
std::condition_variable_any _emptyCondition;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
||||
|
||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||
// NOTE: this method is only ever called when the entity simulation is locally owned
|
||||
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
|
||||
// WIP: print info whenever _bidPriority mismatches what is known to the entity
|
||||
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int numSteps = simulationStep - _lastStep;
|
||||
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||
|
||||
if (_numInactiveUpdates > 0) {
|
||||
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
||||
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
|
||||
// clear local ownership (stop sending updates) and let the server clear itself
|
||||
_entity->clearSimulationOwnership();
|
||||
|
@ -451,8 +448,13 @@ void EntityMotionState::updateSendVelocities() {
|
|||
if (!_body->isKinematicObject()) {
|
||||
clearObjectVelocities();
|
||||
}
|
||||
// we pretend we sent the inactive update for this object
|
||||
_numInactiveUpdates = 1;
|
||||
if (_entity->getEntityHostType() == entity::HostType::AVATAR) {
|
||||
// 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 {
|
||||
glm::vec3 gravity = _entity->getGravity();
|
||||
|
||||
|
|
Loading…
Reference in a new issue