mirror of
https://github.com/overte-org/overte.git
synced 2025-06-20 09:20:19 +02:00
first cut at moving entity scripts into ScriptEngine
This commit is contained in:
parent
56118e4204
commit
18fbf896f1
6 changed files with 283 additions and 316 deletions
|
@ -90,10 +90,12 @@ EntityTreeRenderer::~EntityTreeRenderer() {
|
||||||
|
|
||||||
void EntityTreeRenderer::clear() {
|
void EntityTreeRenderer::clear() {
|
||||||
leaveAllEntities();
|
leaveAllEntities();
|
||||||
|
/*
|
||||||
foreach (const EntityItemID& entityID, _entityScripts.keys()) {
|
foreach (const EntityItemID& entityID, _entityScripts.keys()) {
|
||||||
checkAndCallUnload(entityID);
|
checkAndCallUnload(entityID);
|
||||||
}
|
}
|
||||||
_entityScripts.clear();
|
_entityScripts.clear();
|
||||||
|
*/
|
||||||
|
|
||||||
auto scene = _viewState->getMain3DScene();
|
auto scene = _viewState->getMain3DScene();
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
|
@ -136,175 +138,8 @@ void EntityTreeRenderer::shutdown() {
|
||||||
_shuttingDown = true;
|
_shuttingDown = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::scriptContentsAvailable(const QUrl& url, const QString& scriptContents) {
|
|
||||||
if (_waitingOnPreload.contains(url)) {
|
|
||||||
QList<EntityItemID> entityIDs = _waitingOnPreload.values(url);
|
|
||||||
_waitingOnPreload.remove(url);
|
|
||||||
foreach(EntityItemID entityID, entityIDs) {
|
|
||||||
checkAndCallPreload(entityID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTreeRenderer::errorInLoadingScript(const QUrl& url) {
|
|
||||||
if (_waitingOnPreload.contains(url)) {
|
|
||||||
_waitingOnPreload.remove(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID, bool isPreload, bool reload) {
|
|
||||||
EntityItemPointer entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);
|
|
||||||
return loadEntityScript(entity, isPreload, reload);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut,
|
|
||||||
bool& reload) {
|
|
||||||
isPending = false;
|
|
||||||
QUrl url(scriptMaybeURLorText);
|
|
||||||
|
|
||||||
// If the url is not valid, this must be script text...
|
|
||||||
// We document "direct injection" scripts as starting with "(function...", and that would never be a valid url.
|
|
||||||
// But QUrl thinks it is.
|
|
||||||
if (!url.isValid() || scriptMaybeURLorText.startsWith("(")) {
|
|
||||||
isURL = false;
|
|
||||||
return scriptMaybeURLorText;
|
|
||||||
}
|
|
||||||
isURL = true;
|
|
||||||
urlOut = url;
|
|
||||||
|
|
||||||
QString scriptContents; // assume empty
|
|
||||||
|
|
||||||
// if the scheme length is one or lower, maybe they typed in a file, let's try
|
|
||||||
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
|
|
||||||
if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
|
||||||
url = QUrl::fromLocalFile(scriptMaybeURLorText);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ok, let's see if it's valid... and if so, load it
|
|
||||||
if (url.isValid()) {
|
|
||||||
if (url.scheme() == "file") {
|
|
||||||
QString fileName = url.toLocalFile();
|
|
||||||
QFile scriptFile(fileName);
|
|
||||||
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
|
|
||||||
qCDebug(entitiesrenderer) << "Loading file:" << fileName;
|
|
||||||
QTextStream in(&scriptFile);
|
|
||||||
scriptContents = in.readAll();
|
|
||||||
} else {
|
|
||||||
qCDebug(entitiesrenderer) << "ERROR Loading file:" << fileName;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
|
||||||
|
|
||||||
if (!scriptCache->isInBadScriptList(url)) {
|
|
||||||
scriptContents = scriptCache->getScript(url, this, isPending, reload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return scriptContents;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool isPreload, bool reload) {
|
|
||||||
if (_shuttingDown) {
|
|
||||||
return QScriptValue(); // since we're shutting down, we don't load any more scripts
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entity) {
|
|
||||||
return QScriptValue(); // no entity...
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: we keep local variables for the entityID and the script because
|
|
||||||
// below in loadScriptContents() it's possible for us to execute the
|
|
||||||
// application event loop, which may cause our entity to be deleted on
|
|
||||||
// us. We don't really need access the entity after this point, can
|
|
||||||
// can accomplish all we need to here with just the script "text" and the ID.
|
|
||||||
EntityItemID entityID = entity->getEntityItemID();
|
|
||||||
QString entityScript = entity->getScript();
|
|
||||||
|
|
||||||
if (_entityScripts.contains(entityID)) {
|
|
||||||
EntityScriptDetails details = _entityScripts[entityID];
|
|
||||||
|
|
||||||
// check to make sure our script text hasn't changed on us since we last loaded it and we're not redownloading it
|
|
||||||
if (details.scriptText == entityScript && !reload) {
|
|
||||||
return details.scriptObject; // previously loaded
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we got here, then we previously loaded a script, but the entity's script value
|
|
||||||
// has changed and so we need to reload it.
|
|
||||||
_entityScripts.remove(entityID);
|
|
||||||
}
|
|
||||||
if (entityScript.isEmpty()) {
|
|
||||||
return QScriptValue(); // no script
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isURL = false; // loadScriptContents() will tell us if this is a URL or just text.
|
|
||||||
bool isPending = false;
|
|
||||||
QUrl url;
|
|
||||||
QString scriptContents = loadScriptContents(entityScript, isURL, isPending, url, reload);
|
|
||||||
|
|
||||||
if (isPending && isPreload && isURL) {
|
|
||||||
_waitingOnPreload.insert(url, entityID);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
|
||||||
|
|
||||||
if (isURL && scriptCache->isInBadScriptList(url)) {
|
|
||||||
return QScriptValue(); // no script contents...
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scriptContents.isEmpty()) {
|
|
||||||
return QScriptValue(); // no script contents...
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(scriptContents);
|
|
||||||
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
|
|
||||||
qCDebug(entitiesrenderer) << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;
|
|
||||||
qCDebug(entitiesrenderer) << " " << syntaxCheck.errorMessage() << ":"
|
|
||||||
<< syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();
|
|
||||||
qCDebug(entitiesrenderer) << " SCRIPT:" << entityScript;
|
|
||||||
|
|
||||||
scriptCache->addScriptToBadScriptList(url);
|
|
||||||
|
|
||||||
return QScriptValue(); // invalid script
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isURL) {
|
|
||||||
_entitiesScriptEngine->setParentURL(entity->getScript());
|
|
||||||
}
|
|
||||||
QScriptValue entityScriptConstructor = _sandboxScriptEngine->evaluate(scriptContents);
|
|
||||||
|
|
||||||
if (!entityScriptConstructor.isFunction()) {
|
|
||||||
qCDebug(entitiesrenderer) << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;
|
|
||||||
qCDebug(entitiesrenderer) << " NOT CONSTRUCTOR";
|
|
||||||
qCDebug(entitiesrenderer) << " SCRIPT:" << entityScript;
|
|
||||||
|
|
||||||
scriptCache->addScriptToBadScriptList(url);
|
|
||||||
|
|
||||||
return QScriptValue(); // invalid script
|
|
||||||
} else {
|
|
||||||
entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents);
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue entityScriptObject = entityScriptConstructor.construct();
|
|
||||||
EntityScriptDetails newDetails = { entityScript, entityScriptObject };
|
|
||||||
_entityScripts[entityID] = newDetails;
|
|
||||||
|
|
||||||
if (isURL) {
|
|
||||||
_entitiesScriptEngine->setParentURL("");
|
|
||||||
}
|
|
||||||
|
|
||||||
return entityScriptObject; // newly constructed
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue EntityTreeRenderer::getPreviouslyLoadedEntityScript(const EntityItemID& entityID) {
|
|
||||||
if (_entityScripts.contains(entityID)) {
|
|
||||||
EntityScriptDetails details = _entityScripts[entityID];
|
|
||||||
return details.scriptObject; // previously loaded
|
|
||||||
}
|
|
||||||
return QScriptValue(); // no script
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTreeRenderer::setTree(Octree* newTree) {
|
void EntityTreeRenderer::setTree(Octree* newTree) {
|
||||||
OctreeRenderer::setTree(newTree);
|
OctreeRenderer::setTree(newTree);
|
||||||
|
@ -324,11 +159,7 @@ void EntityTreeRenderer::update() {
|
||||||
// and we want to simulate this message here as well as in mouse move
|
// and we want to simulate this message here as well as in mouse move
|
||||||
if (_lastMouseEventValid && !_currentClickingOnEntityID.isInvalidID()) {
|
if (_lastMouseEventValid && !_currentClickingOnEntityID.isInvalidID()) {
|
||||||
emit holdingClickOnEntity(_currentClickingOnEntityID, _lastMouseEvent);
|
emit holdingClickOnEntity(_currentClickingOnEntityID, _lastMouseEvent);
|
||||||
QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, _lastMouseEvent);
|
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", _lastMouseEvent);
|
||||||
QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID);
|
|
||||||
if (currentClickingEntity.property("holdingClickOnEntity").isValid()) {
|
|
||||||
currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -356,19 +187,14 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
|
||||||
_tree->unlock();
|
_tree->unlock();
|
||||||
|
|
||||||
// Note: at this point we don't need to worry about the tree being locked, because we only deal with
|
// Note: at this point we don't need to worry about the tree being locked, because we only deal with
|
||||||
// EntityItemIDs from here. The loadEntityScript() method is robust against attempting to load scripts
|
// EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts
|
||||||
// for entity IDs that no longer exist.
|
// for entity IDs that no longer exist.
|
||||||
|
|
||||||
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
||||||
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
||||||
if (!entitiesContainingAvatar.contains(entityID)) {
|
if (!entitiesContainingAvatar.contains(entityID)) {
|
||||||
emit leaveEntity(entityID);
|
emit leaveEntity(entityID);
|
||||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||||
QScriptValue entityScript = loadEntityScript(entityID);
|
|
||||||
if (entityScript.property("leaveEntity").isValid()) {
|
|
||||||
entityScript.property("leaveEntity").call(entityScript, entityArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,11 +202,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
|
||||||
foreach(const EntityItemID& entityID, entitiesContainingAvatar) {
|
foreach(const EntityItemID& entityID, entitiesContainingAvatar) {
|
||||||
if (!_currentEntitiesInside.contains(entityID)) {
|
if (!_currentEntitiesInside.contains(entityID)) {
|
||||||
emit enterEntity(entityID);
|
emit enterEntity(entityID);
|
||||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
_entitiesScriptEngine->callEntityScriptMethod(entityID, "enterEntity");
|
||||||
QScriptValue entityScript = loadEntityScript(entityID);
|
|
||||||
if (entityScript.property("enterEntity").isValid()) {
|
|
||||||
entityScript.property("enterEntity").call(entityScript, entityArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_currentEntitiesInside = entitiesContainingAvatar;
|
_currentEntitiesInside = entitiesContainingAvatar;
|
||||||
|
@ -395,11 +217,7 @@ void EntityTreeRenderer::leaveAllEntities() {
|
||||||
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
||||||
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
||||||
emit leaveEntity(entityID);
|
emit leaveEntity(entityID);
|
||||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||||
QScriptValue entityScript = loadEntityScript(entityID);
|
|
||||||
if (entityScript.property("leaveEntity").isValid()) {
|
|
||||||
entityScript.property("leaveEntity").call(entityScript, entityArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_currentEntitiesInside.clear();
|
_currentEntitiesInside.clear();
|
||||||
|
|
||||||
|
@ -821,27 +639,6 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS
|
||||||
connect(DependencyManager::get<SceneScriptingInterface>().data(), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection);
|
connect(DependencyManager::get<SceneScriptingInterface>().data(), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) {
|
|
||||||
QScriptValueList args;
|
|
||||||
args << entityID.toScriptValue(_entitiesScriptEngine);
|
|
||||||
args << MouseEvent(*event, deviceID).toScriptValue(_entitiesScriptEngine);
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent) {
|
|
||||||
QScriptValueList args;
|
|
||||||
args << entityID.toScriptValue(_entitiesScriptEngine);
|
|
||||||
args << mouseEvent.toScriptValue(_entitiesScriptEngine);
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QScriptValueList EntityTreeRenderer::createEntityArgs(const EntityItemID& entityID) {
|
|
||||||
QScriptValueList args;
|
|
||||||
args << entityID.toScriptValue(_entitiesScriptEngine);
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
// If we don't have a tree, or we're in the process of shutting down, then don't
|
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||||
// process these events.
|
// process these events.
|
||||||
|
@ -864,18 +661,11 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device
|
||||||
}
|
}
|
||||||
|
|
||||||
emit mousePressOnEntity(rayPickResult, event, deviceID);
|
emit mousePressOnEntity(rayPickResult, event, deviceID);
|
||||||
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", MouseEvent(*event, deviceID));
|
||||||
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
|
||||||
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
|
||||||
if (entityScript.property("mousePressOnEntity").isValid()) {
|
|
||||||
entityScript.property("mousePressOnEntity").call(entityScript, entityScriptArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentClickingOnEntityID = rayPickResult.entityID;
|
_currentClickingOnEntityID = rayPickResult.entityID;
|
||||||
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||||
if (entityScript.property("clickDownOnEntity").isValid()) {
|
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", MouseEvent(*event, deviceID));
|
||||||
entityScript.property("clickDownOnEntity").call(entityScript, entityScriptArgs);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
emit mousePressOffEntity(rayPickResult, event, deviceID);
|
emit mousePressOffEntity(rayPickResult, event, deviceID);
|
||||||
}
|
}
|
||||||
|
@ -896,24 +686,14 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||||
emit mouseReleaseOnEntity(rayPickResult, event, deviceID);
|
emit mouseReleaseOnEntity(rayPickResult, event, deviceID);
|
||||||
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", MouseEvent(*event, deviceID));
|
||||||
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
|
||||||
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
|
||||||
if (entityScript.property("mouseReleaseOnEntity").isValid()) {
|
|
||||||
entityScript.property("mouseReleaseOnEntity").call(entityScript, entityScriptArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even if we're no longer intersecting with an entity, if we started clicking on it, and now
|
// Even if we're no longer intersecting with an entity, if we started clicking on it, and now
|
||||||
// we're releasing the button, then this is considered a clickOn event
|
// we're releasing the button, then this is considered a clickOn event
|
||||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||||
emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||||
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", MouseEvent(*event, deviceID));
|
||||||
QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID);
|
|
||||||
QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID);
|
|
||||||
if (currentClickingEntity.property("clickReleaseOnEntity").isValid()) {
|
|
||||||
currentClickingEntity.property("clickReleaseOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// makes it the unknown ID, we just released so we can't be clicking on anything
|
// makes it the unknown ID, we just released so we can't be clicking on anything
|
||||||
|
@ -935,17 +715,9 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
bool precisionPicking = false; // for mouse moves we do not do precision picking
|
bool precisionPicking = false; // for mouse moves we do not do precision picking
|
||||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
|
||||||
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", MouseEvent(*event, deviceID));
|
||||||
// load the entity script if needed...
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", MouseEvent(*event, deviceID));
|
||||||
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
|
||||||
if (entityScript.property("mouseMoveEvent").isValid()) {
|
|
||||||
entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs);
|
|
||||||
}
|
|
||||||
emit mouseMoveOnEntity(rayPickResult, event, deviceID);
|
|
||||||
if (entityScript.property("mouseMoveOnEntity").isValid()) {
|
|
||||||
entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle the hover logic...
|
// handle the hover logic...
|
||||||
|
|
||||||
|
@ -953,30 +725,19 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
// then we need to send the hover leave.
|
// then we need to send the hover leave.
|
||||||
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
||||||
|
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event, deviceID));
|
||||||
QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID);
|
|
||||||
|
|
||||||
QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID);
|
|
||||||
if (currentHoverEntity.property("hoverLeaveEntity").isValid()) {
|
|
||||||
currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the new hover entity does not match the previous hover entity then we are entering the new one
|
// If the new hover entity does not match the previous hover entity then we are entering the new one
|
||||||
// this is true if the _currentHoverOverEntityID is known or unknown
|
// this is true if the _currentHoverOverEntityID is known or unknown
|
||||||
if (rayPickResult.entityID != _currentHoverOverEntityID) {
|
if (rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||||
emit hoverEnterEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", MouseEvent(*event, deviceID));
|
||||||
if (entityScript.property("hoverEnterEntity").isValid()) {
|
|
||||||
entityScript.property("hoverEnterEntity").call(entityScript, entityScriptArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and
|
// and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and
|
||||||
// we should send our hover over event
|
// we should send our hover over event
|
||||||
emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
||||||
if (entityScript.property("hoverOverEntity").isValid()) {
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", MouseEvent(*event, deviceID));
|
||||||
entityScript.property("hoverOverEntity").call(entityScript, entityScriptArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// remember what we're hovering over
|
// remember what we're hovering over
|
||||||
_currentHoverOverEntityID = rayPickResult.entityID;
|
_currentHoverOverEntityID = rayPickResult.entityID;
|
||||||
|
@ -987,14 +748,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
// send the hover leave for our previous entity
|
// send the hover leave for our previous entity
|
||||||
if (!_currentHoverOverEntityID.isInvalidID()) {
|
if (!_currentHoverOverEntityID.isInvalidID()) {
|
||||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
||||||
|
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event, deviceID));
|
||||||
QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID);
|
|
||||||
|
|
||||||
QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID);
|
|
||||||
if (currentHoverEntity.property("hoverLeaveEntity").isValid()) {
|
|
||||||
currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID
|
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1003,13 +757,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
||||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||||
emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||||
|
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", MouseEvent(*event, deviceID));
|
||||||
QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID);
|
|
||||||
|
|
||||||
QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID);
|
|
||||||
if (currentClickingEntity.property("holdingClickOnEntity").isValid()) {
|
|
||||||
currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_lastMouseEvent = MouseEvent(*event, deviceID);
|
_lastMouseEvent = MouseEvent(*event, deviceID);
|
||||||
_lastMouseEventValid = true;
|
_lastMouseEventValid = true;
|
||||||
|
@ -1017,9 +765,9 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
|
|
||||||
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
||||||
if (_tree && !_shuttingDown) {
|
if (_tree && !_shuttingDown) {
|
||||||
checkAndCallUnload(entityID);
|
//checkAndCallUnload(entityID);
|
||||||
}
|
}
|
||||||
_entityScripts.remove(entityID);
|
//_entityScripts.remove(entityID);
|
||||||
|
|
||||||
// here's where we remove the entity payload from the scene
|
// here's where we remove the entity payload from the scene
|
||||||
if (_entitiesInScene.contains(entityID)) {
|
if (_entitiesInScene.contains(entityID)) {
|
||||||
|
@ -1032,7 +780,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
|
void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
|
||||||
checkAndCallPreload(entityID);
|
//checkAndCallPreload(entityID);
|
||||||
auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
|
auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
|
||||||
if (entity) {
|
if (entity) {
|
||||||
addEntityToScene(entity);
|
addEntityToScene(entity);
|
||||||
|
@ -1059,22 +807,14 @@ void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID, const
|
||||||
|
|
||||||
void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const bool reload) {
|
void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const bool reload) {
|
||||||
if (_tree && !_shuttingDown) {
|
if (_tree && !_shuttingDown) {
|
||||||
// load the entity script if needed...
|
EntityItemPointer entity = getTree()->findEntityByEntityItemID(entityID);
|
||||||
QScriptValue entityScript = loadEntityScript(entityID, true, reload); // is preload!
|
_entitiesScriptEngine->loadEntityScript(entityID, entity->getScript(), reload);
|
||||||
if (entityScript.property("preload").isValid()) {
|
|
||||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
|
||||||
entityScript.property("preload").call(entityScript, entityArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) {
|
void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) {
|
||||||
if (_tree && !_shuttingDown) {
|
if (_tree && !_shuttingDown) {
|
||||||
QScriptValue entityScript = getPreviouslyLoadedEntityScript(entityID);
|
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||||
if (entityScript.property("unload").isValid()) {
|
|
||||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
|
||||||
entityScript.property("unload").call(entityScript, entityArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1152,24 +892,9 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
||||||
|
|
||||||
// And now the entity scripts
|
// And now the entity scripts
|
||||||
emit collisionWithEntity(idA, idB, collision);
|
emit collisionWithEntity(idA, idB, collision);
|
||||||
QScriptValue entityScriptA = loadEntityScript(idA);
|
_entitiesScriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision);
|
||||||
if (entityScriptA.property("collisionWithEntity").isValid()) {
|
|
||||||
QScriptValueList args;
|
|
||||||
args << idA.toScriptValue(_entitiesScriptEngine);
|
|
||||||
args << idB.toScriptValue(_entitiesScriptEngine);
|
|
||||||
args << collisionToScriptValue(_entitiesScriptEngine, collision);
|
|
||||||
entityScriptA.property("collisionWithEntity").call(entityScriptA, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit collisionWithEntity(idB, idA, collision);
|
emit collisionWithEntity(idB, idA, collision);
|
||||||
QScriptValue entityScriptB = loadEntityScript(idB);
|
_entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, collision);
|
||||||
if (entityScriptB.property("collisionWithEntity").isValid()) {
|
|
||||||
QScriptValueList args;
|
|
||||||
args << idB.toScriptValue(_entitiesScriptEngine);
|
|
||||||
args << idA.toScriptValue(_entitiesScriptEngine);
|
|
||||||
args << collisionToScriptValue(_entitiesScriptEngine, collision);
|
|
||||||
entityScriptB.property("collisionWithEntity").call(entityScriptA, args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
|
void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
|
||||||
|
|
|
@ -28,14 +28,9 @@ class Model;
|
||||||
class ScriptEngine;
|
class ScriptEngine;
|
||||||
class ZoneEntityItem;
|
class ZoneEntityItem;
|
||||||
|
|
||||||
class EntityScriptDetails {
|
|
||||||
public:
|
|
||||||
QString scriptText;
|
|
||||||
QScriptValue scriptObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generic client side Octree renderer class.
|
// Generic client side Octree renderer class.
|
||||||
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public ScriptUser {
|
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||||
|
@ -87,9 +82,6 @@ public:
|
||||||
/// hovering over, and entering entities
|
/// hovering over, and entering entities
|
||||||
void connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface);
|
void connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface);
|
||||||
|
|
||||||
virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents);
|
|
||||||
virtual void errorInLoadingScript(const QUrl& url);
|
|
||||||
|
|
||||||
// For Scene.shouldRenderEntities
|
// For Scene.shouldRenderEntities
|
||||||
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
||||||
|
|
||||||
|
@ -157,10 +149,8 @@ private:
|
||||||
QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false, bool reload = false);
|
QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false, bool reload = false);
|
||||||
QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID);
|
QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID);
|
||||||
QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url, bool& reload);
|
QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url, bool& reload);
|
||||||
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID);
|
|
||||||
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent);
|
|
||||||
|
|
||||||
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
//QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
||||||
|
|
||||||
void playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision);
|
void playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision);
|
||||||
|
|
||||||
|
|
|
@ -81,4 +81,51 @@ void ScriptCache::scriptDownloaded() {
|
||||||
req->deleteLater();
|
req->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailableCallback contentAvailable, bool forceDownload) {
|
||||||
|
QUrl unnormalizedURL(scriptOrURL);
|
||||||
|
QUrl url = ResourceManager::normalizeURL(unnormalizedURL);
|
||||||
|
|
||||||
|
// attempt to determine if this is a URL to a script, or if this is actually a script itself (which is valid in the entityScript use case)
|
||||||
|
if (url.scheme().isEmpty() && scriptOrURL.simplified().replace(" ", "").contains("(function(){")) {
|
||||||
|
contentAvailable(scriptOrURL, scriptOrURL, false, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_scriptCache.contains(url) && !forceDownload) {
|
||||||
|
qCDebug(scriptengine) << "Found script in cache:" << url.toString();
|
||||||
|
contentAvailable(url.toString(), _scriptCache[url], true, true);
|
||||||
|
} else {
|
||||||
|
bool alreadyWaiting = _contentCallbacks.contains(url);
|
||||||
|
_contentCallbacks.insert(url, contentAvailable);
|
||||||
|
|
||||||
|
if (alreadyWaiting) {
|
||||||
|
qCDebug(scriptengine) << "Already downloading script at:" << url.toString();
|
||||||
|
} else {
|
||||||
|
auto request = ResourceManager::createResourceRequest(this, url);
|
||||||
|
request->setCacheEnabled(!forceDownload);
|
||||||
|
connect(request, &ResourceRequest::finished, this, &ScriptCache::scriptContentAvailable);
|
||||||
|
request->send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptCache::scriptContentAvailable() {
|
||||||
|
ResourceRequest* req = qobject_cast<ResourceRequest*>(sender());
|
||||||
|
QUrl url = req->getUrl();
|
||||||
|
QList<contentAvailableCallback> allCallbacks = _contentCallbacks.values(url);
|
||||||
|
_contentCallbacks.remove(url);
|
||||||
|
|
||||||
|
bool success = req->getResult() == ResourceRequest::Success;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
_scriptCache[url] = req->getData();
|
||||||
|
qCDebug(scriptengine) << "Done downloading script at:" << url.toString();
|
||||||
|
} else {
|
||||||
|
qCWarning(scriptengine) << "Error loading script from URL " << url;
|
||||||
|
}
|
||||||
|
foreach(contentAvailableCallback thisCallback, allCallbacks) {
|
||||||
|
thisCallback(url.toString(), _scriptCache[url], true, success);
|
||||||
|
}
|
||||||
|
req->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,23 +20,33 @@ public:
|
||||||
virtual void errorInLoadingScript(const QUrl& url) = 0;
|
virtual void errorInLoadingScript(const QUrl& url) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using contentAvailableCallback = std::function<void(const QString& scriptOrURL, const QString& contents, bool isURL, bool contentAvailable)>;
|
||||||
|
|
||||||
/// Interface for loading scripts
|
/// Interface for loading scripts
|
||||||
class ScriptCache : public QObject, public Dependency {
|
class ScriptCache : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
void getScriptContents(const QString& scriptOrURL, contentAvailableCallback contentAvailable, bool forceDownload = false);
|
||||||
|
|
||||||
|
|
||||||
QString getScript(const QUrl& unnormalizedURL, ScriptUser* scriptUser, bool& isPending, bool redownload = false);
|
QString getScript(const QUrl& unnormalizedURL, ScriptUser* scriptUser, bool& isPending, bool redownload = false);
|
||||||
void deleteScript(const QUrl& unnormalizedURL);
|
void deleteScript(const QUrl& unnormalizedURL);
|
||||||
|
|
||||||
|
// FIXME - how do we remove a script from the bad script list in the case of a redownload?
|
||||||
void addScriptToBadScriptList(const QUrl& url) { _badScripts.insert(url); }
|
void addScriptToBadScriptList(const QUrl& url) { _badScripts.insert(url); }
|
||||||
bool isInBadScriptList(const QUrl& url) { return _badScripts.contains(url); }
|
bool isInBadScriptList(const QUrl& url) { return _badScripts.contains(url); }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void scriptDownloaded();
|
void scriptDownloaded(); // old version
|
||||||
|
void scriptContentAvailable(); // new version
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ScriptCache(QObject* parent = NULL);
|
ScriptCache(QObject* parent = NULL);
|
||||||
|
|
||||||
|
QMultiMap<QUrl, contentAvailableCallback> _contentCallbacks;
|
||||||
|
|
||||||
QHash<QUrl, QString> _scriptCache;
|
QHash<QUrl, QString> _scriptCache;
|
||||||
QMultiMap<QUrl, ScriptUser*> _scriptUsers;
|
QMultiMap<QUrl, ScriptUser*> _scriptUsers;
|
||||||
QSet<QUrl> _badScripts;
|
QSet<QUrl> _badScripts;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <QtNetwork/QNetworkRequest>
|
#include <QtNetwork/QNetworkRequest>
|
||||||
#include <QtNetwork/QNetworkReply>
|
#include <QtNetwork/QNetworkReply>
|
||||||
#include <QScriptEngine>
|
#include <QScriptEngine>
|
||||||
|
#include <QScriptValue>
|
||||||
|
|
||||||
#include <AudioConstants.h>
|
#include <AudioConstants.h>
|
||||||
#include <AudioEffectOptions.h>
|
#include <AudioEffectOptions.h>
|
||||||
|
@ -848,3 +849,181 @@ void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// since all of these operations can be asynch we will always do the actual work in the response handler
|
||||||
|
// for the download
|
||||||
|
void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
qDebug() << "*** WARNING *** ScriptEngine::loadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID <<
|
||||||
|
"entityScript:" << entityScript <<
|
||||||
|
"forceRedownload:" << forceRedownload;
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "loadEntityScript",
|
||||||
|
Q_ARG(const EntityItemID&, entityID),
|
||||||
|
Q_ARG(const QString&, entityScript),
|
||||||
|
Q_ARG(bool, forceRedownload));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "ScriptEngine::loadEntityScript() called on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID <<
|
||||||
|
"entityScript:" << entityScript <<
|
||||||
|
"forceRedownload:" << forceRedownload;
|
||||||
|
|
||||||
|
// If we've been called our known entityScripts should not know about us..
|
||||||
|
assert(!_entityScripts.contains(entityID));
|
||||||
|
|
||||||
|
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||||
|
|
||||||
|
scriptCache->getScriptContents(entityScript, [=](const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
|
||||||
|
|
||||||
|
// first check the syntax of the script contents
|
||||||
|
QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents);
|
||||||
|
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
|
||||||
|
qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
|
||||||
|
qCDebug(scriptengine) << " " << syntaxCheck.errorMessage() << ":"
|
||||||
|
<< syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();
|
||||||
|
qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
|
||||||
|
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||||
|
return; // done processing script
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isURL) {
|
||||||
|
setParentURL(scriptOrURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptEngine sandbox;
|
||||||
|
QScriptValue testConstructor = sandbox.evaluate(contents);
|
||||||
|
|
||||||
|
if (!testConstructor.isFunction()) {
|
||||||
|
qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
|
||||||
|
qCDebug(scriptengine) << " NOT CONSTRUCTOR";
|
||||||
|
qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
|
||||||
|
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||||
|
return; // done processing script
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue entityScriptConstructor = evaluate(contents);
|
||||||
|
|
||||||
|
QScriptValue entityScriptObject = entityScriptConstructor.construct();
|
||||||
|
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject };
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
|
|
||||||
|
if (isURL) {
|
||||||
|
setParentURL("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got this far, then call the preload method
|
||||||
|
callEntityScriptMethod(entityID, "preload");
|
||||||
|
|
||||||
|
}, forceRedownload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
qDebug() << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID;
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "unloadEntityScript",
|
||||||
|
Q_ARG(const EntityItemID&, entityID));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID;
|
||||||
|
|
||||||
|
if (_entityScripts.contains(entityID)) {
|
||||||
|
callEntityScriptMethod(entityID, "unload");
|
||||||
|
_entityScripts.remove(entityID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID <<
|
||||||
|
"methodName:" << methodName;
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "callEntityScriptMethod",
|
||||||
|
Q_ARG(const EntityItemID&, entityID),
|
||||||
|
Q_ARG(const QString&, methodName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID <<
|
||||||
|
"methodName:" << methodName;
|
||||||
|
|
||||||
|
if (_entityScripts.contains(entityID)) {
|
||||||
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
|
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||||
|
if (entityScript.property(methodName).isFunction()) {
|
||||||
|
QScriptValueList args;
|
||||||
|
args << entityID.toScriptValue(this);
|
||||||
|
entityScript.property(methodName).call(entityScript, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID <<
|
||||||
|
"methodName:" << methodName <<
|
||||||
|
"event: mouseEvent";
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "callEntityScriptMethod",
|
||||||
|
Q_ARG(const EntityItemID&, entityID),
|
||||||
|
Q_ARG(const QString&, methodName),
|
||||||
|
Q_ARG(const MouseEvent&, event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID <<
|
||||||
|
"methodName:" << methodName <<
|
||||||
|
"event: mouseEvent";
|
||||||
|
|
||||||
|
if (_entityScripts.contains(entityID)) {
|
||||||
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
|
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||||
|
if (entityScript.property(methodName).isFunction()) {
|
||||||
|
QScriptValueList args;
|
||||||
|
args << entityID.toScriptValue(this);
|
||||||
|
args << event.toScriptValue(this);
|
||||||
|
entityScript.property(methodName).call(entityScript, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID <<
|
||||||
|
"methodName:" << methodName <<
|
||||||
|
"otherID:" << otherID <<
|
||||||
|
"collision: collision";
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "callEntityScriptMethod",
|
||||||
|
Q_ARG(const EntityItemID&, entityID),
|
||||||
|
Q_ARG(const QString&, methodName),
|
||||||
|
Q_ARG(const EntityItemID&, otherID),
|
||||||
|
Q_ARG(const Collision&, collision));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
|
||||||
|
"entityID:" << entityID <<
|
||||||
|
"methodName:" << methodName <<
|
||||||
|
"otherID:" << otherID <<
|
||||||
|
"collision: collision";
|
||||||
|
|
||||||
|
if (_entityScripts.contains(entityID)) {
|
||||||
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
|
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||||
|
if (entityScript.property(methodName).isFunction()) {
|
||||||
|
QScriptValueList args;
|
||||||
|
args << entityID.toScriptValue(this);
|
||||||
|
args << otherID.toScriptValue(this);
|
||||||
|
args << collisionToScriptValue(this, collision);
|
||||||
|
entityScript.property(methodName).call(entityScript, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,6 +40,12 @@ const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0f / 60.0f) * 1000 * 1
|
||||||
|
|
||||||
typedef QHash<QString, QScriptValueList> RegisteredEventHandlers;
|
typedef QHash<QString, QScriptValueList> RegisteredEventHandlers;
|
||||||
|
|
||||||
|
class EntityScriptDetails {
|
||||||
|
public:
|
||||||
|
QString scriptText;
|
||||||
|
QScriptValue scriptObject;
|
||||||
|
};
|
||||||
|
|
||||||
class ScriptEngine : public QScriptEngine, public ScriptUser {
|
class ScriptEngine : public QScriptEngine, public ScriptUser {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -100,6 +106,13 @@ public:
|
||||||
Q_INVOKABLE void print(const QString& message);
|
Q_INVOKABLE void print(const QString& message);
|
||||||
Q_INVOKABLE QUrl resolvePath(const QString& path) const;
|
Q_INVOKABLE QUrl resolvePath(const QString& path) const;
|
||||||
|
|
||||||
|
// Entity Script Related methods
|
||||||
|
Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload = false); // will call the preload method once loaded
|
||||||
|
Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method
|
||||||
|
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName);
|
||||||
|
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event);
|
||||||
|
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts
|
// NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts
|
||||||
Q_INVOKABLE void stop();
|
Q_INVOKABLE void stop();
|
||||||
|
@ -149,6 +162,9 @@ protected:
|
||||||
QSet<QUrl> _includedURLs;
|
QSet<QUrl> _includedURLs;
|
||||||
bool _wantSignals = true;
|
bool _wantSignals = true;
|
||||||
|
|
||||||
|
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
QString getFilename() const;
|
QString getFilename() const;
|
||||||
|
|
Loading…
Reference in a new issue