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