Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/system/ComponentManager.cpp
/* Copyright (C) 2020 Wildfire Games. | /* Copyright (C) 2020 Wildfire Games. | ||||
Stan: year. | |||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
Show All 9 Lines | |||||
#include "ComponentManager.h" | #include "ComponentManager.h" | ||||
#include "lib/utf8.h" | #include "lib/utf8.h" | ||||
#include "ps/CLogger.h" | #include "ps/CLogger.h" | ||||
#include "ps/Filesystem.h" | #include "ps/Filesystem.h" | ||||
#include "ps/Profile.h" | #include "ps/Profile.h" | ||||
#include "ps/scripting/JSInterface_VFS.h" | #include "ps/scripting/JSInterface_VFS.h" | ||||
#include "scriptinterface/FunctionWrapper.h" | |||||
#include "simulation2/components/ICmpTemplateManager.h" | #include "simulation2/components/ICmpTemplateManager.h" | ||||
#include "simulation2/MessageTypes.h" | #include "simulation2/MessageTypes.h" | ||||
#include "simulation2/system/DynamicSubscription.h" | #include "simulation2/system/DynamicSubscription.h" | ||||
#include "simulation2/system/IComponent.h" | #include "simulation2/system/IComponent.h" | ||||
#include "simulation2/system/ParamNode.h" | #include "simulation2/system/ParamNode.h" | ||||
#include "simulation2/system/SimContext.h" | #include "simulation2/system/SimContext.h" | ||||
/** | /** | ||||
Show All 28 Lines | CComponentManager::CComponentManager(CSimContext& context, shared_ptr<ScriptRuntime> rt, bool skipScriptFunctions) : | ||||
m_ScriptInterface.SetCallbackData(static_cast<void*> (this)); | m_ScriptInterface.SetCallbackData(static_cast<void*> (this)); | ||||
m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG); | m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG); | ||||
// For component script tests, the test system sets up its own scripted implementation of | // For component script tests, the test system sets up its own scripted implementation of | ||||
// these functions, so we skip registering them here in those cases | // these functions, so we skip registering them here in those cases | ||||
if (!skipScriptFunctions) | if (!skipScriptFunctions) | ||||
{ | { | ||||
JSI_VFS::RegisterScriptFunctions_Simulation(m_ScriptInterface); | JSI_VFS::RegisterScriptFunctions_Simulation(m_ScriptInterface); | ||||
m_ScriptInterface.RegisterFunction<void, int, std::string, JS::HandleValue, CComponentManager::Script_RegisterComponentType> ("RegisterComponentType"); | #define Register(name) ScriptWrapper::WrapAndRegister<decltype(&CComponentManager::Script_##name), &CComponentManager::Script_##name, \ | ||||
m_ScriptInterface.RegisterFunction<void, int, std::string, JS::HandleValue, CComponentManager::Script_RegisterSystemComponentType> ("RegisterSystemComponentType"); | CComponentManager, CComponentManager::GetComponentManagerFromCxPrivateData>(m_ScriptInterface, #name); | ||||
m_ScriptInterface.RegisterFunction<void, int, std::string, JS::HandleValue, CComponentManager::Script_ReRegisterComponentType> ("ReRegisterComponentType"); | Register(RegisterComponentType); | ||||
m_ScriptInterface.RegisterFunction<void, std::string, CComponentManager::Script_RegisterInterface> ("RegisterInterface"); | Register(RegisterSystemComponentType); | ||||
m_ScriptInterface.RegisterFunction<void, std::string, CComponentManager::Script_RegisterMessageType> ("RegisterMessageType"); | Register(ReRegisterComponentType); | ||||
m_ScriptInterface.RegisterFunction<void, std::string, JS::HandleValue, CComponentManager::Script_RegisterGlobal> ("RegisterGlobal"); | Register(RegisterInterface); | ||||
m_ScriptInterface.RegisterFunction<IComponent*, int, int, CComponentManager::Script_QueryInterface> ("QueryInterface"); | Register(RegisterMessageType); | ||||
m_ScriptInterface.RegisterFunction<std::vector<int>, int, CComponentManager::Script_GetEntitiesWithInterface> ("GetEntitiesWithInterface"); | Register(RegisterGlobal); | ||||
m_ScriptInterface.RegisterFunction<std::vector<IComponent*>, int, CComponentManager::Script_GetComponentsWithInterface> ("GetComponentsWithInterface"); | Register(QueryInterface); | ||||
m_ScriptInterface.RegisterFunction<void, int, int, JS::HandleValue, CComponentManager::Script_PostMessage> ("PostMessage"); | Register(GetEntitiesWithInterface); | ||||
m_ScriptInterface.RegisterFunction<void, int, JS::HandleValue, CComponentManager::Script_BroadcastMessage> ("BroadcastMessage"); | Register(GetComponentsWithInterface); | ||||
m_ScriptInterface.RegisterFunction<int, std::string, CComponentManager::Script_AddEntity> ("AddEntity"); | Register(PostMessage); | ||||
m_ScriptInterface.RegisterFunction<int, std::string, CComponentManager::Script_AddLocalEntity> ("AddLocalEntity"); | Register(BroadcastMessage); | ||||
m_ScriptInterface.RegisterFunction<void, int, CComponentManager::Script_DestroyEntity> ("DestroyEntity"); | Register(AddEntity); | ||||
m_ScriptInterface.RegisterFunction<void, CComponentManager::Script_FlushDestroyedEntities> ("FlushDestroyedEntities"); | Register(AddLocalEntity); | ||||
Register(DestroyEntity); | |||||
Register(FlushDestroyedEntities); | |||||
#undef Register | |||||
} | } | ||||
// Globalscripts may use VFS script functions | // Globalscripts may use VFS script functions | ||||
m_ScriptInterface.LoadGlobalScripts(); | m_ScriptInterface.LoadGlobalScripts(); | ||||
// Define MT_*, IID_* as script globals, and store their names | // Define MT_*, IID_* as script globals, and store their names | ||||
#define MESSAGE(name) m_ScriptInterface.SetGlobal("MT_" #name, (int)MT_##name); | #define MESSAGE(name) m_ScriptInterface.SetGlobal("MT_" #name, (int)MT_##name); | ||||
#define INTERFACE(name) \ | #define INTERFACE(name) \ | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | if (loadOk != PSRETURN_OK) // VFS will log the failed file and the reason | ||||
return false; | return false; | ||||
std::string content = file.DecodeUTF8(); // assume it's UTF-8 | std::string content = file.DecodeUTF8(); // assume it's UTF-8 | ||||
bool ok = m_ScriptInterface.LoadScript(filename, content); | bool ok = m_ScriptInterface.LoadScript(filename, content); | ||||
m_CurrentlyHotloading = false; | m_CurrentlyHotloading = false; | ||||
return ok; | return ok; | ||||
} | } | ||||
void CComponentManager::Script_RegisterComponentType_Common(ScriptInterface::CxPrivate* pCxPrivate, int iid, const std::string& cname, JS::HandleValue ctor, bool reRegister, bool systemComponent) | CComponentManager* CComponentManager::GetComponentManagerFromCxPrivateData(JSContext* cx, JS::CallArgs&) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | return static_cast<CComponentManager*>(ScriptInterface::GetScriptInterfaceAndCBData(cx)->pCBData); | ||||
JSContext* cx = componentManager->m_ScriptInterface.GetContext(); | } | ||||
void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::string& cname, JS::HandleValue ctor, bool reRegister, bool systemComponent) | |||||
{ | |||||
JSContext* cx = m_ScriptInterface.GetContext(); | |||||
JSAutoRequest rq(cx); | JSAutoRequest rq(cx); | ||||
// Find the C++ component that wraps the interface | // Find the C++ component that wraps the interface | ||||
int cidWrapper = componentManager->GetScriptWrapper(iid); | int cidWrapper = GetScriptWrapper(iid); | ||||
if (cidWrapper == CID__Invalid) | if (cidWrapper == CID__Invalid) | ||||
{ | { | ||||
componentManager->m_ScriptInterface.ReportError("Invalid interface id"); | m_ScriptInterface.ReportError("Invalid interface id"); | ||||
return; | return; | ||||
} | } | ||||
const ComponentType& ctWrapper = componentManager->m_ComponentTypesById[cidWrapper]; | const ComponentType& ctWrapper = m_ComponentTypesById[cidWrapper]; | ||||
bool mustReloadComponents = false; // for hotloading | bool mustReloadComponents = false; // for hotloading | ||||
ComponentTypeId cid = componentManager->LookupCID(cname); | ComponentTypeId cid = LookupCID(cname); | ||||
if (cid == CID__Invalid) | if (cid == CID__Invalid) | ||||
{ | { | ||||
if (reRegister) | if (reRegister) | ||||
{ | { | ||||
componentManager->m_ScriptInterface.ReportError(("ReRegistering component type that was not registered before '" + cname + "'").c_str()); | m_ScriptInterface.ReportError(("ReRegistering component type that was not registered before '" + cname + "'").c_str()); | ||||
return; | return; | ||||
} | } | ||||
// Allocate a new cid number | // Allocate a new cid number | ||||
cid = componentManager->m_NextScriptComponentTypeId++; | cid = m_NextScriptComponentTypeId++; | ||||
componentManager->m_ComponentTypeIdsByName[cname] = cid; | m_ComponentTypeIdsByName[cname] = cid; | ||||
if (systemComponent) | if (systemComponent) | ||||
componentManager->MarkScriptedComponentForSystemEntity(cid); | MarkScriptedComponentForSystemEntity(cid); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
// Component type is already loaded, so do hotloading: | // Component type is already loaded, so do hotloading: | ||||
if (!componentManager->m_CurrentlyHotloading && !reRegister) | if (!m_CurrentlyHotloading && !reRegister) | ||||
{ | { | ||||
componentManager->m_ScriptInterface.ReportError(("Registering component type with already-registered name '" + cname + "'").c_str()); | m_ScriptInterface.ReportError(("Registering component type with already-registered name '" + cname + "'").c_str()); | ||||
return; | return; | ||||
} | } | ||||
const ComponentType& ctPrevious = componentManager->m_ComponentTypesById[cid]; | const ComponentType& ctPrevious = m_ComponentTypesById[cid]; | ||||
// We can only replace scripted component types, not native ones | // We can only replace scripted component types, not native ones | ||||
if (ctPrevious.type != CT_Script) | if (ctPrevious.type != CT_Script) | ||||
{ | { | ||||
componentManager->m_ScriptInterface.ReportError(("Loading script component type with same name '" + cname + "' as native component").c_str()); | m_ScriptInterface.ReportError(("Loading script component type with same name '" + cname + "' as native component").c_str()); | ||||
return; | return; | ||||
} | } | ||||
// We don't support changing the IID of a component type (it would require fiddling | // We don't support changing the IID of a component type (it would require fiddling | ||||
// around with m_ComponentsByInterface and being careful to guarantee uniqueness per entity) | // around with m_ComponentsByInterface and being careful to guarantee uniqueness per entity) | ||||
if (ctPrevious.iid != iid) | if (ctPrevious.iid != iid) | ||||
{ | { | ||||
// ...though it only matters if any components exist with this type | // ...though it only matters if any components exist with this type | ||||
if (!componentManager->m_ComponentsByTypeId[cid].empty()) | if (!m_ComponentsByTypeId[cid].empty()) | ||||
{ | { | ||||
componentManager->m_ScriptInterface.ReportError("Hotloading script component type mustn't change interface ID"); | m_ScriptInterface.ReportError("Hotloading script component type mustn't change interface ID"); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
// Remove the old component type's message subscriptions | // Remove the old component type's message subscriptions | ||||
std::map<MessageTypeId, std::vector<ComponentTypeId> >::iterator it; | std::map<MessageTypeId, std::vector<ComponentTypeId> >::iterator it; | ||||
for (it = componentManager->m_LocalMessageSubscriptions.begin(); it != componentManager->m_LocalMessageSubscriptions.end(); ++it) | for (it = m_LocalMessageSubscriptions.begin(); it != m_LocalMessageSubscriptions.end(); ++it) | ||||
{ | { | ||||
std::vector<ComponentTypeId>& types = it->second; | std::vector<ComponentTypeId>& types = it->second; | ||||
std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid); | std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid); | ||||
if (ctit != types.end()) | if (ctit != types.end()) | ||||
types.erase(ctit); | types.erase(ctit); | ||||
} | } | ||||
for (it = componentManager->m_GlobalMessageSubscriptions.begin(); it != componentManager->m_GlobalMessageSubscriptions.end(); ++it) | for (it = m_GlobalMessageSubscriptions.begin(); it != m_GlobalMessageSubscriptions.end(); ++it) | ||||
{ | { | ||||
std::vector<ComponentTypeId>& types = it->second; | std::vector<ComponentTypeId>& types = it->second; | ||||
std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid); | std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid); | ||||
if (ctit != types.end()) | if (ctit != types.end()) | ||||
types.erase(ctit); | types.erase(ctit); | ||||
} | } | ||||
mustReloadComponents = true; | mustReloadComponents = true; | ||||
} | } | ||||
JS::RootedValue protoVal(cx); | JS::RootedValue protoVal(cx); | ||||
if (!componentManager->m_ScriptInterface.GetProperty(ctor, "prototype", &protoVal)) | if (!m_ScriptInterface.GetProperty(ctor, "prototype", &protoVal)) | ||||
{ | { | ||||
componentManager->m_ScriptInterface.ReportError("Failed to get property 'prototype'"); | m_ScriptInterface.ReportError("Failed to get property 'prototype'"); | ||||
return; | return; | ||||
} | } | ||||
if (!protoVal.isObject()) | if (!protoVal.isObject()) | ||||
{ | { | ||||
componentManager->m_ScriptInterface.ReportError("Component has no constructor"); | m_ScriptInterface.ReportError("Component has no constructor"); | ||||
return; | return; | ||||
} | } | ||||
std::string schema = "<empty/>"; | std::string schema = "<empty/>"; | ||||
if (componentManager->m_ScriptInterface.HasProperty(protoVal, "Schema")) | if (m_ScriptInterface.HasProperty(protoVal, "Schema")) | ||||
componentManager->m_ScriptInterface.GetProperty(protoVal, "Schema", schema); | m_ScriptInterface.GetProperty(protoVal, "Schema", schema); | ||||
// Construct a new ComponentType, using the wrapper's alloc functions | // Construct a new ComponentType, using the wrapper's alloc functions | ||||
ComponentType ct{ | ComponentType ct{ | ||||
CT_Script, | CT_Script, | ||||
iid, | iid, | ||||
ctWrapper.alloc, | ctWrapper.alloc, | ||||
ctWrapper.dealloc, | ctWrapper.dealloc, | ||||
cname, | cname, | ||||
schema, | schema, | ||||
DefPersistentRooted<JS::Value>(cx, ctor) | DefPersistentRooted<JS::Value>(cx, ctor) | ||||
}; | }; | ||||
componentManager->m_ComponentTypesById[cid] = std::move(ct); | m_ComponentTypesById[cid] = std::move(ct); | ||||
componentManager->m_CurrentComponent = cid; // needed by Subscribe | m_CurrentComponent = cid; // needed by Subscribe | ||||
// Find all the ctor prototype's On* methods, and subscribe to the appropriate messages: | // Find all the ctor prototype's On* methods, and subscribe to the appropriate messages: | ||||
std::vector<std::string> methods; | std::vector<std::string> methods; | ||||
if (!componentManager->m_ScriptInterface.EnumeratePropertyNames(protoVal, false, methods)) | if (!m_ScriptInterface.EnumeratePropertyNames(protoVal, false, methods)) | ||||
{ | { | ||||
componentManager->m_ScriptInterface.ReportError("Failed to enumerate component properties."); | m_ScriptInterface.ReportError("Failed to enumerate component properties."); | ||||
return; | return; | ||||
} | } | ||||
for (std::vector<std::string>::const_iterator it = methods.begin(); it != methods.end(); ++it) | for (std::vector<std::string>::const_iterator it = methods.begin(); it != methods.end(); ++it) | ||||
{ | { | ||||
// TODO C++17: string_view | // TODO C++17: string_view | ||||
if (strncmp((it->c_str()), "On", 2) != 0) | if (strncmp((it->c_str()), "On", 2) != 0) | ||||
continue; | continue; | ||||
std::string name = (*it).substr(2); // strip the "On" prefix | std::string name = (*it).substr(2); // strip the "On" prefix | ||||
// Handle "OnGlobalFoo" functions specially | // Handle "OnGlobalFoo" functions specially | ||||
bool isGlobal = false; | bool isGlobal = false; | ||||
if (strncmp(name.c_str(), "Global", 6) == 0) | if (strncmp(name.c_str(), "Global", 6) == 0) | ||||
{ | { | ||||
isGlobal = true; | isGlobal = true; | ||||
name = name.substr(6); | name = name.substr(6); | ||||
} | } | ||||
std::map<std::string, MessageTypeId>::const_iterator mit = componentManager->m_MessageTypeIdsByName.find(name); | std::map<std::string, MessageTypeId>::const_iterator mit = m_MessageTypeIdsByName.find(name); | ||||
if (mit == componentManager->m_MessageTypeIdsByName.end()) | if (mit == m_MessageTypeIdsByName.end()) | ||||
{ | { | ||||
componentManager->m_ScriptInterface.ReportError(("Registered component has unrecognized '" + *it + "' message handler method").c_str()); | m_ScriptInterface.ReportError(("Registered component has unrecognized '" + *it + "' message handler method").c_str()); | ||||
return; | return; | ||||
} | } | ||||
if (isGlobal) | if (isGlobal) | ||||
componentManager->SubscribeGloballyToMessageType(mit->second); | SubscribeGloballyToMessageType(mit->second); | ||||
else | else | ||||
componentManager->SubscribeToMessageType(mit->second); | SubscribeToMessageType(mit->second); | ||||
} | } | ||||
componentManager->m_CurrentComponent = CID__Invalid; | m_CurrentComponent = CID__Invalid; | ||||
if (mustReloadComponents) | if (mustReloadComponents) | ||||
{ | { | ||||
// For every script component with this cid, we need to switch its | // For every script component with this cid, we need to switch its | ||||
// prototype from the old constructor's prototype property to the new one's | // prototype from the old constructor's prototype property to the new one's | ||||
const std::map<entity_id_t, IComponent*>& comps = componentManager->m_ComponentsByTypeId[cid]; | const std::map<entity_id_t, IComponent*>& comps = m_ComponentsByTypeId[cid]; | ||||
std::map<entity_id_t, IComponent*>::const_iterator eit = comps.begin(); | std::map<entity_id_t, IComponent*>::const_iterator eit = comps.begin(); | ||||
for (; eit != comps.end(); ++eit) | for (; eit != comps.end(); ++eit) | ||||
{ | { | ||||
JS::RootedValue instance(cx, eit->second->GetJSInstance()); | JS::RootedValue instance(cx, eit->second->GetJSInstance()); | ||||
if (!instance.isNull()) | if (!instance.isNull()) | ||||
componentManager->m_ScriptInterface.SetPrototype(instance, protoVal); | m_ScriptInterface.SetPrototype(instance, protoVal); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void CComponentManager::Script_RegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, const std::string& cname, JS::HandleValue ctor) | void CComponentManager::Script_RegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | Script_RegisterComponentType_Common(iid, cname, ctor, false, false); | ||||
componentManager->Script_RegisterComponentType_Common(pCxPrivate, iid, cname, ctor, false, false); | m_ScriptInterface.SetGlobal(cname.c_str(), ctor, m_CurrentlyHotloading); | ||||
componentManager->m_ScriptInterface.SetGlobal(cname.c_str(), ctor, componentManager->m_CurrentlyHotloading); | |||||
} | } | ||||
void CComponentManager::Script_RegisterSystemComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, const std::string& cname, JS::HandleValue ctor) | void CComponentManager::Script_RegisterSystemComponentType(int iid, const std::string& cname, JS::HandleValue ctor) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | Script_RegisterComponentType_Common(iid, cname, ctor, false, true); | ||||
componentManager->Script_RegisterComponentType_Common(pCxPrivate, iid, cname, ctor, false, true); | m_ScriptInterface.SetGlobal(cname.c_str(), ctor, m_CurrentlyHotloading); | ||||
componentManager->m_ScriptInterface.SetGlobal(cname.c_str(), ctor, componentManager->m_CurrentlyHotloading); | |||||
} | } | ||||
void CComponentManager::Script_ReRegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, const std::string& cname, JS::HandleValue ctor) | void CComponentManager::Script_ReRegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor) | ||||
{ | { | ||||
Script_RegisterComponentType_Common(pCxPrivate, iid, cname, ctor, true, false); | Script_RegisterComponentType_Common(iid, cname, ctor, true, false); | ||||
} | } | ||||
void CComponentManager::Script_RegisterInterface(ScriptInterface::CxPrivate* pCxPrivate, const std::string& name) | void CComponentManager::Script_RegisterInterface(const std::string& name) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | std::map<std::string, InterfaceId>::iterator it = m_InterfaceIdsByName.find(name); | ||||
if (it != m_InterfaceIdsByName.end()) | |||||
std::map<std::string, InterfaceId>::iterator it = componentManager->m_InterfaceIdsByName.find(name); | |||||
if (it != componentManager->m_InterfaceIdsByName.end()) | |||||
{ | { | ||||
// Redefinitions are fine (and just get ignored) when hotloading; otherwise | // Redefinitions are fine (and just get ignored) when hotloading; otherwise | ||||
// they're probably unintentional and should be reported | // they're probably unintentional and should be reported | ||||
if (!componentManager->m_CurrentlyHotloading) | if (!m_CurrentlyHotloading) | ||||
componentManager->m_ScriptInterface.ReportError(("Registering interface with already-registered name '" + name + "'").c_str()); | m_ScriptInterface.ReportError(("Registering interface with already-registered name '" + name + "'").c_str()); | ||||
return; | return; | ||||
} | } | ||||
// IIDs start at 1, so size+1 is the next unused one | // IIDs start at 1, so size+1 is the next unused one | ||||
size_t id = componentManager->m_InterfaceIdsByName.size() + 1; | size_t id = m_InterfaceIdsByName.size() + 1; | ||||
componentManager->m_InterfaceIdsByName[name] = (InterfaceId)id; | m_InterfaceIdsByName[name] = (InterfaceId)id; | ||||
componentManager->m_ComponentsByInterface.resize(id+1); // add one so we can index by InterfaceId | m_ComponentsByInterface.resize(id+1); // add one so we can index by InterfaceId | ||||
componentManager->m_ScriptInterface.SetGlobal(("IID_" + name).c_str(), (int)id); | m_ScriptInterface.SetGlobal(("IID_" + name).c_str(), (int)id); | ||||
} | } | ||||
void CComponentManager::Script_RegisterMessageType(ScriptInterface::CxPrivate* pCxPrivate, const std::string& name) | void CComponentManager::Script_RegisterMessageType(const std::string& name) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | std::map<std::string, MessageTypeId>::iterator it = m_MessageTypeIdsByName.find(name); | ||||
if (it != m_MessageTypeIdsByName.end()) | |||||
std::map<std::string, MessageTypeId>::iterator it = componentManager->m_MessageTypeIdsByName.find(name); | |||||
if (it != componentManager->m_MessageTypeIdsByName.end()) | |||||
{ | { | ||||
// Redefinitions are fine (and just get ignored) when hotloading; otherwise | // Redefinitions are fine (and just get ignored) when hotloading; otherwise | ||||
// they're probably unintentional and should be reported | // they're probably unintentional and should be reported | ||||
if (!componentManager->m_CurrentlyHotloading) | if (!m_CurrentlyHotloading) | ||||
componentManager->m_ScriptInterface.ReportError(("Registering message type with already-registered name '" + name + "'").c_str()); | m_ScriptInterface.ReportError(("Registering message type with already-registered name '" + name + "'").c_str()); | ||||
return; | return; | ||||
} | } | ||||
// MTIDs start at 1, so size+1 is the next unused one | // MTIDs start at 1, so size+1 is the next unused one | ||||
size_t id = componentManager->m_MessageTypeIdsByName.size() + 1; | size_t id = m_MessageTypeIdsByName.size() + 1; | ||||
componentManager->RegisterMessageType((MessageTypeId)id, name.c_str()); | RegisterMessageType((MessageTypeId)id, name.c_str()); | ||||
componentManager->m_ScriptInterface.SetGlobal(("MT_" + name).c_str(), (int)id); | m_ScriptInterface.SetGlobal(("MT_" + name).c_str(), (int)id); | ||||
} | } | ||||
void CComponentManager::Script_RegisterGlobal(ScriptInterface::CxPrivate* pCxPrivate, const std::string& name, JS::HandleValue value) | void CComponentManager::Script_RegisterGlobal(const std::string& name, JS::HandleValue value) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | m_ScriptInterface.SetGlobal(name.c_str(), value, m_CurrentlyHotloading); | ||||
componentManager->m_ScriptInterface.SetGlobal(name.c_str(), value, componentManager->m_CurrentlyHotloading); | |||||
} | } | ||||
IComponent* CComponentManager::Script_QueryInterface(ScriptInterface::CxPrivate* pCxPrivate, int ent, int iid) | // TESTDIRECT_CALL | ||||
IComponent* CComponentManager::Script_QueryInterface(int ent, int iid) | |||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | return QueryInterface((entity_id_t)ent, iid); | ||||
IComponent* component = componentManager->QueryInterface((entity_id_t)ent, iid); | |||||
return component; | |||||
} | } | ||||
std::vector<int> CComponentManager::Script_GetEntitiesWithInterface(ScriptInterface::CxPrivate* pCxPrivate, int iid) | std::vector<int> CComponentManager::Script_GetEntitiesWithInterface(int iid) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | |||||
std::vector<int> ret; | std::vector<int> ret; | ||||
const InterfaceListUnordered& ents = componentManager->GetEntitiesWithInterfaceUnordered(iid); | const InterfaceListUnordered& ents = GetEntitiesWithInterfaceUnordered(iid); | ||||
for (InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) | for (InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) | ||||
if (!ENTITY_IS_LOCAL(it->first)) | if (!ENTITY_IS_LOCAL(it->first)) | ||||
ret.push_back(it->first); | ret.push_back(it->first); | ||||
std::sort(ret.begin(), ret.end()); | std::sort(ret.begin(), ret.end()); | ||||
return ret; | return ret; | ||||
} | } | ||||
std::vector<IComponent*> CComponentManager::Script_GetComponentsWithInterface(ScriptInterface::CxPrivate* pCxPrivate, int iid) | std::vector<IComponent*> CComponentManager::Script_GetComponentsWithInterface(int iid) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | |||||
std::vector<IComponent*> ret; | std::vector<IComponent*> ret; | ||||
InterfaceList ents = componentManager->GetEntitiesWithInterface(iid); | InterfaceList ents = GetEntitiesWithInterface(iid); | ||||
for (InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it) | for (InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it) | ||||
ret.push_back(it->second); // TODO: maybe we should exclude local entities | ret.push_back(it->second); // TODO: maybe we should exclude local entities | ||||
return ret; | return ret; | ||||
} | } | ||||
CMessage* CComponentManager::ConstructMessage(int mtid, JS::HandleValue data) | CMessage* CComponentManager::ConstructMessage(int mtid, JS::HandleValue data) | ||||
{ | { | ||||
if (mtid == MT__Invalid || mtid > (int)m_MessageTypeIdsByName.size()) // (IDs start at 1 so use '>' here) | if (mtid == MT__Invalid || mtid > (int)m_MessageTypeIdsByName.size()) // (IDs start at 1 so use '>' here) | ||||
LOGERROR("PostMessage with invalid message type ID '%d'", mtid); | LOGERROR("PostMessage with invalid message type ID '%d'", mtid); | ||||
if (mtid < MT__LastNative) | if (mtid < MT__LastNative) | ||||
{ | { | ||||
return CMessageFromJSVal(mtid, m_ScriptInterface, data); | return CMessageFromJSVal(mtid, m_ScriptInterface, data); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
return new CMessageScripted(m_ScriptInterface, mtid, m_MessageTypeNamesById[mtid], data); | return new CMessageScripted(m_ScriptInterface, mtid, m_MessageTypeNamesById[mtid], data); | ||||
} | } | ||||
} | } | ||||
void CComponentManager::Script_PostMessage(ScriptInterface::CxPrivate* pCxPrivate, int ent, int mtid, JS::HandleValue data) | void CComponentManager::Script_PostMessage(int ent, int mtid, JS::HandleValue data) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | CMessage* msg = ConstructMessage(mtid, data); | ||||
CMessage* msg = componentManager->ConstructMessage(mtid, data); | |||||
if (!msg) | if (!msg) | ||||
return; // error | return; // error | ||||
componentManager->PostMessage(ent, *msg); | PostMessage(ent, *msg); | ||||
delete msg; | delete msg; | ||||
} | } | ||||
void CComponentManager::Script_BroadcastMessage(ScriptInterface::CxPrivate* pCxPrivate, int mtid, JS::HandleValue data) | void CComponentManager::Script_BroadcastMessage(int mtid, JS::HandleValue data) | ||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | CMessage* msg = ConstructMessage(mtid, data); | ||||
CMessage* msg = componentManager->ConstructMessage(mtid, data); | |||||
if (!msg) | if (!msg) | ||||
return; // error | return; // error | ||||
componentManager->BroadcastMessage(*msg); | BroadcastMessage(*msg); | ||||
delete msg; | delete msg; | ||||
} | } | ||||
int CComponentManager::Script_AddEntity(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName) | // TESTDIRECT_CALL | ||||
int CComponentManager::Script_AddEntity(const std::string& templateName) | |||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | |||||
std::wstring name(templateName.begin(), templateName.end()); | std::wstring name(templateName.begin(), templateName.end()); | ||||
// TODO: should validate the string to make sure it doesn't contain scary characters | // TODO: should validate the string to make sure it doesn't contain scary characters | ||||
// that will let it access non-component-template files | // that will let it access non-component-template files | ||||
entity_id_t ent = componentManager->AddEntity(name, componentManager->AllocateNewEntity()); | entity_id_t ent = AddEntity(name, AllocateNewEntity()); | ||||
return (int)ent; | return (int)ent; | ||||
} | } | ||||
int CComponentManager::Script_AddLocalEntity(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName) | // TESTDIRECT_CALL | ||||
int CComponentManager::Script_AddLocalEntity(const std::string& templateName) | |||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | |||||
std::wstring name(templateName.begin(), templateName.end()); | std::wstring name(templateName.begin(), templateName.end()); | ||||
// TODO: should validate the string to make sure it doesn't contain scary characters | // TODO: should validate the string to make sure it doesn't contain scary characters | ||||
// that will let it access non-component-template files | // that will let it access non-component-template files | ||||
entity_id_t ent = componentManager->AddEntity(name, componentManager->AllocateNewLocalEntity()); | entity_id_t ent = AddEntity(name, AllocateNewLocalEntity()); | ||||
return (int)ent; | return (int)ent; | ||||
} | } | ||||
void CComponentManager::Script_DestroyEntity(ScriptInterface::CxPrivate* pCxPrivate, int ent) | // TESTDIRECT_CALL | ||||
void CComponentManager::Script_DestroyEntity(int ent) | |||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | DestroyComponentsSoon(ent); | ||||
componentManager->DestroyComponentsSoon(ent); | |||||
} | } | ||||
void CComponentManager::Script_FlushDestroyedEntities(ScriptInterface::CxPrivate *pCxPrivate) | // TESTDIRECT_CALL | ||||
void CComponentManager::Script_FlushDestroyedEntities() | |||||
{ | { | ||||
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); | FlushDestroyedComponents(); | ||||
componentManager->FlushDestroyedComponents(); | |||||
} | } | ||||
void CComponentManager::ResetState() | void CComponentManager::ResetState() | ||||
{ | { | ||||
// Delete all dynamic message subscriptions | // Delete all dynamic message subscriptions | ||||
m_DynamicMessageSubscriptionsNonsync.clear(); | m_DynamicMessageSubscriptionsNonsync.clear(); | ||||
m_DynamicMessageSubscriptionsNonsyncByComponent.clear(); | m_DynamicMessageSubscriptionsNonsyncByComponent.clear(); | ||||
▲ Show 20 Lines • Show All 678 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
year.