Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CCmpAIManager.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 
21 #include "ICmpAIManager.h"
22 
24 
25 #include "graphics/Terrain.h"
26 #include "lib/timer.h"
27 #include "lib/tex/tex.h"
29 #include "ps/CLogger.h"
30 #include "ps/Filesystem.h"
31 #include "ps/Util.h"
44 
45 /**
46  * @file
47  * Player AI interface.
48  * AI is primarily scripted, and the CCmpAIManager component defined here
49  * takes care of managing all the scripts.
50  *
51  * To avoid slow AI scripts causing jerky rendering, they are run in a background
52  * thread (maintained by CAIWorker) so that it's okay if they take a whole simulation
53  * turn before returning their results (though preferably they shouldn't use nearly
54  * that much CPU).
55  *
56  * CCmpAIManager grabs the world state after each turn (making use of AIInterface.js
57  * and AIProxy.js to decide what data to include) then passes it to CAIWorker.
58  * The AI scripts will then run asynchronously and return a list of commands to execute.
59  * Any attempts to read the command list (including indirectly via serialization)
60  * will block until it's actually completed, so the rest of the engine should avoid
61  * reading it for as long as possible.
62  *
63  * JS values are passed between the game and AI threads using ScriptInterface::StructuredClone.
64  *
65  * TODO: actually the thread isn't implemented yet, because performance hasn't been
66  * sufficiently problematic to justify the complexity yet, but the CAIWorker interface
67  * is designed to hopefully support threading when we want it.
68  */
69 
70 /**
71  * Implements worker thread for CCmpAIManager.
72  */
73 class CAIWorker
74 {
75 private:
76  class CAIPlayer
77  {
79  public:
80  CAIPlayer(CAIWorker& worker, const std::wstring& aiName, player_id_t player, uint8_t difficulty,
81  const shared_ptr<ScriptRuntime>& runtime, boost::rand48& rng) :
82  m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty), m_ScriptInterface("Engine", "AI", runtime)
83  {
84  m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
85 
88 
89  m_ScriptInterface.RegisterFunction<void, std::wstring, CAIPlayer::IncludeModule>("IncludeModule");
90  m_ScriptInterface.RegisterFunction<void, CAIPlayer::DumpHeap>("DumpHeap");
91  m_ScriptInterface.RegisterFunction<void, CAIPlayer::ForceGC>("ForceGC");
92  m_ScriptInterface.RegisterFunction<void, CScriptValRooted, CAIPlayer::PostCommand>("PostCommand");
93 
94  m_ScriptInterface.RegisterFunction<void, std::wstring, std::vector<u32>, u32, u32, u32, CAIPlayer::DumpImage>("DumpImage");
95 
96  m_ScriptInterface.RegisterFunction<void, std::wstring, CScriptVal, CAIPlayer::RegisterSerializablePrototype>("RegisterSerializablePrototype");
97  }
98 
100  {
101  // Clean up rooted objects before destroying their script context
103  m_Commands.clear();
104  }
105 
106  static void IncludeModule(void* cbdata, std::wstring name)
107  {
108  CAIPlayer* self = static_cast<CAIPlayer*> (cbdata);
109 
110  self->LoadScripts(name);
111  }
112  static void DumpHeap(void* cbdata)
113  {
114  CAIPlayer* self = static_cast<CAIPlayer*> (cbdata);
115 
116  //std::cout << JS_GetGCParameter(self->m_ScriptInterface.GetRuntime(), JSGC_BYTES) << std::endl;
117  self->m_ScriptInterface.DumpHeap();
118  }
119  static void ForceGC(void* cbdata)
120  {
121  CAIPlayer* self = static_cast<CAIPlayer*> (cbdata);
122 
123  JS_GC(self->m_ScriptInterface.GetContext());
124  }
125  static void PostCommand(void* cbdata, CScriptValRooted cmd)
126  {
127  CAIPlayer* self = static_cast<CAIPlayer*> (cbdata);
128 
129  self->m_Commands.push_back(self->m_ScriptInterface.WriteStructuredClone(cmd.get()));
130  }
131 
132  /**
133  * Debug function for AI scripts to dump 2D array data (e.g. terrain tile weights).
134  * TODO: check if this needs to be here too.
135  */
136  static void DumpImage(void* UNUSED(cbdata), std::wstring name, std::vector<u32> data, u32 w, u32 h, u32 max)
137  {
138  // TODO: this is totally not threadsafe.
139 
140  VfsPath filename = L"screenshots/aidump/" + name;
141 
142  if (data.size() != w*h)
143  {
144  debug_warn(L"DumpImage: data size doesn't match w*h");
145  return;
146  }
147 
148  if (max == 0)
149  {
150  debug_warn(L"DumpImage: max must not be 0");
151  return;
152  }
153 
154  const size_t bpp = 8;
155  int flags = TEX_BOTTOM_UP|TEX_GREY;
156 
157  const size_t img_size = w * h * bpp/8;
158  const size_t hdr_size = tex_hdr_size(filename);
159  shared_ptr<u8> buf;
160  AllocateAligned(buf, hdr_size+img_size, maxSectorSize);
161  Tex t;
162  if (tex_wrap(w, h, bpp, flags, buf, hdr_size, &t) < 0)
163  return;
164 
165  u8* img = buf.get() + hdr_size;
166  for (size_t i = 0; i < data.size(); ++i)
167  img[i] = (u8)((data[i] * 255) / max);
168 
169  tex_write(&t, filename);
170  tex_free(&t);
171  }
172 
173  static void RegisterSerializablePrototype(void* cbdata, std::wstring name, CScriptVal proto)
174  {
175  CAIPlayer* self = static_cast<CAIPlayer*> (cbdata);
176  // Add our player number to avoid name conflicts with other prototypes
177  // TODO: it would be better if serializable prototypes were stored in ScriptInterfaces
178  // and then each serializer would access those matching its own context, but that's
179  // not possible with AIs sharing data across contexts
180  std::wstringstream protoID;
181  protoID << self->m_Player << L"." << name.c_str();
182  self->m_Worker.RegisterSerializablePrototype(protoID.str(), proto);
183  }
184 
185  bool LoadScripts(const std::wstring& moduleName)
186  {
187  // Ignore modules that are already loaded
188  if (m_LoadedModules.find(moduleName) != m_LoadedModules.end())
189  return true;
190 
191  // Mark this as loaded, to prevent it recursively loading itself
192  m_LoadedModules.insert(moduleName);
193 
194  // Load and execute *.js
195  VfsPaths pathnames;
196  vfs::GetPathnames(g_VFS, L"simulation/ai/" + moduleName + L"/", L"*.js", pathnames);
197  for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
198  {
200  {
201  LOGERROR(L"Failed to load script %ls", it->string().c_str());
202  return false;
203  }
204  }
205 
206  return true;
207  }
208 
209  bool Initialise(bool callConstructor)
210  {
211  if (!LoadScripts(m_AIName))
212  return false;
213 
214  OsPath path = L"simulation/ai/" + m_AIName + L"/data.json";
215  CScriptValRooted metadata = m_Worker.LoadMetadata(path);
216  if (metadata.uninitialised())
217  {
218  LOGERROR(L"Failed to create AI player: can't find %ls", path.string().c_str());
219  return false;
220  }
221 
222  // Get the constructor name from the metadata
223  std::string constructor;
224  if (!m_ScriptInterface.GetProperty(metadata.get(), "constructor", constructor))
225  {
226  LOGERROR(L"Failed to create AI player: %ls: missing 'constructor'", path.string().c_str());
227  return false;
228  }
229 
230  // Get the constructor function from the loaded scripts
231  CScriptVal ctor;
232  if (!m_ScriptInterface.GetProperty(m_ScriptInterface.GetGlobalObject(), constructor.c_str(), ctor)
233  || ctor.undefined())
234  {
235  LOGERROR(L"Failed to create AI player: %ls: can't find constructor '%hs'", path.string().c_str(), constructor.c_str());
236  return false;
237  }
238 
239  m_ScriptInterface.GetProperty(metadata.get(), "useShared", m_UseSharedComponent);
240 
241  CScriptVal obj;
242 
243  if (callConstructor)
244  {
245  // Set up the data to pass as the constructor argument
246  CScriptVal settings;
247  m_ScriptInterface.Eval(L"({})", settings);
248  m_ScriptInterface.SetProperty(settings.get(), "player", m_Player, false);
249  m_ScriptInterface.SetProperty(settings.get(), "difficulty", m_Difficulty, false);
251  m_ScriptInterface.SetProperty(settings.get(), "templates", m_Worker.m_EntityTemplates, false);
252 
253  obj = m_ScriptInterface.CallConstructor(ctor.get(), settings.get());
254  }
255  else
256  {
257  // For deserialization, we want to create the object with the correct prototype
258  // but don't want to actually run the constructor again
259  // XXX: actually we don't currently use this path for deserialization - maybe delete it?
261  }
262 
263  if (obj.undefined())
264  {
265  LOGERROR(L"Failed to create AI player: %ls: error calling constructor '%hs'", path.string().c_str(), constructor.c_str());
266  return false;
267  }
268 
270  return true;
271  }
272 
274  {
275  m_Commands.clear();
276  m_ScriptInterface.CallFunctionVoid(m_Obj.get(), "HandleMessage", state);
277  }
278  // overloaded with a sharedAI part.
279  // javascript can handle both natively on the same function.
281  {
282  m_Commands.clear();
283  m_ScriptInterface.CallFunctionVoid(m_Obj.get(), "HandleMessage", state, SharedAI);
284  }
286  {
287  m_Commands.clear();
288  m_ScriptInterface.CallFunctionVoid(m_Obj.get(), "InitWithSharedScript", state, SharedAI);
289  }
290 
292  std::wstring m_AIName;
296 
299  std::vector<shared_ptr<ScriptInterface::StructuredClone> > m_Commands;
300  std::set<std::wstring> m_LoadedModules;
301  };
302 
303 public:
305  {
307  std::vector<shared_ptr<ScriptInterface::StructuredClone> > commands;
308  };
309 
311  // TODO: Passing a 32 MB argument to CreateRuntime() is a temporary fix
312  // to prevent frequent AI out-of-memory crashes. The argument should be
313  // removed as soon whenever the new pathfinder is committed
314  // And the AIs can stop relying on their own little hands.
315  m_ScriptRuntime(ScriptInterface::CreateRuntime(33554432)),
316  m_ScriptInterface("Engine", "AI", m_ScriptRuntime),
317  m_TurnNum(0),
318  m_CommandsComputed(true),
320  m_HasSharedComponent(false)
321  {
322 
323  // TODO: ought to seed the RNG (in a network-synchronised way) before we use it
326 
328 
329  m_ScriptInterface.RegisterFunction<void, CScriptValRooted, CAIWorker::PostCommand>("PostCommand");
330  m_ScriptInterface.RegisterFunction<void, CAIWorker::DumpHeap>("DumpHeap");
331  m_ScriptInterface.RegisterFunction<void, CAIWorker::ForceGC>("ForceGC");
332 
333  m_ScriptInterface.RegisterFunction<void, std::wstring, std::vector<u32>, u32, u32, u32, CAIWorker::DumpImage>("DumpImage");
334  }
335 
337  {
338  // Clear rooted script values before destructing the script interface
340  m_PlayerMetadata.clear();
341  m_Players.clear();
342  m_GameState.reset();
345  }
346 
347  // This is called by AIs if they use the v3 API.
348  // If the AIs originate the call, cbdata is not NULL.
349  // If the shared component does, it is, so it must not be taken into account.
350  static void PostCommand(void* cbdata, CScriptValRooted cmd)
351  {
352  if (cbdata == NULL) {
353  debug_warn(L"Warning: the shared component has tried to push an engine command. Ignoring.");
354  return;
355  }
356  CAIPlayer* self = static_cast<CAIPlayer*> (cbdata);
357  self->m_Commands.push_back(self->m_ScriptInterface.WriteStructuredClone(cmd.get()));
358  }
359  // The next two ought to be implmeneted someday but for now as it returns "null" it can't
360  static void DumpHeap(void* cbdata)
361  {
362  if (cbdata == NULL) {
363  debug_warn(L"Warning: the shared component has asked for DumpHeap. Ignoring.");
364  return;
365  }
366  CAIWorker* self = static_cast<CAIWorker*> (cbdata);
367  self->m_ScriptInterface.DumpHeap();
368  }
369  static void ForceGC(void* cbdata)
370  {
371  if (cbdata == NULL) {
372  debug_warn(L"Warning: the shared component has asked for ForceGC. Ignoring.");
373  return;
374  }
375  CAIWorker* self = static_cast<CAIWorker*> (cbdata);
376  PROFILE3("AI compute GC");
377  JS_GC(self->m_ScriptInterface.GetContext());
378  }
379 
380  /**
381  * Debug function for AI scripts to dump 2D array data (e.g. terrain tile weights).
382  */
383  static void DumpImage(void* UNUSED(cbdata), std::wstring name, std::vector<u32> data, u32 w, u32 h, u32 max)
384  {
385  // TODO: this is totally not threadsafe.
386  VfsPath filename = L"screenshots/aidump/" + name;
387 
388  if (data.size() != w*h)
389  {
390  debug_warn(L"DumpImage: data size doesn't match w*h");
391  return;
392  }
393 
394  if (max == 0)
395  {
396  debug_warn(L"DumpImage: max must not be 0");
397  return;
398  }
399 
400  const size_t bpp = 8;
401  int flags = TEX_BOTTOM_UP|TEX_GREY;
402 
403  const size_t img_size = w * h * bpp/8;
404  const size_t hdr_size = tex_hdr_size(filename);
405  shared_ptr<u8> buf;
406  AllocateAligned(buf, hdr_size+img_size, maxSectorSize);
407  Tex t;
408  if (tex_wrap(w, h, bpp, flags, buf, hdr_size, &t) < 0)
409  return;
410 
411  u8* img = buf.get() + hdr_size;
412  for (size_t i = 0; i < data.size(); ++i)
413  img[i] = (u8)((data[i] * 255) / max);
414 
415  tex_write(&t, filename);
416  tex_free(&t);
417  }
418 
419  bool TryLoadSharedComponent(bool hasTechs)
420  {
421  // we don't need to load it.
423  return false;
424 
425  // reset the value so it can be used to determine if we actually initialized it.
426  m_HasSharedComponent = false;
427 
428  VfsPaths sharedPathnames;
429  // Check for "shared" module.
430  vfs::GetPathnames(g_VFS, L"simulation/ai/common-api-v3/", L"*.js", sharedPathnames);
431  for (VfsPaths::iterator it = sharedPathnames.begin(); it != sharedPathnames.end(); ++it)
432  {
434  {
435  LOGERROR(L"Failed to load shared script %ls", it->string().c_str());
436  return false;
437  }
438  m_HasSharedComponent = true;
439  }
441  return false;
442 
443  // mainly here for the error messages
444  OsPath path = L"simulation/ai/common-api-v2/";
445 
446  // Constructor name is SharedScript
447  CScriptVal ctor;
448  if (!m_ScriptInterface.GetProperty(m_ScriptInterface.GetGlobalObject(), "SharedScript", ctor)
449  || ctor.undefined())
450  {
451  LOGERROR(L"Failed to create shared AI component: %ls: can't find constructor '%hs'", path.string().c_str(), "SharedScript");
452  return false;
453  }
454 
455  if (hasTechs)
456  {
457  // Set up the data to pass as the constructor argument
458  CScriptVal settings;
459  m_ScriptInterface.Eval(L"({})", settings);
460  CScriptVal playersID;
461  m_ScriptInterface.Eval(L"({})", playersID);
462 
463  for (size_t i = 0; i < m_Players.size(); ++i)
464  {
465  jsval val = m_ScriptInterface.ToJSVal(m_ScriptInterface.GetContext(), m_Players[i]->m_Player);
466  m_ScriptInterface.SetPropertyInt(playersID.get(), i, CScriptVal(val), true);
467  }
468 
469  m_ScriptInterface.SetProperty(settings.get(), "players", playersID);
470 
472  m_ScriptInterface.SetProperty(settings.get(), "templates", m_EntityTemplates, false);
473 
474  m_ScriptInterface.SetProperty(settings.get(), "techTemplates", m_TechTemplates, false);
475 
477  }
478  else
479  {
480  // won't get the tech templates directly.
481  // Set up the data to pass as the constructor argument
482  CScriptVal settings;
483  m_ScriptInterface.Eval(L"({})", settings);
484  CScriptVal playersID;
485  m_ScriptInterface.Eval(L"({})", playersID);
486  for (size_t i = 0; i < m_Players.size(); ++i)
487  {
488  jsval val = m_ScriptInterface.ToJSVal(m_ScriptInterface.GetContext(), m_Players[i]->m_Player);
489  m_ScriptInterface.SetPropertyInt(playersID.get(), i, CScriptVal(val), true);
490  }
491 
492  m_ScriptInterface.SetProperty(settings.get(), "players", playersID);
493 
494  CScriptVal m_fakeTech;
495  m_ScriptInterface.Eval("({})", m_fakeTech);
496  m_ScriptInterface.SetProperty(settings.get(), "techTemplates", m_fakeTech, false);
497 
499  m_ScriptInterface.SetProperty(settings.get(), "templates", m_EntityTemplates, false);
501  }
502 
503  if (m_SharedAIObj.undefined())
504  {
505  LOGERROR(L"Failed to create shared AI component: %ls: error calling constructor '%hs'", path.string().c_str(), "SharedScript");
506  return false;
507  }
508 
509  return true;
510  }
511 
512  bool AddPlayer(const std::wstring& aiName, player_id_t player, uint8_t difficulty, bool callConstructor)
513  {
514  shared_ptr<CAIPlayer> ai(new CAIPlayer(*this, aiName, player, difficulty, m_ScriptRuntime, m_RNG));
515  if (!ai->Initialise(callConstructor))
516  return false;
517 
518  // this will be set to true if we need to load the shared Component.
520  m_HasSharedComponent = ai->m_UseSharedComponent;
521 
523 
524  m_Players.push_back(ai);
525 
526  return true;
527  }
528 
529  bool RunGamestateInit(const shared_ptr<ScriptInterface::StructuredClone>& gameState, const Grid<u16>& passabilityMap, const Grid<u8>& territoryMap)
530  {
531  // this will be run last by InitGame.Js, passing the full game representation.
532  // For now it will run for the shared Component.
533  // This is NOT run during deserialization.
535  JSContext* cx = m_ScriptInterface.GetContext();
536 
540  {
541  m_ScriptInterface.SetProperty(state.get(), "passabilityMap", m_PassabilityMapVal, true);
542  m_ScriptInterface.SetProperty(state.get(), "territoryMap", m_TerritoryMapVal, true);
543 
546 
547  for (size_t i = 0; i < m_Players.size(); ++i)
548  {
549  if (m_HasSharedComponent && m_Players[i]->m_UseSharedComponent)
550  m_Players[i]->InitWithSharedScript(state,m_SharedAIObj);
551  }
552  }
553 
554  return true;
555  }
556  void StartComputation(const shared_ptr<ScriptInterface::StructuredClone>& gameState, const Grid<u16>& passabilityMap, const Grid<u8>& territoryMap, bool territoryMapDirty)
557  {
559 
560  m_GameState = gameState;
561 
562  if (passabilityMap.m_DirtyID != m_PassabilityMap.m_DirtyID)
563  {
564  m_PassabilityMap = passabilityMap;
565 
566  JSContext* cx = m_ScriptInterface.GetContext();
568  }
569 
570  if (territoryMapDirty)
571  {
572  m_TerritoryMap = territoryMap;
573 
574  JSContext* cx = m_ScriptInterface.GetContext();
576  }
577 
578  m_CommandsComputed = false;
579  }
580 
582  {
583  if (!m_CommandsComputed)
584  {
586  m_CommandsComputed = true;
587  }
588  }
589 
590  void GetCommands(std::vector<SCommandSets>& commands)
591  {
593 
594  commands.clear();
595  commands.resize(m_Players.size());
596  for (size_t i = 0; i < m_Players.size(); ++i)
597  {
598  commands[i].player = m_Players[i]->m_Player;
599  commands[i].commands = m_Players[i]->m_Commands;
600  }
601  }
602 
603  void RegisterTechTemplates(const shared_ptr<ScriptInterface::StructuredClone>& techTemplates) {
604  JSContext* cx = m_ScriptInterface.GetContext();
606  }
607 
608  void LoadEntityTemplates(const std::vector<std::pair<std::string, const CParamNode*> >& templates)
609  {
611 
613 
614  for (size_t i = 0; i < templates.size(); ++i)
615  {
616  jsval val = templates[i].second->ToJSVal(m_ScriptInterface.GetContext(), false);
617  m_ScriptInterface.SetProperty(m_EntityTemplates.get(), templates[i].first.c_str(), CScriptVal(val), true);
618  }
619 
620  // Since the template data is shared between AI players, freeze it
621  // to stop any of them changing it and confusing the other players
623  }
624 
625  void Serialize(std::ostream& stream, bool isDebug)
626  {
628 
629  if (isDebug)
630  {
631  CDebugSerializer serializer(m_ScriptInterface, stream);
632  serializer.Indent(4);
633  SerializeState(serializer);
634  }
635  else
636  {
637  CStdSerializer serializer(m_ScriptInterface, stream);
638  // TODO: see comment in Deserialize()
640  SerializeState(serializer);
641  }
642  }
643 
644  void SerializeState(ISerializer& serializer)
645  {
646  std::stringstream rngStream;
647  rngStream << m_RNG;
648  serializer.StringASCII("rng", rngStream.str(), 0, 32);
649 
650  serializer.NumberU32_Unbounded("turn", m_TurnNum);
651 
652  serializer.NumberU32_Unbounded("num ais", (u32)m_Players.size());
653 
654  serializer.Bool("useSharedScript", m_HasSharedComponent);
656  {
657  CScriptVal sharedData;
658  if (!m_ScriptInterface.CallFunction(m_SharedAIObj.get(), "Serialize", sharedData))
659  LOGERROR(L"AI shared script Serialize call failed");
660  serializer.ScriptVal("sharedData", sharedData);
661  }
662  for (size_t i = 0; i < m_Players.size(); ++i)
663  {
664  serializer.String("name", m_Players[i]->m_AIName, 1, 256);
665  serializer.NumberI32_Unbounded("player", m_Players[i]->m_Player);
666  serializer.NumberU8_Unbounded("difficulty", m_Players[i]->m_Difficulty);
667 
668  serializer.NumberU32_Unbounded("num commands", (u32)m_Players[i]->m_Commands.size());
669  for (size_t j = 0; j < m_Players[i]->m_Commands.size(); ++j)
670  {
672  serializer.ScriptVal("command", val);
673  }
674 
675  bool hasCustomSerialize = m_ScriptInterface.HasProperty(m_Players[i]->m_Obj.get(), "Serialize");
676  if (hasCustomSerialize)
677  {
678  CScriptVal scriptData;
679  if (!m_ScriptInterface.CallFunction(m_Players[i]->m_Obj.get(), "Serialize", scriptData))
680  LOGERROR(L"AI script Serialize call failed");
681  serializer.ScriptVal("data", scriptData);
682  }
683  else
684  {
685  serializer.ScriptVal("data", m_Players[i]->m_Obj.get());
686  }
687  }
688  }
689 
690  void Deserialize(std::istream& stream)
691  {
692  ENSURE(m_CommandsComputed); // deserializing while we're still actively computing would be bad
693 
694  CStdDeserializer deserializer(m_ScriptInterface, stream);
695 
696  m_PlayerMetadata.clear();
697  m_Players.clear();
698 
699  std::string rngString;
700  std::stringstream rngStream;
701  deserializer.StringASCII("rng", rngString, 0, 32);
702  rngStream << rngString;
703  rngStream >> m_RNG;
704 
705  deserializer.NumberU32_Unbounded("turn", m_TurnNum);
706 
707  uint32_t numAis;
708  deserializer.NumberU32_Unbounded("num ais", numAis);
709 
710  deserializer.Bool("useSharedScript", m_HasSharedComponent);
711  TryLoadSharedComponent(false);
713  {
714  CScriptVal sharedData;
715  deserializer.ScriptVal("sharedData", sharedData);
716  if (!m_ScriptInterface.CallFunctionVoid(m_SharedAIObj.get(), "Deserialize", sharedData))
717  LOGERROR(L"AI shared script Deserialize call failed");
718  }
719 
720  for (size_t i = 0; i < numAis; ++i)
721  {
722  std::wstring name;
723  player_id_t player;
724  uint8_t difficulty;
725  deserializer.String("name", name, 1, 256);
726  deserializer.NumberI32_Unbounded("player", player);
727  deserializer.NumberU8_Unbounded("difficulty",difficulty);
728  if (!AddPlayer(name, player, difficulty, true))
730 
731  uint32_t numCommands;
732  deserializer.NumberU32_Unbounded("num commands", numCommands);
733  m_Players.back()->m_Commands.reserve(numCommands);
734  for (size_t j = 0; j < numCommands; ++j)
735  {
736  CScriptVal val;
737  deserializer.ScriptVal("command", val);
738  m_Players.back()->m_Commands.push_back(m_ScriptInterface.WriteStructuredClone(val.get()));
739  }
740 
741  // TODO: this is yucky but necessary while the AIs are sharing data between contexts;
742  // ideally a new (de)serializer instance would be created for each player
743  // so they would have a single, consistent script context to use and serializable
744  // prototypes could be stored in their ScriptInterface
746 
747  bool hasCustomDeserialize = m_ScriptInterface.HasProperty(m_Players.back()->m_Obj.get(), "Deserialize");
748  if (hasCustomDeserialize)
749  {
750  CScriptVal scriptData;
751  deserializer.ScriptVal("data", scriptData);
752  if (m_Players[i]->m_UseSharedComponent)
753  {
754  if (!m_ScriptInterface.CallFunctionVoid(m_Players.back()->m_Obj.get(), "Deserialize", scriptData, m_SharedAIObj))
755  LOGERROR(L"AI script Deserialize call failed");
756  }
757  else if (!m_ScriptInterface.CallFunctionVoid(m_Players.back()->m_Obj.get(), "Deserialize", scriptData))
758  {
759  LOGERROR(L"AI script deserialize() call failed");
760  }
761  }
762  else
763  {
764  deserializer.ScriptVal("data", m_Players.back()->m_Obj);
765  }
766  }
767  }
768 
770  {
771  return m_Players.size();
772  }
773 
774  void RegisterSerializablePrototype(std::wstring name, CScriptVal proto)
775  {
776  // Require unique prototype and name (for reverse lookup)
777  // TODO: this is yucky - see comment in Deserialize()
778  JSObject* obj = JSVAL_TO_OBJECT(proto.get());
779  std::pair<std::map<JSObject*, std::wstring>::iterator, bool> ret1 = m_SerializablePrototypes.insert(std::make_pair(obj, name));
780  std::pair<std::map<std::wstring, JSObject*>::iterator, bool> ret2 = m_DeserializablePrototypes.insert(std::make_pair(name, obj));
781  if (!ret1.second || !ret2.second)
782  LOGERROR(L"RegisterSerializablePrototype called with same prototype multiple times: p=%p n='%ls'", obj, name.c_str());
783  }
784 
785 private:
787  {
788  if (m_PlayerMetadata.find(path) == m_PlayerMetadata.end())
789  {
790  // Load and cache the AI player metadata
792  }
793 
794  return m_PlayerMetadata[path];
795  }
796 
798  {
799  if (m_Players.size() == 0)
800  {
801  // Run the GC every so often.
802  // (This isn't particularly necessary, but it makes profiling clearer
803  // since it avoids random GC delays while running other scripts)
804  if (m_TurnNum++ % 50 == 0)
805  {
806  PROFILE3("AI compute GC");
808  }
809  return;
810  }
811 
812  // Deserialize the game state, to pass to the AI's HandleMessage
814  {
815  PROFILE3("AI compute read state");
817  m_ScriptInterface.SetProperty(state.get(), "passabilityMap", m_PassabilityMapVal, true);
818  m_ScriptInterface.SetProperty(state.get(), "territoryMap", m_TerritoryMapVal, true);
819  }
820 
821  // It would be nice to do
822  // m_ScriptInterface.FreezeObject(state.get(), true);
823  // to prevent AI scripts accidentally modifying the state and
824  // affecting other AI scripts they share it with. But the performance
825  // cost is far too high, so we won't do that.
826  // If there is a shared component, run it
827 
829  {
830  PROFILE3("AI run shared component");
832  }
833 
834  for (size_t i = 0; i < m_Players.size(); ++i)
835  {
836  PROFILE3("AI script");
837  PROFILE2_ATTR("player: %d", m_Players[i]->m_Player);
838  PROFILE2_ATTR("script: %ls", m_Players[i]->m_AIName.c_str());
839  if (m_HasSharedComponent && m_Players[i]->m_UseSharedComponent)
840  m_Players[i]->Run(state,m_SharedAIObj);
841  else
842  m_Players[i]->Run(state);
843  }
844 
845  // Run GC if we are about to overflow
846  if (JS_GetGCParameter(m_ScriptInterface.GetRuntime(), JSGC_BYTES) > 33000000)
847  {
848  PROFILE3("AI compute GC");
849 
850  JS_GC(m_ScriptInterface.GetContext());
851  }
852 
853  // Run the GC every so often.
854  // (This isn't particularly necessary, but it makes profiling clearer
855  // since it avoids random GC delays while running other scripts)
856  /*if (m_TurnNum++ % 20 == 0)
857  {
858  PROFILE3("AI compute GC");
859  m_ScriptInterface.MaybeGC();
860  }*/
861  }
862 
863  shared_ptr<ScriptRuntime> m_ScriptRuntime;
865  boost::rand48 m_RNG;
867 
871 
872  std::map<VfsPath, CScriptValRooted> m_PlayerMetadata;
873  std::vector<shared_ptr<CAIPlayer> > m_Players; // use shared_ptr just to avoid copying
874 
877  std::vector<SCommandSets> m_Commands;
878 
879  shared_ptr<ScriptInterface::StructuredClone> m_GameState;
884 
886 
887  std::map<JSObject*, std::wstring> m_SerializablePrototypes;
888  std::map<std::wstring, JSObject*> m_DeserializablePrototypes;
889 };
890 
891 
892 /**
893  * Implementation of ICmpAIManager.
894  */
896 {
897 public:
898  static void ClassInit(CComponentManager& componentManager)
899  {
900  componentManager.SubscribeToMessageType(MT_ProgressiveLoad);
901  }
902 
904 
905  static std::string GetSchema()
906  {
907  return "<a:component type='system'/><empty/>";
908  }
909 
910  virtual void Init(const CParamNode& UNUSED(paramNode))
911  {
913 
915  }
916 
917  virtual void Deinit()
918  {
919  }
920 
921  virtual void Serialize(ISerializer& serialize)
922  {
923  // Because the AI worker uses its own ScriptInterface, we can't use the
924  // ISerializer (which was initialised with the simulation ScriptInterface)
925  // directly. So we'll just grab the ISerializer's stream and write to it
926  // with an independent serializer.
927 
928  m_Worker.Serialize(serialize.GetStream(), serialize.IsDebug());
929  }
930 
931  virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
932  {
933  Init(paramNode);
934 
936 
937  m_Worker.Deserialize(deserialize.GetStream());
938  }
939 
940  virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
941  {
942  switch (msg.GetType())
943  {
944  case MT_ProgressiveLoad:
945  {
946  const CMessageProgressiveLoad& msgData = static_cast<const CMessageProgressiveLoad&> (msg);
947 
948  *msgData.total += (int)m_TemplateNames.size();
949 
950  if (*msgData.progressed)
951  break;
952 
954  *msgData.progressed = true;
955 
956  *msgData.progress += (int)m_TemplateLoadedIdx;
957 
958  break;
959  }
960  }
961  }
962 
963  virtual void AddPlayer(std::wstring id, player_id_t player, uint8_t difficulty)
964  {
965  m_Worker.AddPlayer(id, player, difficulty, true);
966 
967  // AI players can cheat and see through FoW/SoD, since that greatly simplifies
968  // their implementation.
969  // (TODO: maybe cleverer AIs should be able to optionally retain FoW/SoD)
970  CmpPtr<ICmpRangeManager> cmpRangeManager(GetSystemEntity());
971  if (cmpRangeManager)
972  cmpRangeManager->SetLosRevealAll(player, true);
973  }
974 
975  virtual void TryLoadSharedComponent()
976  {
977  ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
978  // load the technology templates
979  CmpPtr<ICmpTechnologyTemplateManager> cmpTechTemplateManager(GetSystemEntity());
980  ENSURE(cmpTechTemplateManager);
981 
982  // Get the game state from AIInterface
983  CScriptVal techTemplates = cmpTechTemplateManager->GetAllTechs();
984 
985  m_Worker.RegisterTechTemplates(scriptInterface.WriteStructuredClone(techTemplates.get()));
987  }
988 
989  virtual void RunGamestateInit()
990  {
991  ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
992 
993  CmpPtr<ICmpAIInterface> cmpAIInterface(GetSystemEntity());
994  ENSURE(cmpAIInterface);
995 
996  // Get the game state from AIInterface
997  CScriptVal state = cmpAIInterface->GetFullRepresentation();
998 
999  // Get the passability data
1000  Grid<u16> dummyGrid;
1001  const Grid<u16>* passabilityMap = &dummyGrid;
1002  CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
1003  if (cmpPathfinder)
1004  passabilityMap = &cmpPathfinder->GetPassabilityGrid();
1005 
1006  // Get the territory data
1007  // Since getting the territory grid can trigger a recalculation, we check NeedUpdate first
1008  Grid<u8> dummyGrid2;
1009  const Grid<u8>* territoryMap = &dummyGrid2;
1010  CmpPtr<ICmpTerritoryManager> cmpTerritoryManager(GetSystemEntity());
1011  if (cmpTerritoryManager && cmpTerritoryManager->NeedUpdate(&m_TerritoriesDirtyID))
1012  {
1013  territoryMap = &cmpTerritoryManager->GetTerritoryGrid();
1014  }
1015 
1016  LoadPathfinderClasses(state);
1017 
1018  m_Worker.RunGamestateInit(scriptInterface.WriteStructuredClone(state.get()), *passabilityMap, *territoryMap);
1019  }
1020 
1021  virtual void StartComputation()
1022  {
1023  PROFILE("AI setup");
1024 
1026 
1027  ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
1028 
1029  if (m_Worker.getPlayerSize() == 0)
1030  return;
1031 
1032  CmpPtr<ICmpAIInterface> cmpAIInterface(GetSystemEntity());
1033  ENSURE(cmpAIInterface);
1034 
1035  // Get the game state from AIInterface
1036  CScriptVal state = cmpAIInterface->GetRepresentation();
1037 
1038  // Get the passability data
1039  Grid<u16> dummyGrid;
1040  const Grid<u16>* passabilityMap = &dummyGrid;
1041  CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
1042  if (cmpPathfinder)
1043  passabilityMap = &cmpPathfinder->GetPassabilityGrid();
1044 
1045  // Get the territory data
1046  // Since getting the territory grid can trigger a recalculation, we check NeedUpdate first
1047  bool territoryMapDirty = false;
1048  Grid<u8> dummyGrid2;
1049  const Grid<u8>* territoryMap = &dummyGrid2;
1050  CmpPtr<ICmpTerritoryManager> cmpTerritoryManager(GetSystemEntity());
1051  if (cmpTerritoryManager && cmpTerritoryManager->NeedUpdate(&m_TerritoriesDirtyID))
1052  {
1053  territoryMap = &cmpTerritoryManager->GetTerritoryGrid();
1054  territoryMapDirty = true;
1055  }
1056 
1057  LoadPathfinderClasses(state);
1058 
1059  m_Worker.StartComputation(scriptInterface.WriteStructuredClone(state.get()), *passabilityMap, *territoryMap, territoryMapDirty);
1060  }
1061 
1062  virtual void PushCommands()
1063  {
1064  ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
1065 
1066  std::vector<CAIWorker::SCommandSets> commands;
1067  m_Worker.GetCommands(commands);
1068 
1069  CmpPtr<ICmpCommandQueue> cmpCommandQueue(GetSystemEntity());
1070  if (!cmpCommandQueue)
1071  return;
1072 
1073  for (size_t i = 0; i < commands.size(); ++i)
1074  {
1075  for (size_t j = 0; j < commands[i].commands.size(); ++j)
1076  {
1077  cmpCommandQueue->PushLocalCommand(commands[i].player,
1078  scriptInterface.ReadStructuredClone(commands[i].commands[j]));
1079  }
1080  }
1081  }
1082 
1083 private:
1084  std::vector<std::string> m_TemplateNames;
1086  std::vector<std::pair<std::string, const CParamNode*> > m_Templates;
1088 
1090  {
1091  CmpPtr<ICmpTemplateManager> cmpTemplateManager(GetSystemEntity());
1092  ENSURE(cmpTemplateManager);
1093 
1094  m_TemplateNames = cmpTemplateManager->FindAllTemplates(false);
1095  m_TemplateLoadedIdx = 0;
1096  m_Templates.reserve(m_TemplateNames.size());
1097  }
1098 
1099  // Tries to load the next entity template. Returns true if we did some work.
1101  {
1102  if (m_TemplateLoadedIdx >= m_TemplateNames.size())
1103  return false;
1104 
1105  CmpPtr<ICmpTemplateManager> cmpTemplateManager(GetSystemEntity());
1106  ENSURE(cmpTemplateManager);
1107 
1108  const CParamNode* node = cmpTemplateManager->GetTemplateWithoutValidation(m_TemplateNames[m_TemplateLoadedIdx]);
1109  if (node)
1110  m_Templates.push_back(std::make_pair(m_TemplateNames[m_TemplateLoadedIdx], node));
1111 
1112  m_TemplateLoadedIdx++;
1113 
1114  // If this was the last template, send the data to the worker
1115  if (m_TemplateLoadedIdx == m_TemplateNames.size())
1117 
1118  return true;
1119  }
1120 
1122  {
1123  while (ContinueLoadEntityTemplates())
1124  {
1125  }
1126  }
1127 
1129  {
1130  CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
1131  if (!cmpPathfinder)
1132  return;
1133 
1134  ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
1135 
1136  CScriptVal classesVal;
1137  scriptInterface.Eval("({ pathfinderObstruction: 1, foundationObstruction: 2 })", classesVal);
1138 
1139  std::map<std::string, ICmpPathfinder::pass_class_t> classes = cmpPathfinder->GetPassabilityClasses();
1140  for (std::map<std::string, ICmpPathfinder::pass_class_t>::iterator it = classes.begin(); it != classes.end(); ++it)
1141  scriptInterface.SetProperty(classesVal.get(), it->first.c_str(), it->second, true);
1142 
1143  scriptInterface.SetProperty(state.get(), "passabilityClasses", classesVal, true);
1144  }
1145 
1147 };
1148 
1149 REGISTER_COMPONENT_TYPE(AIManager)
void GetCommands(std::vector< SCommandSets > &commands)
An entity initialisation parameter node.
Definition: ParamNode.h:112
void SubscribeToMessageType(MessageTypeId mtid)
Subscribe the current component type to the given message type.
virtual void StringASCII(const char *name, std::string &out, uint32_t minlength, uint32_t maxlength)
virtual std::map< std::string, pass_class_t > GetPassabilityClasses()=0
Get the list of all known passability classes.
void Serialize(std::ostream &stream, bool isDebug)
#define u8
Definition: types.h:39
bool Initialise(bool callConstructor)
bool ReplaceNondeterministicRNG(boost::rand48 &rng)
Replace the default JS random number geenrator with a seeded, network-sync&#39;d one. ...
bool uninitialised() const
Returns whether the value is uninitialised.
Definition: ScriptVal.cpp:63
Grid< u16 > m_PassabilityMap
virtual const Grid< u16 > & GetPassabilityGrid()=0
#define REGISTER_COMPONENT_TYPE(cname)
Definition: Component.h:30
virtual void Bool(const char *name, bool &out)
Helper templates for serializing/deserializing common objects.
static void PostCommand(void *cbdata, CScriptValRooted cmd)
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
std::map< std::wstring, JSObject * > m_DeserializablePrototypes
static void ForceGC(void *cbdata)
#define LOGERROR
Definition: CLogger.h:35
virtual bool IsDebug() const
Returns true if the serializer is being used in debug mode.
bool HasProperty(jsval obj, const char *name)
Check the named property has been defined on the given object.
void SetCallbackData(void *cbdata)
virtual CScriptVal GetAllTechs()=0
Serialization interface; see serialization overview.
Definition: ISerializer.h:120
static const uintptr_t maxSectorSize
Definition: alignment.h:82
bool AddPlayer(const std::wstring &aiName, player_id_t player, uint8_t difficulty, bool callConstructor)
bool CallFunctionVoid(jsval val, const char *name)
Call the named property on the given object, with void return type and 0 arguments.
virtual void Serialize(ISerializer &serialize)
indicates the image is 8bpp greyscale.
Definition: tex.h:178
CScriptValRooted m_SharedAIObj
static void PostCommand(void *cbdata, CScriptValRooted cmd)
void NumberU8_Unbounded(const char *name, uint8_t value)
Serialize a number.
Definition: ISerializer.h:150
static void IncludeModule(void *cbdata, std::wstring name)
const jsval & get() const
Returns the current value.
Definition: ScriptVal.h:38
ScriptInterface m_ScriptInterface
void WaitToFinishComputation()
virtual void NumberU32_Unbounded(const char *name, uint32_t &out)
Serialize to a human-readable YAML-like format.
size_t tex_hdr_size(const VfsPath &filename)
return the minimum header size (i.e.
Definition: tex.cpp:703
A trivial wrapper around a jsval.
Definition: ScriptVal.h:29
virtual const Grid< u8 > & GetTerritoryGrid()=0
For each tile, the TERRITORY_PLAYER_MASK bits are player ID; TERRITORY_CONNECTED_MASK is set if the t...
void ScriptVal(const char *name, jsval value)
Serialize a jsval.
Definition: ISerializer.cpp:95
JSRuntime * GetRuntime() const
virtual bool NeedUpdate(size_t *dirtyID)=0
bool FreezeObject(jsval obj, bool deep)
std::set< std::wstring > m_LoadedModules
bool LoadGlobalScripts()
Load global scripts that most script contexts need, located in the /globalscripts directory...
virtual CScriptVal GetFullRepresentation()=0
Returns a script object that represents the current world state, to be passed to AI scripts...
void StartComputation(const shared_ptr< ScriptInterface::StructuredClone > &gameState, const Grid< u16 > &passabilityMap, const Grid< u8 > &territoryMap, bool territoryMapDirty)
virtual void NumberU8_Unbounded(const char *name, uint8_t &out)
virtual void ScriptVal(const char *name, jsval &out)
Deserialize a jsval, replacing &#39;out&#39;.
virtual void AddPlayer(std::wstring id, player_id_t player, uint8_t difficulty)
Add a new AI player into the world, based on the AI script identified by id (corresponding to a subdi...
bool SetPropertyInt(jsval obj, int name, const T &value, bool constant=false, bool enumerate=true)
Set the integer-named property on the given object.
virtual void PushLocalCommand(player_id_t player, CScriptVal cmd)=0
Pushes a new command onto the local queue.
void Run(CScriptVal state)
flags &amp; TEX_ORIENTATION is a field indicating orientation, i.e.
Definition: tex.h:190
static void DumpImage(void *cbdata, std::wstring name, std::vector< u32 > data, u32 w, u32 h, u32 max)
Debug function for AI scripts to dump 2D array data (e.g.
int32_t player_id_t
valid player IDs are non-negative (see ICmpOwnership)
Definition: Player.h:24
ScriptInterface m_ScriptInterface
void DumpHeap()
Dump some memory heap debugging information to stderr.
virtual void Init(const CParamNode &paramNode)
jsval NewObjectFromConstructor(jsval ctor)
Create an object as with CallConstructor except don&#39;t actually execute the constructor function...
std::vector< std::pair< std::string, const CParamNode * > > m_Templates
void LoadPathfinderClasses(CScriptVal state)
size_t m_DirtyID
Definition: Grid.h:104
ScriptInterface & GetScriptInterface() const
Definition: SimContext.cpp:63
bool LoadScripts(const std::wstring &moduleName)
std::vector< std::string > m_TemplateNames
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
virtual void SetLosRevealAll(player_id_t player, bool enabled)=0
Set whether the whole map should be made visible to the given player.
size_t m_TemplateLoadedIdx
virtual int GetType() const =0
static void DumpHeap(void *cbdata)
void StringASCII(const char *name, const std::string &value, uint32_t minlength, uint32_t maxlength)
Serialize an ASCII string.
Definition: ISerializer.cpp:70
Implementation of ICmpAIManager.
CScriptValRooted m_TerritoryMapVal
Definition: path.h:75
void LoadEntityTemplates(const std::vector< std::pair< std::string, const CParamNode * > > &templates)
void SerializeState(ISerializer &serializer)
NONCOPYABLE(CAIPlayer)
virtual const CParamNode * GetTemplateWithoutValidation(std::string templateName)=0
Like GetTemplate, except without doing the XML validation (so it&#39;s faster but may return invalid temp...
boost::mt19937 rng
Random number generator (Boost Mersenne Twister)
Definition: Noise.cpp:34
void Indent(int spaces)
shared_ptr< ScriptRuntime > m_ScriptRuntime
CScriptValRooted ReadJSONFile(const VfsPath &path)
Read a JSON file.
const String & string() const
Definition: path.h:123
bool LoadGlobalScriptFile(const VfsPath &path)
Load and execute the given script in the global scope.
std::map< VfsPath, CScriptValRooted > m_PlayerMetadata
unsigned char uint8_t
Definition: wposix_types.h:51
virtual void Deinit()
virtual std::istream & GetStream()=0
Returns a stream which can be used to deserialize data directly.
static void ForceGC(void *cbdata)
std::vector< SCommandSets > m_Commands
virtual void TryLoadSharedComponent()
std::vector< shared_ptr< CAIPlayer > > m_Players
bool undefined() const
Returns whether the value is uninitialised or is JSVAL_VOID.
Definition: ScriptVal.cpp:58
bool Eval(const char *code)
static jsval ToJSVal(JSContext *cx, T const &val)
Convert a C++ type to a jsval.
virtual void SetSerializablePrototypes(std::map< JSObject *, std::wstring > &prototypes)
void Bool(const char *name, bool value)
Serialize a boolean.
Definition: ISerializer.h:199
void Run(CScriptVal state, CScriptValRooted SharedAI)
static void DumpImage(void *cbdata, std::wstring name, std::vector< u32 > data, u32 w, u32 h, u32 max)
Debug function for AI scripts to dump 2D array data (e.g.
std::vector< shared_ptr< ScriptInterface::StructuredClone > > commands
#define PROFILE(name)
Definition: Profile.h:195
virtual void Deserialize(const CParamNode &paramNode, IDeserializer &deserialize)
#define DEFAULT_COMPONENT_ALLOCATOR(cname)
Definition: Component.h:44
CAIWorker m_Worker
const CSimContext & GetSimContext() const
Definition: IComponent.h:52
virtual void PushCommands()
Call this at the start of a turn, to push the computed AI commands into the command queue...
void RegisterTechTemplates(const shared_ptr< ScriptInterface::StructuredClone > &techTemplates)
size_t m_TerritoriesDirtyID
CScriptValRooted m_EntityTemplates
void NumberU32_Unbounded(const char *name, uint32_t value)
Serialize a number.
Definition: ISerializer.h:171
static void RegisterSerializablePrototype(void *cbdata, std::wstring name, CScriptVal proto)
stores all data describing an image.
Definition: tex.h:210
Status tex_write(Tex *t, const VfsPath &filename)
Definition: Util.cpp:173
CScriptValRooted m_PassabilityMapVal
CScriptValRooted m_TechTemplates
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
bool RunGamestateInit(const shared_ptr< ScriptInterface::StructuredClone > &gameState, const Grid< u16 > &passabilityMap, const Grid< u8 > &territoryMap)
virtual void HandleMessage(const CMessage &msg, bool global)
virtual std::vector< std::string > FindAllTemplates(bool includeActors)=0
Returns a list of strings that could be validly passed as templateName to LoadTemplate.
void String(const char *name, const std::wstring &value, uint32_t minlength, uint32_t maxlength)
Serialize a Unicode string.
Definition: ISerializer.cpp:82
void Deserialize(std::istream &stream)
bool GetProperty(jsval obj, const char *name, T &out)
Get the named property on the given object.
void ForceLoadEntityTemplates()
CScriptValRooted LoadMetadata(const VfsPath &path)
CEntityHandle GetSystemEntity() const
Definition: IComponent.h:50
bool CallFunction(jsval val, const char *name, R &ret)
Call the named property on the given object, with return type R and 0 arguments.
shared_ptr< ScriptInterface::StructuredClone > m_GameState
#define PROFILE2_ATTR
Associates a string (with printf-style formatting) with the current region or event.
Definition: Profiler2.h:461
std::map< JSObject *, std::wstring > m_SerializablePrototypes
static void DumpHeap(void *cbdata)
bool m_CommandsComputed
virtual void RunGamestateInit()
static Status AllocateAligned(shared_ptr< T > &p, size_t size, size_t alignment=cacheLineSize)
Definition: shared_ptr.h:66
int getPlayerSize()
#define PROFILE3(name)
Definition: Profile.h:201
#define u32
Definition: types.h:41
Handle progressive loading of resources.
Definition: MessageTypes.h:171
Grid< u8 > m_TerritoryMap
static void ClassInit(CComponentManager &componentManager)
virtual void String(const char *name, std::wstring &out, uint32_t minlength, uint32_t maxlength)
bool ContinueLoadEntityTemplates()
void StartLoadEntityTemplates()
unsigned int uint32_t
Definition: wposix_types.h:53
jsval CallConstructor(jsval ctor, jsval arg)
Call a constructor function, equivalent to JS &quot;new ctor(arg)&quot;.
boost::rand48 m_RNG
bool m_HasLoadedEntityTemplates
Status tex_wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr< u8 > &data, size_t ofs, Tex *t)
store the given image data into a Tex object; this will be as if it had been loaded via tex_load...
Definition: tex.cpp:593
std::vector< VfsPath > VfsPaths
Definition: vfs_path.h:42
jsval get() const
Returns the current value (or JSVAL_VOID if uninitialised).
Definition: ScriptVal.cpp:45
Implements worker thread for CCmpAIManager.
Abstraction around a SpiderMonkey JSContext.
void NumberI32_Unbounded(const char *name, int32_t value)
Serialize a number.
Definition: ISerializer.h:176
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
virtual void StartComputation()
Call this at the end of a turn, to trigger AI computation which will be ready for the next turn...
bool undefined() const
Returns whether the value is JSVAL_VOID.
Definition: ScriptVal.h:43
virtual std::ostream & GetStream()=0
Returns a stream which can be used to serialize data directly.
void RegisterSerializablePrototype(std::wstring name, CScriptVal proto)
bool SetProperty(jsval obj, const char *name, const T &value, bool constant=false, bool enumerate=true)
Set the named property on the given object.
CAIPlayer(CAIWorker &worker, const std::wstring &aiName, player_id_t player, uint8_t difficulty, const shared_ptr< ScriptRuntime > &runtime, boost::rand48 &rng)
jsval ReadStructuredClone(const shared_ptr< StructuredClone > &ptr)
PIVFS g_VFS
Definition: Filesystem.cpp:30
bool TryLoadSharedComponent(bool hasTechs)
void InitWithSharedScript(CScriptVal state, CScriptValRooted SharedAI)
void PerformComputation()
bool m_HasSharedComponent
Status GetPathnames(const PIVFS &fs, const VfsPath &path, const wchar_t *filter, VfsPaths &pathnames)
Definition: vfs_util.cpp:40
virtual void SetSerializablePrototypes(std::map< std::wstring, JSObject * > &prototypes)
virtual CScriptVal GetRepresentation()=0
Returns a script object that represents the current world state, to be passed to AI scripts...
JSContext * GetContext() const
void MaybeGC()
MaybeGC tries to determine whether garbage collection in cx&#39;s runtime would free up enough memory to ...
CScriptValRooted m_Obj
void tex_free(Tex *t)
free all resources associated with the image and make further use of it impossible.
Definition: tex.cpp:610
std::vector< shared_ptr< ScriptInterface::StructuredClone > > m_Commands
static enum @41 state
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:34
virtual void NumberI32_Unbounded(const char *name, int32_t &out)
static std::string GetSchema()
shared_ptr< StructuredClone > WriteStructuredClone(jsval v)