Skip to content

Commit e77a460

Browse files
script entities
1 parent adacca8 commit e77a460

38 files changed

+1663
-145
lines changed

assignment-client/src/scripts/EntityScriptServer.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,18 @@ void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<Receiv
125125
auto replyPacketList = NLPacketList::create(PacketType::EntityScriptGetStatusReply, QByteArray(), true, true);
126126
replyPacketList->writePrimitive(messageID);
127127

128+
EntityTreePointer tree = _entityViewer.getTree();
129+
if (!tree) {
130+
return;
131+
}
132+
133+
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
134+
if (!entity) {
135+
return;
136+
}
137+
128138
EntityScriptDetails details;
129-
if (_entitiesScriptManager->getEntityScriptDetails(entityID, details)) {
139+
if (_entitiesScriptManager->getEntityScriptDetails(entityID, entity->getServerScripts(), details)) {
130140
replyPacketList->writePrimitive(true);
131141
replyPacketList->writePrimitive(details.status);
132142
replyPacketList->writeString(details.errorInfo);
@@ -568,7 +578,7 @@ void EntityScriptServer::addingEntity(const EntityItemID& entityID) {
568578
void EntityScriptServer::deletingEntity(const EntityItemID& entityID) {
569579
if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptManager) {
570580
// TODO: Check if this is running on script engine thread, otherwise lambda capturing script engine pointer is needed
571-
_entitiesScriptManager->unloadEntityScript(entityID, true);
581+
_entitiesScriptManager->unloadAllEntityScriptsForEntity(entityID, true);
572582
}
573583
}
574584

@@ -582,12 +592,17 @@ void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool
582592
if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptManager) {
583593

584594
EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID);
595+
if (!entity) {
596+
return;
597+
}
598+
599+
QString serverScripts = entity->getServerScripts();
585600
EntityScriptDetails details;
586-
bool isRunning = _entitiesScriptManager->getEntityScriptDetails(entityID, details);
587-
if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) {
601+
bool isRunning = _entitiesScriptManager->getEntityScriptDetails(entityID, serverScripts, details);
602+
if (forceRedownload || !isRunning || details.scriptText != serverScripts) {
588603
// TODO: Check if this is running on script engine thread, otherwise lambda capturing script engine pointer is needed
589604
if (isRunning) {
590-
_entitiesScriptManager->unloadEntityScript(entityID, true);
605+
_entitiesScriptManager->unloadEntityScript(entityID, serverScripts, true);
591606
}
592607

593608
QString scriptUrl = entity->getServerScripts();

interface/src/Application_Setup.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
#include <ResourceScriptingInterface.h>
8787
#include <SceneScriptingInterface.h>
8888
#include <ScriptEngines.h>
89+
#include <ScriptEntityItem.h>
8990
#include <scripting/Audio.h>
9091
#include <scripting/AssetMappingsScriptingInterface.h>
9192
#include <scripting/ControllerScriptingInterface.h>
@@ -1668,6 +1669,16 @@ void Application::setupSignalsAndOperators() {
16681669
return nullptr;
16691670
});
16701671

1672+
ScriptEntityItem::setLoadOrReloadScriptOperator([](const EntityItemID& entityID, const QString& oldScriptURL, const QString& newScriptURL) -> bool {
1673+
return DependencyManager::get<EntityTreeRenderer>()->checkAndCallPreload(entityID, true, true, oldScriptURL, newScriptURL);
1674+
});
1675+
ScriptEntityItem::setUnloadScriptOperator([](const EntityItemID& entityID, const QString& scriptURL) -> void {
1676+
DependencyManager::get<EntityTreeRenderer>()->unloadEntityScript(entityID, scriptURL);
1677+
});
1678+
EntityItem::setUpdateScriptUserDataOperator([](const EntityItemID& entityID, const QString& scriptURL, const QString& userData) -> void {
1679+
return DependencyManager::get<EntityTreeRenderer>()->updateScriptUserData(entityID, scriptURL, userData);
1680+
});
1681+
16711682
Procedural::opaqueStencil = [](gpu::StatePointer state, bool useAA) {
16721683
useAA ? PrepareStencil::testMaskDrawShape(*state) : PrepareStencil::testMaskDrawShapeNoAA(*state);
16731684
};

libraries/entities-renderer/src/EntityTreeRenderer.cpp

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
296296
if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) {
297297
auto scriptEnginePtr = _nonPersistentEntitiesScriptManager;
298298
QMetaObject::invokeMethod(scriptEnginePtr.get(), [scriptEnginePtr, entityID]{
299-
scriptEnginePtr->unloadEntityScript(entityID, true);
299+
scriptEnginePtr->unloadAllEntityScriptsForEntity(entityID, true);
300300
});
301301
}
302302
}
@@ -1120,7 +1120,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
11201120
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
11211121
}
11221122
QMetaObject::invokeMethod(scriptEngine.get(), [scriptEngine, entityID]{
1123-
scriptEngine->unloadEntityScript(entityID, true);
1123+
scriptEngine->unloadAllEntityScriptsForEntity(entityID, true);
11241124
});
11251125
}
11261126

@@ -1160,31 +1160,77 @@ void EntityTreeRenderer::entityScriptChanging(const EntityItemID& entityID, bool
11601160
forceRecheckEntities();
11611161
}
11621162

1163-
void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool reload, bool unloadFirst) {
1163+
bool EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool reload, bool unloadFirst,
1164+
const QString& oldOverrideURL, const QString& newOverrideURL) {
11641165
if (_tree && !_shuttingDown) {
11651166
EntityItemPointer entity = getTree()->findEntityByEntityItemID(entityID);
11661167
if (!entity) {
1167-
return;
1168+
return false;
11681169
}
11691170
auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager;
1170-
bool shouldLoad = entity->shouldPreloadScript() && scriptEngine;
1171-
QString scriptUrl = entity->getScript();
1171+
if (!scriptEngine) {
1172+
return false;
1173+
}
1174+
1175+
bool shouldLoad = !newOverrideURL.isEmpty() ? (newOverrideURL != oldOverrideURL) : entity->shouldPreloadScript();
1176+
QString scriptUrl = !oldOverrideURL.isEmpty() ? oldOverrideURL : entity->getScript();
11721177
if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) {
1173-
if (scriptEngine) {
1174-
if (_currentEntitiesInside.contains(entityID)) {
1175-
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
1176-
}
1177-
QMetaObject::invokeMethod(scriptEngine.get(), [scriptEngine, entityID]{
1178-
scriptEngine->unloadEntityScript(entityID);
1179-
});
1178+
QString loadedScript = !oldOverrideURL.isEmpty() ? oldOverrideURL : entity->getLoadedScript();
1179+
if (_currentEntitiesInside.contains(entityID)) {
1180+
scriptEngine->callEntityScriptMethodForScript(entityID, loadedScript, "leaveEntity");
1181+
}
1182+
QMetaObject::invokeMethod(scriptEngine.get(), [scriptEngine, entityID, loadedScript]{
1183+
scriptEngine->unloadEntityScript(entityID, loadedScript, true);
1184+
});
1185+
if (!oldOverrideURL.isEmpty()) {
1186+
entity->scriptHasUnloaded();
11801187
}
1181-
entity->scriptHasUnloaded();
11821188
}
11831189
if (shouldLoad) {
1184-
entity->setScriptHasFinishedPreload(false);
1185-
scriptEngine->loadEntityScript(entityID, resolveScriptURL(scriptUrl), reload);
1186-
entity->scriptHasPreloaded();
1190+
if (!newOverrideURL.isEmpty()) {
1191+
entity->setScriptHasFinishedPreload(false);
1192+
}
1193+
scriptEngine->loadEntityScript(entityID, resolveScriptURL(!newOverrideURL.isEmpty() ? newOverrideURL : scriptUrl), reload);
1194+
if (!newOverrideURL.isEmpty()) {
1195+
entity->scriptHasPreloaded();
1196+
}
11871197
}
1198+
1199+
return true;
1200+
}
1201+
1202+
return false;
1203+
}
1204+
1205+
void EntityTreeRenderer::unloadEntityScript(const EntityItemID& entityID, const QString& scriptURL) {
1206+
if (_tree && !_shuttingDown) {
1207+
EntityItemPointer entity = getTree()->findEntityByEntityItemID(entityID);
1208+
if (!entity) {
1209+
return;
1210+
}
1211+
auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager;
1212+
if (!scriptEngine) {
1213+
return;
1214+
}
1215+
1216+
QMetaObject::invokeMethod(scriptEngine.get(), [scriptEngine, entityID, scriptURL] {
1217+
scriptEngine->unloadEntityScript(entityID, scriptURL, true);
1218+
});
1219+
}
1220+
}
1221+
1222+
void EntityTreeRenderer::updateScriptUserData(const EntityItemID& entityID, const QString& scriptURL, const QString& userData) {
1223+
if (_tree && !_shuttingDown) {
1224+
EntityItemPointer entity = getTree()->findEntityByEntityItemID(entityID);
1225+
if (!entity) {
1226+
return;
1227+
}
1228+
auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager;
1229+
if (!scriptEngine) {
1230+
return;
1231+
}
1232+
1233+
scriptEngine->callEntityScriptMethodForScript(entityID, scriptURL, "updateUserData", userData);
11881234
}
11891235
}
11901236

libraries/entities-renderer/src/EntityTreeRenderer.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@ class EntityTreeRenderer : public OctreeProcessor, public Dependency {
148148
size_t getPrevNumEntityUpdates() const { return _prevNumEntityUpdates; }
149149
size_t getPrevTotalNeededEntityUpdates() const { return _prevTotalNeededEntityUpdates; }
150150

151+
bool checkAndCallPreload(const EntityItemID& entityID,
152+
bool reload = false,
153+
bool unloadFirst = false,
154+
const QString& oldOverrideURL = "",
155+
const QString& newOverrideURL = "");
156+
void unloadEntityScript(const EntityItemID& entityID, const QString& scriptURL);
157+
void updateScriptUserData(const EntityItemID& entityID, const QString& scriptURL, const QString& userData);
158+
151159
signals:
152160
void enterEntity(const EntityItemID& entityItemID);
153161
void leaveEntity(const EntityItemID& entityItemID);
@@ -191,8 +199,6 @@ public slots:
191199
bool applyLayeredZones();
192200
void stopDomainAndNonOwnedEntities();
193201

194-
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false);
195-
196202
EntityItemID _currentHoverOverEntityID;
197203
EntityItemID _currentClickingOnEntityID;
198204

libraries/entities/src/EntityItem.cpp.in

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ int entityItemPointernMetaTypeId = qRegisterMetaType<EntityItemPointer>();
4949

5050
int EntityItem::_maxActionsDataSize = 800;
5151
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
52+
std::function<void(const EntityItemID&, const QString&, const QString&)> EntityItem::_updateScriptUserDataOperator = nullptr;
5253

5354
EntityItem::EntityItem(const EntityItemID& entityItemID) :
5455
SpatiallyNestable(NestableType::Entity, entityItemID)
@@ -2432,9 +2433,15 @@ QString EntityItem::getUserData() const {
24322433
}
24332434

24342435
void EntityItem::setUserData(const QString& value) {
2435-
withWriteLock([&] {
2436-
_userData = value;
2437-
});
2436+
if (getUserData() != value) {
2437+
withWriteLock([&] {
2438+
_userData = value;
2439+
});
2440+
QString scriptURL = getScript();
2441+
if (!scriptURL.isEmpty()) {
2442+
EntityItem::updateScriptUserData(getEntityItemID(), scriptURL, value);
2443+
}
2444+
}
24382445
}
24392446

24402447
QString EntityItem::getHref() const {

libraries/entities/src/EntityItem.h.in

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ public:
387387
void scriptHasUnloaded();
388388
void setScriptHasFinishedPreload(bool value);
389389
bool isScriptPreloadFinished();
390+
QString getLoadedScript() const { return _loadedScript; }
390391
virtual bool isWearable() const;
391392
bool isDomainEntity() const { return _entityHostType == entity::HostType::DOMAIN; }
392393
bool isAvatarEntity() const { return _entityHostType == entity::HostType::AVATAR; }
@@ -447,6 +448,13 @@ public:
447448
bool needsZoneOcclusionUpdate() const { return _needsZoneOcclusionUpdate; }
448449
void resetNeedsZoneOcclusionUpdate() { withWriteLock([&] { _needsZoneOcclusionUpdate = false; }); }
449450

451+
static void setUpdateScriptUserDataOperator(std::function<void(const EntityItemID&, const QString&, const QString&)> updateScriptUserDataOperator) {
452+
_updateScriptUserDataOperator = updateScriptUserDataOperator;
453+
}
454+
static void updateScriptUserData(const EntityItemID& entityID, const QString& scriptURL, const QString& userData) {
455+
_updateScriptUserDataOperator(entityID, scriptURL, userData);
456+
}
457+
450458
signals:
451459
void spaceUpdate(std::pair<int32_t, glm::vec4> data);
452460

@@ -588,6 +596,8 @@ protected:
588596
bool _cullWithParent { false };
589597

590598
mutable bool _needsRenderUpdate { false };
599+
600+
static std::function<void(const EntityItemID&, const QString&, const QString&)> _updateScriptUserDataOperator;
591601
};
592602

593603
#endif // hifi_EntityItem_h

libraries/entities/src/EntityItemProperties.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ enum:COMPOUND_SHAPE_URL prop:compoundShapeURL type:QString default:"" urlPermiss
7171
enum:COLOR prop:color type:u8vec3Color default:ENTITY_ITEM_DEFAULT_COLOR,
7272
enum:ALPHA prop:alpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:0.0f max:1.0f,
7373
enum:UNLIT prop:unlit type:bool default:false,
74+
enum:SCRIPT_URL prop:scriptURL type:QString default:"" urlPermission,
7475
group:pulse,
7576
enum:TEXTURES prop:textures type:QString default:"",
7677
enum:LINE_POINTS prop:linePoints type:qVectorVec3 default:ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC,
@@ -203,7 +204,7 @@ enum:ALPHA prop:alpha type:float default:ENTITY_ITEM_DEFAULT_ALPHA min:0.0f max:
203204
group:pulse common renderUpdateOnSet,
204205
enum:SOURCE_URL prop:sourceUrl type:QString default:WebEntityItem::DEFAULT_SOURCE_URL urlPermission renderProp,
205206
enum:DPI prop:dpi type:uint16_t default:ENTITY_ITEM_DEFAULT_DPI renderProp,
206-
enum:SCRIPT_URL prop:scriptURL type:QString default:"" urlPermission noGetterSetterProp,
207+
enum:SCRIPT_URL prop:scriptURL type:QString default:"" urlPermission common,
207208
enum:MAX_FPS prop:maxFPS type:uint8_t default:WebEntityItem::DEFAULT_MAX_FPS renderProp,
208209
enum:INPUT_MODE prop:inputMode type:WebInputMode default:WebInputMode::TOUCH enum renderProp,
209210
enum:WANTS_KEYBOARD_FOCUS prop:wantsKeyboardFocus type:bool default:true renderProp,
@@ -259,11 +260,14 @@ Gizmo
259260
enum:GIZMO_TYPE prop:gizmoType type:GizmoType default:GizmoType::RING enum renderProp,
260261
group:ring type:RingGizmo renderUpdateOnSet,
261262
Sound
262-
enum:SOUND_URL prop:soundURL type:QString default:"",
263+
enum:SOUND_URL prop:soundURL type:QString default:"" urlPermission,
263264
enum:SOUND_VOLUME prop:volume type:float default:1.0f min:0.0f max:1.0f,
264265
enum:SOUND_TIME_OFFSET prop:timeOffset type:float default:0.0f,
265266
enum:SOUND_PITCH, prop:pitch type:float default:1.0f min:1.0f/16.0f max:16.0f,
266267
enum:SOUND_PLAYING prop:playing type:bool default:true,
267268
enum:SOUND_LOOP prop:loop type:bool default:true,
268269
enum:SOUND_POSITIONAL prop:positional type:bool default:true,
269-
enum:SOUND_LOCAL_ONLY prop:localOnly type:bool default:false,
270+
enum:SOUND_LOCAL_ONLY prop:localOnly type:bool default:false,
271+
Script
272+
enum:SCRIPT_URL prop:scriptURL type:QString default:"" urlPermission common,
273+
enum:SOUND_ENABLED prop:enabled type:bool default:true,

libraries/entities/src/EntityItemPropertiesDocs.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@
200200
* @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine}
201201
* @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox}
202202
* @see {@link Entities.EntityProperties-ProceduralParticleEffect|EntityProperties-ProceduralParticleEffect}
203+
* @see {@link Entities.EntityProperties-Script|EntityProperties-Script}
203204
* @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape}
204205
* @see {@link Entities.EntityProperties-Sound|EntityProperties-Sound}
205206
* @see {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere}
@@ -691,6 +692,23 @@
691692
* });
692693
*/
693694

695+
/*@jsdoc
696+
* The <code>"Script"</code> {@link Entities.EntityType|EntityType} runs an entity script from a URL on its parent. It has properties
697+
* in addition to the common {@link Entities.EntityProperties|EntityProperties}.
698+
*
699+
* @typedef {object} Entities.EntityProperties-Script
700+
* @property {string} scriptURL="" - The URL of the entity script to run, as a js file.
701+
* @property {boolean} enabled=true - Whether or not the script should run.
702+
* @example <caption>Create a Script entity.</caption>
703+
* var entity = Entities.addEntity({
704+
* type: "Script",
705+
* script: TODO,
706+
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })),
707+
* rotation: MyAvatar.orientation,
708+
* lifetime: 300 // Delete after 5 minutes.
709+
* });
710+
*/
711+
694712
/*@jsdoc
695713
* The <code>"Shape"</code> {@link Entities.EntityType|EntityType} displays an entity of a specified <code>shape</code>.
696714
* It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}.

libraries/entities/src/EntityScriptingInterface.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,8 +1574,9 @@ bool EntityPropertyMetadataRequest::script(EntityItemID entityID, const ScriptVa
15741574
});
15751575
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
15761576
entityScriptingInterface->withEntitiesScriptEngine([&](std::shared_ptr<EntitiesScriptEngineProvider> entitiesScriptEngine) {
1577-
if (entitiesScriptEngine) {
1578-
request->setFuture(entitiesScriptEngine->getLocalEntityScriptDetails(entityID));
1577+
EntityItemPointer entity = entityScriptingInterface->getEntityTree()->findEntityByEntityItemID(entityID);
1578+
if (entitiesScriptEngine && entity) {
1579+
request->setFuture(entitiesScriptEngine->getLocalEntityScriptDetails(entityID, entity->getScript()));
15791580
}
15801581
}, entityID);
15811582
if (!request->isStarted()) {

libraries/entities/src/EntityTreeElement.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori
258258
} else {
259259
// if the entity type doesn't support a detailed intersection, then just return the non-AABox results
260260
// Never intersect with particle or sound entities
261-
if (localDistance < distance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::ProceduralParticleEffect && entity->getType() != EntityTypes::Sound)) {
261+
if (localDistance < distance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::ProceduralParticleEffect &&
262+
entity->getType() != EntityTypes::Sound && entity->getType() != EntityTypes::Script)) {
262263
distance = localDistance;
263264
face = localFace;
264265
surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f));
@@ -410,7 +411,8 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3
410411
} else {
411412
// if the entity type doesn't support a detailed intersection, then just return the non-AABox results
412413
// Never intersect with particle or sound entities
413-
if (localDistance < parabolicDistance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::ProceduralParticleEffect && entity->getType() != EntityTypes::Sound)) {
414+
if (localDistance < parabolicDistance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::ProceduralParticleEffect &&
415+
entity->getType() != EntityTypes::Sound && entity->getType() != EntityTypes::Script)) {
414416
parabolicDistance = localDistance;
415417
face = localFace;
416418
surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f));

libraries/entities/src/EntityTypes.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "ZoneEntityItem.h"
3838
#include "MaterialEntityItem.h"
3939
#include "SoundEntityItem.h"
40+
#include "ScriptEntityItem.h"
4041

4142
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
4243
QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
@@ -64,6 +65,7 @@ REGISTER_ENTITY_TYPE(Light)
6465
REGISTER_ENTITY_TYPE(Zone)
6566
REGISTER_ENTITY_TYPE(Material)
6667
REGISTER_ENTITY_TYPE(Sound)
68+
REGISTER_ENTITY_TYPE(Script)
6769

6870
bool EntityTypes::typeIsValid(EntityType type) {
6971
return type > EntityType::Unknown && type <= EntityType::NUM_TYPES;

0 commit comments

Comments
 (0)