mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 03:13:09 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into tuneAvatarInfo
This commit is contained in:
commit
3c3dcbbbaa
93 changed files with 1642 additions and 402 deletions
|
@ -9,6 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "Agent.h"
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QtCore/QEventLoop>
|
#include <QtCore/QEventLoop>
|
||||||
#include <QtCore/QStandardPaths>
|
#include <QtCore/QStandardPaths>
|
||||||
|
@ -46,14 +48,12 @@
|
||||||
#include "RecordingScriptingInterface.h"
|
#include "RecordingScriptingInterface.h"
|
||||||
#include "AbstractAudioInterface.h"
|
#include "AbstractAudioInterface.h"
|
||||||
|
|
||||||
#include "Agent.h"
|
|
||||||
#include "AvatarAudioTimer.h"
|
#include "AvatarAudioTimer.h"
|
||||||
|
|
||||||
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10;
|
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10;
|
||||||
|
|
||||||
Agent::Agent(ReceivedMessage& message) :
|
Agent::Agent(ReceivedMessage& message) :
|
||||||
ThreadedAssignment(message),
|
ThreadedAssignment(message),
|
||||||
_entityEditSender(),
|
|
||||||
_receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES) {
|
_receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES) {
|
||||||
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
|
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ Agent::Agent(ReceivedMessage& message) :
|
||||||
DependencyManager::set<recording::Recorder>();
|
DependencyManager::set<recording::Recorder>();
|
||||||
DependencyManager::set<RecordingScriptingInterface>();
|
DependencyManager::set<RecordingScriptingInterface>();
|
||||||
DependencyManager::set<ScriptCache>();
|
DependencyManager::set<ScriptCache>();
|
||||||
DependencyManager::set<ScriptEngines>();
|
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT);
|
||||||
|
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
||||||
_receivedAudioStream.clearBuffer();
|
_receivedAudioStream.clearBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString AGENT_LOGGING_NAME = "agent";
|
static const QString AGENT_LOGGING_NAME = "agent";
|
||||||
|
|
||||||
void Agent::run() {
|
void Agent::run() {
|
||||||
|
|
||||||
|
@ -321,7 +321,7 @@ void Agent::scriptRequestFinished() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agent::executeScript() {
|
void Agent::executeScript() {
|
||||||
_scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(_scriptContents, _payload));
|
_scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload));
|
||||||
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
|
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
|
||||||
|
|
||||||
// setup an Avatar for the script to use
|
// setup an Avatar for the script to use
|
||||||
|
@ -376,6 +376,9 @@ void Agent::executeScript() {
|
||||||
|
|
||||||
_scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer);
|
_scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer);
|
||||||
|
|
||||||
|
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||||
|
_scriptEngine->registerGlobalObject("Recording", recordingInterface.data());
|
||||||
|
|
||||||
// we need to make sure that init has been called for our EntityScriptingInterface
|
// 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
|
// so that it actually has a jurisdiction listener when we ask it for it next
|
||||||
entityScriptingInterface->init();
|
entityScriptingInterface->init();
|
||||||
|
|
|
@ -12,12 +12,13 @@
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
|
|
||||||
#include "Agent.h"
|
#include "Agent.h"
|
||||||
|
#include "assets/AssetServer.h"
|
||||||
#include "AssignmentFactory.h"
|
#include "AssignmentFactory.h"
|
||||||
#include "audio/AudioMixer.h"
|
#include "audio/AudioMixer.h"
|
||||||
#include "avatars/AvatarMixer.h"
|
#include "avatars/AvatarMixer.h"
|
||||||
#include "entities/EntityServer.h"
|
#include "entities/EntityServer.h"
|
||||||
#include "assets/AssetServer.h"
|
|
||||||
#include "messages/MessagesMixer.h"
|
#include "messages/MessagesMixer.h"
|
||||||
|
#include "scripts/EntityScriptServer.h"
|
||||||
|
|
||||||
ThreadedAssignment* AssignmentFactory::unpackAssignment(ReceivedMessage& message) {
|
ThreadedAssignment* AssignmentFactory::unpackAssignment(ReceivedMessage& message) {
|
||||||
|
|
||||||
|
@ -39,7 +40,9 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(ReceivedMessage& message
|
||||||
return new AssetServer(message);
|
return new AssetServer(message);
|
||||||
case Assignment::MessagesMixerType:
|
case Assignment::MessagesMixerType:
|
||||||
return new MessagesMixer(message);
|
return new MessagesMixer(message);
|
||||||
|
case Assignment::EntityScriptServerType:
|
||||||
|
return new EntityScriptServer(message);
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "NodeType.h"
|
#include "NodeType.h"
|
||||||
#include "SendAssetTask.h"
|
#include "SendAssetTask.h"
|
||||||
#include "UploadAssetTask.h"
|
#include "UploadAssetTask.h"
|
||||||
|
#include <ClientServerUtils.h>
|
||||||
|
|
||||||
static const uint8_t MIN_CORES_FOR_MULTICORE = 4;
|
static const uint8_t MIN_CORES_FOR_MULTICORE = 4;
|
||||||
static const uint8_t CPU_AFFINITY_COUNT_HIGH = 2;
|
static const uint8_t CPU_AFFINITY_COUNT_HIGH = 2;
|
||||||
|
@ -192,7 +193,7 @@ void AssetServer::completeSetup() {
|
||||||
cleanupUnmappedFiles();
|
cleanupUnmappedFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer });
|
||||||
} else {
|
} else {
|
||||||
qCritical() << "Asset Server assignment will not continue because mapping file could not be loaded.";
|
qCritical() << "Asset Server assignment will not continue because mapping file could not be loaded.";
|
||||||
setFinished(true);
|
setFinished(true);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <udt/Packet.h>
|
#include <udt/Packet.h>
|
||||||
|
|
||||||
#include "AssetUtils.h"
|
#include "AssetUtils.h"
|
||||||
|
#include "ClientServerUtils.h"
|
||||||
|
|
||||||
SendAssetTask::SendAssetTask(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
|
SendAssetTask::SendAssetTask(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
|
||||||
QRunnable(),
|
QRunnable(),
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <NLPacketList.h>
|
#include <NLPacketList.h>
|
||||||
|
|
||||||
|
#include "ClientServerUtils.h"
|
||||||
|
|
||||||
|
|
||||||
UploadAssetTask::UploadAssetTask(QSharedPointer<ReceivedMessage> receivedMessage, SharedNodePointer senderNode,
|
UploadAssetTask::UploadAssetTask(QSharedPointer<ReceivedMessage> receivedMessage, SharedNodePointer senderNode,
|
||||||
const QDir& resourcesDir) :
|
const QDir& resourcesDir) :
|
||||||
|
|
|
@ -398,7 +398,7 @@ void AudioMixer::start() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
// prepare the NodeList
|
// prepare the NodeList
|
||||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer });
|
||||||
nodeList->linkedDataCreateCallback = [&](Node* node) { getOrCreateClientData(node); };
|
nodeList->linkedDataCreateCallback = [&](Node* node) { getOrCreateClientData(node); };
|
||||||
|
|
||||||
// parse out any AudioMixer settings
|
// parse out any AudioMixer settings
|
||||||
|
|
|
@ -686,8 +686,8 @@ void AvatarMixer::run() {
|
||||||
|
|
||||||
void AvatarMixer::domainSettingsRequestComplete() {
|
void AvatarMixer::domainSettingsRequestComplete() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer });
|
||||||
|
|
||||||
// parse the settings to pull out the values we need
|
// parse the settings to pull out the values we need
|
||||||
parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject());
|
parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject());
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include "AssignmentParentFinder.h"
|
#include "AssignmentParentFinder.h"
|
||||||
|
|
||||||
|
#include <AvatarHashMap.h>
|
||||||
|
|
||||||
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success, SpatialParentTree* entityTree) const {
|
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success, SpatialParentTree* entityTree) const {
|
||||||
SpatiallyNestableWeakPointer parent;
|
SpatiallyNestableWeakPointer parent;
|
||||||
|
|
||||||
|
@ -25,10 +27,21 @@ SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool&
|
||||||
} else {
|
} else {
|
||||||
parent = _tree->findEntityByEntityItemID(parentID);
|
parent = _tree->findEntityByEntityItemID(parentID);
|
||||||
}
|
}
|
||||||
if (parent.expired()) {
|
if (!parent.expired()) {
|
||||||
success = false;
|
|
||||||
} else {
|
|
||||||
success = true;
|
success = true;
|
||||||
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// search avatars
|
||||||
|
if (DependencyManager::isSet<AvatarHashMap>()) {
|
||||||
|
auto avatarHashMap = DependencyManager::get<AvatarHashMap>();
|
||||||
|
parent = avatarHashMap->getAvatarBySessionID(parentID);
|
||||||
|
if (!parent.expired()) {
|
||||||
|
success = true;
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success = false;
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,7 @@ void MessagesMixer::handleMessages(QSharedPointer<ReceivedMessage> receivedMessa
|
||||||
|
|
||||||
nodeList->eachMatchingNode(
|
nodeList->eachMatchingNode(
|
||||||
[&](const SharedNodePointer& node)->bool {
|
[&](const SharedNodePointer& node)->bool {
|
||||||
return node->getType() == NodeType::Agent && node->getActiveSocket() &&
|
return node->getActiveSocket() && _channelSubscribers[channel].contains(node->getUUID());
|
||||||
_channelSubscribers[channel].contains(node->getUUID());
|
|
||||||
},
|
},
|
||||||
[&](const SharedNodePointer& node) {
|
[&](const SharedNodePointer& node) {
|
||||||
auto packetList = MessagesClient::encodeMessagesPacket(channel, message, senderID);
|
auto packetList = MessagesClient::encodeMessagesPacket(channel, message, senderID);
|
||||||
|
@ -83,5 +82,6 @@ void MessagesMixer::sendStatsPacket() {
|
||||||
|
|
||||||
void MessagesMixer::run() {
|
void MessagesMixer::run() {
|
||||||
ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer);
|
ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer);
|
||||||
DependencyManager::get<NodeList>()->addNodeTypeToInterestSet(NodeType::Agent);
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
}
|
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer });
|
||||||
|
}
|
||||||
|
|
|
@ -316,8 +316,9 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
||||||
int truePacketsSent = 0;
|
int truePacketsSent = 0;
|
||||||
int trueBytesSent = 0;
|
int trueBytesSent = 0;
|
||||||
int packetsSentThisInterval = 0;
|
int packetsSentThisInterval = 0;
|
||||||
bool isFullScene = ((!viewFrustumChanged) && nodeData->getViewFrustumJustStoppedChanging())
|
bool isFullScene = nodeData->haveJSONParametersChanged() ||
|
||||||
|| nodeData->hasLodChanged();
|
(nodeData->getUsesFrustum()
|
||||||
|
&& ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged()));
|
||||||
|
|
||||||
bool somethingToSend = true; // assume we have something
|
bool somethingToSend = true; // assume we have something
|
||||||
|
|
||||||
|
@ -432,7 +433,9 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
||||||
boundaryLevelAdjust, octreeSizeScale,
|
boundaryLevelAdjust, octreeSizeScale,
|
||||||
nodeData->getLastTimeBagEmpty(),
|
nodeData->getLastTimeBagEmpty(),
|
||||||
isFullScene, &nodeData->stats, _myServer->getJurisdiction(),
|
isFullScene, &nodeData->stats, _myServer->getJurisdiction(),
|
||||||
&nodeData->extraEncodeData);
|
&nodeData->extraEncodeData,
|
||||||
|
nodeData->getUsesFrustum(),
|
||||||
|
nodeData);
|
||||||
nodeData->copyCurrentViewFrustum(params.viewFrustum);
|
nodeData->copyCurrentViewFrustum(params.viewFrustum);
|
||||||
if (viewFrustumChanged) {
|
if (viewFrustumChanged) {
|
||||||
nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);
|
nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);
|
||||||
|
|
|
@ -1136,8 +1136,8 @@ void OctreeServer::domainSettingsRequestComplete() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
// we need to ask the DS about agents so we can ping/reply with them
|
// we need to ask the DS about agents so we can ping/reply with them
|
||||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer });
|
||||||
|
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket");
|
packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket");
|
||||||
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
|
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
|
||||||
|
|
372
assignment-client/src/scripts/EntityScriptServer.cpp
Normal file
372
assignment-client/src/scripts/EntityScriptServer.cpp
Normal file
|
@ -0,0 +1,372 @@
|
||||||
|
//
|
||||||
|
// EntityScriptServer.cpp
|
||||||
|
// assignment-client/src/scripts
|
||||||
|
//
|
||||||
|
// Created by Clément Brisset on 1/5/17.
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "EntityScriptServer.h"
|
||||||
|
|
||||||
|
#include <AudioConstants.h>
|
||||||
|
#include <AudioInjectorManager.h>
|
||||||
|
#include <EntityScriptingInterface.h>
|
||||||
|
#include <MessagesClient.h>
|
||||||
|
#include <plugins/CodecPlugin.h>
|
||||||
|
#include <plugins/PluginManager.h>
|
||||||
|
#include <ResourceManager.h>
|
||||||
|
#include <ScriptCache.h>
|
||||||
|
#include <ScriptEngines.h>
|
||||||
|
#include <SoundCache.h>
|
||||||
|
#include <UUID.h>
|
||||||
|
#include <WebSocketServerClass.h>
|
||||||
|
|
||||||
|
#include "ClientServerUtils.h"
|
||||||
|
#include "../entities/AssignmentParentFinder.h"
|
||||||
|
|
||||||
|
int EntityScriptServer::_entitiesScriptEngineCount = 0;
|
||||||
|
|
||||||
|
EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssignment(message) {
|
||||||
|
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
|
||||||
|
|
||||||
|
ResourceManager::init();
|
||||||
|
|
||||||
|
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||||
|
|
||||||
|
DependencyManager::set<ResourceCacheSharedItems>();
|
||||||
|
DependencyManager::set<SoundCache>();
|
||||||
|
DependencyManager::set<AudioInjectorManager>();
|
||||||
|
|
||||||
|
DependencyManager::set<ScriptCache>();
|
||||||
|
DependencyManager::set<ScriptEngines>(ScriptEngine::ENTITY_SERVER_SCRIPT);
|
||||||
|
|
||||||
|
|
||||||
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
|
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");
|
||||||
|
|
||||||
|
packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket");
|
||||||
|
packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const QString ENTITY_SCRIPT_SERVER_LOGGING_NAME = "entity-script-server";
|
||||||
|
|
||||||
|
void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
|
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
||||||
|
// about each other.
|
||||||
|
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
|
||||||
|
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
|
if (_entityViewer.getTree() && !_shuttingDown) {
|
||||||
|
qDebug() << "Reloading: " << entityID;
|
||||||
|
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||||
|
checkAndCallPreload(entityID, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
|
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
||||||
|
// about each other.
|
||||||
|
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
|
||||||
|
MessageID messageID;
|
||||||
|
message->readPrimitive(&messageID);
|
||||||
|
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
|
auto replyPacketList = NLPacketList::create(PacketType::EntityScriptGetStatusReply, QByteArray(), true, true);
|
||||||
|
replyPacketList->writePrimitive(messageID);
|
||||||
|
|
||||||
|
EntityScriptDetails details;
|
||||||
|
if (_entitiesScriptEngine->getEntityScriptDetails(entityID, details)) {
|
||||||
|
replyPacketList->writePrimitive(true);
|
||||||
|
replyPacketList->writePrimitive(details.status);
|
||||||
|
replyPacketList->writeString(details.errorInfo);
|
||||||
|
} else {
|
||||||
|
replyPacketList->writePrimitive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
nodeList->sendPacketList(std::move(replyPacketList), *senderNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptServer::run() {
|
||||||
|
// make sure we request our script once the agent connects to the domain
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
ThreadedAssignment::commonInit(ENTITY_SCRIPT_SERVER_LOGGING_NAME, NodeType::EntityScriptServer);
|
||||||
|
|
||||||
|
// Setup MessagesClient
|
||||||
|
auto messagesClient = DependencyManager::set<MessagesClient>();
|
||||||
|
QThread* messagesThread = new QThread;
|
||||||
|
messagesThread->setObjectName("Messages Client Thread");
|
||||||
|
messagesClient->moveToThread(messagesThread);
|
||||||
|
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
|
||||||
|
messagesThread->start();
|
||||||
|
|
||||||
|
// make sure we hear about connected nodes so we can grab an ATP script if a request is pending
|
||||||
|
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &EntityScriptServer::nodeActivated);
|
||||||
|
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &EntityScriptServer::nodeKilled);
|
||||||
|
|
||||||
|
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
||||||
|
NodeType::Agent, 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();
|
||||||
|
|
||||||
|
// setup the JSON filter that asks for entities with a non-default serverScripts property
|
||||||
|
QJsonObject queryJSONParameters;
|
||||||
|
static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
|
||||||
|
queryJSONParameters[SERVER_SCRIPTS_PROPERTY] = EntityQueryFilterSymbol::NonDefault;
|
||||||
|
|
||||||
|
// setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter
|
||||||
|
_entityViewer.getOctreeQuery().setUsesFrustum(false);
|
||||||
|
_entityViewer.getOctreeQuery().setJSONParameters(queryJSONParameters);
|
||||||
|
|
||||||
|
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::entityServerScriptChanging, this, &EntityScriptServer::entityServerScriptChanging, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptServer::nodeActivated(SharedNodePointer activatedNode) {
|
||||||
|
if (activatedNode->getType() == NodeType::AudioMixer) {
|
||||||
|
negotiateAudioFormat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptServer::negotiateAudioFormat() {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
auto negotiateFormatPacket = NLPacket::create(PacketType::NegotiateAudioFormat);
|
||||||
|
auto codecPlugins = PluginManager::getInstance()->getCodecPlugins();
|
||||||
|
quint8 numberOfCodecs = (quint8)codecPlugins.size();
|
||||||
|
negotiateFormatPacket->writePrimitive(numberOfCodecs);
|
||||||
|
for (auto& plugin : codecPlugins) {
|
||||||
|
auto codecName = plugin->getName();
|
||||||
|
negotiateFormatPacket->writeString(codecName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab our audio mixer from the NodeList, if it exists
|
||||||
|
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||||
|
|
||||||
|
if (audioMixer) {
|
||||||
|
// send off this mute packet
|
||||||
|
nodeList->sendPacket(std::move(negotiateFormatPacket), *audioMixer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptServer::handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message) {
|
||||||
|
QString selectedCodecName = message->readString();
|
||||||
|
selectAudioFormat(selectedCodecName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) {
|
||||||
|
_selectedCodecName = selectedCodecName;
|
||||||
|
|
||||||
|
qDebug() << "Selected Codec:" << _selectedCodecName;
|
||||||
|
|
||||||
|
// release any old codec encoder/decoder first...
|
||||||
|
if (_codec && _encoder) {
|
||||||
|
_codec->releaseEncoder(_encoder);
|
||||||
|
_encoder = nullptr;
|
||||||
|
_codec = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto codecPlugins = PluginManager::getInstance()->getCodecPlugins();
|
||||||
|
for (auto& plugin : codecPlugins) {
|
||||||
|
if (_selectedCodecName == plugin->getName()) {
|
||||||
|
_codec = plugin;
|
||||||
|
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO);
|
||||||
|
qDebug() << "Selected Codec Plugin:" << _codec.get();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptServer::resetEntitiesScriptEngine() {
|
||||||
|
auto engineName = QString("Entities %1").arg(++_entitiesScriptEngineCount);
|
||||||
|
auto newEngine = QSharedPointer<ScriptEngine>(new ScriptEngine(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName));
|
||||||
|
|
||||||
|
auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor);
|
||||||
|
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
connect(newEngine.data(), &ScriptEngine::update, this, [this] {
|
||||||
|
_entityViewer.queryOctree();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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::shutdownScriptEngine() {
|
||||||
|
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::entityServerScriptChanging(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 && _entitiesScriptEngine) {
|
||||||
|
|
||||||
|
EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID);
|
||||||
|
EntityScriptDetails details;
|
||||||
|
bool notRunning = !_entitiesScriptEngine->getEntityScriptDetails(entityID, details);
|
||||||
|
if (entity && (reload || notRunning || details.scriptText != entity->getServerScripts())) {
|
||||||
|
QString scriptUrl = entity->getServerScripts();
|
||||||
|
if (!scriptUrl.isEmpty()) {
|
||||||
|
scriptUrl = ResourceManager::normalizeURL(scriptUrl);
|
||||||
|
qDebug() << "Loading entity server script" << scriptUrl << "for" << entityID;
|
||||||
|
ScriptEngine::loadEntityScript(_entitiesScriptEngine, entityID, scriptUrl, reload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptServer::nodeKilled(SharedNodePointer killedNode) {
|
||||||
|
if (!_shuttingDown && killedNode->getType() == NodeType::EntityServer) {
|
||||||
|
if (_entitiesScriptEngine) {
|
||||||
|
_entitiesScriptEngine->unloadAllEntityScripts();
|
||||||
|
_entitiesScriptEngine->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetEntitiesScriptEngine();
|
||||||
|
|
||||||
|
_entityViewer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
shutdownScriptEngine();
|
||||||
|
|
||||||
|
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||||
|
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||||
|
|
||||||
|
ResourceManager::cleanup();
|
||||||
|
|
||||||
|
// cleanup the AudioInjectorManager (and any still running injectors)
|
||||||
|
DependencyManager::destroy<AudioInjectorManager>();
|
||||||
|
DependencyManager::destroy<ScriptEngines>();
|
||||||
|
|
||||||
|
// cleanup codec & encoder
|
||||||
|
if (_codec && _encoder) {
|
||||||
|
_codec->releaseEncoder(_encoder);
|
||||||
|
_encoder = nullptr;
|
||||||
|
}
|
||||||
|
}
|
70
assignment-client/src/scripts/EntityScriptServer.h
Normal file
70
assignment-client/src/scripts/EntityScriptServer.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
//
|
||||||
|
// EntityScriptServer.h
|
||||||
|
// assignment-client/src/scripts
|
||||||
|
//
|
||||||
|
// Created by Clément Brisset on 1/5/17.
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_EntityScriptServer_h
|
||||||
|
#define hifi_EntityScriptServer_h
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
#include <EntityEditPacketSender.h>
|
||||||
|
#include <EntityTreeHeadlessViewer.h>
|
||||||
|
#include <plugins/CodecPlugin.h>
|
||||||
|
#include <ScriptEngine.h>
|
||||||
|
#include <ThreadedAssignment.h>
|
||||||
|
|
||||||
|
class EntityScriptServer : public ThreadedAssignment {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
EntityScriptServer(ReceivedMessage& message);
|
||||||
|
|
||||||
|
virtual void aboutToFinish() override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void run() override;
|
||||||
|
void nodeActivated(SharedNodePointer activatedNode);
|
||||||
|
void nodeKilled(SharedNodePointer killedNode);
|
||||||
|
void sendStatsPacket() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
|
||||||
|
|
||||||
|
void handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
void handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void negotiateAudioFormat();
|
||||||
|
void selectAudioFormat(const QString& selectedCodecName);
|
||||||
|
|
||||||
|
void resetEntitiesScriptEngine();
|
||||||
|
void clear();
|
||||||
|
void shutdownScriptEngine();
|
||||||
|
|
||||||
|
void addingEntity(const EntityItemID& entityID);
|
||||||
|
void deletingEntity(const EntityItemID& entityID);
|
||||||
|
void entityServerScriptChanging(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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_EntityScriptServer_h
|
|
@ -1285,7 +1285,7 @@
|
||||||
{
|
{
|
||||||
"name": "entityScriptSourceWhitelist",
|
"name": "entityScriptSourceWhitelist",
|
||||||
"label": "Entity Scripts Allowed from:",
|
"label": "Entity Scripts Allowed from:",
|
||||||
"help": "The domains that entity scripts are allowed from. A comma separated list of domains that entity scripts are allowed from, if someone attempts to create and entity or edit an entity to have a different domain, it will be rejected. If left blank, any domain is allowed.",
|
"help": "Comma separated list of URLs (with optional paths) that entity scripts are allowed from. If someone attempts to create and entity or edit an entity to have a different domain, it will be rejected. If left blank, any domain is allowed.",
|
||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
"default": "",
|
"default": "",
|
||||||
"advanced": true
|
"advanced": true
|
||||||
|
|
|
@ -46,10 +46,9 @@ QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||||
<< NodeType::AvatarMixer << NodeType::EntityServer
|
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer
|
||||||
<< NodeType::AssetServer
|
<< NodeType::EntityScriptServer;
|
||||||
<< NodeType::MessagesMixer;
|
|
||||||
|
|
||||||
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessage> message) {
|
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessage> message) {
|
||||||
if (message->getSize() == 0) {
|
if (message->getSize() == 0) {
|
||||||
|
@ -72,7 +71,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
||||||
}
|
}
|
||||||
|
|
||||||
static const NodeSet VALID_NODE_TYPES {
|
static const NodeSet VALID_NODE_TYPES {
|
||||||
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer
|
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer, NodeType::EntityScriptServer
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
|
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
|
||||||
|
@ -107,7 +106,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
// set the sending sock addr and node interest set on this node
|
// set the sending sock addr and node interest set on this node
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
nodeData->setSendingSockAddr(message->getSenderSockAddr());
|
nodeData->setSendingSockAddr(message->getSenderSockAddr());
|
||||||
|
|
||||||
// guard against patched agents asking to hear about other agents
|
// guard against patched agents asking to hear about other agents
|
||||||
|
@ -128,12 +127,12 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
||||||
emit connectedNode(node);
|
emit connectedNode(node);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr()
|
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr()
|
||||||
<< "with hardware address" << nodeConnection.hardwareAddress
|
<< "with hardware address" << nodeConnection.hardwareAddress
|
||||||
<< "and machine fingerprint" << nodeConnection.machineFingerprint;
|
<< "and machine fingerprint" << nodeConnection.machineFingerprint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
|
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
|
||||||
const QString& hardwareAddress, const QUuid& machineFingerprint) {
|
const QString& hardwareAddress, const QUuid& machineFingerprint) {
|
||||||
NodePermissions userPerms;
|
NodePermissions userPerms;
|
||||||
|
|
||||||
|
@ -283,7 +282,7 @@ void DomainGatekeeper::updateNodePermissions() {
|
||||||
QString hardwareAddress;
|
QString hardwareAddress;
|
||||||
QUuid machineFingerprint;
|
QUuid machineFingerprint;
|
||||||
|
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
hardwareAddress = nodeData->getHardwareAddress();
|
hardwareAddress = nodeData->getHardwareAddress();
|
||||||
machineFingerprint = nodeData->getMachineFingerprint();
|
machineFingerprint = nodeData->getMachineFingerprint();
|
||||||
|
@ -336,7 +335,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
|
||||||
// add the new node
|
// add the new node
|
||||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
||||||
|
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||||
|
|
||||||
// set assignment related data on the linked data for this node
|
// set assignment related data on the linked data for this node
|
||||||
nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID());
|
nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID());
|
||||||
|
@ -458,7 +457,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
||||||
newNode->setPermissions(userPerms);
|
newNode->setPermissions(userPerms);
|
||||||
|
|
||||||
// grab the linked data for our new node so we can set the username
|
// grab the linked data for our new node so we can set the username
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||||
|
|
||||||
// if we have a username from the connect request, set it on the DomainServerNodeData
|
// if we have a username from the connect request, set it on the DomainServerNodeData
|
||||||
nodeData->setUsername(username);
|
nodeData->setUsername(username);
|
||||||
|
|
|
@ -107,7 +107,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
|
|
||||||
qRegisterMetaType<DomainServerWebSessionData>("DomainServerWebSessionData");
|
qRegisterMetaType<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||||
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("DomainServerWebSessionData");
|
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||||
|
|
||||||
// make sure we hear about newly connected nodes from our gatekeeper
|
// make sure we hear about newly connected nodes from our gatekeeper
|
||||||
connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode);
|
connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode);
|
||||||
|
|
||||||
|
@ -281,7 +281,7 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() {
|
||||||
QString keyPassphraseString = QProcessEnvironment::systemEnvironment().value(X509_KEY_PASSPHRASE_ENV);
|
QString keyPassphraseString = QProcessEnvironment::systemEnvironment().value(X509_KEY_PASSPHRASE_ENV);
|
||||||
|
|
||||||
qDebug() << "Reading certificate file at" << certPath << "for HTTPS.";
|
qDebug() << "Reading certificate file at" << certPath << "for HTTPS.";
|
||||||
qDebug() << "Reading key file at" << keyPath << "for HTTPS.";
|
qDebug() << "Reading key file at" << keyPath << "for HTTPS.";
|
||||||
|
|
||||||
QFile certFile(certPath);
|
QFile certFile(certPath);
|
||||||
certFile.open(QIODevice::ReadOnly);
|
certFile.open(QIODevice::ReadOnly);
|
||||||
|
@ -528,12 +528,12 @@ void DomainServer::setupNodeListAndAssignments() {
|
||||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
||||||
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
||||||
packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket");
|
packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket");
|
||||||
|
|
||||||
// NodeList won't be available to the settings manager when it is created, so call registerListener here
|
// NodeList won't be available to the settings manager when it is created, so call registerListener here
|
||||||
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
|
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::NodeKickRequest, &_settingsManager, "processNodeKickRequestPacket");
|
packetReceiver.registerListener(PacketType::NodeKickRequest, &_settingsManager, "processNodeKickRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::UsernameFromIDRequest, &_settingsManager, "processUsernameFromIDRequestPacket");
|
packetReceiver.registerListener(PacketType::UsernameFromIDRequest, &_settingsManager, "processUsernameFromIDRequestPacket");
|
||||||
|
|
||||||
// register the gatekeeper for the packets it needs to receive
|
// register the gatekeeper for the packets it needs to receive
|
||||||
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
|
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket");
|
packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket");
|
||||||
|
@ -542,7 +542,7 @@ void DomainServer::setupNodeListAndAssignments() {
|
||||||
|
|
||||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied, this, "processICEServerHeartbeatDenialPacket");
|
packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied, this, "processICEServerHeartbeatDenialPacket");
|
||||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatACK, this, "processICEServerHeartbeatACK");
|
packetReceiver.registerListener(PacketType::ICEServerHeartbeatACK, this, "processICEServerHeartbeatACK");
|
||||||
|
|
||||||
// add whatever static assignments that have been parsed to the queue
|
// add whatever static assignments that have been parsed to the queue
|
||||||
addStaticAssignmentsToQueue();
|
addStaticAssignmentsToQueue();
|
||||||
|
|
||||||
|
@ -808,21 +808,19 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
||||||
for (Assignment::Type defaultedType = Assignment::AudioMixerType;
|
for (Assignment::Type defaultedType = Assignment::AudioMixerType;
|
||||||
defaultedType != Assignment::AllTypes;
|
defaultedType != Assignment::AllTypes;
|
||||||
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
|
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
|
||||||
if (!excludedTypes.contains(defaultedType)
|
if (!excludedTypes.contains(defaultedType) && defaultedType != Assignment::AgentType) {
|
||||||
&& defaultedType != Assignment::UNUSED_1
|
|
||||||
&& defaultedType != Assignment::AgentType) {
|
|
||||||
|
|
||||||
if (defaultedType == Assignment::AssetServerType) {
|
if (defaultedType == Assignment::AssetServerType) {
|
||||||
// Make sure the asset-server is enabled before adding it here.
|
// Make sure the asset-server is enabled before adding it here.
|
||||||
// Initially we do not assign it by default so we can test it in HF domains first
|
// Initially we do not assign it by default so we can test it in HF domains first
|
||||||
static const QString ASSET_SERVER_ENABLED_KEYPATH = "asset_server.enabled";
|
static const QString ASSET_SERVER_ENABLED_KEYPATH = "asset_server.enabled";
|
||||||
|
|
||||||
if (!_settingsManager.valueOrDefaultValueForKeyPath(ASSET_SERVER_ENABLED_KEYPATH).toBool()) {
|
if (!_settingsManager.valueOrDefaultValueForKeyPath(ASSET_SERVER_ENABLED_KEYPATH).toBool()) {
|
||||||
// skip to the next iteration if asset-server isn't enabled
|
// skip to the next iteration if asset-server isn't enabled
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// type has not been set from a command line or config file config, use the default
|
// type has not been set from a command line or config file config, use the default
|
||||||
// by clearing whatever exists and writing a single default assignment with no payload
|
// by clearing whatever exists and writing a single default assignment with no payload
|
||||||
Assignment* newAssignment = new Assignment(Assignment::CreateCommand, (Assignment::Type) defaultedType);
|
Assignment* newAssignment = new Assignment(Assignment::CreateCommand, (Assignment::Type) defaultedType);
|
||||||
|
@ -839,9 +837,9 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
|
||||||
// update this node's sockets in case they have changed
|
// update this node's sockets in case they have changed
|
||||||
sendingNode->setPublicSocket(nodeRequestData.publicSockAddr);
|
sendingNode->setPublicSocket(nodeRequestData.publicSockAddr);
|
||||||
sendingNode->setLocalSocket(nodeRequestData.localSockAddr);
|
sendingNode->setLocalSocket(nodeRequestData.localSockAddr);
|
||||||
|
|
||||||
// update the NodeInterestSet in case there have been any changes
|
// update the NodeInterestSet in case there have been any changes
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
||||||
|
|
||||||
// guard against patched agents asking to hear about other agents
|
// guard against patched agents asking to hear about other agents
|
||||||
auto safeInterestSet = nodeRequestData.interestList.toSet();
|
auto safeInterestSet = nodeRequestData.interestList.toSet();
|
||||||
|
@ -857,6 +855,44 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
|
||||||
sendDomainListToNode(sendingNode, message->getSenderSockAddr());
|
sendDomainListToNode(sendingNode, message->getSenderSockAddr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
|
||||||
|
auto nodeAData = static_cast<DomainServerNodeData*>(nodeA->getLinkedData());
|
||||||
|
auto nodeBData = static_cast<DomainServerNodeData*>(nodeB->getLinkedData());
|
||||||
|
|
||||||
|
// if we have no linked data for node A then B can't possibly be in the interest set
|
||||||
|
if (!nodeAData) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first check if the general interest set A contains the type for B
|
||||||
|
if (nodeAData->getNodeInterestSet().contains(nodeB->getType())) {
|
||||||
|
// given that there is a match in the general interest set, do any special checks
|
||||||
|
|
||||||
|
// (1/19/17) Agents only need to connect to Entity Script Servers to perform administrative tasks
|
||||||
|
// related to entity server scripts. Only agents with rez permissions should be doing that, so
|
||||||
|
// if the agent does not have those permissions, we do not want them and the server to incur the
|
||||||
|
// overhead of connecting to one another. Additionally we exclude agents that do not care about the
|
||||||
|
// Entity Script Server and won't attempt to connect to it.
|
||||||
|
|
||||||
|
bool isAgentWithoutRights = nodeA->getType() == NodeType::Agent
|
||||||
|
&& nodeB->getType() == NodeType::EntityScriptServer
|
||||||
|
&& !nodeA->getCanRez() && !nodeA->getCanRezTmp();
|
||||||
|
|
||||||
|
if (isAgentWithoutRights) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isScriptServerForIneffectiveAgent =
|
||||||
|
(nodeA->getType() == NodeType::EntityScriptServer && nodeB->getType() == NodeType::Agent)
|
||||||
|
&& ((nodeBData && !nodeBData->getNodeInterestSet().contains(NodeType::EntityScriptServer))
|
||||||
|
|| (!nodeB->getCanRez() && !nodeB->getCanRezTmp()));
|
||||||
|
|
||||||
|
return !isScriptServerForIneffectiveAgent;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int DomainServer::countConnectedUsers() {
|
unsigned int DomainServer::countConnectedUsers() {
|
||||||
unsigned int result = 0;
|
unsigned int result = 0;
|
||||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
@ -928,14 +964,14 @@ void DomainServer::handleConnectedNode(SharedNodePointer newNode) {
|
||||||
|
|
||||||
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr) {
|
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr) {
|
||||||
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NUM_BYTES_RFC4122_UUID + 2;
|
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NUM_BYTES_RFC4122_UUID + 2;
|
||||||
|
|
||||||
// setup the extended header for the domain list packets
|
// setup the extended header for the domain list packets
|
||||||
// this data is at the beginning of each of the domain list packets
|
// this data is at the beginning of each of the domain list packets
|
||||||
QByteArray extendedHeader(NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES, 0);
|
QByteArray extendedHeader(NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES, 0);
|
||||||
QDataStream extendedHeaderStream(&extendedHeader, QIODevice::WriteOnly);
|
QDataStream extendedHeaderStream(&extendedHeader, QIODevice::WriteOnly);
|
||||||
|
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
extendedHeaderStream << limitedNodeList->getSessionUUID();
|
extendedHeaderStream << limitedNodeList->getSessionUUID();
|
||||||
extendedHeaderStream << node->getUUID();
|
extendedHeaderStream << node->getUUID();
|
||||||
extendedHeaderStream << node->getPermissions();
|
extendedHeaderStream << node->getPermissions();
|
||||||
|
@ -945,7 +981,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
||||||
// always send the node their own UUID back
|
// always send the node their own UUID back
|
||||||
QDataStream domainListStream(domainListPackets.get());
|
QDataStream domainListStream(domainListPackets.get());
|
||||||
|
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
|
||||||
// store the nodeInterestSet on this DomainServerNodeData, in case it has changed
|
// store the nodeInterestSet on this DomainServerNodeData, in case it has changed
|
||||||
auto& nodeInterestSet = nodeData->getNodeInterestSet();
|
auto& nodeInterestSet = nodeData->getNodeInterestSet();
|
||||||
|
@ -955,10 +991,8 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
||||||
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
||||||
if (nodeData->isAuthenticated()) {
|
if (nodeData->isAuthenticated()) {
|
||||||
// if this authenticated node has any interest types, send back those nodes as well
|
// if this authenticated node has any interest types, send back those nodes as well
|
||||||
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){
|
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||||
if (otherNode->getUUID() != node->getUUID()
|
if (otherNode->getUUID() != node->getUUID() && isInInterestSet(node, otherNode)) {
|
||||||
&& nodeInterestSet.contains(otherNode->getType())) {
|
|
||||||
|
|
||||||
// since we're about to add a node to the packet we start a segment
|
// since we're about to add a node to the packet we start a segment
|
||||||
domainListPackets->startSegment();
|
domainListPackets->startSegment();
|
||||||
|
|
||||||
|
@ -974,7 +1008,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// send an empty list to the node, in case there were no other nodes
|
// send an empty list to the node, in case there were no other nodes
|
||||||
domainListPackets->closeCurrentPacket(true);
|
domainListPackets->closeCurrentPacket(true);
|
||||||
|
|
||||||
|
@ -983,8 +1017,8 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
||||||
}
|
}
|
||||||
|
|
||||||
QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
|
QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
|
||||||
DomainServerNodeData* nodeAData = dynamic_cast<DomainServerNodeData*>(nodeA->getLinkedData());
|
DomainServerNodeData* nodeAData = static_cast<DomainServerNodeData*>(nodeA->getLinkedData());
|
||||||
DomainServerNodeData* nodeBData = dynamic_cast<DomainServerNodeData*>(nodeB->getLinkedData());
|
DomainServerNodeData* nodeBData = static_cast<DomainServerNodeData*>(nodeB->getLinkedData());
|
||||||
|
|
||||||
if (nodeAData && nodeBData) {
|
if (nodeAData && nodeBData) {
|
||||||
QUuid& secretUUID = nodeAData->getSessionSecretHash()[nodeB->getUUID()];
|
QUuid& secretUUID = nodeAData->getSessionSecretHash()[nodeB->getUUID()];
|
||||||
|
@ -994,7 +1028,7 @@ QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, con
|
||||||
secretUUID = QUuid::createUuid();
|
secretUUID = QUuid::createUuid();
|
||||||
|
|
||||||
// set it on the other Node's sessionSecretHash
|
// set it on the other Node's sessionSecretHash
|
||||||
reinterpret_cast<DomainServerNodeData*>(nodeBData)->getSessionSecretHash().insert(nodeA->getUUID(), secretUUID);
|
static_cast<DomainServerNodeData*>(nodeBData)->getSessionSecretHash().insert(nodeA->getUUID(), secretUUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
return secretUUID;
|
return secretUUID;
|
||||||
|
@ -1020,8 +1054,7 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
||||||
[&](const SharedNodePointer& node)->bool {
|
[&](const SharedNodePointer& node)->bool {
|
||||||
if (node->getLinkedData() && node->getActiveSocket() && node != addedNode) {
|
if (node->getLinkedData() && node->getActiveSocket() && node != addedNode) {
|
||||||
// is the added Node in this node's interest list?
|
// is the added Node in this node's interest list?
|
||||||
DomainServerNodeData* nodeData = dynamic_cast<DomainServerNodeData*>(node->getLinkedData());
|
return isInInterestSet(node, addedNode);
|
||||||
return nodeData->getNodeInterestSet().contains(addedNode->getType());
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1124,7 +1157,7 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<ReceivedMessage
|
||||||
void DomainServer::setupPendingAssignmentCredits() {
|
void DomainServer::setupPendingAssignmentCredits() {
|
||||||
// enumerate the NodeList to find the assigned nodes
|
// enumerate the NodeList to find the assigned nodes
|
||||||
DependencyManager::get<LimitedNodeList>()->eachNode([&](const SharedNodePointer& node){
|
DependencyManager::get<LimitedNodeList>()->eachNode([&](const SharedNodePointer& node){
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
|
||||||
if (!nodeData->getAssignmentUUID().isNull() && !nodeData->getWalletUUID().isNull()) {
|
if (!nodeData->getAssignmentUUID().isNull() && !nodeData->getWalletUUID().isNull()) {
|
||||||
// check if we have a non-finalized transaction for this node to add this amount to
|
// check if we have a non-finalized transaction for this node to add this amount to
|
||||||
|
@ -1510,7 +1543,7 @@ void DomainServer::sendHeartbeatToIceServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode) {
|
void DomainServer::processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode) {
|
||||||
auto nodeData = dynamic_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
auto nodeData = static_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
nodeData->updateJSONStats(packetList->getMessage());
|
nodeData->updateJSONStats(packetList->getMessage());
|
||||||
}
|
}
|
||||||
|
@ -1556,7 +1589,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
|
||||||
nodeJson[JSON_KEY_UPTIME] = QString::number(double(QDateTime::currentMSecsSinceEpoch() - node->getWakeTimestamp()) / 1000.0);
|
nodeJson[JSON_KEY_UPTIME] = QString::number(double(QDateTime::currentMSecsSinceEpoch() - node->getWakeTimestamp()) / 1000.0);
|
||||||
|
|
||||||
// if the node has pool information, add it
|
// if the node has pool information, add it
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
|
||||||
// add the node username, if it exists
|
// add the node username, if it exists
|
||||||
nodeJson[JSON_KEY_USERNAME] = nodeData->getUsername();
|
nodeJson[JSON_KEY_USERNAME] = nodeData->getUsername();
|
||||||
|
@ -1624,23 +1657,23 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation
|
if (connection->requestOperation() == QNetworkAccessManager::GetOperation
|
||||||
&& assignmentRegex.indexIn(url.path()) != -1) {
|
&& assignmentRegex.indexIn(url.path()) != -1) {
|
||||||
QUuid nodeUUID = QUuid(assignmentRegex.cap(1));
|
QUuid nodeUUID = QUuid(assignmentRegex.cap(1));
|
||||||
|
|
||||||
auto matchingNode = nodeList->nodeWithUUID(nodeUUID);
|
auto matchingNode = nodeList->nodeWithUUID(nodeUUID);
|
||||||
|
|
||||||
// don't handle if we don't have a matching node
|
// don't handle if we don't have a matching node
|
||||||
if (!matchingNode) {
|
if (!matchingNode) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeData = dynamic_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
auto nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
||||||
|
|
||||||
// don't handle if we don't have node data for this node
|
// don't handle if we don't have node data for this node
|
||||||
if (!nodeData) {
|
if (!nodeData) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID());
|
SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID());
|
||||||
|
|
||||||
// check if we have an assignment that matches this temp UUID, and it is a scripted assignment
|
// check if we have an assignment that matches this temp UUID, and it is a scripted assignment
|
||||||
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
|
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
|
||||||
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
|
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
|
||||||
|
@ -1655,7 +1688,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// request not handled
|
// request not handled
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1687,7 +1720,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
// enumerate the NodeList to find the assigned nodes
|
// enumerate the NodeList to find the assigned nodes
|
||||||
nodeList->eachNode([this, &assignedNodesJSON](const SharedNodePointer& node){
|
nodeList->eachNode([this, &assignedNodesJSON](const SharedNodePointer& node){
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
|
||||||
if (!nodeData->getAssignmentUUID().isNull()) {
|
if (!nodeData->getAssignmentUUID().isNull()) {
|
||||||
// add the node using the UUID as the key
|
// add the node using the UUID as the key
|
||||||
|
@ -1775,7 +1808,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
if (matchingNode) {
|
if (matchingNode) {
|
||||||
// create a QJsonDocument with the stats QJsonObject
|
// create a QJsonDocument with the stats QJsonObject
|
||||||
QJsonObject statsObject =
|
QJsonObject statsObject =
|
||||||
reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData())->getStatsJSONObject();
|
static_cast<DomainServerNodeData*>(matchingNode->getLinkedData())->getStatsJSONObject();
|
||||||
|
|
||||||
// add the node type to the JSON data for output purposes
|
// add the node type to the JSON data for output purposes
|
||||||
statsObject["node_type"] = NodeType::getNodeTypeName(matchingNode->getType()).toLower().replace(' ', '-');
|
statsObject["node_type"] = NodeType::getNodeTypeName(matchingNode->getType()).toLower().replace(' ', '-');
|
||||||
|
@ -2247,7 +2280,7 @@ void DomainServer::addStaticAssignmentsToQueue() {
|
||||||
// if the domain-server has just restarted,
|
// if the domain-server has just restarted,
|
||||||
// check if there are static assignments that we need to throw into the assignment queue
|
// check if there are static assignments that we need to throw into the assignment queue
|
||||||
auto sharedAssignments = _allAssignments.values();
|
auto sharedAssignments = _allAssignments.values();
|
||||||
|
|
||||||
// sort the assignments to put the server/mixer assignments first
|
// sort the assignments to put the server/mixer assignments first
|
||||||
qSort(sharedAssignments.begin(), sharedAssignments.end(), [](SharedAssignmentPointer a, SharedAssignmentPointer b){
|
qSort(sharedAssignments.begin(), sharedAssignments.end(), [](SharedAssignmentPointer a, SharedAssignmentPointer b){
|
||||||
if (a->getType() == b->getType()) {
|
if (a->getType() == b->getType()) {
|
||||||
|
@ -2258,9 +2291,9 @@ void DomainServer::addStaticAssignmentsToQueue() {
|
||||||
return a->getType() != Assignment::AgentType;
|
return a->getType() != Assignment::AgentType;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
auto staticAssignment = sharedAssignments.begin();
|
auto staticAssignment = sharedAssignments.begin();
|
||||||
|
|
||||||
while (staticAssignment != sharedAssignments.end()) {
|
while (staticAssignment != sharedAssignments.end()) {
|
||||||
// add any of the un-matched static assignments to the queue
|
// add any of the un-matched static assignments to the queue
|
||||||
|
|
||||||
|
@ -2371,7 +2404,6 @@ void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMes
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::handleKillNode(SharedNodePointer nodeToKill) {
|
void DomainServer::handleKillNode(SharedNodePointer nodeToKill) {
|
||||||
auto nodeType = nodeToKill->getType();
|
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
const QUuid& nodeUUID = nodeToKill->getUUID();
|
const QUuid& nodeUUID = nodeToKill->getUUID();
|
||||||
|
|
||||||
|
@ -2383,10 +2415,9 @@ void DomainServer::handleKillNode(SharedNodePointer nodeToKill) {
|
||||||
removedNodePacket->write(nodeUUID.toRfc4122());
|
removedNodePacket->write(nodeUUID.toRfc4122());
|
||||||
|
|
||||||
// broadcast out the DomainServerRemovedNode message
|
// broadcast out the DomainServerRemovedNode message
|
||||||
limitedNodeList->eachMatchingNode([&nodeType](const SharedNodePointer& otherNode) -> bool {
|
limitedNodeList->eachMatchingNode([this, &nodeToKill](const SharedNodePointer& otherNode) -> bool {
|
||||||
// only send the removed node packet to nodes that care about the type of node this was
|
// only send the removed node packet to nodes that care about the type of node this was
|
||||||
auto nodeLinkedData = dynamic_cast<DomainServerNodeData*>(otherNode->getLinkedData());
|
return isInInterestSet(otherNode, nodeToKill);
|
||||||
return (nodeLinkedData != nullptr) && nodeLinkedData->getNodeInterestSet().contains(nodeType);
|
|
||||||
}, [&limitedNodeList](const SharedNodePointer& otherNode){
|
}, [&limitedNodeList](const SharedNodePointer& otherNode){
|
||||||
limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode);
|
limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode);
|
||||||
});
|
});
|
||||||
|
|
|
@ -132,6 +132,8 @@ private:
|
||||||
|
|
||||||
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr);
|
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr);
|
||||||
|
|
||||||
|
bool isInInterestSet(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB);
|
||||||
|
|
||||||
QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB);
|
QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB);
|
||||||
void broadcastNewNode(const SharedNodePointer& node);
|
void broadcastNewNode(const SharedNodePointer& node);
|
||||||
|
|
||||||
|
|
|
@ -725,7 +725,7 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
||||||
}
|
}
|
||||||
|
|
||||||
// potentially remove connect permissions for the MAC address and machine fingerprint
|
// potentially remove connect permissions for the MAC address and machine fingerprint
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
// mac address first
|
// mac address first
|
||||||
NodePermissionsKey macAddressKey(nodeData->getHardwareAddress(), 0);
|
NodePermissionsKey macAddressKey(nodeData->getHardwareAddress(), 0);
|
||||||
|
@ -807,7 +807,7 @@ void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPoin
|
||||||
usernameFromIDReplyPacket->writeString(verifiedUsername);
|
usernameFromIDReplyPacket->writeString(verifiedUsername);
|
||||||
|
|
||||||
// now put in the machine fingerprint
|
// now put in the machine fingerprint
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
||||||
machineFingerprint = nodeData ? nodeData->getMachineFingerprint() : QUuid();
|
machineFingerprint = nodeData ? nodeData->getMachineFingerprint() : QUuid();
|
||||||
usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122());
|
usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
#include <CursorManager.h>
|
#include <CursorManager.h>
|
||||||
#include <DebugDraw.h>
|
#include <DebugDraw.h>
|
||||||
#include <DeferredLightingEffect.h>
|
#include <DeferredLightingEffect.h>
|
||||||
#include <display-plugins/DisplayPlugin.h>
|
#include <EntityScriptClient.h>
|
||||||
#include <EntityScriptingInterface.h>
|
#include <EntityScriptingInterface.h>
|
||||||
#include <ErrorDialog.h>
|
#include <ErrorDialog.h>
|
||||||
#include <FileScriptingInterface.h>
|
#include <FileScriptingInterface.h>
|
||||||
|
@ -173,6 +173,7 @@
|
||||||
#include "FrameTimingsScriptingInterface.h"
|
#include "FrameTimingsScriptingInterface.h"
|
||||||
#include <GPUIdent.h>
|
#include <GPUIdent.h>
|
||||||
#include <gl/GLHelpers.h>
|
#include <gl/GLHelpers.h>
|
||||||
|
#include <EntityScriptClient.h>
|
||||||
|
|
||||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||||
// FIXME seems to be broken.
|
// FIXME seems to be broken.
|
||||||
|
@ -458,7 +459,7 @@ bool setupEssentials(int& argc, char** argv) {
|
||||||
// Set dependencies
|
// Set dependencies
|
||||||
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
|
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
|
||||||
DependencyManager::set<StatTracker>();
|
DependencyManager::set<StatTracker>();
|
||||||
DependencyManager::set<ScriptEngines>();
|
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
|
||||||
DependencyManager::set<Preferences>();
|
DependencyManager::set<Preferences>();
|
||||||
DependencyManager::set<recording::Deck>();
|
DependencyManager::set<recording::Deck>();
|
||||||
DependencyManager::set<recording::Recorder>();
|
DependencyManager::set<recording::Recorder>();
|
||||||
|
@ -514,6 +515,7 @@ bool setupEssentials(int& argc, char** argv) {
|
||||||
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
|
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
|
||||||
DependencyManager::set<CompositorHelper>();
|
DependencyManager::set<CompositorHelper>();
|
||||||
DependencyManager::set<OffscreenQmlSurfaceCache>();
|
DependencyManager::set<OffscreenQmlSurfaceCache>();
|
||||||
|
DependencyManager::set<EntityScriptClient>();
|
||||||
return previousSessionCrashed;
|
return previousSessionCrashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,7 +854,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
|
|
||||||
// tell the NodeList instance who to tell the domain server we care about
|
// tell the NodeList instance who to tell the domain server we care about
|
||||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||||
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
|
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer << NodeType::EntityScriptServer);
|
||||||
|
|
||||||
// connect to the packet sent signal of the _entityEditSender
|
// connect to the packet sent signal of the _entityEditSender
|
||||||
connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
|
connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
|
||||||
|
@ -5494,6 +5496,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
||||||
scriptEngine->registerGlobalObject("Controller", scriptingInterface.data());
|
scriptEngine->registerGlobalObject("Controller", scriptingInterface.data());
|
||||||
UserInputMapper::registerControllerTypes(scriptEngine);
|
UserInputMapper::registerControllerTypes(scriptEngine);
|
||||||
|
|
||||||
|
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||||
|
scriptEngine->registerGlobalObject("Recording", recordingInterface.data());
|
||||||
|
|
||||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||||
connect(scriptEngine, &ScriptEngine::printedMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onPrintedMessage);
|
connect(scriptEngine, &ScriptEngine::printedMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onPrintedMessage);
|
||||||
connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onErrorMessage);
|
connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onErrorMessage);
|
||||||
|
|
|
@ -180,9 +180,6 @@ bool AudioInjector::injectLocally() {
|
||||||
} else {
|
} else {
|
||||||
qCDebug(audio) << "AudioInjector::injectLocally called without any data in Sound QByteArray";
|
qCDebug(audio) << "AudioInjector::injectLocally called without any data in Sound QByteArray";
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
qCDebug(audio) << "AudioInjector::injectLocally cannot inject locally with no local audio interface present.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
|
|
@ -45,9 +45,7 @@
|
||||||
|
|
||||||
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||||
AbstractScriptingServicesInterface* scriptingServices) :
|
AbstractScriptingServicesInterface* scriptingServices) :
|
||||||
OctreeRenderer(),
|
|
||||||
_wantScripts(wantScripts),
|
_wantScripts(wantScripts),
|
||||||
_entitiesScriptEngine(NULL),
|
|
||||||
_lastPointerEventValid(false),
|
_lastPointerEventValid(false),
|
||||||
_viewState(viewState),
|
_viewState(viewState),
|
||||||
_scriptingServices(scriptingServices),
|
_scriptingServices(scriptingServices),
|
||||||
|
@ -103,7 +101,7 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() {
|
||||||
// Keep a ref to oldEngine until newEngine is ready so EntityScriptingInterface has something to use
|
// Keep a ref to oldEngine until newEngine is ready so EntityScriptingInterface has something to use
|
||||||
auto oldEngine = _entitiesScriptEngine;
|
auto oldEngine = _entitiesScriptEngine;
|
||||||
|
|
||||||
auto newEngine = new ScriptEngine(NO_SCRIPT, QString("Entities %1").arg(++_entitiesScriptEngineCount));
|
auto newEngine = new ScriptEngine(ScriptEngine::ENTITY_CLIENT_SCRIPT, NO_SCRIPT, QString("Entities %1").arg(++_entitiesScriptEngineCount));
|
||||||
_entitiesScriptEngine = QSharedPointer<ScriptEngine>(newEngine, entitiesScriptEngineDeleter);
|
_entitiesScriptEngine = QSharedPointer<ScriptEngine>(newEngine, entitiesScriptEngineDeleter);
|
||||||
|
|
||||||
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine.data());
|
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine.data());
|
||||||
|
@ -169,7 +167,7 @@ void EntityTreeRenderer::init() {
|
||||||
connect(entityTree.get(), &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity, Qt::QueuedConnection);
|
connect(entityTree.get(), &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity, Qt::QueuedConnection);
|
||||||
connect(entityTree.get(), &EntityTree::addingEntity, this, &EntityTreeRenderer::addingEntity, Qt::QueuedConnection);
|
connect(entityTree.get(), &EntityTree::addingEntity, this, &EntityTreeRenderer::addingEntity, Qt::QueuedConnection);
|
||||||
connect(entityTree.get(), &EntityTree::entityScriptChanging,
|
connect(entityTree.get(), &EntityTree::entityScriptChanging,
|
||||||
this, &EntityTreeRenderer::entitySciptChanging, Qt::QueuedConnection);
|
this, &EntityTreeRenderer::entityScriptChanging, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::shutdown() {
|
void EntityTreeRenderer::shutdown() {
|
||||||
|
@ -939,7 +937,7 @@ void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID, const bool reload) {
|
void EntityTreeRenderer::entityScriptChanging(const EntityItemID& entityID, const bool reload) {
|
||||||
if (_tree && !_shuttingDown) {
|
if (_tree && !_shuttingDown) {
|
||||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||||
checkAndCallPreload(entityID, reload);
|
checkAndCallPreload(entityID, reload);
|
||||||
|
@ -1063,7 +1061,7 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCollisionOwner(myNodeID, entityTree, idA, collision)) {
|
if (isCollisionOwner(myNodeID, entityTree, idB, collision)) {
|
||||||
emit collisionWithEntity(idB, idA, collision);
|
emit collisionWithEntity(idB, idA, collision);
|
||||||
if (_entitiesScriptEngine) {
|
if (_entitiesScriptEngine) {
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, collision);
|
_entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, collision);
|
||||||
|
|
|
@ -122,7 +122,7 @@ signals:
|
||||||
public slots:
|
public slots:
|
||||||
void addingEntity(const EntityItemID& entityID);
|
void addingEntity(const EntityItemID& entityID);
|
||||||
void deletingEntity(const EntityItemID& entityID);
|
void deletingEntity(const EntityItemID& entityID);
|
||||||
void entitySciptChanging(const EntityItemID& entityID, const bool reload);
|
void entityScriptChanging(const EntityItemID& entityID, const bool reload);
|
||||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||||
void updateEntityRenderStatus(bool shouldRenderEntities);
|
void updateEntityRenderStatus(bool shouldRenderEntities);
|
||||||
void updateZone(const EntityItemID& id);
|
void updateZone(const EntityItemID& id);
|
||||||
|
|
|
@ -121,6 +121,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
||||||
requestedProperties += PROP_LIFETIME;
|
requestedProperties += PROP_LIFETIME;
|
||||||
requestedProperties += PROP_SCRIPT;
|
requestedProperties += PROP_SCRIPT;
|
||||||
requestedProperties += PROP_SCRIPT_TIMESTAMP;
|
requestedProperties += PROP_SCRIPT_TIMESTAMP;
|
||||||
|
requestedProperties += PROP_SERVER_SCRIPTS;
|
||||||
requestedProperties += PROP_COLLISION_SOUND_URL;
|
requestedProperties += PROP_COLLISION_SOUND_URL;
|
||||||
requestedProperties += PROP_REGISTRATION_POINT;
|
requestedProperties += PROP_REGISTRATION_POINT;
|
||||||
requestedProperties += PROP_ANGULAR_DAMPING;
|
requestedProperties += PROP_ANGULAR_DAMPING;
|
||||||
|
@ -265,6 +266,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
||||||
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, getLifetime());
|
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, getLifetime());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, getScript());
|
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, getScript());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, getScriptTimestamp());
|
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, getScriptTimestamp());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SERVER_SCRIPTS, getServerScripts());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint());
|
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping());
|
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, getVisible());
|
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, getVisible());
|
||||||
|
@ -778,6 +780,19 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||||
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
||||||
|
|
||||||
|
{
|
||||||
|
// We use this scope to work around an issue stopping server script changes
|
||||||
|
// from being received by an entity script server running a script that continously updates an entity.
|
||||||
|
|
||||||
|
// Basically, we'll allow recent changes to the server scripts even if there are local changes to other properties
|
||||||
|
// that have been made more recently.
|
||||||
|
|
||||||
|
bool overwriteLocalData = !ignoreServerPacket || (lastEditedFromBufferAdjusted > _serverScriptsChangedTimestamp);
|
||||||
|
|
||||||
|
READ_ENTITY_PROPERTY(PROP_SERVER_SCRIPTS, QString, setServerScripts);
|
||||||
|
}
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, updateRegistrationPoint);
|
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, updateRegistrationPoint);
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping);
|
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping);
|
||||||
|
@ -1186,6 +1201,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptTimestamp, getScriptTimestamp);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptTimestamp, getScriptTimestamp);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(serverScripts, getServerScripts);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionSoundURL, getCollisionSoundURL);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionSoundURL, getCollisionSoundURL);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getLocalAngularVelocity);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getLocalAngularVelocity);
|
||||||
|
@ -1298,6 +1314,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||||
// non-simulation properties below
|
// non-simulation properties below
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptTimestamp, setScriptTimestamp);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptTimestamp, setScriptTimestamp);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(serverScripts, setServerScripts);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
|
||||||
|
@ -2228,3 +2245,30 @@ void EntityItem::globalizeProperties(EntityItemProperties& properties, const QSt
|
||||||
QUuid empty;
|
QUuid empty;
|
||||||
properties.setParentID(empty);
|
properties.setParentID(empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool EntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
|
||||||
|
|
||||||
|
// The intention for the query JSON filter and this method is to be flexible to handle a variety of filters for
|
||||||
|
// ALL entity properties. Some work will need to be done to the property system so that it can be more flexible
|
||||||
|
// (to grab the value and default value of a property given the string representation of that property, for example)
|
||||||
|
|
||||||
|
// currently the only property filter we handle is '+' for serverScripts
|
||||||
|
// which means that we only handle a filtered query asking for entities where the serverScripts property is non-default
|
||||||
|
|
||||||
|
static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
|
||||||
|
|
||||||
|
foreach(const auto& property, jsonFilters.keys()) {
|
||||||
|
if (property == SERVER_SCRIPTS_PROPERTY && jsonFilters[property] == EntityQueryFilterSymbol::NonDefault) {
|
||||||
|
// check if this entity has a non-default value for serverScripts
|
||||||
|
if (_serverScripts != ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the json filter syntax did not match what we expected, return a match
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ public:
|
||||||
void markAsChangedOnServer() { _changedOnServer = usecTimestampNow(); }
|
void markAsChangedOnServer() { _changedOnServer = usecTimestampNow(); }
|
||||||
quint64 getLastChangedOnServer() const { return _changedOnServer; }
|
quint64 getLastChangedOnServer() const { return _changedOnServer; }
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
|
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
|
||||||
|
|
||||||
virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
@ -254,12 +254,16 @@ public:
|
||||||
using SpatiallyNestable::getQueryAACube;
|
using SpatiallyNestable::getQueryAACube;
|
||||||
virtual AACube getQueryAACube(bool& success) const override;
|
virtual AACube getQueryAACube(bool& success) const override;
|
||||||
|
|
||||||
const QString& getScript() const { return _script; }
|
QString getScript() const { return _script; }
|
||||||
void setScript(const QString& value) { _script = value; }
|
void setScript(const QString& value) { _script = value; }
|
||||||
|
|
||||||
quint64 getScriptTimestamp() const { return _scriptTimestamp; }
|
quint64 getScriptTimestamp() const { return _scriptTimestamp; }
|
||||||
void setScriptTimestamp(const quint64 value) { _scriptTimestamp = value; }
|
void setScriptTimestamp(const quint64 value) { _scriptTimestamp = value; }
|
||||||
|
|
||||||
|
QString getServerScripts() const { return _serverScripts; }
|
||||||
|
void setServerScripts(const QString& serverScripts)
|
||||||
|
{ _serverScripts = serverScripts; _serverScriptsChangedTimestamp = usecTimestampNow(); }
|
||||||
|
|
||||||
const QString& getCollisionSoundURL() const { return _collisionSoundURL; }
|
const QString& getCollisionSoundURL() const { return _collisionSoundURL; }
|
||||||
void setCollisionSoundURL(const QString& value);
|
void setCollisionSoundURL(const QString& value);
|
||||||
|
|
||||||
|
@ -463,6 +467,8 @@ public:
|
||||||
|
|
||||||
QUuid getLastEditedBy() const { return _lastEditedBy; }
|
QUuid getLastEditedBy() const { return _lastEditedBy; }
|
||||||
void setLastEditedBy(QUuid value) { _lastEditedBy = value; }
|
void setLastEditedBy(QUuid value) { _lastEditedBy = value; }
|
||||||
|
|
||||||
|
bool matchesJSONFilters(const QJsonObject& jsonFilters) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
@ -511,6 +517,10 @@ protected:
|
||||||
QString _loadedScript; /// the value of _script when the last preload signal was sent
|
QString _loadedScript; /// the value of _script when the last preload signal was sent
|
||||||
quint64 _scriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP }; /// the script loaded property used for forced reload
|
quint64 _scriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP }; /// the script loaded property used for forced reload
|
||||||
|
|
||||||
|
QString _serverScripts;
|
||||||
|
/// keep track of time when _serverScripts property was last changed
|
||||||
|
quint64 _serverScriptsChangedTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP };
|
||||||
|
|
||||||
/// the value of _scriptTimestamp when the last preload signal was sent
|
/// the value of _scriptTimestamp when the last preload signal was sent
|
||||||
// NOTE: on construction we want this to be different from _scriptTimestamp so we intentionally bump it
|
// NOTE: on construction we want this to be different from _scriptTimestamp so we intentionally bump it
|
||||||
quint64 _loadedScriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 };
|
quint64 _loadedScriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 };
|
||||||
|
|
|
@ -32,21 +32,21 @@ KeyLightPropertyGroup EntityItemProperties::_staticKeyLight;
|
||||||
EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1);
|
EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1);
|
||||||
|
|
||||||
EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties) :
|
EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties) :
|
||||||
|
_id(UNKNOWN_ENTITY_ID),
|
||||||
|
_idSet(false),
|
||||||
|
_lastEdited(0),
|
||||||
|
_type(EntityTypes::Unknown),
|
||||||
|
|
||||||
_id(UNKNOWN_ENTITY_ID),
|
_localRenderAlpha(1.0f),
|
||||||
_idSet(false),
|
|
||||||
_lastEdited(0),
|
|
||||||
_type(EntityTypes::Unknown),
|
|
||||||
|
|
||||||
_localRenderAlpha(1.0f),
|
_localRenderAlphaChanged(false),
|
||||||
|
|
||||||
_localRenderAlphaChanged(false),
|
_defaultSettings(true),
|
||||||
|
_naturalDimensions(1.0f, 1.0f, 1.0f),
|
||||||
_defaultSettings(true),
|
_naturalPosition(0.0f, 0.0f, 0.0f),
|
||||||
_naturalDimensions(1.0f, 1.0f, 1.0f),
|
_desiredProperties(desiredProperties)
|
||||||
_naturalPosition(0.0f, 0.0f, 0.0f),
|
|
||||||
_desiredProperties(desiredProperties)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& sittingPoints) {
|
void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& sittingPoints) {
|
||||||
|
@ -241,6 +241,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
CHECK_PROPERTY_CHANGE(PROP_LIFETIME, lifetime);
|
CHECK_PROPERTY_CHANGE(PROP_LIFETIME, lifetime);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script);
|
CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp);
|
CHECK_PROPERTY_CHANGE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_SERVER_SCRIPTS, serverScripts);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_COLLISION_SOUND_URL, collisionSoundURL);
|
CHECK_PROPERTY_CHANGE(PROP_COLLISION_SOUND_URL, collisionSoundURL);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
|
CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_COLOR_SPREAD, colorSpread);
|
CHECK_PROPERTY_CHANGE(PROP_COLOR_SPREAD, colorSpread);
|
||||||
|
@ -388,6 +389,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LIFETIME, lifetime);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LIFETIME, lifetime);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT, script);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT, script);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SERVER_SCRIPTS, serverScripts);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_REGISTRATION_POINT, registrationPoint);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_REGISTRATION_POINT, registrationPoint);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_VELOCITY, angularVelocity);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_VELOCITY, angularVelocity);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_DAMPING, angularDamping);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_DAMPING, angularDamping);
|
||||||
|
@ -628,6 +630,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(lifetime, float, setLifetime);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(lifetime, float, setLifetime);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(script, QString, setScript);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(script, QString, setScript);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptTimestamp, quint64, setScriptTimestamp);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptTimestamp, quint64, setScriptTimestamp);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(serverScripts, QString, setServerScripts);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(registrationPoint, glmVec3, setRegistrationPoint);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(registrationPoint, glmVec3, setRegistrationPoint);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(angularVelocity, glmVec3, setAngularVelocity);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(angularVelocity, glmVec3, setAngularVelocity);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping);
|
||||||
|
@ -917,6 +920,7 @@ QScriptValue EntityItemProperties::entityPropertyFlagsToScriptValue(QScriptEngin
|
||||||
static QHash<QString, EntityPropertyList> _propertyStringsToEnums;
|
static QHash<QString, EntityPropertyList> _propertyStringsToEnums;
|
||||||
|
|
||||||
void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags) {
|
void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags) {
|
||||||
|
|
||||||
static std::once_flag initMap;
|
static std::once_flag initMap;
|
||||||
|
|
||||||
std::call_once(initMap, [](){
|
std::call_once(initMap, [](){
|
||||||
|
@ -934,6 +938,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
||||||
ADD_PROPERTY_TO_MAP(PROP_LIFETIME, Lifetime, lifetime, float);
|
ADD_PROPERTY_TO_MAP(PROP_LIFETIME, Lifetime, lifetime, float);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_SCRIPT, Script, script, QString);
|
ADD_PROPERTY_TO_MAP(PROP_SCRIPT, Script, script, QString);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64);
|
ADD_PROPERTY_TO_MAP(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString);
|
ADD_PROPERTY_TO_MAP(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, xColor);
|
ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, xColor);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_COLOR_SPREAD, ColorSpread, colorSpread, xColor);
|
ADD_PROPERTY_TO_MAP(PROP_COLOR_SPREAD, ColorSpread, colorSpread, xColor);
|
||||||
|
@ -1201,6 +1206,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
||||||
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, properties.getLifetime());
|
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, properties.getLifetime());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, properties.getScript());
|
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, properties.getScript());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, properties.getScriptTimestamp());
|
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, properties.getScriptTimestamp());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SERVER_SCRIPTS, properties.getServerScripts());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, properties.getRegistrationPoint());
|
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, properties.getRegistrationPoint());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, properties.getAngularVelocity());
|
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, properties.getAngularVelocity());
|
||||||
|
@ -1501,6 +1507,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT, QString, setScript);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT, QString, setScript);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SERVER_SCRIPTS, QString, setServerScripts);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, xColor, setColor);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, xColor, setColor);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity);
|
||||||
|
@ -1627,7 +1634,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
||||||
|
|
||||||
// NOTE: Spheres and Boxes are just special cases of Shape, and they need to include their PROP_SHAPE
|
// NOTE: Spheres and Boxes are just special cases of Shape, and they need to include their PROP_SHAPE
|
||||||
// when encoding/decoding edits because otherwise they can't polymorph to other shape types
|
// when encoding/decoding edits because otherwise they can't polymorph to other shape types
|
||||||
if (properties.getType() == EntityTypes::Shape ||
|
if (properties.getType() == EntityTypes::Shape ||
|
||||||
properties.getType() == EntityTypes::Box ||
|
properties.getType() == EntityTypes::Box ||
|
||||||
properties.getType() == EntityTypes::Sphere) {
|
properties.getType() == EntityTypes::Sphere) {
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape);
|
||||||
|
@ -1688,6 +1695,7 @@ void EntityItemProperties::markAllChanged() {
|
||||||
_userDataChanged = true;
|
_userDataChanged = true;
|
||||||
_scriptChanged = true;
|
_scriptChanged = true;
|
||||||
_scriptTimestampChanged = true;
|
_scriptTimestampChanged = true;
|
||||||
|
_serverScriptsChanged = true;
|
||||||
_collisionSoundURLChanged = true;
|
_collisionSoundURLChanged = true;
|
||||||
_registrationPointChanged = true;
|
_registrationPointChanged = true;
|
||||||
_angularVelocityChanged = true;
|
_angularVelocityChanged = true;
|
||||||
|
@ -1896,6 +1904,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
||||||
if (scriptTimestampChanged()) {
|
if (scriptTimestampChanged()) {
|
||||||
out += "scriptTimestamp";
|
out += "scriptTimestamp";
|
||||||
}
|
}
|
||||||
|
if (serverScriptsChanged()) {
|
||||||
|
out += "serverScripts";
|
||||||
|
}
|
||||||
if (collisionSoundURLChanged()) {
|
if (collisionSoundURLChanged()) {
|
||||||
out += "collisionSoundURL";
|
out += "collisionSoundURL";
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,6 +221,8 @@ public:
|
||||||
|
|
||||||
DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY);
|
DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY);
|
||||||
|
|
||||||
|
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
|
||||||
|
|
||||||
static QString getBackgroundModeString(BackgroundMode mode);
|
static QString getBackgroundModeString(BackgroundMode mode);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ const bool ENTITY_ITEM_DEFAULT_VISIBLE = true;
|
||||||
|
|
||||||
const QString ENTITY_ITEM_DEFAULT_SCRIPT = QString("");
|
const QString ENTITY_ITEM_DEFAULT_SCRIPT = QString("");
|
||||||
const quint64 ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP = 0;
|
const quint64 ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP = 0;
|
||||||
|
const QString ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS = QString("");
|
||||||
const QString ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL = QString("");
|
const QString ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL = QString("");
|
||||||
const glm::vec3 ENTITY_ITEM_DEFAULT_REGISTRATION_POINT = ENTITY_ITEM_HALF_VEC3; // center
|
const glm::vec3 ENTITY_ITEM_DEFAULT_REGISTRATION_POINT = ENTITY_ITEM_HALF_VEC3; // center
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
|
|
||||||
#include "../octree/OctreeQueryNode.h"
|
#include <OctreeQueryNode.h>
|
||||||
|
|
||||||
class EntityNodeData : public OctreeQueryNode {
|
class EntityNodeData : public OctreeQueryNode {
|
||||||
public:
|
public:
|
||||||
|
@ -22,9 +22,15 @@ public:
|
||||||
|
|
||||||
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
|
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
|
||||||
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
|
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
|
||||||
|
|
||||||
|
// these can only be called from the OctreeSendThread for the given Node
|
||||||
|
void insertEntitySentLastFrame(const QUuid& entityID) { _entitiesSentLastFrame.insert(entityID); }
|
||||||
|
void removeEntitySentLastFrame(const QUuid& entityID) { _entitiesSentLastFrame.remove(entityID); }
|
||||||
|
bool sentEntityLastFrame(const QUuid& entityID) { return _entitiesSentLastFrame.contains(entityID); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() };
|
quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() };
|
||||||
|
QSet<QUuid> _entitiesSentLastFrame;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityNodeData_h
|
#endif // hifi_EntityNodeData_h
|
|
@ -183,6 +183,8 @@ enum EntityPropertyList {
|
||||||
|
|
||||||
PROP_LAST_EDITED_BY,
|
PROP_LAST_EDITED_BY,
|
||||||
|
|
||||||
|
PROP_SERVER_SCRIPTS,
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// ATTENTION: add new properties to end of list just ABOVE this line
|
// ATTENTION: add new properties to end of list just ABOVE this line
|
||||||
PROP_AFTER_LAST_ITEM,
|
PROP_AFTER_LAST_ITEM,
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "QVariantGLM.h"
|
#include "QVariantGLM.h"
|
||||||
#include "SimulationOwner.h"
|
#include "SimulationOwner.h"
|
||||||
#include "ZoneEntityItem.h"
|
#include "ZoneEntityItem.h"
|
||||||
|
#include <EntityScriptClient.h>
|
||||||
|
|
||||||
|
|
||||||
EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) :
|
EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) :
|
||||||
|
@ -670,6 +671,38 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) {
|
||||||
|
auto client = DependencyManager::get<EntityScriptClient>();
|
||||||
|
return client->reloadServerScript(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityScriptingInterface::getServerScriptStatus(QUuid entityID, QScriptValue callback) {
|
||||||
|
auto client = DependencyManager::get<EntityScriptClient>();
|
||||||
|
auto request = client->createScriptStatusRequest(entityID);
|
||||||
|
connect(request, &GetScriptStatusRequest::finished, callback.engine(), [callback](GetScriptStatusRequest* request) mutable {
|
||||||
|
QString statusString;
|
||||||
|
switch (request->getStatus()) {
|
||||||
|
case RUNNING:
|
||||||
|
statusString = "running";
|
||||||
|
break;
|
||||||
|
case ERROR_LOADING_SCRIPT:
|
||||||
|
statusString = "error_loading_script";
|
||||||
|
break;
|
||||||
|
case ERROR_RUNNING_SCRIPT:
|
||||||
|
statusString = "error_running_script";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
statusString = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QScriptValueList args { request->getResponseReceived(), request->getIsRunning(), statusString, request->getErrorInfo() };
|
||||||
|
callback.call(QScriptValue(), args);
|
||||||
|
request->deleteLater();
|
||||||
|
});
|
||||||
|
request->start();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void EntityScriptingInterface::setLightsArePickable(bool value) {
|
void EntityScriptingInterface::setLightsArePickable(bool value) {
|
||||||
LightEntityItem::setLightsArePickable(value);
|
LightEntityItem::setLightsArePickable(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,6 +210,9 @@ public slots:
|
||||||
/// order to return an accurate result
|
/// order to return an accurate result
|
||||||
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue());
|
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue());
|
||||||
|
|
||||||
|
Q_INVOKABLE bool reloadServerScripts(QUuid entityID);
|
||||||
|
Q_INVOKABLE bool getServerScriptStatus(QUuid entityID, QScriptValue callback);
|
||||||
|
|
||||||
Q_INVOKABLE void setLightsArePickable(bool value);
|
Q_INVOKABLE void setLightsArePickable(bool value);
|
||||||
Q_INVOKABLE bool getLightsArePickable() const;
|
Q_INVOKABLE bool getLightsArePickable() const;
|
||||||
|
|
||||||
|
|
|
@ -390,10 +390,14 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID, const bool reload) {
|
void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID, bool reload) {
|
||||||
emit entityScriptChanging(entityItemID, reload);
|
emit entityScriptChanging(entityItemID, reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityTree::emitEntityServerScriptChanging(const EntityItemID& entityItemID, bool reload) {
|
||||||
|
emit entityServerScriptChanging(entityItemID, reload);
|
||||||
|
}
|
||||||
|
|
||||||
void EntityTree::notifyNewCollisionSoundURL(const QString& newURL, const EntityItemID& entityID) {
|
void EntityTree::notifyNewCollisionSoundURL(const QString& newURL, const EntityItemID& entityID) {
|
||||||
emit newCollisionSoundURL(QUrl(newURL), entityID);
|
emit newCollisionSoundURL(QUrl(newURL), entityID);
|
||||||
}
|
}
|
||||||
|
@ -958,9 +962,16 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
|
|
||||||
if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) {
|
if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) {
|
||||||
bool passedWhiteList = false;
|
bool passedWhiteList = false;
|
||||||
auto entityScript = properties.getScript();
|
|
||||||
|
// grab a URL representation of the entity script so we can check the host for this script
|
||||||
|
auto entityScriptURL = QUrl::fromUserInput(properties.getScript());
|
||||||
|
|
||||||
for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) {
|
for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) {
|
||||||
if (entityScript.startsWith(whiteListedPrefix, Qt::CaseInsensitive)) {
|
auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix);
|
||||||
|
|
||||||
|
// check if this script URL matches the whitelist domain and, optionally, is beneath the path
|
||||||
|
if (entityScriptURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 &&
|
||||||
|
entityScriptURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) {
|
||||||
passedWhiteList = true;
|
passedWhiteList = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,9 @@ using ModelWeakPointer = std::weak_ptr<Model>;
|
||||||
|
|
||||||
class EntitySimulation;
|
class EntitySimulation;
|
||||||
|
|
||||||
|
namespace EntityQueryFilterSymbol {
|
||||||
|
static const QString NonDefault = "+";
|
||||||
|
}
|
||||||
|
|
||||||
class NewlyCreatedEntityHook {
|
class NewlyCreatedEntityHook {
|
||||||
public:
|
public:
|
||||||
|
@ -201,7 +204,8 @@ public:
|
||||||
|
|
||||||
void entityChanged(EntityItemPointer entity);
|
void entityChanged(EntityItemPointer entity);
|
||||||
|
|
||||||
void emitEntityScriptChanging(const EntityItemID& entityItemID, const bool reload);
|
void emitEntityScriptChanging(const EntityItemID& entityItemID, bool reload);
|
||||||
|
void emitEntityServerScriptChanging(const EntityItemID& entityItemID, bool reload);
|
||||||
|
|
||||||
void setSimulation(EntitySimulationPointer simulation);
|
void setSimulation(EntitySimulationPointer simulation);
|
||||||
EntitySimulationPointer getSimulation() const { return _simulation; }
|
EntitySimulationPointer getSimulation() const { return _simulation; }
|
||||||
|
@ -270,6 +274,7 @@ signals:
|
||||||
void deletingEntity(const EntityItemID& entityID);
|
void deletingEntity(const EntityItemID& entityID);
|
||||||
void addingEntity(const EntityItemID& entityID);
|
void addingEntity(const EntityItemID& entityID);
|
||||||
void entityScriptChanging(const EntityItemID& entityItemID, const bool reload);
|
void entityScriptChanging(const EntityItemID& entityItemID, const bool reload);
|
||||||
|
void entityServerScriptChanging(const EntityItemID& entityItemID, const bool reload);
|
||||||
void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID);
|
void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID);
|
||||||
void clearingEntities();
|
void clearingEntities();
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <OctreeUtils.h>
|
#include <OctreeUtils.h>
|
||||||
|
|
||||||
#include "EntitiesLogging.h"
|
#include "EntitiesLogging.h"
|
||||||
|
#include "EntityNodeData.h"
|
||||||
#include "EntityItemProperties.h"
|
#include "EntityItemProperties.h"
|
||||||
#include "EntityTree.h"
|
#include "EntityTree.h"
|
||||||
#include "EntityTreeElement.h"
|
#include "EntityTreeElement.h"
|
||||||
|
@ -94,7 +95,7 @@ void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params)
|
||||||
bool EntityTreeElement::shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const {
|
bool EntityTreeElement::shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const {
|
||||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||||
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
|
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
|
||||||
|
|
||||||
if (extraEncodeData->contains(this)) {
|
if (extraEncodeData->contains(this)) {
|
||||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData
|
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData
|
||||||
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
|
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
|
||||||
|
@ -231,7 +232,7 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params) con
|
||||||
}
|
}
|
||||||
|
|
||||||
OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData,
|
OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData,
|
||||||
EncodeBitstreamParams& params) const {
|
EncodeBitstreamParams& params) const {
|
||||||
|
|
||||||
OctreeElement::AppendState appendElementState = OctreeElement::COMPLETED; // assume the best...
|
OctreeElement::AppendState appendElementState = OctreeElement::COMPLETED; // assume the best...
|
||||||
|
|
||||||
|
@ -278,25 +279,57 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
||||||
int numberOfEntitiesOffset = 0;
|
int numberOfEntitiesOffset = 0;
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
QVector<uint16_t> indexesOfEntitiesToInclude;
|
QVector<uint16_t> indexesOfEntitiesToInclude;
|
||||||
|
|
||||||
// It's possible that our element has been previous completed. In this case we'll simply not include any of our
|
// It's possible that our element has been previous completed. In this case we'll simply not include any of our
|
||||||
// entities for encoding. This is needed because we encode the element data at the "parent" level, and so we
|
// entities for encoding. This is needed because we encode the element data at the "parent" level, and so we
|
||||||
// need to handle the case where our sibling elements need encoding but we don't.
|
// need to handle the case where our sibling elements need encoding but we don't.
|
||||||
if (!entityTreeElementExtraEncodeData->elementCompleted) {
|
if (!entityTreeElementExtraEncodeData->elementCompleted) {
|
||||||
|
|
||||||
|
QJsonObject jsonFilters;
|
||||||
|
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
|
||||||
|
|
||||||
|
if (entityNodeData) {
|
||||||
|
// we have an EntityNodeData instance
|
||||||
|
// so we should assume that means we might have JSON filters to check
|
||||||
|
jsonFilters = entityNodeData->getJSONParameters();
|
||||||
|
}
|
||||||
|
|
||||||
for (uint16_t i = 0; i < _entityItems.size(); i++) {
|
for (uint16_t i = 0; i < _entityItems.size(); i++) {
|
||||||
EntityItemPointer entity = _entityItems[i];
|
EntityItemPointer entity = _entityItems[i];
|
||||||
bool includeThisEntity = true;
|
bool includeThisEntity = true;
|
||||||
|
|
||||||
if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastViewFrustumSent) {
|
if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastQuerySent) {
|
||||||
includeThisEntity = false;
|
includeThisEntity = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hadElementExtraData) {
|
// if this entity has been updated since our last full send and there are json filters, check them
|
||||||
includeThisEntity = includeThisEntity &&
|
if (includeThisEntity && !jsonFilters.isEmpty()) {
|
||||||
entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID());
|
|
||||||
|
// if params include JSON filters, check if this entity matches
|
||||||
|
bool entityMatchesFilters = entity->matchesJSONFilters(jsonFilters);
|
||||||
|
|
||||||
|
if (entityMatchesFilters) {
|
||||||
|
// make sure this entity is in the set of entities sent last frame
|
||||||
|
entityNodeData->insertEntitySentLastFrame(entity->getID());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// we might include this entity if it matched in the previous frame
|
||||||
|
if (entityNodeData->sentEntityLastFrame(entity->getID())) {
|
||||||
|
|
||||||
|
entityNodeData->removeEntitySentLastFrame(entity->getID());
|
||||||
|
} else {
|
||||||
|
includeThisEntity = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeThisEntity || params.recurseEverything) {
|
if (includeThisEntity && hadElementExtraData) {
|
||||||
|
includeThisEntity = entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID());
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only check the bounds against our frustum and LOD if the query has asked us to check against the frustum
|
||||||
|
// which can sometimes not be the case when JSON filters are sent
|
||||||
|
if (params.usesFrustum && (includeThisEntity || params.recurseEverything)) {
|
||||||
|
|
||||||
// we want to use the maximum possible box for this, so that we don't have to worry about the nuance of
|
// we want to use the maximum possible box for this, so that we don't have to worry about the nuance of
|
||||||
// simulation changing what's visible. consider the case where the entity contains an angular velocity
|
// simulation changing what's visible. consider the case where the entity contains an angular velocity
|
||||||
|
@ -925,6 +958,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
||||||
// 3) remember the old cube for the entity so we can mark it as dirty
|
// 3) remember the old cube for the entity so we can mark it as dirty
|
||||||
if (entityItem) {
|
if (entityItem) {
|
||||||
QString entityScriptBefore = entityItem->getScript();
|
QString entityScriptBefore = entityItem->getScript();
|
||||||
|
QString entityServerScriptsBefore = entityItem->getServerScripts();
|
||||||
quint64 entityScriptTimestampBefore = entityItem->getScriptTimestamp();
|
quint64 entityScriptTimestampBefore = entityItem->getScriptTimestamp();
|
||||||
bool bestFitBefore = bestFitEntityBounds(entityItem);
|
bool bestFitBefore = bestFitEntityBounds(entityItem);
|
||||||
EntityTreeElementPointer currentContainingElement = _myTree->getContainingElement(entityItemID);
|
EntityTreeElementPointer currentContainingElement = _myTree->getContainingElement(entityItemID);
|
||||||
|
@ -948,6 +982,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
||||||
}
|
}
|
||||||
|
|
||||||
QString entityScriptAfter = entityItem->getScript();
|
QString entityScriptAfter = entityItem->getScript();
|
||||||
|
QString entityServerScriptsAfter = entityItem->getServerScripts();
|
||||||
quint64 entityScriptTimestampAfter = entityItem->getScriptTimestamp();
|
quint64 entityScriptTimestampAfter = entityItem->getScriptTimestamp();
|
||||||
bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter;
|
bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter;
|
||||||
|
|
||||||
|
@ -956,6 +991,9 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
||||||
if (entityScriptBefore != entityScriptAfter || reload) {
|
if (entityScriptBefore != entityScriptAfter || reload) {
|
||||||
_myTree->emitEntityScriptChanging(entityItemID, reload); // the entity script has changed
|
_myTree->emitEntityScriptChanging(entityItemID, reload); // the entity script has changed
|
||||||
}
|
}
|
||||||
|
if (entityServerScriptsBefore != entityServerScriptsAfter || reload) {
|
||||||
|
_myTree->emitEntityServerScriptChanging(entityItemID, reload); // the entity server script has changed
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
entityItem = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args);
|
entityItem = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args);
|
||||||
|
|
|
@ -127,7 +127,6 @@ public:
|
||||||
|
|
||||||
bool alreadyFullyEncoded(EncodeBitstreamParams& params) const;
|
bool alreadyFullyEncoded(EncodeBitstreamParams& params) const;
|
||||||
|
|
||||||
|
|
||||||
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
|
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
|
||||||
virtual OctreeElement::AppendState appendElementData(OctreePacketData* packetData,
|
virtual OctreeElement::AppendState appendElementData(OctreePacketData* packetData,
|
||||||
EncodeBitstreamParams& params) const override;
|
EncodeBitstreamParams& params) const override;
|
||||||
|
|
|
@ -174,7 +174,7 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
requestedProperties += PROP_IS_SPOTLIGHT;
|
requestedProperties += PROP_IS_SPOTLIGHT;
|
||||||
|
|
|
@ -126,7 +126,7 @@ int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags LineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags LineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
requestedProperties += PROP_COLOR;
|
requestedProperties += PROP_COLOR;
|
||||||
|
|
|
@ -26,7 +26,7 @@ class LineEntityItem : public EntityItem {
|
||||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||||
|
|
||||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
|
|
@ -160,7 +160,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||||
|
|
||||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
|
|
@ -469,7 +469,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
requestedProperties += PROP_COLOR;
|
requestedProperties += PROP_COLOR;
|
||||||
|
|
|
@ -26,7 +26,7 @@ class PolyLineEntityItem : public EntityItem {
|
||||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||||
|
|
||||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
|
|
@ -179,7 +179,7 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
requestedProperties += PROP_VOXEL_VOLUME_SIZE;
|
requestedProperties += PROP_VOXEL_VOLUME_SIZE;
|
||||||
|
|
|
@ -26,7 +26,7 @@ class PolyVoxEntityItem : public EntityItem {
|
||||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||||
|
|
||||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
|
|
@ -137,7 +137,7 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
requestedProperties += PROP_SHAPE;
|
requestedProperties += PROP_SHAPE;
|
||||||
|
|
|
@ -99,7 +99,7 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
requestedProperties += PROP_TEXT;
|
requestedProperties += PROP_TEXT;
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||||
|
|
||||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
|
|
@ -84,7 +84,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
requestedProperties += PROP_SOURCE_URL;
|
requestedProperties += PROP_SOURCE_URL;
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||||
|
|
||||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
|
|
@ -133,7 +133,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||||
|
|
||||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
|
|
@ -21,11 +21,10 @@
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
|
||||||
#include "AssetUtils.h"
|
#include "AssetUtils.h"
|
||||||
|
#include "ClientServerUtils.h"
|
||||||
#include "LimitedNodeList.h"
|
#include "LimitedNodeList.h"
|
||||||
#include "NLPacket.h"
|
|
||||||
#include "Node.h"
|
#include "Node.h"
|
||||||
#include "ReceivedMessage.h"
|
#include "ReceivedMessage.h"
|
||||||
#include "ResourceCache.h"
|
|
||||||
|
|
||||||
class GetMappingRequest;
|
class GetMappingRequest;
|
||||||
class SetMappingRequest;
|
class SetMappingRequest;
|
||||||
|
@ -60,8 +59,6 @@ public:
|
||||||
Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
|
Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
|
||||||
Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data);
|
Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data);
|
||||||
|
|
||||||
static const MessageID INVALID_MESSAGE_ID = 0;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ void AssetRequest::start() {
|
||||||
_assetInfoRequestID = assetClient->getAssetInfo(_hash,
|
_assetInfoRequestID = assetClient->getAssetInfo(_hash,
|
||||||
[this](bool responseReceived, AssetServerError serverError, AssetInfo info) {
|
[this](bool responseReceived, AssetServerError serverError, AssetInfo info) {
|
||||||
|
|
||||||
_assetInfoRequestID = AssetClient::INVALID_MESSAGE_ID;
|
_assetInfoRequestID = INVALID_MESSAGE_ID;
|
||||||
|
|
||||||
_info = info;
|
_info = info;
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ void AssetRequest::start() {
|
||||||
// If the request is dead, return
|
// If the request is dead, return
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_assetRequestID = AssetClient::INVALID_MESSAGE_ID;
|
_assetRequestID = INVALID_MESSAGE_ID;
|
||||||
|
|
||||||
if (!responseReceived) {
|
if (!responseReceived) {
|
||||||
_error = NetworkError;
|
_error = NetworkError;
|
||||||
|
|
|
@ -64,8 +64,8 @@ private:
|
||||||
QString _hash;
|
QString _hash;
|
||||||
QByteArray _data;
|
QByteArray _data;
|
||||||
int _numPendingRequests { 0 };
|
int _numPendingRequests { 0 };
|
||||||
MessageID _assetRequestID { AssetClient::INVALID_MESSAGE_ID };
|
MessageID _assetRequestID { INVALID_MESSAGE_ID };
|
||||||
MessageID _assetInfoRequestID { AssetClient::INVALID_MESSAGE_ID };
|
MessageID _assetInfoRequestID { INVALID_MESSAGE_ID };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
|
|
||||||
using MessageID = uint32_t;
|
|
||||||
using DataOffset = int64_t;
|
using DataOffset = int64_t;
|
||||||
|
|
||||||
using AssetPath = QString;
|
using AssetPath = QString;
|
||||||
|
@ -64,4 +63,4 @@ bool isValidFilePath(const AssetPath& path);
|
||||||
bool isValidPath(const AssetPath& path);
|
bool isValidPath(const AssetPath& path);
|
||||||
bool isValidHash(const QString& hashString);
|
bool isValidHash(const QString& hashString);
|
||||||
|
|
||||||
#endif
|
#endif // hifi_AssetUtils_h
|
||||||
|
|
|
@ -35,6 +35,8 @@ Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) {
|
||||||
return Assignment::AssetServerType;
|
return Assignment::AssetServerType;
|
||||||
case NodeType::MessagesMixer:
|
case NodeType::MessagesMixer:
|
||||||
return Assignment::MessagesMixerType;
|
return Assignment::MessagesMixerType;
|
||||||
|
case NodeType::EntityScriptServer:
|
||||||
|
return Assignment::EntityScriptServerType;
|
||||||
default:
|
default:
|
||||||
return Assignment::AllTypes;
|
return Assignment::AllTypes;
|
||||||
}
|
}
|
||||||
|
@ -139,6 +141,8 @@ const char* Assignment::getTypeName() const {
|
||||||
return "entity-server";
|
return "entity-server";
|
||||||
case Assignment::MessagesMixerType:
|
case Assignment::MessagesMixerType:
|
||||||
return "messages-mixer";
|
return "messages-mixer";
|
||||||
|
case Assignment::EntityScriptServerType:
|
||||||
|
return "entity-script-server";
|
||||||
default:
|
default:
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ public:
|
||||||
AgentType = 2,
|
AgentType = 2,
|
||||||
AssetServerType = 3,
|
AssetServerType = 3,
|
||||||
MessagesMixerType = 4,
|
MessagesMixerType = 4,
|
||||||
UNUSED_1 = 5,
|
EntityScriptServerType = 5,
|
||||||
EntityServerType = 6,
|
EntityServerType = 6,
|
||||||
AllTypes = 7
|
AllTypes = 7
|
||||||
};
|
};
|
||||||
|
|
21
libraries/networking/src/ClientServerUtils.h
Normal file
21
libraries/networking/src/ClientServerUtils.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
//
|
||||||
|
// ClientServerUtils.h
|
||||||
|
// libraries/networking/src
|
||||||
|
//
|
||||||
|
// Created by Ryan Huffman on 2017/01/20
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_ClientServerUtils_h
|
||||||
|
#define hifi_ClientServerUtils_h
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
using MessageID = uint32_t;
|
||||||
|
const MessageID INVALID_MESSAGE_ID = 0;
|
||||||
|
|
||||||
|
#endif // hifi_ClientServerUtils_h
|
165
libraries/networking/src/EntityScriptClient.cpp
Normal file
165
libraries/networking/src/EntityScriptClient.cpp
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
#include "EntityScriptClient.h"
|
||||||
|
#include "NodeList.h"
|
||||||
|
#include "NetworkLogging.h"
|
||||||
|
#include "EntityScriptUtils.h"
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
MessageID EntityScriptClient::_currentID = 0;
|
||||||
|
|
||||||
|
GetScriptStatusRequest::GetScriptStatusRequest(QUuid entityID) : _entityID(entityID) {
|
||||||
|
}
|
||||||
|
|
||||||
|
GetScriptStatusRequest::~GetScriptStatusRequest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetScriptStatusRequest::start() {
|
||||||
|
auto client = DependencyManager::get<EntityScriptClient>();
|
||||||
|
client->getEntityServerScriptStatus(_entityID, [this](bool responseReceived, bool isRunning, EntityScriptStatus status, QString errorInfo) {
|
||||||
|
_responseReceived = responseReceived;
|
||||||
|
_isRunning = isRunning;
|
||||||
|
_status = status;
|
||||||
|
_errorInfo = errorInfo;
|
||||||
|
|
||||||
|
emit finished(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityScriptClient::EntityScriptClient() {
|
||||||
|
setCustomDeleter([](Dependency* dependency){
|
||||||
|
static_cast<EntityScriptClient*>(dependency)->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||||
|
|
||||||
|
packetReceiver.registerListener(PacketType::EntityScriptGetStatusReply, this, "handleGetScriptStatusReply");
|
||||||
|
|
||||||
|
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &EntityScriptClient::handleNodeKilled);
|
||||||
|
connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset,
|
||||||
|
this, &EntityScriptClient::handleNodeClientConnectionReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetScriptStatusRequest* EntityScriptClient::createScriptStatusRequest(QUuid entityID) {
|
||||||
|
auto request = new GetScriptStatusRequest(entityID);
|
||||||
|
|
||||||
|
request->moveToThread(thread());
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityScriptClient::reloadServerScript(QUuid entityID) {
|
||||||
|
// Send packet to entity script server
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::EntityScriptServer);
|
||||||
|
|
||||||
|
if (entityScriptServer) {
|
||||||
|
auto id = entityID.toRfc4122();
|
||||||
|
auto payloadSize = id.size();
|
||||||
|
auto packet = NLPacket::create(PacketType::ReloadEntityServerScript, payloadSize, true);
|
||||||
|
|
||||||
|
packet->write(id);
|
||||||
|
|
||||||
|
if (nodeList->sendPacket(std::move(packet), *entityScriptServer) != -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageID EntityScriptClient::getEntityServerScriptStatus(QUuid entityID, GetScriptStatusCallback callback) {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::EntityScriptServer);
|
||||||
|
|
||||||
|
if (entityScriptServer) {
|
||||||
|
auto packetList = NLPacketList::create(PacketType::EntityScriptGetStatus, QByteArray(), true, true);
|
||||||
|
|
||||||
|
auto messageID = ++_currentID;
|
||||||
|
packetList->writePrimitive(messageID);
|
||||||
|
|
||||||
|
packetList->write(entityID.toRfc4122());
|
||||||
|
|
||||||
|
if (nodeList->sendPacketList(std::move(packetList), *entityScriptServer) != -1) {
|
||||||
|
_pendingEntityScriptStatusRequests[entityScriptServer][messageID] = callback;
|
||||||
|
|
||||||
|
return messageID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(false, false, ERROR_LOADING_SCRIPT, "");
|
||||||
|
return INVALID_MESSAGE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptClient::handleGetScriptStatusReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
|
MessageID messageID;
|
||||||
|
bool isKnown { false };
|
||||||
|
EntityScriptStatus status = ERROR_LOADING_SCRIPT;
|
||||||
|
QString errorInfo { "" };
|
||||||
|
|
||||||
|
message->readPrimitive(&messageID);
|
||||||
|
message->readPrimitive(&isKnown);
|
||||||
|
|
||||||
|
if (isKnown) {
|
||||||
|
message->readPrimitive(&status);
|
||||||
|
errorInfo = message->readString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have any pending requests for this node
|
||||||
|
auto messageMapIt = _pendingEntityScriptStatusRequests.find(senderNode);
|
||||||
|
if (messageMapIt != _pendingEntityScriptStatusRequests.end()) {
|
||||||
|
|
||||||
|
// Found the node, get the MessageID -> Callback map
|
||||||
|
auto& messageCallbackMap = messageMapIt->second;
|
||||||
|
|
||||||
|
// Check if we have this pending request
|
||||||
|
auto requestIt = messageCallbackMap.find(messageID);
|
||||||
|
if (requestIt != messageCallbackMap.end()) {
|
||||||
|
auto callback = requestIt->second;
|
||||||
|
callback(true, isKnown, status, errorInfo);
|
||||||
|
messageCallbackMap.erase(requestIt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Although the messageCallbackMap may now be empty, we won't delete the node until we have disconnected from
|
||||||
|
// it to avoid constantly creating/deleting the map on subsequent requests.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptClient::handleNodeKilled(SharedNodePointer node) {
|
||||||
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
|
if (node->getType() != NodeType::EntityScriptServer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
forceFailureOfPendingRequests(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptClient::handleNodeClientConnectionReset(SharedNodePointer node) {
|
||||||
|
// a client connection to a Node was reset
|
||||||
|
// if it was an EntityScriptServer we need to cause anything pending to fail so it is re-attempted
|
||||||
|
|
||||||
|
if (node->getType() != NodeType::EntityScriptServer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//qCDebug(entity_script_client) << "EntityScriptClient detected client connection reset handshake with Asset Server - failing any pending requests";
|
||||||
|
|
||||||
|
forceFailureOfPendingRequests(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityScriptClient::forceFailureOfPendingRequests(SharedNodePointer node) {
|
||||||
|
|
||||||
|
{
|
||||||
|
auto messageMapIt = _pendingEntityScriptStatusRequests.find(node);
|
||||||
|
if (messageMapIt != _pendingEntityScriptStatusRequests.end()) {
|
||||||
|
for (const auto& value : messageMapIt->second) {
|
||||||
|
value.second(false, false, ERROR_LOADING_SCRIPT, "");
|
||||||
|
}
|
||||||
|
messageMapIt->second.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
libraries/networking/src/EntityScriptClient.h
Normal file
75
libraries/networking/src/EntityScriptClient.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
//
|
||||||
|
// EntityScriptClient.h
|
||||||
|
// libraries/networking/src
|
||||||
|
//
|
||||||
|
// Created by Ryan Huffman on 2017/01/13
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_EntityScriptClient_h
|
||||||
|
#define hifi_EntityScriptClient_h
|
||||||
|
|
||||||
|
#include "ClientServerUtils.h"
|
||||||
|
#include "LimitedNodeList.h"
|
||||||
|
#include "ReceivedMessage.h"
|
||||||
|
#include "AssetUtils.h"
|
||||||
|
#include "EntityScriptUtils.h"
|
||||||
|
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
using GetScriptStatusCallback = std::function<void(bool responseReceived, bool isRunning, EntityScriptStatus status, QString errorInfo)>;
|
||||||
|
|
||||||
|
class GetScriptStatusRequest : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
GetScriptStatusRequest(QUuid);
|
||||||
|
~GetScriptStatusRequest();
|
||||||
|
|
||||||
|
Q_INVOKABLE void start();
|
||||||
|
|
||||||
|
bool getResponseReceived() const { return _responseReceived; }
|
||||||
|
bool getIsRunning() const { return _isRunning; }
|
||||||
|
EntityScriptStatus getStatus() const { return _status; }
|
||||||
|
QString getErrorInfo() const { return _errorInfo; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished(GetScriptStatusRequest* request);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QUuid _entityID;
|
||||||
|
MessageID _messageID;
|
||||||
|
|
||||||
|
bool _responseReceived;
|
||||||
|
bool _isRunning;
|
||||||
|
EntityScriptStatus _status;
|
||||||
|
QString _errorInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EntityScriptClient : public QObject, public Dependency {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
EntityScriptClient();
|
||||||
|
|
||||||
|
Q_INVOKABLE GetScriptStatusRequest* createScriptStatusRequest(QUuid entityID);
|
||||||
|
|
||||||
|
bool reloadServerScript(QUuid entityID);
|
||||||
|
MessageID getEntityServerScriptStatus(QUuid entityID, GetScriptStatusCallback callback);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleNodeKilled(SharedNodePointer node);
|
||||||
|
void handleNodeClientConnectionReset(SharedNodePointer node);
|
||||||
|
|
||||||
|
void handleGetScriptStatusReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static MessageID _currentID;
|
||||||
|
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, GetScriptStatusCallback>> _pendingEntityScriptStatusRequests;
|
||||||
|
|
||||||
|
void forceFailureOfPendingRequests(SharedNodePointer node);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
21
libraries/networking/src/EntityScriptUtils.h
Normal file
21
libraries/networking/src/EntityScriptUtils.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// EntityScriptUtils.h
|
||||||
|
// libraries/networking/src
|
||||||
|
//
|
||||||
|
// Created by Ryan Huffman on 2017/01/13
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_EntityScriptUtils_h
|
||||||
|
#define hifi_EntityScriptUtils_h
|
||||||
|
|
||||||
|
enum EntityScriptStatus {
|
||||||
|
ERROR_LOADING_SCRIPT,
|
||||||
|
ERROR_RUNNING_SCRIPT,
|
||||||
|
RUNNING
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_EntityScriptUtils_h
|
|
@ -68,7 +68,7 @@ void GetMappingRequest::doStart() {
|
||||||
_mappingRequestID = assetClient->getAssetMapping(_path,
|
_mappingRequestID = assetClient->getAssetMapping(_path,
|
||||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||||
|
|
||||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
_mappingRequestID = INVALID_MESSAGE_ID;
|
||||||
if (!responseReceived) {
|
if (!responseReceived) {
|
||||||
_error = NetworkError;
|
_error = NetworkError;
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,7 +100,7 @@ void GetAllMappingsRequest::doStart() {
|
||||||
_mappingRequestID = assetClient->getAllAssetMappings(
|
_mappingRequestID = assetClient->getAllAssetMappings(
|
||||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||||
|
|
||||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
_mappingRequestID = INVALID_MESSAGE_ID;
|
||||||
|
|
||||||
if (!responseReceived) {
|
if (!responseReceived) {
|
||||||
_error = NetworkError;
|
_error = NetworkError;
|
||||||
|
@ -152,7 +152,7 @@ void SetMappingRequest::doStart() {
|
||||||
_mappingRequestID = assetClient->setAssetMapping(_path, _hash,
|
_mappingRequestID = assetClient->setAssetMapping(_path, _hash,
|
||||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||||
|
|
||||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
_mappingRequestID = INVALID_MESSAGE_ID;
|
||||||
if (!responseReceived) {
|
if (!responseReceived) {
|
||||||
_error = NetworkError;
|
_error = NetworkError;
|
||||||
} else {
|
} else {
|
||||||
|
@ -195,7 +195,7 @@ void DeleteMappingsRequest::doStart() {
|
||||||
_mappingRequestID = assetClient->deleteAssetMappings(_paths,
|
_mappingRequestID = assetClient->deleteAssetMappings(_paths,
|
||||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||||
|
|
||||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
_mappingRequestID = INVALID_MESSAGE_ID;
|
||||||
if (!responseReceived) {
|
if (!responseReceived) {
|
||||||
_error = NetworkError;
|
_error = NetworkError;
|
||||||
} else {
|
} else {
|
||||||
|
@ -237,7 +237,7 @@ void RenameMappingRequest::doStart() {
|
||||||
_mappingRequestID = assetClient->renameAssetMapping(_oldPath, _newPath,
|
_mappingRequestID = assetClient->renameAssetMapping(_oldPath, _newPath,
|
||||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||||
|
|
||||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
_mappingRequestID = INVALID_MESSAGE_ID;
|
||||||
if (!responseReceived) {
|
if (!responseReceived) {
|
||||||
_error = NetworkError;
|
_error = NetworkError;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -40,7 +40,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Error _error { NoError };
|
Error _error { NoError };
|
||||||
MessageID _mappingRequestID { AssetClient::INVALID_MESSAGE_ID };
|
MessageID _mappingRequestID { INVALID_MESSAGE_ID };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void doStart() = 0;
|
virtual void doStart() = 0;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
Q_DECLARE_LOGGING_CATEGORY(resourceLog)
|
Q_DECLARE_LOGGING_CATEGORY(resourceLog)
|
||||||
Q_DECLARE_LOGGING_CATEGORY(networking)
|
Q_DECLARE_LOGGING_CATEGORY(networking)
|
||||||
Q_DECLARE_LOGGING_CATEGORY(asset_client)
|
Q_DECLARE_LOGGING_CATEGORY(asset_client)
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(entity_script_client)
|
||||||
Q_DECLARE_LOGGING_CATEGORY(messages_client)
|
Q_DECLARE_LOGGING_CATEGORY(messages_client)
|
||||||
|
|
||||||
#endif // hifi_NetworkLogging_h
|
#endif // hifi_NetworkLogging_h
|
||||||
|
|
|
@ -41,6 +41,7 @@ void NodeType::init() {
|
||||||
TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer");
|
TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer");
|
||||||
TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer");
|
TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer");
|
||||||
TypeNameHash.insert(NodeType::AssetServer, "Asset Server");
|
TypeNameHash.insert(NodeType::AssetServer, "Asset Server");
|
||||||
|
TypeNameHash.insert(NodeType::EntityScriptServer, "Entity Script Server");
|
||||||
TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
|
TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace NodeType {
|
||||||
const NodeType_t AvatarMixer = 'W';
|
const NodeType_t AvatarMixer = 'W';
|
||||||
const NodeType_t AssetServer = 'A';
|
const NodeType_t AssetServer = 'A';
|
||||||
const NodeType_t MessagesMixer = 'm';
|
const NodeType_t MessagesMixer = 'm';
|
||||||
|
const NodeType_t EntityScriptServer = 'S';
|
||||||
const NodeType_t Unassigned = 1;
|
const NodeType_t Unassigned = 1;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
|
@ -48,7 +48,9 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::EntityAdd:
|
case PacketType::EntityAdd:
|
||||||
case PacketType::EntityEdit:
|
case PacketType::EntityEdit:
|
||||||
case PacketType::EntityData:
|
case PacketType::EntityData:
|
||||||
return VERSION_ENTITIES_LAST_EDITED_BY;
|
return VERSION_ENTITIES_SERVER_SCRIPTS;
|
||||||
|
case PacketType::EntityQuery:
|
||||||
|
return static_cast<PacketVersion>(EntityQueryPacketVersion::JsonFilter);
|
||||||
case PacketType::AvatarIdentity:
|
case PacketType::AvatarIdentity:
|
||||||
case PacketType::AvatarData:
|
case PacketType::AvatarData:
|
||||||
case PacketType::BulkAvatarData:
|
case PacketType::BulkAvatarData:
|
||||||
|
|
|
@ -107,7 +107,10 @@ public:
|
||||||
RequestsDomainListData,
|
RequestsDomainListData,
|
||||||
ExitingSpaceBubble,
|
ExitingSpaceBubble,
|
||||||
PerAvatarGainSet,
|
PerAvatarGainSet,
|
||||||
LAST_PACKET_TYPE = PerAvatarGainSet
|
EntityScriptGetStatus,
|
||||||
|
EntityScriptGetStatusReply,
|
||||||
|
ReloadEntityServerScript,
|
||||||
|
LAST_PACKET_TYPE = ReloadEntityServerScript
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -197,6 +200,11 @@ const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS = 62;
|
||||||
const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63;
|
const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63;
|
||||||
const PacketVersion VERSION_ENTITIES_ARROW_ACTION = 64;
|
const PacketVersion VERSION_ENTITIES_ARROW_ACTION = 64;
|
||||||
const PacketVersion VERSION_ENTITIES_LAST_EDITED_BY = 65;
|
const PacketVersion VERSION_ENTITIES_LAST_EDITED_BY = 65;
|
||||||
|
const PacketVersion VERSION_ENTITIES_SERVER_SCRIPTS = 66;
|
||||||
|
|
||||||
|
enum class EntityQueryPacketVersion: PacketVersion {
|
||||||
|
JsonFilter = 18
|
||||||
|
};
|
||||||
|
|
||||||
enum class AssetServerPacketVersion: PacketVersion {
|
enum class AssetServerPacketVersion: PacketVersion {
|
||||||
VegasCongestionControl = 19
|
VegasCongestionControl = 19
|
||||||
|
|
|
@ -899,7 +899,7 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're at a element that is out of view, then we can return, because no nodes below us will be in view!
|
// If we're at a element that is out of view, then we can return, because no nodes below us will be in view!
|
||||||
if (!params.recurseEverything && !element->isInView(params.viewFrustum)) {
|
if (params.usesFrustum && !params.recurseEverything && !element->isInView(params.viewFrustum)) {
|
||||||
params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
|
params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
|
||||||
return bytesWritten;
|
return bytesWritten;
|
||||||
}
|
}
|
||||||
|
@ -1015,7 +1015,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewFrustum::intersection nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside
|
ViewFrustum::intersection nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside
|
||||||
if (!params.recurseEverything) {
|
if (params.usesFrustum && !params.recurseEverything) {
|
||||||
float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
|
float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
|
||||||
params.octreeElementSizeScale);
|
params.octreeElementSizeScale);
|
||||||
|
|
||||||
|
@ -1077,7 +1077,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
||||||
// If we were previously in the view, then we normally will return out of here and stop recursing. But
|
// If we were previously in the view, then we normally will return out of here and stop recursing. But
|
||||||
// if we're in deltaView mode, and this element has changed since it was last sent, then we do
|
// if we're in deltaView mode, and this element has changed since it was last sent, then we do
|
||||||
// need to send it.
|
// need to send it.
|
||||||
if (wasInView && !(params.deltaView && element->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE))) {
|
if (wasInView && !(params.deltaView && element->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE))) {
|
||||||
if (params.stats) {
|
if (params.stats) {
|
||||||
params.stats->skippedWasInView(element);
|
params.stats->skippedWasInView(element);
|
||||||
}
|
}
|
||||||
|
@ -1088,7 +1088,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
||||||
// If we're not in delta sending mode, and we weren't asked to do a force send, and the voxel hasn't changed,
|
// If we're not in delta sending mode, and we weren't asked to do a force send, and the voxel hasn't changed,
|
||||||
// then we can also bail early and save bits
|
// then we can also bail early and save bits
|
||||||
if (!params.forceSendScene && !params.deltaView &&
|
if (!params.forceSendScene && !params.deltaView &&
|
||||||
!element->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE)) {
|
!element->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE)) {
|
||||||
if (params.stats) {
|
if (params.stats) {
|
||||||
params.stats->skippedNoChange(element);
|
params.stats->skippedNoChange(element);
|
||||||
}
|
}
|
||||||
|
@ -1176,7 +1176,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
||||||
int originalIndex = indexOfChildren[i];
|
int originalIndex = indexOfChildren[i];
|
||||||
|
|
||||||
bool childIsInView = (childElement &&
|
bool childIsInView = (childElement &&
|
||||||
(params.recurseEverything ||
|
(params.recurseEverything || !params.usesFrustum ||
|
||||||
(nodeLocationThisView == ViewFrustum::INSIDE) || // parent was fully in view, we can assume ALL children are
|
(nodeLocationThisView == ViewFrustum::INSIDE) || // parent was fully in view, we can assume ALL children are
|
||||||
(nodeLocationThisView == ViewFrustum::INTERSECT &&
|
(nodeLocationThisView == ViewFrustum::INTERSECT &&
|
||||||
childElement->isInView(params.viewFrustum)) // the parent intersects and the child is in view
|
childElement->isInView(params.viewFrustum)) // the parent intersects and the child is in view
|
||||||
|
@ -1189,7 +1189,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Before we consider this further, let's see if it's in our LOD scope...
|
// Before we consider this further, let's see if it's in our LOD scope...
|
||||||
float boundaryDistance = params.recurseEverything ? 1 :
|
float boundaryDistance = params.recurseEverything || !params.usesFrustum ? 1 :
|
||||||
boundaryDistanceForRenderLevel(childElement->getLevel() + params.boundaryLevelAdjust,
|
boundaryDistanceForRenderLevel(childElement->getLevel() + params.boundaryLevelAdjust,
|
||||||
params.octreeElementSizeScale);
|
params.octreeElementSizeScale);
|
||||||
|
|
||||||
|
@ -1211,7 +1211,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
||||||
|
|
||||||
bool childIsOccluded = false; // assume it's not occluded
|
bool childIsOccluded = false; // assume it's not occluded
|
||||||
|
|
||||||
bool shouldRender = params.recurseEverything ||
|
bool shouldRender = params.recurseEverything || !params.usesFrustum ||
|
||||||
childElement->calculateShouldRender(params.viewFrustum,
|
childElement->calculateShouldRender(params.viewFrustum,
|
||||||
params.octreeElementSizeScale, params.boundaryLevelAdjust);
|
params.octreeElementSizeScale, params.boundaryLevelAdjust);
|
||||||
|
|
||||||
|
@ -1247,7 +1247,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
||||||
// need to send it.
|
// need to send it.
|
||||||
if (!childWasInView ||
|
if (!childWasInView ||
|
||||||
(params.deltaView &&
|
(params.deltaView &&
|
||||||
childElement->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE))){
|
childElement->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE))){
|
||||||
|
|
||||||
childrenDataBits += (1 << (7 - originalIndex));
|
childrenDataBits += (1 << (7 - originalIndex));
|
||||||
inViewWithColorCount++;
|
inViewWithColorCount++;
|
||||||
|
@ -1451,7 +1451,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
||||||
// called databits), then we wouldn't send the children. So those types of Octree's should tell us to keep
|
// called databits), then we wouldn't send the children. So those types of Octree's should tell us to keep
|
||||||
// recursing, by returning TRUE in recurseChildrenWithData().
|
// recursing, by returning TRUE in recurseChildrenWithData().
|
||||||
|
|
||||||
if (params.recurseEverything || recurseChildrenWithData() || !oneAtBit(childrenDataBits, originalIndex)) {
|
if (params.recurseEverything || !params.usesFrustum
|
||||||
|
|| recurseChildrenWithData() || !oneAtBit(childrenDataBits, originalIndex)) {
|
||||||
|
|
||||||
// Allow the datatype a chance to determine if it really wants to recurse this tree. Usually this
|
// Allow the datatype a chance to determine if it really wants to recurse this tree. Usually this
|
||||||
// will be true. But if the tree has already been encoded, we will skip this.
|
// will be true. But if the tree has already been encoded, we will skip this.
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QtCore/QJsonObject>
|
||||||
|
|
||||||
#include <shared/ReadWriteLockable.h>
|
#include <shared/ReadWriteLockable.h>
|
||||||
#include <SimpleMovingAverage.h>
|
#include <SimpleMovingAverage.h>
|
||||||
|
@ -68,7 +69,7 @@ class EncodeBitstreamParams {
|
||||||
public:
|
public:
|
||||||
ViewFrustum viewFrustum;
|
ViewFrustum viewFrustum;
|
||||||
ViewFrustum lastViewFrustum;
|
ViewFrustum lastViewFrustum;
|
||||||
quint64 lastViewFrustumSent;
|
quint64 lastQuerySent;
|
||||||
int maxEncodeLevel;
|
int maxEncodeLevel;
|
||||||
int maxLevelReached;
|
int maxLevelReached;
|
||||||
bool includeExistsBits;
|
bool includeExistsBits;
|
||||||
|
@ -81,6 +82,8 @@ public:
|
||||||
OctreeSceneStats* stats;
|
OctreeSceneStats* stats;
|
||||||
JurisdictionMap* jurisdictionMap;
|
JurisdictionMap* jurisdictionMap;
|
||||||
OctreeElementExtraEncodeData* extraEncodeData;
|
OctreeElementExtraEncodeData* extraEncodeData;
|
||||||
|
bool usesFrustum;
|
||||||
|
NodeData* nodeData;
|
||||||
|
|
||||||
// output hints from the encode process
|
// output hints from the encode process
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -104,12 +107,14 @@ public:
|
||||||
bool useDeltaView = false,
|
bool useDeltaView = false,
|
||||||
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
|
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
|
||||||
float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
|
float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
|
||||||
quint64 lastViewFrustumSent = IGNORE_LAST_SENT,
|
quint64 lastQuerySent = IGNORE_LAST_SENT,
|
||||||
bool forceSendScene = true,
|
bool forceSendScene = true,
|
||||||
OctreeSceneStats* stats = IGNORE_SCENE_STATS,
|
OctreeSceneStats* stats = IGNORE_SCENE_STATS,
|
||||||
JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP,
|
JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP,
|
||||||
OctreeElementExtraEncodeData* extraEncodeData = NULL) :
|
OctreeElementExtraEncodeData* extraEncodeData = nullptr,
|
||||||
lastViewFrustumSent(lastViewFrustumSent),
|
bool usesFrustum = true,
|
||||||
|
NodeData* nodeData = nullptr) :
|
||||||
|
lastQuerySent(lastQuerySent),
|
||||||
maxEncodeLevel(maxEncodeLevel),
|
maxEncodeLevel(maxEncodeLevel),
|
||||||
maxLevelReached(0),
|
maxLevelReached(0),
|
||||||
includeExistsBits(includeExistsBits),
|
includeExistsBits(includeExistsBits),
|
||||||
|
@ -121,6 +126,8 @@ public:
|
||||||
stats(stats),
|
stats(stats),
|
||||||
jurisdictionMap(jurisdictionMap),
|
jurisdictionMap(jurisdictionMap),
|
||||||
extraEncodeData(extraEncodeData),
|
extraEncodeData(extraEncodeData),
|
||||||
|
usesFrustum(usesFrustum),
|
||||||
|
nodeData(nodeData),
|
||||||
stopReason(UNKNOWN)
|
stopReason(UNKNOWN)
|
||||||
{
|
{
|
||||||
lastViewFrustum.invalidate();
|
lastViewFrustum.invalidate();
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#ifndef hifi_OctreeConstants_h
|
#ifndef hifi_OctreeConstants_h
|
||||||
#define hifi_OctreeConstants_h
|
#define hifi_OctreeConstants_h
|
||||||
|
|
||||||
#include <QtGlobal> // for quint64
|
#include <QtCore/QString> // for quint64/QString
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
const quint64 CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels
|
const quint64 CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels
|
||||||
|
|
|
@ -108,7 +108,7 @@ public:
|
||||||
virtual bool isRendered() const { return getShouldRender(); }
|
virtual bool isRendered() const { return getShouldRender(); }
|
||||||
|
|
||||||
virtual bool deleteApproved() const { return true; }
|
virtual bool deleteApproved() const { return true; }
|
||||||
|
|
||||||
virtual bool canRayIntersect() const { return isLeaf(); }
|
virtual bool canRayIntersect() const { return isLeaf(); }
|
||||||
/// \param center center of sphere in meters
|
/// \param center center of sphere in meters
|
||||||
/// \param radius radius of sphere in meters
|
/// \param radius radius of sphere in meters
|
||||||
|
|
|
@ -36,6 +36,8 @@ public:
|
||||||
virtual void render(RenderArgs* renderArgs) override { /* swallow these */ }
|
virtual void render(RenderArgs* renderArgs) override { /* swallow these */ }
|
||||||
|
|
||||||
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
|
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
|
||||||
|
|
||||||
|
OctreeQuery& getOctreeQuery() { return _octreeQuery; }
|
||||||
|
|
||||||
static int parseOctreeStats(QSharedPointer<ReceivedMessage> message, SharedNodePointer sourceNode);
|
static int parseOctreeStats(QSharedPointer<ReceivedMessage> message, SharedNodePointer sourceNode);
|
||||||
static void trackIncomingOctreePacket(const QByteArray& packet, const SharedNodePointer& sendingNode, bool wasStatsPacket);
|
static void trackIncomingOctreePacket(const QByteArray& packet, const SharedNodePointer& sendingNode, bool wasStatsPacket);
|
||||||
|
|
|
@ -9,13 +9,14 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <QtCore/QJsonDocument>
|
||||||
|
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
|
|
||||||
#include "OctreeConstants.h"
|
#include "OctreeConstants.h"
|
||||||
#include "OctreeQuery.h"
|
#include "OctreeQuery.h"
|
||||||
|
|
||||||
|
|
||||||
OctreeQuery::OctreeQuery() {
|
OctreeQuery::OctreeQuery() {
|
||||||
_maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
|
_maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
|
||||||
}
|
}
|
||||||
|
@ -23,35 +24,27 @@ OctreeQuery::OctreeQuery() {
|
||||||
int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
|
int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
|
||||||
unsigned char* bufferStart = destinationBuffer;
|
unsigned char* bufferStart = destinationBuffer;
|
||||||
|
|
||||||
// TODO: DRY this up to a shared method
|
// back a boolean (cut to 1 byte) to designate if this query uses the sent view frustum
|
||||||
// that can pack any type given the number of bytes
|
memcpy(destinationBuffer, &_usesFrustum, sizeof(_usesFrustum));
|
||||||
// and return the number of bytes to push the pointer
|
destinationBuffer += sizeof(_usesFrustum);
|
||||||
|
|
||||||
|
if (_usesFrustum) {
|
||||||
|
// TODO: DRY this up to a shared method
|
||||||
|
// that can pack any type given the number of bytes
|
||||||
|
// and return the number of bytes to push the pointer
|
||||||
|
|
||||||
|
// camera details
|
||||||
|
memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
|
||||||
|
destinationBuffer += sizeof(_cameraPosition);
|
||||||
|
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _cameraOrientation);
|
||||||
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _cameraFov);
|
||||||
|
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _cameraAspectRatio);
|
||||||
|
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraNearClip);
|
||||||
|
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraFarClip);
|
||||||
|
memcpy(destinationBuffer, &_cameraEyeOffsetPosition, sizeof(_cameraEyeOffsetPosition));
|
||||||
|
destinationBuffer += sizeof(_cameraEyeOffsetPosition);
|
||||||
|
}
|
||||||
|
|
||||||
// camera details
|
|
||||||
memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
|
|
||||||
destinationBuffer += sizeof(_cameraPosition);
|
|
||||||
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _cameraOrientation);
|
|
||||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _cameraFov);
|
|
||||||
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _cameraAspectRatio);
|
|
||||||
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraNearClip);
|
|
||||||
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraFarClip);
|
|
||||||
memcpy(destinationBuffer, &_cameraEyeOffsetPosition, sizeof(_cameraEyeOffsetPosition));
|
|
||||||
destinationBuffer += sizeof(_cameraEyeOffsetPosition);
|
|
||||||
|
|
||||||
// bitMask of less than byte wide items
|
|
||||||
unsigned char bitItems = 0;
|
|
||||||
|
|
||||||
// NOTE: we need to keep these here for new clients to talk to old servers. After we know that the clients and
|
|
||||||
// servers and clients have all been updated we could remove these bits. New servers will always force these
|
|
||||||
// features on old clients even if they don't ask for them. (which old clients will properly handle). New clients
|
|
||||||
// will always ask for these so that old servers will use these features.
|
|
||||||
setAtBit(bitItems, WANT_LOW_RES_MOVING_BIT);
|
|
||||||
setAtBit(bitItems, WANT_COLOR_AT_BIT);
|
|
||||||
setAtBit(bitItems, WANT_DELTA_AT_BIT);
|
|
||||||
setAtBit(bitItems, WANT_COMPRESSION);
|
|
||||||
|
|
||||||
*destinationBuffer++ = bitItems;
|
|
||||||
|
|
||||||
// desired Max Octree PPS
|
// desired Max Octree PPS
|
||||||
memcpy(destinationBuffer, &_maxQueryPPS, sizeof(_maxQueryPPS));
|
memcpy(destinationBuffer, &_maxQueryPPS, sizeof(_maxQueryPPS));
|
||||||
destinationBuffer += sizeof(_maxQueryPPS);
|
destinationBuffer += sizeof(_maxQueryPPS);
|
||||||
|
@ -67,6 +60,25 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
|
||||||
memcpy(destinationBuffer, &_cameraCenterRadius, sizeof(_cameraCenterRadius));
|
memcpy(destinationBuffer, &_cameraCenterRadius, sizeof(_cameraCenterRadius));
|
||||||
destinationBuffer += sizeof(_cameraCenterRadius);
|
destinationBuffer += sizeof(_cameraCenterRadius);
|
||||||
|
|
||||||
|
// create a QByteArray that holds the binary representation of the JSON parameters
|
||||||
|
QByteArray binaryParametersDocument;
|
||||||
|
|
||||||
|
if (!_jsonParameters.isEmpty()) {
|
||||||
|
binaryParametersDocument = QJsonDocument(_jsonParameters).toBinaryData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the size of the JSON parameters
|
||||||
|
uint16_t binaryParametersBytes = binaryParametersDocument.size();
|
||||||
|
memcpy(destinationBuffer, &binaryParametersBytes, sizeof(binaryParametersBytes));
|
||||||
|
destinationBuffer += sizeof(binaryParametersBytes);
|
||||||
|
|
||||||
|
// pack the binary JSON parameters
|
||||||
|
// NOTE: for now we assume that the filters that will be set are all small enough that we will not have a packet > MTU
|
||||||
|
if (binaryParametersDocument.size() > 0) {
|
||||||
|
memcpy(destinationBuffer, binaryParametersDocument.data(), binaryParametersBytes);
|
||||||
|
destinationBuffer += binaryParametersBytes;
|
||||||
|
}
|
||||||
|
|
||||||
return destinationBuffer - bufferStart;
|
return destinationBuffer - bufferStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,24 +88,22 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
|
||||||
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(message.getRawMessage());
|
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(message.getRawMessage());
|
||||||
const unsigned char* sourceBuffer = startPosition;
|
const unsigned char* sourceBuffer = startPosition;
|
||||||
|
|
||||||
// camera details
|
// check if this query uses a view frustum
|
||||||
memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
|
memcpy(&_usesFrustum, sourceBuffer, sizeof(_usesFrustum));
|
||||||
sourceBuffer += sizeof(_cameraPosition);
|
sourceBuffer += sizeof(_usesFrustum);
|
||||||
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _cameraOrientation);
|
|
||||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_cameraFov);
|
if (_usesFrustum) {
|
||||||
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer,_cameraAspectRatio);
|
// unpack camera details
|
||||||
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraNearClip);
|
memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
|
||||||
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraFarClip);
|
sourceBuffer += sizeof(_cameraPosition);
|
||||||
memcpy(&_cameraEyeOffsetPosition, sourceBuffer, sizeof(_cameraEyeOffsetPosition));
|
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _cameraOrientation);
|
||||||
sourceBuffer += sizeof(_cameraEyeOffsetPosition);
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_cameraFov);
|
||||||
|
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer,_cameraAspectRatio);
|
||||||
// optional feature flags
|
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraNearClip);
|
||||||
unsigned char bitItems = 0;
|
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraFarClip);
|
||||||
bitItems = (unsigned char)*sourceBuffer++;
|
memcpy(&_cameraEyeOffsetPosition, sourceBuffer, sizeof(_cameraEyeOffsetPosition));
|
||||||
|
sourceBuffer += sizeof(_cameraEyeOffsetPosition);
|
||||||
// NOTE: we used to use these bits to set feature request items if we need to extend the protocol with optional features
|
}
|
||||||
// do it here with... wantFeature= oneAtBit(bitItems, WANT_FEATURE_BIT);
|
|
||||||
Q_UNUSED(bitItems);
|
|
||||||
|
|
||||||
// desired Max Octree PPS
|
// desired Max Octree PPS
|
||||||
memcpy(&_maxQueryPPS, sourceBuffer, sizeof(_maxQueryPPS));
|
memcpy(&_maxQueryPPS, sourceBuffer, sizeof(_maxQueryPPS));
|
||||||
|
@ -106,13 +116,28 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
|
||||||
// desired boundaryLevelAdjust
|
// desired boundaryLevelAdjust
|
||||||
memcpy(&_boundaryLevelAdjust, sourceBuffer, sizeof(_boundaryLevelAdjust));
|
memcpy(&_boundaryLevelAdjust, sourceBuffer, sizeof(_boundaryLevelAdjust));
|
||||||
sourceBuffer += sizeof(_boundaryLevelAdjust);
|
sourceBuffer += sizeof(_boundaryLevelAdjust);
|
||||||
|
|
||||||
auto bytesRead = sourceBuffer - startPosition;
|
memcpy(&_cameraCenterRadius, sourceBuffer, sizeof(_cameraCenterRadius));
|
||||||
auto bytesLeft = message.getSize() - bytesRead;
|
sourceBuffer += sizeof(_cameraCenterRadius);
|
||||||
if (bytesLeft >= (int)sizeof(_cameraCenterRadius)) {
|
|
||||||
memcpy(&_cameraCenterRadius, sourceBuffer, sizeof(_cameraCenterRadius));
|
// check if we have a packed JSON filter
|
||||||
sourceBuffer += sizeof(_cameraCenterRadius);
|
uint16_t binaryParametersBytes;
|
||||||
|
memcpy(&binaryParametersBytes, sourceBuffer, sizeof(binaryParametersBytes));
|
||||||
|
sourceBuffer += sizeof(binaryParametersBytes);
|
||||||
|
|
||||||
|
if (binaryParametersBytes > 0) {
|
||||||
|
// unpack the binary JSON parameters
|
||||||
|
QByteArray binaryJSONParameters { binaryParametersBytes, 0 };
|
||||||
|
memcpy(binaryJSONParameters.data(), sourceBuffer, binaryParametersBytes);
|
||||||
|
sourceBuffer += binaryParametersBytes;
|
||||||
|
|
||||||
|
// grab the parameter object from the packed binary representation of JSON
|
||||||
|
auto newJsonDocument = QJsonDocument::fromBinaryData(binaryJSONParameters);
|
||||||
|
|
||||||
|
QWriteLocker jsonParameterLocker { &_jsonParametersLock };
|
||||||
|
_jsonParameters = newJsonDocument.object();
|
||||||
}
|
}
|
||||||
|
|
||||||
return sourceBuffer - startPosition;
|
return sourceBuffer - startPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,14 +31,11 @@ typedef unsigned long long quint64;
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <QtCore/QJsonObject>
|
||||||
|
#include <QtCore/QReadWriteLock>
|
||||||
|
|
||||||
#include <NodeData.h>
|
#include <NodeData.h>
|
||||||
|
|
||||||
// First bitset
|
|
||||||
const int WANT_LOW_RES_MOVING_BIT = 0;
|
|
||||||
const int WANT_COLOR_AT_BIT = 1;
|
|
||||||
const int WANT_DELTA_AT_BIT = 2;
|
|
||||||
const int UNUSED_BIT_3 = 3; // unused... available for new feature
|
|
||||||
const int WANT_COMPRESSION = 4; // 5th bit
|
|
||||||
|
|
||||||
class OctreeQuery : public NodeData {
|
class OctreeQuery : public NodeData {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -71,11 +68,19 @@ public:
|
||||||
void setCameraFarClip(float farClip) { _cameraFarClip = farClip; }
|
void setCameraFarClip(float farClip) { _cameraFarClip = farClip; }
|
||||||
void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; }
|
void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; }
|
||||||
void setCameraCenterRadius(float radius) { _cameraCenterRadius = radius; }
|
void setCameraCenterRadius(float radius) { _cameraCenterRadius = radius; }
|
||||||
|
|
||||||
|
// getters/setters for JSON filter
|
||||||
|
QJsonObject getJSONParameters() { QReadLocker locker { &_jsonParametersLock }; return _jsonParameters; }
|
||||||
|
void setJSONParameters(const QJsonObject& jsonParameters)
|
||||||
|
{ QWriteLocker locker { &_jsonParametersLock }; _jsonParameters = jsonParameters; }
|
||||||
|
|
||||||
// related to Octree Sending strategies
|
// related to Octree Sending strategies
|
||||||
int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; }
|
int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; }
|
||||||
float getOctreeSizeScale() const { return _octreeElementSizeScale; }
|
float getOctreeSizeScale() const { return _octreeElementSizeScale; }
|
||||||
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||||
|
|
||||||
|
bool getUsesFrustum() { return _usesFrustum; }
|
||||||
|
void setUsesFrustum(bool usesFrustum) { _usesFrustum = usesFrustum; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setMaxQueryPacketsPerSecond(int maxQueryPPS) { _maxQueryPPS = maxQueryPPS; }
|
void setMaxQueryPacketsPerSecond(int maxQueryPPS) { _maxQueryPPS = maxQueryPPS; }
|
||||||
|
@ -97,7 +102,12 @@ protected:
|
||||||
int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
|
int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
|
||||||
float _octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE; /// used for LOD calculations
|
float _octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE; /// used for LOD calculations
|
||||||
int _boundaryLevelAdjust = 0; /// used for LOD calculations
|
int _boundaryLevelAdjust = 0; /// used for LOD calculations
|
||||||
|
|
||||||
|
uint8_t _usesFrustum = true;
|
||||||
|
|
||||||
|
QJsonObject _jsonParameters;
|
||||||
|
QReadWriteLock _jsonParametersLock;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// privatize the copy constructor and assignment operator so they cannot be called
|
// privatize the copy constructor and assignment operator so they cannot be called
|
||||||
OctreeQuery(const OctreeQuery&);
|
OctreeQuery(const OctreeQuery&);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// OctreeQueryNode.cpp
|
// OctreeQueryNode.cpp
|
||||||
// assignment-client/src/octree
|
// libraries/octree/src
|
||||||
//
|
//
|
||||||
// Created by Stephen Birarda on 3/21/13.
|
// Created by Stephen Birarda on 3/21/13.
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
@ -18,7 +18,6 @@
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
|
||||||
#include "OctreeSendThread.h"
|
|
||||||
|
|
||||||
void OctreeQueryNode::nodeKilled() {
|
void OctreeQueryNode::nodeKilled() {
|
||||||
_isShuttingDown = true;
|
_isShuttingDown = true;
|
||||||
|
@ -156,65 +155,71 @@ bool OctreeQueryNode::updateCurrentViewFrustum() {
|
||||||
if (_isShuttingDown) {
|
if (_isShuttingDown) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool currentViewFrustumChanged = false;
|
if (!_usesFrustum) {
|
||||||
ViewFrustum newestViewFrustum;
|
// this client does not use a view frustum so the view frustum for this query has not changed
|
||||||
// get position and orientation details from the camera
|
return false;
|
||||||
newestViewFrustum.setPosition(getCameraPosition());
|
|
||||||
newestViewFrustum.setOrientation(getCameraOrientation());
|
|
||||||
|
|
||||||
newestViewFrustum.setCenterRadius(getCameraCenterRadius());
|
|
||||||
|
|
||||||
// Also make sure it's got the correct lens details from the camera
|
|
||||||
float originalFOV = getCameraFov();
|
|
||||||
float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
|
|
||||||
|
|
||||||
if (0.0f != getCameraAspectRatio() &&
|
|
||||||
0.0f != getCameraNearClip() &&
|
|
||||||
0.0f != getCameraFarClip() &&
|
|
||||||
getCameraNearClip() != getCameraFarClip()) {
|
|
||||||
newestViewFrustum.setProjection(glm::perspective(
|
|
||||||
glm::radians(wideFOV), // hack
|
|
||||||
getCameraAspectRatio(),
|
|
||||||
getCameraNearClip(),
|
|
||||||
getCameraFarClip()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{ // if there has been a change, then recalculate
|
|
||||||
QMutexLocker viewLocker(&_viewMutex);
|
|
||||||
if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
|
|
||||||
_currentViewFrustum = newestViewFrustum;
|
|
||||||
_currentViewFrustum.calculate();
|
|
||||||
currentViewFrustumChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also check for LOD changes from the client
|
|
||||||
if (_lodInitialized) {
|
|
||||||
if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
|
|
||||||
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
|
|
||||||
_lodChanged = true;
|
|
||||||
}
|
|
||||||
if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
|
|
||||||
_lastClientOctreeSizeScale = getOctreeSizeScale();
|
|
||||||
_lodChanged = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
_lodInitialized = true;
|
bool currentViewFrustumChanged = false;
|
||||||
_lastClientOctreeSizeScale = getOctreeSizeScale();
|
|
||||||
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
|
ViewFrustum newestViewFrustum;
|
||||||
_lodChanged = false;
|
// get position and orientation details from the camera
|
||||||
|
newestViewFrustum.setPosition(getCameraPosition());
|
||||||
|
newestViewFrustum.setOrientation(getCameraOrientation());
|
||||||
|
|
||||||
|
newestViewFrustum.setCenterRadius(getCameraCenterRadius());
|
||||||
|
|
||||||
|
// Also make sure it's got the correct lens details from the camera
|
||||||
|
float originalFOV = getCameraFov();
|
||||||
|
float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
|
||||||
|
|
||||||
|
if (0.0f != getCameraAspectRatio() &&
|
||||||
|
0.0f != getCameraNearClip() &&
|
||||||
|
0.0f != getCameraFarClip() &&
|
||||||
|
getCameraNearClip() != getCameraFarClip()) {
|
||||||
|
newestViewFrustum.setProjection(glm::perspective(
|
||||||
|
glm::radians(wideFOV), // hack
|
||||||
|
getCameraAspectRatio(),
|
||||||
|
getCameraNearClip(),
|
||||||
|
getCameraFarClip()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{ // if there has been a change, then recalculate
|
||||||
|
QMutexLocker viewLocker(&_viewMutex);
|
||||||
|
if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
|
||||||
|
_currentViewFrustum = newestViewFrustum;
|
||||||
|
_currentViewFrustum.calculate();
|
||||||
|
currentViewFrustumChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check for LOD changes from the client
|
||||||
|
if (_lodInitialized) {
|
||||||
|
if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
|
||||||
|
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
|
||||||
|
_lodChanged = true;
|
||||||
|
}
|
||||||
|
if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
|
||||||
|
_lastClientOctreeSizeScale = getOctreeSizeScale();
|
||||||
|
_lodChanged = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_lodInitialized = true;
|
||||||
|
_lastClientOctreeSizeScale = getOctreeSizeScale();
|
||||||
|
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
|
||||||
|
_lodChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When we first detect that the view stopped changing, we record this.
|
||||||
|
// but we don't change it back to false until we've completely sent this
|
||||||
|
// scene.
|
||||||
|
if (_viewFrustumChanging && !currentViewFrustumChanged) {
|
||||||
|
_viewFrustumJustStoppedChanging = true;
|
||||||
|
}
|
||||||
|
_viewFrustumChanging = currentViewFrustumChanged;
|
||||||
|
return currentViewFrustumChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we first detect that the view stopped changing, we record this.
|
|
||||||
// but we don't change it back to false until we've completely sent this
|
|
||||||
// scene.
|
|
||||||
if (_viewFrustumChanging && !currentViewFrustumChanged) {
|
|
||||||
_viewFrustumJustStoppedChanging = true;
|
|
||||||
}
|
|
||||||
_viewFrustumChanging = currentViewFrustumChanged;
|
|
||||||
return currentViewFrustumChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeQueryNode::setViewSent(bool viewSent) {
|
void OctreeQueryNode::setViewSent(bool viewSent) {
|
||||||
|
@ -315,3 +320,15 @@ void OctreeQueryNode::parseNackPacket(ReceivedMessage& message) {
|
||||||
_nackedSequenceNumbers.enqueue(sequenceNumber);
|
_nackedSequenceNumbers.enqueue(sequenceNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OctreeQueryNode::haveJSONParametersChanged() {
|
||||||
|
bool parametersChanged = false;
|
||||||
|
auto currentParameters = getJSONParameters();
|
||||||
|
|
||||||
|
if (_lastCheckJSONParameters != currentParameters) {
|
||||||
|
parametersChanged = true;
|
||||||
|
_lastCheckJSONParameters = currentParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parametersChanged;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// OctreeQueryNode.h
|
// OctreeQueryNode.h
|
||||||
// assignment-client/src/octree
|
// libraries/octree/src
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 12/4/13.
|
// Created by Brad Hefta-Gaub on 12/4/13.
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
@ -15,11 +15,11 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <NodeData.h>
|
#include <NodeData.h>
|
||||||
#include <OctreeConstants.h>
|
#include "OctreeConstants.h"
|
||||||
#include <OctreeElementBag.h>
|
#include "OctreeElementBag.h"
|
||||||
#include <OctreePacketData.h>
|
#include "OctreePacketData.h"
|
||||||
#include <OctreeQuery.h>
|
#include "OctreeQuery.h"
|
||||||
#include <OctreeSceneStats.h>
|
#include "OctreeSceneStats.h"
|
||||||
#include "SentPacketHistory.h"
|
#include "SentPacketHistory.h"
|
||||||
#include <qqueue.h>
|
#include <qqueue.h>
|
||||||
|
|
||||||
|
@ -100,6 +100,9 @@ public:
|
||||||
bool hasNextNackedPacket() const;
|
bool hasNextNackedPacket() const;
|
||||||
const NLPacket* getNextNackedPacket();
|
const NLPacket* getNextNackedPacket();
|
||||||
|
|
||||||
|
// call only from OctreeSendThread for the given node
|
||||||
|
bool haveJSONParametersChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OctreeQueryNode(const OctreeQueryNode &);
|
OctreeQueryNode(const OctreeQueryNode &);
|
||||||
OctreeQueryNode& operator= (const OctreeQueryNode&);
|
OctreeQueryNode& operator= (const OctreeQueryNode&);
|
||||||
|
@ -143,6 +146,8 @@ private:
|
||||||
quint64 _sceneSendStartTime = 0;
|
quint64 _sceneSendStartTime = 0;
|
||||||
|
|
||||||
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
|
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
|
||||||
|
|
||||||
|
QJsonObject _lastCheckJSONParameters;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_OctreeQueryNode_h
|
#endif // hifi_OctreeQueryNode_h
|
|
@ -27,7 +27,7 @@ class PhysicalEntitySimulation;
|
||||||
using PhysicalEntitySimulationPointer = std::shared_ptr<PhysicalEntitySimulation>;
|
using PhysicalEntitySimulationPointer = std::shared_ptr<PhysicalEntitySimulation>;
|
||||||
using SetOfEntityMotionStates = QSet<EntityMotionState*>;
|
using SetOfEntityMotionStates = QSet<EntityMotionState*>;
|
||||||
|
|
||||||
class PhysicalEntitySimulation :public EntitySimulation {
|
class PhysicalEntitySimulation : public EntitySimulation {
|
||||||
public:
|
public:
|
||||||
PhysicalEntitySimulation();
|
PhysicalEntitySimulation();
|
||||||
~PhysicalEntitySimulation();
|
~PhysicalEntitySimulation();
|
||||||
|
|
|
@ -43,14 +43,14 @@ public:
|
||||||
class SteamScriptingInterface : public QObject {
|
class SteamScriptingInterface : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(bool isRunning READ isRunning)
|
Q_PROPERTY(bool running READ isRunning)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SteamScriptingInterface(QObject* parent, SteamClientPlugin* plugin) : QObject(parent) {}
|
SteamScriptingInterface(QObject* parent, SteamClientPlugin* plugin) : QObject(parent), _plugin(plugin) {}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool isRunning() const { return _plugin->isRunning(); }
|
bool isRunning() const { return _plugin && _plugin->isRunning(); }
|
||||||
void openInviteOverlay() const { _plugin->openInviteOverlay(); }
|
void openInviteOverlay() const { if (_plugin) { _plugin->openInviteOverlay(); } }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SteamClientPlugin* _plugin;
|
SteamClientPlugin* _plugin;
|
||||||
|
|
|
@ -147,25 +147,29 @@ static bool hasCorrectSyntax(const QScriptProgram& program, ScriptEngine* report
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName, ScriptEngine* reportingEngine) {
|
static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName, ScriptEngine* reportingEngine, QString* exceptionMessage = nullptr) {
|
||||||
if (engine.hasUncaughtException()) {
|
if (engine.hasUncaughtException()) {
|
||||||
const auto backtrace = engine.uncaughtExceptionBacktrace();
|
const auto backtrace = engine.uncaughtExceptionBacktrace();
|
||||||
const auto exception = engine.uncaughtException().toString();
|
const auto exception = engine.uncaughtException().toString();
|
||||||
const auto line = QString::number(engine.uncaughtExceptionLineNumber());
|
const auto line = QString::number(engine.uncaughtExceptionLineNumber());
|
||||||
engine.clearExceptions();
|
engine.clearExceptions();
|
||||||
|
|
||||||
auto message = QString(SCRIPT_EXCEPTION_FORMAT).arg(exception, fileName, line);
|
QString message = QString(SCRIPT_EXCEPTION_FORMAT).arg(exception, fileName, line);
|
||||||
if (!backtrace.empty()) {
|
if (!backtrace.empty()) {
|
||||||
static const auto lineSeparator = "\n ";
|
static const auto lineSeparator = "\n ";
|
||||||
message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
|
message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
|
||||||
}
|
}
|
||||||
reportingEngine->scriptErrorMessage(qPrintable(message));
|
reportingEngine->scriptErrorMessage(qPrintable(message));
|
||||||
|
if (exceptionMessage) {
|
||||||
|
*exceptionMessage = message;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString) :
|
ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const QString& fileNameString) :
|
||||||
|
_context(context),
|
||||||
_scriptContents(scriptContents),
|
_scriptContents(scriptContents),
|
||||||
_timerFunctionMap(),
|
_timerFunctionMap(),
|
||||||
_fileNameString(fileNameString),
|
_fileNameString(fileNameString),
|
||||||
|
@ -180,6 +184,22 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
|
||||||
setProcessEventsInterval(MSECS_PER_SECOND);
|
setProcessEventsInterval(MSECS_PER_SECOND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ScriptEngine::getContext() const {
|
||||||
|
switch (_context) {
|
||||||
|
case CLIENT_SCRIPT:
|
||||||
|
return "client";
|
||||||
|
case ENTITY_CLIENT_SCRIPT:
|
||||||
|
return "entity_client";
|
||||||
|
case ENTITY_SERVER_SCRIPT:
|
||||||
|
return "entity_server";
|
||||||
|
case AGENT_SCRIPT:
|
||||||
|
return "agent";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
ScriptEngine::~ScriptEngine() {
|
ScriptEngine::~ScriptEngine() {
|
||||||
scriptInfoMessage("Script Engine shutting down:" + getFilename());
|
scriptInfoMessage("Script Engine shutting down:" + getFilename());
|
||||||
|
|
||||||
|
@ -562,9 +582,6 @@ void ScriptEngine::init() {
|
||||||
// constants
|
// constants
|
||||||
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
|
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
|
||||||
|
|
||||||
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
|
||||||
registerGlobalObject("Recording", recordingInterface.data());
|
|
||||||
|
|
||||||
registerGlobalObject("Assets", &_assetScriptingInterface);
|
registerGlobalObject("Assets", &_assetScriptingInterface);
|
||||||
registerGlobalObject("Resources", DependencyManager::get<ResourceScriptingInterface>().data());
|
registerGlobalObject("Resources", DependencyManager::get<ResourceScriptingInterface>().data());
|
||||||
}
|
}
|
||||||
|
@ -1374,6 +1391,15 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const {
|
||||||
|
auto it = _entityScripts.constFind(entityID);
|
||||||
|
if (it == _entityScripts.constEnd()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
details = it.value();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// since all of these operations can be asynch we will always do the actual work in the response handler
|
// since all of these operations can be asynch we will always do the actual work in the response handler
|
||||||
// for the download
|
// for the download
|
||||||
void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
||||||
|
@ -1421,11 +1447,24 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
||||||
bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
|
bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
|
||||||
auto fileName = isURL ? scriptOrURL : "EmbeddedEntityScript";
|
auto fileName = isURL ? scriptOrURL : "EmbeddedEntityScript";
|
||||||
|
|
||||||
|
EntityScriptDetails newDetails;
|
||||||
|
newDetails.scriptText = scriptOrURL;
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
newDetails.status = ERROR_LOADING_SCRIPT;
|
||||||
|
newDetails.errorInfo = "Failed to load script";
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QScriptProgram program(contents, fileName);
|
QScriptProgram program(contents, fileName);
|
||||||
if (!hasCorrectSyntax(program, this)) {
|
if (!hasCorrectSyntax(program, this)) {
|
||||||
if (!isFileUrl) {
|
if (!isFileUrl) {
|
||||||
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||||
}
|
}
|
||||||
|
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||||
|
newDetails.errorInfo = "Bad syntax";
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
return; // done processing script
|
return; // done processing script
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1450,7 +1489,13 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
||||||
});
|
});
|
||||||
testConstructor = sandbox.evaluate(program);
|
testConstructor = sandbox.evaluate(program);
|
||||||
}
|
}
|
||||||
if (hadUncaughtExceptions(sandbox, program.fileName(), this)) {
|
|
||||||
|
QString exceptionMessage;
|
||||||
|
if (hadUncaughtExceptions(sandbox, program.fileName(), this, &exceptionMessage)) {
|
||||||
|
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||||
|
newDetails.errorInfo = exceptionMessage;
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1473,6 +1518,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
||||||
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||||
|
newDetails.errorInfo = "Could not find constructor";
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
|
|
||||||
return; // done processing script
|
return; // done processing script
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1489,8 +1538,11 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
||||||
};
|
};
|
||||||
doWithEnvironment(entityID, sandboxURL, initialization);
|
doWithEnvironment(entityID, sandboxURL, initialization);
|
||||||
|
|
||||||
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified, sandboxURL };
|
newDetails.scriptObject = entityScriptObject;
|
||||||
|
newDetails.lastModified = lastModified;
|
||||||
|
newDetails.definingSandboxURL = sandboxURL;
|
||||||
_entityScripts[entityID] = newDetails;
|
_entityScripts[entityID] = newDetails;
|
||||||
|
|
||||||
if (isURL) {
|
if (isURL) {
|
||||||
setParentURL("");
|
setParentURL("");
|
||||||
}
|
}
|
||||||
|
@ -1516,7 +1568,9 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_entityScripts.contains(entityID)) {
|
if (_entityScripts.contains(entityID)) {
|
||||||
callEntityScriptMethod(entityID, "unload");
|
if (_entityScripts[entityID].status == RUNNING) {
|
||||||
|
callEntityScriptMethod(entityID, "unload");
|
||||||
|
}
|
||||||
_entityScripts.remove(entityID);
|
_entityScripts.remove(entityID);
|
||||||
stopAllTimersForEntityScript(entityID);
|
stopAllTimersForEntityScript(entityID);
|
||||||
}
|
}
|
||||||
|
@ -1535,7 +1589,9 @@ void ScriptEngine::unloadAllEntityScripts() {
|
||||||
qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
|
qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
|
||||||
#endif
|
#endif
|
||||||
foreach(const EntityItemID& entityID, _entityScripts.keys()) {
|
foreach(const EntityItemID& entityID, _entityScripts.keys()) {
|
||||||
callEntityScriptMethod(entityID, "unload");
|
if (_entityScripts[entityID].status == RUNNING) {
|
||||||
|
callEntityScriptMethod(entityID, "unload");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_entityScripts.clear();
|
_entityScripts.clear();
|
||||||
|
|
||||||
|
@ -1574,7 +1630,7 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
|
||||||
QString scriptContents = QTextStream(&file).readAll();
|
QString scriptContents = QTextStream(&file).readAll();
|
||||||
this->unloadEntityScript(entityID);
|
this->unloadEntityScript(entityID);
|
||||||
this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true);
|
this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true);
|
||||||
if (!_entityScripts.contains(entityID)) {
|
if (!_entityScripts.contains(entityID) || _entityScripts[entityID].status != RUNNING) {
|
||||||
scriptWarningMessage("Reload script " + details.scriptText + " failed");
|
scriptWarningMessage("Reload script " + details.scriptText + " failed");
|
||||||
} else {
|
} else {
|
||||||
details = _entityScripts[entityID];
|
details = _entityScripts[entityID];
|
||||||
|
@ -1633,7 +1689,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
refreshFileScript(entityID);
|
refreshFileScript(entityID);
|
||||||
if (_entityScripts.contains(entityID)) {
|
if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
|
||||||
EntityScriptDetails details = _entityScripts[entityID];
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
QScriptValue entityScript = details.scriptObject; // previously loaded
|
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||||
if (entityScript.property(methodName).isFunction()) {
|
if (entityScript.property(methodName).isFunction()) {
|
||||||
|
@ -1665,7 +1721,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
refreshFileScript(entityID);
|
refreshFileScript(entityID);
|
||||||
if (_entityScripts.contains(entityID)) {
|
if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
|
||||||
EntityScriptDetails details = _entityScripts[entityID];
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
QScriptValue entityScript = details.scriptObject; // previously loaded
|
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||||
if (entityScript.property(methodName).isFunction()) {
|
if (entityScript.property(methodName).isFunction()) {
|
||||||
|
@ -1698,7 +1754,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
refreshFileScript(entityID);
|
refreshFileScript(entityID);
|
||||||
if (_entityScripts.contains(entityID)) {
|
if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
|
||||||
EntityScriptDetails details = _entityScripts[entityID];
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
QScriptValue entityScript = details.scriptObject; // previously loaded
|
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||||
if (entityScript.property(methodName).isFunction()) {
|
if (entityScript.property(methodName).isFunction()) {
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <LimitedNodeList.h>
|
#include <LimitedNodeList.h>
|
||||||
#include <EntityItemID.h>
|
#include <EntityItemID.h>
|
||||||
#include <EntitiesScriptEngineProvider.h>
|
#include <EntitiesScriptEngineProvider.h>
|
||||||
|
#include <EntityScriptUtils.h>
|
||||||
|
|
||||||
#include "PointerEvent.h"
|
#include "PointerEvent.h"
|
||||||
#include "ArrayBufferClass.h"
|
#include "ArrayBufferClass.h"
|
||||||
|
@ -58,16 +59,30 @@ typedef QHash<QString, CallbackList> RegisteredEventHandlers;
|
||||||
|
|
||||||
class EntityScriptDetails {
|
class EntityScriptDetails {
|
||||||
public:
|
public:
|
||||||
QString scriptText;
|
EntityScriptStatus status { RUNNING };
|
||||||
QScriptValue scriptObject;
|
|
||||||
int64_t lastModified;
|
// If status indicates an error, this contains a human-readable string giving more information about the error.
|
||||||
QUrl definingSandboxURL;
|
QString errorInfo { "" };
|
||||||
|
|
||||||
|
QString scriptText { "" };
|
||||||
|
QScriptValue scriptObject { QScriptValue() };
|
||||||
|
int64_t lastModified { 0 };
|
||||||
|
QUrl definingSandboxURL { QUrl() };
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider {
|
class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString context READ getContext)
|
||||||
public:
|
public:
|
||||||
ScriptEngine(const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString(""));
|
|
||||||
|
enum Context {
|
||||||
|
CLIENT_SCRIPT,
|
||||||
|
ENTITY_CLIENT_SCRIPT,
|
||||||
|
ENTITY_SERVER_SCRIPT,
|
||||||
|
AGENT_SCRIPT
|
||||||
|
};
|
||||||
|
|
||||||
|
ScriptEngine(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString(""));
|
||||||
~ScriptEngine();
|
~ScriptEngine();
|
||||||
|
|
||||||
/// run the script in a dedicated thread. This will have the side effect of evalulating
|
/// run the script in a dedicated thread. This will have the side effect of evalulating
|
||||||
|
@ -118,6 +133,12 @@ public:
|
||||||
/// to scripts. we may not need this to be invokable
|
/// to scripts. we may not need this to be invokable
|
||||||
void loadURL(const QUrl& scriptURL, bool reload);
|
void loadURL(const QUrl& scriptURL, bool reload);
|
||||||
|
|
||||||
|
Q_INVOKABLE QString getContext() const;
|
||||||
|
Q_INVOKABLE bool isClientScript() const { return _context == CLIENT_SCRIPT; }
|
||||||
|
Q_INVOKABLE bool isEntityClientScript() const { return _context == ENTITY_CLIENT_SCRIPT; }
|
||||||
|
Q_INVOKABLE bool isEntityServerScript() const { return _context == ENTITY_SERVER_SCRIPT; }
|
||||||
|
Q_INVOKABLE bool isAgentScript() const { return _context == AGENT_SCRIPT; }
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// NOTE - these are intended to be public interfaces available to scripts
|
// NOTE - these are intended to be public interfaces available to scripts
|
||||||
Q_INVOKABLE void addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler);
|
Q_INVOKABLE void addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler);
|
||||||
|
@ -178,6 +199,8 @@ public:
|
||||||
void scriptWarningMessage(const QString& message);
|
void scriptWarningMessage(const QString& message);
|
||||||
void scriptInfoMessage(const QString& message);
|
void scriptInfoMessage(const QString& message);
|
||||||
|
|
||||||
|
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
|
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
|
||||||
void updateMemoryCost(const qint64&);
|
void updateMemoryCost(const qint64&);
|
||||||
|
@ -200,6 +223,30 @@ signals:
|
||||||
void doneRunning();
|
void doneRunning();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void init();
|
||||||
|
|
||||||
|
bool evaluatePending() const { return _evaluatesPending > 0; }
|
||||||
|
void timerFired();
|
||||||
|
void stopAllTimers();
|
||||||
|
void stopAllTimersForEntityScript(const EntityItemID& entityID);
|
||||||
|
void refreshFileScript(const EntityItemID& entityID);
|
||||||
|
|
||||||
|
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
|
||||||
|
|
||||||
|
QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
|
||||||
|
void stopTimer(QTimer* timer);
|
||||||
|
|
||||||
|
QHash<EntityItemID, RegisteredEventHandlers> _registeredHandlers;
|
||||||
|
void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs);
|
||||||
|
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success);
|
||||||
|
|
||||||
|
EntityItemID currentEntityIdentifier {}; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution.
|
||||||
|
QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty.
|
||||||
|
void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation);
|
||||||
|
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
|
||||||
|
|
||||||
|
Context _context;
|
||||||
|
|
||||||
QString _scriptContents;
|
QString _scriptContents;
|
||||||
QString _parentURL;
|
QString _parentURL;
|
||||||
std::atomic<bool> _isFinished { false };
|
std::atomic<bool> _isFinished { false };
|
||||||
|
@ -215,19 +262,6 @@ protected:
|
||||||
bool _debuggable { false };
|
bool _debuggable { false };
|
||||||
qint64 _lastUpdate;
|
qint64 _lastUpdate;
|
||||||
|
|
||||||
void init();
|
|
||||||
|
|
||||||
bool evaluatePending() const { return _evaluatesPending > 0; }
|
|
||||||
void timerFired();
|
|
||||||
void stopAllTimers();
|
|
||||||
void stopAllTimersForEntityScript(const EntityItemID& entityID);
|
|
||||||
void refreshFileScript(const EntityItemID& entityID);
|
|
||||||
|
|
||||||
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
|
|
||||||
|
|
||||||
QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
|
|
||||||
void stopTimer(QTimer* timer);
|
|
||||||
|
|
||||||
QString _fileNameString;
|
QString _fileNameString;
|
||||||
Quat _quatLibrary;
|
Quat _quatLibrary;
|
||||||
Vec3 _vec3Library;
|
Vec3 _vec3Library;
|
||||||
|
@ -240,15 +274,6 @@ protected:
|
||||||
|
|
||||||
AssetScriptingInterface _assetScriptingInterface{ this };
|
AssetScriptingInterface _assetScriptingInterface{ this };
|
||||||
|
|
||||||
QHash<EntityItemID, RegisteredEventHandlers> _registeredHandlers;
|
|
||||||
void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs);
|
|
||||||
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success);
|
|
||||||
|
|
||||||
EntityItemID currentEntityIdentifier {}; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution.
|
|
||||||
QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty.
|
|
||||||
void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation);
|
|
||||||
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
|
|
||||||
|
|
||||||
std::function<bool()> _emitScriptUpdates{ [](){ return true; } };
|
std::function<bool()> _emitScriptUpdates{ [](){ return true; } };
|
||||||
|
|
||||||
std::recursive_mutex _lock;
|
std::recursive_mutex _lock;
|
||||||
|
|
|
@ -62,8 +62,9 @@ void ScriptEngines::onErrorLoadingScript(const QString& url) {
|
||||||
emit errorLoadingScript(url, scriptName);
|
emit errorLoadingScript(url, scriptName);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngines::ScriptEngines()
|
ScriptEngines::ScriptEngines(ScriptEngine::Context context)
|
||||||
: _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION)
|
: _context(context),
|
||||||
|
_scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION)
|
||||||
{
|
{
|
||||||
_scriptsModelFilter.setSourceModel(&_scriptsModel);
|
_scriptsModelFilter.setSourceModel(&_scriptsModel);
|
||||||
_scriptsModelFilter.sort(0, Qt::AscendingOrder);
|
_scriptsModelFilter.sort(0, Qt::AscendingOrder);
|
||||||
|
@ -453,7 +454,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
|
||||||
return scriptEngine;
|
return scriptEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptEngine = new ScriptEngine(NO_SCRIPT, "");
|
scriptEngine = new ScriptEngine(_context, NO_SCRIPT, "");
|
||||||
scriptEngine->setUserLoaded(isUserLoaded);
|
scriptEngine->setUserLoaded(isUserLoaded);
|
||||||
connect(scriptEngine, &ScriptEngine::doneRunning, this, [scriptEngine] {
|
connect(scriptEngine, &ScriptEngine::doneRunning, this, [scriptEngine] {
|
||||||
scriptEngine->deleteLater();
|
scriptEngine->deleteLater();
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <SettingHandle.h>
|
#include <SettingHandle.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
|
||||||
|
#include "ScriptEngine.h"
|
||||||
#include "ScriptsModel.h"
|
#include "ScriptsModel.h"
|
||||||
#include "ScriptsModelFilter.h"
|
#include "ScriptsModelFilter.h"
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ class ScriptEngines : public QObject, public Dependency {
|
||||||
public:
|
public:
|
||||||
using ScriptInitializer = std::function<void(ScriptEngine*)>;
|
using ScriptInitializer = std::function<void(ScriptEngine*)>;
|
||||||
|
|
||||||
ScriptEngines();
|
ScriptEngines(ScriptEngine::Context context);
|
||||||
void registerScriptInitializer(ScriptInitializer initializer);
|
void registerScriptInitializer(ScriptInitializer initializer);
|
||||||
|
|
||||||
void loadScripts();
|
void loadScripts();
|
||||||
|
@ -90,7 +91,6 @@ public slots:
|
||||||
protected slots:
|
protected slots:
|
||||||
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine);
|
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine);
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class ScriptEngine;
|
friend class ScriptEngine;
|
||||||
|
|
||||||
|
@ -101,6 +101,7 @@ protected:
|
||||||
void onScriptEngineError(const QString& scriptFilename);
|
void onScriptEngineError(const QString& scriptFilename);
|
||||||
void launchScriptEngine(ScriptEngine* engine);
|
void launchScriptEngine(ScriptEngine* engine);
|
||||||
|
|
||||||
|
ScriptEngine::Context _context;
|
||||||
QReadWriteLock _scriptEnginesHashLock;
|
QReadWriteLock _scriptEnginesHashLock;
|
||||||
QHash<QUrl, ScriptEngine*> _scriptEnginesHash;
|
QHash<QUrl, ScriptEngine*> _scriptEnginesHash;
|
||||||
QSet<ScriptEngine*> _allKnownScriptEngines;
|
QSet<ScriptEngine*> _allKnownScriptEngines;
|
||||||
|
|
|
@ -156,39 +156,42 @@ void ScriptsModel::reloadDefaultFiles() {
|
||||||
void ScriptsModel::requestDefaultFiles(QString marker) {
|
void ScriptsModel::requestDefaultFiles(QString marker) {
|
||||||
QUrl url(defaultScriptsLocation());
|
QUrl url(defaultScriptsLocation());
|
||||||
|
|
||||||
if (url.isLocalFile()) {
|
// targets that don't have a scripts folder in the appropriate location will have an empty URL here
|
||||||
// if the url indicates a local directory, use QDirIterator
|
if (!url.isEmpty()) {
|
||||||
QString localDir = expandScriptUrl(url).toLocalFile();
|
if (url.isLocalFile()) {
|
||||||
int localDirPartCount = localDir.split("/").size();
|
// if the url indicates a local directory, use QDirIterator
|
||||||
if (localDir.endsWith("/")) {
|
QString localDir = expandScriptUrl(url).toLocalFile();
|
||||||
localDirPartCount--;
|
int localDirPartCount = localDir.split("/").size();
|
||||||
}
|
if (localDir.endsWith("/")) {
|
||||||
#ifdef Q_OS_WIN
|
localDirPartCount--;
|
||||||
localDirPartCount++; // one for the drive letter
|
}
|
||||||
#endif
|
#ifdef Q_OS_WIN
|
||||||
QDirIterator it(localDir, QStringList() << "*.js", QDir::Files, QDirIterator::Subdirectories);
|
localDirPartCount++; // one for the drive letter
|
||||||
while (it.hasNext()) {
|
#endif
|
||||||
QUrl jsFullPath = QUrl::fromLocalFile(it.next());
|
QDirIterator it(localDir, QStringList() << "*.js", QDir::Files, QDirIterator::Subdirectories);
|
||||||
QString jsPartialPath = jsFullPath.path().split("/").mid(localDirPartCount).join("/");
|
while (it.hasNext()) {
|
||||||
jsFullPath = normalizeScriptURL(jsFullPath);
|
QUrl jsFullPath = QUrl::fromLocalFile(it.next());
|
||||||
_treeNodes.append(new TreeNodeScript(jsPartialPath, jsFullPath.toString(), SCRIPT_ORIGIN_DEFAULT));
|
QString jsPartialPath = jsFullPath.path().split("/").mid(localDirPartCount).join("/");
|
||||||
}
|
jsFullPath = normalizeScriptURL(jsFullPath);
|
||||||
_loadingScripts = false;
|
_treeNodes.append(new TreeNodeScript(jsPartialPath, jsFullPath.toString(), SCRIPT_ORIGIN_DEFAULT));
|
||||||
} else {
|
}
|
||||||
// the url indicates http(s), use QNetworkRequest
|
_loadingScripts = false;
|
||||||
QUrlQuery query;
|
} else {
|
||||||
query.addQueryItem(PREFIX_PARAMETER_NAME, ".");
|
// the url indicates http(s), use QNetworkRequest
|
||||||
if (!marker.isEmpty()) {
|
QUrlQuery query;
|
||||||
query.addQueryItem(MARKER_PARAMETER_NAME, marker);
|
query.addQueryItem(PREFIX_PARAMETER_NAME, ".");
|
||||||
}
|
if (!marker.isEmpty()) {
|
||||||
url.setQuery(query);
|
query.addQueryItem(MARKER_PARAMETER_NAME, marker);
|
||||||
|
}
|
||||||
|
url.setQuery(query);
|
||||||
|
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||||
QNetworkReply* reply = networkAccessManager.get(request);
|
QNetworkReply* reply = networkAccessManager.get(request);
|
||||||
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,7 +186,7 @@ void LogHandler::verboseMessageHandler(QtMsgType type, const QMessageLogContext&
|
||||||
getInstance().printMessage((LogMsgType) type, context, message);
|
getInstance().printMessage((LogMsgType) type, context, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& LogHandler::addRepeatedMessageRegex(const QString& regexString) {
|
void LogHandler::setupRepeatedMessageFlusher() {
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
std::call_once(once, [&] {
|
std::call_once(once, [&] {
|
||||||
// setup our timer to flush the verbose logs every 5 seconds
|
// setup our timer to flush the verbose logs every 5 seconds
|
||||||
|
@ -194,6 +194,11 @@ const QString& LogHandler::addRepeatedMessageRegex(const QString& regexString) {
|
||||||
connect(logFlushTimer, &QTimer::timeout, this, &LogHandler::flushRepeatedMessages);
|
connect(logFlushTimer, &QTimer::timeout, this, &LogHandler::flushRepeatedMessages);
|
||||||
logFlushTimer->start(VERBOSE_LOG_INTERVAL_SECONDS * 1000);
|
logFlushTimer->start(VERBOSE_LOG_INTERVAL_SECONDS * 1000);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& LogHandler::addRepeatedMessageRegex(const QString& regexString) {
|
||||||
|
// make sure we setup the repeated message flusher, but do it on the LogHandler thread
|
||||||
|
QMetaObject::invokeMethod(this, "setupRepeatedMessageFlusher");
|
||||||
|
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
return *_repeatedMessageRegexes.insert(regexString);
|
return *_repeatedMessageRegexes.insert(regexString);
|
||||||
|
|
|
@ -53,6 +53,9 @@ public:
|
||||||
const QString& addRepeatedMessageRegex(const QString& regexString);
|
const QString& addRepeatedMessageRegex(const QString& regexString);
|
||||||
const QString& addOnlyOnceMessageRegex(const QString& regexString);
|
const QString& addOnlyOnceMessageRegex(const QString& regexString);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void setupRepeatedMessageFlusher();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LogHandler();
|
LogHandler();
|
||||||
~LogHandler();
|
~LogHandler();
|
||||||
|
|
|
@ -114,7 +114,7 @@ void Tooltip::handleAPIResponse(QNetworkReply& requestReply) {
|
||||||
|
|
||||||
if (_description.isEmpty()) {
|
if (_description.isEmpty()) {
|
||||||
const QString DESCRIPTION_KEY = "description";
|
const QString DESCRIPTION_KEY = "description";
|
||||||
// we have an empty description - did a non-empty desciption come back?
|
// we have an empty description - did a non-empty description come back?
|
||||||
if (placeObject.contains(DESCRIPTION_KEY)) {
|
if (placeObject.contains(DESCRIPTION_KEY)) {
|
||||||
QString placeDescription = placeObject[DESCRIPTION_KEY].toString();
|
QString placeDescription = placeObject[DESCRIPTION_KEY].toString();
|
||||||
|
|
||||||
|
|
|
@ -1387,6 +1387,35 @@ function pushCommandForSelections(createdEntityData, deletedEntityData) {
|
||||||
|
|
||||||
var ENTITY_PROPERTIES_URL = Script.resolvePath('html/entityProperties.html');
|
var ENTITY_PROPERTIES_URL = Script.resolvePath('html/entityProperties.html');
|
||||||
|
|
||||||
|
var ServerScriptStatusMonitor = function(entityID, statusCallback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.entityID = entityID;
|
||||||
|
self.active = true;
|
||||||
|
self.sendRequestTimerID = null;
|
||||||
|
|
||||||
|
var onStatusReceived = function(success, isRunning, status, errorInfo) {
|
||||||
|
if (self.active) {
|
||||||
|
statusCallback({
|
||||||
|
statusRetrieved: success,
|
||||||
|
isRunning: isRunning,
|
||||||
|
status: status,
|
||||||
|
errorInfo: errorInfo
|
||||||
|
});
|
||||||
|
self.sendRequestTimerID = Script.setTimeout(function() {
|
||||||
|
if (self.active) {
|
||||||
|
Entities.getServerScriptStatus(entityID, onStatusReceived);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
self.stop = function() {
|
||||||
|
self.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entities.getServerScriptStatus(entityID, onStatusReceived);
|
||||||
|
};
|
||||||
|
|
||||||
var PropertiesTool = function (opts) {
|
var PropertiesTool = function (opts) {
|
||||||
var that = {};
|
var that = {};
|
||||||
|
|
||||||
|
@ -1398,6 +1427,11 @@ var PropertiesTool = function (opts) {
|
||||||
|
|
||||||
var visible = false;
|
var visible = false;
|
||||||
|
|
||||||
|
// This keeps track of the last entity ID that was selected. If multiple entities
|
||||||
|
// are selected or if no entity is selected this will be `null`.
|
||||||
|
var currentSelectedEntityID = null;
|
||||||
|
var statusMonitor = null;
|
||||||
|
|
||||||
webView.setVisible(visible);
|
webView.setVisible(visible);
|
||||||
|
|
||||||
that.setVisible = function (newVisible) {
|
that.setVisible = function (newVisible) {
|
||||||
|
@ -1405,10 +1439,44 @@ var PropertiesTool = function (opts) {
|
||||||
webView.setVisible(visible);
|
webView.setVisible(visible);
|
||||||
};
|
};
|
||||||
|
|
||||||
selectionManager.addEventListener(function () {
|
function updateScriptStatus(info) {
|
||||||
|
info.type = "server_script_status";
|
||||||
|
webView.emitScriptEvent(JSON.stringify(info));
|
||||||
|
};
|
||||||
|
|
||||||
|
function resetScriptStatus() {
|
||||||
|
updateScriptStatus({
|
||||||
|
statusRetrieved: false,
|
||||||
|
isRunning: false,
|
||||||
|
status: "",
|
||||||
|
errorInfo: ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
selectionManager.addEventListener(function (selectionUpdated) {
|
||||||
var data = {
|
var data = {
|
||||||
type: 'update'
|
type: 'update'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (selectionUpdated) {
|
||||||
|
resetScriptStatus();
|
||||||
|
|
||||||
|
if (selectionManager.selections.length !== 1) {
|
||||||
|
if (statusMonitor !== null) {
|
||||||
|
statusMonitor.stop();
|
||||||
|
statusMonitor = null;
|
||||||
|
}
|
||||||
|
currentSelectedEntityID = null;
|
||||||
|
} else if (currentSelectedEntityID != selectionManager.selections[0]) {
|
||||||
|
if (statusMonitor !== null) {
|
||||||
|
statusMonitor.stop();
|
||||||
|
}
|
||||||
|
var entityID = selectionManager.selections[0];
|
||||||
|
currentSelectedEntityID = entityID;
|
||||||
|
statusMonitor = new ServerScriptStatusMonitor(entityID, updateScriptStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var selections = [];
|
var selections = [];
|
||||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||||
var entity = {};
|
var entity = {};
|
||||||
|
@ -1568,7 +1636,7 @@ var PropertiesTool = function (opts) {
|
||||||
pushCommandForSelections();
|
pushCommandForSelections();
|
||||||
selectionManager._update();
|
selectionManager._update();
|
||||||
}
|
}
|
||||||
} else if (data.action === "reloadScript") {
|
} else if (data.action === "reloadClientScripts") {
|
||||||
if (selectionManager.hasSelection()) {
|
if (selectionManager.hasSelection()) {
|
||||||
var timestamp = Date.now();
|
var timestamp = Date.now();
|
||||||
for (i = 0; i < selectionManager.selections.length; i++) {
|
for (i = 0; i < selectionManager.selections.length; i++) {
|
||||||
|
@ -1577,6 +1645,12 @@ var PropertiesTool = function (opts) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (data.action === "reloadServerScripts") {
|
||||||
|
if (selectionManager.hasSelection()) {
|
||||||
|
for (i = 0; i < selectionManager.selections.length; i++) {
|
||||||
|
Entities.reloadServerScripts(selectionManager.selections[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -318,6 +318,18 @@
|
||||||
<input type="text" id="property-script-url">
|
<input type="text" id="property-script-url">
|
||||||
<input type="button" id="reload-script-button" class="glyph" value="F">
|
<input type="button" id="reload-script-button" class="glyph" value="F">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="behavior-group property url refresh">
|
||||||
|
<label for="property-server-scripts">Server Script URL</label>
|
||||||
|
<input type="text" id="property-server-scripts">
|
||||||
|
<input type="button" id="reload-server-scripts-button" class="glyph" value="F">
|
||||||
|
</div>
|
||||||
|
<div class="behavior-group property">
|
||||||
|
<label for="server-script-status">Server Script Status</label>
|
||||||
|
<span id="server-script-status"></span>
|
||||||
|
</div>
|
||||||
|
<div class="behavior-group property">
|
||||||
|
<textarea id="server-script-error"></textarea>
|
||||||
|
</div>
|
||||||
<div class="section-header model-group model-section zone-section">
|
<div class="section-header model-group model-section zone-section">
|
||||||
<label>Model</label><span>M</span>
|
<label>Model</label><span>M</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -590,7 +590,11 @@ function loaded() {
|
||||||
var elLifetime = document.getElementById("property-lifetime");
|
var elLifetime = document.getElementById("property-lifetime");
|
||||||
var elScriptURL = document.getElementById("property-script-url");
|
var elScriptURL = document.getElementById("property-script-url");
|
||||||
var elScriptTimestamp = document.getElementById("property-script-timestamp");
|
var elScriptTimestamp = document.getElementById("property-script-timestamp");
|
||||||
var elReloadScriptButton = document.getElementById("reload-script-button");
|
var elReloadScriptsButton = document.getElementById("reload-script-button");
|
||||||
|
var elServerScripts = document.getElementById("property-server-scripts");
|
||||||
|
var elReloadServerScriptsButton = document.getElementById("reload-server-scripts-button");
|
||||||
|
var elServerScriptStatus = document.getElementById("server-script-status");
|
||||||
|
var elServerScriptError = document.getElementById("server-script-error");
|
||||||
var elUserData = document.getElementById("property-user-data");
|
var elUserData = document.getElementById("property-user-data");
|
||||||
var elClearUserData = document.getElementById("userdata-clear");
|
var elClearUserData = document.getElementById("userdata-clear");
|
||||||
var elSaveUserData = document.getElementById("userdata-save");
|
var elSaveUserData = document.getElementById("userdata-save");
|
||||||
|
@ -708,7 +712,27 @@ function loaded() {
|
||||||
var properties;
|
var properties;
|
||||||
EventBridge.scriptEventReceived.connect(function(data) {
|
EventBridge.scriptEventReceived.connect(function(data) {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
if (data.type == "update") {
|
if (data.type == "server_script_status") {
|
||||||
|
if (!data.statusRetrieved) {
|
||||||
|
elServerScriptStatus.innerHTML = "Failed to retrieve status";
|
||||||
|
elServerScriptError.style.display = "none";
|
||||||
|
} else if (data.isRunning) {
|
||||||
|
if (data.status == "running") {
|
||||||
|
elServerScriptStatus.innerHTML = "Running";
|
||||||
|
elServerScriptError.style.display = "none";
|
||||||
|
} else if (data.status == "error_loading_script") {
|
||||||
|
elServerScriptStatus.innerHTML = "Error loading script";
|
||||||
|
elServerScriptError.style.display = "block";
|
||||||
|
} else if (data.status == "error_running_script") {
|
||||||
|
elServerScriptStatus.innerHTML = "Error running script";
|
||||||
|
elServerScriptError.style.display = "block";
|
||||||
|
}
|
||||||
|
elServerScriptError.innerHTML = data.errorInfo;;
|
||||||
|
} else {
|
||||||
|
elServerScriptStatus.innerHTML = "Not running";
|
||||||
|
elServerScriptError.style.display = "none";
|
||||||
|
}
|
||||||
|
} else if (data.type == "update") {
|
||||||
|
|
||||||
if (data.selections.length == 0) {
|
if (data.selections.length == 0) {
|
||||||
if (editor !== null && lastEntityID !== null) {
|
if (editor !== null && lastEntityID !== null) {
|
||||||
|
@ -847,6 +871,7 @@ function loaded() {
|
||||||
elLifetime.value = properties.lifetime;
|
elLifetime.value = properties.lifetime;
|
||||||
elScriptURL.value = properties.script;
|
elScriptURL.value = properties.script;
|
||||||
elScriptTimestamp.value = properties.scriptTimestamp;
|
elScriptTimestamp.value = properties.scriptTimestamp;
|
||||||
|
elServerScripts.value = properties.serverScripts;
|
||||||
|
|
||||||
var json = null;
|
var json = null;
|
||||||
try {
|
try {
|
||||||
|
@ -1143,6 +1168,7 @@ function loaded() {
|
||||||
elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
|
elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
|
||||||
elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
|
elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
|
||||||
elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp'));
|
elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp'));
|
||||||
|
elServerScripts.addEventListener('change', createEmitTextPropertyUpdateFunction('serverScripts'));
|
||||||
|
|
||||||
elClearUserData.addEventListener("click", function() {
|
elClearUserData.addEventListener("click", function() {
|
||||||
deleteJSONEditor();
|
deleteJSONEditor();
|
||||||
|
@ -1395,12 +1421,19 @@ function loaded() {
|
||||||
percentage: parseFloat(elRescaleDimensionsPct.value),
|
percentage: parseFloat(elRescaleDimensionsPct.value),
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
elReloadScriptButton.addEventListener("click", function() {
|
elReloadScriptsButton.addEventListener("click", function() {
|
||||||
EventBridge.emitWebEvent(JSON.stringify({
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
type: "action",
|
type: "action",
|
||||||
action: "reloadScript"
|
action: "reloadClientScripts"
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
elReloadServerScriptsButton.addEventListener("click", function() {
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
|
type: "action",
|
||||||
|
action: "reloadServerScripts"
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
window.onblur = function() {
|
window.onblur = function() {
|
||||||
// Fake a change event
|
// Fake a change event
|
||||||
|
|
|
@ -113,7 +113,7 @@ SelectionManager = (function() {
|
||||||
that.selections.push(entityID);
|
that.selections.push(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
that._update();
|
that._update(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
that.addEntity = function(entityID, toggleSelection) {
|
that.addEntity = function(entityID, toggleSelection) {
|
||||||
|
@ -132,7 +132,7 @@ SelectionManager = (function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
that._update();
|
that._update(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
that.removeEntity = function(entityID) {
|
that.removeEntity = function(entityID) {
|
||||||
|
@ -140,15 +140,15 @@ SelectionManager = (function() {
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
that.selections.splice(idx, 1);
|
that.selections.splice(idx, 1);
|
||||||
}
|
}
|
||||||
that._update();
|
that._update(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
that.clearSelections = function() {
|
that.clearSelections = function() {
|
||||||
that.selections = [];
|
that.selections = [];
|
||||||
that._update();
|
that._update(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
that._update = function() {
|
that._update = function(selectionUpdated) {
|
||||||
if (that.selections.length == 0) {
|
if (that.selections.length == 0) {
|
||||||
that.localDimensions = null;
|
that.localDimensions = null;
|
||||||
that.localPosition = null;
|
that.localPosition = null;
|
||||||
|
@ -205,7 +205,7 @@ SelectionManager = (function() {
|
||||||
|
|
||||||
for (var i = 0; i < listeners.length; i++) {
|
for (var i = 0; i < listeners.length; i++) {
|
||||||
try {
|
try {
|
||||||
listeners[i]();
|
listeners[i](selectionUpdated === true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("EntitySelectionTool got exception: " + JSON.stringify(e));
|
print("EntitySelectionTool got exception: " + JSON.stringify(e));
|
||||||
}
|
}
|
||||||
|
|
|
@ -582,7 +582,7 @@ function checkNewContent() {
|
||||||
if (argv.noUpdater) {
|
if (argv.noUpdater) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start downloading content set
|
// Start downloading content set
|
||||||
var req = request.head({
|
var req = request.head({
|
||||||
url: HOME_CONTENT_URL
|
url: HOME_CONTENT_URL
|
||||||
|
@ -859,7 +859,7 @@ function onContentLoaded() {
|
||||||
|
|
||||||
if (dsPath && acPath) {
|
if (dsPath && acPath) {
|
||||||
domainServer = new Process('domain-server', dsPath, ["--get-temp-name"], logPath);
|
domainServer = new Process('domain-server', dsPath, ["--get-temp-name"], logPath);
|
||||||
acMonitor = new ACMonitorProcess('ac-monitor', acPath, ['-n6',
|
acMonitor = new ACMonitorProcess('ac-monitor', acPath, ['-n7',
|
||||||
'--log-directory', logPath,
|
'--log-directory', logPath,
|
||||||
'--http-status-port', httpStatusPort], httpStatusPort, logPath);
|
'--http-status-port', httpStatusPort], httpStatusPort, logPath);
|
||||||
homeServer = new ProcessGroup('home', [domainServer, acMonitor]);
|
homeServer = new ProcessGroup('home', [domainServer, acMonitor]);
|
||||||
|
|
Loading…
Reference in a new issue