Showing the changes to libraries/entities/src/EntityScriptingInterface.cpp (1 of 53 files). Show all
Unified · Split
| @@ -48,6 +48,36 @@ · 47 unmodified lines | ||
| 48 | 48 | const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}"; |
| 49 | 49 | const QString NOT_GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":false}}"; |
| 50 | 50 | |
| 51 | +static void staticScriptInitializer(ScriptManager* manager) { | |
| 52 | + auto scriptEngine = manager->engine().data(); | |
| 53 | + | |
| 54 | + auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | |
| 55 | + entityScriptingInterface->init(); | |
| 56 | + auto ifacePtr = entityScriptingInterface.data(); // using this when we don't want to leak a reference | |
| 57 | + | |
| 58 | + registerMetaTypes(scriptEngine); | |
| 59 | + | |
| 60 | + scriptRegisterMetaType(scriptEngine, EntityPropertyFlagsToScriptValue, EntityPropertyFlagsFromScriptValue); | |
| 61 | + scriptRegisterMetaType(scriptEngine, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); | |
| 62 | + scriptRegisterMetaType(scriptEngine, EntityPropertyInfoToScriptValue, EntityPropertyInfoFromScriptValue); | |
| 63 | + scriptRegisterMetaType(scriptEngine, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); | |
| 64 | + scriptRegisterMetaType(scriptEngine, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue); | |
| 65 | + | |
| 66 | + scriptEngine->registerGlobalObject("Entities", entityScriptingInterface.data()); | |
| 67 | + scriptEngine->registerFunction("Entities", "getMultipleEntityProperties", EntityScriptingInterface::getMultipleEntityProperties); | |
| 68 | + | |
| 69 | + // "The return value of QObject::sender() is not valid when the slot is called via a Qt::DirectConnection from a thread | |
| 70 | + // different from this object's thread. Do not use this function in this type of scenario." | |
| 71 | + // so... yay lambdas everywhere to get the sender | |
| 72 | + manager->connect( | |
| 73 | + manager, &ScriptManager::attachDefaultEventHandlers, entityScriptingInterface.data(), | |
| 74 | + [ifacePtr, manager] { ifacePtr->attachDefaultEventHandlers(manager); }, | |
| 75 | + Qt::DirectConnection); | |
| 76 | + manager->connect(manager, &ScriptManager::releaseEntityPacketSenderMessages, entityScriptingInterface.data(), | |
| 77 | + &EntityScriptingInterface::releaseEntityPacketSenderMessages, Qt::DirectConnection); | |
| 78 | +} | |
| 79 | +STATIC_SCRIPT_INITIALIZER(staticScriptInitializer); | |
| 80 | + | |
| 51 | 81 | EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) : |
| 52 | 82 | _entityTree(nullptr), |
| 53 | 83 | _bidOnSimulationOwnership(bidOnSimulationOwnership) |
| @@ -67,6 +97,146 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership · 13 unmodified lines | ||
| 67 | 97 | PacketReceiver::makeSourcedListenerReference<EntityScriptingInterface>(this, &EntityScriptingInterface::handleEntityScriptCallMethodPacket)); |
| 68 | 98 | } |
| 69 | 99 | |
| 100 | +void EntityScriptingInterface::releaseEntityPacketSenderMessages(bool wait) { | |
| 101 | + EntityEditPacketSender* entityPacketSender = getEntityPacketSender(); | |
| 102 | + if (entityPacketSender->serversExist()) { | |
| 103 | + // release the queue of edit entity messages. | |
| 104 | + entityPacketSender->releaseQueuedMessages(); | |
| 105 | + | |
| 106 | + // since we're in non-threaded mode, call process so that the packets are sent | |
| 107 | + if (!entityPacketSender->isThreaded()) { | |
| 108 | + if (!wait) { | |
| 109 | + entityPacketSender->process(); | |
| 110 | + } else { | |
| 111 | + // wait here till the edit packet sender is completely done sending | |
| 112 | + while (entityPacketSender->hasPacketsToSend()) { | |
| 113 | + entityPacketSender->process(); | |
| 114 | + QCoreApplication::processEvents(); | |
| 115 | + } | |
| 116 | + } | |
| 117 | + } else { | |
| 118 | + // FIXME - do we need to have a similar "wait here" loop for non-threaded packet senders? | |
| 119 | + } | |
| 120 | + } | |
| 121 | +} | |
| 122 | + | |
| 123 | + | |
| 124 | +void EntityScriptingInterface::attachDefaultEventHandlers(ScriptManager* manager) { | |
| 125 | + // Connect up ALL the handlers to the global entities object's signals. | |
| 126 | + // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.) | |
| 127 | + | |
| 128 | + // Bug? These handlers are deleted when entityID is deleted, which is nice. | |
| 129 | + // But if they are created by an entity script on a different entity, should they also be deleted when the entity script unloads? | |
| 130 | + // E.g., suppose a bow has an entity script that causes arrows to be created with a potential lifetime greater than the bow, | |
| 131 | + // and that the entity script adds (e.g., collision) handlers to the arrows. Should those handlers fire if the bow is unloaded? | |
| 132 | + // Also, what about when the entity script is REloaded? | |
| 133 | + // For now, we are leaving them around. Changing that would require some non-trivial digging around to find the | |
| 134 | + // handlers that were added while a given currentEntityIdentifier was in place. I don't think this is dangerous. Just perhaps unexpected. -HRS | |
| 135 | + connect(this, &EntityScriptingInterface::deletingEntity, manager, | |
| 136 | + [manager](const EntityItemID& entityID) { manager->removeAllEventHandlers(entityID); }); | |
| 137 | + | |
| 138 | + // Two common cases of event handler, differing only in argument signature. | |
| 139 | + | |
| 140 | + /*@jsdoc | |
| 141 | + * Called when an entity event occurs on an entity as registered with {@link Script.addEventHandler}. | |
| 142 | + * @callback Script~entityEventCallback | |
| 143 | + * @param {Uuid} entityID - The ID of the entity the event has occured on. | |
| 144 | + */ | |
| 145 | + using SingleEntityHandler = std::function<void(const EntityItemID&)>; | |
| 146 | + auto makeSingleEntityHandler = [manager](QString eventName) -> SingleEntityHandler { | |
| 147 | + return [manager, eventName](const EntityItemID& entityItemID) { | |
| 148 | + manager->forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(manager->engine().data()) }); | |
| 149 | + }; | |
| 150 | + }; | |
| 151 | + | |
| 152 | + /*@jsdoc | |
| 153 | + * Called when a pointer event occurs on an entity as registered with {@link Script.addEventHandler}. | |
| 154 | + * @callback Script~pointerEventCallback | |
| 155 | + * @param {Uuid} entityID - The ID of the entity the event has occurred on. | |
| 156 | + * @param {PointerEvent} pointerEvent - Details of the event. | |
| 157 | + */ | |
| 158 | + using PointerHandler = std::function<void(const EntityItemID&, const PointerEvent&)>; | |
| 159 | + auto makePointerHandler = [manager](QString eventName) -> PointerHandler { | |
| 160 | + return [manager, eventName](const EntityItemID& entityItemID, const PointerEvent& event) { | |
| 161 | + if (!EntityTree::areEntityClicksCaptured()) { | |
| 162 | + ScriptEngine* engine = manager->engine().data(); | |
| 163 | + manager->forwardHandlerCall(entityItemID, eventName, | |
| 164 | + { entityItemID.toScriptValue(engine), event.toScriptValue(engine) }); | |
| 165 | + } | |
| 166 | + }; | |
| 167 | + }; | |
| 168 | + | |
| 169 | + /*@jsdoc | |
| 170 | + * Called when a collision event occurs on an entity as registered with {@link Script.addEventHandler}. | |
| 171 | + * @callback Script~collisionEventCallback | |
| 172 | + * @param {Uuid} entityA - The ID of one entity in the collision. | |
| 173 | + * @param {Uuid} entityB - The ID of the other entity in the collision. | |
| 174 | + * @param {Collision} collisionEvent - Details of the collision. | |
| 175 | + */ | |
| 176 | + using CollisionHandler = std::function<void(const EntityItemID&, const EntityItemID&, const Collision&)>; | |
| 177 | + auto makeCollisionHandler = [manager](QString eventName) -> CollisionHandler { | |
| 178 | + return [manager, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { | |
| 179 | + ScriptEngine* engine = manager->engine().data(); | |
| 180 | + manager->forwardHandlerCall(idA, eventName, | |
| 181 | + { idA.toScriptValue(engine), idB.toScriptValue(engine), | |
| 182 | + collisionToScriptValue(engine, collision) }); | |
| 183 | + }; | |
| 184 | + }; | |
| 185 | + | |
| 186 | + /*@jsdoc | |
| 187 | + * <p>The name of an entity event. When the entity event occurs, any function that has been registered for that event | |
| 188 | + * via {@link Script.addEventHandler} is called with parameters per the entity event.</p> | |
| 189 | + * <table> | |
| 190 | + * <thead> | |
| 191 | + * <tr><th>Event Name</th><th>Callback Type</th><th>Entity Event</th></tr> | |
| 192 | + * </thead> | |
| 193 | + * <tbody> | |
| 194 | + * <tr><td><code>"enterEntity"</code></td><td>{@link Script~entityEventCallback|entityEventCallback}</td> | |
| 195 | + * <td>{@link Entities.enterEntity}</td></tr> | |
| 196 | + * <tr><td><code>"leaveEntity"</code></td><td>{@link Script~entityEventCallback|entityEventCallback}</td> | |
| 197 | + * <td>{@link Entities.leaveEntity}</td></tr> | |
| 198 | + * <tr><td><code>"mousePressOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | |
| 199 | + * <td>{@link Entities.mousePressOnEntity}</td></tr> | |
| 200 | + * <tr><td><code>"mouseMoveOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | |
| 201 | + * <td>{@link Entities.mouseMoveOnEntity}</td></tr> | |
| 202 | + * <tr><td><code>"mouseReleaseOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | |
| 203 | + * <td>{@link Entities.mouseReleaseOnEntity}</td></tr> | |
| 204 | + * <tr><td><code>"clickDownOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | |
| 205 | + * <td>{@link Entities.clickDownOnEntity}</td></tr> | |
| 206 | + * <tr><td><code>"holdingClickOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | |
| 207 | + * <td>{@link Entities.holdingClickOnEntity}</td></tr> | |
| 208 | + * <tr><td><code>"clickReleaseOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | |
| 209 | + * <td>{@link Entities.clickReleaseOnEntity}</td></tr> | |
| 210 | + * <tr><td><code>"hoverEnterEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | |
| 211 | + * <td>{@link Entities.hoverEnterEntity}</td></tr> | |
| 212 | + * <tr><td><code>"hoverOverEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | |
| 213 | + * <td>{@link Entities.hoverOverEntity}</td></tr> | |
| 214 | + * <tr><td><code>"hoverLeaveEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | |
| 215 | + * <td>{@link Entities.hoverLeaveEntity}</td></tr> | |
| 216 | + * <tr><td><code>"collisionWithEntity"</code><td>{@link Script~collisionEventCallback|collisionEventCallback}</td> | |
| 217 | + * </td><td>{@link Entities.collisionWithEntity}</td></tr> | |
| 218 | + * </tbody> | |
| 219 | + * </table> | |
| 220 | + * @typedef {string} Script.EntityEvent | |
| 221 | + */ | |
| 222 | + connect(this, &EntityScriptingInterface::enterEntity, manager, makeSingleEntityHandler("enterEntity")); | |
| 223 | + connect(this, &EntityScriptingInterface::leaveEntity, manager, makeSingleEntityHandler("leaveEntity")); | |
| 224 | + | |
| 225 | + connect(this, &EntityScriptingInterface::mousePressOnEntity, manager, makePointerHandler("mousePressOnEntity")); | |
| 226 | + connect(this, &EntityScriptingInterface::mouseMoveOnEntity, manager, makePointerHandler("mouseMoveOnEntity")); | |
| 227 | + connect(this, &EntityScriptingInterface::mouseReleaseOnEntity, manager, makePointerHandler("mouseReleaseOnEntity")); | |
| 228 | + | |
| 229 | + connect(this, &EntityScriptingInterface::clickDownOnEntity, manager, makePointerHandler("clickDownOnEntity")); | |
| 230 | + connect(this, &EntityScriptingInterface::holdingClickOnEntity, manager, makePointerHandler("holdingClickOnEntity")); | |
| 231 | + connect(this, &EntityScriptingInterface::clickReleaseOnEntity, manager, makePointerHandler("clickReleaseOnEntity")); | |
| 232 | + | |
| 233 | + connect(this, &EntityScriptingInterface::hoverEnterEntity, manager, makePointerHandler("hoverEnterEntity")); | |
| 234 | + connect(this, &EntityScriptingInterface::hoverOverEntity, manager, makePointerHandler("hoverOverEntity")); | |
| 235 | + connect(this, &EntityScriptingInterface::hoverLeaveEntity, manager, makePointerHandler("hoverLeaveEntity")); | |
| 236 | + | |
| 237 | + connect(this, &EntityScriptingInterface::collisionWithEntity, manager, makeCollisionHandler("collisionWithEntity")); | |
| 238 | +} | |
| 239 | + | |
| 70 | 240 | void EntityScriptingInterface::queueEntityMessage(PacketType packetType, |
| 71 | 241 | EntityItemID entityID, const EntityItemProperties& properties) { |
| 72 | 242 | getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties); |