mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 12:49:34 +02:00
first pass cloneables WIP
This commit is contained in:
parent
b9656e5c72
commit
3241c18271
19 changed files with 410 additions and 164 deletions
|
@ -44,6 +44,7 @@ EntityServer::EntityServer(ReceivedMessage& message) :
|
||||||
|
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd,
|
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd,
|
||||||
|
PacketType::EntityClone,
|
||||||
PacketType::EntityEdit,
|
PacketType::EntityEdit,
|
||||||
PacketType::EntityErase,
|
PacketType::EntityErase,
|
||||||
PacketType::EntityPhysics,
|
PacketType::EntityPhysics,
|
||||||
|
|
|
@ -34,7 +34,7 @@ void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer<Received
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew) {
|
void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew) {
|
||||||
if (type == PacketType::EntityAdd || type == PacketType::EntityEdit || type == PacketType::EntityPhysics) {
|
if (type == PacketType::EntityAdd || type == PacketType::EntityClone || type == PacketType::EntityEdit || type == PacketType::EntityPhysics) {
|
||||||
EntityItem::adjustEditPacketForClockSkew(buffer, clockSkew);
|
EntityItem::adjustEditPacketForClockSkew(buffer, clockSkew);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,3 +162,15 @@ void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityI
|
||||||
queueOctreeEditMessage(PacketType::EntityErase, bufferOut);
|
queueOctreeEditMessage(PacketType::EntityErase, bufferOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityEditPacketSender::queueCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID) {
|
||||||
|
if (!_shouldSend) {
|
||||||
|
return; // bail early
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray bufferOut(NLPacket::maxPayloadSize(PacketType::EntityClone), 0);
|
||||||
|
|
||||||
|
if (EntityItemProperties::encodeCloneEntityMessage(entityIDToClone, newEntityID, bufferOut)) {
|
||||||
|
queueOctreeEditMessage(PacketType::EntityClone, bufferOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ public:
|
||||||
|
|
||||||
|
|
||||||
void queueEraseEntityMessage(const EntityItemID& entityItemID);
|
void queueEraseEntityMessage(const EntityItemID& entityItemID);
|
||||||
|
void queueCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID);
|
||||||
|
|
||||||
// My server type is the model server
|
// My server type is the model server
|
||||||
virtual char getMyNodeType() const override { return NodeType::EntityServer; }
|
virtual char getMyNodeType() const override { return NodeType::EntityServer; }
|
||||||
|
|
|
@ -124,6 +124,11 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
||||||
|
|
||||||
requestedProperties += PROP_LAST_EDITED_BY;
|
requestedProperties += PROP_LAST_EDITED_BY;
|
||||||
|
|
||||||
|
requestedProperties += PROP_CLONEABLE;
|
||||||
|
requestedProperties += PROP_CLONEABLE_LIFETIME;
|
||||||
|
requestedProperties += PROP_CLONEABLE_LIMIT;
|
||||||
|
requestedProperties += PROP_CLONEABLE_DYNAMIC;
|
||||||
|
|
||||||
return requestedProperties;
|
return requestedProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,6 +293,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
||||||
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube());
|
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy());
|
APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy());
|
||||||
|
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, getCloneable());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, getCloneableLifetime());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, getCloneableLimit());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, getCloneableDynamic());
|
||||||
|
|
||||||
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
|
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
|
||||||
requestedProperties,
|
requestedProperties,
|
||||||
propertyFlags,
|
propertyFlags,
|
||||||
|
@ -848,6 +858,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy);
|
READ_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy);
|
||||||
|
|
||||||
|
READ_ENTITY_PROPERTY(PROP_CLONEABLE, bool, setCloneable);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, float, setCloneableLifetime);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, float, setCloneableLimit);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, bool, setCloneableDynamic);
|
||||||
|
|
||||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||||
propertyFlags, overwriteLocalData, somethingChanged);
|
propertyFlags, overwriteLocalData, somethingChanged);
|
||||||
|
|
||||||
|
@ -1275,6 +1290,11 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
|
||||||
|
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy);
|
||||||
|
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneable, getCloneable);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneableLifetime, getCloneableLifetime);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneableLimit, getCloneableLimit);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneableDynamic, getCloneableDynamic);
|
||||||
|
|
||||||
properties._defaultSettings = false;
|
properties._defaultSettings = false;
|
||||||
|
|
||||||
return properties;
|
return properties;
|
||||||
|
@ -1382,6 +1402,11 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||||
|
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy);
|
||||||
|
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneable, setCloneable);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneableLifetime, setCloneableLifetime);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneableLimit, setCloneableLimit);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneableDynamic, setCloneableDynamic);
|
||||||
|
|
||||||
if (updateQueryAACube()) {
|
if (updateQueryAACube()) {
|
||||||
somethingChanged = true;
|
somethingChanged = true;
|
||||||
}
|
}
|
||||||
|
@ -2975,3 +3000,76 @@ std::unordered_map<std::string, graphics::MultiMaterial> EntityItem::getMaterial
|
||||||
}
|
}
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntityItem::getCloneable() const {
|
||||||
|
bool result;
|
||||||
|
withReadLock([&] {
|
||||||
|
result = _cloneable;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::setCloneable(bool value) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_cloneable = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
float EntityItem::getCloneableLifetime() const {
|
||||||
|
float result;
|
||||||
|
withReadLock([&] {
|
||||||
|
result = _cloneableLifetime;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::setCloneableLifetime(float value) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_cloneableLifetime = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
float EntityItem::getCloneableLimit() const {
|
||||||
|
float result;
|
||||||
|
withReadLock([&] {
|
||||||
|
result = _cloneableLimit;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::setCloneableLimit(float value) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_cloneableLimit = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityItem::getCloneableDynamic() const {
|
||||||
|
bool result;
|
||||||
|
withReadLock([&] {
|
||||||
|
result = _cloneableDynamic;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::setCloneableDynamic(const bool value) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_cloneableDynamic = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityItem::addCloneID(const QUuid& cloneID) {
|
||||||
|
if (!_cloneIDs.contains(cloneID)) {
|
||||||
|
_cloneIDs.append(cloneID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityItem::removeCloneID(const QUuid& cloneID) {
|
||||||
|
int index = _cloneIDs.indexOf(cloneID);
|
||||||
|
if (index > 0) {
|
||||||
|
_cloneIDs.removeAt(index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -341,6 +341,15 @@ public:
|
||||||
quint32 getStaticCertificateVersion() const;
|
quint32 getStaticCertificateVersion() const;
|
||||||
void setStaticCertificateVersion(const quint32&);
|
void setStaticCertificateVersion(const quint32&);
|
||||||
|
|
||||||
|
bool getCloneable() const;
|
||||||
|
void setCloneable(bool value);
|
||||||
|
float getCloneableLifetime() const;
|
||||||
|
void setCloneableLifetime(float value);
|
||||||
|
float getCloneableLimit() const;
|
||||||
|
void setCloneableLimit(float value);
|
||||||
|
bool getCloneableDynamic() const;
|
||||||
|
void setCloneableDynamic(const bool value);
|
||||||
|
|
||||||
// TODO: get rid of users of getRadius()...
|
// TODO: get rid of users of getRadius()...
|
||||||
float getRadius() const;
|
float getRadius() const;
|
||||||
|
|
||||||
|
@ -494,6 +503,12 @@ public:
|
||||||
void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; }
|
void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; }
|
||||||
uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; }
|
uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; }
|
||||||
|
|
||||||
|
bool addCloneID(const QUuid& cloneID);
|
||||||
|
bool removeCloneID(const QUuid& cloneID);
|
||||||
|
const QList<QUuid>& getCloneIDs() const { return _cloneIDs; }
|
||||||
|
void setCloneParent(const QUuid& cloneParentID) { _cloneParentID = cloneParentID; }
|
||||||
|
const QUuid& getCloneParent() const { return _cloneParentID; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void requestRenderUpdate();
|
void requestRenderUpdate();
|
||||||
|
|
||||||
|
@ -648,6 +663,14 @@ protected:
|
||||||
|
|
||||||
bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera
|
bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera
|
||||||
|
|
||||||
|
bool _cloneable;
|
||||||
|
float _cloneableLifetime;
|
||||||
|
float _cloneableLimit;
|
||||||
|
bool _cloneableDynamic;
|
||||||
|
|
||||||
|
QList<QUuid> _cloneIDs;
|
||||||
|
QUuid _cloneParentID;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
|
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
|
||||||
std::mutex _materialsLock;
|
std::mutex _materialsLock;
|
||||||
|
|
|
@ -436,6 +436,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
|
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
|
CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
|
||||||
|
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_CLONEABLE, cloneable);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_LIFETIME, cloneableLifetime);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_LIMIT, cloneableLimit);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_DYNAMIC, cloneableDynamic);
|
||||||
|
|
||||||
changedProperties += _animation.getChangedProperties();
|
changedProperties += _animation.getChangedProperties();
|
||||||
changedProperties += _keyLight.getChangedProperties();
|
changedProperties += _keyLight.getChangedProperties();
|
||||||
changedProperties += _ambientLight.getChangedProperties();
|
changedProperties += _ambientLight.getChangedProperties();
|
||||||
|
@ -1430,6 +1435,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable except at entity creation
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable except at entity creation
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable
|
||||||
|
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE, cloneable);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_LIFETIME, cloneableLifetime);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_LIMIT, cloneableLimit);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_DYNAMIC, cloneableDynamic);
|
||||||
|
|
||||||
// Rendering info
|
// Rendering info
|
||||||
if (!skipDefaults && !strictSemantics) {
|
if (!skipDefaults && !strictSemantics) {
|
||||||
QScriptValue renderInfo = engine->newObject();
|
QScriptValue renderInfo = engine->newObject();
|
||||||
|
@ -1642,6 +1652,11 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
|
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI);
|
||||||
|
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneable, bool, setCloneable);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableLifetime, float, setCloneableLifetime);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableLimit, float, setCloneableLimit);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableDynamic, bool, setCloneableDynamic);
|
||||||
|
|
||||||
_lastEdited = usecTimestampNow();
|
_lastEdited = usecTimestampNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2017,6 +2032,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
||||||
|
|
||||||
ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t);
|
ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t);
|
||||||
|
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_CLONEABLE, Cloneable, cloneable, bool);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_LIFETIME, CloneableLifetime, cloneableLifetime, float);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_LIMIT, CloneableLimit, cloneableLimit, float);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_DYNAMIC, CloneableDynamic, cloneableDynamic, bool);
|
||||||
|
|
||||||
// FIXME - these are not yet handled
|
// FIXME - these are not yet handled
|
||||||
//ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64);
|
//ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64);
|
||||||
|
|
||||||
|
@ -2331,6 +2351,11 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber());
|
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID());
|
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion());
|
APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion());
|
||||||
|
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, properties.getCloneable());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, properties.getCloneableLifetime());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, properties.getCloneableLimit());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, properties.getCloneableDynamic());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propertyCount > 0) {
|
if (propertyCount > 0) {
|
||||||
|
@ -2701,6 +2726,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion);
|
||||||
|
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE, bool, setCloneable);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_LIFETIME, float, setCloneableLifetime);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_LIMIT, float, setCloneableLimit);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_DYNAMIC, bool, setCloneableDynamic);
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2780,6 +2810,54 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer) {
|
||||||
|
|
||||||
|
char* copyAt = buffer.data();
|
||||||
|
int outputLength = 0;
|
||||||
|
|
||||||
|
if (buffer.size() < (int)(sizeof(NUM_BYTES_RFC4122_UUID) * 2)) {
|
||||||
|
qCDebug(entities) << "ERROR - encodeCloneEntityMessage() called with buffer that is too small!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(copyAt, entityIDToClone.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
|
||||||
|
copyAt += NUM_BYTES_RFC4122_UUID;
|
||||||
|
outputLength += NUM_BYTES_RFC4122_UUID;
|
||||||
|
|
||||||
|
memcpy(copyAt, newEntityID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
|
||||||
|
copyAt += NUM_BYTES_RFC4122_UUID;
|
||||||
|
outputLength += NUM_BYTES_RFC4122_UUID;
|
||||||
|
|
||||||
|
buffer.resize(outputLength);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityItemProperties::decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID) {
|
||||||
|
|
||||||
|
const unsigned char* packetData = (const unsigned char*)buffer.constData();
|
||||||
|
const unsigned char* dataAt = packetData;
|
||||||
|
size_t packetLength = buffer.size();
|
||||||
|
processedBytes = 0;
|
||||||
|
|
||||||
|
if (NUM_BYTES_RFC4122_UUID * 2 > packetLength) {
|
||||||
|
qCDebug(entities) << "EntityItemProperties::processEraseMessageDetails().... bailing because not enough bytes in buffer";
|
||||||
|
return false; // bail to prevent buffer overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID);
|
||||||
|
entityIDToClone = QUuid::fromRfc4122(encodedID);
|
||||||
|
dataAt += encodedID.size();
|
||||||
|
processedBytes += encodedID.size();
|
||||||
|
|
||||||
|
encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID);
|
||||||
|
newEntityID = QUuid::fromRfc4122(encodedID);
|
||||||
|
dataAt += encodedID.size();
|
||||||
|
processedBytes += encodedID.size();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void EntityItemProperties::markAllChanged() {
|
void EntityItemProperties::markAllChanged() {
|
||||||
_lastEditedByChanged = true;
|
_lastEditedByChanged = true;
|
||||||
_simulationOwnerChanged = true;
|
_simulationOwnerChanged = true;
|
||||||
|
@ -2941,6 +3019,11 @@ void EntityItemProperties::markAllChanged() {
|
||||||
|
|
||||||
_dpiChanged = true;
|
_dpiChanged = true;
|
||||||
_relayParentJointsChanged = true;
|
_relayParentJointsChanged = true;
|
||||||
|
|
||||||
|
_cloneableChanged = true;
|
||||||
|
_cloneableLifetimeChanged = true;
|
||||||
|
_cloneableLimitChanged = true;
|
||||||
|
_cloneableDynamicChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The minimum bounding box for the entity.
|
// The minimum bounding box for the entity.
|
||||||
|
@ -3373,6 +3456,20 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
||||||
out += "isUVModeStretch";
|
out += "isUVModeStretch";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cloneableChanged()) {
|
||||||
|
out += "cloneable";
|
||||||
|
}
|
||||||
|
if (cloneableLifetimeChanged()) {
|
||||||
|
out += "cloneableLifetime";
|
||||||
|
}
|
||||||
|
if (cloneableLimitChanged()) {
|
||||||
|
out += "cloneableLimit";
|
||||||
|
}
|
||||||
|
if (cloneableDynamicChanged()) {
|
||||||
|
out += "cloneableDynamic";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
getAnimation().listChangedProperties(out);
|
getAnimation().listChangedProperties(out);
|
||||||
getKeyLight().listChangedProperties(out);
|
getKeyLight().listChangedProperties(out);
|
||||||
getAmbientLight().listChangedProperties(out);
|
getAmbientLight().listChangedProperties(out);
|
||||||
|
|
|
@ -272,6 +272,11 @@ public:
|
||||||
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
|
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
|
||||||
DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS);
|
DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS);
|
||||||
|
|
||||||
|
DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_CLONEABLE);
|
||||||
|
DEFINE_PROPERTY(PROP_CLONEABLE_LIFETIME, CloneableLifetime, cloneableLifetime, float, ENTITY_ITEM_CLONEABLE_LIFETIME);
|
||||||
|
DEFINE_PROPERTY(PROP_CLONEABLE_LIMIT, CloneableLimit, cloneableLimit, float, ENTITY_ITEM_CLONEABLE_LIMIT);
|
||||||
|
DEFINE_PROPERTY(PROP_CLONEABLE_DYNAMIC, CloneableDynamic, cloneableDynamic, bool, ENTITY_ITEM_CLONEABLE_DYNAMIC);
|
||||||
|
|
||||||
static QString getComponentModeString(uint32_t mode);
|
static QString getComponentModeString(uint32_t mode);
|
||||||
static QString getComponentModeAsString(uint32_t mode);
|
static QString getComponentModeAsString(uint32_t mode);
|
||||||
|
|
||||||
|
@ -294,6 +299,8 @@ public:
|
||||||
QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties);
|
QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties);
|
||||||
|
|
||||||
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer);
|
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer);
|
||||||
|
static bool encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer);
|
||||||
|
static bool decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID);
|
||||||
|
|
||||||
static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
|
static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
|
||||||
EntityItemID& entityID, EntityItemProperties& properties);
|
EntityItemID& entityID, EntityItemProperties& properties);
|
||||||
|
|
|
@ -97,4 +97,9 @@ const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid();
|
||||||
|
|
||||||
const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false;
|
const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false;
|
||||||
|
|
||||||
|
const bool ENTITY_ITEM_CLONEABLE = false;
|
||||||
|
const float ENTITY_ITEM_CLONEABLE_LIFETIME = 300.0f;
|
||||||
|
const int ENTITY_ITEM_CLONEABLE_LIMIT = 0;
|
||||||
|
const bool ENTITY_ITEM_CLONEABLE_DYNAMIC = false;
|
||||||
|
|
||||||
#endif // hifi_EntityItemPropertiesDefaults_h
|
#endif // hifi_EntityItemPropertiesDefaults_h
|
||||||
|
|
|
@ -242,6 +242,11 @@ enum EntityPropertyList {
|
||||||
PROP_MATERIAL_MAPPING_ROT,
|
PROP_MATERIAL_MAPPING_ROT,
|
||||||
PROP_MATERIAL_DATA,
|
PROP_MATERIAL_DATA,
|
||||||
|
|
||||||
|
PROP_CLONEABLE,
|
||||||
|
PROP_CLONEABLE_LIFETIME,
|
||||||
|
PROP_CLONEABLE_LIMIT,
|
||||||
|
PROP_CLONEABLE_DYNAMIC,
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// ATTENTION: add new properties to end of list just ABOVE this line
|
// ATTENTION: add new properties to end of list just ABOVE this line
|
||||||
PROP_AFTER_LAST_ITEM,
|
PROP_AFTER_LAST_ITEM,
|
||||||
|
|
|
@ -258,33 +258,9 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
||||||
propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);
|
propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);
|
||||||
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
|
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
|
||||||
|
|
||||||
EntityItemID id = EntityItemID(QUuid::createUuid());
|
EntityItemID id;
|
||||||
|
|
||||||
// If we have a local entity tree set, then also update it.
|
// If we have a local entity tree set, then also update it.
|
||||||
bool success = true;
|
bool success = addLocalEntityCopy(propertiesWithSimID, id);
|
||||||
if (_entityTree) {
|
|
||||||
_entityTree->withWriteLock([&] {
|
|
||||||
EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
|
|
||||||
if (entity) {
|
|
||||||
if (propertiesWithSimID.queryAACubeRelatedPropertyChanged()) {
|
|
||||||
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
|
|
||||||
bool success;
|
|
||||||
AACube queryAACube = entity->getQueryAACube(success);
|
|
||||||
if (success) {
|
|
||||||
propertiesWithSimID.setQueryAACube(queryAACube);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
|
||||||
// since we're creating this object we will immediately volunteer to own its simulation
|
|
||||||
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
|
|
||||||
propertiesWithSimID.setLastEdited(entity->getLastEdited());
|
|
||||||
} else {
|
|
||||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// queue the packet
|
// queue the packet
|
||||||
if (success) {
|
if (success) {
|
||||||
|
@ -295,6 +271,39 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properties, EntityItemID& id) {
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
id = EntityItemID(QUuid::createUuid());
|
||||||
|
|
||||||
|
if (_entityTree) {
|
||||||
|
_entityTree->withWriteLock([&] {
|
||||||
|
EntityItemPointer entity = _entityTree->addEntity(id, properties);
|
||||||
|
if (entity) {
|
||||||
|
if (properties.queryAACubeRelatedPropertyChanged()) {
|
||||||
|
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
|
||||||
|
bool success;
|
||||||
|
AACube queryAACube = entity->getQueryAACube(success);
|
||||||
|
if (success) {
|
||||||
|
properties.setQueryAACube(queryAACube);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
|
// since we're creating this object we will immediately volunteer to own its simulation
|
||||||
|
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
|
||||||
|
properties.setLastEdited(entity->getLastEdited());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& textures,
|
QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& textures,
|
||||||
const QString& shapeType, bool dynamic, bool collisionless,
|
const QString& shapeType, bool dynamic, bool collisionless,
|
||||||
const glm::vec3& position, const glm::vec3& gravity) {
|
const glm::vec3& position, const glm::vec3& gravity) {
|
||||||
|
@ -320,6 +329,18 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin
|
||||||
return addEntity(properties);
|
return addEntity(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) {
|
||||||
|
EntityItemID newEntityID;
|
||||||
|
EntityItemProperties properties = getEntityProperties(entityIDToClone);
|
||||||
|
if (addLocalEntityCopy(properties, newEntityID)) {
|
||||||
|
qCDebug(entities) << "DBACK POOPY cloneEntity addLocalEntityCopy" << newEntityID;
|
||||||
|
getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID);
|
||||||
|
return newEntityID;
|
||||||
|
} else {
|
||||||
|
return QUuid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {
|
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {
|
||||||
EntityPropertyFlags noSpecificProperties;
|
EntityPropertyFlags noSpecificProperties;
|
||||||
return getEntityProperties(identity, noSpecificProperties);
|
return getEntityProperties(identity, noSpecificProperties);
|
||||||
|
|
|
@ -224,6 +224,8 @@ public slots:
|
||||||
Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic,
|
Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic,
|
||||||
bool collisionless, const glm::vec3& position, const glm::vec3& gravity);
|
bool collisionless, const glm::vec3& position, const glm::vec3& gravity);
|
||||||
|
|
||||||
|
Q_INVOKABLE QUuid cloneEntity(QUuid entityIDToClone);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Get the properties of an entity.
|
* Get the properties of an entity.
|
||||||
* @function Entities.getEntityProperties
|
* @function Entities.getEntityProperties
|
||||||
|
@ -1875,6 +1877,7 @@ private:
|
||||||
bool polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
bool polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
||||||
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
|
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
|
||||||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||||
|
bool addLocalEntityCopy(EntityItemProperties& propertiesWithSimID, EntityItemID& id);
|
||||||
|
|
||||||
EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID,
|
EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID,
|
||||||
EntityTypes::EntityType entityType = EntityTypes::Unknown);
|
EntityTypes::EntityType entityType = EntityTypes::Unknown);
|
||||||
|
|
|
@ -228,6 +228,7 @@ bool EntityTree::handlesEditPacketType(PacketType packetType) const {
|
||||||
// we handle these types of "edit" packets
|
// we handle these types of "edit" packets
|
||||||
switch (packetType) {
|
switch (packetType) {
|
||||||
case PacketType::EntityAdd:
|
case PacketType::EntityAdd:
|
||||||
|
case PacketType::EntityClone:
|
||||||
case PacketType::EntityEdit:
|
case PacketType::EntityEdit:
|
||||||
case PacketType::EntityErase:
|
case PacketType::EntityErase:
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
|
@ -592,6 +593,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ign
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeCloneIDFromCloneParent(entityID);
|
||||||
unhookChildAvatar(entityID);
|
unhookChildAvatar(entityID);
|
||||||
emit deletingEntity(entityID);
|
emit deletingEntity(entityID);
|
||||||
emit deletingEntityPointer(existingEntity.get());
|
emit deletingEntityPointer(existingEntity.get());
|
||||||
|
@ -625,6 +627,19 @@ void EntityTree::unhookChildAvatar(const EntityItemID entityID) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityTree::removeCloneIDFromCloneParent(const EntityItemID& entityID) {
|
||||||
|
EntityItemPointer entity = findEntityByEntityItemID(entityID);
|
||||||
|
if (entity) {
|
||||||
|
const QUuid& cloneParentID = entity->getCloneParent();
|
||||||
|
if (!cloneParentID.isNull()) {
|
||||||
|
EntityItemPointer cloneParent = findEntityByID(cloneParentID);
|
||||||
|
if (cloneParent) {
|
||||||
|
cloneParent->removeCloneID(entityID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool ignoreWarnings) {
|
void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool ignoreWarnings) {
|
||||||
// NOTE: callers must lock the tree before using this method
|
// NOTE: callers must lock the tree before using this method
|
||||||
DeleteEntityOperator theOperator(getThisPointer());
|
DeleteEntityOperator theOperator(getThisPointer());
|
||||||
|
@ -653,6 +668,7 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool i
|
||||||
}
|
}
|
||||||
|
|
||||||
// tell our delete operator about this entityID
|
// tell our delete operator about this entityID
|
||||||
|
removeCloneIDFromCloneParent(entityID);
|
||||||
unhookChildAvatar(entityID);
|
unhookChildAvatar(entityID);
|
||||||
theOperator.addEntityIDToDeleteList(entityID);
|
theOperator.addEntityIDToDeleteList(entityID);
|
||||||
emit deletingEntity(entityID);
|
emit deletingEntity(entityID);
|
||||||
|
@ -1392,6 +1408,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
|
|
||||||
int processedBytes = 0;
|
int processedBytes = 0;
|
||||||
bool isAdd = false;
|
bool isAdd = false;
|
||||||
|
bool isClone = false;
|
||||||
// we handle these types of "edit" packets
|
// we handle these types of "edit" packets
|
||||||
switch (message.getType()) {
|
switch (message.getType()) {
|
||||||
case PacketType::EntityErase: {
|
case PacketType::EntityErase: {
|
||||||
|
@ -1400,6 +1417,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case PacketType::EntityClone:
|
||||||
|
isClone = true; // fall through to next case
|
||||||
case PacketType::EntityAdd:
|
case PacketType::EntityAdd:
|
||||||
isAdd = true; // fall through to next case
|
isAdd = true; // fall through to next case
|
||||||
// FALLTHRU
|
// FALLTHRU
|
||||||
|
@ -1422,8 +1441,21 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
EntityItemProperties properties;
|
EntityItemProperties properties;
|
||||||
startDecode = usecTimestampNow();
|
startDecode = usecTimestampNow();
|
||||||
|
|
||||||
bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes,
|
bool validEditPacket = false;
|
||||||
entityItemID, properties);
|
EntityItemID entityIDToClone;
|
||||||
|
EntityItemPointer entityToClone;
|
||||||
|
if (isClone) {
|
||||||
|
QByteArray buffer = QByteArray::fromRawData(reinterpret_cast<const char*>(editData), maxLength);
|
||||||
|
validEditPacket = EntityItemProperties::decodeCloneEntityMessage(buffer, processedBytes, entityIDToClone, entityItemID);
|
||||||
|
entityToClone = findEntityByEntityItemID(entityIDToClone);
|
||||||
|
if (entityToClone) {
|
||||||
|
properties = entityToClone->getProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes, entityItemID, properties);
|
||||||
|
}
|
||||||
|
|
||||||
endDecode = usecTimestampNow();
|
endDecode = usecTimestampNow();
|
||||||
|
|
||||||
EntityItemPointer existingEntity;
|
EntityItemPointer existingEntity;
|
||||||
|
@ -1491,6 +1523,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isClone) {
|
||||||
if ((isAdd || properties.lifetimeChanged()) &&
|
if ((isAdd || properties.lifetimeChanged()) &&
|
||||||
((!senderNode->getCanRez() && senderNode->getCanRezTmp()) ||
|
((!senderNode->getCanRez() && senderNode->getCanRezTmp()) ||
|
||||||
(!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) {
|
(!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) {
|
||||||
|
@ -1508,6 +1541,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
properties.setLocked(false);
|
properties.setLocked(false);
|
||||||
bumpTimestamp(properties);
|
bumpTimestamp(properties);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we got a valid edit packet, then it could be a new entity or it could be an update to
|
// If we got a valid edit packet, then it could be a new entity or it could be an update to
|
||||||
// an existing entity... handle appropriately
|
// an existing entity... handle appropriately
|
||||||
|
@ -1566,17 +1600,39 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
} else if (isAdd) {
|
} else if (isAdd) {
|
||||||
bool failedAdd = !allowed;
|
bool failedAdd = !allowed;
|
||||||
bool isCertified = !properties.getCertificateID().isEmpty();
|
bool isCertified = !properties.getCertificateID().isEmpty();
|
||||||
|
bool isCloneable = properties.getCloneable();
|
||||||
|
int cloneLimit = properties.getCloneableLimit();
|
||||||
if (!allowed) {
|
if (!allowed) {
|
||||||
qCDebug(entities) << "Filtered entity add. ID:" << entityItemID;
|
qCDebug(entities) << "Filtered entity add. ID:" << entityItemID;
|
||||||
} else if (!isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
|
} else if (!isClone && !isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
|
||||||
failedAdd = true;
|
failedAdd = true;
|
||||||
qCDebug(entities) << "User without 'uncertified rez rights' [" << senderNode->getUUID()
|
qCDebug(entities) << "User without 'uncertified rez rights' [" << senderNode->getUUID()
|
||||||
<< "] attempted to add an uncertified entity with ID:" << entityItemID;
|
<< "] attempted to add an uncertified entity with ID:" << entityItemID;
|
||||||
} else if (isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) {
|
} else if (!isClone && isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) {
|
||||||
failedAdd = true;
|
failedAdd = true;
|
||||||
qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID()
|
qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID()
|
||||||
<< "] attempted to add a certified entity with ID:" << entityItemID;
|
<< "] attempted to add a certified entity with ID:" << entityItemID;
|
||||||
|
} else if (isClone && isCertified) {
|
||||||
|
failedAdd = true;
|
||||||
|
qCDebug(entities) << "User attempted to clone certified entity from entity ID:" << entityIDToClone;
|
||||||
|
} else if (isClone && !isCloneable) {
|
||||||
|
failedAdd = true;
|
||||||
|
qCDebug(entities) << "User attempted to clone non-cloneable entity from entity ID:" << entityIDToClone;
|
||||||
|
} else if (isClone && entityToClone && entityToClone->getCloneIDs().size() >= cloneLimit) {
|
||||||
|
failedAdd = true;
|
||||||
|
qCDebug(entities) << "User attempted to clone entity ID:" << entityIDToClone << " which reached it's cloneable limit.";
|
||||||
} else {
|
} else {
|
||||||
|
if (isClone) {
|
||||||
|
properties.setName(properties.getName() + "-clone-" + entityIDToClone.toString());
|
||||||
|
properties.setLocked(false);
|
||||||
|
properties.setLifetime(properties.getCloneableLifetime());
|
||||||
|
properties.setDynamic(properties.getCloneableDynamic());
|
||||||
|
properties.setCloneable(ENTITY_ITEM_CLONEABLE);
|
||||||
|
properties.setCloneableLifetime(ENTITY_ITEM_CLONEABLE_LIFETIME);
|
||||||
|
properties.setCloneableLimit(ENTITY_ITEM_CLONEABLE_LIMIT);
|
||||||
|
properties.setCloneableDynamic(ENTITY_ITEM_CLONEABLE_DYNAMIC);
|
||||||
|
}
|
||||||
|
|
||||||
// this is a new entity... assign a new entityID
|
// this is a new entity... assign a new entityID
|
||||||
properties.setCreated(properties.getLastEdited());
|
properties.setCreated(properties.getLastEdited());
|
||||||
properties.setLastEditedBy(senderNode->getUUID());
|
properties.setLastEditedBy(senderNode->getUUID());
|
||||||
|
@ -1600,6 +1656,11 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
if (newEntity) {
|
if (newEntity) {
|
||||||
newEntity->markAsChangedOnServer();
|
newEntity->markAsChangedOnServer();
|
||||||
notifyNewlyCreatedEntity(*newEntity, senderNode);
|
notifyNewlyCreatedEntity(*newEntity, senderNode);
|
||||||
|
if (isClone) {
|
||||||
|
entityToClone->addCloneID(newEntity->getEntityItemID());
|
||||||
|
newEntity->setCloneParent(entityIDToClone);
|
||||||
|
qCDebug(entities) << "DBACK POOPY addedEntity clone " << newEntity->getEntityItemID();
|
||||||
|
}
|
||||||
|
|
||||||
startLogging = usecTimestampNow();
|
startLogging = usecTimestampNow();
|
||||||
if (wantEditLogging()) {
|
if (wantEditLogging()) {
|
||||||
|
|
|
@ -117,6 +117,7 @@ public:
|
||||||
|
|
||||||
// check if the avatar is a child of this entity, If so set the avatar parentID to null
|
// check if the avatar is a child of this entity, If so set the avatar parentID to null
|
||||||
void unhookChildAvatar(const EntityItemID entityID);
|
void unhookChildAvatar(const EntityItemID entityID);
|
||||||
|
void removeCloneIDFromCloneParent(const EntityItemID& entityID);
|
||||||
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true);
|
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true);
|
||||||
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = true);
|
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = true);
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ Buffer::Update::Update(const Buffer& parent) : buffer(parent) {
|
||||||
void Buffer::Update::apply() const {
|
void Buffer::Update::apply() const {
|
||||||
// Make sure we're loaded in order
|
// Make sure we're loaded in order
|
||||||
buffer._applyUpdateCount++;
|
buffer._applyUpdateCount++;
|
||||||
assert(buffer._applyUpdateCount == updateNumber);
|
//assert(buffer._applyUpdateCount == updateNumber);
|
||||||
|
|
||||||
const auto pageSize = buffer._pages._pageSize;
|
const auto pageSize = buffer._pages._pageSize;
|
||||||
buffer._renderSysmem.resize(size);
|
buffer._renderSysmem.resize(size);
|
||||||
|
|
|
@ -29,10 +29,11 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::DomainList:
|
case PacketType::DomainList:
|
||||||
return static_cast<PacketVersion>(DomainListVersion::GetMachineFingerprintFromUUIDSupport);
|
return static_cast<PacketVersion>(DomainListVersion::GetMachineFingerprintFromUUIDSupport);
|
||||||
case PacketType::EntityAdd:
|
case PacketType::EntityAdd:
|
||||||
|
case PacketType::EntityClone:
|
||||||
case PacketType::EntityEdit:
|
case PacketType::EntityEdit:
|
||||||
case PacketType::EntityData:
|
case PacketType::EntityData:
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
return static_cast<PacketVersion>(EntityVersion::MaterialData);
|
return static_cast<PacketVersion>(EntityVersion::CloneableData);
|
||||||
case PacketType::EntityQuery:
|
case PacketType::EntityQuery:
|
||||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||||
case PacketType::AvatarIdentity:
|
case PacketType::AvatarIdentity:
|
||||||
|
|
|
@ -75,6 +75,7 @@ public:
|
||||||
EntityData,
|
EntityData,
|
||||||
EntityQuery,
|
EntityQuery,
|
||||||
EntityAdd,
|
EntityAdd,
|
||||||
|
EntityClone,
|
||||||
EntityErase,
|
EntityErase,
|
||||||
EntityEdit,
|
EntityEdit,
|
||||||
DomainServerConnectionToken,
|
DomainServerConnectionToken,
|
||||||
|
@ -232,7 +233,8 @@ enum class EntityVersion : PacketVersion {
|
||||||
SoftEntities,
|
SoftEntities,
|
||||||
MaterialEntities,
|
MaterialEntities,
|
||||||
ShadowControl,
|
ShadowControl,
|
||||||
MaterialData
|
MaterialData,
|
||||||
|
CloneableData
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||||
|
|
|
@ -1040,11 +1040,11 @@ function loaded() {
|
||||||
elWantsTrigger.checked = false;
|
elWantsTrigger.checked = false;
|
||||||
elIgnoreIK.checked = true;
|
elIgnoreIK.checked = true;
|
||||||
|
|
||||||
elCloneable.checked = false;
|
elCloneable.checked = properties.cloneable;
|
||||||
elCloneableDynamic.checked = false;
|
elCloneableDynamic.checked = properties.cloneableDynamic;
|
||||||
elCloneableGroup.style.display = elCloneable.checked ? "block": "none";
|
elCloneableGroup.style.display = elCloneable.checked ? "block": "none";
|
||||||
elCloneableLimit.value = 0;
|
elCloneableLimit.value = properties.cloneableLimit;
|
||||||
elCloneableLifetime.value = 300;
|
elCloneableLifetime.value = properties.cloneableLifetime;
|
||||||
|
|
||||||
var grabbablesSet = false;
|
var grabbablesSet = false;
|
||||||
var parsedUserData = {};
|
var parsedUserData = {};
|
||||||
|
@ -1069,27 +1069,6 @@ function loaded() {
|
||||||
} else {
|
} else {
|
||||||
elIgnoreIK.checked = true;
|
elIgnoreIK.checked = true;
|
||||||
}
|
}
|
||||||
if ("cloneable" in grabbableData) {
|
|
||||||
elCloneable.checked = grabbableData.cloneable;
|
|
||||||
elCloneableGroup.style.display = elCloneable.checked ? "block" : "none";
|
|
||||||
elCloneableDynamic.checked =
|
|
||||||
grabbableData.cloneDynamic ? grabbableData.cloneDynamic : properties.dynamic;
|
|
||||||
if (elCloneable.checked) {
|
|
||||||
if ("cloneLifetime" in grabbableData) {
|
|
||||||
elCloneableLifetime.value =
|
|
||||||
grabbableData.cloneLifetime ? grabbableData.cloneLifetime : 300;
|
|
||||||
}
|
|
||||||
if ("cloneLimit" in grabbableData) {
|
|
||||||
elCloneableLimit.value = grabbableData.cloneLimit ? grabbableData.cloneLimit : 0;
|
|
||||||
}
|
|
||||||
if ("cloneAvatarEntity" in grabbableData) {
|
|
||||||
elCloneableAvatarEntity.checked =
|
|
||||||
grabbableData.cloneAvatarEntity ? grabbableData.cloneAvatarEntity : false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
elCloneable.checked = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO: What should go here?
|
// TODO: What should go here?
|
||||||
|
@ -1460,45 +1439,11 @@ function loaded() {
|
||||||
}
|
}
|
||||||
userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, true);
|
userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, true);
|
||||||
});
|
});
|
||||||
elCloneableDynamic.addEventListener('change', function(event) {
|
|
||||||
userDataChanger("grabbableKey", "cloneDynamic", event.target, elUserData, -1);
|
|
||||||
});
|
|
||||||
|
|
||||||
elCloneableAvatarEntity.addEventListener('change', function(event) {
|
elCloneable.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneable'));
|
||||||
userDataChanger("grabbableKey", "cloneAvatarEntity", event.target, elUserData, -1);
|
elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneableDynamic'));
|
||||||
});
|
elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLifetime'));
|
||||||
|
elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLimit'));
|
||||||
elCloneable.addEventListener('change', function (event) {
|
|
||||||
var checked = event.target.checked;
|
|
||||||
if (checked) {
|
|
||||||
multiDataUpdater("grabbableKey", {
|
|
||||||
cloneLifetime: elCloneableLifetime,
|
|
||||||
cloneLimit: elCloneableLimit,
|
|
||||||
cloneDynamic: elCloneableDynamic,
|
|
||||||
cloneAvatarEntity: elCloneableAvatarEntity,
|
|
||||||
cloneable: event.target,
|
|
||||||
grabbable: null
|
|
||||||
}, elUserData, {});
|
|
||||||
elCloneableGroup.style.display = "block";
|
|
||||||
updateProperty('dynamic', false);
|
|
||||||
} else {
|
|
||||||
multiDataUpdater("grabbableKey", {
|
|
||||||
cloneLifetime: null,
|
|
||||||
cloneLimit: null,
|
|
||||||
cloneDynamic: null,
|
|
||||||
cloneAvatarEntity: null,
|
|
||||||
cloneable: false
|
|
||||||
}, elUserData, {});
|
|
||||||
elCloneableGroup.style.display = "none";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var numberListener = function (event) {
|
|
||||||
userDataChanger("grabbableKey",
|
|
||||||
event.target.getAttribute("data-user-data-type"), parseInt(event.target.value), elUserData, false);
|
|
||||||
};
|
|
||||||
elCloneableLifetime.addEventListener('change', numberListener);
|
|
||||||
elCloneableLimit.addEventListener('change', numberListener);
|
|
||||||
|
|
||||||
elWantsTrigger.addEventListener('change', function() {
|
elWantsTrigger.addEventListener('change', function() {
|
||||||
userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false);
|
userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false);
|
||||||
|
|
|
@ -33,8 +33,7 @@ if (typeof Object.assign !== 'function') {
|
||||||
|
|
||||||
entityIsCloneable = function(props) {
|
entityIsCloneable = function(props) {
|
||||||
if (props) {
|
if (props) {
|
||||||
var grabbableData = getGrabbableData(props);
|
return props.cloneable;
|
||||||
return grabbableData.cloneable;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -42,56 +41,19 @@ entityIsCloneable = function(props) {
|
||||||
propsAreCloneDynamic = function(props) {
|
propsAreCloneDynamic = function(props) {
|
||||||
var cloneable = entityIsCloneable(props);
|
var cloneable = entityIsCloneable(props);
|
||||||
if (cloneable) {
|
if (cloneable) {
|
||||||
var grabInfo = getGrabbableData(props);
|
return props.cloneableDynamic;
|
||||||
if (grabInfo.cloneDynamic) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
cloneEntity = function(props, worldEntityProps) {
|
cloneEntity = function(props, worldEntityProps) {
|
||||||
// we need all the properties, for this
|
var entityToClone = props.id;
|
||||||
var cloneableProps = Entities.getEntityProperties(props.id);
|
var certificateID = Entities.getEntityProperties(entityToClone, ['certificateID']).certificateID;
|
||||||
|
// ensure entity is cloneable and does not have a certificate ID, whereas cloneable limits
|
||||||
var count = 0;
|
// will now be handled by the server where the entity add will fail if limit reached
|
||||||
worldEntityProps.forEach(function(itemWE) {
|
if (entityIsCloneable(props) && (certificateID === undefined || certificateID.length === 0)) {
|
||||||
if (itemWE.name.indexOf('-clone-' + cloneableProps.id) !== -1) {
|
var cloneID = Entities.cloneEntity(entityToClone);
|
||||||
count++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var grabInfo = getGrabbableData(cloneableProps);
|
|
||||||
var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0;
|
|
||||||
if (count >= limit && limit !== 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
cloneableProps.name = cloneableProps.name + '-clone-' + cloneableProps.id;
|
|
||||||
var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300;
|
|
||||||
var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false;
|
|
||||||
var triggerable = grabInfo.triggerable ? grabInfo.triggerable : false;
|
|
||||||
var avatarEntity = grabInfo.cloneAvatarEntity ? grabInfo.cloneAvatarEntity : false;
|
|
||||||
var cUserData = Object.assign({}, JSON.parse(cloneableProps.userData));
|
|
||||||
var cProperties = Object.assign({}, cloneableProps);
|
|
||||||
|
|
||||||
|
|
||||||
delete cUserData.grabbableKey.cloneLifetime;
|
|
||||||
delete cUserData.grabbableKey.cloneable;
|
|
||||||
delete cUserData.grabbableKey.cloneDynamic;
|
|
||||||
delete cUserData.grabbableKey.cloneLimit;
|
|
||||||
delete cUserData.grabbableKey.cloneAvatarEntity;
|
|
||||||
delete cProperties.id;
|
|
||||||
|
|
||||||
|
|
||||||
cProperties.dynamic = dynamic;
|
|
||||||
cProperties.locked = false;
|
|
||||||
cUserData.grabbableKey.triggerable = triggerable;
|
|
||||||
cUserData.grabbableKey.grabbable = true;
|
|
||||||
cProperties.lifetime = lifetime;
|
|
||||||
cProperties.userData = JSON.stringify(cUserData);
|
|
||||||
|
|
||||||
var cloneID = Entities.addEntity(cProperties, avatarEntity);
|
|
||||||
return cloneID;
|
return cloneID;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -125,7 +125,8 @@ DISPATCHER_PROPERTIES = [
|
||||||
"dimensions",
|
"dimensions",
|
||||||
"userData",
|
"userData",
|
||||||
"type",
|
"type",
|
||||||
"href"
|
"href",
|
||||||
|
"cloneable"
|
||||||
];
|
];
|
||||||
|
|
||||||
// priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step
|
// priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step
|
||||||
|
|
Loading…
Reference in a new issue