More entity script server work

This commit is contained in:
Atlante45 2017-01-17 13:46:13 -08:00
parent 529286b2ea
commit 5db37fff95
5 changed files with 190 additions and 72 deletions

View file

@ -11,6 +11,7 @@
#include "EntityScriptServer.h"
#include <AudioConstants.h>
#include <AudioInjectorManager.h>
#include <EntityScriptingInterface.h>
#include <MessagesClient.h>
@ -20,15 +21,13 @@
#include <ScriptCache.h>
#include <ScriptEngines.h>
#include <SoundCache.h>
#include <WebSocketServerClass.h>
#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<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
ResourceManager::init();
@ -44,14 +43,15 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) :
auto& packetReceiver = DependencyManager::get<NodeList>()->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<AvatarHashMap>();
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>();
entityScriptingInterface->init();
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
_entityViewer.init();
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
DependencyManager::set<AssignmentParentFinder>(_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<ReceivedMessage> message) {
_receivedAudioStream.parseData(*message);
void EntityScriptServer::resetEntitiesScriptEngine() {
auto engineName = QString("Entities %1").arg(++_entitiesScriptEngineCount);
auto newEngine = QSharedPointer<ScriptEngine>(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<NodeList>()->soloNodeOfType(NodeType::EntityServer);
// return !entityServerNode || isPhysicsEnabled();
// });
newEngine->registerGlobalObject("AvatarList", DependencyManager::get<AvatarHashMap>().data());
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
auto scriptEngines = DependencyManager::get<ScriptEngines>().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<EntityScriptingInterface>()->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<ReceivedMessage> 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<NLPacket> for it
int piggyBackedSizeWithHeader = message->getSize() - statsMessageLength;
auto buffer = std::unique_ptr<char[]>(new char[piggyBackedSizeWithHeader]);
memcpy(buffer.get(), message->getRawMessage() + statsMessageLength, piggyBackedSizeWithHeader);
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, message->getSenderSockAddr());
message = QSharedPointer<ReceivedMessage>::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<ReceivedMessage> message, SharedNodePointer senderNode) {
NodeType_t nodeType;
message->peekPrimitive(&nodeType);
// PacketType_JURISDICTION, first byte is the node type...
if (nodeType == NodeType::EntityServer) {
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
queueReceivedPacket(message, senderNode);
}
}
void EntityScriptServer::aboutToFinish() {

View file

@ -15,11 +15,11 @@
#include <QtCore/QObject>
#include <EntityEditPacketSender.h>
#include <EntityTreeHeadlessViewer.h>
#include <plugins/CodecPlugin.h>
#include <ScriptEngine.h>
#include <ThreadedAssignment.h>
#include "MixedAudioStream.h"
class EntityScriptServer : public ThreadedAssignment {
Q_OBJECT
@ -38,19 +38,29 @@ private slots:
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
void handleAudioPacket(QSharedPointer<ReceivedMessage> 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<ScriptEngine> _entitiesScriptEngine;
EntityEditPacketSender _entityEditSender;
EntityTreeHeadlessViewer _entityViewer;
QString _selectedCodecName;
CodecPluginPointer _codec;
Encoder* _encoder { nullptr };
MixedAudioStream _receivedAudioStream;
float _lastReceivedAudioLoudness;
};
#endif // hifi_EntityScriptServer_h

View file

@ -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<ScriptEngine>(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<EntityScriptingInterface>()->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);
}
}
}

View file

@ -27,7 +27,7 @@ class PhysicalEntitySimulation;
using PhysicalEntitySimulationPointer = std::shared_ptr<PhysicalEntitySimulation>;
using SetOfEntityMotionStates = QSet<EntityMotionState*>;
class PhysicalEntitySimulation :public EntitySimulation {
class PhysicalEntitySimulation : public EntitySimulation {
public:
PhysicalEntitySimulation();
~PhysicalEntitySimulation();

View file

@ -90,7 +90,6 @@ public slots:
protected slots:
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine);
protected:
friend class ScriptEngine;