mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 18:42:58 +02:00
Merge pull request #14721 from Atlante45/feat/upstream-limitter
Limit upstream bandwidth to the Avatar Mixer
This commit is contained in:
commit
8c4c488676
18 changed files with 114 additions and 153 deletions
|
@ -506,16 +506,6 @@ void Agent::executeScript() {
|
||||||
|
|
||||||
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
|
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
|
||||||
|
|
||||||
// Agents should run at 45hz
|
|
||||||
static const int AVATAR_DATA_HZ = 45;
|
|
||||||
static const int AVATAR_DATA_IN_MSECS = MSECS_PER_SECOND / AVATAR_DATA_HZ;
|
|
||||||
QTimer* avatarDataTimer = new QTimer(this);
|
|
||||||
connect(avatarDataTimer, &QTimer::timeout, this, &Agent::processAgentAvatar);
|
|
||||||
avatarDataTimer->setSingleShot(false);
|
|
||||||
avatarDataTimer->setInterval(AVATAR_DATA_IN_MSECS);
|
|
||||||
avatarDataTimer->setTimerType(Qt::PreciseTimer);
|
|
||||||
avatarDataTimer->start();
|
|
||||||
|
|
||||||
_scriptEngine->run();
|
_scriptEngine->run();
|
||||||
|
|
||||||
Frame::clearFrameHandler(AUDIO_FRAME_TYPE);
|
Frame::clearFrameHandler(AUDIO_FRAME_TYPE);
|
||||||
|
@ -529,8 +519,6 @@ void Agent::executeScript() {
|
||||||
recordingInterface->stopRecording();
|
recordingInterface->stopRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
avatarDataTimer->stop();
|
|
||||||
|
|
||||||
setIsAvatar(false); // will stop timers for sending identity packets
|
setIsAvatar(false); // will stop timers for sending identity packets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,20 +573,16 @@ void Agent::setIsAvatar(bool isAvatar) {
|
||||||
|
|
||||||
auto scriptableAvatar = DependencyManager::get<ScriptableAvatar>();
|
auto scriptableAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||||
if (_isAvatar) {
|
if (_isAvatar) {
|
||||||
if (!_avatarIdentityTimer) {
|
if (!_avatarQueryTimer) {
|
||||||
// set up the avatar timers
|
// set up the avatar timers
|
||||||
_avatarIdentityTimer = new QTimer(this);
|
|
||||||
_avatarQueryTimer = new QTimer(this);
|
_avatarQueryTimer = new QTimer(this);
|
||||||
|
|
||||||
// connect our slot
|
// connect our slot
|
||||||
connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
|
|
||||||
connect(_avatarQueryTimer, &QTimer::timeout, this, &Agent::queryAvatars);
|
connect(_avatarQueryTimer, &QTimer::timeout, this, &Agent::queryAvatars);
|
||||||
|
|
||||||
static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
|
|
||||||
static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000;
|
static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||||
|
|
||||||
// start the timers
|
// start the timer
|
||||||
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets
|
|
||||||
_avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS);
|
_avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS);
|
||||||
|
|
||||||
connect(_scriptEngine.data(), &ScriptEngine::update,
|
connect(_scriptEngine.data(), &ScriptEngine::update,
|
||||||
|
@ -610,11 +594,7 @@ void Agent::setIsAvatar(bool isAvatar) {
|
||||||
|
|
||||||
_entityEditSender.setMyAvatar(scriptableAvatar.data());
|
_entityEditSender.setMyAvatar(scriptableAvatar.data());
|
||||||
} else {
|
} else {
|
||||||
if (_avatarIdentityTimer) {
|
if (_avatarQueryTimer) {
|
||||||
_avatarIdentityTimer->stop();
|
|
||||||
delete _avatarIdentityTimer;
|
|
||||||
_avatarIdentityTimer = nullptr;
|
|
||||||
|
|
||||||
_avatarQueryTimer->stop();
|
_avatarQueryTimer->stop();
|
||||||
delete _avatarQueryTimer;
|
delete _avatarQueryTimer;
|
||||||
_avatarQueryTimer = nullptr;
|
_avatarQueryTimer = nullptr;
|
||||||
|
@ -647,14 +627,6 @@ void Agent::setIsAvatar(bool isAvatar) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agent::sendAvatarIdentityPacket() {
|
|
||||||
if (_isAvatar) {
|
|
||||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
|
||||||
scriptedAvatar->markIdentityDataChanged();
|
|
||||||
scriptedAvatar->sendIdentityPacket();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Agent::queryAvatars() {
|
void Agent::queryAvatars() {
|
||||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||||
|
|
||||||
|
@ -682,44 +654,6 @@ void Agent::queryAvatars() {
|
||||||
{ NodeType::AvatarMixer });
|
{ NodeType::AvatarMixer });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agent::processAgentAvatar() {
|
|
||||||
if (!_scriptEngine->isFinished() && _isAvatar) {
|
|
||||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
|
||||||
|
|
||||||
AvatarData::AvatarDataDetail dataDetail = (randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO) ? AvatarData::SendAllData : AvatarData::CullSmallData;
|
|
||||||
QByteArray avatarByteArray = scriptedAvatar->toByteArrayStateful(dataDetail);
|
|
||||||
|
|
||||||
int maximumByteArraySize = NLPacket::maxPayloadSize(PacketType::AvatarData) - sizeof(AvatarDataSequenceNumber);
|
|
||||||
|
|
||||||
if (avatarByteArray.size() > maximumByteArraySize) {
|
|
||||||
qWarning() << " scriptedAvatar->toByteArrayStateful() resulted in very large buffer:" << avatarByteArray.size() << "... attempt to drop facial data";
|
|
||||||
avatarByteArray = scriptedAvatar->toByteArrayStateful(dataDetail, true);
|
|
||||||
|
|
||||||
if (avatarByteArray.size() > maximumByteArraySize) {
|
|
||||||
qWarning() << " scriptedAvatar->toByteArrayStateful() without facial data resulted in very large buffer:" << avatarByteArray.size() << "... reduce to MinimumData";
|
|
||||||
avatarByteArray = scriptedAvatar->toByteArrayStateful(AvatarData::MinimumData, true);
|
|
||||||
|
|
||||||
if (avatarByteArray.size() > maximumByteArraySize) {
|
|
||||||
qWarning() << " scriptedAvatar->toByteArrayStateful() MinimumData resulted in very large buffer:" << avatarByteArray.size() << "... FAIL!!";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptedAvatar->doneEncoding(true);
|
|
||||||
|
|
||||||
static AvatarDataSequenceNumber sequenceNumber = 0;
|
|
||||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size() + sizeof(sequenceNumber));
|
|
||||||
avatarPacket->writePrimitive(sequenceNumber++);
|
|
||||||
|
|
||||||
avatarPacket->write(avatarByteArray);
|
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
|
|
||||||
nodeList->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Agent::encodeFrameOfZeros(QByteArray& encodedZeros) {
|
void Agent::encodeFrameOfZeros(QByteArray& encodedZeros) {
|
||||||
_flushEncoder = false;
|
_flushEncoder = false;
|
||||||
static const QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, 0);
|
static const QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, 0);
|
||||||
|
|
|
@ -81,7 +81,6 @@ private slots:
|
||||||
void nodeActivated(SharedNodePointer activatedNode);
|
void nodeActivated(SharedNodePointer activatedNode);
|
||||||
void nodeKilled(SharedNodePointer killedNode);
|
void nodeKilled(SharedNodePointer killedNode);
|
||||||
|
|
||||||
void processAgentAvatar();
|
|
||||||
void processAgentAvatarAudio();
|
void processAgentAvatarAudio();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -99,7 +98,6 @@ private:
|
||||||
|
|
||||||
void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; }
|
void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; }
|
||||||
|
|
||||||
void sendAvatarIdentityPacket();
|
|
||||||
void queryAvatars();
|
void queryAvatars();
|
||||||
|
|
||||||
QString _scriptContents;
|
QString _scriptContents;
|
||||||
|
@ -110,7 +108,6 @@ private:
|
||||||
bool _shouldMuteRecordingAudio { false };
|
bool _shouldMuteRecordingAudio { false };
|
||||||
int _numAvatarSoundSentBytes = 0;
|
int _numAvatarSoundSentBytes = 0;
|
||||||
bool _isAvatar = false;
|
bool _isAvatar = false;
|
||||||
QTimer* _avatarIdentityTimer = nullptr;
|
|
||||||
QTimer* _avatarQueryTimer = nullptr;
|
QTimer* _avatarQueryTimer = nullptr;
|
||||||
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,6 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||||
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
|
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
||||||
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.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket");
|
||||||
|
|
||||||
|
@ -582,36 +581,6 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> mes
|
||||||
_handleAvatarIdentityPacketElapsedTime += (end - start);
|
_handleAvatarIdentityPacketElapsedTime += (end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixer::handleAvatarIdentityRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
|
||||||
if (message->getSize() < NUM_BYTES_RFC4122_UUID) {
|
|
||||||
qCDebug(avatars) << "Malformed AvatarIdentityRequest received from" << message->getSenderSockAddr().toString();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUuid avatarID(QUuid::fromRfc4122(message->getMessage()) );
|
|
||||||
if (!avatarID.isNull()) {
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
auto requestedNode = nodeList->nodeWithUUID(avatarID);
|
|
||||||
|
|
||||||
if (requestedNode) {
|
|
||||||
AvatarMixerClientData* avatarClientData = static_cast<AvatarMixerClientData*>(requestedNode->getLinkedData());
|
|
||||||
if (avatarClientData) {
|
|
||||||
const AvatarData& avatarData = avatarClientData->getAvatar();
|
|
||||||
QByteArray serializedAvatar = avatarData.identityByteArray();
|
|
||||||
auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
|
|
||||||
identityPackets->write(serializedAvatar);
|
|
||||||
nodeList->sendPacketList(std::move(identityPackets), *senderNode);
|
|
||||||
++_sumIdentityPackets;
|
|
||||||
}
|
|
||||||
|
|
||||||
AvatarMixerClientData* senderData = static_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
|
|
||||||
if (senderData) {
|
|
||||||
senderData->resetSentTraitData(requestedNode->getLocalID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) {
|
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) {
|
||||||
auto start = usecTimestampNow();
|
auto start = usecTimestampNow();
|
||||||
handleAvatarKilled(node);
|
handleAvatarKilled(node);
|
||||||
|
|
|
@ -54,7 +54,6 @@ private slots:
|
||||||
void handleRequestsDomainListDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleRequestsDomainListDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleReplicatedPacket(QSharedPointer<ReceivedMessage> message);
|
void handleReplicatedPacket(QSharedPointer<ReceivedMessage> message);
|
||||||
void handleReplicatedBulkAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
void handleReplicatedBulkAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
||||||
void handleAvatarIdentityRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
|
||||||
void domainSettingsRequestComplete();
|
void domainSettingsRequestComplete();
|
||||||
void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
|
void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
|
||||||
void start();
|
void start();
|
||||||
|
|
|
@ -91,6 +91,39 @@ void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||||
updateJointMappings();
|
updateJointMappings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ScriptableAvatar::sendAvatarDataPacket(bool sendAll) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto now = Clock::now();
|
||||||
|
|
||||||
|
int MAX_DATA_RATE_MBPS = 3;
|
||||||
|
int maxDataRateBytesPerSeconds = MAX_DATA_RATE_MBPS * BYTES_PER_KILOBYTE * KILO_PER_MEGA / BITS_IN_BYTE;
|
||||||
|
int maxDataRateBytesPerMilliseconds = maxDataRateBytesPerSeconds / MSECS_PER_SECOND;
|
||||||
|
|
||||||
|
auto bytesSent = 0;
|
||||||
|
|
||||||
|
if (now > _nextTraitsSendWindow) {
|
||||||
|
if (getIdentityDataChanged()) {
|
||||||
|
bytesSent += sendIdentityPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesSent += _clientTraitsHandler->sendChangedTraitsToMixer();
|
||||||
|
|
||||||
|
// Compute the next send window based on how much data we sent and what
|
||||||
|
// data rate we're trying to max at.
|
||||||
|
milliseconds timeUntilNextSend { bytesSent / maxDataRateBytesPerMilliseconds };
|
||||||
|
_nextTraitsSendWindow += timeUntilNextSend;
|
||||||
|
|
||||||
|
// Don't let the next send window lag behind if we're not sending a lot of data.
|
||||||
|
if (_nextTraitsSendWindow < now) {
|
||||||
|
_nextTraitsSendWindow = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesSent += AvatarData::sendAvatarDataPacket(sendAll);
|
||||||
|
|
||||||
|
return bytesSent;
|
||||||
|
}
|
||||||
|
|
||||||
static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, const glm::vec3 translation) {
|
static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, const glm::vec3 translation) {
|
||||||
glm::mat4 translationMat = glm::translate(translation);
|
glm::mat4 translationMat = glm::translate(translation);
|
||||||
glm::mat4 rotationMat = glm::mat4_cast(joint.preRotation * rotation * joint.postRotation);
|
glm::mat4 rotationMat = glm::mat4_cast(joint.preRotation * rotation * joint.postRotation);
|
||||||
|
@ -161,7 +194,13 @@ void ScriptableAvatar::update(float deltatime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_clientTraitsHandler->sendChangedTraitsToMixer();
|
quint64 now = usecTimestampNow();
|
||||||
|
quint64 dt = now - _lastSendAvatarDataTime;
|
||||||
|
|
||||||
|
if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS) {
|
||||||
|
sendAvatarDataPacket();
|
||||||
|
_lastSendAvatarDataTime = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptableAvatar::updateJointMappings() {
|
void ScriptableAvatar::updateJointMappings() {
|
||||||
|
|
|
@ -123,6 +123,10 @@
|
||||||
|
|
||||||
class ScriptableAvatar : public AvatarData, public Dependency {
|
class ScriptableAvatar : public AvatarData, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
using Clock = std::chrono::system_clock;
|
||||||
|
using TimePoint = Clock::time_point;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ScriptableAvatar();
|
ScriptableAvatar();
|
||||||
|
@ -177,6 +181,8 @@ public:
|
||||||
|
|
||||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||||
|
|
||||||
|
int sendAvatarDataPacket(bool sendAll = false) override;
|
||||||
|
|
||||||
virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking = false) override;
|
virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking = false) override;
|
||||||
|
|
||||||
void setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement);
|
void setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement);
|
||||||
|
@ -228,6 +234,10 @@ private:
|
||||||
|
|
||||||
/// Loads the joint indices, names from the FST file (if any)
|
/// Loads the joint indices, names from the FST file (if any)
|
||||||
void updateJointMappings();
|
void updateJointMappings();
|
||||||
|
|
||||||
|
quint64 _lastSendAvatarDataTime { 0 };
|
||||||
|
|
||||||
|
TimePoint _nextTraitsSendWindow;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ScriptableAvatar_h
|
#endif // hifi_ScriptableAvatar_h
|
||||||
|
|
|
@ -2201,7 +2201,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
|| ((rightHandPose.valid || lastRightHandPose.valid) && (rightHandPose != lastRightHandPose));
|
|| ((rightHandPose.valid || lastRightHandPose.valid) && (rightHandPose != lastRightHandPose));
|
||||||
lastLeftHandPose = leftHandPose;
|
lastLeftHandPose = leftHandPose;
|
||||||
lastRightHandPose = rightHandPose;
|
lastRightHandPose = rightHandPose;
|
||||||
properties["avatar_identity_requests_sent"] = DependencyManager::get<AvatarManager>()->getIdentityRequestsSent();
|
|
||||||
|
|
||||||
UserActivityLogger::getInstance().logAction("stats", properties);
|
UserActivityLogger::getInstance().logAction("stats", properties);
|
||||||
});
|
});
|
||||||
|
|
|
@ -48,8 +48,6 @@
|
||||||
// 50 times per second - target is 45hz, but this helps account for any small deviations
|
// 50 times per second - target is 45hz, but this helps account for any small deviations
|
||||||
// in the update loop - this also results in ~30hz when in desktop mode which is essentially
|
// in the update loop - this also results in ~30hz when in desktop mode which is essentially
|
||||||
// what we want
|
// what we want
|
||||||
const int CLIENT_TO_AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 50;
|
|
||||||
static const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND / CLIENT_TO_AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND;
|
|
||||||
|
|
||||||
// We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key.
|
// We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key.
|
||||||
const QUuid MY_AVATAR_KEY; // NULL key
|
const QUuid MY_AVATAR_KEY; // NULL key
|
||||||
|
@ -352,25 +350,6 @@ void AvatarManager::postUpdate(float deltaTime, const render::ScenePointer& scen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::sendIdentityRequest(const QUuid& avatarID) const {
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
QWeakPointer<NodeList> nodeListWeak = nodeList;
|
|
||||||
nodeList->eachMatchingNode(
|
|
||||||
[](const SharedNodePointer& node)->bool {
|
|
||||||
return node->getType() == NodeType::AvatarMixer && node->getActiveSocket();
|
|
||||||
},
|
|
||||||
[this, avatarID, nodeListWeak](const SharedNodePointer& node) {
|
|
||||||
auto nodeList = nodeListWeak.lock();
|
|
||||||
if (nodeList) {
|
|
||||||
auto packet = NLPacket::create(PacketType::AvatarIdentityRequest, NUM_BYTES_RFC4122_UUID, true);
|
|
||||||
packet->write(avatarID.toRfc4122());
|
|
||||||
nodeList->sendPacket(std::move(packet), *node);
|
|
||||||
++_identityRequestsSent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||||
if (_avatarsToFadeOut.empty()) {
|
if (_avatarsToFadeOut.empty()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -92,7 +92,6 @@ public:
|
||||||
|
|
||||||
void updateMyAvatar(float deltaTime);
|
void updateMyAvatar(float deltaTime);
|
||||||
void updateOtherAvatars(float deltaTime);
|
void updateOtherAvatars(float deltaTime);
|
||||||
void sendIdentityRequest(const QUuid& avatarID) const;
|
|
||||||
|
|
||||||
void setMyAvatarDataPacketsPaused(bool puase);
|
void setMyAvatarDataPacketsPaused(bool puase);
|
||||||
|
|
||||||
|
@ -191,7 +190,6 @@ public:
|
||||||
Q_INVOKABLE QVariantMap getPalData(const QStringList& specificAvatarIdentifiers = QStringList());
|
Q_INVOKABLE QVariantMap getPalData(const QStringList& specificAvatarIdentifiers = QStringList());
|
||||||
|
|
||||||
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
|
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
|
||||||
int getIdentityRequestsSent() const { return _identityRequestsSent; }
|
|
||||||
|
|
||||||
void queuePhysicsChange(const OtherAvatarPointer& avatar);
|
void queuePhysicsChange(const OtherAvatarPointer& avatar);
|
||||||
void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||||
|
@ -241,7 +239,6 @@ private:
|
||||||
float _avatarSimulationTime { 0.0f };
|
float _avatarSimulationTime { 0.0f };
|
||||||
bool _shouldRender { true };
|
bool _shouldRender { true };
|
||||||
bool _myAvatarDataPacketsPaused { false };
|
bool _myAvatarDataPacketsPaused { false };
|
||||||
mutable int _identityRequestsSent { 0 };
|
|
||||||
|
|
||||||
mutable std::mutex _spaceLock;
|
mutable std::mutex _spaceLock;
|
||||||
workload::SpacePointer _space;
|
workload::SpacePointer _space;
|
||||||
|
|
|
@ -668,12 +668,6 @@ void MyAvatar::update(float deltaTime) {
|
||||||
Q_ARG(glm::vec3, (getWorldPosition() - halfBoundingBoxDimensions)),
|
Q_ARG(glm::vec3, (getWorldPosition() - halfBoundingBoxDimensions)),
|
||||||
Q_ARG(glm::vec3, (halfBoundingBoxDimensions*2.0f)));
|
Q_ARG(glm::vec3, (halfBoundingBoxDimensions*2.0f)));
|
||||||
|
|
||||||
if (getIdentityDataChanged()) {
|
|
||||||
sendIdentityPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
_clientTraitsHandler->sendChangedTraitsToMixer();
|
|
||||||
|
|
||||||
simulate(deltaTime, true);
|
simulate(deltaTime, true);
|
||||||
|
|
||||||
currentEnergy += energyChargeRate;
|
currentEnergy += energyChargeRate;
|
||||||
|
@ -3103,6 +3097,39 @@ void MyAvatar::preDisplaySide(const RenderArgs* renderArgs) {
|
||||||
_prevShouldDrawHead = shouldDrawHead;
|
_prevShouldDrawHead = shouldDrawHead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MyAvatar::sendAvatarDataPacket(bool sendAll) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto now = Clock::now();
|
||||||
|
|
||||||
|
int MAX_DATA_RATE_MBPS = 3;
|
||||||
|
int maxDataRateBytesPerSeconds = MAX_DATA_RATE_MBPS * BYTES_PER_KILOBYTE * KILO_PER_MEGA / BITS_IN_BYTE;
|
||||||
|
int maxDataRateBytesPerMilliseconds = maxDataRateBytesPerSeconds / MSECS_PER_SECOND;
|
||||||
|
|
||||||
|
auto bytesSent = 0;
|
||||||
|
|
||||||
|
if (now > _nextTraitsSendWindow) {
|
||||||
|
if (getIdentityDataChanged()) {
|
||||||
|
bytesSent += sendIdentityPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesSent += _clientTraitsHandler->sendChangedTraitsToMixer();
|
||||||
|
|
||||||
|
// Compute the next send window based on how much data we sent and what
|
||||||
|
// data rate we're trying to max at.
|
||||||
|
milliseconds timeUntilNextSend { bytesSent / maxDataRateBytesPerMilliseconds };
|
||||||
|
_nextTraitsSendWindow += timeUntilNextSend;
|
||||||
|
|
||||||
|
// Don't let the next send window lag behind if we're not sending a lot of data.
|
||||||
|
if (_nextTraitsSendWindow < now) {
|
||||||
|
_nextTraitsSendWindow = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesSent += Avatar::sendAvatarDataPacket(sendAll);
|
||||||
|
|
||||||
|
return bytesSent;
|
||||||
|
}
|
||||||
|
|
||||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
|
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
|
||||||
|
|
||||||
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
|
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
|
||||||
|
|
|
@ -253,6 +253,9 @@ class MyAvatar : public Avatar {
|
||||||
const QString DOMINANT_LEFT_HAND = "left";
|
const QString DOMINANT_LEFT_HAND = "left";
|
||||||
const QString DOMINANT_RIGHT_HAND = "right";
|
const QString DOMINANT_RIGHT_HAND = "right";
|
||||||
|
|
||||||
|
using Clock = std::chrono::system_clock;
|
||||||
|
using TimePoint = Clock::time_point;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum DriveKeys {
|
enum DriveKeys {
|
||||||
TRANSLATE_X = 0,
|
TRANSLATE_X = 0,
|
||||||
|
@ -1213,6 +1216,7 @@ public:
|
||||||
void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override;
|
void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override;
|
||||||
void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) override;
|
void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) override;
|
||||||
void avatarEntityDataToJson(QJsonObject& root) const override;
|
void avatarEntityDataToJson(QJsonObject& root) const override;
|
||||||
|
int sendAvatarDataPacket(bool sendAll = false) override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
@ -1937,6 +1941,8 @@ private:
|
||||||
bool _skeletonModelLoaded { false };
|
bool _skeletonModelLoaded { false };
|
||||||
bool _reloadAvatarEntityDataFromSettings { true };
|
bool _reloadAvatarEntityDataFromSettings { true };
|
||||||
|
|
||||||
|
TimePoint _nextTraitsSendWindow;
|
||||||
|
|
||||||
Setting::Handle<QString> _dominantHandSetting;
|
Setting::Handle<QString> _dominantHandSetting;
|
||||||
Setting::Handle<float> _headPitchSetting;
|
Setting::Handle<float> _headPitchSetting;
|
||||||
Setting::Handle<float> _scaleSetting;
|
Setting::Handle<float> _scaleSetting;
|
||||||
|
|
|
@ -2157,7 +2157,7 @@ void AvatarData::detachAll(const QString& modelURL, const QString& jointName) {
|
||||||
setAttachmentData(attachmentData);
|
setAttachmentData(attachmentData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::sendAvatarDataPacket(bool sendAll) {
|
int AvatarData::sendAvatarDataPacket(bool sendAll) {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
// about 2% of the time, we send a full update (meaning, we transmit all the joint data), even if nothing has changed.
|
// about 2% of the time, we send a full update (meaning, we transmit all the joint data), even if nothing has changed.
|
||||||
|
@ -2170,16 +2170,14 @@ void AvatarData::sendAvatarDataPacket(bool sendAll) {
|
||||||
int maximumByteArraySize = NLPacket::maxPayloadSize(PacketType::AvatarData) - sizeof(AvatarDataSequenceNumber);
|
int maximumByteArraySize = NLPacket::maxPayloadSize(PacketType::AvatarData) - sizeof(AvatarDataSequenceNumber);
|
||||||
|
|
||||||
if (avatarByteArray.size() > maximumByteArraySize) {
|
if (avatarByteArray.size() > maximumByteArraySize) {
|
||||||
qCWarning(avatars) << "toByteArrayStateful() resulted in very large buffer:" << avatarByteArray.size() << "... attempt to drop facial data";
|
|
||||||
avatarByteArray = toByteArrayStateful(dataDetail, true);
|
avatarByteArray = toByteArrayStateful(dataDetail, true);
|
||||||
|
|
||||||
if (avatarByteArray.size() > maximumByteArraySize) {
|
if (avatarByteArray.size() > maximumByteArraySize) {
|
||||||
qCWarning(avatars) << "toByteArrayStateful() without facial data resulted in very large buffer:" << avatarByteArray.size() << "... reduce to MinimumData";
|
|
||||||
avatarByteArray = toByteArrayStateful(MinimumData, true);
|
avatarByteArray = toByteArrayStateful(MinimumData, true);
|
||||||
|
|
||||||
if (avatarByteArray.size() > maximumByteArraySize) {
|
if (avatarByteArray.size() > maximumByteArraySize) {
|
||||||
qCWarning(avatars) << "toByteArrayStateful() MinimumData resulted in very large buffer:" << avatarByteArray.size() << "... FAIL!!";
|
qCWarning(avatars) << "toByteArrayStateful() MinimumData resulted in very large buffer:" << avatarByteArray.size() << "... FAIL!!";
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2191,18 +2189,20 @@ void AvatarData::sendAvatarDataPacket(bool sendAll) {
|
||||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size() + sizeof(sequenceNumber));
|
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size() + sizeof(sequenceNumber));
|
||||||
avatarPacket->writePrimitive(sequenceNumber++);
|
avatarPacket->writePrimitive(sequenceNumber++);
|
||||||
avatarPacket->write(avatarByteArray);
|
avatarPacket->write(avatarByteArray);
|
||||||
|
auto packetSize = avatarPacket->getWireSize();
|
||||||
|
|
||||||
nodeList->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
|
nodeList->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
|
||||||
|
|
||||||
|
return packetSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::sendIdentityPacket() {
|
int AvatarData::sendIdentityPacket() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
if (_identityDataChanged) {
|
if (_identityDataChanged) {
|
||||||
// if the identity data has changed, push the sequence number forwards
|
// if the identity data has changed, push the sequence number forwards
|
||||||
++_identitySequenceNumber;
|
++_identitySequenceNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray identityData = identityByteArray();
|
QByteArray identityData = identityByteArray();
|
||||||
|
|
||||||
auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
|
auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
|
||||||
|
@ -2216,6 +2216,7 @@ void AvatarData::sendIdentityPacket() {
|
||||||
});
|
});
|
||||||
|
|
||||||
_identityDataChanged = false;
|
_identityDataChanged = false;
|
||||||
|
return identityData.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QString JSON_ATTACHMENT_URL = QStringLiteral("modelUrl");
|
static const QString JSON_ATTACHMENT_URL = QStringLiteral("modelUrl");
|
||||||
|
|
|
@ -1271,12 +1271,12 @@ public slots:
|
||||||
* @function MyAvatar.sendAvatarDataPacket
|
* @function MyAvatar.sendAvatarDataPacket
|
||||||
* @param {boolean} [sendAll=false]
|
* @param {boolean} [sendAll=false]
|
||||||
*/
|
*/
|
||||||
void sendAvatarDataPacket(bool sendAll = false);
|
virtual int sendAvatarDataPacket(bool sendAll = false);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function MyAvatar.sendIdentityPacket
|
* @function MyAvatar.sendIdentityPacket
|
||||||
*/
|
*/
|
||||||
void sendIdentityPacket();
|
int sendIdentityPacket();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function MyAvatar.setSessionUUID
|
* @function MyAvatar.setSessionUUID
|
||||||
|
|
|
@ -32,6 +32,9 @@
|
||||||
#include "AvatarData.h"
|
#include "AvatarData.h"
|
||||||
#include "AssociatedTraitValues.h"
|
#include "AssociatedTraitValues.h"
|
||||||
|
|
||||||
|
const int CLIENT_TO_AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 50;
|
||||||
|
const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND / CLIENT_TO_AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* <strong>Note:</strong> An <code>AvatarList</code> API is also provided for Interface and client entity scripts: it is a
|
* <strong>Note:</strong> An <code>AvatarList</code> API is also provided for Interface and client entity scripts: it is a
|
||||||
* synonym for the {@link AvatarManager} API.
|
* synonym for the {@link AvatarManager} API.
|
||||||
|
|
|
@ -65,8 +65,9 @@ void ClientTraitsHandler::resetForNewMixer() {
|
||||||
_owningAvatar->prepareResetTraitInstances();
|
_owningAvatar->prepareResetTraitInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
int ClientTraitsHandler::sendChangedTraitsToMixer() {
|
||||||
std::unique_lock<Mutex> lock(_traitLock);
|
std::unique_lock<Mutex> lock(_traitLock);
|
||||||
|
int bytesWritten = 0;
|
||||||
|
|
||||||
if (hasChangedTraits() || _shouldPerformInitialSend) {
|
if (hasChangedTraits() || _shouldPerformInitialSend) {
|
||||||
// we have at least one changed trait to send
|
// we have at least one changed trait to send
|
||||||
|
@ -75,7 +76,7 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
||||||
auto avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
auto avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
||||||
if (!avatarMixer || !avatarMixer->getActiveSocket()) {
|
if (!avatarMixer || !avatarMixer->getActiveSocket()) {
|
||||||
// we don't have an avatar mixer with an active socket, we can't send changed traits at this time
|
// we don't have an avatar mixer with an active socket, we can't send changed traits at this time
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we have a mixer to send to, setup our set traits packet
|
// we have a mixer to send to, setup our set traits packet
|
||||||
|
@ -106,7 +107,7 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
||||||
|
|
||||||
if (initialSend || *simpleIt == Updated) {
|
if (initialSend || *simpleIt == Updated) {
|
||||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||||
_owningAvatar->packTrait(traitType, *traitsPacketList);
|
bytesWritten += _owningAvatar->packTrait(traitType, *traitsPacketList);
|
||||||
|
|
||||||
// keep track of our skeleton version in case we get an override back
|
// keep track of our skeleton version in case we get an override back
|
||||||
_currentSkeletonVersion = _currentTraitVersion;
|
_currentSkeletonVersion = _currentTraitVersion;
|
||||||
|
@ -123,10 +124,10 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
||||||
|| instanceIDValuePair.value == Updated) {
|
|| instanceIDValuePair.value == Updated) {
|
||||||
// this is a changed trait we need to send or we haven't send out trait information yet
|
// this is a changed trait we need to send or we haven't send out trait information yet
|
||||||
// ask the owning avatar to pack it
|
// ask the owning avatar to pack it
|
||||||
_owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList);
|
bytesWritten += _owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList);
|
||||||
} else if (!initialSend && instanceIDValuePair.value == Deleted) {
|
} else if (!initialSend && instanceIDValuePair.value == Deleted) {
|
||||||
// pack delete for this trait instance
|
// pack delete for this trait instance
|
||||||
AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id,
|
bytesWritten += AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id,
|
||||||
*traitsPacketList);
|
*traitsPacketList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,6 +137,8 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
||||||
|
|
||||||
nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer);
|
nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return bytesWritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ class ClientTraitsHandler : public QObject {
|
||||||
public:
|
public:
|
||||||
ClientTraitsHandler(AvatarData* owningAvatar);
|
ClientTraitsHandler(AvatarData* owningAvatar);
|
||||||
|
|
||||||
void sendChangedTraitsToMixer();
|
int sendChangedTraitsToMixer();
|
||||||
|
|
||||||
bool hasChangedTraits() const { return _hasChangedTraits; }
|
bool hasChangedTraits() const { return _hasChangedTraits; }
|
||||||
|
|
||||||
|
|
|
@ -93,8 +93,6 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
return static_cast<PacketVersion>(PingVersion::IncludeConnectionID);
|
return static_cast<PacketVersion>(PingVersion::IncludeConnectionID);
|
||||||
case PacketType::AvatarQuery:
|
case PacketType::AvatarQuery:
|
||||||
return static_cast<PacketVersion>(AvatarQueryVersion::ConicalFrustums);
|
return static_cast<PacketVersion>(AvatarQueryVersion::ConicalFrustums);
|
||||||
case PacketType::AvatarIdentityRequest:
|
|
||||||
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::BulkAvatarTraitsAck:
|
||||||
|
|
|
@ -57,7 +57,7 @@ public:
|
||||||
ICEServerQuery,
|
ICEServerQuery,
|
||||||
OctreeStats,
|
OctreeStats,
|
||||||
SetAvatarTraits,
|
SetAvatarTraits,
|
||||||
AvatarIdentityRequest,
|
UNUSED_PACKET_TYPE,
|
||||||
AssignmentClientStatus,
|
AssignmentClientStatus,
|
||||||
NoisyMute,
|
NoisyMute,
|
||||||
AvatarIdentity,
|
AvatarIdentity,
|
||||||
|
|
Loading…
Reference in a new issue