1 /* Copyright (C) 2017 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #ifndef INCLUDED_COMPONENTMANAGER 19 #define INCLUDED_COMPONENTMANAGER 20 21 #include "Entity.h" 22 #include "Components.h" 23 #include "scriptinterface/ScriptInterface.h" 24 #include "scriptinterface/ScriptVal.h" 25 #include "simulation2/helpers/Player.h" 26 #include "ps/Filesystem.h" 27 28 #include <boost/random/linear_congruential.hpp> 29 #include <boost/unordered_map.hpp> 30 31 #include <map> 32 #include <unordered_map> 33 34 class IComponent; 35 class CParamNode; 36 class CMessage; 37 class CSimContext; 38 class CDynamicSubscription; 39 40 class CComponentManager 41 { 42 NONCOPYABLE(CComponentManager); 43 public: 44 // We can't use EInterfaceId/etc directly, since scripts dynamically generate new IDs 45 // and casting arbitrary ints to enums is undefined behaviour, so use 'int' typedefs 46 typedef int InterfaceId; 47 typedef int ComponentTypeId; 48 typedef int MessageTypeId; 49 50 private: 51 // Component allocation types 52 typedef IComponent* (*AllocFunc)(const ScriptInterface& scriptInterface, JS::HandleValue ctor); 53 typedef void (*DeallocFunc)(IComponent*); 54 55 // ComponentTypes come in three types: 56 // Native: normal C++ component 57 // ScriptWrapper: C++ component that wraps a JS component implementation 58 // Script: a ScriptWrapper linked to a specific JS component implementation 59 enum EComponentTypeType 60 { 61 CT_Native, 62 CT_ScriptWrapper, 63 CT_Script 64 }; 65 66 // Representation of a component type, to be used when instantiating components 67 struct ComponentType 68 { 69 EComponentTypeType type; 70 InterfaceId iid; 71 AllocFunc alloc; 72 DeallocFunc dealloc; 73 std::string name; 74 std::string schema; // RelaxNG fragment 75 DefPersistentRooted<JS::Value> ctor; // only valid if type == CT_Script 76 77 // TODO: Constructor, move assignment operator and move constructor only have to be 78 // explicitly defined for Visual Studio. VS2013 is still behind on C++11 support 79 // What's missing is what they call "Rvalue references v3.0", see 80 // https://msdn.microsoft.com/en-us/library/hh567368.aspx#rvref ComponentTypeComponentType81 ComponentType() {} ComponentTypeComponentType82 ComponentType (EComponentTypeType type, InterfaceId iid, AllocFunc alloc, 83 DeallocFunc dealloc, std::string name, std::string schema, DefPersistentRooted<JS::Value> ctor) : 84 type(type), 85 iid(iid), 86 alloc(alloc), 87 dealloc(dealloc), 88 name(name), 89 schema(schema), 90 ctor(std::move(ctor)) 91 { 92 } 93 94 ComponentType& operator= (ComponentType&& other) 95 { 96 type = std::move(other.type); 97 iid = std::move(other.iid); 98 alloc = std::move(other.alloc); 99 dealloc = std::move(other.dealloc); 100 name = std::move(other.name); 101 schema = std::move(other.schema); 102 ctor = std::move(other.ctor); 103 return *this; 104 } 105 ComponentTypeComponentType106 ComponentType(ComponentType&& other) 107 { 108 type = std::move(other.type); 109 iid = std::move(other.iid); 110 alloc = std::move(other.alloc); 111 dealloc = std::move(other.dealloc); 112 name = std::move(other.name); 113 schema = std::move(other.schema); 114 ctor = std::move(other.ctor); 115 } 116 }; 117 118 public: 119 CComponentManager(CSimContext&, shared_ptr<ScriptRuntime> rt, bool skipScriptFunctions = false); 120 ~CComponentManager(); 121 122 void LoadComponentTypes(); 123 124 /** 125 * Load a script and execute it in a new function scope. 126 * @param filename VFS path to load 127 * @param hotload set to true if this script has been loaded before, and redefinitions of 128 * existing components should not be considered errors 129 */ 130 bool LoadScript(const VfsPath& filename, bool hotload = false); 131 132 void RegisterMessageType(MessageTypeId mtid, const char* name); 133 134 void RegisterComponentType(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char*, const std::string& schema); 135 void RegisterComponentTypeScriptWrapper(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char*, const std::string& schema); 136 137 void MarkScriptedComponentForSystemEntity(CComponentManager::ComponentTypeId cid); 138 139 /** 140 * Subscribe the current component type to the given message type. 141 * Each component's HandleMessage will be called on any BroadcastMessage of this message type, 142 * or on any PostMessage of this type targeted at the component's entity. 143 * Must only be called by a component type's ClassInit. 144 */ 145 void SubscribeToMessageType(MessageTypeId mtid); 146 147 /** 148 * Subscribe the current component type to all messages of the given message type. 149 * Each component's HandleMessage will be called on any BroadcastMessage or PostMessage of this message type, 150 * regardless of the entity. 151 * Must only be called by a component type's ClassInit. 152 */ 153 void SubscribeGloballyToMessageType(MessageTypeId mtid); 154 155 /** 156 * Subscribe the given component instance to all messages of the given message type. 157 * The component's HandleMessage will be called on any BroadcastMessage or PostMessage of 158 * this message type, regardless of the entity. 159 * 160 * This can be called at any time (including inside the HandleMessage callback for this message type). 161 * 162 * The component type must not have statically subscribed to this message type in its ClassInit. 163 * 164 * The subscription status is not saved or network-synchronised. Components must remember to 165 * resubscribe in their Deserialize methods if they still want the message. 166 * 167 * This is primarily intended for Interpolate and RenderSubmit messages, to avoid the cost of 168 * sending the message to components that do not currently need to do any rendering. 169 */ 170 void DynamicSubscriptionNonsync(MessageTypeId mtid, IComponent* component, bool enabled); 171 172 /** 173 * @param cname Requested component type name (not including any "CID" or "CCmp" prefix) 174 * @return The component type id, or CID__Invalid if not found 175 */ 176 ComponentTypeId LookupCID(const std::string& cname) const; 177 178 /** 179 * @return The name of the given component type, or "" if not found 180 */ 181 std::string LookupComponentTypeName(ComponentTypeId cid) const; 182 183 /** 184 * Set up an empty SYSTEM_ENTITY. Must be called after ResetState() and before GetSystemEntity(). 185 */ 186 void InitSystemEntity(); 187 188 /** 189 * Returns a CEntityHandle with id SYSTEM_ENTITY. 190 */ GetSystemEntity()191 CEntityHandle GetSystemEntity() { ASSERT(m_SystemEntity.GetId() == SYSTEM_ENTITY); return m_SystemEntity; } 192 193 /** 194 * Returns a CEntityHandle with id @p ent. 195 * If @p allowCreate is true and there is no existing CEntityHandle, a new handle will be allocated. 196 */ 197 CEntityHandle LookupEntityHandle(entity_id_t ent, bool allowCreate = false); 198 199 /** 200 * Returns a new entity ID that has never been used before. 201 * This affects the simulation state so it must only be called in network-synchronised ways. 202 */ 203 entity_id_t AllocateNewEntity(); 204 205 /** 206 * Returns a new local entity ID that has never been used before. 207 * This entity will not be synchronised over the network, stored in saved games, etc. 208 */ 209 entity_id_t AllocateNewLocalEntity(); 210 211 /** 212 * Returns a new entity ID that has never been used before. 213 * If possible, returns preferredId, and ensures this ID won't be allocated again. 214 * This affects the simulation state so it must only be called in network-synchronised ways. 215 */ 216 entity_id_t AllocateNewEntity(entity_id_t preferredId); 217 218 /** 219 * Constructs a component of type 'cid', initialised with data 'paramNode', 220 * and attaches it to entity 'ent'. 221 * 222 * @return true on success; false on failure, and logs an error message 223 */ 224 bool AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode& paramNode); 225 226 /** 227 * Add all system components to the system entity (skip the scripted components or the AI components on demand) 228 */ 229 void AddSystemComponents(bool skipScriptedComponents, bool skipAI); 230 231 /** 232 * Adds an externally-created component, so that it is returned by QueryInterface 233 * but does not get destroyed and does not receive messages from the component manager. 234 * (This is intended for unit tests that need to add mock objects the tested components 235 * expect to exist.) 236 */ 237 void AddMockComponent(CEntityHandle ent, InterfaceId iid, IComponent& component); 238 239 /** 240 * Allocates a component object of type 'cid', and attaches it to entity 'ent'. 241 * (The component's Init is not called here - either Init or Deserialize must be called 242 * before using the returned object.) 243 */ 244 IComponent* ConstructComponent(CEntityHandle ent, ComponentTypeId cid); 245 246 /** 247 * Constructs an entity based on the given template, and adds it the world with 248 * entity ID @p ent. There should not be any existing components with that entity ID. 249 * @return ent, or INVALID_ENTITY on error 250 */ 251 entity_id_t AddEntity(const std::wstring& templateName, entity_id_t ent); 252 253 /** 254 * Destroys all the components belonging to the specified entity when FlushDestroyedComponents is called. 255 * Has no effect if the entity does not exist, or has already been added to the destruction queue. 256 */ 257 void DestroyComponentsSoon(entity_id_t ent); 258 259 /** 260 * Does the actual destruction of components from DestroyComponentsSoon. 261 * This must not be called if the component manager is on the call stack (since it 262 * will break internal iterators). 263 */ 264 void FlushDestroyedComponents(); 265 266 IComponent* QueryInterface(entity_id_t ent, InterfaceId iid) const; 267 268 typedef std::pair<entity_id_t, IComponent*> InterfacePair; 269 typedef std::vector<InterfacePair> InterfaceList; 270 typedef boost::unordered_map<entity_id_t, IComponent*> InterfaceListUnordered; 271 272 InterfaceList GetEntitiesWithInterface(InterfaceId iid) const; 273 const InterfaceListUnordered& GetEntitiesWithInterfaceUnordered(InterfaceId iid) const; 274 275 /** 276 * Send a message, targeted at a particular entity. The message will be received by any 277 * components of that entity which subscribed to the message type, and by any other components 278 * that subscribed globally to the message type. 279 */ 280 void PostMessage(entity_id_t ent, const CMessage& msg); 281 282 /** 283 * Send a message, not targeted at any particular entity. The message will be received by any 284 * components that subscribed (either globally or not) to the message type. 285 */ 286 void BroadcastMessage(const CMessage& msg); 287 288 /** 289 * Resets the dynamic simulation state (deletes all entities, resets entity ID counters; 290 * doesn't unload/reload component scripts). 291 */ 292 void ResetState(); 293 294 /** 295 * Initializes the random number generator with a seed determined by the host. 296 */ 297 void SetRNGSeed(u32 seed); 298 299 // Various state serialization functions: 300 bool ComputeStateHash(std::string& outHash, bool quick) const; 301 bool DumpDebugState(std::ostream& stream, bool includeDebugInfo) const; 302 // FlushDestroyedComponents must be called before SerializeState (since the destruction queue 303 // won't get serialized) 304 bool SerializeState(std::ostream& stream) const; 305 bool DeserializeState(std::istream& stream); 306 307 std::string GenerateSchema() const; 308 GetScriptInterface()309 ScriptInterface& GetScriptInterface() { return m_ScriptInterface; } 310 311 private: 312 // Implementations of functions exposed to scripts 313 static void Script_RegisterComponentType_Common(ScriptInterface::CxPrivate* pCxPrivate, int iid, const std::string& cname, JS::HandleValue ctor, bool reRegister, bool systemComponent); 314 static void Script_RegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, const std::string& cname, JS::HandleValue ctor); 315 static void Script_RegisterSystemComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, const std::string& cname, JS::HandleValue ctor); 316 static void Script_ReRegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, const std::string& cname, JS::HandleValue ctor); 317 static void Script_RegisterInterface(ScriptInterface::CxPrivate* pCxPrivate, const std::string& name); 318 static void Script_RegisterMessageType(ScriptInterface::CxPrivate* pCxPrivate, const std::string& name); 319 static void Script_RegisterGlobal(ScriptInterface::CxPrivate* pCxPrivate, const std::string& name, JS::HandleValue value); 320 static IComponent* Script_QueryInterface(ScriptInterface::CxPrivate* pCxPrivate, int ent, int iid); 321 static std::vector<int> Script_GetEntitiesWithInterface(ScriptInterface::CxPrivate* pCxPrivate, int iid); 322 static std::vector<IComponent*> Script_GetComponentsWithInterface(ScriptInterface::CxPrivate* pCxPrivate, int iid); 323 static void Script_PostMessage(ScriptInterface::CxPrivate* pCxPrivate, int ent, int mtid, JS::HandleValue data); 324 static void Script_BroadcastMessage(ScriptInterface::CxPrivate* pCxPrivate, int mtid, JS::HandleValue data); 325 static int Script_AddEntity(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName); 326 static int Script_AddLocalEntity(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName); 327 static void Script_DestroyEntity(ScriptInterface::CxPrivate* pCxPrivate, int ent); 328 static void Script_FlushDestroyedEntities(ScriptInterface::CxPrivate* pCxPrivate); 329 330 CMessage* ConstructMessage(int mtid, JS::HandleValue data); 331 void SendGlobalMessage(entity_id_t ent, const CMessage& msg); 332 333 void FlattenDynamicSubscriptions(); 334 void RemoveComponentDynamicSubscriptions(IComponent* component); 335 336 ComponentTypeId GetScriptWrapper(InterfaceId iid); 337 338 CEntityHandle AllocateEntityHandle(entity_id_t ent); 339 340 ScriptInterface m_ScriptInterface; 341 CSimContext& m_SimContext; 342 343 CEntityHandle m_SystemEntity; 344 345 ComponentTypeId m_CurrentComponent; // used when loading component types 346 bool m_CurrentlyHotloading; 347 348 // TODO: some of these should be vectors 349 std::map<ComponentTypeId, ComponentType> m_ComponentTypesById; 350 std::vector<CComponentManager::ComponentTypeId> m_ScriptedSystemComponents; 351 std::vector<boost::unordered_map<entity_id_t, IComponent*> > m_ComponentsByInterface; // indexed by InterfaceId 352 std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> > m_ComponentsByTypeId; 353 std::map<MessageTypeId, std::vector<ComponentTypeId> > m_LocalMessageSubscriptions; 354 std::map<MessageTypeId, std::vector<ComponentTypeId> > m_GlobalMessageSubscriptions; 355 std::map<std::string, ComponentTypeId> m_ComponentTypeIdsByName; 356 std::map<std::string, MessageTypeId> m_MessageTypeIdsByName; 357 std::map<MessageTypeId, std::string> m_MessageTypeNamesById; 358 std::map<std::string, InterfaceId> m_InterfaceIdsByName; 359 360 std::map<MessageTypeId, CDynamicSubscription> m_DynamicMessageSubscriptionsNonsync; 361 std::map<IComponent*, std::set<MessageTypeId> > m_DynamicMessageSubscriptionsNonsyncByComponent; 362 363 std::unordered_map<entity_id_t, SEntityComponentCache*> m_ComponentCaches; 364 365 // TODO: maintaining both ComponentsBy* is nasty; can we get rid of one, 366 // while keeping QueryInterface and PostMessage sufficiently efficient? 367 368 std::vector<entity_id_t> m_DestructionQueue; 369 370 ComponentTypeId m_NextScriptComponentTypeId; 371 entity_id_t m_NextEntityId; 372 entity_id_t m_NextLocalEntityId; 373 374 boost::rand48 m_RNG; 375 376 friend class TestComponentManager; 377 }; 378 379 #endif // INCLUDED_COMPONENTMANAGER 380