Merge pull request #6393 from hyperlogic/tony/avatar-mixer-identity-fixes

Fix for missing avatars on entry
This commit is contained in:
Philip Rosedale 2015-11-14 20:23:34 -08:00
commit 8e6c860a24
3 changed files with 76 additions and 64 deletions

View file

@ -63,7 +63,9 @@ AvatarMixer::~AvatarMixer() {
_broadcastThread.wait();
}
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f;
// An 80% chance of sending a identity packet within a 5 second interval.
// assuming 60 htz update rate.
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f;
// NOTE: some additional optimizations to consider.
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
@ -243,6 +245,47 @@ void AvatarMixer::broadcastAvatarData() {
return;
}
// if an avatar has just connected make sure we send out the mesh and billboard
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets()
|| !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID());
// we will also force a send of billboard or identity packet
// if either has changed in the last frame
if (otherNodeData->getBillboardChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray rfcUUID = otherNode->getUUID().toRfc4122();
QByteArray billboard = otherNodeData->getAvatar().getBillboard();
auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size());
billboardPacket->write(rfcUUID);
billboardPacket->write(billboard);
nodeList->sendPacket(std::move(billboardPacket), *node);
++_sumBillboardPackets;
}
if (otherNodeData->getIdentityChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
identityPacket->write(individualData);
nodeList->sendPacket(std::move(identityPacket), *node);
++_sumIdentityPackets;
}
AvatarData& otherAvatar = otherNodeData->getAvatar();
// Decide whether to send this avatar's data based on it's distance from us
@ -254,10 +297,10 @@ void AvatarMixer::broadcastAvatarData() {
// potentially update the max full rate distance for this frame
maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar);
if (distanceToAvatar != 0.0f
if (distanceToAvatar != 0.0f
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
return;
}
return;
}
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber();
@ -291,53 +334,11 @@ void AvatarMixer::broadcastAvatarData() {
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
numAvatarDataBytes +=
avatarPacketList->write(otherAvatar.toByteArray(false, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO));
avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO));
avatarPacketList->endSegment();
// if the receiving avatar has just connected make sure we send out the mesh and billboard
// for this avatar (assuming they exist)
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets();
// we will also force a send of billboard or identity packet
// if either has changed in the last frame
if (otherNodeData->getBillboardChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray rfcUUID = otherNode->getUUID().toRfc4122();
QByteArray billboard = otherNodeData->getAvatar().getBillboard();
auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size());
billboardPacket->write(rfcUUID);
billboardPacket->write(billboard);
nodeList->sendPacket(std::move(billboardPacket), *node);
++_sumBillboardPackets;
}
if (otherNodeData->getIdentityChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
identityPacket->write(individualData);
nodeList->sendPacket(std::move(identityPacket), *node);
++_sumIdentityPackets;
}
});
// close the current packet so that we're always sending something
avatarPacketList->closeCurrentPacket(true);
@ -484,7 +485,7 @@ 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->getOutboundBandwidth();
avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth();
@ -537,7 +538,7 @@ void AvatarMixer::run() {
qDebug() << "Waiting for domain settings from domain-server.";
// block until we get the settingsRequestComplete signal
QEventLoop loop;
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);

View file

@ -16,7 +16,7 @@
int AvatarMixerClientData::parseData(NLPacket& packet) {
// pull the sequence number from the data first
packet.readPrimitive(&_lastReceivedSequenceNumber);
// compute the offset to the data payload
return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead()));
}
@ -27,6 +27,14 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {
return oldValue;
}
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid) {
if (_hasReceivedFirstPacketsFrom.find(uuid) == _hasReceivedFirstPacketsFrom.end()) {
_hasReceivedFirstPacketsFrom.insert(uuid);
return false;
}
return true;
}
uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
// return the matching PacketSequenceNumber, or the default if we don't have it
auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID);
@ -45,9 +53,9 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
jsonObject["avg_other_av_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond();
jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond();
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["av_data_receive_rate"] = _avatar.getReceiveRate();
}

View file

@ -15,6 +15,7 @@
#include <algorithm>
#include <cfloat>
#include <unordered_map>
#include <unordered_set>
#include <QtCore/QJsonObject>
#include <QtCore/QUrl>
@ -34,25 +35,26 @@ class AvatarMixerClientData : public NodeData {
public:
int parseData(NLPacket& packet);
AvatarData& getAvatar() { return _avatar; }
bool checkAndSetHasReceivedFirstPackets();
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber)
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; }
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
float getFullRateDistance() const { return _fullRateDistance; }
void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; }
float getMaxAvatarDistance() const { return _maxAvatarDistance; }
@ -73,31 +75,32 @@ public:
void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; }
void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); }
float getOutboundAvatarDataKbps() const
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
void loadJSONStats(QJsonObject& jsonObject) const;
private:
AvatarData _avatar;
uint16_t _lastReceivedSequenceNumber { 0 };
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
bool _hasReceivedFirstPackets = false;
quint64 _billboardChangeTimestamp = 0;
quint64 _identityChangeTimestamp = 0;
float _fullRateDistance = FLT_MAX;
float _maxAvatarDistance = FLT_MAX;
int _numAvatarsSentLastFrame = 0;
int _numFramesSinceAdjustment = 0;
SimpleMovingAverage _otherAvatarStarves;
SimpleMovingAverage _otherAvatarSkips;
int _numOutOfOrderSends = 0;
SimpleMovingAverage _avgOtherAvatarDataRate;
};