Line data Source code
1 : /* Copyright (C) 2022 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 : #include "precompiled.h"
19 :
20 : #include "ComponentManager.h"
21 :
22 : #include "lib/utf8.h"
23 : #include "ps/CLogger.h"
24 : #include "ps/Filesystem.h"
25 : #include "ps/Profile.h"
26 : #include "ps/scripting/JSInterface_VFS.h"
27 : #include "scriptinterface/FunctionWrapper.h"
28 : #include "simulation2/components/ICmpTemplateManager.h"
29 : #include "simulation2/MessageTypes.h"
30 : #include "simulation2/system/DynamicSubscription.h"
31 : #include "simulation2/system/IComponent.h"
32 : #include "simulation2/system/ParamNode.h"
33 : #include "simulation2/system/SimContext.h"
34 :
35 : #include <string_view>
36 :
37 : /**
38 : * Used for script-only message types.
39 : */
40 0 : class CMessageScripted final : public CMessage
41 : {
42 : public:
43 0 : virtual int GetType() const { return mtid; }
44 0 : virtual const char* GetScriptHandlerName() const { return handlerName.c_str(); }
45 0 : virtual const char* GetScriptGlobalHandlerName() const { return globalHandlerName.c_str(); }
46 0 : virtual JS::Value ToJSVal(const ScriptInterface& UNUSED(scriptInterface)) const { return msg.get(); }
47 :
48 0 : CMessageScripted(const ScriptInterface& scriptInterface, int mtid, const std::string& name, JS::HandleValue msg) :
49 0 : mtid(mtid), handlerName("On" + name), globalHandlerName("OnGlobal" + name), msg(scriptInterface.GetGeneralJSContext(), msg)
50 : {
51 0 : }
52 :
53 : int mtid;
54 : std::string handlerName;
55 : std::string globalHandlerName;
56 : JS::PersistentRootedValue msg;
57 : };
58 :
59 129 : CComponentManager::CComponentManager(CSimContext& context, std::shared_ptr<ScriptContext> cx, bool skipScriptFunctions) :
60 : m_NextScriptComponentTypeId(CID__LastNative),
61 : m_ScriptInterface("Engine", "Simulation", cx),
62 129 : m_SimContext(context), m_CurrentlyHotloading(false)
63 : {
64 129 : context.SetComponentManager(this);
65 :
66 129 : m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
67 129 : m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG);
68 :
69 : // For component script tests, the test system sets up its own scripted implementation of
70 : // these functions, so we skip registering them here in those cases
71 129 : if (!skipScriptFunctions)
72 : {
73 62 : JSI_VFS::RegisterScriptFunctions_ReadOnlySimulation(m_ScriptInterface);
74 124 : ScriptRequest rq(m_ScriptInterface);
75 62 : constexpr ScriptFunction::ObjectGetter<CComponentManager> Getter = &ScriptInterface::ObjectFromCBData<CComponentManager>;
76 62 : ScriptFunction::Register<&CComponentManager::Script_RegisterComponentType, Getter>(rq, "RegisterComponentType");
77 62 : ScriptFunction::Register<&CComponentManager::Script_RegisterSystemComponentType, Getter>(rq, "RegisterSystemComponentType");
78 62 : ScriptFunction::Register<&CComponentManager::Script_ReRegisterComponentType, Getter>(rq, "ReRegisterComponentType");
79 62 : ScriptFunction::Register<&CComponentManager::Script_RegisterInterface, Getter>(rq, "RegisterInterface");
80 62 : ScriptFunction::Register<&CComponentManager::Script_RegisterMessageType, Getter>(rq, "RegisterMessageType");
81 62 : ScriptFunction::Register<&CComponentManager::Script_RegisterGlobal, Getter>(rq, "RegisterGlobal");
82 62 : ScriptFunction::Register<&CComponentManager::Script_GetEntitiesWithInterface, Getter>(rq, "GetEntitiesWithInterface");
83 62 : ScriptFunction::Register<&CComponentManager::Script_GetComponentsWithInterface, Getter>(rq, "GetComponentsWithInterface");
84 62 : ScriptFunction::Register<&CComponentManager::Script_PostMessage, Getter>(rq, "PostMessage");
85 62 : ScriptFunction::Register<&CComponentManager::Script_BroadcastMessage, Getter>(rq, "BroadcastMessage");
86 62 : ScriptFunction::Register<&CComponentManager::Script_AddEntity, Getter>(rq, "AddEntity");
87 62 : ScriptFunction::Register<&CComponentManager::Script_AddLocalEntity, Getter>(rq, "AddLocalEntity");
88 62 : ScriptFunction::Register<&CComponentManager::QueryInterface, Getter>(rq, "QueryInterface");
89 62 : ScriptFunction::Register<&CComponentManager::DestroyComponentsSoon, Getter>(rq, "DestroyEntity");
90 62 : ScriptFunction::Register<&CComponentManager::FlushDestroyedComponents, Getter>(rq, "FlushDestroyedEntities");
91 62 : ScriptFunction::Register<&CComponentManager::Script_GetTemplate, Getter>(rq, "GetTemplate");
92 :
93 : }
94 :
95 : // Globalscripts may use VFS script functions
96 129 : m_ScriptInterface.LoadGlobalScripts();
97 :
98 : // Define MT_*, IID_* as script globals, and store their names
99 : #define MESSAGE(name) m_ScriptInterface.SetGlobal("MT_" #name, (int)MT_##name);
100 : #define INTERFACE(name) \
101 : m_ScriptInterface.SetGlobal("IID_" #name, (int)IID_##name); \
102 : m_InterfaceIdsByName[#name] = IID_##name;
103 : #define COMPONENT(name)
104 : #include "simulation2/TypeList.h"
105 : #undef MESSAGE
106 : #undef INTERFACE
107 : #undef COMPONENT
108 :
109 129 : m_ScriptInterface.SetGlobal("INVALID_ENTITY", (int)INVALID_ENTITY);
110 129 : m_ScriptInterface.SetGlobal("INVALID_PLAYER", (int)INVALID_PLAYER);
111 129 : m_ScriptInterface.SetGlobal("SYSTEM_ENTITY", (int)SYSTEM_ENTITY);
112 :
113 129 : m_ComponentsByInterface.resize(IID__LastNative);
114 :
115 129 : ResetState();
116 129 : }
117 :
118 258 : CComponentManager::~CComponentManager()
119 : {
120 129 : ResetState();
121 129 : }
122 :
123 116 : void CComponentManager::LoadComponentTypes()
124 : {
125 : #define MESSAGE(name) \
126 : RegisterMessageType(MT_##name, #name);
127 : #define INTERFACE(name) \
128 : extern void RegisterComponentInterface_##name(ScriptInterface&); \
129 : RegisterComponentInterface_##name(m_ScriptInterface);
130 : #define COMPONENT(name) \
131 : extern void RegisterComponentType_##name(CComponentManager&); \
132 : m_CurrentComponent = CID_##name; \
133 : RegisterComponentType_##name(*this);
134 :
135 : #include "simulation2/TypeList.h"
136 :
137 116 : m_CurrentComponent = CID__Invalid;
138 :
139 : #undef MESSAGE
140 : #undef INTERFACE
141 : #undef COMPONENT
142 116 : }
143 :
144 :
145 494 : bool CComponentManager::LoadScript(const VfsPath& filename, bool hotload)
146 : {
147 494 : m_CurrentlyHotloading = hotload;
148 988 : CVFSFile file;
149 494 : PSRETURN loadOk = file.Load(g_VFS, filename);
150 494 : if (loadOk != PSRETURN_OK) // VFS will log the failed file and the reason
151 0 : return false;
152 988 : std::string content = file.DecodeUTF8(); // assume it's UTF-8
153 494 : bool ok = m_ScriptInterface.LoadScript(filename, content);
154 :
155 494 : m_CurrentlyHotloading = false;
156 494 : return ok;
157 : }
158 :
159 71 : void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::string& cname, JS::HandleValue ctor, bool reRegister, bool systemComponent)
160 : {
161 141 : ScriptRequest rq(m_ScriptInterface);
162 :
163 : // Find the C++ component that wraps the interface
164 71 : int cidWrapper = GetScriptWrapper(iid);
165 71 : if (cidWrapper == CID__Invalid)
166 : {
167 1 : ScriptException::Raise(rq, "Invalid interface id");
168 1 : return;
169 : }
170 70 : const ComponentType& ctWrapper = m_ComponentTypesById[cidWrapper];
171 :
172 70 : bool mustReloadComponents = false; // for hotloading
173 :
174 70 : ComponentTypeId cid = LookupCID(cname);
175 70 : if (cid == CID__Invalid)
176 : {
177 63 : if (reRegister)
178 : {
179 0 : ScriptException::Raise(rq, "ReRegistering component type that was not registered before '%s'", cname.c_str());
180 0 : return;
181 : }
182 : // Allocate a new cid number
183 63 : cid = m_NextScriptComponentTypeId++;
184 63 : m_ComponentTypeIdsByName[cname] = cid;
185 63 : if (systemComponent)
186 0 : MarkScriptedComponentForSystemEntity(cid);
187 : }
188 : else
189 : {
190 : // Component type is already loaded, so do hotloading:
191 :
192 7 : if (!m_CurrentlyHotloading && !reRegister)
193 : {
194 0 : ScriptException::Raise(rq, "Registering component type with already-registered name '%s'", cname.c_str());
195 0 : return;
196 : }
197 :
198 7 : const ComponentType& ctPrevious = m_ComponentTypesById[cid];
199 :
200 : // We can only replace scripted component types, not native ones
201 7 : if (ctPrevious.type != CT_Script)
202 : {
203 0 : ScriptException::Raise(rq, "Loading script component type with same name '%s' as native component", cname.c_str());
204 0 : return;
205 : }
206 :
207 : // We don't support changing the IID of a component type (it would require fiddling
208 : // around with m_ComponentsByInterface and being careful to guarantee uniqueness per entity)
209 7 : if (ctPrevious.iid != iid)
210 : {
211 : // ...though it only matters if any components exist with this type
212 0 : if (!m_ComponentsByTypeId[cid].empty())
213 : {
214 0 : ScriptException::Raise(rq, "Hotloading script component type mustn't change interface ID");
215 0 : return;
216 : }
217 : }
218 :
219 : // Remove the old component type's message subscriptions
220 7 : std::map<MessageTypeId, std::vector<ComponentTypeId> >::iterator it;
221 154 : for (it = m_LocalMessageSubscriptions.begin(); it != m_LocalMessageSubscriptions.end(); ++it)
222 : {
223 147 : std::vector<ComponentTypeId>& types = it->second;
224 147 : std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid);
225 147 : if (ctit != types.end())
226 0 : types.erase(ctit);
227 : }
228 70 : for (it = m_GlobalMessageSubscriptions.begin(); it != m_GlobalMessageSubscriptions.end(); ++it)
229 : {
230 63 : std::vector<ComponentTypeId>& types = it->second;
231 63 : std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid);
232 63 : if (ctit != types.end())
233 0 : types.erase(ctit);
234 : }
235 :
236 7 : mustReloadComponents = true;
237 : }
238 :
239 140 : JS::RootedValue protoVal(rq.cx);
240 70 : if (!Script::GetProperty(rq, ctor, "prototype", &protoVal))
241 : {
242 0 : ScriptException::Raise(rq, "Failed to get property 'prototype'");
243 0 : return;
244 : }
245 70 : if (!protoVal.isObject())
246 : {
247 0 : ScriptException::Raise(rq, "Component has no constructor");
248 0 : return;
249 : }
250 140 : std::string schema = "<empty/>";
251 :
252 70 : if (Script::HasProperty(rq, protoVal, "Schema"))
253 16 : Script::GetProperty(rq, protoVal, "Schema", schema);
254 :
255 : // Construct a new ComponentType, using the wrapper's alloc functions
256 70 : ComponentType ct{
257 : CT_Script,
258 : iid,
259 70 : ctWrapper.alloc,
260 70 : ctWrapper.dealloc,
261 : cname,
262 : schema,
263 : std::make_unique<JS::PersistentRootedValue>(rq.cx, ctor)
264 280 : };
265 70 : m_ComponentTypesById[cid] = std::move(ct);
266 :
267 70 : m_CurrentComponent = cid; // needed by Subscribe
268 :
269 : // Find all the ctor prototype's On* methods, and subscribe to the appropriate messages:
270 140 : std::vector<std::string> methods;
271 :
272 70 : if (!Script::EnumeratePropertyNames(rq, protoVal, false, methods))
273 : {
274 0 : ScriptException::Raise(rq, "Failed to enumerate component properties.");
275 0 : return;
276 : }
277 :
278 1124 : for (const std::string& method : methods)
279 : {
280 1054 : if (std::string_view{method}.substr(0, 2) != "On")
281 1049 : continue;
282 :
283 5 : std::string_view name{std::string_view{method}.substr(2)}; // strip the "On" prefix
284 :
285 : // Handle "OnGlobalFoo" functions specially
286 5 : bool isGlobal = false;
287 5 : if (std::string_view{name}.substr(0, 6) == "Global")
288 : {
289 1 : isGlobal = true;
290 1 : name.remove_prefix(6);
291 : }
292 :
293 5 : auto mit = m_MessageTypeIdsByName.find(std::string{name});
294 5 : if (mit == m_MessageTypeIdsByName.end())
295 : {
296 0 : ScriptException::Raise(rq,
297 : "Registered component has unrecognized '%s' message handler method",
298 : method.c_str());
299 0 : return;
300 : }
301 :
302 5 : if (isGlobal)
303 1 : SubscribeGloballyToMessageType(mit->second);
304 : else
305 4 : SubscribeToMessageType(mit->second);
306 : }
307 :
308 70 : m_CurrentComponent = CID__Invalid;
309 :
310 70 : if (mustReloadComponents)
311 : {
312 : // For every script component with this cid, we need to switch its
313 : // prototype from the old constructor's prototype property to the new one's
314 7 : const std::map<entity_id_t, IComponent*>& comps = m_ComponentsByTypeId[cid];
315 7 : std::map<entity_id_t, IComponent*>::const_iterator eit = comps.begin();
316 17 : for (; eit != comps.end(); ++eit)
317 : {
318 10 : JS::RootedValue instance(rq.cx, eit->second->GetJSInstance());
319 5 : if (!instance.isNull())
320 5 : m_ScriptInterface.SetPrototype(instance, protoVal);
321 : }
322 : }
323 : }
324 :
325 70 : void CComponentManager::Script_RegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor)
326 : {
327 70 : Script_RegisterComponentType_Common(iid, cname, ctor, false, false);
328 70 : m_ScriptInterface.SetGlobal(cname.c_str(), ctor, m_CurrentlyHotloading);
329 70 : }
330 :
331 0 : void CComponentManager::Script_RegisterSystemComponentType(int iid, const std::string& cname, JS::HandleValue ctor)
332 : {
333 0 : Script_RegisterComponentType_Common(iid, cname, ctor, false, true);
334 0 : m_ScriptInterface.SetGlobal(cname.c_str(), ctor, m_CurrentlyHotloading);
335 0 : }
336 :
337 1 : void CComponentManager::Script_ReRegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor)
338 : {
339 1 : Script_RegisterComponentType_Common(iid, cname, ctor, true, false);
340 1 : }
341 :
342 6 : void CComponentManager::Script_RegisterInterface(const std::string& name)
343 : {
344 6 : std::map<std::string, InterfaceId>::iterator it = m_InterfaceIdsByName.find(name);
345 6 : if (it != m_InterfaceIdsByName.end())
346 : {
347 : // Redefinitions are fine (and just get ignored) when hotloading; otherwise
348 : // they're probably unintentional and should be reported
349 3 : if (!m_CurrentlyHotloading)
350 : {
351 0 : ScriptRequest rq(m_ScriptInterface);
352 0 : ScriptException::Raise(rq, "Registering interface with already-registered name '%s'", name.c_str());
353 : }
354 6 : return;
355 : }
356 :
357 : // IIDs start at 1, so size+1 is the next unused one
358 3 : size_t id = m_InterfaceIdsByName.size() + 1;
359 3 : m_InterfaceIdsByName[name] = (InterfaceId)id;
360 3 : m_ComponentsByInterface.resize(id+1); // add one so we can index by InterfaceId
361 3 : m_ScriptInterface.SetGlobal(("IID_" + name).c_str(), (int)id);
362 : }
363 :
364 0 : void CComponentManager::Script_RegisterMessageType(const std::string& name)
365 : {
366 0 : std::map<std::string, MessageTypeId>::iterator it = m_MessageTypeIdsByName.find(name);
367 0 : if (it != m_MessageTypeIdsByName.end())
368 : {
369 : // Redefinitions are fine (and just get ignored) when hotloading; otherwise
370 : // they're probably unintentional and should be reported
371 0 : if (!m_CurrentlyHotloading)
372 : {
373 0 : ScriptRequest rq(m_ScriptInterface);
374 0 : ScriptException::Raise(rq, "Registering message type with already-registered name '%s'", name.c_str());
375 : }
376 0 : return;
377 : }
378 :
379 : // MTIDs start at 1, so size+1 is the next unused one
380 0 : size_t id = m_MessageTypeIdsByName.size() + 1;
381 0 : RegisterMessageType((MessageTypeId)id, name.c_str());
382 0 : m_ScriptInterface.SetGlobal(("MT_" + name).c_str(), (int)id);
383 : }
384 :
385 6 : void CComponentManager::Script_RegisterGlobal(const std::string& name, JS::HandleValue value)
386 : {
387 6 : m_ScriptInterface.SetGlobal(name.c_str(), value, m_CurrentlyHotloading);
388 6 : }
389 :
390 0 : const CParamNode& CComponentManager::Script_GetTemplate(const std::string& templateName)
391 : {
392 0 : static CParamNode nullNode(false);
393 :
394 0 : ICmpTemplateManager* cmpTemplateManager = static_cast<ICmpTemplateManager*> (QueryInterface(SYSTEM_ENTITY, IID_TemplateManager));
395 0 : if (!cmpTemplateManager)
396 : {
397 0 : LOGERROR("Template manager is not loaded");
398 0 : return nullNode;
399 : }
400 :
401 0 : const CParamNode* tmpl = cmpTemplateManager->GetTemplate(templateName);
402 0 : if (!tmpl)
403 0 : return nullNode;
404 0 : return *tmpl;
405 : }
406 :
407 0 : std::vector<int> CComponentManager::Script_GetEntitiesWithInterface(int iid)
408 : {
409 0 : std::vector<int> ret;
410 0 : const InterfaceListUnordered& ents = GetEntitiesWithInterfaceUnordered(iid);
411 0 : for (InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
412 0 : if (!ENTITY_IS_LOCAL(it->first))
413 0 : ret.push_back(it->first);
414 0 : std::sort(ret.begin(), ret.end());
415 0 : return ret;
416 : }
417 :
418 0 : std::vector<IComponent*> CComponentManager::Script_GetComponentsWithInterface(int iid)
419 : {
420 0 : std::vector<IComponent*> ret;
421 0 : InterfaceList ents = GetEntitiesWithInterface(iid);
422 0 : for (InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
423 0 : ret.push_back(it->second); // TODO: maybe we should exclude local entities
424 0 : return ret;
425 : }
426 :
427 3 : CMessage* CComponentManager::ConstructMessage(int mtid, JS::HandleValue data)
428 : {
429 3 : if (mtid == MT__Invalid || mtid > (int)m_MessageTypeIdsByName.size()) // (IDs start at 1 so use '>' here)
430 0 : LOGERROR("PostMessage with invalid message type ID '%d'", mtid);
431 :
432 3 : if (mtid < MT__LastNative)
433 : {
434 3 : return CMessageFromJSVal(mtid, m_ScriptInterface, data);
435 : }
436 : else
437 : {
438 0 : return new CMessageScripted(m_ScriptInterface, mtid, m_MessageTypeNamesById[mtid], data);
439 : }
440 : }
441 :
442 2 : void CComponentManager::Script_PostMessage(int ent, int mtid, JS::HandleValue data)
443 : {
444 2 : CMessage* msg = ConstructMessage(mtid, data);
445 2 : if (!msg)
446 0 : return; // error
447 :
448 2 : PostMessage(ent, *msg);
449 :
450 2 : delete msg;
451 : }
452 :
453 1 : void CComponentManager::Script_BroadcastMessage(int mtid, JS::HandleValue data)
454 : {
455 1 : CMessage* msg = ConstructMessage(mtid, data);
456 1 : if (!msg)
457 0 : return; // error
458 :
459 1 : BroadcastMessage(*msg);
460 :
461 1 : delete msg;
462 : }
463 :
464 2 : int CComponentManager::Script_AddEntity(const std::wstring& templateName)
465 : {
466 : // TODO: should validate the string to make sure it doesn't contain scary characters
467 : // that will let it access non-component-template files
468 2 : return AddEntity(templateName, AllocateNewEntity());
469 : }
470 :
471 2 : int CComponentManager::Script_AddLocalEntity(const std::wstring& templateName)
472 : {
473 : // TODO: should validate the string to make sure it doesn't contain scary characters
474 : // that will let it access non-component-template files
475 2 : return AddEntity(templateName, AllocateNewLocalEntity());
476 : }
477 :
478 265 : void CComponentManager::ResetState()
479 : {
480 : // Delete all dynamic message subscriptions
481 265 : m_DynamicMessageSubscriptionsNonsync.clear();
482 265 : m_DynamicMessageSubscriptionsNonsyncByComponent.clear();
483 :
484 : // Delete all IComponents in reverse order of creation.
485 265 : std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::reverse_iterator iit = m_ComponentsByTypeId.rbegin();
486 529 : for (; iit != m_ComponentsByTypeId.rend(); ++iit)
487 : {
488 132 : std::map<entity_id_t, IComponent*>::iterator eit = iit->second.begin();
489 420 : for (; eit != iit->second.end(); ++eit)
490 : {
491 144 : eit->second->Deinit();
492 144 : m_ComponentTypesById[iit->first].dealloc(eit->second);
493 : }
494 : }
495 :
496 265 : std::vector<std::unordered_map<entity_id_t, IComponent*> >::iterator ifcit = m_ComponentsByInterface.begin();
497 26773 : for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
498 13254 : ifcit->clear();
499 :
500 265 : m_ComponentsByTypeId.clear();
501 :
502 : // Delete all SEntityComponentCaches
503 265 : std::unordered_map<entity_id_t, SEntityComponentCache*>::iterator ccit = m_ComponentCaches.begin();
504 469 : for (; ccit != m_ComponentCaches.end(); ++ccit)
505 102 : free(ccit->second);
506 265 : m_ComponentCaches.clear();
507 265 : m_SystemEntity = CEntityHandle();
508 :
509 265 : m_DestructionQueue.clear();
510 :
511 : // Reset IDs
512 265 : m_NextEntityId = SYSTEM_ENTITY + 1;
513 265 : m_NextLocalEntityId = FIRST_LOCAL_ENTITY;
514 265 : }
515 :
516 2 : void CComponentManager::SetRNGSeed(u32 seed)
517 : {
518 2 : m_RNG.seed(seed);
519 2 : }
520 :
521 3828 : void CComponentManager::RegisterComponentType(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc,
522 : const char* name, const std::string& schema)
523 : {
524 7656 : ComponentType c{ CT_Native, iid, alloc, dealloc, name, schema, std::unique_ptr<JS::PersistentRootedValue>() };
525 3828 : m_ComponentTypesById.insert(std::make_pair(cid, std::move(c)));
526 3828 : m_ComponentTypeIdsByName[name] = cid;
527 3828 : }
528 :
529 2436 : void CComponentManager::RegisterComponentTypeScriptWrapper(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc,
530 : DeallocFunc dealloc, const char* name, const std::string& schema)
531 : {
532 4872 : ComponentType c{ CT_ScriptWrapper, iid, alloc, dealloc, name, schema, std::unique_ptr<JS::PersistentRootedValue>() };
533 2436 : m_ComponentTypesById.insert(std::make_pair(cid, std::move(c)));
534 2436 : m_ComponentTypeIdsByName[name] = cid;
535 : // TODO: merge with RegisterComponentType
536 2436 : }
537 :
538 0 : void CComponentManager::MarkScriptedComponentForSystemEntity(CComponentManager::ComponentTypeId cid)
539 : {
540 0 : m_ScriptedSystemComponents.push_back(cid);
541 0 : }
542 :
543 3712 : void CComponentManager::RegisterMessageType(MessageTypeId mtid, const char* name)
544 : {
545 3712 : m_MessageTypeIdsByName[name] = mtid;
546 3712 : m_MessageTypeNamesById[mtid] = name;
547 3712 : }
548 :
549 8820 : void CComponentManager::SubscribeToMessageType(MessageTypeId mtid)
550 : {
551 : // TODO: verify mtid
552 8820 : ENSURE(m_CurrentComponent != CID__Invalid);
553 8820 : std::vector<ComponentTypeId>& types = m_LocalMessageSubscriptions[mtid];
554 8820 : types.push_back(m_CurrentComponent);
555 8820 : std::sort(types.begin(), types.end()); // TODO: just sort once at the end of LoadComponents
556 8820 : }
557 :
558 1509 : void CComponentManager::SubscribeGloballyToMessageType(MessageTypeId mtid)
559 : {
560 : // TODO: verify mtid
561 1509 : ENSURE(m_CurrentComponent != CID__Invalid);
562 1509 : std::vector<ComponentTypeId>& types = m_GlobalMessageSubscriptions[mtid];
563 1509 : types.push_back(m_CurrentComponent);
564 1509 : std::sort(types.begin(), types.end()); // TODO: just sort once at the end of LoadComponents
565 1509 : }
566 :
567 4 : void CComponentManager::FlattenDynamicSubscriptions()
568 : {
569 4 : std::map<MessageTypeId, CDynamicSubscription>::iterator it;
570 5 : for (it = m_DynamicMessageSubscriptionsNonsync.begin();
571 5 : it != m_DynamicMessageSubscriptionsNonsync.end(); ++it)
572 : {
573 1 : it->second.Flatten();
574 : }
575 4 : }
576 :
577 5 : void CComponentManager::DynamicSubscriptionNonsync(MessageTypeId mtid, IComponent* component, bool enable)
578 : {
579 5 : if (enable)
580 : {
581 2 : bool newlyInserted = m_DynamicMessageSubscriptionsNonsyncByComponent[component].insert(mtid).second;
582 2 : if (newlyInserted)
583 2 : m_DynamicMessageSubscriptionsNonsync[mtid].Add(component);
584 : }
585 : else
586 : {
587 3 : size_t numRemoved = m_DynamicMessageSubscriptionsNonsyncByComponent[component].erase(mtid);
588 3 : if (numRemoved)
589 1 : m_DynamicMessageSubscriptionsNonsync[mtid].Remove(component);
590 : }
591 5 : }
592 :
593 7 : void CComponentManager::RemoveComponentDynamicSubscriptions(IComponent* component)
594 : {
595 7 : std::map<IComponent*, std::set<MessageTypeId> >::iterator it = m_DynamicMessageSubscriptionsNonsyncByComponent.find(component);
596 7 : if (it == m_DynamicMessageSubscriptionsNonsyncByComponent.end())
597 3 : return;
598 :
599 4 : std::set<MessageTypeId>::iterator mtit;
600 5 : for (mtit = it->second.begin(); mtit != it->second.end(); ++mtit)
601 : {
602 1 : m_DynamicMessageSubscriptionsNonsync[*mtit].Remove(component);
603 :
604 : // Need to flatten the subscription lists immediately to avoid dangling IComponent* references
605 1 : m_DynamicMessageSubscriptionsNonsync[*mtit].Flatten();
606 : }
607 :
608 4 : m_DynamicMessageSubscriptionsNonsyncByComponent.erase(it);
609 : }
610 :
611 133 : CComponentManager::ComponentTypeId CComponentManager::LookupCID(const std::string& cname) const
612 : {
613 133 : std::map<std::string, ComponentTypeId>::const_iterator it = m_ComponentTypeIdsByName.find(cname);
614 133 : if (it == m_ComponentTypeIdsByName.end())
615 63 : return CID__Invalid;
616 70 : return it->second;
617 : }
618 :
619 8 : std::string CComponentManager::LookupComponentTypeName(ComponentTypeId cid) const
620 : {
621 8 : std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.find(cid);
622 8 : if (it == m_ComponentTypesById.end())
623 0 : return "";
624 8 : return it->second.name;
625 : }
626 :
627 71 : CComponentManager::ComponentTypeId CComponentManager::GetScriptWrapper(InterfaceId iid)
628 : {
629 71 : if (iid >= IID__LastNative && iid <= (int)m_InterfaceIdsByName.size()) // use <= since IDs start at 1
630 6 : return CID_UnknownScript;
631 :
632 65 : std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.begin();
633 457 : for (; it != m_ComponentTypesById.end(); ++it)
634 260 : if (it->second.iid == iid && it->second.type == CT_ScriptWrapper)
635 64 : return it->first;
636 :
637 1 : std::map<std::string, InterfaceId>::const_iterator iiit = m_InterfaceIdsByName.begin();
638 99 : for (; iiit != m_InterfaceIdsByName.end(); ++iiit)
639 49 : if (iiit->second == iid)
640 : {
641 0 : LOGERROR("No script wrapper found for interface id %d '%s'", iid, iiit->first.c_str());
642 0 : return CID__Invalid;
643 : }
644 :
645 1 : LOGERROR("No script wrapper found for interface id %d", iid);
646 1 : return CID__Invalid;
647 : }
648 :
649 15 : entity_id_t CComponentManager::AllocateNewEntity()
650 : {
651 15 : entity_id_t id = m_NextEntityId++;
652 : // TODO: check for overflow
653 15 : return id;
654 : }
655 :
656 6 : entity_id_t CComponentManager::AllocateNewLocalEntity()
657 : {
658 6 : entity_id_t id = m_NextLocalEntityId++;
659 : // TODO: check for overflow
660 6 : return id;
661 : }
662 :
663 2 : entity_id_t CComponentManager::AllocateNewEntity(entity_id_t preferredId)
664 : {
665 : // TODO: ensure this ID hasn't been allocated before
666 : // (this might occur with broken map files)
667 : // Trying to actually add two entities with the same id will fail in AddEntitiy
668 2 : entity_id_t id = preferredId;
669 :
670 : // Ensure this ID won't be allocated again
671 2 : if (id >= m_NextEntityId)
672 2 : m_NextEntityId = id+1;
673 : // TODO: check for overflow
674 :
675 2 : return id;
676 : }
677 :
678 139 : bool CComponentManager::AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode& paramNode)
679 : {
680 139 : IComponent* component = ConstructComponent(ent, cid);
681 139 : if (!component)
682 2 : return false;
683 :
684 137 : component->Init(paramNode);
685 137 : return true;
686 : }
687 :
688 3 : void CComponentManager::AddSystemComponents(bool skipScriptedComponents, bool skipAI)
689 : {
690 6 : CParamNode noParam;
691 3 : AddComponent(m_SystemEntity, CID_TemplateManager, noParam);
692 3 : AddComponent(m_SystemEntity, CID_CinemaManager, noParam);
693 3 : AddComponent(m_SystemEntity, CID_CommandQueue, noParam);
694 3 : AddComponent(m_SystemEntity, CID_ObstructionManager, noParam);
695 3 : AddComponent(m_SystemEntity, CID_ParticleManager, noParam);
696 3 : AddComponent(m_SystemEntity, CID_Pathfinder, noParam);
697 3 : AddComponent(m_SystemEntity, CID_ProjectileManager, noParam);
698 3 : AddComponent(m_SystemEntity, CID_RangeManager, noParam);
699 3 : AddComponent(m_SystemEntity, CID_SoundManager, noParam);
700 3 : AddComponent(m_SystemEntity, CID_Terrain, noParam);
701 3 : AddComponent(m_SystemEntity, CID_TerritoryManager, noParam);
702 3 : AddComponent(m_SystemEntity, CID_UnitMotionManager, noParam);
703 3 : AddComponent(m_SystemEntity, CID_UnitRenderer, noParam);
704 3 : AddComponent(m_SystemEntity, CID_WaterManager, noParam);
705 :
706 : // Add scripted system components:
707 3 : if (!skipScriptedComponents)
708 : {
709 0 : for (uint32_t i = 0; i < m_ScriptedSystemComponents.size(); ++i)
710 0 : AddComponent(m_SystemEntity, m_ScriptedSystemComponents[i], noParam);
711 0 : if (!skipAI)
712 0 : AddComponent(m_SystemEntity, CID_AIManager, noParam);
713 : }
714 3 : }
715 :
716 153 : IComponent* CComponentManager::ConstructComponent(CEntityHandle ent, ComponentTypeId cid)
717 : {
718 306 : ScriptRequest rq(m_ScriptInterface);
719 :
720 153 : std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.find(cid);
721 153 : if (it == m_ComponentTypesById.end())
722 : {
723 1 : LOGERROR("Invalid component id %d", cid);
724 1 : return NULL;
725 : }
726 :
727 152 : const ComponentType& ct = it->second;
728 :
729 152 : ENSURE((size_t)ct.iid < m_ComponentsByInterface.size());
730 :
731 152 : std::unordered_map<entity_id_t, IComponent*>& emap1 = m_ComponentsByInterface[ct.iid];
732 152 : if (emap1.find(ent.GetId()) != emap1.end())
733 : {
734 1 : LOGERROR("Multiple components for interface %d", ct.iid);
735 1 : return NULL;
736 : }
737 :
738 151 : std::map<entity_id_t, IComponent*>& emap2 = m_ComponentsByTypeId[cid];
739 :
740 : // If this is a scripted component, construct the appropriate JS object first
741 302 : JS::RootedValue obj(rq.cx);
742 151 : if (ct.type == CT_Script)
743 : {
744 49 : m_ScriptInterface.CallConstructor(*ct.ctor, JS::HandleValueArray::empty(), &obj);
745 49 : if (obj.isNull())
746 : {
747 0 : LOGERROR("Script component constructor failed");
748 0 : return NULL;
749 : }
750 : }
751 :
752 : // Construct the new component
753 : // NB: The unit motion manager relies on components not moving in memory once constructed.
754 151 : IComponent* component = ct.alloc(m_ScriptInterface, obj);
755 151 : ENSURE(component);
756 :
757 151 : component->SetEntityHandle(ent);
758 151 : component->SetSimContext(m_SimContext);
759 :
760 : // Store a reference to the new component
761 151 : emap1.insert(std::make_pair(ent.GetId(), component));
762 151 : emap2.insert(std::make_pair(ent.GetId(), component));
763 : // TODO: We need to more careful about this - if an entity is constructed by a component
764 : // while we're iterating over all components, this will invalidate the iterators and everything
765 : // will break.
766 : // We probably need some kind of delayed addition, so they get pushed onto a queue and then
767 : // inserted into the world later on. (Be careful about immediation deletion in that case, too.)
768 :
769 151 : SEntityComponentCache* cache = ent.GetComponentCache();
770 151 : ENSURE(cache != NULL && ct.iid < (int)cache->numInterfaces && cache->interfaces[ct.iid] == NULL);
771 151 : cache->interfaces[ct.iid] = component;
772 :
773 151 : return component;
774 : }
775 :
776 19 : void CComponentManager::AddMockComponent(CEntityHandle ent, InterfaceId iid, IComponent& component)
777 : {
778 : // Just add it into the by-interface map, not the by-component-type map,
779 : // so it won't be considered for messages or deletion etc
780 :
781 19 : std::unordered_map<entity_id_t, IComponent*>& emap1 = m_ComponentsByInterface.at(iid);
782 19 : if (emap1.find(ent.GetId()) != emap1.end())
783 0 : debug_warn(L"Multiple components for interface");
784 19 : emap1.insert(std::make_pair(ent.GetId(), &component));
785 :
786 19 : SEntityComponentCache* cache = ent.GetComponentCache();
787 19 : ENSURE(cache != NULL && iid < (int)cache->numInterfaces && cache->interfaces[iid] == NULL);
788 19 : cache->interfaces[iid] = &component;
789 19 : }
790 :
791 106 : CEntityHandle CComponentManager::AllocateEntityHandle(entity_id_t ent)
792 : {
793 106 : ENSURE(!EntityExists(ent));
794 :
795 : // Interface IDs start at 1, and SEntityComponentCache is defined with a 1-sized array,
796 : // so we need space for an extra m_InterfaceIdsByName.size() items
797 106 : SEntityComponentCache* cache = (SEntityComponentCache*)calloc(1,
798 212 : sizeof(SEntityComponentCache) + sizeof(IComponent*) * m_InterfaceIdsByName.size());
799 106 : ENSURE(cache != NULL);
800 106 : cache->numInterfaces = m_InterfaceIdsByName.size() + 1;
801 :
802 106 : m_ComponentCaches[ent] = cache;
803 :
804 106 : return CEntityHandle(ent, cache);
805 : }
806 :
807 50 : CEntityHandle CComponentManager::LookupEntityHandle(entity_id_t ent, bool allowCreate)
808 : {
809 50 : std::unordered_map<entity_id_t, SEntityComponentCache*>::iterator it;
810 50 : it = m_ComponentCaches.find(ent);
811 50 : if (it == m_ComponentCaches.end())
812 : {
813 27 : if (allowCreate)
814 27 : return AllocateEntityHandle(ent);
815 : else
816 0 : return CEntityHandle(ent, NULL);
817 : }
818 : else
819 23 : return CEntityHandle(ent, it->second);
820 : }
821 :
822 26 : void CComponentManager::InitSystemEntity()
823 : {
824 26 : ENSURE(m_SystemEntity.GetId() == INVALID_ENTITY);
825 26 : m_SystemEntity = AllocateEntityHandle(SYSTEM_ENTITY);
826 26 : m_SimContext.SetSystemEntity(m_SystemEntity);
827 26 : }
828 :
829 10 : entity_id_t CComponentManager::AddEntity(const std::wstring& templateName, entity_id_t ent)
830 : {
831 10 : ICmpTemplateManager *cmpTemplateManager = static_cast<ICmpTemplateManager*> (QueryInterface(SYSTEM_ENTITY, IID_TemplateManager));
832 10 : if (!cmpTemplateManager)
833 : {
834 0 : debug_warn(L"No ICmpTemplateManager loaded");
835 0 : return INVALID_ENTITY;
836 : }
837 :
838 10 : const CParamNode* tmpl = cmpTemplateManager->LoadTemplate(ent, utf8_from_wstring(templateName));
839 10 : if (!tmpl)
840 2 : return INVALID_ENTITY; // LoadTemplate will have reported the error
841 :
842 : // This also ensures that ent does not exist
843 8 : CEntityHandle handle = AllocateEntityHandle(ent);
844 :
845 : // Construct a component for each child of the root element
846 8 : const CParamNode::ChildrenMap& tmplChilds = tmpl->GetChildren();
847 24 : for (CParamNode::ChildrenMap::const_iterator it = tmplChilds.begin(); it != tmplChilds.end(); ++it)
848 : {
849 : // Ignore attributes on the root element
850 16 : if (it->first.length() && it->first[0] == '@')
851 1 : continue;
852 :
853 15 : CComponentManager::ComponentTypeId cid = LookupCID(it->first);
854 15 : if (cid == CID__Invalid)
855 : {
856 0 : LOGERROR("Unrecognized component type name '%s' in entity template '%s'", it->first, utf8_from_wstring(templateName));
857 0 : return INVALID_ENTITY;
858 : }
859 :
860 15 : if (!AddComponent(handle, cid, it->second))
861 : {
862 0 : LOGERROR("Failed to construct component type name '%s' in entity template '%s'", it->first, utf8_from_wstring(templateName));
863 0 : return INVALID_ENTITY;
864 : }
865 : // TODO: maybe we should delete already-constructed components if one of them fails?
866 : }
867 :
868 16 : CMessageCreate msg(ent);
869 8 : PostMessage(ent, msg);
870 :
871 8 : return ent;
872 : }
873 :
874 113 : bool CComponentManager::EntityExists(entity_id_t ent) const
875 : {
876 113 : return m_ComponentCaches.find(ent) != m_ComponentCaches.end();
877 : }
878 :
879 :
880 7 : void CComponentManager::DestroyComponentsSoon(entity_id_t ent)
881 : {
882 7 : m_DestructionQueue.push_back(ent);
883 7 : }
884 :
885 7 : void CComponentManager::FlushDestroyedComponents()
886 : {
887 14 : PROFILE2("Flush Destroyed Components");
888 19 : while (!m_DestructionQueue.empty())
889 : {
890 : // Make a copy of the destruction queue, so that the iterators won't be invalidated if the
891 : // CMessageDestroy handlers try to destroy more entities themselves
892 12 : std::vector<entity_id_t> queue;
893 6 : queue.swap(m_DestructionQueue);
894 :
895 13 : for (std::vector<entity_id_t>::iterator it = queue.begin(); it != queue.end(); ++it)
896 : {
897 7 : entity_id_t ent = *it;
898 :
899 : // Do nothing if invalid, destroyed, etc.
900 7 : if (!EntityExists(ent))
901 3 : continue;
902 :
903 4 : CEntityHandle handle = LookupEntityHandle(ent);
904 :
905 8 : CMessageDestroy msg(ent);
906 4 : PostMessage(ent, msg);
907 :
908 : // Flatten all the dynamic subscriptions to ensure there are no dangling
909 : // references in the 'removed' lists to components we're going to delete
910 : // Some components may have dynamically unsubscribed following the Destroy message
911 4 : FlattenDynamicSubscriptions();
912 :
913 : // Destroy the components, and remove from m_ComponentsByTypeId:
914 4 : std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::iterator iit = m_ComponentsByTypeId.begin();
915 74 : for (; iit != m_ComponentsByTypeId.end(); ++iit)
916 : {
917 35 : std::map<entity_id_t, IComponent*>::iterator eit = iit->second.find(ent);
918 35 : if (eit != iit->second.end())
919 : {
920 7 : eit->second->Deinit();
921 7 : RemoveComponentDynamicSubscriptions(eit->second);
922 7 : m_ComponentTypesById[iit->first].dealloc(eit->second);
923 7 : iit->second.erase(ent);
924 7 : handle.GetComponentCache()->interfaces[m_ComponentTypesById[iit->first].iid] = NULL;
925 : }
926 : }
927 :
928 4 : free(handle.GetComponentCache());
929 4 : m_ComponentCaches.erase(ent);
930 :
931 : // Remove from m_ComponentsByInterface
932 4 : std::vector<std::unordered_map<entity_id_t, IComponent*> >::iterator ifcit = m_ComponentsByInterface.begin();
933 404 : for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
934 : {
935 200 : ifcit->erase(ent);
936 : }
937 : }
938 : }
939 7 : }
940 :
941 293 : IComponent* CComponentManager::QueryInterface(entity_id_t ent, InterfaceId iid) const
942 : {
943 293 : if ((size_t)iid >= m_ComponentsByInterface.size())
944 : {
945 : // Invalid iid
946 0 : return NULL;
947 : }
948 :
949 293 : std::unordered_map<entity_id_t, IComponent*>::const_iterator eit = m_ComponentsByInterface[iid].find(ent);
950 293 : if (eit == m_ComponentsByInterface[iid].end())
951 : {
952 : // This entity doesn't implement this interface
953 32 : return NULL;
954 : }
955 :
956 261 : return eit->second;
957 : }
958 :
959 0 : CComponentManager::InterfaceList CComponentManager::GetEntitiesWithInterface(InterfaceId iid) const
960 : {
961 0 : std::vector<std::pair<entity_id_t, IComponent*> > ret;
962 :
963 0 : if ((size_t)iid >= m_ComponentsByInterface.size())
964 : {
965 : // Invalid iid
966 0 : return ret;
967 : }
968 :
969 0 : ret.reserve(m_ComponentsByInterface[iid].size());
970 :
971 0 : std::unordered_map<entity_id_t, IComponent*>::const_iterator it = m_ComponentsByInterface[iid].begin();
972 0 : for (; it != m_ComponentsByInterface[iid].end(); ++it)
973 0 : ret.push_back(*it);
974 :
975 0 : std::sort(ret.begin(), ret.end()); // lexicographic pair comparison means this'll sort by entity ID
976 :
977 0 : return ret;
978 : }
979 :
980 1 : static CComponentManager::InterfaceListUnordered g_EmptyEntityMap;
981 0 : const CComponentManager::InterfaceListUnordered& CComponentManager::GetEntitiesWithInterfaceUnordered(InterfaceId iid) const
982 : {
983 0 : if ((size_t)iid >= m_ComponentsByInterface.size())
984 : {
985 : // Invalid iid
986 0 : return g_EmptyEntityMap;
987 : }
988 :
989 0 : return m_ComponentsByInterface[iid];
990 : }
991 :
992 54 : void CComponentManager::PostMessage(entity_id_t ent, const CMessage& msg)
993 : {
994 108 : PROFILE2_IFSPIKE("Post Message", 0.0005);
995 54 : PROFILE2_ATTR("%s", msg.GetScriptHandlerName());
996 : // Send the message to components of ent, that subscribed locally to this message
997 54 : std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it;
998 54 : it = m_LocalMessageSubscriptions.find(msg.GetType());
999 54 : if (it != m_LocalMessageSubscriptions.end())
1000 : {
1001 52 : std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
1002 344 : for (; ctit != it->second.end(); ++ctit)
1003 : {
1004 : // Find the component instances of this type (if any)
1005 146 : std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
1006 146 : if (emap == m_ComponentsByTypeId.end())
1007 134 : continue;
1008 :
1009 : // Send the message to all of them
1010 12 : std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.find(ent);
1011 12 : if (eit != emap->second.end())
1012 8 : eit->second->HandleMessage(msg, false);
1013 : }
1014 : }
1015 :
1016 54 : SendGlobalMessage(ent, msg);
1017 54 : }
1018 :
1019 10 : void CComponentManager::BroadcastMessage(const CMessage& msg)
1020 : {
1021 : // Send the message to components of all entities that subscribed locally to this message
1022 10 : std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it;
1023 10 : it = m_LocalMessageSubscriptions.find(msg.GetType());
1024 10 : if (it != m_LocalMessageSubscriptions.end())
1025 : {
1026 10 : std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
1027 150 : for (; ctit != it->second.end(); ++ctit)
1028 : {
1029 : // Find the component instances of this type (if any)
1030 70 : std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
1031 70 : if (emap == m_ComponentsByTypeId.end())
1032 56 : continue;
1033 :
1034 : // Send the message to all of them
1035 14 : std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin();
1036 52 : for (; eit != emap->second.end(); ++eit)
1037 19 : eit->second->HandleMessage(msg, false);
1038 : }
1039 : }
1040 :
1041 10 : SendGlobalMessage(INVALID_ENTITY, msg);
1042 10 : }
1043 :
1044 64 : void CComponentManager::SendGlobalMessage(entity_id_t ent, const CMessage& msg)
1045 : {
1046 128 : PROFILE2_IFSPIKE("SendGlobalMessage", 0.001);
1047 64 : PROFILE2_ATTR("%s", msg.GetScriptHandlerName());
1048 : // (Common functionality for PostMessage and BroadcastMessage)
1049 :
1050 : // Send the message to components of all entities that subscribed globally to this message
1051 64 : std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it;
1052 64 : it = m_GlobalMessageSubscriptions.find(msg.GetType());
1053 64 : if (it != m_GlobalMessageSubscriptions.end())
1054 : {
1055 30 : std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
1056 124 : for (; ctit != it->second.end(); ++ctit)
1057 : {
1058 : // Special case: Messages for local entities shouldn't be sent to script
1059 : // components that subscribed globally, so that we don't have to worry about
1060 : // them accidentally picking up non-network-synchronised data.
1061 47 : if (ENTITY_IS_LOCAL(ent))
1062 : {
1063 1 : std::map<ComponentTypeId, ComponentType>::const_iterator cit = m_ComponentTypesById.find(*ctit);
1064 1 : if (cit != m_ComponentTypesById.end() && cit->second.type == CT_Script)
1065 0 : continue;
1066 : }
1067 :
1068 : // Find the component instances of this type (if any)
1069 47 : std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
1070 47 : if (emap == m_ComponentsByTypeId.end())
1071 32 : continue;
1072 :
1073 : // Send the message to all of them
1074 15 : std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin();
1075 45 : for (; eit != emap->second.end(); ++eit)
1076 15 : eit->second->HandleMessage(msg, true);
1077 : }
1078 : }
1079 :
1080 : // Send the message to component instances that dynamically subscribed to this message
1081 64 : std::map<MessageTypeId, CDynamicSubscription>::iterator dit = m_DynamicMessageSubscriptionsNonsync.find(msg.GetType());
1082 64 : if (dit != m_DynamicMessageSubscriptionsNonsync.end())
1083 : {
1084 0 : dit->second.Flatten();
1085 0 : const std::vector<IComponent*>& dynamic = dit->second.GetComponents();
1086 0 : for (size_t i = 0; i < dynamic.size(); i++)
1087 0 : dynamic[i]->HandleMessage(msg, false);
1088 : }
1089 64 : }
1090 :
1091 11 : std::string CComponentManager::GenerateSchema() const
1092 : {
1093 : std::string schema =
1094 : "<grammar xmlns='http://relaxng.org/ns/structure/1.0' xmlns:a='http://ns.wildfiregames.com/entity' datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes'>"
1095 : "<define name='decimal'>"
1096 : "<data type='decimal'/>"
1097 : "</define>"
1098 : "<define name='nonNegativeDecimal'>"
1099 : "<data type='decimal'><param name='minInclusive'>0</param></data>"
1100 : "</define>"
1101 : "<define name='positiveDecimal'>"
1102 : "<data type='decimal'><param name='minExclusive'>0</param></data>"
1103 : "</define>"
1104 : "<define name='anything'>"
1105 : "<zeroOrMore>"
1106 : "<choice>"
1107 : "<attribute><anyName/></attribute>"
1108 : "<text/>"
1109 : "<element>"
1110 : "<anyName/>"
1111 : "<ref name='anything'/>"
1112 : "</element>"
1113 : "</choice>"
1114 : "</zeroOrMore>"
1115 11 : "</define>";
1116 :
1117 22 : std::map<InterfaceId, std::vector<std::string> > interfaceComponentTypes;
1118 :
1119 22 : std::vector<std::string> componentTypes;
1120 :
1121 628 : for (std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.begin(); it != m_ComponentTypesById.end(); ++it)
1122 : {
1123 617 : schema +=
1124 1234 : "<define name='component." + it->second.name + "'>"
1125 1851 : "<element name='" + it->second.name + "'>"
1126 1851 : "<interleave>" + it->second.schema + "</interleave>"
1127 : "</element>"
1128 : "</define>";
1129 :
1130 617 : interfaceComponentTypes[it->second.iid].push_back(it->second.name);
1131 617 : componentTypes.push_back(it->second.name);
1132 : }
1133 :
1134 : // Declare the implementation of each interface, for documentation
1135 551 : for (std::map<std::string, InterfaceId>::const_iterator it = m_InterfaceIdsByName.begin(); it != m_InterfaceIdsByName.end(); ++it)
1136 : {
1137 540 : schema += "<define name='interface." + it->first + "'><choice>";
1138 540 : std::vector<std::string>& cts = interfaceComponentTypes[it->second];
1139 1157 : for (size_t i = 0; i < cts.size(); ++i)
1140 617 : schema += "<ref name='component." + cts[i] + "'/>";
1141 540 : schema += "</choice></define>";
1142 : }
1143 :
1144 : // List all the component types, in alphabetical order (to match the reordering performed by CParamNode).
1145 : // (We do it this way, rather than <interleave>ing all the interface definitions (which would additionally perform
1146 : // a check that we don't use multiple component types of the same interface in one file), because libxml2 gives
1147 : // useless error messages in the latter case; this way lets it report the real error.)
1148 11 : std::sort(componentTypes.begin(), componentTypes.end());
1149 11 : schema +=
1150 : "<start>"
1151 : "<element>"
1152 : "<anyName/>"
1153 : "<optional><attribute name='parent'/></optional>";
1154 628 : for (std::vector<std::string>::const_iterator it = componentTypes.begin(); it != componentTypes.end(); ++it)
1155 617 : schema += "<optional><ref name='component." + *it + "'/></optional>";
1156 11 : schema +=
1157 : "</element>"
1158 : "</start>";
1159 :
1160 11 : schema += "</grammar>";
1161 :
1162 22 : return schema;
1163 3 : }
|