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 | const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}"; | 48 | const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}"; |
| 49 | const QString NOT_GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":false}}"; | 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 | EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) : | 81 | EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) : |
| 52 | _entityTree(nullptr), | 82 | _entityTree(nullptr), |
| 53 | _bidOnSimulationOwnership(bidOnSimulationOwnership) | 83 | _bidOnSimulationOwnership(bidOnSimulationOwnership) |
| @@ -67,6 +97,146 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership · 13 unmodified lines | |||
| 67 | PacketReceiver::makeSourcedListenerReference<EntityScriptingInterface>(this, &EntityScriptingInterface::handleEntityScriptCallMethodPacket)); | 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 | void EntityScriptingInterface::queueEntityMessage(PacketType packetType, | 240 | void EntityScriptingInterface::queueEntityMessage(PacketType packetType, |
| 71 | EntityItemID entityID, const EntityItemProperties& properties) { | 241 | EntityItemID entityID, const EntityItemProperties& properties) { |
| 72 | getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties); | 242 | getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties); |