From 5db37fff955eb45b978a3d852b82c70e67244288 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 17 Jan 2017 13:46:13 -0800 Subject: [PATCH] More entity script server work --- .../src/scripts/EntityScriptServer.cpp | 159 ++++++++++++++++-- .../src/scripts/EntityScriptServer.h | 20 ++- .../src/EntityTreeRenderer.cpp | 80 ++++----- .../physics/src/PhysicalEntitySimulation.h | 2 +- libraries/script-engine/src/ScriptEngines.h | 1 - 5 files changed, 190 insertions(+), 72 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 2883d1a2ed..58e1d1e71b 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -11,6 +11,7 @@ #include "EntityScriptServer.h" +#include #include #include #include @@ -20,15 +21,13 @@ #include #include #include +#include #include "../entities/AssignmentParentFinder.h" -static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10; +int EntityScriptServer::_entitiesScriptEngineCount = 0; -EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : - ThreadedAssignment(message), - _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES) -{ +EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssignment(message) { DependencyManager::get()->setPacketSender(&_entityEditSender); ResourceManager::init(); @@ -44,14 +43,15 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - - packetReceiver.registerListenerForTypes({ PacketType::MixedAudio, PacketType::SilentAudioFrame }, - this, "handleAudioPacket"); packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, this, "handleOctreePacket"); packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket"); packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); + auto avatarHashMap = DependencyManager::set(); + packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); + packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); + packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); } static const QString ENTITY_SCRIPT_SERVER_LOGGING_NAME = "entity-script-server"; @@ -77,6 +77,27 @@ void EntityScriptServer::run() { nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer }); + + // Setup Script Engine + resetEntitiesScriptEngine(); + + // we need to make sure that init has been called for our EntityScriptingInterface + // so that it actually has a jurisdiction listener when we ask it for it next + auto entityScriptingInterface = DependencyManager::get(); + entityScriptingInterface->init(); + _entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener()); + + _entityViewer.init(); + + entityScriptingInterface->setEntityTree(_entityViewer.getTree()); + + DependencyManager::set(_entityViewer.getTree()); + + + auto tree = _entityViewer.getTree().get(); + connect(tree, &EntityTree::deletingEntity, this, &EntityScriptServer::deletingEntity, Qt::QueuedConnection); + connect(tree, &EntityTree::addingEntity, this, &EntityScriptServer::addingEntity, Qt::QueuedConnection); + connect(tree, &EntityTree::entityScriptChanging, this, &EntityScriptServer::entitySciptChanging, Qt::QueuedConnection); } void EntityScriptServer::nodeActivated(SharedNodePointer activatedNode) { @@ -121,13 +142,11 @@ void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) { _encoder = nullptr; _codec = nullptr; } - _receivedAudioStream.cleanupCodec(); auto codecPlugins = PluginManager::getInstance()->getCodecPlugins(); for (auto& plugin : codecPlugins) { if (_selectedCodecName == plugin->getName()) { _codec = plugin; - _receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO); _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO); qDebug() << "Selected Codec Plugin:" << _codec.get(); break; @@ -135,11 +154,91 @@ void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) { } } -void EntityScriptServer::handleAudioPacket(QSharedPointer message) { - _receivedAudioStream.parseData(*message); +void EntityScriptServer::resetEntitiesScriptEngine() { + auto engineName = QString("Entities %1").arg(++_entitiesScriptEngineCount); + auto newEngine = QSharedPointer(new ScriptEngine(NO_SCRIPT, engineName)); - _lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness(); - _receivedAudioStream.clearBuffer(); + newEngine->registerGlobalObject("EntityViewer", &_entityViewer); + + auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor); + newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); + + +// newEngine->setEmitScriptUpdatesFunction([this]() { +// SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer); +// return !entityServerNode || isPhysicsEnabled(); +// }); + + newEngine->registerGlobalObject("AvatarList", DependencyManager::get().data()); + newEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); + + // connect this script engines printedMessage signal to the global ScriptEngines these various messages + auto scriptEngines = DependencyManager::get().data(); + connect(newEngine.data(), &ScriptEngine::printedMessage, scriptEngines, &ScriptEngines::onPrintedMessage); + connect(newEngine.data(), &ScriptEngine::errorMessage, scriptEngines, &ScriptEngines::onErrorMessage); + connect(newEngine.data(), &ScriptEngine::warningMessage, scriptEngines, &ScriptEngines::onWarningMessage); + connect(newEngine.data(), &ScriptEngine::infoMessage, scriptEngines, &ScriptEngines::onInfoMessage); + + + newEngine->runInThread(); + DependencyManager::get()->setEntitiesScriptEngine(newEngine.data()); + + _entitiesScriptEngine.swap(newEngine); +} + + +void EntityScriptServer::clear() { + // unload and stop the engine + if (_entitiesScriptEngine) { + // do this here (instead of in deleter) to avoid marshalling unload signals back to this thread + _entitiesScriptEngine->unloadAllEntityScripts(); + _entitiesScriptEngine->stop(); + } + + // reset the engine + if (!_shuttingDown) { + resetEntitiesScriptEngine(); + } + + _entityViewer.clear(); +} + +void EntityScriptServer::shutdown() { + if (_entitiesScriptEngine) { + _entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential + } + _shuttingDown = true; + + clear(); // always clear() on shutdown +} + +void EntityScriptServer::addingEntity(const EntityItemID& entityID) { + checkAndCallPreload(entityID); +} + +void EntityScriptServer::deletingEntity(const EntityItemID& entityID) { + if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) { + _entitiesScriptEngine->unloadEntityScript(entityID); + } +} + +void EntityScriptServer::entitySciptChanging(const EntityItemID& entityID, const bool reload) { + if (_entityViewer.getTree() && !_shuttingDown) { + _entitiesScriptEngine->unloadEntityScript(entityID); + checkAndCallPreload(entityID, reload); + } +} + +void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, const bool reload) { + if (_entityViewer.getTree() && !_shuttingDown) { + EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID); + if (entity && entity->shouldPreloadScript() && _entitiesScriptEngine) { + QString scriptUrl = entity->getScript(); + scriptUrl = ResourceManager::normalizeURL(scriptUrl); + ScriptEngine::loadEntityScript(_entitiesScriptEngine, entityID, scriptUrl, reload); + entity->scriptHasPreloaded(); + } + } } void EntityScriptServer::nodeKilled(SharedNodePointer killedNode) { @@ -151,11 +250,43 @@ void EntityScriptServer::sendStatsPacket() { } void EntityScriptServer::handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode) { + auto packetType = message->getType(); + if (packetType == PacketType::OctreeStats) { + + int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(message, senderNode); + if (message->getSize() > statsMessageLength) { + // pull out the piggybacked packet and create a new QSharedPointer for it + int piggyBackedSizeWithHeader = message->getSize() - statsMessageLength; + + auto buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); + memcpy(buffer.get(), message->getRawMessage() + statsMessageLength, piggyBackedSizeWithHeader); + + auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, message->getSenderSockAddr()); + message = QSharedPointer::create(*newPacket); + } else { + return; // bail since no piggyback data + } + + packetType = message->getType(); + } // fall through to piggyback message + + if (packetType == PacketType::EntityData) { + _entityViewer.processDatagram(*message, senderNode); + } else if (packetType == PacketType::EntityErase) { + _entityViewer.processEraseMessage(*message, senderNode); + } } void EntityScriptServer::handleJurisdictionPacket(QSharedPointer message, SharedNodePointer senderNode) { + NodeType_t nodeType; + message->peekPrimitive(&nodeType); + // PacketType_JURISDICTION, first byte is the node type... + if (nodeType == NodeType::EntityServer) { + DependencyManager::get()->getJurisdictionListener()-> + queueReceivedPacket(message, senderNode); + } } void EntityScriptServer::aboutToFinish() { diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h index 2d69b46057..63b1418534 100644 --- a/assignment-client/src/scripts/EntityScriptServer.h +++ b/assignment-client/src/scripts/EntityScriptServer.h @@ -15,11 +15,11 @@ #include #include +#include #include +#include #include -#include "MixedAudioStream.h" - class EntityScriptServer : public ThreadedAssignment { Q_OBJECT @@ -38,19 +38,29 @@ private slots: void handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer message, SharedNodePointer senderNode); void handleSelectedAudioFormat(QSharedPointer message); - void handleAudioPacket(QSharedPointer message); private: void negotiateAudioFormat(); void selectAudioFormat(const QString& selectedCodecName); + void resetEntitiesScriptEngine(); + void clear(); + void shutdown(); + void addingEntity(const EntityItemID& entityID); + void deletingEntity(const EntityItemID& entityID); + void entitySciptChanging(const EntityItemID& entityID, const bool reload); + void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false); + + bool _shuttingDown { false }; + + static int _entitiesScriptEngineCount; + QSharedPointer _entitiesScriptEngine; EntityEditPacketSender _entityEditSender; + EntityTreeHeadlessViewer _entityViewer; QString _selectedCodecName; CodecPluginPointer _codec; Encoder* _encoder { nullptr }; - MixedAudioStream _receivedAudioStream; - float _lastReceivedAudioLoudness; }; #endif // hifi_EntityScriptServer_h diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 7b80e97767..f851bdfe24 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -45,9 +45,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : - OctreeRenderer(), _wantScripts(wantScripts), - _entitiesScriptEngine(NULL), _lastPointerEventValid(false), _viewState(viewState), _scriptingServices(scriptingServices), @@ -106,6 +104,35 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() { auto newEngine = new ScriptEngine(NO_SCRIPT, QString("Entities %1").arg(++_entitiesScriptEngineCount)); _entitiesScriptEngine = QSharedPointer(newEngine, entitiesScriptEngineDeleter); + auto makeSlotForSignal = [&](QString name) { + return [newEngine, name](const EntityItemID& entityItemID, const PointerEvent& event) { + newEngine->callEntityScriptMethod(entityItemID, name, event); + }; + }; + + auto makeSlotForSignalNoEvent = [&](QString name) { + return [newEngine, name](const EntityItemID& entityItemID) { + newEngine->callEntityScriptMethod(entityItemID, name); + }; + }; + + connect(this, &EntityTreeRenderer::mousePressOnEntity, newEngine, makeSlotForSignal("mousePressOnEntity")); + connect(this, &EntityTreeRenderer::mouseMoveOnEntity, newEngine, makeSlotForSignal("mouseMoveOnEntity")); + connect(this, &EntityTreeRenderer::mouseMoveOnEntity, newEngine, makeSlotForSignal("mouseMoveEvent")); + connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, newEngine, makeSlotForSignal("mouseReleaseOnEntity")); + + connect(this, &EntityTreeRenderer::clickDownOnEntity, newEngine, makeSlotForSignal("clickDownOnEntity")); + connect(this, &EntityTreeRenderer::holdingClickOnEntity, newEngine, makeSlotForSignal("holdingClickOnEntity")); + connect(this, &EntityTreeRenderer::clickReleaseOnEntity, newEngine, makeSlotForSignal("clickReleaseOnEntity")); + + connect(this, &EntityTreeRenderer::hoverEnterEntity, newEngine, makeSlotForSignal("hoverEnterEntity")); + connect(this, &EntityTreeRenderer::hoverOverEntity, newEngine, makeSlotForSignal("hoverOverEntity")); + connect(this, &EntityTreeRenderer::hoverLeaveEntity, newEngine, makeSlotForSignal("hoverLeaveEntity")); + + connect(this, &EntityTreeRenderer::enterEntity, newEngine, makeSlotForSignalNoEvent("enterEntity")); + connect(this, &EntityTreeRenderer::leaveEntity, newEngine, makeSlotForSignalNoEvent("leaveEntity")); + + _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine.data()); _entitiesScriptEngine->runInThread(); DependencyManager::get()->setEntitiesScriptEngine(_entitiesScriptEngine.data()); @@ -208,7 +235,6 @@ void EntityTreeRenderer::update() { // and we want to simulate this message here as well as in mouse move if (_lastPointerEventValid && !_currentClickingOnEntityID.isInvalidID()) { emit holdingClickOnEntity(_currentClickingOnEntityID, _lastPointerEvent); - _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", _lastPointerEvent); } } @@ -302,9 +328,6 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { foreach(const EntityItemID& entityID, _currentEntitiesInside) { if (!entitiesContainingAvatar.contains(entityID)) { emit leaveEntity(entityID); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); - } } } @@ -312,9 +335,6 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { foreach(const EntityItemID& entityID, entitiesContainingAvatar) { if (!_currentEntitiesInside.contains(entityID)) { emit enterEntity(entityID); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "enterEntity"); - } } } _currentEntitiesInside = entitiesContainingAvatar; @@ -329,9 +349,6 @@ void EntityTreeRenderer::leaveAllEntities() { // for all of our previous containing entities, if they are no longer containing then send them a leave event foreach(const EntityItemID& entityID, _currentEntitiesInside) { emit leaveEntity(entityID); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); - } } _currentEntitiesInside.clear(); forceRecheckEntities(); @@ -719,15 +736,8 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { emit mousePressOnEntity(rayPickResult.entityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", pointerEvent); - } - _currentClickingOnEntityID = rayPickResult.entityID; emit clickDownOnEntity(_currentClickingOnEntityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", pointerEvent); - } _lastPointerEvent = pointerEvent; _lastPointerEventValid = true; @@ -758,9 +768,6 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { toPointerButton(*event), toPointerButtons(*event)); emit mouseReleaseOnEntity(rayPickResult.entityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", pointerEvent); - } _lastPointerEvent = pointerEvent; _lastPointerEventValid = true; @@ -778,9 +785,6 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { toPointerButton(*event), toPointerButtons(*event)); emit clickReleaseOnEntity(_currentClickingOnEntityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", pointerEvent); - } } // makes it the unknown ID, we just released so we can't be clicking on anything @@ -809,11 +813,6 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { emit mouseMoveOnEntity(rayPickResult.entityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", pointerEvent); - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", pointerEvent); - } - // handle the hover logic... // if we were previously hovering over an entity, and this new entity is not the same as our previous entity @@ -828,26 +827,17 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { toPointerButton(*event), toPointerButtons(*event)); emit hoverLeaveEntity(_currentHoverOverEntityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", pointerEvent); - } } // 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 if (rayPickResult.entityID != _currentHoverOverEntityID) { emit hoverEnterEntity(rayPickResult.entityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", pointerEvent); - } } // 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 emit hoverOverEntity(rayPickResult.entityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", pointerEvent); - } // remember what we're hovering over _currentHoverOverEntityID = rayPickResult.entityID; @@ -869,9 +859,6 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { toPointerButton(*event), toPointerButtons(*event)); emit hoverLeaveEntity(_currentHoverOverEntityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", pointerEvent); - } _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID } } @@ -888,9 +875,6 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { toPointerButton(*event), toPointerButtons(*event)); emit holdingClickOnEntity(_currentClickingOnEntityID, pointerEvent); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", pointerEvent); - } } } @@ -1058,16 +1042,10 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons // And now the entity scripts if (isCollisionOwner(myNodeID, entityTree, idA, collision)) { emit collisionWithEntity(idA, idB, collision); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision); - } } if (isCollisionOwner(myNodeID, entityTree, idA, collision)) { emit collisionWithEntity(idB, idA, collision); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, collision); - } } } diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 24b9ba95f0..af5def9775 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -27,7 +27,7 @@ class PhysicalEntitySimulation; using PhysicalEntitySimulationPointer = std::shared_ptr; using SetOfEntityMotionStates = QSet; -class PhysicalEntitySimulation :public EntitySimulation { +class PhysicalEntitySimulation : public EntitySimulation { public: PhysicalEntitySimulation(); ~PhysicalEntitySimulation(); diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 583b6c0ea2..b0f0e24ac6 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -90,7 +90,6 @@ public slots: protected slots: void onScriptFinished(const QString& fileNameString, ScriptEngine* engine); - protected: friend class ScriptEngine;