Merge remote-tracking branch 'upstream/master' into android_new_login

This commit is contained in:
Gabriel Calero 2018-05-02 17:00:32 -03:00
commit d0ab81d1f9
19 changed files with 185 additions and 12 deletions

View file

@ -1269,6 +1269,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] { connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] {
getEntities()->reloadEntityScripts(); getEntities()->reloadEntityScripts();
loadAvatarScripts(getMyAvatar()->getScriptUrls());
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
connect(scriptEngines, &ScriptEngines::scriptLoadError, connect(scriptEngines, &ScriptEngines::scriptLoadError,
@ -4835,6 +4836,31 @@ void Application::init() {
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
} }
void Application::loadAvatarScripts(const QVector<QString>& urls) {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
auto runningScripts = scriptEngines->getRunningScripts();
for (auto url : urls) {
int index = runningScripts.indexOf(url);
if (index < 0) {
auto scriptEnginePointer = scriptEngines->loadScript(url, false);
if (scriptEnginePointer) {
scriptEnginePointer->setType(ScriptEngine::Type::AVATAR);
}
}
}
}
void Application::unloadAvatarScripts() {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
auto urls = scriptEngines->getRunningScripts();
for (auto url : urls) {
auto scriptEngine = scriptEngines->getScriptEngine(url);
if (scriptEngine->getType() == ScriptEngine::Type::AVATAR) {
scriptEngines->stopScript(url, false);
}
}
}
void Application::updateLOD(float deltaTime) const { void Application::updateLOD(float deltaTime) const {
PerformanceTimer perfTimer("LOD"); PerformanceTimer perfTimer("LOD");
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode

View file

@ -290,6 +290,9 @@ public:
void replaceDomainContent(const QString& url); void replaceDomainContent(const QString& url);
void loadAvatarScripts(const QVector<QString>& urls);
void unloadAvatarScripts();
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
void enterBackground(); void enterBackground();
void enterForeground(); void enterForeground();

View file

@ -156,9 +156,11 @@ bool ModelPackager::zipModel() {
QByteArray nameField = _mapping.value(NAME_FIELD).toByteArray(); QByteArray nameField = _mapping.value(NAME_FIELD).toByteArray();
tempDir.mkpath(nameField + "/textures"); tempDir.mkpath(nameField + "/textures");
tempDir.mkpath(nameField + "/scripts");
QDir fbxDir(tempDir.path() + "/" + nameField); QDir fbxDir(tempDir.path() + "/" + nameField);
QDir texDir(fbxDir.path() + "/textures"); QDir texDir(fbxDir.path() + "/textures");
QDir scriptDir(fbxDir.path() + "/scripts");
// Copy textures // Copy textures
listTextures(); listTextures();
if (!_textures.empty()) { if (!_textures.empty()) {
@ -166,6 +168,23 @@ bool ModelPackager::zipModel() {
_texDir = _modelFile.path() + "/" + texdirField; _texDir = _modelFile.path() + "/" + texdirField;
copyTextures(_texDir, texDir); copyTextures(_texDir, texDir);
} }
// Copy scripts
QByteArray scriptField = _mapping.value(SCRIPT_FIELD).toByteArray();
_mapping.remove(SCRIPT_FIELD);
if (scriptField.size() > 1) {
tempDir.mkpath(nameField + "/scripts");
_scriptDir = _modelFile.path() + "/" + scriptField;
QDir wdir = QDir(_scriptDir);
_mapping.remove(SCRIPT_FIELD);
wdir.setSorting(QDir::Name | QDir::Reversed);
auto list = wdir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries);
for (auto script : list) {
auto sc = tempDir.relativeFilePath(scriptDir.path()) + "/" + QUrl(script).fileName();
_mapping.insertMulti(SCRIPT_FIELD, sc);
}
copyDirectoryContent(wdir, scriptDir);
}
// Copy LODs // Copy LODs
QVariantHash lodField = _mapping.value(LOD_FIELD).toHash(); QVariantHash lodField = _mapping.value(LOD_FIELD).toHash();
@ -189,7 +208,10 @@ bool ModelPackager::zipModel() {
// Correct FST // Correct FST
_mapping[FILENAME_FIELD] = tempDir.relativeFilePath(newPath); _mapping[FILENAME_FIELD] = tempDir.relativeFilePath(newPath);
_mapping[TEXDIR_FIELD] = tempDir.relativeFilePath(texDir.path()); _mapping[TEXDIR_FIELD] = tempDir.relativeFilePath(texDir.path());
for (auto multi : _mapping.values(SCRIPT_FIELD)) {
multi.fromValue(tempDir.relativeFilePath(scriptDir.path()) + multi.toString());
}
// Copy FST // Copy FST
QFile fst(tempDir.path() + "/" + nameField + ".fst"); QFile fst(tempDir.path() + "/" + nameField + ".fst");
if (fst.open(QIODevice::WriteOnly)) { if (fst.open(QIODevice::WriteOnly)) {
@ -237,7 +259,9 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
if (!mapping.contains(TEXDIR_FIELD)) { if (!mapping.contains(TEXDIR_FIELD)) {
mapping.insert(TEXDIR_FIELD, "."); mapping.insert(TEXDIR_FIELD, ".");
} }
if (!mapping.contains(SCRIPT_FIELD)) {
mapping.insert(SCRIPT_FIELD, ".");
}
// mixamo/autodesk defaults // mixamo/autodesk defaults
if (!mapping.contains(SCALE_FIELD)) { if (!mapping.contains(SCALE_FIELD)) {
mapping.insert(SCALE_FIELD, 1.0); mapping.insert(SCALE_FIELD, 1.0);

View file

@ -37,10 +37,12 @@ private:
QFileInfo _fbxInfo; QFileInfo _fbxInfo;
FSTReader::ModelType _modelType; FSTReader::ModelType _modelType;
QString _texDir; QString _texDir;
QString _scriptDir;
QVariantHash _mapping; QVariantHash _mapping;
std::unique_ptr<FBXGeometry> _geometry; std::unique_ptr<FBXGeometry> _geometry;
QStringList _textures; QStringList _textures;
QStringList _scripts;
}; };

View file

@ -43,6 +43,9 @@ _geometry(geometry)
form->addRow("Texture Directory:", _textureDirectory = new QPushButton()); form->addRow("Texture Directory:", _textureDirectory = new QPushButton());
connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory())); connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory()));
form->addRow("Script Directory:", _scriptDirectory = new QPushButton());
connect(_scriptDirectory, SIGNAL(clicked(bool)), SLOT(chooseScriptDirectory()));
form->addRow("Scale:", _scale = new QDoubleSpinBox()); form->addRow("Scale:", _scale = new QDoubleSpinBox());
_scale->setMaximum(FLT_MAX); _scale->setMaximum(FLT_MAX);
_scale->setSingleStep(0.01); _scale->setSingleStep(0.01);
@ -100,6 +103,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
mapping.insert(TYPE_FIELD, getType()); mapping.insert(TYPE_FIELD, getType());
mapping.insert(NAME_FIELD, _name->text()); mapping.insert(NAME_FIELD, _name->text());
mapping.insert(TEXDIR_FIELD, _textureDirectory->text()); mapping.insert(TEXDIR_FIELD, _textureDirectory->text());
mapping.insert(SCRIPT_FIELD, _scriptDirectory->text());
mapping.insert(SCALE_FIELD, QString::number(_scale->value())); mapping.insert(SCALE_FIELD, QString::number(_scale->value()));
// update the joint indices // update the joint indices
@ -157,6 +161,7 @@ void ModelPropertiesDialog::reset() {
_name->setText(_originalMapping.value(NAME_FIELD).toString()); _name->setText(_originalMapping.value(NAME_FIELD).toString());
_textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString()); _textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString());
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble()); _scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
_scriptDirectory->setText(_originalMapping.value(SCRIPT_FIELD).toString());
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
@ -207,6 +212,20 @@ void ModelPropertiesDialog::chooseTextureDirectory() {
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1)); _textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
} }
void ModelPropertiesDialog::chooseScriptDirectory() {
QString directory = QFileDialog::getExistingDirectory(this, "Choose Script Directory",
_basePath + "/" + _scriptDirectory->text());
if (directory.isEmpty()) {
return;
}
if (!directory.startsWith(_basePath)) {
OffscreenUi::asyncWarning(NULL, "Invalid script directory", "Script directory must be child of base path.");
return;
}
_scriptDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
}
void ModelPropertiesDialog::updatePivotJoint() { void ModelPropertiesDialog::updatePivotJoint() {
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked()); _pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
} }

View file

@ -37,6 +37,7 @@ public:
private slots: private slots:
void reset(); void reset();
void chooseTextureDirectory(); void chooseTextureDirectory();
void chooseScriptDirectory();
void updatePivotJoint(); void updatePivotJoint();
void createNewFreeJoint(const QString& joint = QString()); void createNewFreeJoint(const QString& joint = QString());
@ -52,6 +53,7 @@ private:
FBXGeometry _geometry; FBXGeometry _geometry;
QLineEdit* _name = nullptr; QLineEdit* _name = nullptr;
QPushButton* _textureDirectory = nullptr; QPushButton* _textureDirectory = nullptr;
QPushButton* _scriptDirectory = nullptr;
QDoubleSpinBox* _scale = nullptr; QDoubleSpinBox* _scale = nullptr;
QDoubleSpinBox* _translationX = nullptr; QDoubleSpinBox* _translationX = nullptr;
QDoubleSpinBox* _translationY = nullptr; QDoubleSpinBox* _translationY = nullptr;

View file

@ -121,6 +121,19 @@ MyAvatar::MyAvatar(QThread* thread) :
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr); _skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) {
if (success) {
qApp->unloadAvatarScripts();
_shouldLoadScripts = true;
}
});
connect(_skeletonModel.get(), &Model::rigReady, this, [this]() {
if (_shouldLoadScripts) {
auto geometry = getSkeletonModel()->getFBXGeometry();
qApp->loadAvatarScripts(geometry.scripts);
_shouldLoadScripts = false;
}
});
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);
@ -2839,6 +2852,11 @@ void MyAvatar::setWalkSpeed(float value) {
_walkSpeed.set(value); _walkSpeed.set(value);
} }
QVector<QString> MyAvatar::getScriptUrls() {
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>();
return scripts;
}
glm::vec3 MyAvatar::getPositionForAudio() { glm::vec3 MyAvatar::getPositionForAudio() {
glm::vec3 result; glm::vec3 result;
switch (_audioListenerMode) { switch (_audioListenerMode) {

View file

@ -985,6 +985,8 @@ public:
void setWalkSpeed(float value); void setWalkSpeed(float value);
float getWalkSpeed() const; float getWalkSpeed() const;
QVector<QString> getScriptUrls();
public slots: public slots:
/**jsdoc /**jsdoc
@ -1322,7 +1324,6 @@ signals:
private slots: private slots:
void leaveDomain(); void leaveDomain();
protected: protected:
virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override; virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override;
virtual void forgetChild(SpatiallyNestablePointer newChild) const override; virtual void forgetChild(SpatiallyNestablePointer newChild) const override;
@ -1564,6 +1565,9 @@ private:
// max unscaled forward movement speed // max unscaled forward movement speed
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED }; ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR }; float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
// load avatar scripts once when rig is ready
bool _shouldLoadScripts { false };
}; };
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);

View file

@ -298,6 +298,7 @@ public:
bool hasSkeletonJoints; bool hasSkeletonJoints;
QVector<FBXMesh> meshes; QVector<FBXMesh> meshes;
QVector<QString> scripts;
QHash<QString, FBXMaterial> materials; QHash<QString, FBXMaterial> materials;

View file

@ -249,7 +249,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
indexToDirect = true; indexToDirect = true;
} }
} }
if (indexToDirect && data.normalIndices.isEmpty()) { if (indexToDirect && data.colorIndices.isEmpty()) {
// hack to work around wacky Makehuman exports // hack to work around wacky Makehuman exports
data.colorsByVertex = true; data.colorsByVertex = true;
} }

View file

@ -84,7 +84,7 @@ void FSTReader::writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it)
QByteArray FSTReader::writeMapping(const QVariantHash& mapping) { QByteArray FSTReader::writeMapping(const QVariantHash& mapping) {
static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << TYPE_FIELD << SCALE_FIELD << FILENAME_FIELD static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << TYPE_FIELD << SCALE_FIELD << FILENAME_FIELD
<< TEXDIR_FIELD << JOINT_FIELD << FREE_JOINT_FIELD << TEXDIR_FIELD << SCRIPT_FIELD << JOINT_FIELD << FREE_JOINT_FIELD
<< BLENDSHAPE_FIELD << JOINT_INDEX_FIELD; << BLENDSHAPE_FIELD << JOINT_INDEX_FIELD;
QBuffer buffer; QBuffer buffer;
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
@ -92,7 +92,7 @@ QByteArray FSTReader::writeMapping(const QVariantHash& mapping) {
for (auto key : PREFERED_ORDER) { for (auto key : PREFERED_ORDER) {
auto it = mapping.find(key); auto it = mapping.find(key);
if (it != mapping.constEnd()) { if (it != mapping.constEnd()) {
if (key == FREE_JOINT_FIELD) { // writeVariant does not handle strings added using insertMulti. if (key == FREE_JOINT_FIELD || key == SCRIPT_FIELD) { // writeVariant does not handle strings added using insertMulti.
for (auto multi : mapping.values(key)) { for (auto multi : mapping.values(key)) {
buffer.write(key.toUtf8()); buffer.write(key.toUtf8());
buffer.write(" = "); buffer.write(" = ");
@ -187,6 +187,26 @@ FSTReader::ModelType FSTReader::predictModelType(const QVariantHash& mapping) {
return ENTITY_MODEL; return ENTITY_MODEL;
} }
QVector<QString> FSTReader::getScripts(const QUrl& url, const QVariantHash& mapping) {
auto fstMapping = mapping.isEmpty() ? downloadMapping(url.toString()) : mapping;
QVector<QString> scriptPaths;
if (!fstMapping.value(SCRIPT_FIELD).isNull()) {
auto scripts = fstMapping.values(SCRIPT_FIELD).toVector();
for (auto &script : scripts) {
QString scriptPath = script.toString();
if (QUrl(scriptPath).isRelative()) {
if (scriptPath.at(0) == '/') {
scriptPath = scriptPath.right(scriptPath.length() - 1);
}
scriptPath = url.resolved(QUrl(scriptPath)).toString();
}
scriptPaths.push_back(scriptPath);
}
}
return scriptPaths;
}
QVariantHash FSTReader::downloadMapping(const QString& url) { QVariantHash FSTReader::downloadMapping(const QString& url) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(url); QNetworkRequest networkRequest = QNetworkRequest(url);

View file

@ -28,6 +28,7 @@ static const QString TRANSLATION_Z_FIELD = "tz";
static const QString JOINT_FIELD = "joint"; static const QString JOINT_FIELD = "joint";
static const QString FREE_JOINT_FIELD = "freeJoint"; static const QString FREE_JOINT_FIELD = "freeJoint";
static const QString BLENDSHAPE_FIELD = "bs"; static const QString BLENDSHAPE_FIELD = "bs";
static const QString SCRIPT_FIELD = "script";
class FSTReader { class FSTReader {
public: public:
@ -49,6 +50,8 @@ public:
/// Predicts the type of model by examining the mapping /// Predicts the type of model by examining the mapping
static ModelType predictModelType(const QVariantHash& mapping); static ModelType predictModelType(const QVariantHash& mapping);
static QVector<QString> getScripts(const QUrl& fstUrl, const QVariantHash& mapping = QVariantHash());
static QString getNameFromType(ModelType modelType); static QString getNameFromType(ModelType modelType);
static FSTReader::ModelType getTypeFromName(const QString& name); static FSTReader::ModelType getTypeFromName(const QString& name);
static QVariantHash downloadMapping(const QString& url); static QVariantHash downloadMapping(const QString& url);

View file

@ -66,6 +66,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
auto mapping = FSTReader::readMapping(data); auto mapping = FSTReader::readMapping(data);
QString filename = mapping.value("filename").toString(); QString filename = mapping.value("filename").toString();
if (filename.isNull()) { if (filename.isNull()) {
qCDebug(modelnetworking) << "Mapping file" << _url << "has no \"filename\" field"; qCDebug(modelnetworking) << "Mapping file" << _url << "has no \"filename\" field";
finishedLoading(false); finishedLoading(false);
@ -82,6 +83,14 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
_textureBaseUrl = url.resolved(QUrl(".")); _textureBaseUrl = url.resolved(QUrl("."));
} }
auto scripts = FSTReader::getScripts(_url, mapping);
if (scripts.size() > 0) {
mapping.remove(SCRIPT_FIELD);
for (auto &scriptPath : scripts) {
mapping.insertMulti(SCRIPT_FIELD, scriptPath);
}
}
auto animGraphVariant = mapping.value("animGraphUrl"); auto animGraphVariant = mapping.value("animGraphUrl");
if (animGraphVariant.isValid()) { if (animGraphVariant.isValid()) {
QUrl fstUrl(animGraphVariant.toString()); QUrl fstUrl(animGraphVariant.toString());
@ -209,6 +218,14 @@ void GeometryReader::run() {
throw QString("unsupported format"); throw QString("unsupported format");
} }
// Add scripts to fbxgeometry
if (!_mapping.value(SCRIPT_FIELD).isNull()) {
QVariantList scripts = _mapping.values(SCRIPT_FIELD);
for (auto &script : scripts) {
fbxGeometry->scripts.push_back(script.toString());
}
}
// Ensure the resource has not been deleted // Ensure the resource has not been deleted
auto resource = _resource.toStrongRef(); auto resource = _resource.toStrongRef();
if (!resource) { if (!resource) {

View file

@ -447,7 +447,12 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
// this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor // this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID())); assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
if (_entity->dynamicDataNeedsTransmit() || _entity->queryAACubeNeedsUpdate()) { // shouldSendUpdate() sould NOT be triggering updates to maintain the queryAACube of dynamic entities.
// The server is supposed to predict the transform of such moving things. The client performs a "double prediction"
// where it predicts what the the server is doing, and only sends updates whent the entity's true transform
// differs significantly. That is what the remoteSimulationOutOfSync() logic is all about.
if (_entity->dynamicDataNeedsTransmit() ||
(!_entity->getDynamic() && _entity->queryAACubeNeedsUpdate())) {
return true; return true;
} }

View file

@ -180,6 +180,21 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const
// don't delete `ScriptEngines` until all `ScriptEngine`s are gone // don't delete `ScriptEngines` until all `ScriptEngine`s are gone
_scriptEngines(DependencyManager::get<ScriptEngines>()) _scriptEngines(DependencyManager::get<ScriptEngines>())
{ {
switch (_context) {
case Context::CLIENT_SCRIPT:
_type = Type::CLIENT;
break;
case Context::ENTITY_CLIENT_SCRIPT:
_type = Type::ENTITY_CLIENT;
break;
case Context::ENTITY_SERVER_SCRIPT:
_type = Type::ENTITY_SERVER;
break;
case Context::AGENT_SCRIPT:
_type = Type::AGENT;
break;
}
connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) { connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
if (hasUncaughtException()) { if (hasUncaughtException()) {
// the engine's uncaughtException() seems to produce much better stack traces here // the engine's uncaughtException() seems to produce much better stack traces here

View file

@ -103,6 +103,14 @@ public:
AGENT_SCRIPT AGENT_SCRIPT
}; };
enum Type {
CLIENT,
ENTITY_CLIENT,
ENTITY_SERVER,
AGENT,
AVATAR
};
static int processLevelMaxRetries; static int processLevelMaxRetries;
ScriptEngine(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine")); ScriptEngine(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine"));
~ScriptEngine(); ~ScriptEngine();
@ -493,6 +501,9 @@ public:
*/ */
Q_INVOKABLE QUuid generateUUID() { return QUuid::createUuid(); } Q_INVOKABLE QUuid generateUUID() { return QUuid::createUuid(); }
void setType(Type type) { _type = type; };
Type getType() { return _type; };
bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget
bool isRunning() const { return _isRunning; } // used by ScriptWidget bool isRunning() const { return _isRunning; } // used by ScriptWidget
@ -724,6 +735,7 @@ protected:
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args); void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
Context _context; Context _context;
Type _type;
QString _scriptContents; QString _scriptContents;
QString _parentURL; QString _parentURL;
std::atomic<bool> _isFinished { false }; std::atomic<bool> _isFinished { false };

View file

@ -427,11 +427,13 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) {
if (_scriptEnginesHash.contains(scriptURL)) { if (_scriptEnginesHash.contains(scriptURL)) {
ScriptEnginePointer scriptEngine = _scriptEnginesHash[scriptURL]; ScriptEnginePointer scriptEngine = _scriptEnginesHash[scriptURL];
if (restart) { if (restart) {
bool isUserLoaded = scriptEngine->isUserLoaded();
ScriptEngine::Type type = scriptEngine->getType();
auto scriptCache = DependencyManager::get<ScriptCache>(); auto scriptCache = DependencyManager::get<ScriptCache>();
scriptCache->deleteScript(scriptURL); scriptCache->deleteScript(scriptURL);
connect(scriptEngine.data(), &ScriptEngine::finished, connect(scriptEngine.data(), &ScriptEngine::finished,
this, [this](QString scriptName, ScriptEnginePointer engine) { this, [this, isUserLoaded, type](QString scriptName, ScriptEnginePointer engine) {
reloadScript(scriptName); reloadScript(scriptName, isUserLoaded)->setType(type);
}); });
} }
scriptEngine->stop(); scriptEngine->stop();

View file

@ -259,7 +259,7 @@ protected slots:
protected: protected:
friend class ScriptEngine; friend class ScriptEngine;
void reloadScript(const QString& scriptName) { loadScript(scriptName, true, false, false, true); } ScriptEnginePointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); }
void removeScriptEngine(ScriptEnginePointer); void removeScriptEngine(ScriptEnginePointer);
void onScriptEngineLoaded(const QString& scriptFilename); void onScriptEngineLoaded(const QString& scriptFilename);
void onScriptEngineError(const QString& scriptFilename); void onScriptEngineError(const QString& scriptFilename);

View file

@ -103,7 +103,7 @@ EntityListTool = function(opts) {
var selectedIDs = []; var selectedIDs = [];
for (var j = 0; j < selectionManager.selections.length; j++) { for (var j = 0; j < selectionManager.selections.length; j++) {
selectedIDs.push(selectionManager.selections[j].id); selectedIDs.push(selectionManager.selections[j]);
} }
var data = { var data = {