diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp index 7a054e3aad..0bd08fc71a 100644 --- a/source/simulation2/Simulation2.cpp +++ b/source/simulation2/Simulation2.cpp @@ -64,20 +64,6 @@ public: UnregisterFileReloadFunc(ReloadChangedFileCB, this); } - CParamNode LoadXML(const std::wstring& name) - { - CParamNode ret; - - VfsPath path = VfsPath(L"simulation/templates/") / name; - CXeromyces xero; - PSRETURN ok = xero.Load(g_VFS, path); - if (ok != PSRETURN_OK) - return ret; // (Xeromyces already logged an error) - - CParamNode::LoadXML(ret, xero); - return ret; - } - void ResetState(bool skipScriptedComponents) { m_ComponentManager.ResetState(); @@ -94,7 +80,7 @@ public: m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_CommandQueue, noParam); m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_ObstructionManager, noParam); - m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_Pathfinder, LoadXML(L"special/pathfinder.xml").GetChild("Pathfinder")); + m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_Pathfinder, noParam); m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_ProjectileManager, noParam); m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_RangeManager, noParam); m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_SoundManager, noParam); diff --git a/source/simulation2/components/CCmpFootprint.cpp b/source/simulation2/components/CCmpFootprint.cpp index 3f2fc525e5..b30f4b80f1 100644 --- a/source/simulation2/components/CCmpFootprint.cpp +++ b/source/simulation2/components/CCmpFootprint.cpp @@ -102,6 +102,7 @@ public: virtual void Serialize(ISerializer& UNUSED(serialize)) { + // No dynamic state to serialize } virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) diff --git a/source/simulation2/components/CCmpMinimap.cpp b/source/simulation2/components/CCmpMinimap.cpp index 5ae4d08c4c..9fadf7228b 100644 --- a/source/simulation2/components/CCmpMinimap.cpp +++ b/source/simulation2/components/CCmpMinimap.cpp @@ -37,10 +37,16 @@ public: DEFAULT_COMPONENT_ALLOCATOR(Minimap) - bool m_Active; + // Template state: + bool m_UsePlayerColour; - u8 m_R, m_G, m_B; - entity_pos_t m_X, m_Z; // cache the latest position for more efficient rendering + + u8 m_R, m_G, m_B; // static template state if m_UsePlayerColour false; dynamic state if true + + // Dynamic state: + + bool m_Active; + entity_pos_t m_X, m_Z; // cache the latest position for more efficient rendering; only valid when m_Active true static std::string GetSchema() { @@ -99,16 +105,35 @@ public: { } + template + void SerializeCommon(S& serialize) + { + if (m_UsePlayerColour) + { + serialize.NumberU8_Unbounded("r", m_R); + serialize.NumberU8_Unbounded("g", m_G); + serialize.NumberU8_Unbounded("b", m_B); + } + + serialize.Bool("active", m_Active); + + if (m_Active) + { + serialize.NumberFixed_Unbounded("x", m_X); + serialize.NumberFixed_Unbounded("z", m_Z); + } + } + virtual void Serialize(ISerializer& serialize) { - // TODO + SerializeCommon(serialize); } virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize) { Init(context, paramNode); - // TODO + SerializeCommon(deserialize); } virtual void HandleMessage(const CSimContext& context, const CMessage& msg, bool UNUSED(global)) diff --git a/source/simulation2/components/CCmpObstruction.cpp b/source/simulation2/components/CCmpObstruction.cpp index eb793c92cc..b82755903a 100644 --- a/source/simulation2/components/CCmpObstruction.cpp +++ b/source/simulation2/components/CCmpObstruction.cpp @@ -111,16 +111,24 @@ public: { } - virtual void Serialize(ISerializer& UNUSED(serialize)) + template + void SerializeCommon(S& serialize) { - // TODO: Coordinate with CCmpObstructionManager serialisation + serialize.Bool("moving", m_Moving); + serialize.NumberU32_Unbounded("control group", m_ControlGroup); + serialize.NumberU32_Unbounded("tag", m_Tag.n); } - virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) + virtual void Serialize(ISerializer& serialize) + { + SerializeCommon(serialize); + } + + virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize) { Init(context, paramNode); - // TODO: Coordinate with CCmpObstructionManager serialisation + SerializeCommon(deserialize); } virtual void HandleMessage(const CSimContext& context, const CMessage& msg, bool UNUSED(global)) diff --git a/source/simulation2/components/CCmpObstructionManager.cpp b/source/simulation2/components/CCmpObstructionManager.cpp index ee244ca332..0419d1ccde 100644 --- a/source/simulation2/components/CCmpObstructionManager.cpp +++ b/source/simulation2/components/CCmpObstructionManager.cpp @@ -24,6 +24,7 @@ #include "simulation2/helpers/Geometry.h" #include "simulation2/helpers/Render.h" #include "simulation2/helpers/Spatial.h" +#include "simulation2/serialization/SerializeTemplates.h" #include "graphics/Overlay.h" #include "graphics/Terrain.h" @@ -63,6 +64,41 @@ struct StaticShape entity_pos_t hw, hh; // half width/height in local coordinate space }; +/** + * Serialization helper template for UnitShape + */ +struct SerializeUnitShape +{ + template + void operator()(S& serialize, const char* UNUSED(name), UnitShape& value) + { + serialize.NumberFixed_Unbounded("x", value.x); + serialize.NumberFixed_Unbounded("z", value.z); + serialize.NumberFixed_Unbounded("r", value.r); + serialize.Bool("moving", value.moving); + serialize.NumberU32_Unbounded("group", value.group); + } +}; + +/** + * Serialization helper template for StaticShape + */ +struct SerializeStaticShape +{ + template + void operator()(S& serialize, const char* UNUSED(name), StaticShape& value) + { + serialize.NumberFixed_Unbounded("x", value.x); + serialize.NumberFixed_Unbounded("z", value.z); + serialize.NumberFixed_Unbounded("u.x", value.u.X); + serialize.NumberFixed_Unbounded("u.y", value.u.Y); + serialize.NumberFixed_Unbounded("v.x", value.v.X); + serialize.NumberFixed_Unbounded("v.y", value.v.Y); + serialize.NumberFixed_Unbounded("hw", value.hw); + serialize.NumberFixed_Unbounded("hh", value.hh); + } +}; + class CCmpObstructionManager : public ICmpObstructionManager { public: @@ -96,7 +132,7 @@ public: return ""; } - virtual void Init(const CSimContext& context, const CParamNode& UNUSED(paramNode)) + virtual void Init(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode)) { m_DebugOverlayEnabled = false; m_DebugOverlayDirty = true; @@ -117,18 +153,36 @@ public: { } + template + void SerializeCommon(S& serialize) + { + SerializeSpatialSubdivision()(serialize, "unit subdiv", m_UnitSubdivision); + SerializeSpatialSubdivision()(serialize, "static subdiv", m_StaticSubdivision); + + SerializeMap()(serialize, "unit shapes", m_UnitShapes); + SerializeMap()(serialize, "static shapes", m_StaticShapes); + serialize.NumberU32_Unbounded("unit shape next", m_UnitShapeNext); + serialize.NumberU32_Unbounded("static shape next", m_StaticShapeNext); + + serialize.NumberFixed_Unbounded("world x0", m_WorldX0); + serialize.NumberFixed_Unbounded("world z0", m_WorldZ0); + serialize.NumberFixed_Unbounded("world x1", m_WorldX1); + serialize.NumberFixed_Unbounded("world z1", m_WorldZ1); + } + virtual void Serialize(ISerializer& serialize) { - // TODO: do something here - // (Do we need to serialise the obstruction state, or is it fine to regenerate it from - // the original entities after deserialisation?) + // TODO: this could perhaps be optimised by not storing all the obstructions, + // and instead regenerating them from the other entities on Deserialize + + SerializeCommon(serialize); } virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize) { Init(context, paramNode); - // TODO + SerializeCommon(deserialize); } virtual void HandleMessage(const CSimContext& context, const CMessage& msg, bool UNUSED(global)) diff --git a/source/simulation2/components/CCmpPathfinder.cpp b/source/simulation2/components/CCmpPathfinder.cpp index 5b7287323c..29befaec3d 100644 --- a/source/simulation2/components/CCmpPathfinder.cpp +++ b/source/simulation2/components/CCmpPathfinder.cpp @@ -40,7 +40,7 @@ const int DEFAULT_MOVE_COST = 256; REGISTER_COMPONENT_TYPE(Pathfinder) -void CCmpPathfinder::Init(const CSimContext& UNUSED(context), const CParamNode& paramNode) +void CCmpPathfinder::Init(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode)) { m_MapSize = 0; m_Grid = NULL; @@ -52,7 +52,14 @@ void CCmpPathfinder::Init(const CSimContext& UNUSED(context), const CParamNode& m_DebugGrid = NULL; m_DebugPath = NULL; - const CParamNode::ChildrenMap& passClasses = paramNode.GetChild("PassabilityClasses").GetChildren(); + // Since this is used as a system component (not loaded from an entity template), + // we can't use the real paramNode (it won't get handled properly when deserializing), + // so load the data from a special XML file. + CParamNode externalParamNode; + CParamNode::LoadXML(externalParamNode, L"simulation/templates/special/pathfinder.xml"); + + + const CParamNode::ChildrenMap& passClasses = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses").GetChildren(); for (CParamNode::ChildrenMap::const_iterator it = passClasses.begin(); it != passClasses.end(); ++it) { std::string name = it->first; @@ -63,7 +70,7 @@ void CCmpPathfinder::Init(const CSimContext& UNUSED(context), const CParamNode& } - const CParamNode::ChildrenMap& moveClasses = paramNode.GetChild("MovementClasses").GetChildren(); + const CParamNode::ChildrenMap& moveClasses = externalParamNode.GetChild("Pathfinder").GetChild("MovementClasses").GetChildren(); // First find the set of unit classes used by any terrain classes, // and assign unique tags to terrain classes diff --git a/source/simulation2/components/CCmpPathfinder_Common.h b/source/simulation2/components/CCmpPathfinder_Common.h index cc6f5a4ce9..33922ffd46 100644 --- a/source/simulation2/components/CCmpPathfinder_Common.h +++ b/source/simulation2/components/CCmpPathfinder_Common.h @@ -157,6 +157,8 @@ public: DEFAULT_COMPONENT_ALLOCATOR(Pathfinder) + // Template state: + std::map m_PassClassMasks; std::vector m_PassClasses; @@ -165,10 +167,14 @@ public: std::vector > m_MoveCosts; // costs[unitClass][terrainClass] std::vector > m_MoveSpeeds; // speeds[unitClass][terrainClass] + // Dynamic state: + std::vector m_AsyncLongPathRequests; std::vector m_AsyncShortPathRequests; u32 m_NextAsyncTicket; // unique IDs for asynchronous path requests + // Lazily-constructed dynamic state (not serialized): + u16 m_MapSize; // tiles per side Grid* m_Grid; // terrain/passability information Grid* m_ObstructionGrid; // cached obstruction information (TODO: we shouldn't bother storing this, it's redundant with LSBs of m_Grid) diff --git a/source/simulation2/components/CCmpPosition.cpp b/source/simulation2/components/CCmpPosition.cpp index 110bdfb435..70d7dfbe1f 100644 --- a/source/simulation2/components/CCmpPosition.cpp +++ b/source/simulation2/components/CCmpPosition.cpp @@ -60,7 +60,6 @@ public: PITCH_ROLL = 2, } m_AnchorType; - entity_pos_t m_YOffset; bool m_Floating; float m_RotYSpeed; // maximum radians per second, used by InterpolatedRotY to follow RotY @@ -68,6 +67,7 @@ public: bool m_InWorld; entity_pos_t m_X, m_Z, m_LastX, m_LastZ; // these values contain undefined junk if !InWorld + entity_pos_t m_YOffset; entity_angle_t m_RotX, m_RotY, m_RotZ; float m_InterpolatedRotY; // not serialized diff --git a/source/simulation2/components/CCmpRangeManager.cpp b/source/simulation2/components/CCmpRangeManager.cpp index 10677e4cb4..7bc86be6a4 100644 --- a/source/simulation2/components/CCmpRangeManager.cpp +++ b/source/simulation2/components/CCmpRangeManager.cpp @@ -44,7 +44,7 @@ struct Query entity_id_t source; entity_pos_t maxRange; u32 ownersMask; - int interface; + i32 interface; std::vector lastMatch; }; @@ -75,6 +75,42 @@ struct EntityData cassert(sizeof(EntityData) == 16); + +/** + * Serialization helper template for Query + */ +struct SerializeQuery +{ + template + void operator()(S& serialize, const char* UNUSED(name), Query& value) + { + serialize.Bool("enabled", value.enabled); + serialize.NumberU32_Unbounded("source", value.source); + serialize.NumberFixed_Unbounded("max range", value.maxRange); + serialize.NumberU32_Unbounded("owners mask", value.ownersMask); + serialize.NumberI32_Unbounded("interface", value.interface); + SerializeVector()(serialize, "last match", value.lastMatch); + } +}; + +/** + * Serialization helper template for EntityData + */ +struct SerializeEntityData +{ + template + void operator()(S& serialize, const char* UNUSED(name), EntityData& value) + { + serialize.NumberFixed_Unbounded("x", value.x); + serialize.NumberFixed_Unbounded("z", value.z); + serialize.NumberFixed_Unbounded("vision", value.visionRange); + serialize.NumberU8("retain in fog", value.retainInFog, 0, 1); + serialize.NumberI8_Unbounded("owner", value.owner); + serialize.NumberU8("in world", value.inWorld, 0, 1); + } +}; + + /** * Functor for sorting entities by distance from a source point. * It must only be passed entities that are in 'entities' @@ -133,17 +169,22 @@ public: bool m_DebugOverlayDirty; std::vector m_DebugOverlayLines; - SpatialSubdivision m_Subdivision; + // World bounds (entities are expected to be within this range) + entity_pos_t m_WorldX0; + entity_pos_t m_WorldZ0; + entity_pos_t m_WorldX1; + entity_pos_t m_WorldZ1; // Range query state: tag_t m_QueryNext; // next allocated id std::map m_Queries; std::map m_EntityData; + SpatialSubdivision m_Subdivision; // spatial index of m_EntityData // LOS state: bool m_LosRevealAll; - ssize_t m_TerrainVerticesPerSide; + i32 m_TerrainVerticesPerSide; // Counts of units seeing vertex, per vertex, per player (starting with player 0). // Use u16 to avoid overflows when we have very large (but not infeasibly large) numbers @@ -168,6 +209,8 @@ public: m_DebugOverlayEnabled = false; m_DebugOverlayDirty = true; + m_WorldX0 = m_WorldZ0 = m_WorldX1 = m_WorldZ1 = entity_pos_t::Zero(); + // Initialise with bogus values (these will get replaced when // SetBounds is called) ResetSubdivisions(entity_pos_t::FromInt(1), entity_pos_t::FromInt(1)); @@ -180,14 +223,38 @@ public: { } - virtual void Serialize(ISerializer& UNUSED(serialize)) + template + void SerializeCommon(S& serialize) { - // TODO + serialize.NumberFixed_Unbounded("world x0", m_WorldX0); + serialize.NumberFixed_Unbounded("world z0", m_WorldZ0); + serialize.NumberFixed_Unbounded("world x1", m_WorldX1); + serialize.NumberFixed_Unbounded("world z1", m_WorldZ1); + + serialize.NumberU32_Unbounded("query next", m_QueryNext); + SerializeMap()(serialize, "queries", m_Queries); + SerializeMap()(serialize, "entity data", m_EntityData); + + serialize.Bool("los reveal all", m_LosRevealAll); + serialize.NumberI32_Unbounded("terrain verts per side", m_TerrainVerticesPerSide); + + // We don't serialize m_Subdivision, m_LosPlayerCounts, m_LosState + // since they can be recomputed from the entity data when deserializing } - virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) + virtual void Serialize(ISerializer& serialize) + { + SerializeCommon(serialize); + } + + virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize) { Init(context, paramNode); + + SerializeCommon(deserialize); + + // Reinitialise subdivisions and LOS data + SetBounds(m_WorldX0, m_WorldZ0, m_WorldX1, m_WorldZ1, m_TerrainVerticesPerSide); } virtual void HandleMessage(const CSimContext& UNUSED(context), const CMessage& msg, bool UNUSED(global)) @@ -330,10 +397,15 @@ public: virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, ssize_t vertices) { + m_WorldX0 = x0; + m_WorldZ0 = z0; + m_WorldX1 = x1; + m_WorldZ1 = z1; + m_TerrainVerticesPerSide = vertices; + debug_assert(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet ResetSubdivisions(x1, z1); - m_TerrainVerticesPerSide = vertices; m_LosPlayerCounts.clear(); m_LosPlayerCounts.resize(MAX_LOS_PLAYER_ID+1); m_LosState.clear(); @@ -760,25 +832,25 @@ private: // Compute top/bottom coordinates, and clamp to exclude the 1-tile border around the map // (so that we never render the sharp edge of the map) - ssize_t j0 = ((pos.Y - visionRange)/(int)CELL_SIZE).ToInt_RoundToInfinity(); - ssize_t j1 = ((pos.Y + visionRange)/(int)CELL_SIZE).ToInt_RoundToNegInfinity(); - ssize_t j0clamp = std::max(j0, (ssize_t)1); - ssize_t j1clamp = std::min(j1, m_TerrainVerticesPerSide-2); + i32 j0 = ((pos.Y - visionRange)/(int)CELL_SIZE).ToInt_RoundToInfinity(); + i32 j1 = ((pos.Y + visionRange)/(int)CELL_SIZE).ToInt_RoundToNegInfinity(); + i32 j0clamp = std::max(j0, 1); + i32 j1clamp = std::min(j1, m_TerrainVerticesPerSide-2); entity_pos_t xscale = pos.X / (int)CELL_SIZE; entity_pos_t yscale = pos.Y / (int)CELL_SIZE; entity_pos_t rsquared = (visionRange / (int)CELL_SIZE).Square(); - for (ssize_t j = j0clamp; j <= j1clamp; ++j) + for (i32 j = j0clamp; j <= j1clamp; ++j) { // Compute values such that (i - x)^2 + (j - y)^2 <= r^2 // (TODO: is this sqrt slow? can we optimise it?) entity_pos_t di = (rsquared - (entity_pos_t::FromInt(j) - yscale).Square()).Sqrt(); - ssize_t i0 = (xscale - di).ToInt_RoundToInfinity(); - ssize_t i1 = (xscale + di).ToInt_RoundToNegInfinity(); + i32 i0 = (xscale - di).ToInt_RoundToInfinity(); + i32 i1 = (xscale + di).ToInt_RoundToNegInfinity(); - ssize_t i0clamp = std::max(i0, (ssize_t)1); - ssize_t i1clamp = std::min(i1, m_TerrainVerticesPerSide-2); + i32 i0clamp = std::max(i0, 1); + i32 i1clamp = std::min(i1, m_TerrainVerticesPerSide-2); LosUpdateStripHelper(owner, i0clamp, i1clamp, j, amount, counts); } } diff --git a/source/simulation2/components/CCmpSelectable.cpp b/source/simulation2/components/CCmpSelectable.cpp index 9537a2e8e4..ef3d3c6b22 100644 --- a/source/simulation2/components/CCmpSelectable.cpp +++ b/source/simulation2/components/CCmpSelectable.cpp @@ -44,6 +44,8 @@ public: DEFAULT_COMPONENT_ALLOCATOR(Selectable) + SOverlayLine m_Overlay; + CCmpSelectable() { m_Overlay.m_Thickness = 2; @@ -154,8 +156,6 @@ public: collector.Submit(&m_Overlay); } - - SOverlayLine m_Overlay; }; REGISTER_COMPONENT_TYPE(Selectable) diff --git a/source/simulation2/components/CCmpVision.cpp b/source/simulation2/components/CCmpVision.cpp index 47fd194be9..68101165ce 100644 --- a/source/simulation2/components/CCmpVision.cpp +++ b/source/simulation2/components/CCmpVision.cpp @@ -29,6 +29,8 @@ public: DEFAULT_COMPONENT_ALLOCATOR(Vision) + // Template state: + entity_pos_t m_Range; bool m_RetainInFog; bool m_AlwaysVisible; @@ -60,6 +62,7 @@ public: virtual void Serialize(ISerializer& UNUSED(serialize)) { + // No dynamic state to serialize } virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp index 94c4a8dfa8..199e01db7f 100644 --- a/source/simulation2/components/CCmpVisualActor.cpp +++ b/source/simulation2/components/CCmpVisualActor.cpp @@ -150,12 +150,18 @@ public: // TODO: store animation state - // TODO: store shading colour + serialize.NumberFixed_Unbounded("r", m_R); + serialize.NumberFixed_Unbounded("g", m_G); + serialize.NumberFixed_Unbounded("b", m_B); } - virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) + virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize) { Init(context, paramNode); + + deserialize.NumberFixed_Unbounded("r", m_R); + deserialize.NumberFixed_Unbounded("g", m_G); + deserialize.NumberFixed_Unbounded("b", m_B); } virtual void HandleMessage(const CSimContext& UNUSED(context), const CMessage& msg, bool UNUSED(global)) diff --git a/source/simulation2/components/CCmpWaterManager.cpp b/source/simulation2/components/CCmpWaterManager.cpp index fb8e030f2a..70e610c930 100644 --- a/source/simulation2/components/CCmpWaterManager.cpp +++ b/source/simulation2/components/CCmpWaterManager.cpp @@ -33,6 +33,8 @@ public: DEFAULT_COMPONENT_ALLOCATOR(WaterManager) + // Dynamic state: + entity_pos_t m_WaterHeight; static std::string GetSchema() @@ -49,13 +51,16 @@ public: { } - virtual void Serialize(ISerializer& UNUSED(serialize)) + virtual void Serialize(ISerializer& serialize) { + serialize.NumberFixed_Unbounded("height", m_WaterHeight); } - virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) + virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize) { Init(context, paramNode); + + deserialize.NumberFixed_Unbounded("height", m_WaterHeight); } virtual void HandleMessage(const CSimContext& UNUSED(context), const CMessage& msg, bool UNUSED(global)) diff --git a/source/simulation2/helpers/Spatial.h b/source/simulation2/helpers/Spatial.h index a13c45189e..4dbf04220d 100644 --- a/source/simulation2/helpers/Spatial.h +++ b/source/simulation2/helpers/Spatial.h @@ -15,6 +15,11 @@ * along with 0 A.D. If not, see . */ +#ifndef INCLUDED_SPATIAL +#define INCLUDED_SPATIAL + +#include "simulation2/serialization/SerializeTemplates.h" + /** * A very basic subdivision scheme for finding items in ranges. * Items are stored in lists in fixed-size divisions. @@ -221,4 +226,37 @@ private: std::vector > m_Divisions; size_t m_DivisionsW; size_t m_DivisionsH; + + template friend struct SerializeSpatialSubdivision; }; + +/** + * Serialization helper template for SpatialSubdivision + */ +template +struct SerializeSpatialSubdivision +{ + template + void operator()(ISerializer& serialize, const char* UNUSED(name), SpatialSubdivision& value) + { + serialize.NumberFixed_Unbounded("div size", value.m_DivisionSize); + SerializeVector >()(serialize, "divs", value.m_Divisions); + serialize.NumberU32_Unbounded("divs w", value.m_DivisionsW); + serialize.NumberU32_Unbounded("divs h", value.m_DivisionsH); + } + + template + void operator()(IDeserializer& serialize, const char* UNUSED(name), SpatialSubdivision& value) + { + serialize.NumberFixed_Unbounded("div size", value.m_DivisionSize); + SerializeVector >()(serialize, "divs", value.m_Divisions); + + u32 w, h; + serialize.NumberU32_Unbounded("divs w", w); + serialize.NumberU32_Unbounded("divs h", h); + value.m_DivisionsW = w; + value.m_DivisionsH = h; + } +}; + +#endif // INCLUDED_SPATIAL diff --git a/source/simulation2/serialization/BinarySerializer.h b/source/simulation2/serialization/BinarySerializer.h index b8f07a28be..6b1f96f6a8 100644 --- a/source/simulation2/serialization/BinarySerializer.h +++ b/source/simulation2/serialization/BinarySerializer.h @@ -92,10 +92,9 @@ protected: m_Impl.Put(name, (const u8*)&value, sizeof(uint8_t)); } - virtual void PutNumber(const char* name, int32_t value) + virtual void PutNumber(const char* name, int8_t value) { - int32_t v = (i32)to_le32((u32)value); - m_Impl.Put(name, (const u8*)&v, sizeof(int32_t)); + m_Impl.Put(name, (const u8*)&value, sizeof(int8_t)); } virtual void PutNumber(const char* name, uint32_t value) @@ -104,6 +103,12 @@ protected: m_Impl.Put(name, (const u8*)&v, sizeof(uint32_t)); } + virtual void PutNumber(const char* name, int32_t value) + { + int32_t v = (i32)to_le32((u32)value); + m_Impl.Put(name, (const u8*)&v, sizeof(int32_t)); + } + virtual void PutNumber(const char* name, float value) { m_Impl.Put(name, (const u8*)&value, sizeof(float)); diff --git a/source/simulation2/serialization/DebugSerializer.cpp b/source/simulation2/serialization/DebugSerializer.cpp index 8a12cde63f..6b43ef883a 100644 --- a/source/simulation2/serialization/DebugSerializer.cpp +++ b/source/simulation2/serialization/DebugSerializer.cpp @@ -85,12 +85,17 @@ void CDebugSerializer::PutNumber(const char* name, uint8_t value) m_Stream << INDENT << name << ": " << (int)value << "\n"; } -void CDebugSerializer::PutNumber(const char* name, int32_t value) +void CDebugSerializer::PutNumber(const char* name, int8_t value) +{ + m_Stream << INDENT << name << ": " << (int)value << "\n"; +} + +void CDebugSerializer::PutNumber(const char* name, uint32_t value) { m_Stream << INDENT << name << ": " << value << "\n"; } -void CDebugSerializer::PutNumber(const char* name, uint32_t value) +void CDebugSerializer::PutNumber(const char* name, int32_t value) { m_Stream << INDENT << name << ": " << value << "\n"; } diff --git a/source/simulation2/serialization/DebugSerializer.h b/source/simulation2/serialization/DebugSerializer.h index c271331068..a853e58de7 100644 --- a/source/simulation2/serialization/DebugSerializer.h +++ b/source/simulation2/serialization/DebugSerializer.h @@ -42,8 +42,9 @@ public: protected: virtual void PutNumber(const char* name, uint8_t value); - virtual void PutNumber(const char* name, int32_t value); + virtual void PutNumber(const char* name, int8_t value); virtual void PutNumber(const char* name, uint32_t value); + virtual void PutNumber(const char* name, int32_t value); virtual void PutNumber(const char* name, float value); virtual void PutNumber(const char* name, double value); virtual void PutNumber(const char* name, fixed value); diff --git a/source/simulation2/serialization/IDeserializer.cpp b/source/simulation2/serialization/IDeserializer.cpp index 891c9eb075..9e5262c46b 100644 --- a/source/simulation2/serialization/IDeserializer.cpp +++ b/source/simulation2/serialization/IDeserializer.cpp @@ -44,9 +44,9 @@ void IDeserializer::NumberU8(const char* UNUSED(name), uint8_t& out, uint8_t low out = Number_(lower, upper); } -void IDeserializer::NumberI32(const char* UNUSED(name), int32_t& out, int32_t lower, int32_t upper) +void IDeserializer::NumberI8(const char* UNUSED(name), int8_t& out, int8_t lower, int8_t upper) { - out = (i32)to_le32((u32)Number_(lower, upper)); + out = Number_(lower, upper); } void IDeserializer::NumberU32(const char* UNUSED(name), uint32_t& out, uint32_t lower, uint32_t upper) @@ -54,16 +54,19 @@ void IDeserializer::NumberU32(const char* UNUSED(name), uint32_t& out, uint32_t out = to_le32(Number_(lower, upper)); } +void IDeserializer::NumberI32(const char* UNUSED(name), int32_t& out, int32_t lower, int32_t upper) +{ + out = (i32)to_le32((u32)Number_(lower, upper)); +} + void IDeserializer::NumberU8_Unbounded(const char* UNUSED(name), uint8_t& out) { Get((u8*)&out, sizeof(uint8_t)); } -void IDeserializer::NumberI32_Unbounded(const char* UNUSED(name), int32_t& out) +void IDeserializer::NumberI8_Unbounded(const char* UNUSED(name), int8_t& out) { - int32_t value; - Get((u8*)&value, sizeof(int32_t)); - out = (i32)to_le32((u32)value); + Get((u8*)&out, sizeof(int8_t)); } void IDeserializer::NumberU32_Unbounded(const char* UNUSED(name), uint32_t& out) @@ -73,6 +76,13 @@ void IDeserializer::NumberU32_Unbounded(const char* UNUSED(name), uint32_t& out) out = to_le32(value); } +void IDeserializer::NumberI32_Unbounded(const char* UNUSED(name), int32_t& out) +{ + int32_t value; + Get((u8*)&value, sizeof(int32_t)); + out = (i32)to_le32((u32)value); +} + void IDeserializer::NumberFloat_Unbounded(const char* UNUSED(name), float& out) { Get((u8*)&out, sizeof(float)); diff --git a/source/simulation2/serialization/IDeserializer.h b/source/simulation2/serialization/IDeserializer.h index aefecac682..7f8b92e89d 100644 --- a/source/simulation2/serialization/IDeserializer.h +++ b/source/simulation2/serialization/IDeserializer.h @@ -37,11 +37,13 @@ public: virtual ~IDeserializer(); virtual void NumberU8(const char* name, uint8_t& out, uint8_t lower, uint8_t upper); - virtual void NumberI32(const char* name, int32_t& out, int32_t lower, int32_t upper); + virtual void NumberI8(const char* name, int8_t& out, int8_t lower, int8_t upper); virtual void NumberU32(const char* name, uint32_t& out, uint32_t lower, uint32_t upper); + virtual void NumberI32(const char* name, int32_t& out, int32_t lower, int32_t upper); virtual void NumberU8_Unbounded(const char* name, uint8_t& out); - virtual void NumberI32_Unbounded(const char* name, int32_t& out); + virtual void NumberI8_Unbounded(const char* name, int8_t& out); virtual void NumberU32_Unbounded(const char* name, uint32_t& out); + virtual void NumberI32_Unbounded(const char* name, int32_t& out); virtual void NumberFloat_Unbounded(const char* name, float& out); virtual void NumberDouble_Unbounded(const char* name, double& out); virtual void NumberFixed_Unbounded(const char* name, fixed& out); diff --git a/source/simulation2/serialization/ISerializer.cpp b/source/simulation2/serialization/ISerializer.cpp index 03a8b3c3e5..c6b19f1a36 100644 --- a/source/simulation2/serialization/ISerializer.cpp +++ b/source/simulation2/serialization/ISerializer.cpp @@ -32,7 +32,7 @@ void ISerializer::NumberU8(const char* name, uint8_t value, uint8_t lower, uint8 PutNumber(name, value); } -void ISerializer::NumberI32(const char* name, int32_t value, int32_t lower, int32_t upper) +void ISerializer::NumberI8(const char* name, int8_t value, int8_t lower, int8_t upper) { if (!(lower <= value && value <= upper)) throw PSERROR_Serialize_OutOfBounds(); @@ -46,6 +46,13 @@ void ISerializer::NumberU32(const char* name, uint32_t value, uint32_t lower, ui PutNumber(name, value); } +void ISerializer::NumberI32(const char* name, int32_t value, int32_t lower, int32_t upper) +{ + if (!(lower <= value && value <= upper)) + throw PSERROR_Serialize_OutOfBounds(); + PutNumber(name, value); +} + void ISerializer::StringASCII(const char* name, const std::string& value, uint32_t minlength, uint32_t maxlength) { if (!(minlength <= value.length() && value.length() <= maxlength)) diff --git a/source/simulation2/serialization/ISerializer.h b/source/simulation2/serialization/ISerializer.h index 348020d720..1e945b78fa 100644 --- a/source/simulation2/serialization/ISerializer.h +++ b/source/simulation2/serialization/ISerializer.h @@ -135,8 +135,9 @@ public: * @param upper inclusive upper bound */ void NumberU8(const char* name, uint8_t value, uint8_t lower, uint8_t upper); - void NumberI32(const char* name, int32_t value, int32_t lower, int32_t upper); ///< @copydoc NumberU8 + void NumberI8(const char* name, int8_t value, int8_t lower, int8_t upper); void NumberU32(const char* name, uint32_t value, uint32_t lower, uint32_t upper); ///< @copydoc NumberU8 + void NumberI32(const char* name, int32_t value, int32_t lower, int32_t upper); ///< @copydoc NumberU8 /** * Serialize a number. @@ -150,7 +151,7 @@ public: PutNumber(name, value); } - void NumberI32_Unbounded(const char* name, int32_t value) ///@copydoc NumberU8_Unbounded() + void NumberI8_Unbounded(const char* name, int8_t value) ///@copydoc NumberU8_Unbounded() { PutNumber(name, value); } @@ -160,6 +161,11 @@ public: PutNumber(name, value); } + void NumberI32_Unbounded(const char* name, int32_t value) ///@copydoc NumberU8_Unbounded() + { + PutNumber(name, value); + } + void NumberFloat_Unbounded(const char* name, float value) ///@copydoc NumberU8_Unbounded() { PutNumber(name, value); @@ -234,8 +240,9 @@ public: protected: virtual void PutNumber(const char* name, uint8_t value) = 0; - virtual void PutNumber(const char* name, int32_t value) = 0; + virtual void PutNumber(const char* name, int8_t value) = 0; virtual void PutNumber(const char* name, uint32_t value) = 0; + virtual void PutNumber(const char* name, int32_t value) = 0; virtual void PutNumber(const char* name, float value) = 0; virtual void PutNumber(const char* name, double value) = 0; virtual void PutNumber(const char* name, fixed value) = 0; diff --git a/source/simulation2/serialization/SerializeTemplates.h b/source/simulation2/serialization/SerializeTemplates.h index 45767918eb..5634ee19bf 100644 --- a/source/simulation2/serialization/SerializeTemplates.h +++ b/source/simulation2/serialization/SerializeTemplates.h @@ -15,6 +15,9 @@ * along with 0 A.D. If not, see . */ +#ifndef INCLUDED_SERIALIZETEMPLATES +#define INCLUDED_SERIALIZETEMPLATES + /** * @file * Helper templates for serializing/deserializing common objects. @@ -35,6 +38,7 @@ struct SerializeVector template void operator()(IDeserializer& deserialize, const char* name, std::vector& value) { + value.clear(); u32 len; deserialize.NumberU32_Unbounded("length", len); value.reserve(len); // TODO: watch out for out-of-memory @@ -47,18 +51,35 @@ struct SerializeVector } }; -struct SerializeWaypoint +template +struct SerializeMap { - void operator()(ISerializer& serialize, const char* UNUSED(name), const ICmpPathfinder::Waypoint& value) + template + void operator()(ISerializer& serialize, const char* UNUSED(name), std::map& value) { - serialize.NumberFixed_Unbounded("waypoint x", value.x); - serialize.NumberFixed_Unbounded("waypoint z", value.z); + size_t len = value.size(); + serialize.NumberU32_Unbounded("length", (u32)len); + for (typename std::map::iterator it = value.begin(); it != value.end(); ++it) + { + KS()(serialize, "key", it->first); + VS()(serialize, "value", it->second); + } } - void operator()(IDeserializer& deserialize, const char* UNUSED(name), ICmpPathfinder::Waypoint& value) + template + void operator()(IDeserializer& deserialize, const char* UNUSED(name), std::map& value) { - deserialize.NumberFixed_Unbounded("waypoint x", value.x); - deserialize.NumberFixed_Unbounded("waypoint z", value.z); + value.clear(); + u32 len; + deserialize.NumberU32_Unbounded("length", len); + for (size_t i = 0; i < len; ++i) + { + K k; + V v; + KS()(deserialize, "key", k); + VS()(deserialize, "value", v); + value.insert(std::make_pair(k, v)); + } } }; @@ -78,6 +99,34 @@ struct SerializeU8_Enum } }; +struct SerializeU32_Unbounded +{ + void operator()(ISerializer& serialize, const char* name, u32 value) + { + serialize.NumberU32_Unbounded(name, value); + } + + void operator()(IDeserializer& deserialize, const char* name, u32& value) + { + deserialize.NumberU32_Unbounded(name, value); + } +}; + +struct SerializeWaypoint +{ + void operator()(ISerializer& serialize, const char* UNUSED(name), const ICmpPathfinder::Waypoint& value) + { + serialize.NumberFixed_Unbounded("waypoint x", value.x); + serialize.NumberFixed_Unbounded("waypoint z", value.z); + } + + void operator()(IDeserializer& deserialize, const char* UNUSED(name), ICmpPathfinder::Waypoint& value) + { + deserialize.NumberFixed_Unbounded("waypoint x", value.x); + deserialize.NumberFixed_Unbounded("waypoint z", value.z); + } +}; + struct SerializeGoal { template @@ -94,3 +143,5 @@ struct SerializeGoal serialize.NumberFixed_Unbounded("goal hh", value.hh); } }; + +#endif // INCLUDED_SERIALIZETEMPLATES diff --git a/source/simulation2/system/ParamNode.cpp b/source/simulation2/system/ParamNode.cpp index fc03054217..4d57021f5c 100644 --- a/source/simulation2/system/ParamNode.cpp +++ b/source/simulation2/system/ParamNode.cpp @@ -20,6 +20,7 @@ #include "ParamNode.h" #include "ps/CStr.h" +#include "ps/Filesystem.h" #include "ps/XML/Xeromyces.h" #include "js/jsapi.h" @@ -46,6 +47,16 @@ void CParamNode::LoadXML(CParamNode& ret, const XMBFile& xmb) ret.ApplyLayer(xmb, xmb.GetRoot()); } +void CParamNode::LoadXML(CParamNode& ret, const VfsPath& path) +{ + CXeromyces xero; + PSRETURN ok = xero.Load(g_VFS, path); + if (ok != PSRETURN_OK) + return; // (Xeromyces already logged an error) + + LoadXML(ret, xero); +} + PSRETURN CParamNode::LoadXMLString(CParamNode& ret, const char* xml) { CXeromyces xero; diff --git a/source/simulation2/system/ParamNode.h b/source/simulation2/system/ParamNode.h index 70dfec936b..4248090b73 100644 --- a/source/simulation2/system/ParamNode.h +++ b/source/simulation2/system/ParamNode.h @@ -18,6 +18,7 @@ #ifndef INCLUDED_PARAMNODE #define INCLUDED_PARAMNODE +#include "lib/file/vfs/vfs_path.h" #include "maths/Fixed.h" #include "ps/Errors.h" #include "scriptinterface/ScriptVal.h" @@ -124,6 +125,13 @@ public: */ static void LoadXML(CParamNode& ret, const XMBFile& file); + /** + * Loads the XML data specified by @a path into the node @a ret. + * Any existing data in @a ret will be overwritten or else kept, so this + * can be called multiple times to build up a node from multiple inputs. + */ + static void LoadXML(CParamNode& ret, const VfsPath& path); + /** * See LoadXML, but parses the XML string @a xml. * @return error code if parsing failed, else @c PSRETURN_OK