bool CComponentManager::ComputeStateHash(std::string& outHash, bool quick) { // Hash serialization: this includes the minimal data necessary to detect // differences in the state, and ignores things like counts and names // If 'quick' is set, this checks even fewer things, so that it will // be fast enough to run every turn but will typically detect any // out-of-syncs fairly soon CHashSerializer serializer(m_ScriptInterface); serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32); std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator cit = m_ComponentsByTypeId.begin(); for (; cit != m_ComponentsByTypeId.end(); ++cit) { // In quick mode, only check unit positions if (quick && !(cit->first == CID_Position)) continue; // Only emit component types if they have a component that will be serialized bool needsSerialization = false; for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit) { // Don't serialize local entities if (ENTITY_IS_LOCAL(eit->first)) continue; needsSerialization = true; break; } if (!needsSerialization) continue; serializer.NumberI32_Unbounded("component type id", cit->first); for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit) { // Don't serialize local entities if (ENTITY_IS_LOCAL(eit->first)) continue; serializer.NumberU32_Unbounded("entity id", eit->first); eit->second->Serialize(serializer); } } outHash = std::string((const char*)serializer.ComputeHash(), serializer.GetHashLength()); // TODO: catch exceptions return true; }
void CComponentManager::SendGlobalMessage(entity_id_t ent, const CMessage& msg) const { // (Common functionality for PostMessage and BroadcastMessage) // Send the message to components of all entities that subscribed globally to this message std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it; it = m_GlobalMessageSubscriptions.find(msg.GetType()); if (it != m_GlobalMessageSubscriptions.end()) { std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin(); for (; ctit != it->second.end(); ++ctit) { // Special case: Messages for non-local entities shouldn't be sent to script // components that subscribed globally, so that we don't have to worry about // them accidentally picking up non-network-synchronised data. if (ENTITY_IS_LOCAL(ent)) { std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.find(*ctit); if (it != m_ComponentTypesById.end() && it->second.type == CT_Script) continue; } // Find the component instances of this type (if any) std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit); if (emap == m_ComponentsByTypeId.end()) continue; // Send the message to all of them std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin(); for (; eit != emap->second.end(); ++eit) eit->second->HandleMessage(msg, true); } } }
std::vector<int> CComponentManager::Script_GetEntitiesWithInterface(ScriptInterface::CxPrivate* pCxPrivate, int iid) { CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData); std::vector<int> ret; const InterfaceListUnordered& ents = componentManager->GetEntitiesWithInterfaceUnordered(iid); for (InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) if (!ENTITY_IS_LOCAL(it->first)) ret.push_back(it->first); std::sort(ret.begin(), ret.end()); return ret; }
virtual void Serialize(ISerializer& serialize) { size_t count = 0; for (std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it) { if (ENTITY_IS_LOCAL(it->first)) continue; ++count; } serialize.NumberU32_Unbounded("num entities", (u32)count); for (std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it) { if (ENTITY_IS_LOCAL(it->first)) continue; serialize.NumberU32_Unbounded("id", it->first); serialize.StringASCII("template", it->second, 0, 256); } // TODO: maybe we should do some kind of interning thing instead of printing so many strings? // TODO: will need to serialize techs too, because we need to be giving out // template data before other components (like the tech components) have been deserialized }
bool CComponentManager::DumpDebugState(std::ostream& stream) { CDebugSerializer serializer(m_ScriptInterface, stream); serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32); serializer.TextLine("entities:"); // We want the output to be grouped by entity ID, so invert the CComponentManager data structures std::map<entity_id_t, std::map<ComponentTypeId, IComponent*> > components; std::map<ComponentTypeId, std::string> names; std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator ctit = m_ComponentsByTypeId.begin(); for (; ctit != m_ComponentsByTypeId.end(); ++ctit) { std::map<entity_id_t, IComponent*>::const_iterator eit = ctit->second.begin(); for (; eit != ctit->second.end(); ++eit) { components[eit->first][ctit->first] = eit->second; } } std::map<entity_id_t, std::map<ComponentTypeId, IComponent*> >::const_iterator cit = components.begin(); for (; cit != components.end(); ++cit) { std::stringstream n; n << "- id: " << cit->first; serializer.TextLine(n.str()); if (ENTITY_IS_LOCAL(cit->first)) serializer.TextLine(" type: local"); std::map<ComponentTypeId, IComponent*>::const_iterator ctit = cit->second.begin(); for (; ctit != cit->second.end(); ++ctit) { std::stringstream n; n << " " << LookupComponentTypeName(ctit->first) << ":"; serializer.TextLine(n.str()); serializer.Indent(4); ctit->second->Serialize(serializer); serializer.Dedent(4); } serializer.TextLine(""); } // TODO: catch exceptions return true; }
virtual void Init(const CParamNode& paramNode) { m_Active = !paramNode.GetChild("Inactive").IsOk(); m_DelayTime = paramNode.GetChild("DelayTime").ToFixed().ToFloat(); m_SinkRate = paramNode.GetChild("SinkRate").ToFixed().ToFloat(); m_SinkAccel = paramNode.GetChild("SinkAccel").ToFixed().ToFloat(); m_CurrentTime = 0.f; m_TotalSinkDepth = -1.f; // Detect unsafe misconfiguration if (m_Active && !ENTITY_IS_LOCAL(GetEntityId())) { debug_warn(L"CCmpDecay must not be used on non-local (network-synchronised) entities"); m_Active = false; } }
void CComponentManager::SendGlobalMessage(entity_id_t ent, const CMessage& msg) { PROFILE2_IFSPIKE("SendGlobalMessage", 0.001); PROFILE2_ATTR("%s", msg.GetScriptHandlerName()); // (Common functionality for PostMessage and BroadcastMessage) // Send the message to components of all entities that subscribed globally to this message std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it; it = m_GlobalMessageSubscriptions.find(msg.GetType()); if (it != m_GlobalMessageSubscriptions.end()) { std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin(); for (; ctit != it->second.end(); ++ctit) { // Special case: Messages for local entities shouldn't be sent to script // components that subscribed globally, so that we don't have to worry about // them accidentally picking up non-network-synchronised data. if (ENTITY_IS_LOCAL(ent)) { std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.find(*ctit); if (it != m_ComponentTypesById.end() && it->second.type == CT_Script) continue; } // Find the component instances of this type (if any) std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit); if (emap == m_ComponentsByTypeId.end()) continue; // Send the message to all of them std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin(); for (; eit != emap->second.end(); ++eit) eit->second->HandleMessage(msg, true); } } // Send the message to component instances that dynamically subscribed to this message std::map<MessageTypeId, CDynamicSubscription>::iterator dit = m_DynamicMessageSubscriptionsNonsync.find(msg.GetType()); if (dit != m_DynamicMessageSubscriptionsNonsync.end()) { dit->second.Flatten(); const std::vector<IComponent*>& dynamic = dit->second.GetComponents(); for (size_t i = 0; i < dynamic.size(); i++) dynamic[i]->HandleMessage(msg, false); } }
bool CComponentManager::SerializeState(std::ostream& stream) { CStdSerializer serializer(m_ScriptInterface, stream); // We don't serialize the destruction queue, since we'd have to be careful to skip local entities etc // and it's (hopefully) easier to just expect callers to flush the queue before serializing ENSURE(m_DestructionQueue.empty()); serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32); serializer.NumberU32_Unbounded("next entity id", m_NextEntityId); uint32_t numComponentTypes = 0; std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator cit; for (cit = m_ComponentsByTypeId.begin(); cit != m_ComponentsByTypeId.end(); ++cit) { if (cit->second.empty()) continue; numComponentTypes++; } serializer.NumberU32_Unbounded("num component types", numComponentTypes); for (cit = m_ComponentsByTypeId.begin(); cit != m_ComponentsByTypeId.end(); ++cit) { if (cit->second.empty()) continue; std::map<ComponentTypeId, ComponentType>::const_iterator ctit = m_ComponentTypesById.find(cit->first); if (ctit == m_ComponentTypesById.end()) { debug_warn(L"Invalid ctit"); // this should never happen return false; } serializer.StringASCII("name", ctit->second.name, 0, 255); std::map<entity_id_t, IComponent*>::const_iterator eit; // Count the components before serializing any of them uint32_t numComponents = 0; for (eit = cit->second.begin(); eit != cit->second.end(); ++eit) { // Don't serialize local entities if (ENTITY_IS_LOCAL(eit->first)) continue; numComponents++; } // Emit the count serializer.NumberU32_Unbounded("num components", numComponents); // Serialize the components now for (eit = cit->second.begin(); eit != cit->second.end(); ++eit) { // Don't serialize local entities if (ENTITY_IS_LOCAL(eit->first)) continue; serializer.NumberU32_Unbounded("entity id", eit->first); eit->second->Serialize(serializer); } } // TODO: catch exceptions return true; }
void CMapWriter::WriteXML(const VfsPath& filename, WaterManager* pWaterMan, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema, CPostprocManager* pPostproc, CSimulation2* pSimulation2) { XML_Start(); { XML_Element("Scenario"); XML_Attribute("version", (int)FILE_VERSION); ENSURE(pSimulation2); CSimulation2& sim = *pSimulation2; if (!sim.GetStartupScript().empty()) { XML_Element("Script"); XML_CDATA(sim.GetStartupScript().c_str()); } { XML_Element("Environment"); XML_Setting("SkySet", pSkyMan->GetSkySet()); { XML_Element("SunColor"); XML_Attribute("r", pLightEnv->m_SunColor.X); // yes, it's X/Y/Z... XML_Attribute("g", pLightEnv->m_SunColor.Y); XML_Attribute("b", pLightEnv->m_SunColor.Z); } { XML_Element("SunElevation"); XML_Attribute("angle", pLightEnv->m_Elevation); } { XML_Element("SunRotation"); XML_Attribute("angle", pLightEnv->m_Rotation); } { XML_Element("TerrainAmbientColor"); XML_Attribute("r", pLightEnv->m_TerrainAmbientColor.X); XML_Attribute("g", pLightEnv->m_TerrainAmbientColor.Y); XML_Attribute("b", pLightEnv->m_TerrainAmbientColor.Z); } { XML_Element("UnitsAmbientColor"); XML_Attribute("r", pLightEnv->m_UnitsAmbientColor.X); XML_Attribute("g", pLightEnv->m_UnitsAmbientColor.Y); XML_Attribute("b", pLightEnv->m_UnitsAmbientColor.Z); } { XML_Element("Fog"); XML_Setting("FogFactor", pLightEnv->m_FogFactor); XML_Setting("FogThickness", pLightEnv->m_FogMax); { XML_Element("FogColor"); XML_Attribute("r", pLightEnv->m_FogColor.X); XML_Attribute("g", pLightEnv->m_FogColor.Y); XML_Attribute("b", pLightEnv->m_FogColor.Z); } } { XML_Element("Water"); { XML_Element("WaterBody"); CmpPtr<ICmpWaterManager> cmpWaterManager(sim, SYSTEM_ENTITY); ENSURE(cmpWaterManager); XML_Setting("Type", pWaterMan->m_WaterType); { XML_Element("Color"); XML_Attribute("r", pWaterMan->m_WaterColor.r); XML_Attribute("g", pWaterMan->m_WaterColor.g); XML_Attribute("b", pWaterMan->m_WaterColor.b); } { XML_Element("Tint"); XML_Attribute("r", pWaterMan->m_WaterTint.r); XML_Attribute("g", pWaterMan->m_WaterTint.g); XML_Attribute("b", pWaterMan->m_WaterTint.b); } XML_Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0)); XML_Setting("Waviness", pWaterMan->m_Waviness); XML_Setting("Murkiness", pWaterMan->m_Murkiness); XML_Setting("WindAngle", pWaterMan->m_WindAngle); } } { XML_Element("Postproc"); { XML_Setting("Brightness", pLightEnv->m_Brightness); XML_Setting("Contrast", pLightEnv->m_Contrast); XML_Setting("Saturation", pLightEnv->m_Saturation); XML_Setting("Bloom", pLightEnv->m_Bloom); XML_Setting("PostEffect", pPostproc->GetPostEffect()); } } } { XML_Element("Camera"); { XML_Element("Position"); CVector3D pos = pCamera->m_Orientation.GetTranslation(); XML_Attribute("x", pos.X); XML_Attribute("y", pos.Y); XML_Attribute("z", pos.Z); } CVector3D in = pCamera->m_Orientation.GetIn(); // Convert to spherical coordinates float rotation = atan2(in.X, in.Z); float declination = atan2(sqrt(in.X*in.X + in.Z*in.Z), in.Y) - (float)M_PI/2; { XML_Element("Rotation"); XML_Attribute("angle", rotation); } { XML_Element("Declination"); XML_Attribute("angle", declination); } } { std::string settings = sim.GetMapSettingsString(); if (!settings.empty()) { XML_Element("ScriptSettings"); XML_CDATA(("\n" + settings + "\n").c_str()); } } { XML_Element("Entities"); CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY); ENSURE(cmpTemplateManager); // This will probably need to be changed in the future, but for now we'll // just save all entities that have a position CSimulation2::InterfaceList ents = sim.GetEntitiesWithInterface(IID_Position); for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it) { entity_id_t ent = it->first; // Don't save local entities (placement previews etc) if (ENTITY_IS_LOCAL(ent)) continue; XML_Element("Entity"); XML_Attribute("uid", ent); XML_Setting("Template", cmpTemplateManager->GetCurrentTemplateName(ent)); CmpPtr<ICmpOwnership> cmpOwnership(sim, ent); if (cmpOwnership) XML_Setting("Player", (int)cmpOwnership->GetOwner()); CmpPtr<ICmpPosition> cmpPosition(sim, ent); if (cmpPosition) { CFixedVector3D pos; if (cmpPosition->IsInWorld()) pos = cmpPosition->GetPosition(); CFixedVector3D rot = cmpPosition->GetRotation(); { XML_Element("Position"); XML_Attribute("x", pos.X); XML_Attribute("z", pos.Z); // TODO: height offset etc } { XML_Element("Orientation"); XML_Attribute("y", rot.Y); // TODO: X, Z maybe } } CmpPtr<ICmpObstruction> cmpObstruction(sim, ent); if (cmpObstruction) { // TODO: Currently only necessary because Atlas // does not set up control groups for its walls. cmpObstruction->ResolveFoundationCollisions(); entity_id_t group = cmpObstruction->GetControlGroup(); entity_id_t group2 = cmpObstruction->GetControlGroup2(); // Don't waste space writing the default control groups. if (group != ent || group2 != INVALID_ENTITY) { XML_Element("Obstruction"); if (group != ent) XML_Attribute("group", group); if (group2 != INVALID_ENTITY) XML_Attribute("group2", group2); } } CmpPtr<ICmpVisual> cmpVisual(sim, ent); if (cmpVisual) { u32 seed = cmpVisual->GetActorSeed(); if (seed != (u32)ent) { XML_Element("Actor"); XML_Attribute("seed", seed); } // TODO: variation/selection strings } } } const std::map<CStrW, CCinemaPath>& paths = pCinema->GetAllPaths(); std::map<CStrW, CCinemaPath>::const_iterator it = paths.begin(); { XML_Element("Paths"); for ( ; it != paths.end(); ++it ) { fixed timescale = it->second.GetTimescale(); const std::vector<SplineData>& nodes = it->second.GetAllNodes(); const std::vector<SplineData>& target_nodes = it->second.GetTargetSpline().GetAllNodes(); const CCinemaData* data = it->second.GetData(); XML_Element("Path"); XML_Attribute("name", data->m_Name); XML_Attribute("timescale", timescale); XML_Attribute("orientation", data->m_Orientation); XML_Attribute("mode", data->m_Mode); XML_Attribute("style", data->m_Style); fixed last_target = fixed::Zero(); for (size_t i = 0, j = 0; i < nodes.size(); ++i) { XML_Element("Node"); fixed distance = i > 0 ? nodes[i - 1].Distance : fixed::Zero(); last_target += distance; XML_Attribute("deltatime", distance); { XML_Element("Position"); XML_Attribute("x", nodes[i].Position.X); XML_Attribute("y", nodes[i].Position.Y); XML_Attribute("z", nodes[i].Position.Z); } { XML_Element("Rotation"); XML_Attribute("x", nodes[i].Rotation.X); XML_Attribute("y", nodes[i].Rotation.Y); XML_Attribute("z", nodes[i].Rotation.Z); } if (j >= target_nodes.size()) continue; fixed target_distance = j > 0 ? target_nodes[j - 1].Distance : fixed::Zero(); if (target_distance > last_target) continue; { XML_Element("Target"); XML_Attribute("x", target_nodes[j].Position.X); XML_Attribute("y", target_nodes[j].Position.Y); XML_Attribute("z", target_nodes[j].Position.Z); } last_target = fixed::Zero(); ++j; } } } } if (!XML_StoreVFS(g_VFS, filename)) LOGERROR("Failed to write map '%s'", filename.string8()); }