From 8a981021958d61949c9ea1d935c437705b6744c3 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Fri, 17 Sep 2010 17:53:26 +0000 Subject: [PATCH] Fix serialize/deserialize API asymmetry. Add serialization support to more components. This was SVN commit r8122. --- source/network/NetMessageSim.cpp | 10 +- source/network/NetTurnManager.cpp | 9 ++ .../components/CCmpCommandQueue.cpp | 6 +- .../simulation2/components/CCmpMotionBall.cpp | 4 +- .../simulation2/components/CCmpOwnership.cpp | 2 +- .../simulation2/components/CCmpPathfinder.cpp | 50 ++++++++++ .../components/CCmpPathfinder_Common.h | 22 ++--- .../simulation2/components/CCmpPosition.cpp | 18 ++-- .../components/CCmpTemplateManager.cpp | 6 +- source/simulation2/components/CCmpTest.cpp | 6 +- .../simulation2/components/CCmpUnitMotion.cpp | 63 ++++++++---- .../simulation2/components/ICmpPathfinder.h | 2 +- .../simulation2/scripting/ScriptComponent.cpp | 2 +- .../serialization/IDeserializer.cpp | 36 +++---- .../simulation2/serialization/IDeserializer.h | 34 +++---- .../serialization/SerializeTemplates.h | 96 +++++++++++++++++++ .../serialization/StdDeserializer.cpp | 26 ++--- .../serialization/StdDeserializer.h | 10 +- .../system/ComponentManagerSerialization.cpp | 12 +-- source/simulation2/system/Message.h | 4 + source/simulation2/tests/test_Serializer.h | 38 ++++---- 21 files changed, 317 insertions(+), 139 deletions(-) create mode 100644 source/simulation2/serialization/SerializeTemplates.h diff --git a/source/network/NetMessageSim.cpp b/source/network/NetMessageSim.cpp index 8075b70d1d..9877f82db8 100644 --- a/source/network/NetMessageSim.cpp +++ b/source/network/NetMessageSim.cpp @@ -126,10 +126,10 @@ const u8* CSimulationMessage::Deserialize(const u8* pStart, const u8* pEnd) const u8* pos = CNetMessage::Deserialize(pStart, pEnd); std::istringstream stream(std::string(pos, pEnd)); CStdDeserializer deserializer(m_ScriptInterface, stream); - deserializer.NumberU32_Unbounded(m_Client); - deserializer.NumberI32_Unbounded(m_Player); - deserializer.NumberU32_Unbounded(m_Turn); - deserializer.ScriptVal(m_Data); + deserializer.NumberU32_Unbounded("client", m_Client); + deserializer.NumberI32_Unbounded("player", m_Player); + deserializer.NumberU32_Unbounded("turn", m_Turn); + deserializer.ScriptVal("command", m_Data); return pEnd; } @@ -184,7 +184,7 @@ const u8* CGameSetupMessage::Deserialize(const u8* pStart, const u8* pEnd) const u8* pos = CNetMessage::Deserialize(pStart, pEnd); std::istringstream stream(std::string(pos, pEnd)); CStdDeserializer deserializer(m_ScriptInterface, stream); - deserializer.ScriptVal(m_Data); + deserializer.ScriptVal("command", m_Data); return pEnd; } diff --git a/source/network/NetTurnManager.cpp b/source/network/NetTurnManager.cpp index 5065c10409..c232561261 100644 --- a/source/network/NetTurnManager.cpp +++ b/source/network/NetTurnManager.cpp @@ -264,6 +264,15 @@ void CNetLocalTurnManager::NotifyFinishedOwnCommands(u32 turn) void CNetLocalTurnManager::NotifyFinishedUpdate(u32 UNUSED(turn)) { +#if 0 // this hurts performance and is only useful for verifying log replays + std::string hash; + { + PROFILE("state hash check"); + bool ok = m_Simulation2.ComputeStateHash(hash); + debug_assert(ok); + } + m_Replay.Hash(hash); +#endif } void CNetLocalTurnManager::OnSimulationMessage(CSimulationMessage* UNUSED(msg)) diff --git a/source/simulation2/components/CCmpCommandQueue.cpp b/source/simulation2/components/CCmpCommandQueue.cpp index bafb27e435..ab23137d95 100644 --- a/source/simulation2/components/CCmpCommandQueue.cpp +++ b/source/simulation2/components/CCmpCommandQueue.cpp @@ -63,13 +63,13 @@ public: JSContext* cx = context.GetScriptInterface().GetContext(); u32 numCmds; - deserialize.NumberU32_Unbounded(numCmds); + deserialize.NumberU32_Unbounded("num commands", numCmds); for (size_t i = 0; i < numCmds; ++i) { i32 player; jsval data; - deserialize.NumberI32_Unbounded(player); - deserialize.ScriptVal(data); + deserialize.NumberI32_Unbounded("player", player); + deserialize.ScriptVal("data", data); SimulationCommand c = { player, CScriptValRooted(cx, data) }; m_LocalQueue.push_back(c); } diff --git a/source/simulation2/components/CCmpMotionBall.cpp b/source/simulation2/components/CCmpMotionBall.cpp index 253df95c20..64a9d5f8e9 100644 --- a/source/simulation2/components/CCmpMotionBall.cpp +++ b/source/simulation2/components/CCmpMotionBall.cpp @@ -62,8 +62,8 @@ public: virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize) { Init(context, paramNode); - deserialize.NumberFloat_Unbounded(m_SpeedX); - deserialize.NumberFloat_Unbounded(m_SpeedZ); + deserialize.NumberFloat_Unbounded("speed x", m_SpeedX); + deserialize.NumberFloat_Unbounded("speed z", m_SpeedZ); } virtual void HandleMessage(const CSimContext& context, const CMessage& msg, bool UNUSED(global)) diff --git a/source/simulation2/components/CCmpOwnership.cpp b/source/simulation2/components/CCmpOwnership.cpp index 940f8d5314..7a1709ab0f 100644 --- a/source/simulation2/components/CCmpOwnership.cpp +++ b/source/simulation2/components/CCmpOwnership.cpp @@ -61,7 +61,7 @@ public: virtual void Deserialize(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode), IDeserializer& deserialize) { - deserialize.NumberI32_Unbounded(m_Owner); + deserialize.NumberI32_Unbounded("owner", m_Owner); } virtual void HandleMessage(const CSimContext& UNUSED(context), const CMessage& msg, bool UNUSED(global)) diff --git a/source/simulation2/components/CCmpPathfinder.cpp b/source/simulation2/components/CCmpPathfinder.cpp index 4a7bc94517..5b7287323c 100644 --- a/source/simulation2/components/CCmpPathfinder.cpp +++ b/source/simulation2/components/CCmpPathfinder.cpp @@ -31,6 +31,7 @@ #include "simulation2/MessageTypes.h" #include "simulation2/components/ICmpObstructionManager.h" #include "simulation2/components/ICmpWaterManager.h" +#include "simulation2/serialization/SerializeTemplates.h" // Default cost to move a single tile is a fairly arbitrary number, which should be big // enough to be precise when multiplied/divided and small enough to never overflow when @@ -126,6 +127,55 @@ void CCmpPathfinder::Deinit(const CSimContext& UNUSED(context)) delete m_ObstructionGrid; } +struct SerializeLongRequest +{ + template + void operator()(S& serialize, const char* UNUSED(name), AsyncLongPathRequest& value) + { + serialize.NumberU32_Unbounded("ticket", value.ticket); + serialize.NumberFixed_Unbounded("x0", value.x0); + serialize.NumberFixed_Unbounded("z0", value.z0); + SerializeGoal()(serialize, "goal", value.goal); + serialize.NumberU8_Unbounded("pass class", value.passClass); + serialize.NumberU8_Unbounded("cost class", value.costClass); + serialize.NumberU32_Unbounded("notify", value.notify); + } +}; + +struct SerializeShortRequest +{ + template + void operator()(S& serialize, const char* UNUSED(name), AsyncShortPathRequest& value) + { + serialize.NumberU32_Unbounded("ticket", value.ticket); + serialize.NumberFixed_Unbounded("x0", value.x0); + serialize.NumberFixed_Unbounded("z0", value.z0); + serialize.NumberFixed_Unbounded("r", value.r); + serialize.NumberFixed_Unbounded("range", value.range); + SerializeGoal()(serialize, "goal", value.goal); + serialize.NumberU8_Unbounded("pass class", value.passClass); + serialize.Bool("avoid moving units", value.avoidMovingUnits); + serialize.NumberU32_Unbounded("group", value.group); + serialize.NumberU32_Unbounded("notify", value.notify); + } +}; + +void CCmpPathfinder::Serialize(ISerializer& serialize) +{ + SerializeVector()(serialize, "long requests", m_AsyncLongPathRequests); + SerializeVector()(serialize, "short requests", m_AsyncShortPathRequests); + serialize.NumberU32_Unbounded("next ticket", m_NextAsyncTicket); +} + +void CCmpPathfinder::Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize) +{ + Init(context, paramNode); + + SerializeVector()(deserialize, "long requests", m_AsyncLongPathRequests); + SerializeVector()(deserialize, "short requests", m_AsyncShortPathRequests); + deserialize.NumberU32_Unbounded("next ticket", m_NextAsyncTicket); +} + void CCmpPathfinder::HandleMessage(const CSimContext& context, const CMessage& msg, bool UNUSED(global)) { switch (msg.GetType()) diff --git a/source/simulation2/components/CCmpPathfinder_Common.h b/source/simulation2/components/CCmpPathfinder_Common.h index 75cb94a892..cc6f5a4ce9 100644 --- a/source/simulation2/components/CCmpPathfinder_Common.h +++ b/source/simulation2/components/CCmpPathfinder_Common.h @@ -167,12 +167,12 @@ public: std::vector m_AsyncLongPathRequests; std::vector m_AsyncShortPathRequests; + u32 m_NextAsyncTicket; // unique IDs for asynchronous path requests 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) bool m_TerrainDirty; // indicates if m_Grid has been updated since terrain changed - u32 m_NextAsyncTicket; // unique IDs for asynchronous path requests // Debugging - output from last pathfind operation: Grid* m_DebugGrid; @@ -188,25 +188,15 @@ public: return ""; } - virtual void Init(const CSimContext& UNUSED(context), const CParamNode& paramNode); + virtual void Init(const CSimContext& context, const CParamNode& paramNode); - virtual void Deinit(const CSimContext& UNUSED(context)); + virtual void Deinit(const CSimContext& context); - virtual void Serialize(ISerializer& UNUSED(serialize)) - { - // TODO: do something here - // (Do we need to serialise the pathfinder state, or is it fine to regenerate it from - // the original entities after deserialisation?) - } + virtual void Serialize(ISerializer& serialize); - virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) - { - Init(context, paramNode); + virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize); - // TODO - } - - virtual void HandleMessage(const CSimContext& context, const CMessage& msg, bool UNUSED(global)); + virtual void HandleMessage(const CSimContext& context, const CMessage& msg, bool global); virtual u8 GetPassabilityClass(const std::string& name); diff --git a/source/simulation2/components/CCmpPosition.cpp b/source/simulation2/components/CCmpPosition.cpp index 6b5419e28f..110bdfb435 100644 --- a/source/simulation2/components/CCmpPosition.cpp +++ b/source/simulation2/components/CCmpPosition.cpp @@ -156,18 +156,18 @@ public: { Init(context, paramNode); - deserialize.Bool(m_InWorld); + deserialize.Bool("in world", m_InWorld); if (m_InWorld) { - deserialize.NumberFixed_Unbounded(m_X); - deserialize.NumberFixed_Unbounded(m_Z); - deserialize.NumberFixed_Unbounded(m_LastX); - deserialize.NumberFixed_Unbounded(m_LastZ); + deserialize.NumberFixed_Unbounded("x", m_X); + deserialize.NumberFixed_Unbounded("z", m_Z); + deserialize.NumberFixed_Unbounded("last x", m_LastX); + deserialize.NumberFixed_Unbounded("last z", m_LastZ); } - deserialize.NumberFixed_Unbounded(m_RotX); - deserialize.NumberFixed_Unbounded(m_RotY); - deserialize.NumberFixed_Unbounded(m_RotZ); - deserialize.NumberFixed_Unbounded(m_YOffset); + deserialize.NumberFixed_Unbounded("rot x", m_RotX); + deserialize.NumberFixed_Unbounded("rot y", m_RotY); + deserialize.NumberFixed_Unbounded("rot z", m_RotZ); + deserialize.NumberFixed_Unbounded("altitude", m_YOffset); // TODO: should there be range checks on all these values? m_InterpolatedRotY = m_RotY.ToFloat(); diff --git a/source/simulation2/components/CCmpTemplateManager.cpp b/source/simulation2/components/CCmpTemplateManager.cpp index a35aecfd9a..c562cbbdee 100644 --- a/source/simulation2/components/CCmpTemplateManager.cpp +++ b/source/simulation2/components/CCmpTemplateManager.cpp @@ -89,13 +89,13 @@ public: Init(context, paramNode); u32 numEntities; - deserialize.NumberU32_Unbounded(numEntities); + deserialize.NumberU32_Unbounded("num entities", numEntities); for (u32 i = 0; i < numEntities; ++i) { entity_id_t ent; std::string templateName; - deserialize.NumberU32_Unbounded(ent); - deserialize.StringASCII(templateName, 0, 256); + deserialize.NumberU32_Unbounded("id", ent); + deserialize.StringASCII("template", templateName, 0, 256); m_LatestTemplates[ent] = templateName; } } diff --git a/source/simulation2/components/CCmpTest.cpp b/source/simulation2/components/CCmpTest.cpp index 18d927f73d..fb303d71f9 100644 --- a/source/simulation2/components/CCmpTest.cpp +++ b/source/simulation2/components/CCmpTest.cpp @@ -60,7 +60,7 @@ public: virtual void Deserialize(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode), IDeserializer& deserialize) { - deserialize.NumberI32_Unbounded(m_x); + deserialize.NumberI32_Unbounded("x", m_x); } virtual int GetX() @@ -121,7 +121,7 @@ public: virtual void Deserialize(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode), IDeserializer& deserialize) { - deserialize.NumberI32_Unbounded(m_x); + deserialize.NumberI32_Unbounded("x", m_x); } virtual int GetX() @@ -182,7 +182,7 @@ public: virtual void Deserialize(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode), IDeserializer& deserialize) { - deserialize.NumberI32_Unbounded(m_x); + deserialize.NumberI32_Unbounded("x", m_x); } virtual int GetX() diff --git a/source/simulation2/components/CCmpUnitMotion.cpp b/source/simulation2/components/CCmpUnitMotion.cpp index 7e9d907d3c..cb33ce98cc 100644 --- a/source/simulation2/components/CCmpUnitMotion.cpp +++ b/source/simulation2/components/CCmpUnitMotion.cpp @@ -27,6 +27,7 @@ #include "simulation2/MessageTypes.h" #include "simulation2/helpers/Geometry.h" #include "simulation2/helpers/Render.h" +#include "simulation2/serialization/SerializeTemplates.h" #include "graphics/Overlay.h" #include "graphics/Terrain.h" @@ -74,7 +75,7 @@ public: entity_pos_t m_Radius; - enum + enum State { STATE_IDLE, // not moving at all STATE_STOPPING, // will go IDLE next turn (this delay fixes animation timings) @@ -86,7 +87,8 @@ public: // Entities that represent individual units or formation controllers: // ('target' is m_FinalGoal) STATE_INDIVIDUAL_PATH_COMPUTING, // waiting for a path to target - STATE_INDIVIDUAL_PATH_FOLLOWING // following the computed path to target + STATE_INDIVIDUAL_PATH_FOLLOWING, // following the computed path to target + STATE_MAX }; int m_State; @@ -97,7 +99,6 @@ public: fixed m_Speed; - // These values contain undefined junk if !HasTarget: ICmpPathfinder::Path m_LongPath; ICmpPathfinder::Path m_ShortPath; entity_pos_t m_ShortTargetX, m_ShortTargetZ; @@ -157,10 +158,6 @@ public: m_RunSpeed = m_WalkSpeed; } - CmpPtr cmpObstruction(context, GetEntityId()); - if (!cmpObstruction.null()) - m_Radius = cmpObstruction->GetUnitRadius(); - CmpPtr cmpPathfinder(context, SYSTEM_ENTITY); if (!cmpPathfinder.null()) { @@ -168,10 +165,18 @@ public: m_CostClass = cmpPathfinder->GetCostClass(paramNode.GetChild("CostClass").ToASCIIString()); } + CmpPtr cmpObstruction(context, GetEntityId()); + if (!cmpObstruction.null()) + m_Radius = cmpObstruction->GetUnitRadius(); + m_State = STATE_IDLE; m_ExpectedPathTicket = 0; + m_TargetEntity = 0; + + m_FinalGoal.type = ICmpPathfinder::Goal::POINT; + m_DebugOverlayEnabled = false; } @@ -181,24 +186,46 @@ public: virtual void Serialize(ISerializer& serialize) { -// serialize.Bool("has target", m_HasTarget); -// if (m_HasTarget) - { - // TODO: m_Path - // TODO: m_FinalTargetAngle - } + serialize.NumberFixed_Unbounded("radius", m_Radius); - // TODO: m_State + serialize.NumberU8("state", m_State, 0, STATE_MAX-1); + + serialize.NumberU32_Unbounded("ticket", m_ExpectedPathTicket); + + serialize.NumberU32_Unbounded("target entity", m_TargetEntity); + serialize.NumberFixed_Unbounded("target offset x", m_TargetOffsetX); + serialize.NumberFixed_Unbounded("target offset z", m_TargetOffsetZ); + serialize.NumberFixed_Unbounded("speed", m_Speed); + + SerializeVector()(serialize, "long path", m_LongPath.m_Waypoints); + SerializeVector()(serialize, "short path", m_ShortPath.m_Waypoints); + serialize.NumberFixed_Unbounded("short target x", m_ShortTargetX); + serialize.NumberFixed_Unbounded("short target z", m_ShortTargetZ); + SerializeGoal()(serialize, "goal", m_FinalGoal); } virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize) { Init(context, paramNode); -// deserialize.Bool(m_HasTarget); -// if (m_HasTarget) - { - } + deserialize.NumberFixed_Unbounded("radius", m_Radius); + + u8 state; + deserialize.NumberU8("state", state, 0, STATE_MAX-1); + m_State = (State)state; + + deserialize.NumberU32_Unbounded("ticket", m_ExpectedPathTicket); + + deserialize.NumberU32_Unbounded("target entity", m_TargetEntity); + deserialize.NumberFixed_Unbounded("target offset x", m_TargetOffsetX); + deserialize.NumberFixed_Unbounded("target offset z", m_TargetOffsetZ); + deserialize.NumberFixed_Unbounded("speed", m_Speed); + + SerializeVector()(deserialize, "long path", m_LongPath.m_Waypoints); + SerializeVector()(deserialize, "short path", m_ShortPath.m_Waypoints); + deserialize.NumberFixed_Unbounded("short target x", m_ShortTargetX); + deserialize.NumberFixed_Unbounded("short target z", m_ShortTargetZ); + SerializeGoal()(deserialize, "goal", m_FinalGoal); } virtual void HandleMessage(const CSimContext& UNUSED(context), const CMessage& msg, bool UNUSED(global)) diff --git a/source/simulation2/components/ICmpPathfinder.h b/source/simulation2/components/ICmpPathfinder.h index 6ef2a7a438..87e7e69285 100644 --- a/source/simulation2/components/ICmpPathfinder.h +++ b/source/simulation2/components/ICmpPathfinder.h @@ -46,7 +46,7 @@ class ICmpPathfinder : public IComponent public: struct Goal { - enum { + enum Type { POINT, CIRCLE, SQUARE diff --git a/source/simulation2/scripting/ScriptComponent.cpp b/source/simulation2/scripting/ScriptComponent.cpp index e896275e11..fe957a40a3 100644 --- a/source/simulation2/scripting/ScriptComponent.cpp +++ b/source/simulation2/scripting/ScriptComponent.cpp @@ -82,7 +82,7 @@ void CComponentTypeScript::Deserialize(const CSimContext& UNUSED(context), const // Use ScriptObjectAppend so we don't lose the carefully-constructed // prototype/parent of this object - deserialize.ScriptObjectAppend(m_Instance); + deserialize.ScriptObjectAppend("object", m_Instance); m_ScriptInterface.SetProperty(m_Instance, "entity", (int)ent, true); m_ScriptInterface.SetProperty(m_Instance, "template", paramNode, true); diff --git a/source/simulation2/serialization/IDeserializer.cpp b/source/simulation2/serialization/IDeserializer.cpp index 69e69f797d..891c9eb075 100644 --- a/source/simulation2/serialization/IDeserializer.cpp +++ b/source/simulation2/serialization/IDeserializer.cpp @@ -39,68 +39,68 @@ T IDeserializer::Number_(T lower, T upper) return value; } -void IDeserializer::NumberU8(uint8_t& out, uint8_t lower, uint8_t upper) +void IDeserializer::NumberU8(const char* UNUSED(name), uint8_t& out, uint8_t lower, uint8_t upper) { out = Number_(lower, upper); } -void IDeserializer::NumberI32(int32_t& out, int32_t lower, int32_t 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::NumberU32(uint32_t& out, uint32_t lower, uint32_t upper) +void IDeserializer::NumberU32(const char* UNUSED(name), uint32_t& out, uint32_t lower, uint32_t upper) { out = to_le32(Number_(lower, upper)); } -void IDeserializer::NumberU8_Unbounded(uint8_t& out) +void IDeserializer::NumberU8_Unbounded(const char* UNUSED(name), uint8_t& out) { Get((u8*)&out, sizeof(uint8_t)); } -void IDeserializer::NumberI32_Unbounded(int32_t& out) +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::NumberU32_Unbounded(uint32_t& out) +void IDeserializer::NumberU32_Unbounded(const char* UNUSED(name), uint32_t& out) { uint32_t value; Get((u8*)&value, sizeof(uint32_t)); out = to_le32(value); } -void IDeserializer::NumberFloat_Unbounded(float& out) +void IDeserializer::NumberFloat_Unbounded(const char* UNUSED(name), float& out) { Get((u8*)&out, sizeof(float)); } -void IDeserializer::NumberDouble_Unbounded(double& out) +void IDeserializer::NumberDouble_Unbounded(const char* UNUSED(name), double& out) { Get((u8*)&out, sizeof(double)); } -void IDeserializer::NumberFixed_Unbounded(fixed& out) +void IDeserializer::NumberFixed_Unbounded(const char* name, fixed& out) { int32_t n; - NumberI32_Unbounded(n); + NumberI32_Unbounded(name, n); out.SetInternalValue(n); } -void IDeserializer::Bool(bool& out) +void IDeserializer::Bool(const char* name, bool& out) { uint8_t i; - NumberU8(i, 0, 1); + NumberU8(name, i, 0, 1); out = (i != 0); } -void IDeserializer::StringASCII(std::string& out, uint32_t minlength, uint32_t maxlength) +void IDeserializer::StringASCII(const char* UNUSED(name), std::string& out, uint32_t minlength, uint32_t maxlength) { uint32_t len; - NumberU32(len, minlength, maxlength); + NumberU32("string length", len, minlength, maxlength); out.resize(len); // TODO: should check len <= bytes remaining in stream Get((u8*)out.data(), len); @@ -109,11 +109,11 @@ void IDeserializer::StringASCII(std::string& out, uint32_t minlength, uint32_t m throw PSERROR_Deserialize_InvalidCharInString(); } -void IDeserializer::String(std::wstring& out, uint32_t minlength, uint32_t maxlength) +void IDeserializer::String(const char* UNUSED(name), std::wstring& out, uint32_t minlength, uint32_t maxlength) { std::string str; uint32_t len; - NumberU32_Unbounded(len); + NumberU32_Unbounded("string length", len); str.resize(len); // TODO: should check len <= bytes remaining in stream Get((u8*)str.data(), len); @@ -126,7 +126,7 @@ void IDeserializer::String(std::wstring& out, uint32_t minlength, uint32_t maxle throw PSERROR_Deserialize_OutOfBounds(); } -void IDeserializer::RawBytes(u8* data, size_t len) +void IDeserializer::RawBytes(const char* UNUSED(name), u8* data, size_t len) { Get(data, len); } @@ -140,7 +140,7 @@ int IDeserializer::GetVersion() const void IDeserializer::ReadString(std::string& out) { uint32_t len; - NumberU32_Unbounded(len); + NumberU32_Unbounded("string length", len); out.resize(len); // TODO: should check len <= bytes remaining in stream Get((u8*)out.data(), len); } diff --git a/source/simulation2/serialization/IDeserializer.h b/source/simulation2/serialization/IDeserializer.h index 88f2b0f40a..aefecac682 100644 --- a/source/simulation2/serialization/IDeserializer.h +++ b/source/simulation2/serialization/IDeserializer.h @@ -36,29 +36,31 @@ class IDeserializer public: virtual ~IDeserializer(); - virtual void NumberU8(uint8_t& out, uint8_t lower, uint8_t upper); - virtual void NumberI32(int32_t& out, int32_t lower, int32_t upper); - virtual void NumberU32(uint32_t& out, uint32_t lower, uint32_t upper); - virtual void NumberU8_Unbounded(uint8_t& out); - virtual void NumberI32_Unbounded(int32_t& out); - virtual void NumberU32_Unbounded(uint32_t& out); - virtual void NumberFloat_Unbounded(float& out); - virtual void NumberDouble_Unbounded(double& out); - virtual void NumberFixed_Unbounded(fixed& out); - virtual void Bool(bool& out); - virtual void StringASCII(std::string& out, uint32_t minlength, uint32_t maxlength); - virtual void String(std::wstring& out, uint32_t minlength, uint32_t maxlength); + 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 NumberU32(const char* name, uint32_t& out, uint32_t lower, uint32_t upper); + virtual void NumberU8_Unbounded(const char* name, uint8_t& out); + virtual void NumberI32_Unbounded(const char* name, int32_t& out); + virtual void NumberU32_Unbounded(const char* name, uint32_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); + virtual void Bool(const char* name, bool& out); + virtual void StringASCII(const char* name, std::string& out, uint32_t minlength, uint32_t maxlength); + virtual void String(const char* name, std::wstring& out, uint32_t minlength, uint32_t maxlength); /// Deserialize a jsval, replacing 'out' - virtual void ScriptVal(jsval& out) = 0; + virtual void ScriptVal(const char* name, jsval& out) = 0; + virtual void ScriptVal(const char* name, CScriptVal& out) = 0; + virtual void ScriptVal(const char* name, CScriptValRooted& out) = 0; /// Deserialize an object jsval, appending properties to object 'obj' - virtual void ScriptObjectAppend(jsval& obj) = 0; + virtual void ScriptObjectAppend(const char* name, jsval& obj) = 0; /// Deserialize a JSString - virtual void ScriptString(JSString*& out) = 0; + virtual void ScriptString(const char* name, JSString*& out) = 0; - virtual void RawBytes(u8* data, size_t len); + virtual void RawBytes(const char* name, u8* data, size_t len); // Features for simulation-state serialisation: virtual int GetVersion() const; diff --git a/source/simulation2/serialization/SerializeTemplates.h b/source/simulation2/serialization/SerializeTemplates.h new file mode 100644 index 0000000000..45767918eb --- /dev/null +++ b/source/simulation2/serialization/SerializeTemplates.h @@ -0,0 +1,96 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +/** + * @file + * Helper templates for serializing/deserializing common objects. + */ + +template +struct SerializeVector +{ + template + void operator()(ISerializer& serialize, const char* name, std::vector& value) + { + size_t len = value.size(); + serialize.NumberU32_Unbounded("length", (u32)len); + for (size_t i = 0; i < len; ++i) + ELEM()(serialize, name, value[i]); + } + + template + void operator()(IDeserializer& deserialize, const char* name, std::vector& value) + { + u32 len; + deserialize.NumberU32_Unbounded("length", len); + value.reserve(len); // TODO: watch out for out-of-memory + for (size_t i = 0; i < len; ++i) + { + T el; + ELEM()(deserialize, name, el); + value.push_back(el); + } + } +}; + +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); + } +}; + +template +struct SerializeU8_Enum +{ + void operator()(ISerializer& serialize, const char* name, T value) + { + serialize.NumberU8(name, value, 0, max); + } + + void operator()(IDeserializer& deserialize, const char* name, T& value) + { + u8 val; + deserialize.NumberU8(name, val, 0, max); + value = (T)val; + } +}; + +struct SerializeGoal +{ + template + void operator()(S& serialize, const char* UNUSED(name), ICmpPathfinder::Goal& value) + { + SerializeU8_Enum()(serialize, "type", value.type); + serialize.NumberFixed_Unbounded("goal x", value.x); + serialize.NumberFixed_Unbounded("goal z", value.z); + serialize.NumberFixed_Unbounded("goal u x", value.u.X); + serialize.NumberFixed_Unbounded("goal u z", value.u.Y); + serialize.NumberFixed_Unbounded("goal v x", value.v.X); + serialize.NumberFixed_Unbounded("goal v z", value.v.Y); + serialize.NumberFixed_Unbounded("goal hw", value.hw); + serialize.NumberFixed_Unbounded("goal hh", value.hh); + } +}; diff --git a/source/simulation2/serialization/StdDeserializer.cpp b/source/simulation2/serialization/StdDeserializer.cpp index 64a19447b5..43b2844130 100644 --- a/source/simulation2/serialization/StdDeserializer.cpp +++ b/source/simulation2/serialization/StdDeserializer.cpp @@ -76,7 +76,7 @@ jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent) JSContext* cx = m_ScriptInterface.GetContext(); uint8_t type; - NumberU8_Unbounded(type); + NumberU8_Unbounded("type", type); switch (type) { case SCRIPT_TYPE_VOID: @@ -103,7 +103,7 @@ jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent) AddScriptBackref(obj); uint32_t numProps; - NumberU32_Unbounded(numProps); + NumberU32_Unbounded("num props", numProps); for (uint32_t i = 0; i < numProps; ++i) { @@ -122,19 +122,19 @@ jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent) case SCRIPT_TYPE_STRING: { JSString* str; - ScriptString(str); + ScriptString("string", str); return STRING_TO_JSVAL(str); } case SCRIPT_TYPE_INT: { int32_t value; - NumberI32(value, JSVAL_INT_MIN, JSVAL_INT_MAX); + NumberI32("value", value, JSVAL_INT_MIN, JSVAL_INT_MAX); return INT_TO_JSVAL(value); } case SCRIPT_TYPE_DOUBLE: { double value; - NumberDouble_Unbounded(value); + NumberDouble_Unbounded("value", value); jsval rval; if (!JS_NewNumberValue(cx, value, &rval)) throw PSERROR_Deserialize_ScriptError("JS_NewNumberValue failed"); @@ -143,13 +143,13 @@ jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent) case SCRIPT_TYPE_BOOLEAN: { uint8_t value; - NumberU8(value, 0, 1); + NumberU8("value", value, 0, 1); return BOOLEAN_TO_JSVAL(value ? JS_TRUE : JS_FALSE); } case SCRIPT_TYPE_BACKREF: { u32 tag; - NumberU32_Unbounded(tag); + NumberU32_Unbounded("tag", tag); JSObject* obj = GetScriptBackref(tag); if (!obj) throw PSERROR_Deserialize_ScriptError("Invalid backref tag"); @@ -163,12 +163,12 @@ jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent) void CStdDeserializer::ReadStringUTF16(utf16string& str) { uint32_t len; - NumberU32_Unbounded(len); + NumberU32_Unbounded("string length", len); str.resize(len); // TODO: should check len*2 <= bytes remaining in stream, before resizing Get((u8*)str.data(), len*2); } -void CStdDeserializer::ScriptString(JSString*& out) +void CStdDeserializer::ScriptString(const char* UNUSED(name), JSString*& out) { utf16string str; ReadStringUTF16(str); @@ -182,22 +182,22 @@ void CStdDeserializer::ScriptString(JSString*& out) throw PSERROR_Deserialize_ScriptError("JS_NewUCStringCopyN failed"); } -void CStdDeserializer::ScriptVal(jsval& out) +void CStdDeserializer::ScriptVal(const char* UNUSED(name), jsval& out) { out = ReadScriptVal(NULL); } -void CStdDeserializer::ScriptVal(CScriptVal& out) +void CStdDeserializer::ScriptVal(const char* UNUSED(name), CScriptVal& out) { out = ReadScriptVal(NULL); } -void CStdDeserializer::ScriptVal(CScriptValRooted& out) +void CStdDeserializer::ScriptVal(const char* UNUSED(name), CScriptValRooted& out) { out = CScriptValRooted(m_ScriptInterface.GetContext(), ReadScriptVal(NULL)); } -void CStdDeserializer::ScriptObjectAppend(jsval& obj) +void CStdDeserializer::ScriptObjectAppend(const char* UNUSED(name), jsval& obj) { if (!JSVAL_IS_OBJECT(obj)) throw PSERROR_Deserialize_ScriptError(); diff --git a/source/simulation2/serialization/StdDeserializer.h b/source/simulation2/serialization/StdDeserializer.h index e85f116904..979f296fca 100644 --- a/source/simulation2/serialization/StdDeserializer.h +++ b/source/simulation2/serialization/StdDeserializer.h @@ -31,11 +31,11 @@ public: CStdDeserializer(ScriptInterface& scriptInterface, std::istream& stream); virtual ~CStdDeserializer(); - virtual void ScriptVal(jsval& out); - virtual void ScriptVal(CScriptVal& out); - virtual void ScriptVal(CScriptValRooted& out); - virtual void ScriptObjectAppend(jsval& obj); - virtual void ScriptString(JSString*& out); + virtual void ScriptVal(const char* name, jsval& out); + virtual void ScriptVal(const char* name, CScriptVal& out); + virtual void ScriptVal(const char* name, CScriptValRooted& out); + virtual void ScriptObjectAppend(const char* name, jsval& obj); + virtual void ScriptString(const char* name, JSString*& out); protected: virtual void Get(u8* data, size_t len); diff --git a/source/simulation2/system/ComponentManagerSerialization.cpp b/source/simulation2/system/ComponentManagerSerialization.cpp index 85273cc594..0a7c45449d 100644 --- a/source/simulation2/system/ComponentManagerSerialization.cpp +++ b/source/simulation2/system/ComponentManagerSerialization.cpp @@ -236,13 +236,13 @@ bool CComponentManager::DeserializeState(std::istream& stream) ResetState(); std::string rng; - deserializer.StringASCII(rng, 0, 32); + deserializer.StringASCII("rng", rng, 0, 32); DeserializeRNG(rng, m_RNG); - deserializer.NumberU32_Unbounded(m_NextEntityId); // TODO: use sensible bounds + deserializer.NumberU32_Unbounded("next entity id", m_NextEntityId); // TODO: use sensible bounds uint32_t numComponentTypes; - deserializer.NumberU32_Unbounded(numComponentTypes); + deserializer.NumberU32_Unbounded("num component types", numComponentTypes); ICmpTemplateManager* templateManager = NULL; CParamNode noParam; @@ -250,7 +250,7 @@ bool CComponentManager::DeserializeState(std::istream& stream) for (size_t i = 0; i < numComponentTypes; ++i) { std::string ctname; - deserializer.StringASCII(ctname, 0, 255); + deserializer.StringASCII("name", ctname, 0, 255); ComponentTypeId ctid = LookupCID(ctname); if (ctid == CID__Invalid) @@ -260,12 +260,12 @@ bool CComponentManager::DeserializeState(std::istream& stream) } uint32_t numComponents; - deserializer.NumberU32_Unbounded(numComponents); + deserializer.NumberU32_Unbounded("num components", numComponents); for (size_t j = 0; j < numComponents; ++j) { entity_id_t ent; - deserializer.NumberU32_Unbounded(ent); + deserializer.NumberU32_Unbounded("entity id", ent); IComponent* component = ConstructComponent(ent, ctid); if (!component) return false; diff --git a/source/simulation2/system/Message.h b/source/simulation2/system/Message.h index 12e81712db..4a18fde615 100644 --- a/source/simulation2/system/Message.h +++ b/source/simulation2/system/Message.h @@ -19,6 +19,7 @@ #define INCLUDED_MESSAGE #include "scriptinterface/ScriptTypes.h" +#include "scriptinterface/ScriptVal.h" class CMessage { @@ -31,6 +32,9 @@ public: virtual const char* GetScriptHandlerName() const = 0; virtual const char* GetScriptGlobalHandlerName() const = 0; virtual jsval ToJSVal(ScriptInterface&) const = 0; + jsval ToJSValCached(ScriptInterface&) const; +private: + mutable CScriptValRooted m_Cached; }; // TODO: GetType could be replaced with a plain member variable to avoid some // virtual calls, if that turns out to be worthwhile diff --git a/source/simulation2/tests/test_Serializer.h b/source/simulation2/tests/test_Serializer.h index f2a800d304..96c2d58ae3 100644 --- a/source/simulation2/tests/test_Serializer.h +++ b/source/simulation2/tests/test_Serializer.h @@ -153,11 +153,11 @@ public: CStdDeserializer deserialize(script, stream); int32_t n; - deserialize.NumberI32_Unbounded(n); + deserialize.NumberI32_Unbounded("x", n); TS_ASSERT_EQUALS(n, -123); - deserialize.NumberI32_Unbounded(n); + deserialize.NumberI32_Unbounded("y", n); TS_ASSERT_EQUALS(n, 1234); - deserialize.NumberI32(n, 0, 65535); + deserialize.NumberI32("z", n, 0, 65535); TS_ASSERT_EQUALS(n, 12345); TS_ASSERT(stream.good()); @@ -184,37 +184,37 @@ public: std::wstring wstr; u8 cbuf[256]; - deserialize.NumberU8_Unbounded(u8v); + deserialize.NumberU8_Unbounded("u8", u8v); TS_ASSERT_EQUALS(u8v, 255); - deserialize.NumberI32_Unbounded(i32v); + deserialize.NumberI32_Unbounded("i32", i32v); TS_ASSERT_EQUALS(i32v, -123); - deserialize.NumberU32_Unbounded(u32v); + deserialize.NumberU32_Unbounded("u32", u32v); TS_ASSERT_EQUALS(u32v, 4294967173u); - deserialize.NumberFloat_Unbounded(flt); + deserialize.NumberFloat_Unbounded("float", flt); TS_ASSERT_EQUALS(flt, 1e+30f); - deserialize.NumberDouble_Unbounded(dbl); + deserialize.NumberDouble_Unbounded("double", dbl); TS_ASSERT_EQUALS(dbl, 1e+300); - deserialize.NumberFixed_Unbounded(fxd); + deserialize.NumberFixed_Unbounded("fixed", fxd); TS_ASSERT_EQUALS(fxd.ToDouble(), 1234.5); - deserialize.Bool(bl); + deserialize.Bool("t", bl); TS_ASSERT_EQUALS(bl, true); - deserialize.Bool(bl); + deserialize.Bool("f", bl); TS_ASSERT_EQUALS(bl, false); - deserialize.StringASCII(str, 0, 255); + deserialize.StringASCII("string", str, 0, 255); TS_ASSERT_STR_EQUALS(str, "example"); - deserialize.StringASCII(str, 0, 255); + deserialize.StringASCII("string 2", str, 0, 255); TS_ASSERT_STR_EQUALS(str, "example\"\\\""); - deserialize.StringASCII(str, 0, 255); + deserialize.StringASCII("string 3", str, 0, 255); TS_ASSERT_STR_EQUALS(str, "example\n\ntest"); wchar_t testw[] = { 't', 0xEA, 's', 't', 0 }; - deserialize.String(wstr, 0, 255); + deserialize.String("string 4", wstr, 0, 255); TS_ASSERT_WSTR_EQUALS(wstr, testw); cbuf[6] = 0x42; // sentinel - deserialize.RawBytes(cbuf, 6); + deserialize.RawBytes("raw bytes", cbuf, 6); TS_ASSERT_SAME_DATA(cbuf, (const u8*)"\0\1\2\3\x0f\x10\x42", 7); TS_ASSERT(stream.good()); @@ -297,7 +297,7 @@ public: CStdDeserializer deserialize(script, stream); jsval newobj; - deserialize.ScriptVal(newobj); + deserialize.ScriptVal("script", newobj); TS_ASSERT(stream.good()); TS_ASSERT_EQUALS(stream.peek(), EOF); @@ -326,7 +326,7 @@ public: CStdDeserializer deserialize(script, stream); jsval newobj; - deserialize.ScriptVal(newobj); + deserialize.ScriptVal("script", newobj); TSM_ASSERT(msg, stream.good()); TSM_ASSERT_EQUALS(msg, stream.peek(), EOF); @@ -436,7 +436,7 @@ public: CStdDeserializer deserialize(script, stream); jsval newobj; - deserialize.ScriptVal(newobj); + deserialize.ScriptVal("script", newobj); TS_ASSERT(stream.good()); TS_ASSERT_EQUALS(stream.peek(), EOF);