merge andrew's sim-ownership branch

This commit is contained in:
Seth Alves 2015-06-29 09:50:38 -07:00
commit 1d122060a6
23 changed files with 612 additions and 226 deletions

View file

@ -148,10 +148,6 @@ QUuid AvatarMotionState::getSimulatorID() const {
return _avatar->getSessionUUID(); return _avatar->getSessionUUID();
} }
// virtual
void AvatarMotionState::bump() {
}
// virtual // virtual
int16_t AvatarMotionState::computeCollisionGroup() { int16_t AvatarMotionState::computeCollisionGroup() {
return COLLISION_GROUP_OTHER_AVATAR; return COLLISION_GROUP_OTHER_AVATAR;

View file

@ -55,7 +55,6 @@ public:
virtual const QUuid& getObjectID() const; virtual const QUuid& getObjectID() const;
virtual QUuid getSimulatorID() const; virtual QUuid getSimulatorID() const;
virtual void bump();
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal); void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "EntityItem.h"
#include <QtCore/QObject> #include <QtCore/QObject>
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
@ -22,12 +24,15 @@
#include <SoundCache.h> #include <SoundCache.h>
#include "EntityScriptingInterface.h" #include "EntityScriptingInterface.h"
#include "EntityItem.h"
#include "EntitiesLogging.h" #include "EntitiesLogging.h"
#include "EntityTree.h" #include "EntityTree.h"
#include "EntitySimulation.h" #include "EntitySimulation.h"
#include "EntityActionFactoryInterface.h" #include "EntityActionFactoryInterface.h"
const quint64 DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND);
const quint64 MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD = 2 * USECS_PER_SECOND;
bool EntityItem::_sendPhysicsUpdates = true; bool EntityItem::_sendPhysicsUpdates = true;
int EntityItem::_maxActionDataSize = 800; int EntityItem::_maxActionDataSize = 800;
@ -66,8 +71,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
_collisionsWillMove(ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE), _collisionsWillMove(ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE),
_locked(ENTITY_ITEM_DEFAULT_LOCKED), _locked(ENTITY_ITEM_DEFAULT_LOCKED),
_userData(ENTITY_ITEM_DEFAULT_USER_DATA), _userData(ENTITY_ITEM_DEFAULT_USER_DATA),
_simulatorID(ENTITY_ITEM_DEFAULT_SIMULATOR_ID), _simulationOwner(),
_simulatorIDChangedTime(0),
_marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), _marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID),
_name(ENTITY_ITEM_DEFAULT_NAME), _name(ENTITY_ITEM_DEFAULT_NAME),
_href(""), _href(""),
@ -105,13 +109,16 @@ EntityItem::~EntityItem() {
EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties; EntityPropertyFlags requestedProperties;
requestedProperties += PROP_SIMULATION_OWNER;
requestedProperties += PROP_POSITION; requestedProperties += PROP_POSITION;
requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete
requestedProperties += PROP_ROTATION; requestedProperties += PROP_ROTATION;
requestedProperties += PROP_DENSITY;
requestedProperties += PROP_VELOCITY; requestedProperties += PROP_VELOCITY;
requestedProperties += PROP_GRAVITY; requestedProperties += PROP_ANGULAR_VELOCITY;
requestedProperties += PROP_ACCELERATION; requestedProperties += PROP_ACCELERATION;
requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete
requestedProperties += PROP_DENSITY;
requestedProperties += PROP_GRAVITY;
requestedProperties += PROP_DAMPING; requestedProperties += PROP_DAMPING;
requestedProperties += PROP_RESTITUTION; requestedProperties += PROP_RESTITUTION;
requestedProperties += PROP_FRICTION; requestedProperties += PROP_FRICTION;
@ -120,7 +127,6 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_SCRIPT_TIMESTAMP; requestedProperties += PROP_SCRIPT_TIMESTAMP;
requestedProperties += PROP_COLLISION_SOUND_URL; requestedProperties += PROP_COLLISION_SOUND_URL;
requestedProperties += PROP_REGISTRATION_POINT; requestedProperties += PROP_REGISTRATION_POINT;
requestedProperties += PROP_ANGULAR_VELOCITY;
requestedProperties += PROP_ANGULAR_DAMPING; requestedProperties += PROP_ANGULAR_DAMPING;
requestedProperties += PROP_VISIBLE; requestedProperties += PROP_VISIBLE;
requestedProperties += PROP_IGNORE_FOR_COLLISIONS; requestedProperties += PROP_IGNORE_FOR_COLLISIONS;
@ -129,7 +135,6 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_USER_DATA; requestedProperties += PROP_USER_DATA;
requestedProperties += PROP_MARKETPLACE_ID; requestedProperties += PROP_MARKETPLACE_ID;
requestedProperties += PROP_NAME; requestedProperties += PROP_NAME;
requestedProperties += PROP_SIMULATOR_ID;
requestedProperties += PROP_HREF; requestedProperties += PROP_HREF;
requestedProperties += PROP_DESCRIPTION; requestedProperties += PROP_DESCRIPTION;
requestedProperties += PROP_ACTION_DATA; requestedProperties += PROP_ACTION_DATA;
@ -238,13 +243,16 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
// PROP_PAGED_PROPERTY, // PROP_PAGED_PROPERTY,
// PROP_CUSTOM_PROPERTIES_INCLUDED, // PROP_CUSTOM_PROPERTIES_INCLUDED,
APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, _simulationOwner.toByteArray());
APPEND_ENTITY_PROPERTY(PROP_POSITION, getPosition()); APPEND_ENTITY_PROPERTY(PROP_POSITION, getPosition());
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions()); // NOTE: PROP_RADIUS obsolete
APPEND_ENTITY_PROPERTY(PROP_ROTATION, getRotation()); APPEND_ENTITY_PROPERTY(PROP_ROTATION, getRotation());
APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity());
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, getVelocity()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY, getVelocity());
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, getGravity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getAngularVelocity());
APPEND_ENTITY_PROPERTY(PROP_ACCELERATION, getAcceleration()); APPEND_ENTITY_PROPERTY(PROP_ACCELERATION, getAcceleration());
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions()); // NOTE: PROP_RADIUS obsolete
APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity());
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, getGravity());
APPEND_ENTITY_PROPERTY(PROP_DAMPING, getDamping()); APPEND_ENTITY_PROPERTY(PROP_DAMPING, getDamping());
APPEND_ENTITY_PROPERTY(PROP_RESTITUTION, getRestitution()); APPEND_ENTITY_PROPERTY(PROP_RESTITUTION, getRestitution());
APPEND_ENTITY_PROPERTY(PROP_FRICTION, getFriction()); APPEND_ENTITY_PROPERTY(PROP_FRICTION, getFriction());
@ -252,14 +260,12 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, getScript()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT, getScript());
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, getScriptTimestamp()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, getScriptTimestamp());
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getAngularVelocity());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping());
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, getVisible()); APPEND_ENTITY_PROPERTY(PROP_VISIBLE, getVisible());
APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, getIgnoreForCollisions()); APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, getIgnoreForCollisions());
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, getCollisionsWillMove()); APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, getCollisionsWillMove());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData());
APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, getSimulatorID());
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID());
APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_NAME, getName());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
@ -329,8 +335,8 @@ int EntityItem::expectedBytes() {
} }
// clients use this method to unpack FULL updates from entity-server
int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) { if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) {
// NOTE: This shouldn't happen. The only versions of the bit stream that didn't support split mtu buffers should // NOTE: This shouldn't happen. The only versions of the bit stream that didn't support split mtu buffers should
@ -354,14 +360,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
return 0; return 0;
} }
// if this bitstream indicates that this node is the simulation owner, ignore any physics-related updates.
glm::vec3 savePosition = getPosition();
glm::quat saveRotation = getRotation();
glm::vec3 saveVelocity = _velocity;
glm::vec3 saveAngularVelocity = _angularVelocity;
int originalLength = bytesLeftToRead; int originalLength = bytesLeftToRead;
QByteArray originalDataBuffer((const char*)data, originalLength); // TODO: figure out a way to avoid the big deep copy below.
QByteArray originalDataBuffer((const char*)data, originalLength); // big deep copy!
int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
@ -539,40 +540,70 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
EntityPropertyFlags propertyFlags = encodedPropertyFlags; EntityPropertyFlags propertyFlags = encodedPropertyFlags;
dataAt += propertyFlags.getEncodedLength(); dataAt += propertyFlags.getEncodedLength();
bytesRead += propertyFlags.getEncodedLength(); bytesRead += propertyFlags.getEncodedLength();
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
// Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_SIMULATION_OWNER) {
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) { // pack SimulationOwner and terse update properties near each other
if (propertyFlags.getHasProperty(PROP_RADIUS)) {
float fromBuffer; // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); // even when we would otherwise ignore the rest of the packet.
dataAt += sizeof(fromBuffer);
bytesRead += sizeof(fromBuffer); if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) {
if (overwriteLocalData) { QByteArray simOwnerData;
setRadius(fromBuffer); int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData);
SimulationOwner newSimOwner;
newSimOwner.fromByteArray(simOwnerData);
dataAt += bytes;
bytesRead += bytes;
if (_simulationOwner.set(newSimOwner)) {
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
} }
} }
} else { { // When we own the simulation we don't accept updates to the entity's transform/velocities
// but since we're using macros below we have to temporarily modify overwriteLocalData.
auto nodeList = DependencyManager::get<NodeList>();
bool weOwnIt = _simulationOwner.matchesValidID(nodeList->getSessionUUID());
bool oldOverwrite = overwriteLocalData;
overwriteLocalData = overwriteLocalData && !weOwnIt;
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
overwriteLocalData = oldOverwrite;
}
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions); READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
} READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity); READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity); READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity); READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) { READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
} else {
// legacy order of packing here
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration); READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
} }
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
//READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityInDegrees);
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping); READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping);
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible); READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions); READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions);
@ -580,12 +611,14 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData); READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) { if (args.bitstreamVersion < VERSION_ENTITIES_HAVE_SIMULATION_OWNER) {
// this code for when there is only simulatorID and no simulation priority
// we always accept the server's notion of simulatorID, so we fake overwriteLocalData as true // we always accept the server's notion of simulatorID, so we fake overwriteLocalData as true
// before we try to READ_ENTITY_PROPERTY it // before we try to READ_ENTITY_PROPERTY it
bool temp = overwriteLocalData; bool temp = overwriteLocalData;
overwriteLocalData = true; overwriteLocalData = true;
READ_ENTITY_PROPERTY(PROP_SIMULATOR_ID, QUuid, updateSimulatorID); READ_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, QUuid, updateSimulatorID);
overwriteLocalData = temp; overwriteLocalData = temp;
} }
@ -628,7 +661,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
#ifdef WANT_DEBUG #ifdef WANT_DEBUG
qCDebug(entities) << "skipTimeForward:" << skipTimeForward; qCDebug(entities) << "skipTimeForward:" << skipTimeForward;
#endif #endif
// we want to extrapolate the motion forward to compensate for packet travel time, but // we want to extrapolate the motion forward to compensate for packet travel time, but
// we don't want the side effect of flag setting. // we don't want the side effect of flag setting.
simulateKinematicMotion(skipTimeForward, false); simulateKinematicMotion(skipTimeForward, false);
@ -638,15 +670,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
const QUuid& myNodeID = nodeList->getSessionUUID(); const QUuid& myNodeID = nodeList->getSessionUUID();
if (overwriteLocalData) { if (overwriteLocalData) {
if (_simulatorID == myNodeID && !_simulatorID.isNull()) { if (!_simulationOwner.matchesValidID(myNodeID)) {
// we own the simulation, so we keep our transform+velocities and remove any related dirty flags
// rather than accept the values in the packet
setPosition(savePosition);
setRotation(saveRotation);
_velocity = saveVelocity;
_angularVelocity = saveAngularVelocity;
_dirtyFlags &= ~(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES);
} else {
_lastSimulated = now; _lastSimulated = now;
} }
} }
@ -910,6 +935,7 @@ EntityItemProperties EntityItem::getProperties() const {
properties._type = getType(); properties._type = getType();
COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulationOwner, getSimulationOwner);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPosition); COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPosition);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensions); // NOTE: radius is obsolete COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensions); // NOTE: radius is obsolete
COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation); COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation);
@ -935,7 +961,6 @@ EntityItemProperties EntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove); COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked); COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData); COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulatorID, getSimulatorID);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName); COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref); COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
@ -966,6 +991,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
bool somethingChanged = false; bool somethingChanged = false;
// these affect TerseUpdate properties // these affect TerseUpdate properties
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePosition); SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePosition);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation); SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocity);
@ -987,7 +1013,6 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove); SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated); SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulatorID, updateSimulatorID);
// non-simulation properties below // non-simulation properties below
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
@ -1363,19 +1388,28 @@ void EntityItem::updateCreated(uint64_t value) {
} }
} }
void EntityItem::setSimulatorID(const QUuid& value) { void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) {
_simulatorID = value; _simulationOwner.set(id, priority);
_simulatorIDChangedTime = usecTimestampNow(); }
void EntityItem::setSimulationOwner(const SimulationOwner& owner) {
_simulationOwner.set(owner);
} }
void EntityItem::updateSimulatorID(const QUuid& value) { void EntityItem::updateSimulatorID(const QUuid& value) {
if (_simulatorID != value) { if (_simulationOwner.setID(value)) {
_simulatorID = value;
_simulatorIDChangedTime = usecTimestampNow();
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
} }
} }
void EntityItem::clearSimulationOwnership() {
_simulationOwner.clear();
// don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership()
// is only ever called entity-server-side and the flags are only used client-side
//_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
}
bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) { bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
assert(action); assert(action);
auto actionOwnerEntity = action->getOwnerEntity().lock(); auto actionOwnerEntity = action->getOwnerEntity().lock();

View file

@ -29,6 +29,7 @@
#include "EntityItemProperties.h" #include "EntityItemProperties.h"
#include "EntityItemPropertiesDefaults.h" #include "EntityItemPropertiesDefaults.h"
#include "EntityTypes.h" #include "EntityTypes.h"
#include "SimulationOwner.h"
class EntitySimulation; class EntitySimulation;
class EntityTreeElement; class EntityTreeElement;
@ -60,7 +61,6 @@ const float ACTIVATION_LINEAR_VELOCITY_DELTA = 0.01f;
const float ACTIVATION_GRAVITY_DELTA = 0.1f; const float ACTIVATION_GRAVITY_DELTA = 0.1f;
const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f; const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f;
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { }; #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { };
@ -68,7 +68,6 @@ const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f;
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10)) #define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
#define debugTreeVector(V) V << "[" << V << " in meters ]" #define debugTreeVector(V) V << "[" << V << " in meters ]"
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate /// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
/// one directly, instead you must only construct one of it's derived classes with additional features. /// one directly, instead you must only construct one of it's derived classes with additional features.
@ -92,8 +91,9 @@ public:
DIRTY_LIFETIME = 0x0100, DIRTY_LIFETIME = 0x0100,
DIRTY_UPDATEABLE = 0x0200, DIRTY_UPDATEABLE = 0x0200,
DIRTY_MATERIAL = 0x00400, DIRTY_MATERIAL = 0x00400,
DIRTY_PHYSICS_ACTIVATION = 0x0800, // we want to activate the object DIRTY_PHYSICS_ACTIVATION = 0x0800, // should activate object in physics engine
DIRTY_SIMULATOR_ID = 0x1000, DIRTY_SIMULATOR_OWNERSHIP = 0x1000, // should claim simulator ownership
DIRTY_SIMULATOR_ID = 0x2000, // the simulatorID has changed
DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION, DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION,
DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY
}; };
@ -317,11 +317,16 @@ public:
const QString& getUserData() const { return _userData; } const QString& getUserData() const { return _userData; }
void setUserData(const QString& value) { _userData = value; } void setUserData(const QString& value) { _userData = value; }
QUuid getSimulatorID() const { return _simulatorID; } const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
void setSimulatorID(const QUuid& value); void setSimulationOwner(const QUuid& id, quint8 priority);
void setSimulationOwner(const SimulationOwner& owner);
void promoteSimulationPriority(quint8 priority);
quint8 getSimulationPriority() const { return _simulationOwner.getPriority(); }
QUuid getSimulatorID() const { return _simulationOwner.getID(); }
void updateSimulatorID(const QUuid& value); void updateSimulatorID(const QUuid& value);
quint64 getSimulatorIDChangedTime() const { return _simulatorIDChangedTime; } void clearSimulationOwnership();
const QString& getMarketplaceID() const { return _marketplaceID; } const QString& getMarketplaceID() const { return _marketplaceID; }
void setMarketplaceID(const QString& value) { _marketplaceID = value; } void setMarketplaceID(const QString& value) { _marketplaceID = value; }
@ -358,7 +363,7 @@ public:
virtual void updateShapeType(ShapeType type) { /* do nothing */ } virtual void updateShapeType(ShapeType type) { /* do nothing */ }
uint32_t getDirtyFlags() const { return _dirtyFlags; } uint32_t getDirtyFlags() const { return _dirtyFlags; }
void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; } void clearDirtyFlags(uint32_t mask = 0xffffffff) { _dirtyFlags &= ~mask; }
bool isMoving() const; bool isMoving() const;
@ -379,6 +384,8 @@ public:
void getAllTerseUpdateProperties(EntityItemProperties& properties) const; void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
void flagForOwnership() { _dirtyFlags |= DIRTY_SIMULATOR_OWNERSHIP; }
bool addAction(EntitySimulation* simulation, EntityActionPointer action); bool addAction(EntitySimulation* simulation, EntityActionPointer action);
bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments); bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments);
bool removeAction(EntitySimulation* simulation, const QUuid& actionID); bool removeAction(EntitySimulation* simulation, const QUuid& actionID);
@ -431,8 +438,7 @@ protected:
bool _collisionsWillMove; bool _collisionsWillMove;
bool _locked; bool _locked;
QString _userData; QString _userData;
QUuid _simulatorID; // id of Node which is currently responsible for simulating this Entity SimulationOwner _simulationOwner;
quint64 _simulatorIDChangedTime; // when was _simulatorID last updated?
QString _marketplaceID; QString _marketplaceID;
QString _name; QString _name;
QString _href; //Hyperlink href QString _href; //Hyperlink href

View file

@ -38,7 +38,7 @@ EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM -
EntityItemProperties::EntityItemProperties() : EntityItemProperties::EntityItemProperties() :
CONSTRUCT_PROPERTY(visible, ENTITY_ITEM_DEFAULT_VISIBLE), CONSTRUCT_PROPERTY(visible, ENTITY_ITEM_DEFAULT_VISIBLE),
CONSTRUCT_PROPERTY(position, 0), CONSTRUCT_PROPERTY(position, 0.0f),
CONSTRUCT_PROPERTY(dimensions, ENTITY_ITEM_DEFAULT_DIMENSIONS), CONSTRUCT_PROPERTY(dimensions, ENTITY_ITEM_DEFAULT_DIMENSIONS),
CONSTRUCT_PROPERTY(rotation, ENTITY_ITEM_DEFAULT_ROTATION), CONSTRUCT_PROPERTY(rotation, ENTITY_ITEM_DEFAULT_ROTATION),
CONSTRUCT_PROPERTY(density, ENTITY_ITEM_DEFAULT_DENSITY), CONSTRUCT_PROPERTY(density, ENTITY_ITEM_DEFAULT_DENSITY),
@ -73,7 +73,7 @@ CONSTRUCT_PROPERTY(locked, ENTITY_ITEM_DEFAULT_LOCKED),
CONSTRUCT_PROPERTY(textures, ""), CONSTRUCT_PROPERTY(textures, ""),
CONSTRUCT_PROPERTY(animationSettings, ""), CONSTRUCT_PROPERTY(animationSettings, ""),
CONSTRUCT_PROPERTY(userData, ENTITY_ITEM_DEFAULT_USER_DATA), CONSTRUCT_PROPERTY(userData, ENTITY_ITEM_DEFAULT_USER_DATA),
CONSTRUCT_PROPERTY(simulatorID, ENTITY_ITEM_DEFAULT_SIMULATOR_ID), CONSTRUCT_PROPERTY(simulationOwner, SimulationOwner()),
CONSTRUCT_PROPERTY(text, TextEntityItem::DEFAULT_TEXT), CONSTRUCT_PROPERTY(text, TextEntityItem::DEFAULT_TEXT),
CONSTRUCT_PROPERTY(lineHeight, TextEntityItem::DEFAULT_LINE_HEIGHT), CONSTRUCT_PROPERTY(lineHeight, TextEntityItem::DEFAULT_LINE_HEIGHT),
CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR), CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR),
@ -288,8 +288,8 @@ void EntityItemProperties::setBackgroundModeFromString(const QString& background
EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
EntityPropertyFlags changedProperties; EntityPropertyFlags changedProperties;
CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions);
CHECK_PROPERTY_CHANGE(PROP_POSITION, position); CHECK_PROPERTY_CHANGE(PROP_POSITION, position);
CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions);
CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation); CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation);
CHECK_PROPERTY_CHANGE(PROP_DENSITY, density); CHECK_PROPERTY_CHANGE(PROP_DENSITY, density);
CHECK_PROPERTY_CHANGE(PROP_VELOCITY, velocity); CHECK_PROPERTY_CHANGE(PROP_VELOCITY, velocity);
@ -323,7 +323,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked); CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked);
CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures); CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures);
CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData); CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData);
CHECK_PROPERTY_CHANGE(PROP_SIMULATOR_ID, simulatorID); CHECK_PROPERTY_CHANGE(PROP_SIMULATION_OWNER, simulationOwner);
CHECK_PROPERTY_CHANGE(PROP_TEXT, text); CHECK_PROPERTY_CHANGE(PROP_TEXT, text);
CHECK_PROPERTY_CHANGE(PROP_LINE_HEIGHT, lineHeight); CHECK_PROPERTY_CHANGE(PROP_LINE_HEIGHT, lineHeight);
CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor); CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor);
@ -419,7 +419,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(locked); COPY_PROPERTY_TO_QSCRIPTVALUE(locked);
COPY_PROPERTY_TO_QSCRIPTVALUE(textures); COPY_PROPERTY_TO_QSCRIPTVALUE(textures);
COPY_PROPERTY_TO_QSCRIPTVALUE(userData); COPY_PROPERTY_TO_QSCRIPTVALUE(userData);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(simulatorID, getSimulatorIDAsString()); //COPY_PROPERTY_TO_QSCRIPTVALUE(simulationOwner); // TODO: expose this for JSON saves?
COPY_PROPERTY_TO_QSCRIPTVALUE(text); COPY_PROPERTY_TO_QSCRIPTVALUE(text);
COPY_PROPERTY_TO_QSCRIPTVALUE(lineHeight); COPY_PROPERTY_TO_QSCRIPTVALUE(lineHeight);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(textColor, getTextColor()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(textColor, getTextColor());
@ -572,7 +572,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
auto result = QDateTime::fromMSecsSinceEpoch(_created / 1000, Qt::UTC); // usec per msec auto result = QDateTime::fromMSecsSinceEpoch(_created / 1000, Qt::UTC); // usec per msec
return result; return result;
}); });
COPY_PROPERTY_FROM_QSCRIPTVALUE(simulatorID, QUuid, setSimulatorID); // TODO: expose this to QScriptValue for JSON saves?
//COPY_PROPERTY_FROM_QSCRIPTVALUE(simulationOwner, ???, setSimulatorPriority);
} }
_stage.copyFromScriptValue(object, _defaultSettings); _stage.copyFromScriptValue(object, _defaultSettings);
@ -707,6 +708,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
// PROP_PAGED_PROPERTY, // PROP_PAGED_PROPERTY,
// PROP_CUSTOM_PROPERTIES_INCLUDED, // PROP_CUSTOM_PROPERTIES_INCLUDED,
APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, properties._simulationOwner.toByteArray());
APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition()); APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition());
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete
APPEND_ENTITY_PROPERTY(PROP_ROTATION, properties.getRotation()); APPEND_ENTITY_PROPERTY(PROP_ROTATION, properties.getRotation());
@ -729,7 +731,6 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, properties.getCollisionsWillMove()); APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, properties.getCollisionsWillMove());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData());
APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, properties.getSimulatorID());
APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref()); APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref());
APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription());
@ -962,7 +963,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
EntityPropertyFlags propertyFlags = encodedPropertyFlags; EntityPropertyFlags propertyFlags = encodedPropertyFlags;
dataAt += propertyFlags.getEncodedLength(); dataAt += propertyFlags.getEncodedLength();
processedBytes += propertyFlags.getEncodedLength(); processedBytes += propertyFlags.getEncodedLength();
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATION_OWNER, QByteArray, setSimulationOwner);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation);
@ -985,7 +987,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONS_WILL_MOVE, bool, setCollisionsWillMove); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONS_WILL_MOVE, bool, setCollisionsWillMove);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATOR_ID, QUuid, setSimulatorID);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription);
@ -1102,6 +1103,7 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
} }
void EntityItemProperties::markAllChanged() { void EntityItemProperties::markAllChanged() {
_simulationOwnerChanged = true;
_positionChanged = true; _positionChanged = true;
_dimensionsChanged = true; _dimensionsChanged = true;
_rotationChanged = true; _rotationChanged = true;
@ -1114,7 +1116,6 @@ void EntityItemProperties::markAllChanged() {
_frictionChanged = true; _frictionChanged = true;
_lifetimeChanged = true; _lifetimeChanged = true;
_userDataChanged = true; _userDataChanged = true;
_simulatorIDChanged = true;
_scriptChanged = true; _scriptChanged = true;
_scriptTimestampChanged = true; _scriptTimestampChanged = true;
_collisionSoundURLChanged = true; _collisionSoundURLChanged = true;
@ -1230,3 +1231,27 @@ bool EntityItemProperties::hasTerseUpdateChanges() const {
// a TerseUpdate includes the transform and its derivatives // a TerseUpdate includes the transform and its derivatives
return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged; return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged;
} }
bool EntityItemProperties::hasMiscPhysicsChanges() const {
return _gravityChanged || _dimensionsChanged || _densityChanged || _frictionChanged
|| _restitutionChanged || _dampingChanged || _angularDampingChanged || _registrationPointChanged ||
_compoundShapeURLChanged || _collisionsWillMoveChanged || _ignoreForCollisionsChanged;
}
void EntityItemProperties::clearSimulationOwner() {
_simulationOwner.clear();
_simulationOwnerChanged = true;
}
void EntityItemProperties::setSimulationOwner(const QUuid& id, uint8_t priority) {
if (!_simulationOwner.matchesValidID(id) || _simulationOwner.getPriority() != priority) {
_simulationOwner.set(id, priority);
_simulationOwnerChanged = true;
}
}
void EntityItemProperties::setSimulationOwner(const QByteArray& data) {
if (_simulationOwner.fromByteArray(data)) {
_simulationOwnerChanged = true;
}
}

View file

@ -34,6 +34,7 @@
#include "EntityItemPropertiesMacros.h" #include "EntityItemPropertiesMacros.h"
#include "EntityTypes.h" #include "EntityTypes.h"
#include "EntityPropertyFlags.h" #include "EntityPropertyFlags.h"
#include "SimulationOwner.h"
#include "SkyboxPropertyGroup.h" #include "SkyboxPropertyGroup.h"
#include "StagePropertyGroup.h" #include "StagePropertyGroup.h"
@ -120,7 +121,7 @@ public:
DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString); DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString);
DEFINE_PROPERTY_REF_WITH_SETTER_AND_GETTER(PROP_ANIMATION_SETTINGS, AnimationSettings, animationSettings, QString); DEFINE_PROPERTY_REF_WITH_SETTER_AND_GETTER(PROP_ANIMATION_SETTINGS, AnimationSettings, animationSettings, QString);
DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString); DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString);
DEFINE_PROPERTY_REF(PROP_SIMULATOR_ID, SimulatorID, simulatorID, QUuid); DEFINE_PROPERTY_REF(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner);
DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString); DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString);
DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float); DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float);
DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor); DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor);
@ -197,7 +198,7 @@ public:
const QStringList& getTextureNames() const { return _textureNames; } const QStringList& getTextureNames() const { return _textureNames; }
void setTextureNames(const QStringList& value) { _textureNames = value; } void setTextureNames(const QStringList& value) { _textureNames = value; }
QString getSimulatorIDAsString() const { return _simulatorID.toString().mid(1,36).toUpper(); } QString getSimulatorIDAsString() const { return _simulationOwner.getID().toString().mid(1,36).toUpper(); }
void setVoxelDataDirty() { _voxelDataChanged = true; } void setVoxelDataDirty() { _voxelDataChanged = true; }
@ -206,6 +207,12 @@ public:
void setCreated(QDateTime& v); void setCreated(QDateTime& v);
bool hasTerseUpdateChanges() const; bool hasTerseUpdateChanges() const;
bool hasMiscPhysicsChanges() const;
void clearSimulationOwner();
void setSimulationOwner(const QUuid& id, uint8_t priority);
void setSimulationOwner(const QByteArray& data);
void promoteSimulationPriority(quint8 priority) { _simulationOwner.promotePriority(priority); }
void setActionDataDirty() { _actionDataChanged = true; } void setActionDataDirty() { _actionDataChanged = true; }
@ -287,7 +294,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulatorID, simulatorID, QUuid()); DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulationOwner, simulationOwner, SimulationOwner());
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, "");

View file

@ -104,7 +104,7 @@ enum EntityPropertyList {
PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities
PROP_MARKETPLACE_ID, // all entities PROP_MARKETPLACE_ID, // all entities
PROP_ACCELERATION, // all entities PROP_ACCELERATION, // all entities
PROP_SIMULATOR_ID, // all entities PROP_SIMULATION_OWNER, // formerly known as PROP_SIMULATOR_ID
PROP_NAME, // all entities PROP_NAME, // all entities
PROP_COLLISION_SOUND_URL, PROP_COLLISION_SOUND_URL,
PROP_RESTITUTION, PROP_RESTITUTION,

View file

@ -9,18 +9,20 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "EntityScriptingInterface.h"
#include <VariantMapToScriptValue.h> #include <VariantMapToScriptValue.h>
#include "EntitiesLogging.h"
#include "EntityActionFactoryInterface.h"
#include "EntityActionInterface.h"
#include "EntitySimulation.h"
#include "EntityTree.h" #include "EntityTree.h"
#include "LightEntityItem.h" #include "LightEntityItem.h"
#include "ModelEntityItem.h" #include "ModelEntityItem.h"
#include "SimulationOwner.h"
#include "ZoneEntityItem.h" #include "ZoneEntityItem.h"
#include "EntitiesLogging.h"
#include "EntitySimulation.h"
#include "EntityActionInterface.h"
#include "EntityActionFactoryInterface.h"
#include "EntityScriptingInterface.h"
EntityScriptingInterface::EntityScriptingInterface() : EntityScriptingInterface::EntityScriptingInterface() :
_entityTree(NULL) _entityTree(NULL)
@ -61,16 +63,6 @@ void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) {
} }
} }
void bidForSimulationOwnership(EntityItemProperties& properties) {
// We make a bid for simulation ownership by declaring our sessionID as simulation owner
// in the outgoing properties. The EntityServer may accept the bid or might not.
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
properties.setSimulatorID(myNodeID);
}
QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) { QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {
EntityItemProperties propertiesWithSimID = properties; EntityItemProperties propertiesWithSimID = properties;
@ -83,11 +75,15 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
_entityTree->lockForWrite(); _entityTree->lockForWrite();
EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID); EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
if (entity) { if (entity) {
entity->setLastBroadcast(usecTimestampNow());
// This Node is creating a new object. If it's in motion, set this Node as the simulator. // This Node is creating a new object. If it's in motion, set this Node as the simulator.
bidForSimulationOwnership(propertiesWithSimID); auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
// and make note of it now, so we can act on it right away. // and make note of it now, so we can act on it right away.
entity->setSimulatorID(propertiesWithSimID.getSimulatorID()); entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
entity->setLastBroadcast(usecTimestampNow());
} else { } else {
qCDebug(entities) << "script failed to add new Entity to local Octree"; qCDebug(entities) << "script failed to add new Entity to local Octree";
success = false; success = false;
@ -146,23 +142,35 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties proper
if (entity) { if (entity) {
// make sure the properties has a type, so that the encode can know which properties to include // make sure the properties has a type, so that the encode can know which properties to include
properties.setType(entity->getType()); properties.setType(entity->getType());
if (properties.hasTerseUpdateChanges()) { bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges();
bool hasPhysicsChanges = properties.hasMiscPhysicsChanges() || hasTerseUpdateChanges;
if (hasPhysicsChanges) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID(); const QUuid myNodeID = nodeList->getSessionUUID();
if (entity->getSimulatorID() == myNodeID) { if (entity->getSimulatorID() == myNodeID) {
// we think we already own the simulation, so make sure to send ALL TerseUpdate properties // we think we already own the simulation, so make sure to send ALL TerseUpdate properties
entity->getAllTerseUpdateProperties(properties); if (hasTerseUpdateChanges) {
// TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object entity->getAllTerseUpdateProperties(properties);
// is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update }
// TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object
// is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update
// and instead let the physics simulation decide when to send a terse update. This would remove // and instead let the physics simulation decide when to send a terse update. This would remove
// the "slide-no-rotate" glitch (and typical a double-update) that we see during the "poke rolling // the "slide-no-rotate" glitch (and typical a double-update) that we see during the "poke rolling
// balls" test. However, even if we solve this problem we still need to provide a "slerp the visible // balls" test. However, even if we solve this problem we still need to provide a "slerp the visible
// proxy toward the true physical position" feature to hide the final glitches in the remote watcher's // proxy toward the true physical position" feature to hide the final glitches in the remote watcher's
// simulation. // simulation.
if (entity->getSimulationPriority() < SCRIPT_EDIT_SIMULATION_PRIORITY) {
// we re-assert our simulation ownership at a higher priority
properties.setSimulationOwner(myNodeID,
glm::max(entity->getSimulationPriority(), SCRIPT_EDIT_SIMULATION_PRIORITY));
}
} else {
// we make a bid for simulation ownership
properties.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
entity->flagForOwnership();
} }
// we make a bid for (or assert existing) simulation ownership
properties.setSimulatorID(myNodeID);
} }
entity->setLastBroadcast(usecTimestampNow()); entity->setLastBroadcast(usecTimestampNow());
} }
@ -567,6 +575,11 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
return false; return false;
} }
if (actionFactory->factory(simulation, actionType, actionID, entity, arguments)) { if (actionFactory->factory(simulation, actionType, actionID, entity, arguments)) {
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
if (entity->getSimulatorID() != myNodeID) {
entity->flagForOwnership();
}
return true; return true;
} }
return false; return false;
@ -580,7 +593,15 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments) { bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments) {
return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
return entity->updateAction(simulation, actionID, arguments); bool success = entity->updateAction(simulation, actionID, arguments);
if (success) {
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
if (entity->getSimulatorID() != myNodeID) {
entity->flagForOwnership();
}
}
return success;
}); });
} }

View file

@ -150,23 +150,34 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
} else { } else {
if (getIsServer()) { if (getIsServer()) {
bool simulationBlocked = !entity->getSimulatorID().isNull(); bool simulationBlocked = !entity->getSimulatorID().isNull();
if (properties.simulatorIDChanged()) { if (properties.simulationOwnerChanged()) {
QUuid submittedID = properties.getSimulatorID(); QUuid submittedID = properties.getSimulationOwner().getID();
// a legit interface will only submit their own ID or NULL: // a legit interface will only submit their own ID or NULL:
if (submittedID.isNull()) { if (submittedID.isNull()) {
if (entity->getSimulatorID() == senderID) { if (entity->getSimulatorID() == senderID) {
// We only allow the simulation owner to clear their own simulationID's. // We only allow the simulation owner to clear their own simulationID's.
simulationBlocked = false; simulationBlocked = false;
properties.clearSimulationOwner(); // clear everything
} }
// else: We assume the sender really did believe it was the simulation owner when it sent // else: We assume the sender really did believe it was the simulation owner when it sent
} else if (submittedID == senderID) { } else if (submittedID == senderID) {
// the sender is trying to take or continue ownership // the sender is trying to take or continue ownership
if (entity->getSimulatorID().isNull() || entity->getSimulatorID() == senderID) { if (entity->getSimulatorID().isNull()) {
// the sender it taking ownership
properties.promoteSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
simulationBlocked = false;
} else if (entity->getSimulatorID() == senderID) {
// the sender is asserting ownership
simulationBlocked = false; simulationBlocked = false;
} else { } else {
// the sender is trying to steal ownership from another simulator // the sender is trying to steal ownership from another simulator
// so we apply the ownership change filter // so we apply the rules for ownership change:
if (usecTimestampNow() - entity->getSimulatorIDChangedTime() > SIMULATOR_CHANGE_LOCKOUT_PERIOD) { // (1) higher priority wins
// (2) equal priority wins if ownership filter has expired except...
uint8_t oldPriority = entity->getSimulationPriority();
uint8_t newPriority = properties.getSimulationOwner().getPriority();
if (newPriority > oldPriority ||
(newPriority == oldPriority && properties.getSimulationOwner().hasExpired())) {
simulationBlocked = false; simulationBlocked = false;
} }
} }
@ -174,12 +185,17 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
// the entire update is suspect --> ignore it // the entire update is suspect --> ignore it
return false; return false;
} }
} else {
simulationBlocked = senderID != entity->getSimulatorID();
} }
if (simulationBlocked) { if (simulationBlocked) {
// squash the physics-related changes. // squash ownership and physics-related changes.
properties.setSimulatorIDChanged(false); properties.setSimulationOwnerChanged(false);
properties.setPositionChanged(false); properties.setPositionChanged(false);
properties.setRotationChanged(false); properties.setRotationChanged(false);
properties.setVelocityChanged(false);
properties.setAngularVelocityChanged(false);
properties.setAccelerationChanged(false);
} }
} }
// else client accepts what the server says // else client accepts what the server says

View file

@ -23,19 +23,20 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
// has finished simulating it. // has finished simulating it.
auto nodeList = DependencyManager::get<LimitedNodeList>(); auto nodeList = DependencyManager::get<LimitedNodeList>();
SetOfEntities::iterator itemItr = _hasSimulationOwnerEntities.begin(); SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin();
while (itemItr != _hasSimulationOwnerEntities.end()) { while (itemItr != _entitiesWithSimulator.end()) {
EntityItemPointer entity = *itemItr; EntityItemPointer entity = *itemItr;
if (entity->getSimulatorID().isNull()) { if (entity->getSimulatorID().isNull()) {
itemItr = _hasSimulationOwnerEntities.erase(itemItr); itemItr = _entitiesWithSimulator.erase(itemItr);
} else if (now - entity->getLastChangedOnServer() >= AUTO_REMOVE_SIMULATION_OWNER_USEC) { } else if (now - entity->getLastChangedOnServer() >= AUTO_REMOVE_SIMULATION_OWNER_USEC) {
SharedNodePointer ownerNode = nodeList->nodeWithUUID(entity->getSimulatorID()); SharedNodePointer ownerNode = nodeList->nodeWithUUID(entity->getSimulatorID());
if (ownerNode.isNull() || !ownerNode->isAlive()) { if (ownerNode.isNull() || !ownerNode->isAlive()) {
qCDebug(entities) << "auto-removing simulation owner" << entity->getSimulatorID(); qCDebug(entities) << "auto-removing simulation owner" << entity->getSimulatorID();
entity->setSimulatorID(QUuid()); entity->setSimulatorID(QUuid());
itemItr = _hasSimulationOwnerEntities.erase(itemItr); itemItr = _hasSimulationOwnerEntities.erase(itemItr);
// TODO: zero velocities when we clear simulatorID?
entity->clearSimulationOwnership();
itemItr = _entitiesWithSimulator.erase(itemItr);
// zero the velocity on this entity so that it doesn't drift far away // zero the velocity on this entity so that it doesn't drift far away
entity->setVelocity(glm::vec3(0.0f)); entity->setVelocity(glm::vec3(0.0f));
} else { } else {
@ -50,23 +51,23 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) { void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
EntitySimulation::addEntityInternal(entity); EntitySimulation::addEntityInternal(entity);
if (!entity->getSimulatorID().isNull()) { if (!entity->getSimulatorID().isNull()) {
_hasSimulationOwnerEntities.insert(entity); _entitiesWithSimulator.insert(entity);
} }
} }
void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) { void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
_hasSimulationOwnerEntities.remove(entity); _entitiesWithSimulator.remove(entity);
} }
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) { void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
EntitySimulation::changeEntityInternal(entity); EntitySimulation::changeEntityInternal(entity);
if (!entity->getSimulatorID().isNull()) { if (!entity->getSimulatorID().isNull()) {
_hasSimulationOwnerEntities.insert(entity); _entitiesWithSimulator.insert(entity);
} }
entity->clearDirtyFlags(); entity->clearDirtyFlags();
} }
void SimpleEntitySimulation::clearEntitiesInternal() { void SimpleEntitySimulation::clearEntitiesInternal() {
_hasSimulationOwnerEntities.clear(); _entitiesWithSimulator.clear();
} }

View file

@ -28,7 +28,7 @@ protected:
virtual void changeEntityInternal(EntityItemPointer entity); virtual void changeEntityInternal(EntityItemPointer entity);
virtual void clearEntitiesInternal(); virtual void clearEntitiesInternal();
SetOfEntities _hasSimulationOwnerEntities; SetOfEntities _entitiesWithSimulator;
}; };
#endif // hifi_SimpleEntitySimulation_h #endif // hifi_SimpleEntitySimulation_h

View file

@ -0,0 +1,181 @@
//
// SimulationOwner.cpp
// libraries/entities/src
//
// Created by Andrew Meadows on 2015.06.19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "SimulationOwner.h"
#include <iostream> // included for tests
#include <assert.h>
#include <NumericalConstants.h>
// static
const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1;
SimulationOwner::SimulationOwner(const SimulationOwner& other)
: _id(other._id), _priority(other._priority), _expiry(other._expiry) {
}
QByteArray SimulationOwner::toByteArray() const {
QByteArray data = _id.toRfc4122();
data.append(_priority);
return data;
}
bool SimulationOwner::fromByteArray(const QByteArray& data) {
if (data.size() == NUM_BYTES_ENCODED) {
QByteArray idBytes = data.left(NUM_BYTES_RFC4122_UUID);
_id = QUuid::fromRfc4122(idBytes);
_priority = data[NUM_BYTES_RFC4122_UUID];
return true;
}
return false;
}
void SimulationOwner::clear() {
_id = QUuid();
_priority = 0;
_expiry = 0;
}
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) {
if (priority > _priority) {
_priority = priority;
updateExpiry();
}
}
bool SimulationOwner::setID(const QUuid& id) {
if (_id != id) {
_id = id;
updateExpiry();
if (_id.isNull()) {
_priority = 0;
}
return true;
}
return false;
}
bool SimulationOwner::set(const QUuid& id, quint8 priority) {
setPriority(priority);
return setID(id);
}
bool SimulationOwner::set(const SimulationOwner& owner) {
setPriority(owner._priority);
return setID(owner._id);
}
void SimulationOwner::updateExpiry() {
const quint64 OWNERSHIP_LOCKOUT_EXPIRY = USECS_PER_SECOND / 5;
_expiry = usecTimestampNow() + OWNERSHIP_LOCKOUT_EXPIRY;
}
// NOTE: eventually this code will be moved into unit tests
// static debug
void SimulationOwner::test() {
{ // test default constructor
SimulationOwner simOwner;
if (!simOwner.isNull()) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner should be NULL" << std::endl;
}
if (simOwner.getPriority() != 0) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : unexpeced SimulationOwner priority" << std::endl;
}
}
{ // test set constructor
QUuid id = QUuid::createUuid();
quint8 priority = 128;
SimulationOwner simOwner(id, priority);
if (simOwner.isNull()) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner should NOT be NULL" << std::endl;
}
if (simOwner.getID() != id) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner with unexpected id" << std::endl;
}
if (simOwner.getPriority() != priority) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : unexpeced SimulationOwner priority" << std::endl;
}
QUuid otherID = QUuid::createUuid();
if (simOwner.getID() == otherID) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner with unexpected id" << std::endl;
}
}
{ // test set()
QUuid id = QUuid::createUuid();
quint8 priority = 1;
SimulationOwner simOwner;
simOwner.set(id, priority);
if (simOwner.isNull()) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner should NOT be NULL" << std::endl;
}
if (simOwner.getID() != id) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner with unexpected id" << std::endl;
}
if (simOwner.getPriority() != priority) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : unexpeced SimulationOwner priority" << std::endl;
}
}
{ // test encode/decode
SimulationOwner ownerA(QUuid::createUuid(), 1);
SimulationOwner ownerB(QUuid::createUuid(), 2);
QByteArray data = ownerA.toByteArray();
ownerB.fromByteArray(data);
if (ownerA.getID() != ownerB.getID()) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : ownerA._id should be equal to ownerB._id" << std::endl;
}
}
}
bool SimulationOwner::operator!=(const SimulationOwner& other) {
return (_id != other._id && _priority != other._priority);
}
SimulationOwner& SimulationOwner::operator=(const SimulationOwner& other) {
_priority = other._priority;
if (_priority == 0) {
_id = QUuid();
_expiry = 0;
} else {
if (_id != other._id) {
updateExpiry();
}
_id = other._id;
}
return *this;
}
QDebug& operator<<(QDebug& d, const SimulationOwner& simOwner) {
d << "{ id : " << simOwner._id << ", priority : " << (int)simOwner._priority << " }";
return d;
}

View file

@ -0,0 +1,86 @@
//
// SimulationOwner.h
// libraries/entities/src
//
// Created by Andrew Meadows on 2015.06.19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_SimulationOwner_h
#define hifi_SimulationOwner_h
#include <QtCore/QDebug>
#include <QtCore/QByteArray>
#include <SharedUtil.h>
#include <UUID.h>
// 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
// 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;
// When poking objects with scripts an observer will bid at SCRIPT_EDIT priority.
const quint8 SCRIPT_EDIT_SIMULATION_PRIORITY = 0x80;
// PERSONAL priority (needs a better name) is the level at which a simulation observer will bid for
// objects that collide its MyAvatar.
const quint8 PERSONAL_SIMULATION_PRIORITY = SCRIPT_EDIT_SIMULATION_PRIORITY - 1;
class SimulationOwner {
public:
static const int NUM_BYTES_ENCODED;
SimulationOwner() : _id(), _priority(0), _expiry(0) {}
SimulationOwner(const QUuid& id, quint8 priority) : _id(id), _priority(priority), _expiry(0) {}
SimulationOwner(const SimulationOwner& other);
const QUuid& getID() const { return _id; }
quint8 getPriority() const { return _priority; }
const quint64& getExpiry() const { return _expiry; }
QByteArray toByteArray() const;
bool fromByteArray(const QByteArray& data);
void clear();
void setPriority(quint8 priority);
void promotePriority(quint8 priority);
// return true if id is changed
bool setID(const QUuid& id);
bool set(const QUuid& id, quint8 priority);
bool set(const SimulationOwner& owner);
bool isNull() const { return _id.isNull(); }
bool matchesValidID(const QUuid& id) const { return _id == id && !_id.isNull(); }
void updateExpiry();
bool hasExpired() const { return usecTimestampNow() > _expiry; }
bool operator>=(quint8 priority) const { return _priority >= priority; }
bool operator==(const SimulationOwner& other) { return (_id == other._id && _priority == other._priority); }
bool operator!=(const SimulationOwner& other);
SimulationOwner& operator=(const SimulationOwner& other);
friend QDebug& operator<<(QDebug& d, const SimulationOwner& simOwner);
// debug
static void test();
private:
QUuid _id;
quint8 _priority;
quint64 _expiry;
};
#endif // hifi_SimulationOwner_h

View file

@ -73,7 +73,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketTypeEntityAdd: case PacketTypeEntityAdd:
case PacketTypeEntityEdit: case PacketTypeEntityEdit:
case PacketTypeEntityData: case PacketTypeEntityData:
return VERSION_ACTIONS_OVER_WIRE; return VERSION_ENTITIES_HAVE_SIMULATION_OWNER;
case PacketTypeEntityErase: case PacketTypeEntityErase:
return 2; return 2;
case PacketTypeAudioStreamStats: case PacketTypeAudioStreamStats:

View file

@ -187,5 +187,6 @@ const PacketVersion VERSION_ENTITIES_FACE_CAMERA = 30;
const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP = 31; const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP = 31;
const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP_FIX = 32; const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP_FIX = 32;
const PacketVersion VERSION_ACTIONS_OVER_WIRE = 33; const PacketVersion VERSION_ACTIONS_OVER_WIRE = 33;
const PacketVersion VERSION_ENTITIES_HAVE_SIMULATION_OWNER = 34;
#endif // hifi_PacketHeaders_h #endif // hifi_PacketHeaders_h

View file

@ -247,7 +247,6 @@ public:
static int unpackDataFromBytes(const unsigned char* dataBytes, QVector<glm::vec3>& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector<glm::vec3>& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result);
private: private:
/// appends raw bytes, might fail if byte would cause packet to be too large /// appends raw bytes, might fail if byte would cause packet to be too large
bool append(const unsigned char* data, int length); bool append(const unsigned char* data, int length);

View file

@ -26,6 +26,9 @@
static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f;
static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4;
const uint32_t LOOPS_FOR_SIMULATION_ORPHAN = 50;
//const uint32_t LOOPS_BETWEEN_OWNERSHIP_BIDS = 30;
const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5;
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
bool EntityMotionState::entityTreeIsLocked() const { bool EntityMotionState::entityTreeIsLocked() const {
@ -58,8 +61,7 @@ bool entityTreeIsLocked() {
EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer entity) : EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer entity) :
ObjectMotionState(shape), ObjectMotionState(shape),
_entity(entity), _entity(entity),
_sentActive(false), _sentInactive(true),
_numNonMovingUpdates(0),
_lastStep(0), _lastStep(0),
_serverPosition(0.0f), _serverPosition(0.0f),
_serverRotation(), _serverRotation(),
@ -72,8 +74,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
_measuredAcceleration(glm::vec3(0.0f)), _measuredAcceleration(glm::vec3(0.0f)),
_measuredDeltaTime(0.0f), _measuredDeltaTime(0.0f),
_accelerationNearlyGravityCount(0), _accelerationNearlyGravityCount(0),
_candidateForOwnership(false), _nextOwnershipBid(0),
_loopsSinceOwnershipBid(0),
_loopsWithoutOwner(0) _loopsWithoutOwner(0)
{ {
_type = MOTIONSTATE_TYPE_ENTITY; _type = MOTIONSTATE_TYPE_ENTITY;
@ -97,28 +98,35 @@ void EntityMotionState::updateServerPhysicsVariables() {
} }
// virtual // virtual
void EntityMotionState::handleEasyChanges(uint32_t flags) { void EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) {
assert(entityTreeIsLocked()); assert(entityTreeIsLocked());
updateServerPhysicsVariables(); updateServerPhysicsVariables();
ObjectMotionState::handleEasyChanges(flags); ObjectMotionState::handleEasyChanges(flags, engine);
if (flags & EntityItem::DIRTY_SIMULATOR_ID) { if (flags & EntityItem::DIRTY_SIMULATOR_ID) {
_loopsWithoutOwner = 0; _loopsWithoutOwner = 0;
_candidateForOwnership = 0; if (_entity->getSimulatorID().isNull()) {
if (_entity->getSimulatorID().isNull() // simulation ownership is being removed
&& !_entity->isMoving()
&& _body->isActive()) {
// remove the ACTIVATION flag because this object is coming to rest // remove the ACTIVATION flag because this object is coming to rest
// according to a remote simulation and we don't want to wake it up again // according to a remote simulation and we don't want to wake it up again
flags &= ~EntityItem::DIRTY_PHYSICS_ACTIVATION; flags &= ~EntityItem::DIRTY_PHYSICS_ACTIVATION;
// hint to Bullet that the object is deactivating
_body->setActivationState(WANTS_DEACTIVATION); _body->setActivationState(WANTS_DEACTIVATION);
} else { _outgoingPriority = 0;
auto nodeList = DependencyManager::get<NodeList>(); } else {
const QUuid& sessionID = nodeList->getSessionUUID(); _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
if (_entity->getSimulatorID() != sessionID) { if (engine->getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() > _outgoingPriority) {
_loopsSinceOwnershipBid = 0; // we own the simulation or our priority looses to remote
_outgoingPriority = 0;
} }
} }
} }
if (flags & EntityItem::DIRTY_SIMULATOR_OWNERSHIP) {
// (DIRTY_SIMULATOR_OWNERSHIP really means "we should bid for ownership with SCRIPT priority")
// we're manipulating this object directly via script, so we artificially
// manipulate the logic to trigger an immediate bid for ownership
setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY);
}
if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) {
_body->activate(); _body->activate();
} }
@ -195,13 +203,10 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
if (_entity->getSimulatorID().isNull()) { if (_entity->getSimulatorID().isNull()) {
_loopsWithoutOwner++; _loopsWithoutOwner++;
const uint32_t OWNERSHIP_BID_DELAY = 50; if (_loopsWithoutOwner > LOOPS_FOR_SIMULATION_ORPHAN && usecTimestampNow() > _nextOwnershipBid) {
if (_loopsWithoutOwner > OWNERSHIP_BID_DELAY) {
//qDebug() << "Warning -- claiming something I saw moving." << getName(); //qDebug() << "Warning -- claiming something I saw moving." << getName();
_candidateForOwnership = true; setOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY);
} }
} else {
_loopsWithoutOwner = 0;
} }
#ifdef WANT_DEBUG #ifdef WANT_DEBUG
@ -232,7 +237,7 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const {
return false; return false;
} }
assert(entityTreeIsLocked()); assert(entityTreeIsLocked());
return _candidateForOwnership || sessionID == _entity->getSimulatorID(); return _outgoingPriority > 0 || sessionID == _entity->getSimulatorID();
} }
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
@ -245,7 +250,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
_serverVelocity = bulletToGLM(_body->getLinearVelocity()); _serverVelocity = bulletToGLM(_body->getLinearVelocity());
_serverAngularVelocity = bulletToGLM(_body->getAngularVelocity()); _serverAngularVelocity = bulletToGLM(_body->getAngularVelocity());
_lastStep = simulationStep; _lastStep = simulationStep;
_sentActive = false; _sentInactive = true;
return false; return false;
} }
@ -259,7 +264,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP; float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
const float INACTIVE_UPDATE_PERIOD = 0.5f; const float INACTIVE_UPDATE_PERIOD = 0.5f;
if (!_sentActive) { if (_sentInactive) {
// we resend the inactive update every INACTIVE_UPDATE_PERIOD // we resend the inactive update every INACTIVE_UPDATE_PERIOD
// until it is removed from the outgoing updates // until it is removed from the outgoing updates
// (which happens when we don't own the simulation and it isn't touching our simulation) // (which happens when we don't own the simulation and it isn't touching our simulation)
@ -347,30 +352,20 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s
assert(_body); assert(_body);
assert(entityTreeIsLocked()); assert(entityTreeIsLocked());
if (!remoteSimulationOutOfSync(simulationStep)) { if (_entity->getSimulatorID() != sessionID) {
_candidateForOwnership = false; // we don't own the simulation, but maybe we should...
return false; if (_outgoingPriority > 0) {
} if (_outgoingPriority < _entity->getSimulationPriority()) {
// our priority looses to remote, so we don't bother to bid
if (_entity->getSimulatorID() == sessionID) { _outgoingPriority = 0;
// we own the simulation return false;
_candidateForOwnership = false; }
return true; return usecTimestampNow() > _nextOwnershipBid;
}
const uint32_t FRAMES_BETWEEN_OWNERSHIP_CLAIMS = 30;
if (_candidateForOwnership) {
_candidateForOwnership = false;
++_loopsSinceOwnershipBid;
if (_loopsSinceOwnershipBid > FRAMES_BETWEEN_OWNERSHIP_CLAIMS) {
// we don't own the simulation, but it's time to bid for it
_loopsSinceOwnershipBid = 0;
return true;
} }
} return false;
}
_candidateForOwnership = false;
return false; return remoteSimulationOutOfSync(simulationStep);
} }
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step) { void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step) {
@ -384,7 +379,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
_entity->setVelocity(zero); _entity->setVelocity(zero);
_entity->setAngularVelocity(zero); _entity->setAngularVelocity(zero);
_entity->setAcceleration(zero); _entity->setAcceleration(zero);
_sentActive = false; _sentInactive = true;
} else { } else {
float gravityLength = glm::length(_entity->getGravity()); float gravityLength = glm::length(_entity->getGravity());
float accVsGravity = glm::abs(glm::length(_measuredAcceleration) - gravityLength); float accVsGravity = glm::abs(glm::length(_measuredAcceleration) - gravityLength);
@ -421,7 +416,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
_entity->setVelocity(zero); _entity->setVelocity(zero);
_entity->setAngularVelocity(zero); _entity->setAngularVelocity(zero);
} }
_sentActive = true; _sentInactive = false;
} }
// remember properties for local server prediction // remember properties for local server prediction
@ -431,7 +426,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
_serverAcceleration = _entity->getAcceleration(); _serverAcceleration = _entity->getAcceleration();
_serverAngularVelocity = _entity->getAngularVelocity(); _serverAngularVelocity = _entity->getAngularVelocity();
EntityItemProperties properties = _entity->getProperties(); EntityItemProperties properties;
// explicitly set the properties that changed so that they will be packed // explicitly set the properties that changed so that they will be packed
properties.setPosition(_serverPosition); properties.setPosition(_serverPosition);
@ -457,14 +452,14 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
if (!active) { if (!active) {
// we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID // 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 // but we remember that we do still own it... and rely on the server to tell us that we don't
properties.setSimulatorID(QUuid()); properties.clearSimulationOwner();
} else { _outgoingPriority = 0;
// explicitly set the property's simulatorID so that it is flagged as changed and will be packed
properties.setSimulatorID(sessionID);
} }
// else the ownership is not changing so we don't bother to pack it
} else { } else {
// we don't own the simulation for this entity yet, but we're sending a bid for it // we don't own the simulation for this entity yet, but we're sending a bid for it
properties.setSimulatorID(sessionID); properties.setSimulationOwner(sessionID, glm::max<quint8>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY));
_nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;
} }
if (EntityItem::getSendPhysicsUpdates()) { if (EntityItem::getSendPhysicsUpdates()) {
@ -502,6 +497,13 @@ uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() {
return dirtyFlags; return dirtyFlags;
} }
// virtual
quint8 EntityMotionState::getSimulationPriority() const {
if (_entity) {
return _entity->getSimulationPriority();
}
return 0;
}
// virtual // virtual
QUuid EntityMotionState::getSimulatorID() const { QUuid EntityMotionState::getSimulatorID() const {
@ -512,10 +514,11 @@ QUuid EntityMotionState::getSimulatorID() const {
return QUuid(); return QUuid();
} }
// virtual // virtual
void EntityMotionState::bump() { void EntityMotionState::bump(quint8 priority) {
_candidateForOwnership = true; if (_entity) {
setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
}
} }
void EntityMotionState::resetMeasuredBodyAcceleration() { void EntityMotionState::resetMeasuredBodyAcceleration() {
@ -543,9 +546,10 @@ void EntityMotionState::measureBodyAcceleration() {
glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity()); glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity());
_measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt; _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt;
_lastVelocity = velocity; _lastVelocity = velocity;
if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS && !_candidateForOwnership) { if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) {
_loopsSinceOwnershipBid = 0;
_loopsWithoutOwner = 0; _loopsWithoutOwner = 0;
_lastStep = ObjectMotionState::getWorldSimulationStep();
_sentInactive = false;
} }
} }
} }
@ -584,3 +588,7 @@ int16_t EntityMotionState::computeCollisionGroup() {
} }
return COLLISION_GROUP_DEFAULT; return COLLISION_GROUP_DEFAULT;
} }
void EntityMotionState::setOutgoingPriority(quint8 priority) {
_outgoingPriority = glm::max<quint8>(_outgoingPriority, priority);
}

View file

@ -29,7 +29,7 @@ public:
virtual ~EntityMotionState(); virtual ~EntityMotionState();
void updateServerPhysicsVariables(); void updateServerPhysicsVariables();
virtual void handleEasyChanges(uint32_t flags); virtual void handleEasyChanges(uint32_t flags, PhysicsEngine* engine);
virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine);
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem /// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
@ -68,8 +68,9 @@ public:
virtual const QUuid& getObjectID() const { return _entity->getID(); } virtual const QUuid& getObjectID() const { return _entity->getID(); }
virtual quint8 getSimulationPriority() const;
virtual QUuid getSimulatorID() const; virtual QUuid getSimulatorID() const;
virtual void bump(); virtual void bump(quint8 priority);
EntityItemPointer getEntity() const { return _entity; } EntityItemPointer getEntity() const { return _entity; }
@ -80,6 +81,9 @@ public:
virtual int16_t computeCollisionGroup(); virtual int16_t computeCollisionGroup();
// eternal logic can suggest a simuator priority bid for the next outgoing update
void setOutgoingPriority(quint8 priority);
friend class PhysicalEntitySimulation; friend class PhysicalEntitySimulation;
protected: protected:
@ -93,8 +97,7 @@ protected:
EntityItemPointer _entity; EntityItemPointer _entity;
bool _sentActive; // true if body was active when we sent last update bool _sentInactive; // true if body was inactive when we sent last update
int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects
// these are for the prediction of the remote server's simple extrapolation // these are for the prediction of the remote server's simple extrapolation
uint32_t _lastStep; // last step of server extrapolation uint32_t _lastStep; // last step of server extrapolation
@ -111,9 +114,9 @@ protected:
float _measuredDeltaTime; float _measuredDeltaTime;
quint8 _accelerationNearlyGravityCount; quint8 _accelerationNearlyGravityCount;
bool _candidateForOwnership; quint64 _nextOwnershipBid = 0;
uint32_t _loopsSinceOwnershipBid;
uint32_t _loopsWithoutOwner; uint32_t _loopsWithoutOwner;
quint8 _outgoingPriority = 0;
}; };
#endif // hifi_EntityMotionState_h #endif // hifi_EntityMotionState_h

View file

@ -114,7 +114,7 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) {
} }
} }
void ObjectMotionState::handleEasyChanges(uint32_t flags) { void ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) {
if (flags & EntityItem::DIRTY_POSITION) { if (flags & EntityItem::DIRTY_POSITION) {
btTransform worldTrans; btTransform worldTrans;
if (flags & EntityItem::DIRTY_ROTATION) { if (flags & EntityItem::DIRTY_ROTATION) {
@ -159,7 +159,7 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine*
if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) { if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) {
// no HARD flags remain, so do any EASY changes // no HARD flags remain, so do any EASY changes
if (flags & EASY_DIRTY_PHYSICS_FLAGS) { if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
handleEasyChanges(flags); handleEasyChanges(flags, engine);
} }
return; return;
} }
@ -174,7 +174,7 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine*
} }
} }
if (flags & EASY_DIRTY_PHYSICS_FLAGS) { if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
handleEasyChanges(flags); handleEasyChanges(flags, engine);
} }
// it is possible that there are no HARD flags at this point (if DIRTY_SHAPE was removed) // it is possible that there are no HARD flags at this point (if DIRTY_SHAPE was removed)
// so we check again befoe we reinsert: // so we check again befoe we reinsert:

View file

@ -40,7 +40,8 @@ enum MotionStateType {
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE); const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE);
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES | const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES |
EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP | EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP |
EntityItem::DIRTY_MATERIAL); EntityItem::DIRTY_MATERIAL | EntityItem::DIRTY_SIMULATOR_ID |
EntityItem::DIRTY_SIMULATOR_OWNERSHIP);
// These are the set of incoming flags that the PhysicsEngine needs to hear about: // These are the set of incoming flags that the PhysicsEngine needs to hear about:
const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS | const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS |
@ -70,7 +71,7 @@ public:
ObjectMotionState(btCollisionShape* shape); ObjectMotionState(btCollisionShape* shape);
~ObjectMotionState(); ~ObjectMotionState();
virtual void handleEasyChanges(uint32_t flags); virtual void handleEasyChanges(uint32_t flags, PhysicsEngine* engine);
virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine);
void updateBodyMaterialProperties(); void updateBodyMaterialProperties();
@ -118,8 +119,9 @@ public:
virtual const QUuid& getObjectID() const = 0; virtual const QUuid& getObjectID() const = 0;
virtual quint8 getSimulationPriority() const { return 0; }
virtual QUuid getSimulatorID() const = 0; virtual QUuid getSimulatorID() const = 0;
virtual void bump() = 0; virtual void bump(quint8 priority) {}
virtual QString getName() { return ""; } virtual QString getName() { return ""; }

View file

@ -194,7 +194,7 @@ void PhysicsEngine::changeObjects(VectorOfMotionStates& objects) {
if (flags & HARD_DIRTY_PHYSICS_FLAGS) { if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
object->handleHardAndEasyChanges(flags, this); object->handleHardAndEasyChanges(flags, this);
} else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
object->handleEasyChanges(flags); object->handleEasyChanges(flags, this);
} }
} }
} }
@ -260,9 +260,6 @@ void PhysicsEngine::stepSimulation() {
void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) { void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) {
BT_PROFILE("ownershipInfection"); BT_PROFILE("ownershipInfection");
if (_sessionID.isNull()) {
return;
}
const btCollisionObject* characterObject = _characterController ? _characterController->getCollisionObject() : nullptr; const btCollisionObject* characterObject = _characterController ? _characterController->getCollisionObject() : nullptr;
@ -272,14 +269,16 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const
if (b && ((a && a->getSimulatorID() == _sessionID && !objectA->isStaticObject()) || (objectA == characterObject))) { if (b && ((a && a->getSimulatorID() == _sessionID && !objectA->isStaticObject()) || (objectA == characterObject))) {
// NOTE: we might own the simulation of a kinematic object (A) // NOTE: we might own the simulation of a kinematic object (A)
// but we don't claim ownership of kinematic objects (B) based on collisions here. // but we don't claim ownership of kinematic objects (B) based on collisions here.
if (!objectB->isStaticOrKinematicObject()) { if (!objectB->isStaticOrKinematicObject() && b->getSimulatorID() != _sessionID) {
b->bump(); quint8 priority = a ? a->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
b->bump(priority);
} }
} else if (a && ((b && b->getSimulatorID() == _sessionID && !objectB->isStaticObject()) || (objectB == characterObject))) { } else if (a && ((b && b->getSimulatorID() == _sessionID && !objectB->isStaticObject()) || (objectB == characterObject))) {
// SIMILARLY: we might own the simulation of a kinematic object (B) // SIMILARLY: we might own the simulation of a kinematic object (B)
// but we don't claim ownership of kinematic objects (A) based on collisions here. // but we don't claim ownership of kinematic objects (A) based on collisions here.
if (!objectA->isStaticOrKinematicObject()) { if (!objectA->isStaticOrKinematicObject() && a->getSimulatorID() != _sessionID) {
a->bump(); quint8 priority = b ? b->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
a->bump(priority);
} }
} }
} }
@ -311,7 +310,9 @@ void PhysicsEngine::updateContactMap() {
_contactMap[ContactKey(a, b)].update(_numContactFrames, contactManifold->getContactPoint(0)); _contactMap[ContactKey(a, b)].update(_numContactFrames, contactManifold->getContactPoint(0));
} }
doOwnershipInfection(objectA, objectB); if (!_sessionID.isNull()) {
doOwnershipInfection(objectA, objectB);
}
} }
} }
} }
@ -402,7 +403,7 @@ void PhysicsEngine::bump(ObjectMotionState* motionState) {
if (!objectA->isStaticOrKinematicObject()) { if (!objectA->isStaticOrKinematicObject()) {
ObjectMotionState* motionStateA = static_cast<ObjectMotionState*>(objectA->getUserPointer()); ObjectMotionState* motionStateA = static_cast<ObjectMotionState*>(objectA->getUserPointer());
if (motionStateA) { if (motionStateA) {
motionStateA->bump(); motionStateA->bump(VOLUNTEER_SIMULATION_PRIORITY);
objectA->setActivationState(ACTIVE_TAG); objectA->setActivationState(ACTIVE_TAG);
} }
} }
@ -410,7 +411,7 @@ void PhysicsEngine::bump(ObjectMotionState* motionState) {
if (!objectB->isStaticOrKinematicObject()) { if (!objectB->isStaticOrKinematicObject()) {
ObjectMotionState* motionStateB = static_cast<ObjectMotionState*>(objectB->getUserPointer()); ObjectMotionState* motionStateB = static_cast<ObjectMotionState*>(objectB->getUserPointer());
if (motionStateB) { if (motionStateB) {
motionStateB->bump(); motionStateB->bump(VOLUNTEER_SIMULATION_PRIORITY);
objectB->setActivationState(ACTIVE_TAG); objectB->setActivationState(ACTIVE_TAG);
} }
} }

View file

@ -15,7 +15,7 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp> #include <glm/gtc/quaternion.hpp>
const int PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 4; const int PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 6; // Bullet will start to "lose time" at 10 FPS.
const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 60.0f; const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 60.0f;
// return incremental rotation (Bullet-style) caused by angularVelocity over timeStep // return incremental rotation (Bullet-style) caused by angularVelocity over timeStep

View file

@ -15,9 +15,9 @@
#include "StreamUtils.h" #include "StreamUtils.h"
const char* hex_digits = "0123456789abcdef";
void StreamUtil::dump(std::ostream& s, const QByteArray& buffer) { void StreamUtil::dump(std::ostream& s, const QByteArray& buffer) {
const char* hex_digits = "0123456789abcdef";
int row_size = 32; int row_size = 32;
int i = 0; int i = 0;
while (i < buffer.size()) { while (i < buffer.size()) {