working it seems

But, AABox for zones isn't very helpful (box is _small).  Time to use
the shape of the zone.
This commit is contained in:
David Kelly 2017-02-09 15:39:38 -07:00
parent ff7c9d3546
commit 8d666854c7
8 changed files with 125 additions and 49 deletions

View file

@ -294,16 +294,15 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
tree->setEntityScriptSourceWhitelist("");
}
_entityEditFilters = new EntityEditFilters();
std::static_pointer_cast<EntityTree>(tree)->setEntityEditFilters(_entityEditFilters);
auto entityEditFilters = tree->createEntityEditFilters();
QString filterURL;
if (readOptionString("entityEditFilter", settingsSectionObject, filterURL) && !filterURL.isEmpty()) {
// connect the filterAdded signal, and block edits until you hear back
connect(_entityEditFilters, &EntityEditFilters::filterAdded, this, &EntityServer::entityFilterAdded);
connect(entityEditFilters, &EntityEditFilters::filterAdded, this, &EntityServer::entityFilterAdded);
_entityEditFilters->rejectAll(true);
_entityEditFilters->addFilter(EntityItemID(), filterURL);
entityEditFilters->rejectAll(true);
entityEditFilters->addFilter(EntityItemID(), filterURL);
}
}
@ -311,8 +310,10 @@ void EntityServer::entityFilterAdded(EntityItemID id, bool success) {
if(id.isInvalidID()) {
// this is the domain-wide entity filter, which we want to stop
// the world for
auto entityEditFilters = qobject_cast<EntityEditFilters*>(sender());
if (success) {
_entityEditFilters->rejectAll(false);
qDebug() << "entity edit filter for " << id << "added successfully";
entityEditFilters->rejectAll(false);
} else {
qDebug() << "entity edit filter unsuccessfully added, stopping...";
stop();

View file

@ -77,8 +77,6 @@ private:
QReadWriteLock _viewerSendingStatsLock;
QMap<QUuid, QMap<QUuid, ViewerSendingStats>> _viewerSendingStats;
EntityEditFilters* _entityEditFilters{};
};
#endif // hifi_EntityServer_h

View file

@ -13,58 +13,99 @@
#include <QUrl>
#include <ResourceManager.h>
#include "EntityEditFilters.h"
QList<EntityItemID> EntityEditFilters::getZonesByPosition(glm::vec3& position) {
QList<EntityItemID> zones;
_lock.lockForRead();
qCDebug(entities) << "looking at " << _filterFunctionMap.size() << "possible zones";
for (auto it = _filterFunctionMap.begin(); it != _filterFunctionMap.end(); it++) {
auto id = it.key();
if (!id.isInvalidID()) {
// for now, look it up in the tree (soon we need to cache or similar?)
EntityItemPointer itemPtr = _tree->findEntityByEntityItemID(id);
auto zone = std::dynamic_pointer_cast<ZoneEntityItem>(itemPtr);
if (zone && zone->containsPoint(position)) {
zones.append(id);
}
} else {
zones.append(id);
}
}
_lock.unlock();
return zones;
}
bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, EntityTree::FilterType filterType) {
qCDebug(entities) << "in EntityEditFilters";
// allows us to start entity server and reject all edits until filter is setup
if (_rejectAll) {
qCDebug(entities) << "rejecting all edits";
return false;
}
bool accepted = true;
// get the ids of all the zones (plus the global entity edit filter) that the position
// lies within
auto zoneIDs = getZonesByPosition(position);
qCDebug(entities) << "_filterFunctionMap.size:" << _filterFunctionMap.size();
//first off lets call the filter (if any) that is domain-wide (EntityItemID())
FilterFunctionPair* pair = _filterFunctionMap.value(EntityItemID());
QScriptEngine* engine = _filterScriptEngineMap.value(EntityItemID());
qCDebug(entities) << "pair: " << (qint64) pair << ", engine" << (qint64)engine;
if (pair != nullptr && engine != nullptr) {
// temp debugging -- remove!
qCDebug(entities) << "zones:";
for (auto zoneID : zoneIDs) {
qCDebug(entities) << zoneID << ",";
}
auto oldProperties = propertiesIn.getDesiredProperties();
auto specifiedProperties = propertiesIn.getChangedProperties();
propertiesIn.setDesiredProperties(specifiedProperties);
for (auto it = zoneIDs.begin(); it != zoneIDs.end(); it++) {
qCDebug(entities) << "applying filter for zone" << *it;
qCDebug(entities) << "attempting to call filter";
auto oldProperties = propertiesIn.getDesiredProperties();
auto specifiedProperties = propertiesIn.getChangedProperties();
propertiesIn.setDesiredProperties(specifiedProperties);
QScriptValue inputValues = propertiesIn.copyToScriptValue(engine, false, true, true);
propertiesIn.setDesiredProperties(oldProperties);
// get the filter pair, etc...
_lock.lockForRead();
FilterFunctionPair* pair = _filterFunctionMap.value(*it);
QScriptEngine* engine = _filterScriptEngineMap.value(*it);
_lock.unlock();
qCDebug(entities) << "pair: " << (qint64) pair << ", engine" << (qint64)engine;
if (pair != nullptr && engine != nullptr) {
auto in = QJsonValue::fromVariant(inputValues.toVariant()); // grab json copy now, because the inputValues might be side effected by the filter.
QScriptValueList args;
args << inputValues;
args << filterType;
QScriptValue inputValues = propertiesIn.copyToScriptValue(engine, false, true, true);
propertiesIn.setDesiredProperties(oldProperties);
QScriptValue result = pair->first.call(_nullObjectForFilter, args);
if (pair->second()) {
result = QScriptValue();
}
accepted = result.isObject();
if (accepted) {
qCDebug(entities) << "filter result accepted";
// call rest of them soon
propertiesOut.copyFromScriptValue(result, false);
// Javascript objects are == only if they are the same object. To compare arbitrary values, we need to use JSON.
auto out = QJsonValue::fromVariant(result.toVariant());
wasChanged = in != out;
auto in = QJsonValue::fromVariant(inputValues.toVariant()); // grab json copy now, because the inputValues might be side effected by the filter.
QScriptValueList args;
args << inputValues;
args << filterType;
QScriptValue result = pair->first.call(_nullObjectForFilter, args);
if (pair->second()) {
result = QScriptValue();
}
if (result.isObject()){
qCDebug(entities) << "filter result accepted";
// make propertiesIn reflect the changes, for next filter...
propertiesIn.copyFromScriptValue(result, false);
// and update propertiesOut too. #TODO: this could be more efficient...
propertiesOut.copyFromScriptValue(result, false);
// Javascript objects are == only if they are the same object. To compare arbitrary values, we need to use JSON.
auto out = QJsonValue::fromVariant(result.toVariant());
wasChanged |= (in != out);
} else {
// an edit was rejected, so we stop here and return false
qCDebug(entities) << "Edit rejected by " << *it;
}
}
}
return accepted;
// if we made it here,
return true;
}
void EntityEditFilters::removeFilter(EntityItemID& entityID) {
QWriteLocker writeLock(&_lock);
QScriptEngine* engine = _filterScriptEngineMap.value(entityID);
if (engine) {
_filterScriptEngineMap.remove(entityID);
@ -78,6 +119,7 @@ void EntityEditFilters::removeFilter(EntityItemID& entityID) {
}
void EntityEditFilters::addFilter(EntityItemID& entityID, QString filterURL) {
QUrl scriptURL(filterURL);
// The following should be abstracted out for use in Agent.cpp (and maybe later AvatarMixer.cpp)
@ -151,8 +193,10 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) {
engine->evaluate(scriptContents);
if (!hadUncaughtExceptions(*engine, urlString)) {
// put the engine in the engine map (so we don't leak them, etc...)
_lock.lockForWrite();
_filterScriptEngineMap.insert(entityID, engine);
_lock.unlock();
// define the uncaughtException function
FilterFunctionPair* pair = new FilterFunctionPair();
QScriptEngine& engineRef = *engine;
@ -160,12 +204,20 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) {
// now get the filter function
auto global = engine->globalObject();
auto entitiesObject = engine->newObject();
entitiesObject.setProperty("ADD_FILTER_TYPE", EntityTree::FilterType::Add);
entitiesObject.setProperty("EDIT_FILTER_TYPE", EntityTree::FilterType::Edit);
entitiesObject.setProperty("PHYSICS_FILTER_TYPE", EntityTree::FilterType::Physics);
global.setProperty("Entities", entitiesObject);
pair->first = global.property("filter");
if (!pair->first.isFunction()) {
qDebug() << "Filter function specified but not found. Ignoring filter";
return;
}
_filterFunctionMap.insert(entityID, pair);
_lock.lockForWrite();
_filterFunctionMap.insert(entityID, pair);
_lock.unlock();
qDebug() << "script request filter processed for entity id " << entityID;
emit filterAdded(entityID, true);

View file

@ -29,6 +29,7 @@ class EntityEditFilters : public QObject {
Q_OBJECT
public:
EntityEditFilters() {};
EntityEditFilters(EntityTreePointer tree ): _tree(tree) {};
void addFilter(EntityItemID& entityID, QString filterURL);
void removeFilter(EntityItemID& entityID);
@ -43,8 +44,13 @@ private slots:
void scriptRequestFinished(EntityItemID entityID);
private:
QList<EntityItemID> getZonesByPosition(glm::vec3& position);
EntityTreePointer _tree {};
bool _rejectAll {false};
QScriptValue _nullObjectForFilter{};
QReadWriteLock _lock;
QMap<EntityItemID, FilterFunctionPair*> _filterFunctionMap;
QMap<EntityItemID, QScriptEngine*> _filterScriptEngineMap;
};

View file

@ -62,9 +62,6 @@ EntityTree::EntityTree(bool shouldReaverage) :
EntityTree::~EntityTree() {
eraseAllOctreeElements(false);
if (_entityEditFilters) {
delete _entityEditFilters;
}
}
void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) {
@ -1751,3 +1748,8 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const {
}
return entity->getJointNames();
}
EntityEditFilters* EntityTree::createEntityEditFilters() {
_entityEditFilters = new EntityEditFilters(getThisPointer());
return _entityEditFilters;
}

View file

@ -275,7 +275,9 @@ public:
void initEntityEditFilterEngine(QScriptEngine* engine, std::function<bool()> entityEditFilterHadUncaughtExceptions);
void setHasEntityFilter(bool hasFilter) { _hasEntityEditFilter = hasFilter; }
void setEntityEditFilters(EntityEditFilters* entityEditFilters) { _entityEditFilters = entityEditFilters; }
EntityEditFilters* createEntityEditFilters();
EntityEditFilters* getEntityEditFilters() { return _entityEditFilters; }
static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME;
public slots:

View file

@ -19,6 +19,9 @@
#include "EntityTree.h"
#include "EntityTreeElement.h"
#include "ZoneEntityItem.h"
#include "EntityEditFilters.h"
#include <intrin.h>
bool ZoneEntityItem::_zonesArePickable = false;
bool ZoneEntityItem::_drawZoneBoundaries = false;
@ -221,12 +224,24 @@ bool ZoneEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
return _zonesArePickable;
}
void ZoneEntityItem::setFilterURL(QString url) {
_filterURL = url;
if (getTree()) {
auto entityEditFilters = getTree()->getEntityEditFilters();
if (entityEditFilters) {
qCDebug(entities) << "adding filter " << url << "for zone" << getEntityItemID();
entityEditFilters->addFilter(getEntityItemID(), url);
}
}
}
bool ZoneEntityItem::containsPoint(glm::vec3& position) {
// use _shapeType shortly
// for now bounding box just so I can get end-to-end working
bool success;
AABox box = getAABox(success);
if (success) {
qCDebug(entities) << "box: " << box;
return box.contains(position);
}
// just return false if no AABox

View file

@ -75,7 +75,7 @@ public:
bool getGhostingAllowed() const { return _ghostingAllowed; }
void setGhostingAllowed(bool value) { _ghostingAllowed = value; }
QString getFilterURL() const { return _filterURL; }
void setFilterURL(const QString url) { _filterURL = url; }
void setFilterURL(const QString url);
bool containsPoint(glm::vec3& position);
virtual bool supportsDetailedRayIntersection() const override { return true; }