Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ComponentManager.h
Go to the documentation of this file.
1 /* Copyright (C) 2011 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef INCLUDED_COMPONENTMANAGER
19 #define INCLUDED_COMPONENTMANAGER
20 
21 #include "Entity.h"
22 #include "Components.h"
25 #include "ps/Filesystem.h"
26 
27 #include <boost/random/linear_congruential.hpp>
28 #include <boost/unordered_map.hpp>
29 
30 #include <map>
31 
32 class IComponent;
33 class CParamNode;
34 class CMessage;
35 class CSimContext;
36 
38 {
40 public:
41  // We can't use EInterfaceId/etc directly, since scripts dynamically generate new IDs
42  // and casting arbitrary ints to enums is undefined behaviour, so use 'int' typedefs
43  typedef int InterfaceId;
44  typedef int ComponentTypeId;
45  typedef int MessageTypeId;
46 
47 private:
48  // Component allocation types
49  typedef IComponent* (*AllocFunc)(ScriptInterface& scriptInterface, jsval ctor);
50  typedef void (*DeallocFunc)(IComponent*);
51 
52  // ComponentTypes come in three types:
53  // Native: normal C++ component
54  // ScriptWrapper: C++ component that wraps a JS component implementation
55  // Script: a ScriptWrapper linked to a specific JS component implementation
57  {
61  };
62 
63  // Representation of a component type, to be used when instantiating components
65  {
70  std::string name;
71  std::string schema; // RelaxNG fragment
72  CScriptValRooted ctor; // only valid if type == CT_Script
73  };
74 
77  std::vector<std::string> templates;
78  };
79 
80 public:
81  CComponentManager(CSimContext&, bool skipScriptFunctions = false);
83 
84  void LoadComponentTypes();
85 
86  /**
87  * Load a script and execute it in a new function scope.
88  * @param filename VFS path to load
89  * @param hotload set to true if this script has been loaded before, and redefinitions of
90  * existing components should not be considered errors
91  */
92  bool LoadScript(const VfsPath& filename, bool hotload = false);
93 
94  void RegisterMessageType(MessageTypeId mtid, const char* name);
95 
96  void RegisterComponentType(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char*, const std::string& schema);
97  void RegisterComponentTypeScriptWrapper(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char*, const std::string& schema);
98 
99  /**
100  * Subscribe the current component type to the given message type.
101  * Each component's HandleMessage will be called on any BroadcastMessage of this message type,
102  * or on any PostMessage of this type targeted at the component's entity.
103  * Must only be called by a component type's ClassInit.
104  */
106 
107  /**
108  * Subscribe the current component type to all messages of the given message type.
109  * Each component's HandleMessage will be called on any BroadcastMessage or PostMessage of this message type,
110  * regardless of the entity.
111  * Must only be called by a component type's ClassInit.
112  */
114 
115  /**
116  * @param cname Requested component type name (not including any "CID" or "CCmp" prefix)
117  * @return The component type id, or CID__Invalid if not found
118  */
119  ComponentTypeId LookupCID(const std::string& cname) const;
120 
121  /**
122  * @return The name of the given component type, or "" if not found
123  */
124  std::string LookupComponentTypeName(ComponentTypeId cid) const;
125 
126  /**
127  * Set up an empty SYSTEM_ENTITY. Must be called after ResetState() and before GetSystemEntity().
128  */
129  void InitSystemEntity();
130 
131  /**
132  * Returns a CEntityHandle with id SYSTEM_ENTITY.
133  */
135 
136  /**
137  * Returns a CEntityHandle with id @p ent.
138  * If @p allowCreate is true and there is no existing CEntityHandle, a new handle will be allocated.
139  */
140  CEntityHandle LookupEntityHandle(entity_id_t ent, bool allowCreate = false);
141 
142  /**
143  * Returns a new entity ID that has never been used before.
144  * This affects the simulation state so it must only be called in network-synchronised ways.
145  */
147 
148  /**
149  * Returns a new local entity ID that has never been used before.
150  * This entity will not be synchronised over the network, stored in saved games, etc.
151  */
153 
154  /**
155  * Returns a new entity ID that has never been used before.
156  * If possible, returns preferredId, and ensures this ID won't be allocated again.
157  * This affects the simulation state so it must only be called in network-synchronised ways.
158  */
160 
161  /**
162  * Constructs a component of type 'cid', initialised with data 'paramNode',
163  * and attaches it to entity 'ent'.
164  *
165  * @return true on success; false on failure, and logs an error message
166  */
167  bool AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode& paramNode);
168 
169  /**
170  * Adds an externally-created component, so that it is returned by QueryInterface
171  * but does not get destroyed and does not receive messages from the component manager.
172  * (This is intended for unit tests that need to add mock objects the tested components
173  * expect to exist.)
174  */
175  void AddMockComponent(CEntityHandle ent, InterfaceId iid, IComponent& component);
176 
177  /**
178  * Allocates a component object of type 'cid', and attaches it to entity 'ent'.
179  * (The component's Init is not called here - either Init or Deserialize must be called
180  * before using the returned object.)
181  */
183 
184  /**
185  * Constructs an entity based on the given template, and adds it the world with
186  * entity ID @p ent. There should not be any existing components with that entity ID.
187  * @return ent, or INVALID_ENTITY on error
188  */
189  entity_id_t AddEntity(const std::wstring& templateName, entity_id_t ent);
190 
191  /**
192  * Destroys all the components belonging to the specified entity when FlushDestroyedComponents is called.
193  * Has no effect if the entity does not exist, or has already been added to the destruction queue.
194  */
196 
197  /**
198  * Does the actual destruction of components from DestroyComponentsSoon.
199  * This must not be called if the component manager is on the call stack (since it
200  * will break internal iterators).
201  */
203 
205 
206  typedef std::vector<std::pair<entity_id_t, IComponent*> > InterfaceList;
207  typedef boost::unordered_map<entity_id_t, IComponent*> InterfaceListUnordered;
208 
211 
212  /**
213  * Send a message, targeted at a particular entity. The message will be received by any
214  * components of that entity which subscribed to the message type, and by any other components
215  * that subscribed globally to the message type.
216  */
217  void PostMessage(entity_id_t ent, const CMessage& msg) const;
218 
219  /**
220  * Send a message, not targeted at any particular entity. The message will be received by any
221  * components that subscribed (either globally or not) to the message type.
222  */
223  void BroadcastMessage(const CMessage& msg) const;
224 
225  /**
226  * Resets the dynamic simulation state (deletes all entities, resets entity ID counters;
227  * doesn't unload/reload component scripts).
228  */
229  void ResetState();
230 
231  // Various state serialization functions:
232  bool ComputeStateHash(std::string& outHash, bool quick);
233  bool DumpDebugState(std::ostream& stream, bool includeDebugInfo);
234  // FlushDestroyedComponents must be called before SerializeState (since the destruction queue
235  // won't get serialized)
236  bool SerializeState(std::ostream& stream);
237  bool DeserializeState(std::istream& stream);
238 
239  std::string GenerateSchema();
240 
242 
243 private:
244  // Implementations of functions exposed to scripts
245  static void Script_RegisterComponentType(void* cbdata, int iid, std::string cname, CScriptVal ctor);
246  static void Script_RegisterInterface(void* cbdata, std::string name);
247  static void Script_RegisterMessageType(void* cbdata, std::string name);
248  static void Script_RegisterGlobal(void* cbdata, std::string name, CScriptVal value);
249  static IComponent* Script_QueryInterface(void* cbdata, int ent, int iid);
250  static std::vector<int> Script_GetEntitiesWithInterface(void* cbdata, int iid);
251  static std::vector<IComponent*> Script_GetComponentsWithInterface(void* cbdata, int iid);
252  static void Script_PostMessage(void* cbdata, int ent, int mtid, CScriptVal data);
253  static void Script_BroadcastMessage(void* cbdata, int mtid, CScriptVal data);
254  static int Script_AddEntity(void* cbdata, std::string templateName);
255  static int Script_AddLocalEntity(void* cbdata, std::string templateName);
256  static void Script_DestroyEntity(void* cbdata, int ent);
257  static CScriptVal Script_ReadJSONFile(void* cbdata, std::wstring fileName);
258  static CScriptVal Script_ReadCivJSONFile(void* cbdata, std::wstring fileName);
259  static std::vector<std::string> Script_FindJSONFiles(void* cbdata, std::wstring subPath, bool recursive);
260 
261  static CScriptVal ReadJSONFile(void *cbdata, std::wstring filePath, std::wstring fileName);
262 
263  // callback function to handle recursively finding files in a directory
264  static Status FindJSONFilesCallback(const VfsPath&, const CFileInfo&, const uintptr_t);
265 
266  CMessage* ConstructMessage(int mtid, CScriptVal data);
267  void SendGlobalMessage(entity_id_t ent, const CMessage& msg) const;
268 
270 
272 
275 
277 
278  ComponentTypeId m_CurrentComponent; // used when loading component types
280 
281  // TODO: some of these should be vectors
282  std::map<ComponentTypeId, ComponentType> m_ComponentTypesById;
283  std::vector<boost::unordered_map<entity_id_t, IComponent*> > m_ComponentsByInterface; // indexed by InterfaceId
284  std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> > m_ComponentsByTypeId;
285  std::map<MessageTypeId, std::vector<ComponentTypeId> > m_LocalMessageSubscriptions;
286  std::map<MessageTypeId, std::vector<ComponentTypeId> > m_GlobalMessageSubscriptions;
287  std::map<std::string, ComponentTypeId> m_ComponentTypeIdsByName;
288  std::map<std::string, MessageTypeId> m_MessageTypeIdsByName;
289  std::map<MessageTypeId, std::string> m_MessageTypeNamesById;
290  std::map<std::string, InterfaceId> m_InterfaceIdsByName;
291 
292  std::map<entity_id_t, SEntityComponentCache*> m_ComponentCaches;
293 
294  // TODO: maintaining both ComponentsBy* is nasty; can we get rid of one,
295  // while keeping QueryInterface and PostMessage sufficiently efficient?
296 
297  std::vector<entity_id_t> m_DestructionQueue;
298 
302 
303  boost::rand48 m_RNG;
304 
305  friend class TestComponentManager;
306 };
307 
308 #endif // INCLUDED_COMPONENTMANAGER
An entity initialisation parameter node.
Definition: ParamNode.h:112
static CScriptVal Script_ReadCivJSONFile(void *cbdata, std::wstring fileName)
void SubscribeToMessageType(MessageTypeId mtid)
Subscribe the current component type to the given message type.
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&#39;t unload/...
std::vector< std::pair< entity_id_t, IComponent * > > InterfaceList
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...
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
void(* DeallocFunc)(IComponent *)
static void Script_PostMessage(void *cbdata, int ent, int mtid, CScriptVal data)
entity_id_t m_NextLocalEntityId
IComponent *(* AllocFunc)(ScriptInterface &scriptInterface, jsval ctor)
IComponent * QueryInterface(entity_id_t ent, InterfaceId iid) const
Object wrapping an entity_id_t, with a SEntityComponentCache to support fast QueryInterface() / CmpPt...
Definition: Entity.h:80
ScriptInterface & GetScriptInterface()
CSimContext & m_SimContext
friend class TestComponentManager
A trivial wrapper around a jsval.
Definition: ScriptVal.h:29
#define ASSERT(expr)
same as ENSURE in debug mode, does nothing in release mode.
Definition: debug.h:310
Contains pointers to various &#39;global&#39; objects that are needed by the simulation code, to allow easy access without using real (evil) global variables.
Definition: SimContext.h:32
bool SerializeState(std::ostream &stream)
std::map< std::string, MessageTypeId > m_MessageTypeIdsByName
entity_id_t m_NextEntityId
const entity_id_t SYSTEM_ENTITY
Entity ID for singleton &#39;system&#39; components.
Definition: Entity.h:44
void RegisterMessageType(MessageTypeId mtid, const char *name)
CEntityHandle LookupEntityHandle(entity_id_t ent, bool allowCreate=false)
Returns a CEntityHandle with id ent.
boost::rand48 m_RNG
const InterfaceListUnordered & GetEntitiesWithInterfaceUnordered(InterfaceId iid) const
static std::vector< IComponent * > Script_GetComponentsWithInterface(void *cbdata, int iid)
std::map< std::string, ComponentTypeId > m_ComponentTypeIdsByName
InterfaceList GetEntitiesWithInterface(InterfaceId iid) const
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)
Definition: path.h:75
NONCOPYABLE(CComponentManager)
bool ComputeStateHash(std::string &outHash, bool quick)
std::map< std::string, InterfaceId > m_InterfaceIdsByName
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
Definition: Entity.h:89
static std::vector< int > Script_GetEntitiesWithInterface(void *cbdata, int iid)
i64 Status
Error handling system.
Definition: status.h:171
CComponentManager(CSimContext &, bool skipScriptFunctions=false)
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.
bool DeserializeState(std::istream &stream)
static void Script_RegisterGlobal(void *cbdata, std::string name, CScriptVal value)
std::string LookupComponentTypeName(ComponentTypeId cid) const
void PostMessage(entity_id_t ent, const CMessage &msg) const
Send a message, targeted at a particular entity.
bool AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode &paramNode)
Constructs a component of type &#39;cid&#39;, initialised with data &#39;paramNode&#39;, and attaches it to entity &#39;e...
bool DumpDebugState(std::ostream &stream, bool includeDebugInfo)
bool LoadScript(const VfsPath &filename, bool hotload=false)
Load a script and execute it in a new function scope.
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 &#39;cid&#39;, and attaches it to entity &#39;ent&#39;.
void RegisterComponentTypeScriptWrapper(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char *, const std::string &schema)
std::vector< entity_id_t > m_DestructionQueue
void BroadcastMessage(const CMessage &msg) const
Send a message, not targeted at any particular entity.
ComponentTypeId LookupCID(const std::string &cname) const
static std::vector< std::string > Script_FindJSONFiles(void *cbdata, std::wstring subPath, bool recursive)
std::map< entity_id_t, SEntityComponentCache * > m_ComponentCaches
std::string GenerateSchema()
std::map< ComponentTypeId, ComponentType > m_ComponentTypesById
CEntityHandle GetSystemEntity()
Returns a CEntityHandle with id SYSTEM_ENTITY.
boost::unordered_map< entity_id_t, IComponent * > InterfaceListUnordered
static void Script_DestroyEntity(void *cbdata, int ent)
Abstraction around a SpiderMonkey JSContext.
void DestroyComponentsSoon(entity_id_t ent)
Destroys all the components belonging to the specified entity when FlushDestroyedComponents is called...
static void Script_BroadcastMessage(void *cbdata, int mtid, CScriptVal data)
static void Script_RegisterMessageType(void *cbdata, std::string name)
static IComponent * Script_QueryInterface(void *cbdata, int ent, int iid)
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
static int Script_AddLocalEntity(void *cbdata, std::string templateName)
static void Script_RegisterInterface(void *cbdata, std::string name)
CEntityHandle m_SystemEntity
static int Script_AddEntity(void *cbdata, std::string templateName)
void SendGlobalMessage(entity_id_t ent, const CMessage &msg) const
ComponentTypeId GetScriptWrapper(InterfaceId iid)
entity_id_t AllocateNewEntity()
Returns a new entity ID that has never been used before.