Index: binaries/data/mods/public/simulation/components/Guard.js =================================================================== --- binaries/data/mods/public/simulation/components/Guard.js +++ binaries/data/mods/public/simulation/components/Guard.js @@ -56,8 +56,8 @@ Guard.prototype.OnAttacked = function(msg) { - for (let ent of this.entities) - Engine.PostMessage(ent, MT_GuardedAttacked, { "guarded": this.entity, "data": msg }); + if (this.entities.length) + Engine.DistributeMessage(this.entities, MT_GuardedAttacked, { "guarded": this.entity, "data": msg }); }; /** Index: source/simulation2/Simulation2.h =================================================================== --- source/simulation2/Simulation2.h +++ source/simulation2/Simulation2.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -202,6 +202,7 @@ IComponent* QueryInterface(entity_id_t ent, int iid) const; void PostMessage(entity_id_t ent, const CMessage& msg) const; + void DistributeMessage(const std::vector& entities, const CMessage& msg) const; void BroadcastMessage(const CMessage& msg) const; using InterfaceList = Index: source/simulation2/Simulation2.cpp =================================================================== --- source/simulation2/Simulation2.cpp +++ source/simulation2/Simulation2.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -710,6 +710,11 @@ m->m_ComponentManager.PostMessage(ent, msg); } +void CSimulation2::DistributeMessage(const std::vector& entities, const CMessage& msg) const +{ + m->m_ComponentManager.DistributeMessage(entities, msg); +} + void CSimulation2::BroadcastMessage(const CMessage& msg) const { m->m_ComponentManager.BroadcastMessage(msg); Index: source/simulation2/docs/SimulationDocs.h =================================================================== --- source/simulation2/docs/SimulationDocs.h +++ source/simulation2/docs/SimulationDocs.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -565,22 +565,26 @@ For one-to-many communication, you can send indirect messages to components. -From C++, use CComponentManager::PostMessage to send a message to a specific entity, and +From C++, use CComponentManager::PostMessage to send a message to a specific entity, +CComponentManager::DistributeMessage to send to an array of entities and CComponentManager::BroadcastMessage to send to all entities. (In all cases, messages will only be received by components that subscribed to the corresponding message type). @code CMessageExample msg(10, 20); GetSimContext().GetComponentManager().PostMessage(ent, msg); +GetSimContext().GetComponentManager().DistributeMessage(entities, msg); GetSimContext().GetComponentManager().BroadcastMessage(msg); @endcode -From JS, use @ref CComponentManager::Script_PostMessage "Engine.PostMessage" and +From JS, use @ref CComponentManager::Script_PostMessage "Engine.PostMessage", +@ref CComponentManager::Script_DistributeMessage "Engine.DistributeMessage" and @ref CComponentManager::Script_BroadcastMessage "Engine.BroadcastMessage", using the @c MT_* constants to identify the message type: @code Engine.PostMessage(ent, MT_Example, { x: 10, y: 20 }); +Engine.PostMessage(entities, MT_Example, { x: 10, y: 20 }); Engine.BroadcastMessage(MT_Example, { x: 10, y: 20 }); @endcode Index: source/simulation2/system/ComponentManager.h =================================================================== --- source/simulation2/system/ComponentManager.h +++ source/simulation2/system/ComponentManager.h @@ -244,6 +244,13 @@ void PostMessage(entity_id_t ent, const CMessage& msg); /** + * Send a message, targeted at the particular entities. The message will be received by any + * components of those entities which subscribed to the message type, and by any other components + * that subscribed globally to the message type. + */ + void DistributeMessage(const std::vector& entities, const CMessage& msg); + + /** * Send a message, not targeted at any particular entity. The message will be received by any * components that subscribed (either globally or not) to the message type. */ @@ -285,6 +292,7 @@ static std::vector Script_GetEntitiesWithInterface(ScriptInterface::CxPrivate* pCxPrivate, int iid); static std::vector Script_GetComponentsWithInterface(ScriptInterface::CxPrivate* pCxPrivate, int iid); static void Script_PostMessage(ScriptInterface::CxPrivate* pCxPrivate, int ent, int mtid, JS::HandleValue data); + static void Script_DistributeMessage(ScriptInterface::CxPrivate* pCxPrivate, const std::vector& entities, int mtid, JS::HandleValue data); static void Script_BroadcastMessage(ScriptInterface::CxPrivate* pCxPrivate, int mtid, JS::HandleValue data); static int Script_AddEntity(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName); static int Script_AddLocalEntity(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName); @@ -292,6 +300,7 @@ static void Script_FlushDestroyedEntities(ScriptInterface::CxPrivate* pCxPrivate); CMessage* ConstructMessage(int mtid, JS::HandleValue data); + void SendMessageTo(entity_id_t ent, const CMessage& msg); void SendGlobalMessage(entity_id_t ent, const CMessage& msg); void FlattenDynamicSubscriptions(); Index: source/simulation2/system/ComponentManager.cpp =================================================================== --- source/simulation2/system/ComponentManager.cpp +++ source/simulation2/system/ComponentManager.cpp @@ -78,6 +78,7 @@ m_ScriptInterface.RegisterFunction, int, CComponentManager::Script_GetEntitiesWithInterface> ("GetEntitiesWithInterface"); m_ScriptInterface.RegisterFunction, int, CComponentManager::Script_GetComponentsWithInterface> ("GetComponentsWithInterface"); m_ScriptInterface.RegisterFunction ("PostMessage"); + m_ScriptInterface.RegisterFunction, int, JS::HandleValue, CComponentManager::Script_DistributeMessage> ("DistributeMessage"); m_ScriptInterface.RegisterFunction ("BroadcastMessage"); m_ScriptInterface.RegisterFunction ("AddEntity"); m_ScriptInterface.RegisterFunction ("AddLocalEntity"); @@ -437,6 +438,22 @@ delete msg; } +void CComponentManager::Script_DistributeMessage(ScriptInterface::CxPrivate* pCxPrivate, const std::vector& entities, int mtid, JS::HandleValue data) +{ + if (entities.empty()) + return; + + CComponentManager* componentManager = static_cast (pCxPrivate->pCBData); + + CMessage* msg = componentManager->ConstructMessage(mtid, data); + if (!msg) + return; // error + + componentManager->DistributeMessage(entities, *msg); + + delete msg; +} + void CComponentManager::Script_BroadcastMessage(ScriptInterface::CxPrivate* pCxPrivate, int mtid, JS::HandleValue data) { CComponentManager* componentManager = static_cast (pCxPrivate->pCBData); @@ -1002,7 +1019,39 @@ void CComponentManager::PostMessage(entity_id_t ent, const CMessage& msg) { - PROFILE2_IFSPIKE("Post Message", 0.0005); + SendMessageTo(ent, msg); + SendGlobalMessage(ent, msg); +} + +/** + * Sends a local message to each of the specified entities and one global message. + * Since global messages "to" local entities are handled differently than normal, + * we check if there is at least one non-local entity to send the global message to. + */ +void CComponentManager::DistributeMessage(const std::vector& entities, const CMessage& msg) +{ + if (entities.empty()) + return; + + bool globalMessageSent = false; + for (entity_id_t ent : entities) + { + SendMessageTo(ent, msg); + if (!globalMessageSent && !ENTITY_IS_LOCAL(ent)) + { + SendGlobalMessage(ent, msg); + globalMessageSent = true; + } + } + + // All entities were local, just pick the first one. + if (!globalMessageSent) + SendGlobalMessage(entities.front(), msg); +} + +void CComponentManager::SendMessageTo(entity_id_t ent, const CMessage& msg) +{ + PROFILE2_IFSPIKE("Send Message To", 0.0005); PROFILE2_ATTR("%s", msg.GetScriptHandlerName()); // Send the message to components of ent, that subscribed locally to this message std::map >::const_iterator it; @@ -1023,8 +1072,6 @@ eit->second->HandleMessage(msg, false); } } - - SendGlobalMessage(ent, msg); } void CComponentManager::BroadcastMessage(const CMessage& msg)