mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-09 16:52:28 +02:00
server-side release ownership
This commit is contained in:
parent
af57f5d120
commit
2da46ff26a
5 changed files with 76 additions and 60 deletions
|
@ -11,8 +11,11 @@
|
|||
|
||||
//#include <PerfStat.h>
|
||||
|
||||
#include "EntityItem.h"
|
||||
#include "SimpleEntitySimulation.h"
|
||||
|
||||
#include <DirtyOctreeElementOperator.h>
|
||||
|
||||
#include "EntityItem.h"
|
||||
#include "EntitiesLogging.h"
|
||||
|
||||
const quint64 MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD = 2 * USECS_PER_SECOND;
|
||||
|
@ -40,17 +43,22 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
|||
if (entity->getSimulatorID().isNull()) {
|
||||
// no simulators are volunteering
|
||||
// zero the velocity on this entity so that it doesn't drift far away
|
||||
entity->setVelocity(glm::vec3(0.0f));
|
||||
entity->setVelocity(Vectors::ZERO);
|
||||
entity->setAngularVelocity(Vectors::ZERO);
|
||||
entity->setAcceleration(Vectors::ZERO);
|
||||
// remove from list
|
||||
itemItr = _entitiesWithSimulator.erase(itemItr);
|
||||
continue;
|
||||
} else {
|
||||
// the simulator has stopped updating this object
|
||||
// clear ownership and restart timer, giving nearby simulators time to volunteer
|
||||
qCDebug(entities) << "auto-removing simulation owner " << entity.getSimulatorID();
|
||||
qCDebug(entities) << "auto-removing simulation owner " << entity->getSimulatorID();
|
||||
entity->clearSimulationOwnership();
|
||||
entity->markAsChangedOnServer();
|
||||
}
|
||||
entity->markAsChangedOnServer();
|
||||
// dirty all the tree elements that contain the entity
|
||||
DirtyOctreeElementOperator op(entity->getElement());
|
||||
getEntityTree()->recurseTreeWithOperator(&op);
|
||||
} else if (expiry < _nextSimulationExpiry) {
|
||||
_nextSimulationExpiry = expiry;
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
// static
|
||||
// static
|
||||
const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1;
|
||||
|
||||
|
||||
SimulationOwner::SimulationOwner(const SimulationOwner& other)
|
||||
SimulationOwner::SimulationOwner(const SimulationOwner& other)
|
||||
: _id(other._id), _priority(other._priority), _expiry(other._expiry) {
|
||||
}
|
||||
|
||||
|
@ -48,11 +48,6 @@ void SimulationOwner::clear() {
|
|||
|
||||
void SimulationOwner::setPriority(quint8 priority) {
|
||||
_priority = priority;
|
||||
if (_priority == 0) {
|
||||
// when priority is zero we clear everything
|
||||
_expiry = 0;
|
||||
_id = QUuid();
|
||||
}
|
||||
}
|
||||
|
||||
void SimulationOwner::promotePriority(quint8 priority) {
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
const quint8 NO_PRORITY = 0x00;
|
||||
const quint8 ZERO_SIMULATION_PRIORITY = 0x00;
|
||||
|
||||
// Simulation observers will bid to simulate unowned active objects at the lowest possible priority
|
||||
// which is VOLUNTEER. If the server accepts a VOLUNTEER bid it will automatically bump it
|
||||
// which is VOLUNTEER. If the server accepts a VOLUNTEER bid it will automatically bump it
|
||||
// to RECRUIT priority so that other volunteers don't accidentally take over.
|
||||
const quint8 VOLUNTEER_SIMULATION_PRIORITY = 0x01;
|
||||
const quint8 RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
||||
|
|
|
@ -26,10 +26,7 @@
|
|||
#include "EntityTree.h"
|
||||
#endif
|
||||
|
||||
static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f;
|
||||
static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4;
|
||||
|
||||
const uint32_t LOOPS_FOR_SIMULATION_ORPHAN = 50;
|
||||
const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50;
|
||||
const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5;
|
||||
|
||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
||||
|
@ -52,8 +49,6 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
ObjectMotionState(shape),
|
||||
_entityPtr(entity),
|
||||
_entity(entity.get()),
|
||||
_sentInactive(true),
|
||||
_lastStep(0),
|
||||
_serverPosition(0.0f),
|
||||
_serverRotation(),
|
||||
_serverVelocity(0.0f),
|
||||
|
@ -61,13 +56,16 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
_serverGravity(0.0f),
|
||||
_serverAcceleration(0.0f),
|
||||
_serverActionData(QByteArray()),
|
||||
_lastMeasureStep(0),
|
||||
_lastVelocity(glm::vec3(0.0f)),
|
||||
_measuredAcceleration(glm::vec3(0.0f)),
|
||||
_measuredDeltaTime(0.0f),
|
||||
_accelerationNearlyGravityCount(0),
|
||||
_nextOwnershipBid(0),
|
||||
_loopsWithoutOwner(0)
|
||||
_measuredDeltaTime(0.0f),
|
||||
_lastMeasureStep(0),
|
||||
_lastStep(0),
|
||||
_loopsWithoutOwner(0),
|
||||
_accelerationNearlyGravityCount(0),
|
||||
_numInactiveUpdates(1),
|
||||
_outgoingPriority(ZERO_SIMULATION_PRIORITY)
|
||||
{
|
||||
_type = MOTIONSTATE_TYPE_ENTITY;
|
||||
assert(_entity);
|
||||
|
@ -102,20 +100,28 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
ObjectMotionState::handleEasyChanges(flags);
|
||||
|
||||
if (flags & Simulation::DIRTY_SIMULATOR_ID) {
|
||||
_loopsWithoutOwner = 0;
|
||||
if (_entity->getSimulatorID().isNull()) {
|
||||
// simulation ownership has been removed by an external simulator
|
||||
// --> clear the ACTIVATION flag and outgoing priority because this object is coming to rest
|
||||
flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
_body->setActivationState(WANTS_DEACTIVATION);
|
||||
_outgoingPriority = NO_PRORITY;
|
||||
if (glm::length2(_entity->getVelocity()) == 0.0f) {
|
||||
// this object is coming to rest --> clear the ACTIVATION flag and outgoing priority
|
||||
flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
_body->setActivationState(WANTS_DEACTIVATION);
|
||||
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
||||
_loopsWithoutOwner = 0;
|
||||
} else {
|
||||
// unowned object is still moving --> we should volunteer to own it
|
||||
// TODO? put a delay in here proportional to distance from object?
|
||||
setOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
_loopsWithoutOwner = LOOPS_FOR_SIMULATION_ORPHAN;
|
||||
_nextOwnershipBid = 0;
|
||||
}
|
||||
} else {
|
||||
// this entity's simulation is owned by someone, so we push its ownership expiry into the future
|
||||
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
if (Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) {
|
||||
// either we already own the simulation or our old outgoing priority momentarily looses to current owner
|
||||
// so we clear it
|
||||
_outgoingPriority = NO_PRORITY;
|
||||
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -237,10 +243,11 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const {
|
|||
assert(_body);
|
||||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit();
|
||||
return _outgoingPriority != ZERO_SIMULATION_PRIORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit();
|
||||
}
|
||||
|
||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||
// NOTE: we only get here if we think we own the simulation
|
||||
assert(_body);
|
||||
// if we've never checked before, our _lastStep will be 0, and we need to initialize our state
|
||||
if (_lastStep == 0) {
|
||||
|
@ -251,7 +258,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
_serverAngularVelocity = bulletToGLM(_body->getAngularVelocity());
|
||||
_lastStep = simulationStep;
|
||||
_serverActionData = _entity->getActionData();
|
||||
_sentInactive = true;
|
||||
_numInactiveUpdates = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -264,16 +271,21 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
int numSteps = simulationStep - _lastStep;
|
||||
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||
|
||||
const float INACTIVE_UPDATE_PERIOD = 0.5f;
|
||||
if (_sentInactive) {
|
||||
if (_numInactiveUpdates > 0) {
|
||||
const uint8_t MAX_NUM_INACTIVE_UPDATES = 3;
|
||||
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
|
||||
// clear local ownership (stop sending updates) and let the server clear itself
|
||||
_entity->clearSimulationOwnership();
|
||||
return false;
|
||||
}
|
||||
// we resend the inactive update every INACTIVE_UPDATE_PERIOD
|
||||
// until it is removed from the outgoing updates
|
||||
// (which happens when we don't own the simulation and it isn't touching our simulation)
|
||||
const float INACTIVE_UPDATE_PERIOD = 0.5f;
|
||||
return (dt > INACTIVE_UPDATE_PERIOD);
|
||||
}
|
||||
|
||||
bool isActive = _body->isActive();
|
||||
if (!isActive) {
|
||||
if (!_body->isActive()) {
|
||||
// object has gone inactive but our last send was moving --> send non-moving update immediately
|
||||
return true;
|
||||
}
|
||||
|
@ -373,11 +385,11 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s
|
|||
|
||||
if (_entity->getSimulatorID() != sessionID) {
|
||||
// we don't own the simulation
|
||||
if (_outgoingPriority != NO_PRORITY) {
|
||||
if (_outgoingPriority != ZERO_SIMULATION_PRIORITY) {
|
||||
// but we would like to own it
|
||||
if (_outgoingPriority < _entity->getSimulationPriority()) {
|
||||
// but our priority loses to remote, so we don't bother trying
|
||||
_outgoingPriority = NO_PRORITY;
|
||||
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
||||
return false;
|
||||
}
|
||||
return usecTimestampNow() > _nextOwnershipBid;
|
||||
|
@ -399,10 +411,12 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
|
|||
_entity->setVelocity(zero);
|
||||
_entity->setAngularVelocity(zero);
|
||||
_entity->setAcceleration(zero);
|
||||
_sentInactive = true;
|
||||
_numInactiveUpdates++;
|
||||
} else {
|
||||
const uint8_t STEPS_TO_DECIDE_BALLISTIC = 4;
|
||||
float gravityLength = glm::length(_entity->getGravity());
|
||||
float accVsGravity = glm::abs(glm::length(_measuredAcceleration) - gravityLength);
|
||||
const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f;
|
||||
if (accVsGravity < ACCELERATION_EQUIVALENT_EPSILON_RATIO * gravityLength) {
|
||||
// acceleration measured during the most recent simulation step was close to gravity.
|
||||
if (getAccelerationNearlyGravityCount() < STEPS_TO_DECIDE_BALLISTIC) {
|
||||
|
@ -439,7 +453,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
|
|||
_entity->setVelocity(zero);
|
||||
_entity->setAngularVelocity(zero);
|
||||
}
|
||||
_sentInactive = false;
|
||||
_numInactiveUpdates = 0;
|
||||
}
|
||||
|
||||
// remember properties for local server prediction
|
||||
|
@ -487,12 +501,12 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
|
|||
// we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID
|
||||
// but we remember that we do still own it... and rely on the server to tell us that we don't
|
||||
properties.clearSimulationOwner();
|
||||
_outgoingPriority = NO_PRORITY;
|
||||
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
||||
}
|
||||
// else the ownership is not changing so we don't bother to pack it
|
||||
} else {
|
||||
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
||||
properties.setSimulationOwner(sessionID, glm::max<quint8>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY));
|
||||
properties.setSimulationOwner(sessionID, glm::max<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY));
|
||||
_nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
}
|
||||
|
||||
|
@ -557,7 +571,7 @@ void EntityMotionState::clearIncomingDirtyFlags() {
|
|||
}
|
||||
|
||||
// virtual
|
||||
quint8 EntityMotionState::getSimulationPriority() const {
|
||||
uint8_t EntityMotionState::getSimulationPriority() const {
|
||||
return _entity->getSimulationPriority();
|
||||
}
|
||||
|
||||
|
@ -567,7 +581,7 @@ QUuid EntityMotionState::getSimulatorID() const {
|
|||
return _entity->getSimulatorID();
|
||||
}
|
||||
|
||||
void EntityMotionState::bump(quint8 priority) {
|
||||
void EntityMotionState::bump(uint8_t priority) {
|
||||
setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
|
||||
}
|
||||
|
||||
|
@ -600,7 +614,7 @@ void EntityMotionState::measureBodyAcceleration() {
|
|||
if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) {
|
||||
_loopsWithoutOwner = 0;
|
||||
_lastStep = ObjectMotionState::getWorldSimulationStep();
|
||||
_sentInactive = false;
|
||||
_numInactiveUpdates = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -630,6 +644,6 @@ void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma
|
|||
_entity->computeCollisionGroupAndFinalMask(group, mask);
|
||||
}
|
||||
|
||||
void EntityMotionState::setOutgoingPriority(quint8 priority) {
|
||||
_outgoingPriority = glm::max<quint8>(_outgoingPriority, priority);
|
||||
void EntityMotionState::setOutgoingPriority(uint8_t priority) {
|
||||
_outgoingPriority = glm::max<uint8_t>(_outgoingPriority, priority);
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
|
||||
void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; }
|
||||
void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; }
|
||||
quint8 getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; }
|
||||
uint8_t getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; }
|
||||
|
||||
virtual float getObjectRestitution() const override { return _entity->getRestitution(); }
|
||||
virtual float getObjectFriction() const override { return _entity->getFriction(); }
|
||||
|
@ -69,9 +69,9 @@ public:
|
|||
|
||||
virtual const QUuid getObjectID() const override { return _entity->getID(); }
|
||||
|
||||
virtual quint8 getSimulationPriority() const override;
|
||||
virtual uint8_t getSimulationPriority() const override;
|
||||
virtual QUuid getSimulatorID() const override;
|
||||
virtual void bump(quint8 priority) override;
|
||||
virtual void bump(uint8_t priority) override;
|
||||
|
||||
EntityItemPointer getEntity() const { return _entityPtr.lock(); }
|
||||
|
||||
|
@ -83,7 +83,7 @@ public:
|
|||
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override;
|
||||
|
||||
// eternal logic can suggest a simuator priority bid for the next outgoing update
|
||||
void setOutgoingPriority(quint8 priority);
|
||||
void setOutgoingPriority(uint8_t priority);
|
||||
|
||||
friend class PhysicalEntitySimulation;
|
||||
|
||||
|
@ -106,10 +106,6 @@ protected:
|
|||
// Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid.
|
||||
EntityItem* _entity;
|
||||
|
||||
bool _triedToReleaseOwnership; // true if we tried to release ownership
|
||||
|
||||
// these are for the prediction of the remote server's simple extrapolation
|
||||
uint32_t _lastStep; // last step of server extrapolation
|
||||
glm::vec3 _serverPosition; // in simulation-frame (not world-frame)
|
||||
glm::quat _serverRotation;
|
||||
glm::vec3 _serverVelocity;
|
||||
|
@ -118,15 +114,18 @@ protected:
|
|||
glm::vec3 _serverAcceleration;
|
||||
QByteArray _serverActionData;
|
||||
|
||||
uint32_t _lastMeasureStep;
|
||||
glm::vec3 _lastVelocity;
|
||||
glm::vec3 _measuredAcceleration;
|
||||
float _measuredDeltaTime;
|
||||
|
||||
quint8 _accelerationNearlyGravityCount;
|
||||
quint64 _nextOwnershipBid { 0 };
|
||||
quint64 _orphanExpiry { 0 };
|
||||
quint8 _outgoingPriority = NO_PRORITY;
|
||||
|
||||
float _measuredDeltaTime;
|
||||
uint32_t _lastMeasureStep;
|
||||
uint32_t _lastStep; // last step of server extrapolation
|
||||
|
||||
uint8_t _loopsWithoutOwner;
|
||||
uint8_t _accelerationNearlyGravityCount;
|
||||
uint8_t _numInactiveUpdates { 1 };
|
||||
uint8_t _outgoingPriority { ZERO_SIMULATION_PRIORITY };
|
||||
};
|
||||
|
||||
#endif // hifi_EntityMotionState_h
|
||||
|
|
Loading…
Reference in a new issue