Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Simulation2.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "Simulation2.h"
21 
29 
30 #include "graphics/MapReader.h"
31 #include "graphics/Terrain.h"
32 #include "lib/timer.h"
33 #include "lib/file/vfs/vfs_util.h"
34 #include "maths/MathUtil.h"
35 #include "ps/CLogger.h"
36 #include "ps/ConfigDB.h"
37 #include "ps/Filesystem.h"
38 #include "ps/Loader.h"
39 #include "ps/Profile.h"
40 #include "ps/Pyrogenesis.h"
41 #include "ps/XML/Xeromyces.h"
42 
43 #include <iomanip>
44 
45 #if MSC_VERSION
46 #include <process.h>
47 #define getpid _getpid // use the non-deprecated function name
48 #endif
49 
50 static std::string Hexify(const std::string& s) // TODO: shouldn't duplicate this function in so many places
51 {
52  std::stringstream str;
53  str << std::hex;
54  for (size_t i = 0; i < s.size(); ++i)
55  str << std::setfill('0') << std::setw(2) << (int)(unsigned char)s[i];
56  return str.str();
57 }
58 
60 {
61 public:
62  CSimulation2Impl(CUnitManager* unitManager, CTerrain* terrain) :
65  {
66  m_SimContext.m_UnitManager = unitManager;
67  m_SimContext.m_Terrain = terrain;
69 
71 
72  // Tests won't have config initialised
74  {
75  CFG_GET_VAL("ooslog", Bool, m_EnableOOSLog);
76  CFG_GET_VAL("serializationtest", Bool, m_EnableSerializationTest);
77  }
78  }
79 
81  {
83  }
84 
85  void ResetState(bool skipScriptedComponents, bool skipAI)
86  {
87  m_DeltaTime = 0.0;
88  m_LastFrameOffset = 0.0f;
89  m_TurnNumber = 0;
90  ResetComponentState(m_ComponentManager, skipScriptedComponents, skipAI);
91  }
92 
93  static void ResetComponentState(CComponentManager& componentManager, bool skipScriptedComponents, bool skipAI)
94  {
95  componentManager.ResetState();
96 
97  CParamNode noParam;
99 
100  componentManager.InitSystemEntity();
101  CEntityHandle systemEntity = componentManager.GetSystemEntity();
102 
103  // Add native system components:
104  componentManager.AddComponent(systemEntity, CID_TemplateManager, noParam);
105 
106  componentManager.AddComponent(systemEntity, CID_CommandQueue, noParam);
107  componentManager.AddComponent(systemEntity, CID_ObstructionManager, noParam);
108  componentManager.AddComponent(systemEntity, CID_ParticleManager, noParam);
109  componentManager.AddComponent(systemEntity, CID_Pathfinder, noParam);
110  componentManager.AddComponent(systemEntity, CID_ProjectileManager, noParam);
111  componentManager.AddComponent(systemEntity, CID_RangeManager, noParam);
112  componentManager.AddComponent(systemEntity, CID_SoundManager, noParam);
113  componentManager.AddComponent(systemEntity, CID_Terrain, noParam);
114  componentManager.AddComponent(systemEntity, CID_TerritoryManager, noParam);
115  componentManager.AddComponent(systemEntity, CID_WaterManager, noParam);
116 
117  // Add scripted system components:
118  if (!skipScriptedComponents)
119  {
120  // TODO: Load this from a file to allow modders to add scripted components
121  // without having to recompile.
122 #define LOAD_SCRIPTED_COMPONENT(name) \
123  cid = componentManager.LookupCID(name); \
124  if (cid == CID__Invalid) \
125  LOGERROR(L"Can't find component type " L##name); \
126  componentManager.AddComponent(systemEntity, cid, noParam)
127 
128  LOAD_SCRIPTED_COMPONENT("AIInterface");
129  LOAD_SCRIPTED_COMPONENT("Barter");
130  LOAD_SCRIPTED_COMPONENT("EndGameManager");
131  LOAD_SCRIPTED_COMPONENT("GuiInterface");
132  LOAD_SCRIPTED_COMPONENT("PlayerManager");
133  LOAD_SCRIPTED_COMPONENT("TechnologyTemplateManager");
134  LOAD_SCRIPTED_COMPONENT("Timer");
135 
136 #undef LOAD_SCRIPTED_COMPONENT
137 
138  if (!skipAI)
139  {
140  componentManager.AddComponent(systemEntity, CID_AIManager, noParam);
141  }
142 
143  }
144  }
145 
146  static bool LoadDefaultScripts(CComponentManager& componentManager, std::set<VfsPath>* loadedScripts);
147  static bool LoadScripts(CComponentManager& componentManager, std::set<VfsPath>* loadedScripts, const VfsPath& path);
148  Status ReloadChangedFile(const VfsPath& path);
149 
150  static Status ReloadChangedFileCB(void* param, const VfsPath& path)
151  {
152  return static_cast<CSimulation2Impl*>(param)->ReloadChangedFile(path);
153  }
154 
155  int ProgressiveLoad();
156  void Update(int turnLength, const std::vector<SimulationCommand>& commands);
157  static void UpdateComponents(CSimContext& simContext, fixed turnLengthFixed, const std::vector<SimulationCommand>& commands);
158  void Interpolate(float simFrameLength, float frameOffset, float realFrameLength);
159 
160  void DumpState();
161 
164  double m_DeltaTime;
166 
167  std::string m_StartupScript;
170 
171  std::set<VfsPath> m_LoadedScripts;
172 
174 
176 
177 
178  // Functions and data for the serialization test mode: (see Update() for relevant comments)
179 
181 
183  {
184  std::stringstream state;
185  std::stringstream debug;
186  std::string hash;
187  };
188 
190 
192  SerializationTestState* primaryStateBefore, SerializationTestState* primaryStateAfter,
193  SerializationTestState* secondaryStateBefore, SerializationTestState* secondaryStateAfter);
194 
195  static std::vector<SimulationCommand> CloneCommandsFromOtherContext(ScriptInterface& oldScript, ScriptInterface& newScript,
196  const std::vector<SimulationCommand>& commands)
197  {
198  std::vector<SimulationCommand> newCommands = commands;
199  for (size_t i = 0; i < newCommands.size(); ++i)
200  {
201  newCommands[i].data = CScriptValRooted(newScript.GetContext(),
202  newScript.CloneValueFromOtherContext(oldScript, newCommands[i].data.get()));
203  }
204  return newCommands;
205  }
206 };
207 
208 bool CSimulation2Impl::LoadDefaultScripts(CComponentManager& componentManager, std::set<VfsPath>* loadedScripts)
209 {
210  return (
211  LoadScripts(componentManager, loadedScripts, L"simulation/components/interfaces/") &&
212  LoadScripts(componentManager, loadedScripts, L"simulation/helpers/") &&
213  LoadScripts(componentManager, loadedScripts, L"simulation/components/")
214  );
215 }
216 
217 bool CSimulation2Impl::LoadScripts(CComponentManager& componentManager, std::set<VfsPath>* loadedScripts, const VfsPath& path)
218 {
219  VfsPaths pathnames;
220  if (vfs::GetPathnames(g_VFS, path, L"*.js", pathnames) < 0)
221  return false;
222 
223  bool ok = true;
224  for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
225  {
226  VfsPath filename = *it;
227  if (loadedScripts)
228  loadedScripts->insert(filename);
229  LOGMESSAGE(L"Loading simulation script '%ls'", filename.string().c_str());
230  if (! componentManager.LoadScript(filename))
231  ok = false;
232  }
233  return ok;
234 }
235 
237 {
238  // Ignore if this file wasn't loaded as a script
239  // (TODO: Maybe we ought to load in any new .js files that are created in the right directories)
240  if (m_LoadedScripts.find(path) == m_LoadedScripts.end())
241  return INFO::OK;
242 
243  // If the file doesn't exist (e.g. it was deleted), don't bother loading it since that'll give an error message.
244  // (Also don't bother trying to 'unload' it from the component manager, because that's not possible)
245  if (!VfsFileExists(path))
246  return INFO::OK;
247 
248  LOGMESSAGE(L"Reloading simulation script '%ls'", path.string().c_str());
249  if (!m_ComponentManager.LoadScript(path, true))
250  return ERR::FAIL;
251 
252  return INFO::OK;
253 }
254 
256 {
257  // yield after this time is reached. balances increased progress bar
258  // smoothness vs. slowing down loading.
259  const double end_time = timer_Time() + 200e-3;
260 
261  int ret;
262 
263  do
264  {
265  bool progressed = false;
266  int total = 0;
267  int progress = 0;
268 
269  CMessageProgressiveLoad msg(&progressed, &total, &progress);
270 
272 
273  if (!progressed || total == 0)
274  return 0; // we have nothing left to load
275 
276  ret = Clamp(100*progress / total, 1, 100);
277  }
278  while (timer_Time() < end_time);
279 
280  return ret;
281 }
282 
284 {
285  if (!state.hash.empty())
286  {
287  std::ofstream file (OsString(path / (L"hash." + suffix)).c_str(), std::ofstream::out | std::ofstream::trunc);
288  file << Hexify(state.hash);
289  }
290 
291  if (!state.debug.str().empty())
292  {
293  std::ofstream file (OsString(path / (L"debug." + suffix)).c_str(), std::ofstream::out | std::ofstream::trunc);
294  file << state.debug.str();
295  }
296 
297  if (!state.state.str().empty())
298  {
299  std::ofstream file (OsString(path / (L"state." + suffix)).c_str(), std::ofstream::out | std::ofstream::trunc | std::ofstream::binary);
300  file << state.state.str();
301  }
302 }
303 
305  SerializationTestState* primaryStateBefore, SerializationTestState* primaryStateAfter,
306  SerializationTestState* secondaryStateBefore, SerializationTestState* secondaryStateAfter)
307 {
308  OsPath path = psLogDir() / "oos_log";
309  CreateDirectories(path, 0700);
310 
311  // Clean up obsolete files from previous runs
312  wunlink(path / "hash.before.a");
313  wunlink(path / "hash.before.b");
314  wunlink(path / "debug.before.a");
315  wunlink(path / "debug.before.b");
316  wunlink(path / "state.before.a");
317  wunlink(path / "state.before.b");
318  wunlink(path / "hash.after.a");
319  wunlink(path / "hash.after.b");
320  wunlink(path / "debug.after.a");
321  wunlink(path / "debug.after.b");
322  wunlink(path / "state.after.a");
323  wunlink(path / "state.after.b");
324 
325  if (primaryStateBefore)
326  DumpSerializationTestState(*primaryStateBefore, path, L"before.a");
327  if (primaryStateAfter)
328  DumpSerializationTestState(*primaryStateAfter, path, L"after.a");
329  if (secondaryStateBefore)
330  DumpSerializationTestState(*secondaryStateBefore, path, L"before.b");
331  if (secondaryStateAfter)
332  DumpSerializationTestState(*secondaryStateAfter, path, L"after.b");
333 
334  debug_warn(L"Serialization test failure");
335 }
336 
337 void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationCommand>& commands)
338 {
339  PROFILE3("sim update");
340  PROFILE2_ATTR("turn %d", (int)m_TurnNumber);
341 
342  fixed turnLengthFixed = fixed::FromInt(turnLength) / 1000;
343 
344  /*
345  * In serialization test mode, we save the original (primary) simulation state before each turn update.
346  * We run the update, then load the saved state into a secondary context.
347  * We serialize that again and compare to the original serialization (to check that
348  * serialize->deserialize->serialize is equivalent to serialize).
349  * Then we run the update on the secondary context, and check that its new serialized
350  * state matches the primary context after the update (to check that the simulation doesn't depend
351  * on anything that's not serialized).
352  */
353 
354  const bool serializationTestDebugDump = false; // set true to save human-readable state dumps before an error is detected, for debugging (but slow)
355  const bool serializationTestHash = true; // set true to save and compare hash of state
356 
357  SerializationTestState primaryStateBefore;
359  {
360  ENSURE(m_ComponentManager.SerializeState(primaryStateBefore.state));
361  if (serializationTestDebugDump)
362  ENSURE(m_ComponentManager.DumpDebugState(primaryStateBefore.debug, false));
363  if (serializationTestHash)
364  ENSURE(m_ComponentManager.ComputeStateHash(primaryStateBefore.hash, false));
365  }
366 
367 
368  UpdateComponents(m_SimContext, turnLengthFixed, commands);
369 
370 
372  {
373  // Initialise the secondary simulation
374  CTerrain secondaryTerrain;
375  CSimContext secondaryContext;
376  secondaryContext.m_Terrain = &secondaryTerrain;
377  CComponentManager secondaryComponentManager(secondaryContext);
378  secondaryComponentManager.LoadComponentTypes();
379  ENSURE(LoadDefaultScripts(secondaryComponentManager, NULL));
380  ResetComponentState(secondaryComponentManager, false, false);
381 
382  // Load the map into the secondary simulation
383 
385  CMapReader* mapReader = new CMapReader; // automatically deletes itself
386 
387  // TODO: this duplicates CWorld::RegisterInit and could probably be cleaned up a bit
388  std::string mapType;
390  if (mapType == "random")
391  {
392  // TODO: support random map scripts
393  debug_warn(L"Serialization test mode only supports scenarios");
394  }
395  else
396  {
397  std::wstring mapFile;
399 
400  VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp");
401  mapReader->LoadMap(mapfilename, CScriptValRooted(), &secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL,
402  NULL, NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure
403  }
406 
407  ENSURE(secondaryComponentManager.DeserializeState(primaryStateBefore.state));
408 
409  SerializationTestState secondaryStateBefore;
410  ENSURE(secondaryComponentManager.SerializeState(secondaryStateBefore.state));
411  if (serializationTestDebugDump)
412  ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateBefore.debug, false));
413  if (serializationTestHash)
414  ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateBefore.hash, false));
415 
416  if (primaryStateBefore.state.str() != secondaryStateBefore.state.str() ||
417  primaryStateBefore.hash != secondaryStateBefore.hash)
418  {
419  ReportSerializationFailure(&primaryStateBefore, NULL, &secondaryStateBefore, NULL);
420  }
421 
422  SerializationTestState primaryStateAfter;
423  ENSURE(m_ComponentManager.SerializeState(primaryStateAfter.state));
424  if (serializationTestHash)
425  ENSURE(m_ComponentManager.ComputeStateHash(primaryStateAfter.hash, false));
426 
427  UpdateComponents(secondaryContext, turnLengthFixed,
429 
430  SerializationTestState secondaryStateAfter;
431  ENSURE(secondaryComponentManager.SerializeState(secondaryStateAfter.state));
432  if (serializationTestHash)
433  ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateAfter.hash, false));
434 
435  if (primaryStateAfter.state.str() != secondaryStateAfter.state.str() ||
436  primaryStateAfter.hash != secondaryStateAfter.hash)
437  {
438  // Only do the (slow) dumping now we know we're going to need to report it
439  ENSURE(m_ComponentManager.DumpDebugState(primaryStateAfter.debug, false));
440  ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateAfter.debug, false));
441 
442  ReportSerializationFailure(&primaryStateBefore, &primaryStateAfter, &secondaryStateBefore, &secondaryStateAfter);
443  }
444  }
445 
446 // if (m_TurnNumber == 0)
447 // m_ComponentManager.GetScriptInterface().DumpHeap();
448 
449  // Run the GC occasionally
450  // (TODO: we ought to schedule this for a frame where we're not
451  // running the sim update, to spread the load)
452  if (m_TurnNumber % 10 == 0)
454 
455  if (m_EnableOOSLog)
456  DumpState();
457 
458  // Start computing AI for the next turn
460  if (cmpAIManager)
461  cmpAIManager->StartComputation();
462 
463  ++m_TurnNumber;
464 }
465 
466 void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengthFixed, const std::vector<SimulationCommand>& commands)
467 {
468  // TODO: the update process is pretty ugly, with lots of messages and dependencies
469  // between different components. Ought to work out a nicer way to do this.
470 
471  CComponentManager& componentManager = simContext.GetComponentManager();
472 
473  CMessageTurnStart msgTurnStart;
474  componentManager.BroadcastMessage(msgTurnStart);
475 
476  CmpPtr<ICmpPathfinder> cmpPathfinder(simContext, SYSTEM_ENTITY);
477  if (cmpPathfinder)
478  cmpPathfinder->FinishAsyncRequests();
479 
480  // Push AI commands onto the queue before we use them
481  CmpPtr<ICmpAIManager> cmpAIManager(simContext, SYSTEM_ENTITY);
482  if (cmpAIManager)
483  cmpAIManager->PushCommands();
484 
485  CmpPtr<ICmpCommandQueue> cmpCommandQueue(simContext, SYSTEM_ENTITY);
486  if (cmpCommandQueue)
487  cmpCommandQueue->FlushTurn(commands);
488 
489  // Process newly generated move commands so the UI feels snappy
490  if (cmpPathfinder)
491  cmpPathfinder->ProcessSameTurnMoves();
492 
493  // Send all the update phases
494  {
495  CMessageUpdate msgUpdate(turnLengthFixed);
496  componentManager.BroadcastMessage(msgUpdate);
497  }
498  {
499  CMessageUpdate_MotionFormation msgUpdate(turnLengthFixed);
500  componentManager.BroadcastMessage(msgUpdate);
501  }
502 
503  // Process move commands for formations (group proxy)
504  if (cmpPathfinder)
505  cmpPathfinder->ProcessSameTurnMoves();
506 
507  {
508  CMessageUpdate_MotionUnit msgUpdate(turnLengthFixed);
509  componentManager.BroadcastMessage(msgUpdate);
510  }
511  {
512  CMessageUpdate_Final msgUpdate(turnLengthFixed);
513  componentManager.BroadcastMessage(msgUpdate);
514  }
515 
516  // Process moves resulting from group proxy movement (unit needs to catch up or realign) and any others
517  if (cmpPathfinder)
518  cmpPathfinder->ProcessSameTurnMoves();
519 
520  // Clean up any entities destroyed during the simulation update
521  componentManager.FlushDestroyedComponents();
522 }
523 
524 void CSimulation2Impl::Interpolate(float simFrameLength, float frameOffset, float realFrameLength)
525 {
526  PROFILE3("sim interpolate");
527 
528  m_LastFrameOffset = frameOffset;
529 
530  CMessageInterpolate msg(simFrameLength, frameOffset, realFrameLength);
532 
533  // Clean up any entities destroyed during interpolate (e.g. local corpses)
535 }
536 
538 {
539  PROFILE("DumpState");
540 
541  std::stringstream pid;
542  pid << getpid();
543  std::stringstream name;\
544  name << std::setw(5) << std::setfill('0') << m_TurnNumber << ".txt";
545  OsPath path = psLogDir() / "sim_log" / pid.str() / name.str();
546  CreateDirectories(path.Parent(), 0700);
547  std::ofstream file (OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc);
548 
549  file << "State hash: " << std::hex;
550  std::string hashRaw;
551  m_ComponentManager.ComputeStateHash(hashRaw, false);
552  for (size_t i = 0; i < hashRaw.size(); ++i)
553  file << std::setfill('0') << std::setw(2) << (int)(unsigned char)hashRaw[i];
554  file << std::dec << "\n";
555 
556  file << "\n";
557 
559 
560  std::ofstream binfile (OsString(path.ChangeExtension(L".dat")).c_str(), std::ofstream::out | std::ofstream::trunc | std::ofstream::binary);
562 }
563 
564 ////////////////////////////////////////////////////////////////
565 
567  m(new CSimulation2Impl(unitManager, terrain))
568 {
569 }
570 
572 {
573  delete m;
574 }
575 
576 // Forward all method calls to the appropriate CSimulation2Impl/CComponentManager methods:
577 
579 {
580  m->m_EnableOOSLog = true;
581 }
582 
584 {
586 }
587 
588 entity_id_t CSimulation2::AddEntity(const std::wstring& templateName)
589 {
591 }
592 
593 entity_id_t CSimulation2::AddEntity(const std::wstring& templateName, entity_id_t preferredId)
594 {
595  return m->m_ComponentManager.AddEntity(templateName, m->m_ComponentManager.AllocateNewEntity(preferredId));
596 }
597 
598 entity_id_t CSimulation2::AddLocalEntity(const std::wstring& templateName)
599 {
601 }
602 
604 {
606 }
607 
609 {
611 }
612 
614 {
615  return m->m_ComponentManager.QueryInterface(ent, iid);
616 }
617 
618 void CSimulation2::PostMessage(entity_id_t ent, const CMessage& msg) const
619 {
620  m->m_ComponentManager.PostMessage(ent, msg);
621 }
622 
624 {
626 }
627 
629 {
631 }
632 
634 {
636 }
637 
639 {
640  return m->m_SimContext;
641 }
642 
644 {
646 }
647 
649 {
650  GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "InitGame", data);
651 }
652 
654 {
655  GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "InitSavedGame");
656 }
657 
658 void CSimulation2::Update(int turnLength)
659 {
660  std::vector<SimulationCommand> commands;
661  m->Update(turnLength, commands);
662 }
663 
664 void CSimulation2::Update(int turnLength, const std::vector<SimulationCommand>& commands)
665 {
666  m->Update(turnLength, commands);
667 }
668 
669 void CSimulation2::Interpolate(float simFrameLength, float frameOffset, float realFrameLength)
670 {
671  m->Interpolate(simFrameLength, frameOffset, realFrameLength);
672 }
673 
674 void CSimulation2::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)
675 {
676  PROFILE3("sim submit");
677 
678  CMessageRenderSubmit msg(collector, frustum, culling);
680 }
681 
683 {
684  return m->m_LastFrameOffset;
685 }
686 
688 {
689  return m->LoadScripts(m->m_ComponentManager, &m->m_LoadedScripts, path);
690 }
691 
693 {
695 }
696 
697 void CSimulation2::SetStartupScript(const std::string& code)
698 {
699  m->m_StartupScript = code;
700 }
701 
702 const std::string& CSimulation2::GetStartupScript()
703 {
704  return m->m_StartupScript;
705 }
706 
708 {
709  m->m_InitAttributes = attribs;
710 }
711 
713 {
714  return m->m_InitAttributes;
715 }
716 
717 void CSimulation2::SetMapSettings(const std::string& settings)
718 {
720 }
721 
723 {
724  m->m_MapSettings = settings;
725 }
726 
728 {
730 }
731 
733 {
734  return m->m_MapSettings.get();
735 }
736 
737 void CSimulation2::LoadPlayerSettings(bool newPlayers)
738 {
739  GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "LoadPlayerSettings", m->m_MapSettings, newPlayers);
740 }
741 
743 {
744  // Initialize here instead of in Update()
745  GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "LoadMapSettings", m->m_MapSettings);
746 
747  if (!m->m_StartupScript.empty())
748  GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript);
749 }
750 
752 {
753  return m->ProgressiveLoad();
754 }
755 
757 {
758  return m->ReloadChangedFile(path);
759 }
760 
761 void CSimulation2::ResetState(bool skipScriptedComponents, bool skipAI)
762 {
763  m->ResetState(skipScriptedComponents, skipAI);
764 }
765 
766 bool CSimulation2::ComputeStateHash(std::string& outHash, bool quick)
767 {
768  return m->m_ComponentManager.ComputeStateHash(outHash, quick);
769 }
770 
771 bool CSimulation2::DumpDebugState(std::ostream& stream)
772 {
773  return m->m_ComponentManager.DumpDebugState(stream, true);
774 }
775 
776 bool CSimulation2::SerializeState(std::ostream& stream)
777 {
778  return m->m_ComponentManager.SerializeState(stream);
779 }
780 
781 bool CSimulation2::DeserializeState(std::istream& stream)
782 {
783  // TODO: need to make sure the required SYSTEM_ENTITY components get constructed
784  return m->m_ComponentManager.DeserializeState(stream);
785 }
786 
788 {
790 }
791 
792 std::vector<std::string> CSimulation2::GetRMSData()
793 {
794  VfsPath path(L"maps/random/");
795  VfsPaths pathnames;
796 
797  std::vector<std::string> data;
798 
799  // Find all ../maps/random/*.json
800  Status ret = vfs::GetPathnames(g_VFS, path, L"*.json", pathnames);
801  if (ret == INFO::OK)
802  {
803  for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
804  {
805  // Load JSON file
806  CVFSFile file;
807  PSRETURN ret = file.Load(g_VFS, *it);
808  if (ret != PSRETURN_OK)
809  {
810  LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
811  }
812  else
813  {
814  data.push_back(file.DecodeUTF8()); // assume it's UTF-8
815  }
816  }
817  }
818  else
819  {
820  // Some error reading directory
821  wchar_t error[200];
822  LOGERROR(L"Error reading directory '%ls': %ls", path.string().c_str(), StatusDescription(ret, error, ARRAY_SIZE(error)));
823  }
824 
825  return data;
826 }
827 
828 std::vector<std::string> CSimulation2::GetCivData()
829 {
830  VfsPath path(L"civs/");
831  VfsPaths pathnames;
832 
833  std::vector<std::string> data;
834 
835  // Load all JSON files in civs directory
836  Status ret = vfs::GetPathnames(g_VFS, path, L"*.json", pathnames);
837  if (ret == INFO::OK)
838  {
839  for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
840  {
841  // Load JSON file
842  CVFSFile file;
843  PSRETURN ret = file.Load(g_VFS, *it);
844  if (ret != PSRETURN_OK)
845  {
846  LOGERROR(L"CSimulation2::GetCivData: Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
847  }
848  else
849  {
850  data.push_back(file.DecodeUTF8()); // assume it's UTF-8
851  }
852  }
853  }
854  else
855  {
856  // Some error reading directory
857  wchar_t error[200];
858  LOGERROR(L"CSimulation2::GetCivData: Error reading directory '%ls': %ls", path.string().c_str(), StatusDescription(ret, error, ARRAY_SIZE(error)));
859  }
860 
861  return data;
862 }
863 
865 {
866  return ReadJSON(L"simulation/data/player_defaults.json");
867 }
868 
870 {
871  return ReadJSON(L"simulation/data/map_sizes.json");
872 }
873 
875 {
876  std::string data;
877 
878  if (!VfsFileExists(path))
879  {
880  LOGERROR(L"File '%ls' does not exist", path.string().c_str());
881  }
882  else
883  {
884  // Load JSON file
885  CVFSFile file;
886  PSRETURN ret = file.Load(g_VFS, path);
887  if (ret != PSRETURN_OK)
888  {
889  LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
890  }
891  else
892  {
893  data = file.DecodeUTF8(); // assume it's UTF-8
894  }
895  }
896 
897  return data;
898 }
899 
901 {
902  ScriptInterface& scriptInterface = GetScriptInterface();
903  std::vector<CScriptValRooted> aiData = ICmpAIManager::GetAIs(scriptInterface);
904 
905  // Build single JSON string with array of AI data
906  CScriptValRooted ais;
907  if (!scriptInterface.Eval("({})", ais) || !scriptInterface.SetProperty(ais.get(), "AIData", aiData))
908  return std::string();
909 
910  return scriptInterface.StringifyJSON(ais.get());
911 }
An entity initialisation parameter node.
Definition: ParamNode.h:112
Update phase for formation controller movement (must happen before individual units move to follow th...
Definition: MessageTypes.h:74
A simple fixed-point number class.
Definition: Fixed.h:115
virtual void FlushTurn(const std::vector< SimulationCommand > &commands)=0
Calls the ProcessCommand(player, cmd) global script function for each command in the local queue and ...
CStr DecodeUTF8() const
Returns contents of a UTF-8 encoded file as a string with optional BOM removed.
Definition: Filesystem.cpp:153
CSimulation2(CUnitManager *, CTerrain *)
void FlushDestroyedComponents()
Does the actual destruction of components from DestroyComponentsSoon.
static Status ReloadChangedFileCB(void *param, const VfsPath &path)
void ResetState()
Resets the dynamic simulation state (deletes all entities, resets entity ID counters; doesn&#39;t unload/...
void Interpolate(float simFrameLength, float frameOffset, float realFrameLength)
Generic per-turn update message, for things that don&#39;t care much about ordering.
Definition: MessageTypes.h:57
std::string ReadJSON(VfsPath path)
void InitSavedGame()
const CSimContext & GetSimContext() const
Path VfsPath
VFS path of the form &quot;(dir/)*file?&quot;.
Definition: vfs_path.h:40
static std::string Hexify(const std::string &s)
Definition: Simulation2.cpp:50
static std::vector< CScriptValRooted > GetAIs(ScriptInterface &scriptInterface)
Returns a vector of {&quot;id&quot;:&quot;value-for-AddPlayer&quot;, &quot;name&quot;:&quot;Human readable name&quot;} objects, based on all the available AI scripts.
entity_id_t AddEntity(const std::wstring &templateName, entity_id_t ent)
Constructs an entity based on the given template, and adds it the world with entity ID ent...
void SetInitAttributes(const CScriptValRooted &settings)
Set the attributes identifying the scenario/RMS used to initialise this simulation.
IComponent * QueryInterface(entity_id_t ent, int iid) const
virtual void ProcessSameTurnMoves()=0
Process moves during the same turn they were created in to improve responsiveness.
const OsPath & psLogDir()
Definition: Pyrogenesis.cpp:96
const Status OK
Definition: status.h:386
#define LOGERROR
Definition: CLogger.h:35
Status LDR_NonprogressiveLoad()
Definition: Loader.cpp:308
const PSRETURN PSRETURN_OK
Definition: Errors.h:103
Reads a file, then gives read-only access to the contents.
Definition: Filesystem.h:69
virtual void FinishAsyncRequests()=0
Finish computing asynchronous path requests and send the CMessagePathResult messages.
bool DeserializeState(std::istream &stream)
CTerrain * m_Terrain
Definition: SimContext.h:60
IComponent * QueryInterface(entity_id_t ent, InterfaceId iid) const
Add renderable objects to the scene collector.
Definition: MessageTypes.h:145
#define CFG_GET_VAL(name, type, destination)
Definition: ConfigDB.h:147
#define LOGMESSAGE
Definition: CLogger.h:32
entity_id_t AddLocalEntity(const std::wstring &templateName)
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
bool CallFunctionVoid(jsval val, const char *name)
Call the named property on the given object, with void return type and 0 arguments.
Object wrapping an entity_id_t, with a SEntityComponentCache to support fast QueryInterface() / CmpPt...
Definition: Entity.h:80
void Interpolate(float simFrameLength, float frameOffset, float realFrameLength)
std::string m_StartupScript
void Update(int turnLength, const std::vector< SimulationCommand > &commands)
void ResetState(bool skipScriptedComponents=false, bool skipAI=false)
Initialise (or re-initialise) the complete simulation state.
Path Parent() const
Definition: path.h:150
bool m_EnableSerializationTest
ScriptInterface & GetScriptInterface()
A trivial wrapper around a jsval.
Definition: ScriptVal.h:29
std::string GenerateSchema()
Update phase for non-formation-controller unit movement.
Definition: MessageTypes.h:90
void RenderSubmit(SceneCollector &collector, const CFrustum &frustum, bool culling)
bool LoadScript(const VfsPath &filename, const std::string &code)
Load and execute the given script in a new function scope.
T Clamp(T val, T min, T max)
low-level aka &quot;lib&quot;
Definition: lib.h:68
std::string StringifyJSON(jsval obj, bool indent=true)
Stringify to a JSON string, UTF-8 encoded.
const InterfaceListUnordered & GetEntitiesWithInterfaceUnordered(int iid)
Returns a list of components implementing the given interface, and their associated entities...
Contains pointers to various &#39;global&#39; objects that are needed by the simulation code, to allow easy access without using real (evil) global variables.
Definition: SimContext.h:32
bool SerializeState(std::ostream &stream)
CScriptValRooted ParseJSON(const std::string &string_utf8)
Parse a UTF-8-encoded JSON string.
std::vector< std::string > GetRMSData()
Get random map script data.
bool LoadDefaultScripts()
Call LoadScripts for each of the game&#39;s standard simulation script paths.
std::string GetMapSettingsString()
Get the current map settings as a UTF-8 JSON string.
void Update(int turnLength)
float GetLastFrameOffset() const
Returns the last frame offset passed to Interpolate(), i.e.
const entity_id_t SYSTEM_ENTITY
Entity ID for singleton &#39;system&#39; components.
Definition: Entity.h:44
Status ReloadChangedFile(const VfsPath &path)
Reload any scripts that were loaded from the given filename.
#define ARRAY_SIZE(name)
void InitGame(const CScriptVal &data)
Initialise a new game, based on some script data.
This interface accepts renderable objects.
Definition: Scene.h:82
void SetMapSettings(const std::string &settings)
Set the initial map settings (as a UTF-8-encoded JSON string), which will be used to set up the simul...
CSimulation2Impl * m
Definition: Simulation2.h:260
std::vector< std::pair< entity_id_t, IComponent * > > InterfaceList
Definition: Simulation2.h:196
const InterfaceListUnordered & GetEntitiesWithInterfaceUnordered(InterfaceId iid) const
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
std::set< VfsPath > m_LoadedScripts
InterfaceList GetEntitiesWithInterface(InterfaceId iid) const
Final update phase, after all other updates.
Definition: MessageTypes.h:106
#define LOAD_SCRIPTED_COMPONENT(name)
static bool LoadScripts(CComponentManager &componentManager, std::set< VfsPath > *loadedScripts, const VfsPath &path)
void UnregisterFileReloadFunc(FileReloadFunc func, void *obj)
delete a callback function registered with RegisterFileReloadFunc (removes any with the same func and...
Definition: Filesystem.cpp:44
void BroadcastMessage(const CMessage &msg) const
u32 PSRETURN
Definition: Errors.h:75
LIB_API int wunlink(const OsPath &pathname)
void RegisterFileReloadFunc(FileReloadFunc func, void *obj)
register a callback function to be called by ReloadChangedFiles
Definition: Filesystem.cpp:39
virtual void PushCommands()=0
Call this at the start of a turn, to push the computed AI commands into the command queue...
Definition: path.h:75
const String & string() const
Definition: path.h:123
bool ComputeStateHash(std::string &outHash, bool quick)
std::wstring String
Definition: path.h:78
std::string GetPlayerDefaults()
Get player default data.
ScriptInterface & GetScriptInterface() const
bool Eval(const char *code)
uint32_t m_TurnNumber
bool LoadScripts(const VfsPath &path)
Load all scripts in the specified directory (non-recursively), so they can register new component typ...
#define PROFILE(name)
Definition: Profile.h:195
void LoadMap(const VfsPath &pathname, const CScriptValRooted &settings, CTerrain *, WaterManager *, SkyManager *, CLightEnv *, CGameView *, CCinemaManager *, CTriggerManager *, CPostprocManager *pPostproc, CSimulation2 *, const CSimContext *, int playerID, bool skipEntities)
Definition: MapReader.cpp:67
bool ComputeStateHash(std::string &outHash, bool quick)
i64 Status
Error handling system.
Definition: status.h:171
static bool IsInitialised()
Definition: Singleton.h:63
void InitSystemEntity()
Set up an empty SYSTEM_ENTITY.
std::string GetAIData()
Get AI data.
double timer_Time()
Definition: timer.cpp:98
entity_id_t AllocateNewLocalEntity()
Returns a new local entity ID that has never been used before.
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
bool SerializeState(std::ostream &stream)
bool DeserializeState(std::istream &stream)
jsval CloneValueFromOtherContext(ScriptInterface &otherContext, jsval val)
Construct a new value (usable in this ScriptInterface&#39;s context) by cloning a value from a different ...
void ReportSerializationFailure(SerializationTestState *primaryStateBefore, SerializationTestState *primaryStateAfter, SerializationTestState *secondaryStateBefore, SerializationTestState *secondaryStateAfter)
const char * GetErrorString(PSRETURN code)
Definition: Errors.cpp:458
bool GetProperty(jsval obj, const char *name, T &out)
Get the named property on the given object.
virtual void StartComputation()=0
Call this at the end of a turn, to trigger AI computation which will be ready for the next turn...
static CFixed FromInt(int n)
Definition: Fixed.h:136
CComponentManager m_ComponentManager
Status ReloadChangedFile(const VfsPath &path)
#define PROFILE2_ATTR
Associates a string (with printf-style formatting) with the current region or event.
Definition: Profiler2.h:461
void PostMessage(entity_id_t ent, const CMessage &msg) const
Send a message, targeted at a particular entity.
void LoadMapSettings()
Loads the map settings script (called after map is loaded)
bool AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode &paramNode)
Constructs a component of type &#39;cid&#39;, initialised with data &#39;paramNode&#39;, and attaches it to entity &#39;e...
bool DumpDebugState(std::ostream &stream, bool includeDebugInfo)
bool LoadScript(const VfsPath &filename, bool hotload=false)
Load a script and execute it in a new function scope.
std::string GetMapSizes()
Get map sizes data.
CSimContext m_SimContext
#define PROFILE3(name)
Definition: Profile.h:201
Path ChangeExtension(Path extension) const
Definition: path.h:185
Handle progressive loading of resources.
Definition: MessageTypes.h:171
void ResetState(bool skipScriptedComponents, bool skipAI)
Definition: Simulation2.cpp:85
PSRETURN Load(const PIVFS &vfs, const VfsPath &filename)
Returns either PSRETURN_OK or PSRETURN_CVFSFile_LoadFailed.
Definition: Filesystem.cpp:117
InterfaceList GetEntitiesWithInterface(int iid)
Returns a list of components implementing the given interface, and their associated entities...
wchar_t * StatusDescription(Status status, wchar_t *buf, size_t max_chars)
generate textual description of a Status.
Definition: status.cpp:79
unsigned int uint32_t
Definition: wposix_types.h:53
static std::vector< SimulationCommand > CloneCommandsFromOtherContext(ScriptInterface &oldScript, ScriptInterface &newScript, const std::vector< SimulationCommand > &commands)
void LDR_EndRegistering()
Definition: Loader.cpp:130
std::vector< std::string > GetCivData()
Get civilization data.
void BroadcastMessage(const CMessage &msg) const
Send a message, not targeted at any particular entity.
CScriptVal GetMapSettings()
Get the current map settings.
void FlushDestroyedEntities()
Does the actual destruction of entities from DestroyEntity.
bool VfsFileExists(const VfsPath &pathname)
Definition: Filesystem.cpp:34
CScriptValRooted m_InitAttributes
entity_id_t AddEntity(const std::wstring &templateName)
Construct a new entity and add it to the world.
boost::unordered_map< entity_id_t, IComponent * > InterfaceListUnordered
Definition: Simulation2.h:197
void DestroyEntity(entity_id_t ent)
Destroys the specified entity, once FlushDestroyedEntities is called.
std::vector< VfsPath > VfsPaths
Definition: vfs_path.h:42
JSBool error(JSContext *cx, uintN argc, jsval *vp)
std::string GenerateSchema()
jsval get() const
Returns the current value (or JSVAL_VOID if uninitialised).
Definition: ScriptVal.cpp:45
static void UpdateComponents(CSimContext &simContext, fixed turnLengthFixed, const std::vector< SimulationCommand > &commands)
const Status FAIL
Definition: status.h:406
void EnableOOSLog()
CEntityHandle GetSystemEntity()
Returns a CEntityHandle with id SYSTEM_ENTITY.
Abstraction around a SpiderMonkey JSContext.
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
void DestroyComponentsSoon(entity_id_t ent)
Destroys all the components belonging to the specified entity when FlushDestroyedComponents is called...
bool SetProperty(jsval obj, const char *name, const T &value, bool constant=false, bool enumerate=true)
Set the named property on the given object.
int ProgressiveLoad()
RegMemFun incremental loader function.
Prepare for rendering a new frame (set up model positions etc).
Definition: MessageTypes.h:122
CSimulation2Impl(CUnitManager *unitManager, CTerrain *terrain)
Definition: Simulation2.cpp:62
Status CreateDirectories(const OsPath &path, mode_t mode)
PIVFS g_VFS
Definition: Filesystem.cpp:30
void SetStartupScript(const std::string &script)
Set a startup script, which will get executed before the first turn.
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
CUnitManager * m_UnitManager
Definition: SimContext.h:59
const std::string & GetStartupScript()
Get the current startup script.
static const player_id_t INVALID_PLAYER
Definition: Player.h:26
void EnableSerializationTest()
static void ResetComponentState(CComponentManager &componentManager, bool skipScriptedComponents, bool skipAI)
Definition: Simulation2.cpp:93
CScriptValRooted GetInitAttributes()
Get the data passed to SetInitAttributes.
Status GetPathnames(const PIVFS &fs, const VfsPath &path, const wchar_t *filter, VfsPaths &pathnames)
Definition: vfs_util.cpp:40
bool DumpDebugState(std::ostream &stream)
JSContext * GetContext() const
void MaybeGC()
MaybeGC tries to determine whether garbage collection in cx&#39;s runtime would free up enough memory to ...
void DumpSerializationTestState(SerializationTestState &state, const OsPath &path, const OsPath::String &suffix)
void PostMessage(entity_id_t ent, const CMessage &msg) const
static bool LoadDefaultScripts(CComponentManager &componentManager, std::set< VfsPath > *loadedScripts)
CComponentManager & GetComponentManager() const
Definition: SimContext.cpp:35
void LDR_BeginRegistering()
Definition: Loader.cpp:101
static enum @41 state
static std::string OsString(const OsPath &path)
Definition: os_path.h:42
void LoadPlayerSettings(bool newPlayers)
Loads the player settings script (called before map is loaded)
entity_id_t AllocateNewEntity()
Returns a new entity ID that has never been used before.
CScriptValRooted m_MapSettings