Pyrogenesis trunk
ComponentManager.h
Go to the documentation of this file.
1/* Copyright (C) 2023 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 "ps/Filesystem.h"
27
28#include <boost/random/linear_congruential.hpp>
29#include <map>
30#include <set>
31#include <unordered_map>
32
33class IComponent;
34class CParamNode;
35class CMessage;
36class CSimContext;
38
40{
42public:
43 // We can't use EInterfaceId/etc directly, since scripts dynamically generate new IDs
44 // and casting arbitrary ints to enums is undefined behaviour, so use 'int' typedefs
45 typedef int InterfaceId;
46 typedef int ComponentTypeId;
47 typedef int MessageTypeId;
48
49private:
52
53 // ComponentTypes come in three types:
54 // Native: normal C++ component
55 // ScriptWrapper: C++ component that wraps a JS component implementation
56 // Script: a ScriptWrapper linked to a specific JS component implementation
58 {
62 };
63
64 // Representation of a component type, to be used when instantiating components
66 {
71 std::string name;
72 std::string schema; // RelaxNG fragment
73 std::unique_ptr<JS::PersistentRootedValue> ctor; // only valid if type == CT_Script
74 };
75
76public:
77 CComponentManager(CSimContext&, ScriptContext& cx, bool skipScriptFunctions = false);
79
80 void LoadComponentTypes();
81
82 /**
83 * Load a script and execute it in a new function scope.
84 * @param filename VFS path to load
85 * @param hotload set to true if this script has been loaded before, and redefinitions of
86 * existing components should not be considered errors
87 */
88 bool LoadScript(const VfsPath& filename, bool hotload = false);
89
90 void RegisterMessageType(MessageTypeId mtid, const char* name);
91
92 void RegisterComponentType(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char*, const std::string& schema);
93 void RegisterComponentTypeScriptWrapper(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char*, const std::string& schema);
94
96
97 /**
98 * Subscribe the current component type to the given message type.
99 * Each component's HandleMessage will be called on any BroadcastMessage of this message type,
100 * or on any PostMessage of this type targeted at the component's entity.
101 * Must only be called by a component type's ClassInit.
102 */
104
105 /**
106 * Subscribe the current component type to all messages of the given message type.
107 * Each component's HandleMessage will be called on any BroadcastMessage or PostMessage of this message type,
108 * regardless of the entity.
109 * Must only be called by a component type's ClassInit.
110 */
112
113 /**
114 * Subscribe the given component instance to all messages of the given message type.
115 * The component's HandleMessage will be called on any BroadcastMessage or PostMessage of
116 * this message type, regardless of the entity.
117 *
118 * This can be called at any time (including inside the HandleMessage callback for this message type).
119 *
120 * The component type must not have statically subscribed to this message type in its ClassInit.
121 *
122 * The subscription status is not saved or network-synchronised. Components must remember to
123 * resubscribe in their Deserialize methods if they still want the message.
124 *
125 * This is primarily intended for Interpolate and RenderSubmit messages, to avoid the cost of
126 * sending the message to components that do not currently need to do any rendering.
127 */
128 void DynamicSubscriptionNonsync(MessageTypeId mtid, IComponent* component, bool enabled);
129
130 /**
131 * @param cname Requested component type name (not including any "CID" or "CCmp" prefix)
132 * @return The component type id, or CID__Invalid if not found
133 */
134 ComponentTypeId LookupCID(const std::string& cname) const;
135
136 /**
137 * @return The name of the given component type, or "" if not found
138 */
139 std::string LookupComponentTypeName(ComponentTypeId cid) const;
140
141 /**
142 * Set up an empty SYSTEM_ENTITY. Must be called after ResetState() and before GetSystemEntity().
143 */
144 void InitSystemEntity();
145
146 /**
147 * Returns a CEntityHandle with id SYSTEM_ENTITY.
148 */
150
151 /**
152 * Returns a CEntityHandle with id @p ent.
153 * If @p allowCreate is true and there is no existing CEntityHandle, a new handle will be allocated.
154 */
155 CEntityHandle LookupEntityHandle(entity_id_t ent, bool allowCreate = false);
156
157 /**
158 * Returns true if the entity with id @p ent exists.
159 */
160 bool EntityExists(entity_id_t ent) const;
161
162 /**
163 * Returns a new entity ID that has never been used before.
164 * This affects the simulation state so it must only be called in network-synchronised ways.
165 */
167
168 /**
169 * Returns a new local entity ID that has never been used before.
170 * This entity will not be synchronised over the network, stored in saved games, etc.
171 */
173
174 /**
175 * Returns a new entity ID that has never been used before.
176 * If possible, returns preferredId, and ensures this ID won't be allocated again.
177 * This affects the simulation state so it must only be called in network-synchronised ways.
178 */
180
181 /**
182 * Constructs a component of type 'cid', initialised with data 'paramNode',
183 * and attaches it to entity 'ent'.
184 *
185 * @return true on success; false on failure, and logs an error message
186 */
187 bool AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode& paramNode);
188
189 /**
190 * Add all system components to the system entity (skip the scripted components or the AI components on demand)
191 */
192 void AddSystemComponents(bool skipScriptedComponents, bool skipAI);
193
194 /**
195 * Adds an externally-created component, so that it is returned by QueryInterface
196 * but does not get destroyed and does not receive messages from the component manager.
197 * (This is intended for unit tests that need to add mock objects the tested components
198 * expect to exist.)
199 */
200 void AddMockComponent(CEntityHandle ent, InterfaceId iid, IComponent& component);
201
202 /**
203 * Allocates a component object of type 'cid', and attaches it to entity 'ent'.
204 * (The component's Init is not called here - either Init or Deserialize must be called
205 * before using the returned object.)
206 */
208
209 /**
210 * Constructs an entity based on the given template, and adds it the world with
211 * entity ID @p ent. There should not be any existing components with that entity ID.
212 * @return ent, or INVALID_ENTITY on error
213 */
214 entity_id_t AddEntity(const std::wstring& templateName, entity_id_t ent);
215
216 /**
217 * Destroys all the components belonging to the specified entity when FlushDestroyedComponents is called.
218 * Has no effect if the entity does not exist, or has already been added to the destruction queue.
219 */
221
222 /**
223 * Does the actual destruction of components from DestroyComponentsSoon.
224 * This must not be called if the component manager is on the call stack (since it
225 * will break internal iterators).
226 */
228
230
231 using InterfacePair = std::pair<entity_id_t, IComponent*>;
232 using InterfaceList = std::vector<InterfacePair>;
233 using InterfaceListUnordered = std::unordered_map<entity_id_t, IComponent*>;
234
237
238 /**
239 * Send a message, targeted at a particular entity. The message will be received by any
240 * components of that entity which subscribed to the message type, and by any other components
241 * that subscribed globally to the message type.
242 */
243 void PostMessage(entity_id_t ent, const CMessage& msg);
244
245 /**
246 * Send a message, not targeted at any particular entity. The message will be received by any
247 * components that subscribed (either globally or not) to the message type.
248 */
249 void BroadcastMessage(const CMessage& msg);
250
251 /**
252 * Resets the dynamic simulation state (deletes all entities, resets entity ID counters;
253 * doesn't unload/reload component scripts).
254 */
255 void ResetState();
256
257 /**
258 * Initializes the random number generator with a seed determined by the host.
259 */
260 void SetRNGSeed(u32 seed);
261
262 // Various state serialization functions:
263 bool ComputeStateHash(std::string& outHash, bool quick) const;
264 bool DumpDebugState(std::ostream& stream, bool includeDebugInfo) const;
265 // FlushDestroyedComponents must be called before SerializeState (since the destruction queue
266 // won't get serialized)
267 bool SerializeState(std::ostream& stream) const;
268 bool DeserializeState(std::istream& stream);
269
270 std::string GenerateSchema() const;
271
273
274private:
275 // Implementations of functions exposed to scripts
276 void Script_RegisterComponentType_Common(int iid, const std::string& cname, JS::HandleValue ctor, bool reRegister, bool systemComponent);
277 void Script_RegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor);
278 void Script_RegisterSystemComponentType(int iid, const std::string& cname, JS::HandleValue ctor);
279 void Script_ReRegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor);
280 void Script_RegisterInterface(const std::string& name);
281 void Script_RegisterMessageType(const std::string& name);
282 void Script_RegisterGlobal(const std::string& name, JS::HandleValue value);
283 std::vector<int> Script_GetEntitiesWithInterface(int iid);
284 std::vector<IComponent*> Script_GetComponentsWithInterface(int iid);
285 void Script_PostMessage(int ent, int mtid, JS::HandleValue data);
286 void Script_BroadcastMessage(int mtid, JS::HandleValue data);
287 int Script_AddEntity(const std::wstring& templateName);
288 int Script_AddLocalEntity(const std::wstring& templateName);
289 const CParamNode& Script_GetTemplate(const std::string& templateName);
290
291 CMessage* ConstructMessage(int mtid, JS::HandleValue data);
292 void SendGlobalMessage(entity_id_t ent, const CMessage& msg);
293
296
298
300
303
305
306 ComponentTypeId m_CurrentComponent; // used when loading component types
308
309 // TODO: some of these should be vectors
310 std::map<ComponentTypeId, ComponentType> m_ComponentTypesById;
311 std::vector<CComponentManager::ComponentTypeId> m_ScriptedSystemComponents;
312 std::vector<std::unordered_map<entity_id_t, IComponent*> > m_ComponentsByInterface; // indexed by InterfaceId
313 std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> > m_ComponentsByTypeId;
314 std::map<MessageTypeId, std::vector<ComponentTypeId> > m_LocalMessageSubscriptions;
315 std::map<MessageTypeId, std::vector<ComponentTypeId> > m_GlobalMessageSubscriptions;
316 std::map<std::string, ComponentTypeId> m_ComponentTypeIdsByName;
317 std::map<std::string, MessageTypeId> m_MessageTypeIdsByName;
318 std::map<MessageTypeId, std::string> m_MessageTypeNamesById;
319 std::map<std::string, InterfaceId> m_InterfaceIdsByName;
320
321 std::map<MessageTypeId, CDynamicSubscription> m_DynamicMessageSubscriptionsNonsync;
322 std::map<IComponent*, std::set<MessageTypeId> > m_DynamicMessageSubscriptionsNonsyncByComponent;
323
324 std::unordered_map<entity_id_t, SEntityComponentCache*> m_ComponentCaches;
325
326 // TODO: maintaining both ComponentsBy* is nasty; can we get rid of one,
327 // while keeping QueryInterface and PostMessage sufficiently efficient?
328
329 std::vector<entity_id_t> m_DestructionQueue;
330
334
335 boost::rand48 m_RNG;
336
338};
339
340#endif // INCLUDED_COMPONENTMANAGER
Definition: ComponentManager.h:40
bool ComputeStateHash(std::string &outHash, bool quick) const
Definition: ComponentManagerSerialization.cpp:98
void DestroyComponentsSoon(entity_id_t ent)
Destroys all the components belonging to the specified entity when FlushDestroyedComponents is called...
Definition: ComponentManager.cpp:882
const CParamNode & Script_GetTemplate(const std::string &templateName)
Definition: ComponentManager.cpp:391
entity_id_t m_NextEntityId
Definition: ComponentManager.h:332
void InitSystemEntity()
Set up an empty SYSTEM_ENTITY.
Definition: ComponentManager.cpp:824
std::vector< CComponentManager::ComponentTypeId > m_ScriptedSystemComponents
Definition: ComponentManager.h:311
void SendGlobalMessage(entity_id_t ent, const CMessage &msg)
Definition: ComponentManager.cpp:1046
int InterfaceId
Definition: ComponentManager.h:45
void BroadcastMessage(const CMessage &msg)
Send a message, not targeted at any particular entity.
Definition: ComponentManager.cpp:1021
CSimContext & m_SimContext
Definition: ComponentManager.h:302
int ComponentTypeId
Definition: ComponentManager.h:46
int MessageTypeId
Definition: ComponentManager.h:47
entity_id_t AllocateNewEntity()
Returns a new entity ID that has never been used before.
Definition: ComponentManager.cpp:651
void FlattenDynamicSubscriptions()
Definition: ComponentManager.cpp:569
EComponentTypeType
Definition: ComponentManager.h:58
@ CT_Script
Definition: ComponentManager.h:61
@ CT_Native
Definition: ComponentManager.h:59
@ CT_ScriptWrapper
Definition: ComponentManager.h:60
CEntityHandle m_SystemEntity
Definition: ComponentManager.h:304
void PostMessage(entity_id_t ent, const CMessage &msg)
Send a message, targeted at a particular entity.
Definition: ComponentManager.cpp:994
int Script_AddEntity(const std::wstring &templateName)
Definition: ComponentManager.cpp:466
std::map< ComponentTypeId, std::map< entity_id_t, IComponent * > > m_ComponentsByTypeId
Definition: ComponentManager.h:313
std::unordered_map< entity_id_t, IComponent * > InterfaceListUnordered
Definition: ComponentManager.h:233
std::map< ComponentTypeId, ComponentType > m_ComponentTypesById
Definition: ComponentManager.h:310
std::map< std::string, ComponentTypeId > m_ComponentTypeIdsByName
Definition: ComponentManager.h:316
std::string LookupComponentTypeName(ComponentTypeId cid) const
Definition: ComponentManager.cpp:621
InterfaceList GetEntitiesWithInterface(InterfaceId iid) const
Definition: ComponentManager.cpp:961
void Script_ReRegisterComponentType(int iid, const std::string &cname, JS::HandleValue ctor)
Definition: ComponentManager.cpp:338
void RemoveComponentDynamicSubscriptions(IComponent *component)
Definition: ComponentManager.cpp:595
ComponentTypeId m_NextScriptComponentTypeId
Definition: ComponentManager.h:331
const InterfaceListUnordered & GetEntitiesWithInterfaceUnordered(InterfaceId iid) const
Definition: ComponentManager.cpp:983
std::pair< entity_id_t, IComponent * > InterfacePair
Definition: ComponentManager.h:231
ComponentTypeId m_CurrentComponent
Definition: ComponentManager.h:306
CMessage * ConstructMessage(int mtid, JS::HandleValue data)
Definition: ComponentManager.cpp:428
boost::rand48 m_RNG
Definition: ComponentManager.h:335
void Script_RegisterGlobal(const std::string &name, JS::HandleValue value)
Definition: ComponentManager.cpp:386
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.
Definition: ComponentManager.cpp:831
void RegisterMessageType(MessageTypeId mtid, const char *name)
Definition: ComponentManager.cpp:545
void Script_RegisterComponentType_Common(int iid, const std::string &cname, JS::HandleValue ctor, bool reRegister, bool systemComponent)
Definition: ComponentManager.cpp:160
void RegisterComponentTypeScriptWrapper(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char *, const std::string &schema)
Definition: ComponentManager.cpp:531
void AddSystemComponents(bool skipScriptedComponents, bool skipAI)
Add all system components to the system entity (skip the scripted components or the AI components on ...
Definition: ComponentManager.cpp:690
CEntityHandle AllocateEntityHandle(entity_id_t ent)
Definition: ComponentManager.cpp:793
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...
Definition: ComponentManager.cpp:778
std::map< MessageTypeId, CDynamicSubscription > m_DynamicMessageSubscriptionsNonsync
Definition: ComponentManager.h:321
entity_id_t AllocateNewLocalEntity()
Returns a new local entity ID that has never been used before.
Definition: ComponentManager.cpp:658
int Script_AddLocalEntity(const std::wstring &templateName)
Definition: ComponentManager.cpp:473
std::vector< IComponent * > Script_GetComponentsWithInterface(int iid)
Definition: ComponentManager.cpp:419
IComponent::AllocFunc AllocFunc
Definition: ComponentManager.h:50
std::map< IComponent *, std::set< MessageTypeId > > m_DynamicMessageSubscriptionsNonsyncByComponent
Definition: ComponentManager.h:322
void Script_BroadcastMessage(int mtid, JS::HandleValue data)
Definition: ComponentManager.cpp:455
bool DeserializeState(std::istream &stream)
Definition: ComponentManagerSerialization.cpp:297
std::vector< InterfacePair > InterfaceList
Definition: ComponentManager.h:232
~CComponentManager()
Definition: ComponentManager.cpp:119
void SubscribeGloballyToMessageType(MessageTypeId mtid)
Subscribe the current component type to all messages of the given message type.
Definition: ComponentManager.cpp:560
bool LoadScript(const VfsPath &filename, bool hotload=false)
Load a script and execute it in a new function scope.
Definition: ComponentManager.cpp:146
void MarkScriptedComponentForSystemEntity(CComponentManager::ComponentTypeId cid)
Definition: ComponentManager.cpp:540
std::map< std::string, MessageTypeId > m_MessageTypeIdsByName
Definition: ComponentManager.h:317
ComponentTypeId GetScriptWrapper(InterfaceId iid)
Definition: ComponentManager.cpp:629
ScriptInterface m_ScriptInterface
Definition: ComponentManager.h:301
bool AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode &paramNode)
Constructs a component of type 'cid', initialised with data 'paramNode', and attaches it to entity 'e...
Definition: ComponentManager.cpp:680
CEntityHandle LookupEntityHandle(entity_id_t ent, bool allowCreate=false)
Returns a CEntityHandle with id ent.
Definition: ComponentManager.cpp:809
bool EntityExists(entity_id_t ent) const
Returns true if the entity with id ent exists.
Definition: ComponentManager.cpp:876
void FlushDestroyedComponents()
Does the actual destruction of components from DestroyComponentsSoon.
Definition: ComponentManager.cpp:887
void Script_RegisterMessageType(const std::string &name)
Definition: ComponentManager.cpp:365
void ResetState()
Resets the dynamic simulation state (deletes all entities, resets entity ID counters; doesn't unload/...
Definition: ComponentManager.cpp:480
std::map< std::string, InterfaceId > m_InterfaceIdsByName
Definition: ComponentManager.h:319
void SubscribeToMessageType(MessageTypeId mtid)
Subscribe the current component type to the given message type.
Definition: ComponentManager.cpp:551
void Script_PostMessage(int ent, int mtid, JS::HandleValue data)
Definition: ComponentManager.cpp:444
ScriptInterface & GetScriptInterface()
Definition: ComponentManager.h:272
CComponentManager(CSimContext &, ScriptContext &cx, bool skipScriptFunctions=false)
Definition: ComponentManager.cpp:60
IComponent::DeallocFunc DeallocFunc
Definition: ComponentManager.h:51
entity_id_t m_NextLocalEntityId
Definition: ComponentManager.h:333
std::string GenerateSchema() const
Definition: ComponentManager.cpp:1093
std::vector< std::unordered_map< entity_id_t, IComponent * > > m_ComponentsByInterface
Definition: ComponentManager.h:312
NONCOPYABLE(CComponentManager)
void DynamicSubscriptionNonsync(MessageTypeId mtid, IComponent *component, bool enabled)
Subscribe the given component instance to all messages of the given message type.
Definition: ComponentManager.cpp:579
IComponent * QueryInterface(entity_id_t ent, InterfaceId iid) const
Definition: ComponentManager.cpp:943
std::vector< entity_id_t > m_DestructionQueue
Definition: ComponentManager.h:329
friend class TestComponentManager
Definition: ComponentManager.h:337
void RegisterComponentType(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char *, const std::string &schema)
Definition: ComponentManager.cpp:523
std::map< MessageTypeId, std::vector< ComponentTypeId > > m_LocalMessageSubscriptions
Definition: ComponentManager.h:314
CEntityHandle GetSystemEntity()
Returns a CEntityHandle with id SYSTEM_ENTITY.
Definition: ComponentManager.h:149
bool DumpDebugState(std::ostream &stream, bool includeDebugInfo) const
Definition: ComponentManagerSerialization.cpp:49
void LoadComponentTypes()
Definition: ComponentManager.cpp:124
ComponentTypeId LookupCID(const std::string &cname) const
Definition: ComponentManager.cpp:613
std::map< MessageTypeId, std::vector< ComponentTypeId > > m_GlobalMessageSubscriptions
Definition: ComponentManager.h:315
std::unordered_map< entity_id_t, SEntityComponentCache * > m_ComponentCaches
Definition: ComponentManager.h:324
std::map< MessageTypeId, std::string > m_MessageTypeNamesById
Definition: ComponentManager.h:318
bool SerializeState(std::ostream &stream) const
Definition: ComponentManagerSerialization.cpp:180
void Script_RegisterSystemComponentType(int iid, const std::string &cname, JS::HandleValue ctor)
Definition: ComponentManager.cpp:332
void Script_RegisterInterface(const std::string &name)
Definition: ComponentManager.cpp:343
void SetRNGSeed(u32 seed)
Initializes the random number generator with a seed determined by the host.
Definition: ComponentManager.cpp:518
IComponent * ConstructComponent(CEntityHandle ent, ComponentTypeId cid)
Allocates a component object of type 'cid', and attaches it to entity 'ent'.
Definition: ComponentManager.cpp:718
void Script_RegisterComponentType(int iid, const std::string &cname, JS::HandleValue ctor)
Definition: ComponentManager.cpp:326
std::vector< int > Script_GetEntitiesWithInterface(int iid)
Definition: ComponentManager.cpp:408
bool m_CurrentlyHotloading
Definition: ComponentManager.h:307
A list of components that are dynamically subscribed to a particular message.
Definition: DynamicSubscription.h:37
Object wrapping an entity_id_t, with a SEntityComponentCache to support fast QueryInterface() / CmpPt...
Definition: Entity.h:80
entity_id_t GetId() const
Definition: Entity.h:88
Definition: Message.h:26
An entity initialisation parameter node.
Definition: ParamNode.h:151
Contains pointers to various 'global' objects that are needed by the simulation code,...
Definition: SimContext.h:33
Definition: IComponent.h:33
void(*)(IComponent *) DeallocFunc
Definition: IComponent.h:37
IComponent *(*)(const ScriptInterface &scriptInterface, JS::HandleValue ctor) AllocFunc
Definition: IComponent.h:36
Definition: path.h:80
Abstraction around a SpiderMonkey JSContext.
Definition: ScriptContext.h:46
Abstraction around a SpiderMonkey JS::Realm.
Definition: ScriptInterface.h:72
#define ASSERT(expr)
same as ENSURE in debug mode, does nothing in release mode.
Definition: debug.h:318
const entity_id_t SYSTEM_ENTITY
Entity ID for singleton 'system' components.
Definition: Entity.h:43
u32 entity_id_t
Entity ID type.
Definition: Entity.h:29
Definition: ComponentManager.h:66
AllocFunc alloc
Definition: ComponentManager.h:69
std::string schema
Definition: ComponentManager.h:72
InterfaceId iid
Definition: ComponentManager.h:68
EComponentTypeType type
Definition: ComponentManager.h:67
std::unique_ptr< JS::PersistentRootedValue > ctor
Definition: ComponentManager.h:73
std::string name
Definition: ComponentManager.h:71
DeallocFunc dealloc
Definition: ComponentManager.h:70
uint32_t u32
Definition: types.h:39