Add initial serialization test mode.
Reduce some dependencies on CSimulation2 to provide more flexibility. This was SVN commit r10426.
This commit is contained in:
parent
0f3119e36a
commit
29e4f633f1
@ -62,7 +62,7 @@ CMapReader::CMapReader()
|
||||
void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_,
|
||||
WaterManager* pWaterMan_, SkyManager* pSkyMan_,
|
||||
CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_,
|
||||
CSimulation2 *pSimulation2_, int playerID_)
|
||||
CSimulation2 *pSimulation2_, const CSimContext* pSimContext_, int playerID_, bool skipEntities)
|
||||
{
|
||||
// latch parameters (held until DelayedLoadFinished)
|
||||
pTerrain = pTerrain_;
|
||||
@ -73,7 +73,9 @@ void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_,
|
||||
pCinema = pCinema_;
|
||||
pTrigMan = pTrigMan_;
|
||||
pSimulation2 = pSimulation2_;
|
||||
pSimContext = pSimContext_;
|
||||
m_PlayerID = playerID_;
|
||||
m_SkipEntities = skipEntities;
|
||||
m_StartingCameraTarget = INVALID_ENTITY;
|
||||
|
||||
filename_xml = pathname.ChangeExtension(L".xml");
|
||||
@ -144,7 +146,9 @@ void CMapReader::LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted&
|
||||
pCinema = pCinema_;
|
||||
pTrigMan = pTrigMan_;
|
||||
pSimulation2 = pSimulation2_;
|
||||
pSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL;
|
||||
m_PlayerID = playerID_;
|
||||
m_SkipEntities = false;
|
||||
m_StartingCameraTarget = INVALID_ENTITY;
|
||||
|
||||
// delete all existing entities
|
||||
@ -282,7 +286,7 @@ int CMapReader::ApplyData()
|
||||
if (pLightEnv)
|
||||
*pLightEnv = m_LightEnv;
|
||||
|
||||
CmpPtr<ICmpPlayerManager> cmpPlayerManager(*pSimulation2, SYSTEM_ENTITY);
|
||||
CmpPtr<ICmpPlayerManager> cmpPlayerManager(*pSimContext, SYSTEM_ENTITY);
|
||||
|
||||
if (pGameView && !cmpPlayerManager.null())
|
||||
{
|
||||
@ -290,7 +294,7 @@ int CMapReader::ApplyData()
|
||||
pGameView->ResetCameraTarget(pGameView->GetCamera()->GetFocus());
|
||||
|
||||
// TODO: Starting rotation?
|
||||
CmpPtr<ICmpPlayer> cmpPlayer(*pSimulation2, cmpPlayerManager->GetPlayerByID(m_PlayerID));
|
||||
CmpPtr<ICmpPlayer> cmpPlayer(*pSimContext, cmpPlayerManager->GetPlayerByID(m_PlayerID));
|
||||
if (!cmpPlayer.null() && cmpPlayer->HasStartingCamera())
|
||||
{
|
||||
// Use player starting camera
|
||||
@ -300,7 +304,7 @@ int CMapReader::ApplyData()
|
||||
else if (m_StartingCameraTarget != INVALID_ENTITY)
|
||||
{
|
||||
// Point camera at entity
|
||||
CmpPtr<ICmpPosition> cmpPosition(*pSimulation2, m_StartingCameraTarget);
|
||||
CmpPtr<ICmpPosition> cmpPosition(*pSimContext, m_StartingCameraTarget);
|
||||
if (!cmpPosition.null())
|
||||
{
|
||||
CFixedVector3D pos = cmpPosition->GetPosition();
|
||||
@ -309,7 +313,7 @@ int CMapReader::ApplyData()
|
||||
}
|
||||
}
|
||||
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(*pSimulation2, SYSTEM_ENTITY);
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(*pSimContext, SYSTEM_ENTITY);
|
||||
if (!cmpTerrain.null())
|
||||
cmpTerrain->ReloadTerrain();
|
||||
|
||||
@ -630,7 +634,7 @@ void CXMLReader::ReadEnvironment(XMBElement parent)
|
||||
int element_name = waterelement.GetNodeName();
|
||||
if (element_name == el_height)
|
||||
{
|
||||
CmpPtr<ICmpWaterManager> cmpWaterMan(*m_MapReader.pSimulation2, SYSTEM_ENTITY);
|
||||
CmpPtr<ICmpWaterManager> cmpWaterMan(*m_MapReader.pSimContext, SYSTEM_ENTITY);
|
||||
ENSURE(!cmpWaterMan.null());
|
||||
cmpWaterMan->SetWaterLevel(entity_pos_t::FromString(waterelement.GetText()));
|
||||
continue;
|
||||
@ -859,6 +863,7 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
|
||||
{
|
||||
XMBElementList entities = parent.GetChildNodes();
|
||||
|
||||
ENSURE(m_MapReader.pSimulation2);
|
||||
CSimulation2& sim = *m_MapReader.pSimulation2;
|
||||
CmpPtr<ICmpPlayerManager> cmpPlayerManager(sim, SYSTEM_ENTITY);
|
||||
|
||||
@ -981,9 +986,12 @@ int CXMLReader::ProgressiveRead()
|
||||
}
|
||||
else if (name == "Entities")
|
||||
{
|
||||
ret = ReadEntities(node, end_time);
|
||||
if (ret != 0) // error or timed out
|
||||
return ret;
|
||||
if (!m_MapReader.m_SkipEntities)
|
||||
{
|
||||
ret = ReadEntities(node, end_time);
|
||||
if (ret != 0) // error or timed out
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else if (name == "Paths")
|
||||
{
|
||||
@ -995,7 +1003,8 @@ int CXMLReader::ProgressiveRead()
|
||||
}
|
||||
else if (name == "Script")
|
||||
{
|
||||
m_MapReader.pSimulation2->SetStartupScript(node.GetText().FromUTF8());
|
||||
if (m_MapReader.pSimulation2)
|
||||
m_MapReader.pSimulation2->SetStartupScript(node.GetText().FromUTF8());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1021,7 +1030,8 @@ int CMapReader::LoadScriptSettings()
|
||||
xml_reader = new CXMLReader(filename_xml, *this);
|
||||
|
||||
// parse the script settings
|
||||
pSimulation2->SetMapSettings(xml_reader->ReadScriptSettings());
|
||||
if (pSimulation2)
|
||||
pSimulation2->SetMapSettings(xml_reader->ReadScriptSettings());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1029,14 +1039,16 @@ int CMapReader::LoadScriptSettings()
|
||||
// load player settings script
|
||||
int CMapReader::LoadPlayerSettings()
|
||||
{
|
||||
pSimulation2->LoadPlayerSettings(true);
|
||||
if (pSimulation2)
|
||||
pSimulation2->LoadPlayerSettings(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// load map settings script
|
||||
int CMapReader::LoadMapSettings()
|
||||
{
|
||||
pSimulation2->LoadMapSettings();
|
||||
if (pSimulation2)
|
||||
pSimulation2->LoadMapSettings();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ class CLightEnv;
|
||||
class CCinemaManager;
|
||||
class CTriggerManager;
|
||||
class CSimulation2;
|
||||
class CSimContext;
|
||||
class CTerrainTextureEntry;
|
||||
class CScriptValRooted;
|
||||
class ScriptInterface;
|
||||
@ -52,7 +53,7 @@ public:
|
||||
|
||||
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
|
||||
void LoadMap(const VfsPath& pathname, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*,
|
||||
CCinemaManager*, CTriggerManager*, CSimulation2*, int playerID);
|
||||
CCinemaManager*, CTriggerManager*, CSimulation2*, const CSimContext*, int playerID, bool skipEntities);
|
||||
|
||||
void LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*,
|
||||
CCinemaManager*, CTriggerManager*, CSimulation2*, int playerID);
|
||||
@ -133,7 +134,9 @@ private:
|
||||
CCinemaManager* pCinema;
|
||||
CTriggerManager* pTrigMan;
|
||||
CSimulation2* pSimulation2;
|
||||
const CSimContext* pSimContext;
|
||||
int m_PlayerID;
|
||||
bool m_SkipEntities;
|
||||
VfsPath filename_xml;
|
||||
bool only_xml;
|
||||
u32 file_format_version;
|
||||
|
@ -84,9 +84,6 @@ CGame::CGame(bool disableGraphics):
|
||||
**/
|
||||
CGame::~CGame()
|
||||
{
|
||||
// Clear rooted value before destroying its context
|
||||
m_RegisteredAttribs = CScriptValRooted();
|
||||
|
||||
// Again, the in-game call tree is going to be different to the main menu one.
|
||||
if (CProfileManager::IsInitialised())
|
||||
g_Profiler.StructuralReset();
|
||||
@ -117,7 +114,7 @@ void CGame::SetTurnManager(CNetTurnManager* turnManager)
|
||||
**/
|
||||
void CGame::RegisterInit(const CScriptValRooted& attribs)
|
||||
{
|
||||
m_RegisteredAttribs = attribs; // save the attributes for ReallyStartGame
|
||||
m_Simulation2->SetInitAttributes(attribs);
|
||||
|
||||
std::string mapType;
|
||||
m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "mapType", mapType);
|
||||
@ -165,7 +162,7 @@ void CGame::RegisterInit(const CScriptValRooted& attribs)
|
||||
PSRETURN CGame::ReallyStartGame()
|
||||
{
|
||||
CScriptVal settings;
|
||||
m_Simulation2->GetScriptInterface().GetProperty(m_RegisteredAttribs.get(), "settings", settings);
|
||||
m_Simulation2->GetScriptInterface().GetProperty(m_Simulation2->GetInitAttributes().get(), "settings", settings);
|
||||
m_Simulation2->InitGame(settings);
|
||||
|
||||
// Call the reallyStartGame GUI function, but only if it exists
|
||||
|
@ -162,7 +162,6 @@ public:
|
||||
private:
|
||||
void RegisterInit(const CScriptValRooted& attribs);
|
||||
IReplayLogger* m_ReplayLogger;
|
||||
CScriptValRooted m_RegisteredAttribs;
|
||||
|
||||
std::vector<CColor> m_PlayerColours;
|
||||
};
|
||||
|
@ -82,7 +82,7 @@ void CWorld::RegisterInit(const CStrW& mapFile, int playerID)
|
||||
CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL,
|
||||
&g_LightEnv, m_pGame->GetView(),
|
||||
m_pGame->GetView() ? m_pGame->GetView()->GetCinema() : NULL,
|
||||
pTriggerManager, m_pGame->GetSimulation2(), playerID);
|
||||
pTriggerManager, m_pGame->GetSimulation2(), &m_pGame->GetSimulation2()->GetSimContext(), playerID, false);
|
||||
// fails immediately, or registers for delay loading
|
||||
}
|
||||
catch (PSERROR_File& err)
|
||||
|
@ -27,11 +27,14 @@
|
||||
#include "simulation2/components/ICmpCommandQueue.h"
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
|
||||
#include "graphics/MapReader.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/file/vfs/vfs_util.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/Pyrogenesis.h"
|
||||
#include "ps/XML/Xeromyces.h"
|
||||
@ -43,11 +46,21 @@
|
||||
#define getpid _getpid // use the non-deprecated function name
|
||||
#endif
|
||||
|
||||
static std::string Hexify(const std::string& s) // TODO: shouldn't duplicate this function in so many places
|
||||
{
|
||||
std::stringstream str;
|
||||
str << std::hex;
|
||||
for (size_t i = 0; i < s.size(); ++i)
|
||||
str << std::setfill('0') << std::setw(2) << (int)(unsigned char)s[i];
|
||||
return str.str();
|
||||
}
|
||||
|
||||
class CSimulation2Impl
|
||||
{
|
||||
public:
|
||||
CSimulation2Impl(CUnitManager* unitManager, CTerrain* terrain) :
|
||||
m_SimContext(), m_ComponentManager(m_SimContext), m_EnableOOSLog(false)
|
||||
m_SimContext(), m_ComponentManager(m_SimContext),
|
||||
m_EnableOOSLog(false), m_EnableSerializationTest(false)
|
||||
{
|
||||
m_SimContext.m_UnitManager = unitManager;
|
||||
m_SimContext.m_Terrain = terrain;
|
||||
@ -56,6 +69,7 @@ public:
|
||||
RegisterFileReloadFunc(ReloadChangedFileCB, this);
|
||||
|
||||
// m_EnableOOSLog = true; // TODO: this should be a command-line flag or similar
|
||||
// m_EnableSerializationTest = true; // TODO: this should too
|
||||
}
|
||||
|
||||
~CSimulation2Impl()
|
||||
@ -65,41 +79,45 @@ public:
|
||||
|
||||
void ResetState(bool skipScriptedComponents, bool skipAI)
|
||||
{
|
||||
m_ComponentManager.ResetState();
|
||||
|
||||
m_DeltaTime = 0.0;
|
||||
m_LastFrameOffset = 0.0f;
|
||||
m_TurnNumber = 0;
|
||||
ResetComponentState(m_ComponentManager, skipScriptedComponents, skipAI);
|
||||
}
|
||||
|
||||
static void ResetComponentState(CComponentManager& componentManager, bool skipScriptedComponents, bool skipAI)
|
||||
{
|
||||
componentManager.ResetState();
|
||||
|
||||
CParamNode noParam;
|
||||
CComponentManager::ComponentTypeId cid;
|
||||
|
||||
// Add native system components:
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_TemplateManager, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_TemplateManager, noParam);
|
||||
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_CommandQueue, noParam);
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_ObstructionManager, noParam);
|
||||
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);
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_Terrain, noParam);
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_TerritoryManager, noParam);
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_WaterManager, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_CommandQueue, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_ObstructionManager, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_Pathfinder, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_ProjectileManager, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_RangeManager, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_SoundManager, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_Terrain, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_TerritoryManager, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_WaterManager, noParam);
|
||||
|
||||
if (!skipAI)
|
||||
{
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_AIManager, noParam);
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, CID_AIManager, noParam);
|
||||
}
|
||||
|
||||
// Add scripted system components:
|
||||
if (!skipScriptedComponents)
|
||||
{
|
||||
#define LOAD_SCRIPTED_COMPONENT(name) \
|
||||
cid = m_ComponentManager.LookupCID(name); \
|
||||
cid = componentManager.LookupCID(name); \
|
||||
if (cid == CID__Invalid) \
|
||||
LOGERROR(L"Can't find component type " L##name); \
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, cid, noParam)
|
||||
componentManager.AddComponent(SYSTEM_ENTITY, cid, noParam)
|
||||
|
||||
LOAD_SCRIPTED_COMPONENT("AIInterface");
|
||||
LOAD_SCRIPTED_COMPONENT("EndGameManager");
|
||||
@ -111,7 +129,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadScripts(const VfsPath& path);
|
||||
static bool LoadDefaultScripts(CComponentManager& componentManager, std::set<VfsPath>* loadedScripts);
|
||||
static bool LoadScripts(CComponentManager& componentManager, std::set<VfsPath>* loadedScripts, const VfsPath& path);
|
||||
Status ReloadChangedFile(const VfsPath& path);
|
||||
|
||||
static Status ReloadChangedFileCB(void* param, const VfsPath& path)
|
||||
@ -120,7 +139,8 @@ public:
|
||||
}
|
||||
|
||||
int ProgressiveLoad();
|
||||
bool Update(int turnLength, const std::vector<SimulationCommand>& commands);
|
||||
void Update(int turnLength, const std::vector<SimulationCommand>& commands);
|
||||
static void UpdateComponents(CSimContext& simContext, fixed turnLengthFixed, const std::vector<SimulationCommand>& commands);
|
||||
void Interpolate(float frameLength, float frameOffset);
|
||||
|
||||
void DumpState();
|
||||
@ -131,6 +151,7 @@ public:
|
||||
float m_LastFrameOffset;
|
||||
|
||||
std::wstring m_StartupScript;
|
||||
CScriptValRooted m_InitAttributes;
|
||||
CScriptValRooted m_MapSettings;
|
||||
|
||||
std::set<VfsPath> m_LoadedScripts;
|
||||
@ -138,9 +159,48 @@ public:
|
||||
uint32_t m_TurnNumber;
|
||||
|
||||
bool m_EnableOOSLog;
|
||||
|
||||
|
||||
// Functions and data for the serialization test mode: (see Update() for relevant comments)
|
||||
|
||||
bool m_EnableSerializationTest;
|
||||
|
||||
struct SerializationTestState
|
||||
{
|
||||
std::stringstream state;
|
||||
std::stringstream debug;
|
||||
std::string hash;
|
||||
};
|
||||
|
||||
void DumpSerializationTestState(SerializationTestState& state, const OsPath& path, const OsPath::String& suffix);
|
||||
|
||||
void ReportSerializationFailure(
|
||||
SerializationTestState* primaryStateBefore, SerializationTestState* primaryStateAfter,
|
||||
SerializationTestState* secondaryStateBefore, SerializationTestState* secondaryStateAfter);
|
||||
|
||||
static std::vector<SimulationCommand> CloneCommandsFromOtherContext(ScriptInterface& oldScript, ScriptInterface& newScript,
|
||||
const std::vector<SimulationCommand>& commands)
|
||||
{
|
||||
std::vector<SimulationCommand> newCommands = commands;
|
||||
for (size_t i = 0; i < newCommands.size(); ++i)
|
||||
{
|
||||
newCommands[i].data = CScriptValRooted(newScript.GetContext(),
|
||||
newScript.CloneValueFromOtherContext(oldScript, newCommands[i].data.get()));
|
||||
}
|
||||
return newCommands;
|
||||
}
|
||||
};
|
||||
|
||||
bool CSimulation2Impl::LoadScripts(const VfsPath& path)
|
||||
bool CSimulation2Impl::LoadDefaultScripts(CComponentManager& componentManager, std::set<VfsPath>* loadedScripts)
|
||||
{
|
||||
return (
|
||||
LoadScripts(componentManager, loadedScripts, "simulation/components/interfaces/") &&
|
||||
LoadScripts(componentManager, loadedScripts, L"simulation/helpers/") &&
|
||||
LoadScripts(componentManager, loadedScripts, L"simulation/components/")
|
||||
);
|
||||
}
|
||||
|
||||
bool CSimulation2Impl::LoadScripts(CComponentManager& componentManager, std::set<VfsPath>* loadedScripts, const VfsPath& path)
|
||||
{
|
||||
VfsPaths pathnames;
|
||||
if (vfs::GetPathnames(g_VFS, path, L"*.js", pathnames) < 0)
|
||||
@ -150,9 +210,10 @@ bool CSimulation2Impl::LoadScripts(const VfsPath& path)
|
||||
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
|
||||
{
|
||||
VfsPath filename = *it;
|
||||
m_LoadedScripts.insert(filename);
|
||||
if (loadedScripts)
|
||||
loadedScripts->insert(filename);
|
||||
LOGMESSAGE(L"Loading simulation script '%ls'", filename.string().c_str());
|
||||
if (! m_ComponentManager.LoadScript(filename))
|
||||
if (! componentManager.LoadScript(filename))
|
||||
ok = false;
|
||||
}
|
||||
return ok;
|
||||
@ -207,63 +268,167 @@ int CSimulation2Impl::ProgressiveLoad()
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CSimulation2Impl::Update(int turnLength, const std::vector<SimulationCommand>& commands)
|
||||
void CSimulation2Impl::DumpSerializationTestState(SerializationTestState& state, const OsPath& path, const OsPath::String& suffix)
|
||||
{
|
||||
if (!state.hash.empty())
|
||||
{
|
||||
std::ofstream file (OsString(path / (L"hash." + suffix)).c_str(), std::ofstream::out | std::ofstream::trunc);
|
||||
file << Hexify(state.hash);
|
||||
}
|
||||
|
||||
if (!state.debug.str().empty())
|
||||
{
|
||||
std::ofstream file (OsString(path / (L"debug." + suffix)).c_str(), std::ofstream::out | std::ofstream::trunc);
|
||||
file << state.debug.str();
|
||||
}
|
||||
|
||||
if (!state.state.str().empty())
|
||||
{
|
||||
std::ofstream file (OsString(path / (L"state." + suffix)).c_str(), std::ofstream::out | std::ofstream::trunc | std::ofstream::binary);
|
||||
file << state.state.str();
|
||||
}
|
||||
}
|
||||
|
||||
void CSimulation2Impl::ReportSerializationFailure(
|
||||
SerializationTestState* primaryStateBefore, SerializationTestState* primaryStateAfter,
|
||||
SerializationTestState* secondaryStateBefore, SerializationTestState* secondaryStateAfter)
|
||||
{
|
||||
OsPath path = psLogDir() / "oos_log";
|
||||
CreateDirectories(path, 0700);
|
||||
|
||||
// Clean up obsolete files from previous runs
|
||||
wunlink(path / "hash.before.a");
|
||||
wunlink(path / "hash.before.b");
|
||||
wunlink(path / "debug.before.a");
|
||||
wunlink(path / "debug.before.b");
|
||||
wunlink(path / "state.before.a");
|
||||
wunlink(path / "state.before.b");
|
||||
wunlink(path / "hash.after.a");
|
||||
wunlink(path / "hash.after.b");
|
||||
wunlink(path / "debug.after.a");
|
||||
wunlink(path / "debug.after.b");
|
||||
wunlink(path / "state.after.a");
|
||||
wunlink(path / "state.after.b");
|
||||
|
||||
if (primaryStateBefore)
|
||||
DumpSerializationTestState(*primaryStateBefore, path, L"before.a");
|
||||
if (primaryStateAfter)
|
||||
DumpSerializationTestState(*primaryStateAfter, path, L"after.a");
|
||||
if (secondaryStateBefore)
|
||||
DumpSerializationTestState(*secondaryStateBefore, path, L"before.b");
|
||||
if (secondaryStateAfter)
|
||||
DumpSerializationTestState(*secondaryStateAfter, path, L"after.b");
|
||||
|
||||
debug_warn(L"Serialization test failure");
|
||||
}
|
||||
|
||||
void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationCommand>& commands)
|
||||
{
|
||||
fixed turnLengthFixed = fixed::FromInt(turnLength) / 1000;
|
||||
|
||||
// TODO: the update process is pretty ugly, with lots of messages and dependencies
|
||||
// between different components. Ought to work out a nicer way to do this.
|
||||
/*
|
||||
* In serialization test mode, we save the original (primary) simulation state before each turn update.
|
||||
* We run the update, then load the saved state into a secondary context.
|
||||
* We serialize that again and compare to the original serialization (to check that
|
||||
* serialize->deserialize->serialize is equivalent to serialize).
|
||||
* Then we run the update on the secondary context, and check that its new serialized
|
||||
* state matches the primary context after the update (to check that the simulation doesn't depend
|
||||
* on anything that's not serialized).
|
||||
*/
|
||||
|
||||
CMessageTurnStart msgTurnStart;
|
||||
m_ComponentManager.BroadcastMessage(msgTurnStart);
|
||||
const bool serializationTestDebugDump = false; // set true to save human-readable state dumps, for debugging (but slow)
|
||||
const bool serializationTestHash = true; // set true to save and compare hash of state
|
||||
|
||||
CmpPtr<ICmpPathfinder> cmpPathfinder(m_SimContext, SYSTEM_ENTITY);
|
||||
if (!cmpPathfinder.null())
|
||||
cmpPathfinder->FinishAsyncRequests();
|
||||
|
||||
// Push AI commands onto the queue before we use them
|
||||
CmpPtr<ICmpAIManager> cmpAIManager(m_SimContext, SYSTEM_ENTITY);
|
||||
if (!cmpAIManager.null())
|
||||
cmpAIManager->PushCommands();
|
||||
|
||||
CmpPtr<ICmpCommandQueue> cmpCommandQueue(m_SimContext, SYSTEM_ENTITY);
|
||||
if (!cmpCommandQueue.null())
|
||||
cmpCommandQueue->FlushTurn(commands);
|
||||
|
||||
// Process newly generated move commands so the UI feels snappy
|
||||
if (!cmpPathfinder.null())
|
||||
cmpPathfinder->ProcessSameTurnMoves();
|
||||
|
||||
// Send all the update phases
|
||||
SerializationTestState primaryStateBefore;
|
||||
if (m_EnableSerializationTest)
|
||||
{
|
||||
CMessageUpdate msgUpdate(turnLengthFixed);
|
||||
m_ComponentManager.BroadcastMessage(msgUpdate);
|
||||
}
|
||||
{
|
||||
CMessageUpdate_MotionFormation msgUpdate(turnLengthFixed);
|
||||
m_ComponentManager.BroadcastMessage(msgUpdate);
|
||||
ENSURE(m_ComponentManager.SerializeState(primaryStateBefore.state));
|
||||
if (serializationTestDebugDump)
|
||||
ENSURE(m_ComponentManager.DumpDebugState(primaryStateBefore.debug, false));
|
||||
if (serializationTestHash)
|
||||
ENSURE(m_ComponentManager.ComputeStateHash(primaryStateBefore.hash, false));
|
||||
}
|
||||
|
||||
// Process move commands for formations (group proxy)
|
||||
if (!cmpPathfinder.null())
|
||||
cmpPathfinder->ProcessSameTurnMoves();
|
||||
|
||||
UpdateComponents(m_SimContext, turnLengthFixed, commands);
|
||||
|
||||
|
||||
if (m_EnableSerializationTest)
|
||||
{
|
||||
CMessageUpdate_MotionUnit msgUpdate(turnLengthFixed);
|
||||
m_ComponentManager.BroadcastMessage(msgUpdate);
|
||||
// Initialise the secondary simulation
|
||||
CTerrain secondaryTerrain;
|
||||
CSimContext secondaryContext;
|
||||
secondaryContext.m_Terrain = &secondaryTerrain;
|
||||
CComponentManager secondaryComponentManager(secondaryContext);
|
||||
secondaryComponentManager.LoadComponentTypes();
|
||||
ENSURE(LoadDefaultScripts(secondaryComponentManager, NULL));
|
||||
ResetComponentState(secondaryComponentManager, false, false);
|
||||
|
||||
// Load the map into the secondary simulation
|
||||
|
||||
LDR_BeginRegistering();
|
||||
CMapReader* mapReader = new CMapReader; // automatically deletes itself
|
||||
|
||||
// TODO: this duplicates CWorld::RegisterInit and could probably be cleaned up a bit
|
||||
std::string mapType;
|
||||
m_ComponentManager.GetScriptInterface().GetProperty(m_InitAttributes.get(), "mapType", mapType);
|
||||
if (mapType == "scenario")
|
||||
{
|
||||
// Load scenario attributes
|
||||
std::wstring mapFile;
|
||||
m_ComponentManager.GetScriptInterface().GetProperty(m_InitAttributes.get(), "map", mapFile);
|
||||
|
||||
VfsPath mapfilename(VfsPath("maps/scenarios") / (mapFile + L".pmp"));
|
||||
mapReader->LoadMap(mapfilename, &secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: support random map scripts
|
||||
debug_warn(L"Serialization test mode only supports scenarios");
|
||||
}
|
||||
|
||||
LDR_EndRegistering();
|
||||
ENSURE(LDR_NonprogressiveLoad() == INFO::OK);
|
||||
|
||||
ENSURE(secondaryComponentManager.DeserializeState(primaryStateBefore.state));
|
||||
|
||||
SerializationTestState secondaryStateBefore;
|
||||
ENSURE(secondaryComponentManager.SerializeState(secondaryStateBefore.state));
|
||||
if (serializationTestDebugDump)
|
||||
ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateBefore.debug, false));
|
||||
if (serializationTestHash)
|
||||
ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateBefore.hash, false));
|
||||
|
||||
if (primaryStateBefore.state.str() != secondaryStateBefore.state.str() ||
|
||||
primaryStateBefore.hash != secondaryStateBefore.hash)
|
||||
{
|
||||
ReportSerializationFailure(&primaryStateBefore, NULL, &secondaryStateBefore, NULL);
|
||||
}
|
||||
|
||||
SerializationTestState primaryStateAfter;
|
||||
ENSURE(m_ComponentManager.SerializeState(primaryStateAfter.state));
|
||||
if (serializationTestDebugDump)
|
||||
ENSURE(m_ComponentManager.DumpDebugState(primaryStateAfter.debug, false));
|
||||
if (serializationTestHash)
|
||||
ENSURE(m_ComponentManager.ComputeStateHash(primaryStateAfter.hash, false));
|
||||
|
||||
UpdateComponents(secondaryContext, turnLengthFixed,
|
||||
CloneCommandsFromOtherContext(m_ComponentManager.GetScriptInterface(), secondaryComponentManager.GetScriptInterface(), commands));
|
||||
|
||||
SerializationTestState secondaryStateAfter;
|
||||
ENSURE(secondaryComponentManager.SerializeState(secondaryStateAfter.state));
|
||||
if (serializationTestDebugDump)
|
||||
ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateAfter.debug, false));
|
||||
if (serializationTestHash)
|
||||
ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateAfter.hash, false));
|
||||
|
||||
if (primaryStateAfter.state.str() != secondaryStateAfter.state.str() ||
|
||||
primaryStateAfter.hash != secondaryStateAfter.hash)
|
||||
{
|
||||
ReportSerializationFailure(&primaryStateBefore, &primaryStateAfter, &secondaryStateBefore, &secondaryStateAfter);
|
||||
}
|
||||
}
|
||||
{
|
||||
CMessageUpdate_Final msgUpdate(turnLengthFixed);
|
||||
m_ComponentManager.BroadcastMessage(msgUpdate);
|
||||
}
|
||||
|
||||
// Process moves resulting from group proxy movement (unit needs to catch up or realign) and any others
|
||||
if (!cmpPathfinder.null())
|
||||
cmpPathfinder->ProcessSameTurnMoves();
|
||||
|
||||
|
||||
// Clean up any entities destroyed during the simulation update
|
||||
m_ComponentManager.FlushDestroyedComponents();
|
||||
|
||||
// if (m_TurnNumber == 0)
|
||||
// m_ComponentManager.GetScriptInterface().DumpHeap();
|
||||
@ -278,12 +443,69 @@ bool CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
|
||||
DumpState();
|
||||
|
||||
// Start computing AI for the next turn
|
||||
CmpPtr<ICmpAIManager> cmpAIManager(m_SimContext, SYSTEM_ENTITY);
|
||||
if (!cmpAIManager.null())
|
||||
cmpAIManager->StartComputation();
|
||||
|
||||
++m_TurnNumber;
|
||||
}
|
||||
|
||||
return true; // TODO: don't bother with bool return
|
||||
void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengthFixed, const std::vector<SimulationCommand>& commands)
|
||||
{
|
||||
// TODO: the update process is pretty ugly, with lots of messages and dependencies
|
||||
// between different components. Ought to work out a nicer way to do this.
|
||||
|
||||
CComponentManager& componentManager = simContext.GetComponentManager();
|
||||
|
||||
CMessageTurnStart msgTurnStart;
|
||||
componentManager.BroadcastMessage(msgTurnStart);
|
||||
|
||||
CmpPtr<ICmpPathfinder> cmpPathfinder(simContext, SYSTEM_ENTITY);
|
||||
if (!cmpPathfinder.null())
|
||||
cmpPathfinder->FinishAsyncRequests();
|
||||
|
||||
// Push AI commands onto the queue before we use them
|
||||
CmpPtr<ICmpAIManager> cmpAIManager(simContext, SYSTEM_ENTITY);
|
||||
if (!cmpAIManager.null())
|
||||
cmpAIManager->PushCommands();
|
||||
|
||||
CmpPtr<ICmpCommandQueue> cmpCommandQueue(simContext, SYSTEM_ENTITY);
|
||||
if (!cmpCommandQueue.null())
|
||||
cmpCommandQueue->FlushTurn(commands);
|
||||
|
||||
// Process newly generated move commands so the UI feels snappy
|
||||
if (!cmpPathfinder.null())
|
||||
cmpPathfinder->ProcessSameTurnMoves();
|
||||
|
||||
// Send all the update phases
|
||||
{
|
||||
CMessageUpdate msgUpdate(turnLengthFixed);
|
||||
componentManager.BroadcastMessage(msgUpdate);
|
||||
}
|
||||
{
|
||||
CMessageUpdate_MotionFormation msgUpdate(turnLengthFixed);
|
||||
componentManager.BroadcastMessage(msgUpdate);
|
||||
}
|
||||
|
||||
// Process move commands for formations (group proxy)
|
||||
if (!cmpPathfinder.null())
|
||||
cmpPathfinder->ProcessSameTurnMoves();
|
||||
|
||||
{
|
||||
CMessageUpdate_MotionUnit msgUpdate(turnLengthFixed);
|
||||
componentManager.BroadcastMessage(msgUpdate);
|
||||
}
|
||||
{
|
||||
CMessageUpdate_Final msgUpdate(turnLengthFixed);
|
||||
componentManager.BroadcastMessage(msgUpdate);
|
||||
}
|
||||
|
||||
// Process moves resulting from group proxy movement (unit needs to catch up or realign) and any others
|
||||
if (!cmpPathfinder.null())
|
||||
cmpPathfinder->ProcessSameTurnMoves();
|
||||
|
||||
// Clean up any entities destroyed during the simulation update
|
||||
componentManager.FlushDestroyedComponents();
|
||||
}
|
||||
|
||||
void CSimulation2Impl::Interpolate(float frameLength, float frameOffset)
|
||||
@ -316,7 +538,7 @@ void CSimulation2Impl::DumpState()
|
||||
|
||||
file << "\n";
|
||||
|
||||
m_ComponentManager.DumpDebugState(file);
|
||||
m_ComponentManager.DumpDebugState(file, true);
|
||||
|
||||
std::ofstream binfile (OsString(path.ChangeExtension(L".dat")).c_str(), std::ofstream::out | std::ofstream::trunc | std::ofstream::binary);
|
||||
m_ComponentManager.SerializeState(binfile);
|
||||
@ -407,15 +629,15 @@ void CSimulation2::InitGame(const CScriptVal& data)
|
||||
GetScriptInterface().CallFunction(GetScriptInterface().GetGlobalObject(), "InitGame", data, ret);
|
||||
}
|
||||
|
||||
bool CSimulation2::Update(int turnLength)
|
||||
void CSimulation2::Update(int turnLength)
|
||||
{
|
||||
std::vector<SimulationCommand> commands;
|
||||
return m->Update(turnLength, commands);
|
||||
m->Update(turnLength, commands);
|
||||
}
|
||||
|
||||
bool CSimulation2::Update(int turnLength, const std::vector<SimulationCommand>& commands)
|
||||
void CSimulation2::Update(int turnLength, const std::vector<SimulationCommand>& commands)
|
||||
{
|
||||
return m->Update(turnLength, commands);
|
||||
m->Update(turnLength, commands);
|
||||
}
|
||||
|
||||
void CSimulation2::Interpolate(float frameLength, float frameOffset)
|
||||
@ -436,16 +658,12 @@ float CSimulation2::GetLastFrameOffset() const
|
||||
|
||||
bool CSimulation2::LoadScripts(const VfsPath& path)
|
||||
{
|
||||
return m->LoadScripts(path);
|
||||
return m->LoadScripts(m->m_ComponentManager, &m->m_LoadedScripts, path);
|
||||
}
|
||||
|
||||
bool CSimulation2::LoadDefaultScripts()
|
||||
{
|
||||
return (
|
||||
m->LoadScripts(L"simulation/components/interfaces/") &&
|
||||
m->LoadScripts(L"simulation/helpers/") &&
|
||||
m->LoadScripts(L"simulation/components/")
|
||||
);
|
||||
return m->LoadDefaultScripts(m->m_ComponentManager, &m->m_LoadedScripts);
|
||||
}
|
||||
|
||||
void CSimulation2::SetStartupScript(const std::wstring& code)
|
||||
@ -458,6 +676,16 @@ const std::wstring& CSimulation2::GetStartupScript()
|
||||
return m->m_StartupScript;
|
||||
}
|
||||
|
||||
void CSimulation2::SetInitAttributes(const CScriptValRooted& attribs)
|
||||
{
|
||||
m->m_InitAttributes = attribs;
|
||||
}
|
||||
|
||||
CScriptValRooted CSimulation2::GetInitAttributes()
|
||||
{
|
||||
return m->m_InitAttributes;
|
||||
}
|
||||
|
||||
void CSimulation2::SetMapSettings(const std::string& settings)
|
||||
{
|
||||
m->m_MapSettings = m->m_ComponentManager.GetScriptInterface().ParseJSON(settings);
|
||||
@ -514,7 +742,7 @@ bool CSimulation2::ComputeStateHash(std::string& outHash, bool quick)
|
||||
|
||||
bool CSimulation2::DumpDebugState(std::ostream& stream)
|
||||
{
|
||||
return m->m_ComponentManager.DumpDebugState(stream);
|
||||
return m->m_ComponentManager.DumpDebugState(stream, true);
|
||||
}
|
||||
|
||||
bool CSimulation2::SerializeState(std::ostream& stream)
|
||||
|
@ -89,6 +89,17 @@ public:
|
||||
*/
|
||||
const std::wstring& GetStartupScript();
|
||||
|
||||
/**
|
||||
* Set the attributes identifying the scenario/RMS used to initialise this
|
||||
* simulation.
|
||||
*/
|
||||
void SetInitAttributes(const CScriptValRooted& settings);
|
||||
|
||||
/**
|
||||
* Get the data passed to SetInitAttributes.
|
||||
*/
|
||||
CScriptValRooted GetInitAttributes();
|
||||
|
||||
/**
|
||||
* Set the initial map settings (as a UTF-8-encoded JSON string),
|
||||
* which will be used to set up the simulation state.
|
||||
@ -141,8 +152,8 @@ public:
|
||||
*/
|
||||
void InitGame(const CScriptVal& data);
|
||||
|
||||
bool Update(int turnLength);
|
||||
bool Update(int turnLength, const std::vector<SimulationCommand>& commands);
|
||||
void Update(int turnLength);
|
||||
void Update(int turnLength, const std::vector<SimulationCommand>& commands);
|
||||
void Interpolate(float frameLength, float frameOffset);
|
||||
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
|
||||
|
||||
|
@ -68,7 +68,8 @@ public:
|
||||
CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself
|
||||
|
||||
LDR_BeginRegistering();
|
||||
mapReader->LoadMap(L"maps/scenarios/Median Oasis.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, -1);
|
||||
mapReader->LoadMap(L"maps/scenarios/Median Oasis.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
&sim2, &sim2.GetSimContext(), -1, false);
|
||||
LDR_EndRegistering();
|
||||
TS_ASSERT_OK(LDR_NonprogressiveLoad());
|
||||
|
||||
|
@ -52,8 +52,8 @@ std::string canonfloat(T value, int prec)
|
||||
return r;
|
||||
}
|
||||
|
||||
CDebugSerializer::CDebugSerializer(ScriptInterface& scriptInterface, std::ostream& stream) :
|
||||
m_ScriptInterface(scriptInterface), m_Stream(stream), m_Indent(0)
|
||||
CDebugSerializer::CDebugSerializer(ScriptInterface& scriptInterface, std::ostream& stream, bool includeDebugInfo) :
|
||||
m_ScriptInterface(scriptInterface), m_Stream(stream), m_IsDebug(includeDebugInfo), m_Indent(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ void CDebugSerializer::PutRaw(const char* name, const u8* data, size_t len)
|
||||
|
||||
bool CDebugSerializer::IsDebug() const
|
||||
{
|
||||
return true;
|
||||
return m_IsDebug;
|
||||
}
|
||||
|
||||
std::ostream& CDebugSerializer::GetStream()
|
||||
|
@ -30,8 +30,9 @@ public:
|
||||
/**
|
||||
* @param scriptInterface Script interface corresponding to any jsvals passed to ScriptVal()
|
||||
* @param stream Stream to receive UTF-8 encoded output
|
||||
* @param includeDebugInfo If true then additional non-deterministic data will be included in the output
|
||||
*/
|
||||
CDebugSerializer(ScriptInterface& scriptInterface, std::ostream& stream);
|
||||
CDebugSerializer(ScriptInterface& scriptInterface, std::ostream& stream, bool includeDebugInfo = true);
|
||||
|
||||
void Comment(const std::string& comment);
|
||||
void TextLine(const std::string& text);
|
||||
@ -59,6 +60,7 @@ protected:
|
||||
private:
|
||||
ScriptInterface& m_ScriptInterface;
|
||||
std::ostream& m_Stream;
|
||||
bool m_IsDebug;
|
||||
int m_Indent;
|
||||
};
|
||||
|
||||
|
@ -208,7 +208,7 @@ public:
|
||||
|
||||
// Various state serialization functions:
|
||||
bool ComputeStateHash(std::string& outHash, bool quick);
|
||||
bool DumpDebugState(std::ostream& stream);
|
||||
bool DumpDebugState(std::ostream& stream, bool includeDebugInfo);
|
||||
// FlushDestroyedComponents must be called before SerializeState (since the destruction queue
|
||||
// won't get serialized)
|
||||
bool SerializeState(std::ostream& stream);
|
||||
|
@ -51,9 +51,9 @@ void DeserializeRNG(const std::string& str, boost::rand48& rng)
|
||||
s >> rng;
|
||||
}
|
||||
|
||||
bool CComponentManager::DumpDebugState(std::ostream& stream)
|
||||
bool CComponentManager::DumpDebugState(std::ostream& stream, bool includeDebugInfo)
|
||||
{
|
||||
CDebugSerializer serializer(m_ScriptInterface, stream);
|
||||
CDebugSerializer serializer(m_ScriptInterface, stream, includeDebugInfo);
|
||||
|
||||
serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32);
|
||||
|
||||
|
@ -564,7 +564,7 @@ public:
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest2*> (man.QueryInterface(ent3, IID_Test2))->GetX(), 21000);
|
||||
|
||||
std::stringstream debugStream;
|
||||
TS_ASSERT(man.DumpDebugState(debugStream));
|
||||
TS_ASSERT(man.DumpDebugState(debugStream, true));
|
||||
TS_ASSERT_STR_EQUALS(debugStream.str(),
|
||||
"rng: \"78606\"\n"
|
||||
"entities:\n"
|
||||
@ -654,7 +654,7 @@ public:
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent3, IID_Test1))->GetX(), 8);
|
||||
|
||||
std::stringstream debugStream;
|
||||
TS_ASSERT(man.DumpDebugState(debugStream));
|
||||
TS_ASSERT(man.DumpDebugState(debugStream, true));
|
||||
TS_ASSERT_STR_EQUALS(debugStream.str(),
|
||||
"rng: \"78606\"\n"
|
||||
"entities:\n"
|
||||
|
@ -499,7 +499,8 @@ public:
|
||||
CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself
|
||||
|
||||
LDR_BeginRegistering();
|
||||
mapReader->LoadMap(L"maps/scenarios/Latium.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, -1);
|
||||
mapReader->LoadMap(L"maps/scenarios/Latium.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
&sim2, &sim2.GetSimContext(), -1, false);
|
||||
LDR_EndRegistering();
|
||||
TS_ASSERT_OK(LDR_NonprogressiveLoad());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user