mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 17:41:12 +02:00
Merge pull request #6393 from hyperlogic/tony/avatar-mixer-identity-fixes
Fix for missing avatars on entry
This commit is contained in:
commit
8e6c860a24
3 changed files with 76 additions and 64 deletions
|
@ -63,7 +63,9 @@ AvatarMixer::~AvatarMixer() {
|
||||||
_broadcastThread.wait();
|
_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.
|
// 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
|
// 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;
|
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();
|
AvatarData& otherAvatar = otherNodeData->getAvatar();
|
||||||
// Decide whether to send this avatar's data based on it's distance from us
|
// 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
|
// potentially update the max full rate distance for this frame
|
||||||
maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar);
|
maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar);
|
||||||
|
|
||||||
if (distanceToAvatar != 0.0f
|
if (distanceToAvatar != 0.0f
|
||||||
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
|
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
|
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
|
||||||
AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber();
|
AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber();
|
||||||
|
@ -291,53 +334,11 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
|
|
||||||
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
|
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
|
||||||
numAvatarDataBytes +=
|
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();
|
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
|
// close the current packet so that we're always sending something
|
||||||
avatarPacketList->closeCurrentPacket(true);
|
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
|
// 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->getOutboundBandwidth();
|
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth();
|
||||||
avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth();
|
avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth();
|
||||||
|
|
||||||
|
@ -537,7 +538,7 @@ void AvatarMixer::run() {
|
||||||
qDebug() << "Waiting for domain settings from domain-server.";
|
qDebug() << "Waiting for domain settings from domain-server.";
|
||||||
|
|
||||||
// block until we get the settingsRequestComplete signal
|
// block until we get the settingsRequestComplete signal
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
||||||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
int AvatarMixerClientData::parseData(NLPacket& packet) {
|
int AvatarMixerClientData::parseData(NLPacket& packet) {
|
||||||
// pull the sequence number from the data first
|
// pull the sequence number from the data first
|
||||||
packet.readPrimitive(&_lastReceivedSequenceNumber);
|
packet.readPrimitive(&_lastReceivedSequenceNumber);
|
||||||
|
|
||||||
// compute the offset to the data payload
|
// compute the offset to the data payload
|
||||||
return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead()));
|
return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead()));
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,14 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {
|
||||||
return oldValue;
|
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 {
|
uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
|
||||||
// return the matching PacketSequenceNumber, or the default if we don't have it
|
// return the matching PacketSequenceNumber, or the default if we don't have it
|
||||||
auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID);
|
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_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond();
|
||||||
jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond();
|
jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond();
|
||||||
jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends;
|
jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends;
|
||||||
|
|
||||||
jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps();
|
jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps();
|
||||||
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar.getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar.getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
||||||
|
|
||||||
jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate();
|
jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
|
@ -34,25 +35,26 @@ class AvatarMixerClientData : public NodeData {
|
||||||
public:
|
public:
|
||||||
int parseData(NLPacket& packet);
|
int parseData(NLPacket& packet);
|
||||||
AvatarData& getAvatar() { return _avatar; }
|
AvatarData& getAvatar() { return _avatar; }
|
||||||
|
|
||||||
bool checkAndSetHasReceivedFirstPackets();
|
bool checkAndSetHasReceivedFirstPackets();
|
||||||
|
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
|
||||||
|
|
||||||
uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
|
uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
|
||||||
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber)
|
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber)
|
||||||
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
|
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
|
||||||
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
|
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
|
||||||
|
|
||||||
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
||||||
|
|
||||||
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
|
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
|
||||||
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
|
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
|
||||||
|
|
||||||
quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
||||||
void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; }
|
void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; }
|
||||||
|
|
||||||
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
|
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
|
||||||
float getFullRateDistance() const { return _fullRateDistance; }
|
float getFullRateDistance() const { return _fullRateDistance; }
|
||||||
|
|
||||||
void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; }
|
void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; }
|
||||||
float getMaxAvatarDistance() const { return _maxAvatarDistance; }
|
float getMaxAvatarDistance() const { return _maxAvatarDistance; }
|
||||||
|
|
||||||
|
@ -73,31 +75,32 @@ public:
|
||||||
void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; }
|
void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; }
|
||||||
|
|
||||||
void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); }
|
void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); }
|
||||||
|
|
||||||
float getOutboundAvatarDataKbps() const
|
float getOutboundAvatarDataKbps() const
|
||||||
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
|
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
|
||||||
|
|
||||||
void loadJSONStats(QJsonObject& jsonObject) const;
|
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||||
private:
|
private:
|
||||||
AvatarData _avatar;
|
AvatarData _avatar;
|
||||||
|
|
||||||
uint16_t _lastReceivedSequenceNumber { 0 };
|
uint16_t _lastReceivedSequenceNumber { 0 };
|
||||||
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
||||||
|
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
|
||||||
|
|
||||||
bool _hasReceivedFirstPackets = false;
|
bool _hasReceivedFirstPackets = false;
|
||||||
quint64 _billboardChangeTimestamp = 0;
|
quint64 _billboardChangeTimestamp = 0;
|
||||||
quint64 _identityChangeTimestamp = 0;
|
quint64 _identityChangeTimestamp = 0;
|
||||||
|
|
||||||
float _fullRateDistance = FLT_MAX;
|
float _fullRateDistance = FLT_MAX;
|
||||||
float _maxAvatarDistance = FLT_MAX;
|
float _maxAvatarDistance = FLT_MAX;
|
||||||
|
|
||||||
int _numAvatarsSentLastFrame = 0;
|
int _numAvatarsSentLastFrame = 0;
|
||||||
int _numFramesSinceAdjustment = 0;
|
int _numFramesSinceAdjustment = 0;
|
||||||
|
|
||||||
SimpleMovingAverage _otherAvatarStarves;
|
SimpleMovingAverage _otherAvatarStarves;
|
||||||
SimpleMovingAverage _otherAvatarSkips;
|
SimpleMovingAverage _otherAvatarSkips;
|
||||||
int _numOutOfOrderSends = 0;
|
int _numOutOfOrderSends = 0;
|
||||||
|
|
||||||
SimpleMovingAverage _avgOtherAvatarDataRate;
|
SimpleMovingAverage _avgOtherAvatarDataRate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue