18 #include "precompiled.h"
58 m_SimContext(context), m_CurrentlyHotloading(false)
70 if (!skipScriptFunctions)
90 #define MESSAGE(name) m_ScriptInterface.SetGlobal("MT_" #name, (int)MT_##name);
91 #define INTERFACE(name) \
92 m_ScriptInterface.SetGlobal("IID_" #name, (int)IID_##name); \
93 m_InterfaceIdsByName[#name] = IID_##name;
94 #define COMPONENT(name)
115 #define MESSAGE(name) \
116 RegisterMessageType(MT_##name, #name);
117 #define INTERFACE(name) \
118 extern void RegisterComponentInterface_##name(ScriptInterface&); \
119 RegisterComponentInterface_##name(m_ScriptInterface);
120 #define COMPONENT(name) \
121 extern void RegisterComponentType_##name(CComponentManager&); \
122 m_CurrentComponent = CID_##name; \
123 RegisterComponentType_##name(*this);
159 bool mustReloadComponents =
false;
189 if (ctPrevious.
iid != iid)
200 std::map<MessageTypeId, std::vector<ComponentTypeId> >::iterator it;
203 std::vector<ComponentTypeId>& types = it->second;
204 std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid);
205 if (ctit != types.end())
210 std::vector<ComponentTypeId>& types = it->second;
211 std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid);
212 if (ctit != types.end())
216 mustReloadComponents =
true;
219 std::string schema =
"<empty/>";
250 std::vector<std::string> methods;
254 for (std::vector<std::string>::const_iterator it = methods.begin(); it != methods.end(); ++it)
256 std::string name = (*it).substr(2);
259 bool isGlobal =
false;
260 if (name.substr(0, 6) ==
"Global")
263 name = name.substr(6);
266 std::map<std::string, MessageTypeId>::const_iterator mit = componentManager->
m_MessageTypeIdsByName.find(name);
269 std::string msg =
"Registered component has unrecognised '" + *it +
"' message handler method";
282 if (mustReloadComponents)
287 std::map<entity_id_t, IComponent*>::const_iterator eit = comps.begin();
288 for (; eit != comps.end(); ++eit)
290 jsval instance = eit->second->GetJSInstance();
291 if (!JSVAL_IS_NULL(instance))
301 std::map<std::string, InterfaceId>::iterator it = componentManager->
m_InterfaceIdsByName.find(name);
358 std::vector<int> ret;
360 for (InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
362 ret.push_back(it->first);
363 std::sort(ret.begin(), ret.end());
371 std::vector<IComponent*> ret;
373 for (InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
374 ret.push_back(it->second);
381 LOGERROR(L
"PostMessage with invalid message type ID '%d'", mtid);
424 std::wstring name(templateName.begin(), templateName.end());
436 std::wstring name(templateName.begin(), templateName.end());
454 std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::iterator iit =
m_ComponentsByTypeId.begin();
457 std::map<entity_id_t, IComponent*>::iterator eit = iit->second.begin();
458 for (; eit != iit->second.end(); ++eit)
460 eit->second->Deinit();
465 std::vector<boost::unordered_map<entity_id_t, IComponent*> >::iterator ifcit =
m_ComponentsByInterface.begin();
472 std::map<entity_id_t, SEntityComponentCache*>::iterator ccit =
m_ComponentCaches.begin();
486 const char* name,
const std::string& schema)
494 DeallocFunc dealloc,
const char* name,
const std::string& schema)
514 std::sort(types.begin(), types.end());
523 std::sort(types.begin(), types.end());
539 return it->second.name;
551 LOGERROR(L
"No script wrapper found for interface id %d", iid);
589 component->
Init(paramNode);
598 LOGERROR(L
"Invalid component id %d", cid);
607 if (emap1.find(ent.
GetId()) != emap1.end())
609 LOGERROR(L
"Multiple components for interface %d", ct.
iid);
616 jsval obj = JSVAL_NULL;
620 if (JSVAL_IS_VOID(obj))
622 LOGERROR(L
"Script component constructor failed");
635 emap1.insert(std::make_pair(ent.
GetId(), component));
636 emap2.insert(std::make_pair(ent.
GetId(), component));
656 if (emap1.find(ent.
GetId()) != emap1.end())
657 debug_warn(L
"Multiple components for interface");
658 emap1.insert(std::make_pair(ent.
GetId(), &component));
682 std::map<entity_id_t, SEntityComponentCache*>::iterator it;
705 if (!cmpTemplateManager)
721 for (CParamNode::ChildrenMap::const_iterator it = tmplChilds.begin(); it != tmplChilds.end(); ++it)
724 if (it->first.length() && it->first[0] ==
'@')
730 LOGERROR(L
"Unrecognised component type name '%hs' in entity template '%ls'", it->first.c_str(), templateName.c_str());
736 LOGERROR(L
"Failed to construct component type name '%hs' in entity template '%ls'", it->first.c_str(), templateName.c_str());
759 std::vector<entity_id_t> queue;
762 for (std::vector<entity_id_t>::iterator it = queue.begin(); it != queue.end(); ++it)
771 std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::iterator iit =
m_ComponentsByTypeId.begin();
774 std::map<entity_id_t, IComponent*>::iterator eit = iit->second.find(ent);
775 if (eit != iit->second.end())
777 eit->second->Deinit();
779 iit->second.erase(ent);
788 std::vector<boost::unordered_map<entity_id_t, IComponent*> >::iterator ifcit =
m_ComponentsByInterface.begin();
817 std::vector<std::pair<entity_id_t, IComponent*> > ret;
831 std::sort(ret.begin(), ret.end());
851 std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it;
855 std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
856 for (; ctit != it->second.end(); ++ctit)
859 std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap =
m_ComponentsByTypeId.find(*ctit);
864 std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.find(ent);
865 if (eit != emap->second.end())
866 eit->second->HandleMessage(msg,
false);
876 std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it;
880 std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
881 for (; ctit != it->second.end(); ++ctit)
884 std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap =
m_ComponentsByTypeId.find(*ctit);
889 std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin();
890 for (; eit != emap->second.end(); ++eit)
891 eit->second->HandleMessage(msg,
false);
903 std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it;
907 std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
908 for (; ctit != it->second.end(); ++ctit)
921 std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap =
m_ComponentsByTypeId.find(*ctit);
926 std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin();
927 for (; eit != emap->second.end(); ++eit)
928 eit->second->HandleMessage(msg,
true);
937 "<grammar xmlns='http://relaxng.org/ns/structure/1.0' xmlns:a='http://ns.wildfiregames.com/entity' datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes'>"
938 "<define name='nonNegativeDecimal'>"
939 "<data type='decimal'><param name='minInclusive'>0</param></data>"
941 "<define name='positiveDecimal'>"
942 "<data type='decimal'><param name='minExclusive'>0</param></data>"
944 "<define name='anything'>"
947 "<attribute><anyName/></attribute>"
951 "<ref name='anything'/>"
957 std::map<InterfaceId, std::vector<std::string> > interfaceComponentTypes;
959 std::vector<std::string> componentTypes;
964 "<define name='component." + it->second.name +
"'>"
965 "<element name='" + it->second.name +
"'>"
966 "<interleave>" + it->second.schema +
"</interleave>"
970 interfaceComponentTypes[it->second.iid].push_back(it->second.name);
971 componentTypes.push_back(it->second.name);
977 schema +=
"<define name='interface." + it->first +
"'><choice>";
978 std::vector<std::string>& cts = interfaceComponentTypes[it->second];
979 for (
size_t i = 0; i < cts.size(); ++i)
980 schema +=
"<ref name='component." + cts[i] +
"'/>";
981 schema +=
"</choice></define>";
988 std::sort(componentTypes.begin(), componentTypes.end());
991 "<element name='Entity'>"
992 "<optional><attribute name='parent'/></optional>";
993 for (std::vector<std::string>::const_iterator it = componentTypes.begin(); it != componentTypes.end(); ++it)
994 schema +=
"<optional><ref name='component." + *it +
"'/></optional>";
999 schema +=
"</grammar>";
1006 return ReadJSONFile(cbdata, L
"simulation/data", fileName);
1029 std::wstring name = pathstem.
string().substr(data->
path.
string().length());
1031 data->
templates.push_back(std::string(name.begin(), name.end()));
1039 cbData.
path =
VfsPath(L
"simulation/data/" + subPath + L
"/");
1055 return cbData.templates;
bool SetPrototype(jsval obj, jsval proto)
An entity initialisation parameter node.
static CScriptVal Script_ReadCivJSONFile(void *cbdata, std::wstring fileName)
IComponent * interfaces[1]
void SubscribeToMessageType(MessageTypeId mtid)
Subscribe the current component type to the given message type.
CStr DecodeUTF8() const
Returns contents of a UTF-8 encoded file as a string with optional BOM removed.
bool ReplaceNondeterministicRNG(boost::rand48 &rng)
Replace the default JS random number geenrator with a seeded, network-sync'd one. ...
void FlushDestroyedComponents()
Does the actual destruction of components from DestroyComponentsSoon.
std::map< MessageTypeId, std::vector< ComponentTypeId > > m_GlobalMessageSubscriptions
void ResetState()
Resets the dynamic simulation state (deletes all entities, resets entity ID counters; doesn't unload/...
This is sent immediately after a new entity's components have all been created and initialised...
std::vector< std::pair< entity_id_t, IComponent * > > InterfaceList
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
void AddMockComponent(CEntityHandle ent, InterfaceId iid, IComponent &component)
Adds an externally-created component, so that it is returned by QueryInterface but does not get destr...
Path VfsPath
VFS path of the form "(dir/)*file?".
static CScriptVal ReadJSONFile(void *cbdata, std::wstring filePath, std::wstring fileName)
entity_id_t AddEntity(const std::wstring &templateName, entity_id_t ent)
Constructs an entity based on the given template, and adds it the world with entity ID ent...
std::vector< boost::unordered_map< entity_id_t, IComponent * > > m_ComponentsByInterface
bool HasProperty(jsval obj, const char *name)
Check the named property has been defined on the given object.
void SetCallbackData(void *cbdata)
void(* DeallocFunc)(IComponent *)
const PSRETURN PSRETURN_OK
#define ENTITY_IS_LOCAL(id)
static CComponentManager::InterfaceListUnordered g_EmptyEntityMap
Reads a file, then gives read-only access to the contents.
static void Script_PostMessage(void *cbdata, int ent, int mtid, CScriptVal data)
std::string utf8_from_wstring(const std::wstring &src, Status *err)
opposite of wstring_from_utf8
entity_id_t m_NextLocalEntityId
IComponent *(* AllocFunc)(ScriptInterface &scriptInterface, jsval ctor)
virtual void Init(const CParamNode ¶mNode)=0
IComponent * QueryInterface(entity_id_t ent, InterfaceId iid) const
std::string globalHandlerName
Object wrapping an entity_id_t, with a SEntityComponentCache to support fast QueryInterface() / CmpPt...
ScriptInterface & GetScriptInterface()
const jsval & get() const
Returns the current value.
CSimContext & m_SimContext
A trivial wrapper around a jsval.
bool LoadScript(const VfsPath &filename, const std::string &code)
Load and execute the given script in a new function scope.
std::vector< std::string > templates
const ChildrenMap & GetChildren() const
Returns the names/nodes of the children of this node, ordered by name.
bool LoadGlobalScripts()
Load global scripts that most script contexts need, located in the /globalscripts directory...
Contains pointers to various 'global' objects that are needed by the simulation code, to allow easy access without using real (evil) global variables.
void LoadComponentTypes()
std::map< std::string, MessageTypeId > m_MessageTypeIdsByName
Used for script-only message types.
entity_id_t m_NextEntityId
const entity_id_t SYSTEM_ENTITY
Entity ID for singleton 'system' components.
void RegisterMessageType(MessageTypeId mtid, const char *name)
const entity_id_t FIRST_LOCAL_ENTITY
CEntityHandle LookupEntityHandle(entity_id_t ent, bool allowCreate=false)
Returns a CEntityHandle with id ent.
const InterfaceListUnordered & GetEntitiesWithInterfaceUnordered(InterfaceId iid) const
static std::vector< IComponent * > Script_GetComponentsWithInterface(void *cbdata, int iid)
#define ENSURE(expr)
ensure the expression <expr> evaluates to non-zero.
std::map< std::string, ComponentTypeId > m_ComponentTypeIdsByName
virtual int GetType() const =0
InterfaceList GetEntitiesWithInterface(InterfaceId iid) const
bool m_CurrentlyHotloading
std::map< ComponentTypeId, std::map< entity_id_t, IComponent * > > m_ComponentsByTypeId
CMessage * ConstructMessage(int mtid, CScriptVal data)
static Status FindJSONFilesCallback(const VfsPath &, const CFileInfo &, const uintptr_t)
static void Script_RegisterComponentType(void *cbdata, int iid, std::string cname, CScriptVal ctor)
This is sent immediately before a destroyed entity is flushed and really destroyed.
CScriptValRooted ReadJSONFile(const VfsPath &path)
Read a JSON file.
const String & string() const
std::map< std::string, InterfaceId > m_InterfaceIdsByName
CMessageScripted(int mtid, const std::string &name, const CScriptValRooted &msg)
CEntityHandle AllocateEntityHandle(entity_id_t ent)
ScriptInterface m_ScriptInterface
void RegisterComponentType(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char *, const std::string &schema)
std::map< MessageTypeId, std::string > m_MessageTypeNamesById
entity_id_t GetId() const
static std::vector< int > Script_GetEntitiesWithInterface(void *cbdata, int iid)
i64 Status
Error handling system.
CComponentManager(CSimContext &, bool skipScriptFunctions=false)
void SetEntityHandle(CEntityHandle ent)
void InitSystemEntity()
Set up an empty SYSTEM_ENTITY.
void SubscribeGloballyToMessageType(MessageTypeId mtid)
Subscribe the current component type to all messages of the given message type.
entity_id_t AllocateNewLocalEntity()
Returns a new local entity ID that has never been used before.
static void Script_RegisterGlobal(void *cbdata, std::string name, CScriptVal value)
bool GetProperty(jsval obj, const char *name, T &out)
Get the named property on the given object.
std::string LookupComponentTypeName(ComponentTypeId cid) const
void PostMessage(entity_id_t ent, const CMessage &msg) const
Send a message, targeted at a particular entity.
CMessage * CMessageFromJSVal(int mtid, ScriptInterface &scriptingInterface, jsval val)
bool AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode ¶mNode)
Constructs a component of type 'cid', initialised with data 'paramNode', and attaches it to entity 'e...
bool LoadScript(const VfsPath &filename, bool hotload=false)
Load a script and execute it in a new function scope.
virtual const char * GetScriptHandlerName() const
static CScriptVal Script_ReadJSONFile(void *cbdata, std::wstring fileName)
ComponentTypeId m_CurrentComponent
ComponentTypeId m_NextScriptComponentTypeId
std::map< MessageTypeId, std::vector< ComponentTypeId > > m_LocalMessageSubscriptions
IComponent * ConstructComponent(CEntityHandle ent, ComponentTypeId cid)
Allocates a component object of type 'cid', and attaches it to entity 'ent'.
Path ChangeExtension(Path extension) const
Template manager: Handles the loading of entity template files for the initialisation and deserializa...
void SetSystemEntity(CEntityHandle ent)
virtual const char * GetScriptGlobalHandlerName() const
void RegisterComponentTypeScriptWrapper(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char *, const std::string &schema)
Status ForEachFile(const PIVFS &fs, const VfsPath &startPath, FileCallback cb, uintptr_t cbData, const wchar_t *pattern, size_t flags)
call back for each file in a directory tree
PSRETURN Load(const PIVFS &vfs, const VfsPath &filename)
Returns either PSRETURN_OK or PSRETURN_CVFSFile_LoadFailed.
std::vector< entity_id_t > m_DestructionQueue
wchar_t * StatusDescription(Status status, wchar_t *buf, size_t max_chars)
generate textual description of a Status.
jsval CallConstructor(jsval ctor, jsval arg)
Call a constructor function, equivalent to JS "new ctor(arg)".
bool SetGlobal(const char *name, const T &value, bool replace=false)
Set the named property on the global object.
void BroadcastMessage(const CMessage &msg) const
Send a message, not targeted at any particular entity.
virtual jsval ToJSVal(ScriptInterface &scriptInterface) const
ComponentTypeId LookupCID(const std::string &cname) const
static std::vector< std::string > Script_FindJSONFiles(void *cbdata, std::wstring subPath, bool recursive)
virtual const CParamNode * LoadTemplate(entity_id_t ent, const std::string &templateName, int playerID)=0
Loads the template XML file identified by 'templateName' (including inheritance from parent XML files...
JSBool error(JSContext *cx, uintN argc, jsval *vp)
std::map< entity_id_t, SEntityComponentCache * > m_ComponentCaches
std::string GenerateSchema()
std::map< ComponentTypeId, ComponentType > m_ComponentTypesById
jsval get() const
Returns the current value (or JSVAL_VOID if uninitialised).
boost::unordered_map< entity_id_t, IComponent * > InterfaceListUnordered
static void Script_DestroyEntity(void *cbdata, int ent)
static Handle handle(size_t idx, u64 tag)
Abstraction around a SpiderMonkey JSContext.
#define debug_warn(expr)
display the error dialog with the given text.
void DestroyComponentsSoon(entity_id_t ent)
Destroys all the components belonging to the specified entity when FlushDestroyedComponents is called...
void SetComponentManager(CComponentManager *man)
static void Script_BroadcastMessage(void *cbdata, int mtid, CScriptVal data)
static void Script_RegisterMessageType(void *cbdata, std::string name)
const entity_id_t INVALID_ENTITY
Invalid entity ID.
static IComponent * Script_QueryInterface(void *cbdata, int ent, int iid)
u32 entity_id_t
Entity ID type.
static int Script_AddLocalEntity(void *cbdata, std::string templateName)
static void Script_RegisterInterface(void *cbdata, std::string name)
bool EnumeratePropertyNamesWithPrefix(jsval obj, const char *prefix, std::vector< std::string > &out)
void SetSimContext(const CSimContext &context)
std::map< std::string, CParamNode > ChildrenMap
JSContext * GetContext() const
CEntityHandle m_SystemEntity
static int Script_AddEntity(void *cbdata, std::string templateName)
void ReportError(const char *msg)
Report the given error message through the JS error reporting mechanism, and throw a JS exception...
void SendGlobalMessage(entity_id_t ent, const CMessage &msg) const
ComponentTypeId GetScriptWrapper(InterfaceId iid)
SEntityComponentCache * GetComponentCache() const
virtual int GetType() const
entity_id_t AllocateNewEntity()
Returns a new entity ID that has never been used before.