Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MapGenerator.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 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 "MapGenerator.h"
21 
22 #include "lib/timer.h"
23 #include "ps/CLogger.h"
24 
25 
26 // TODO: what's a good default? perhaps based on map size
27 #define RMS_RUNTIME_SIZE 96 * 1024 * 1024
28 
29 
31 {
32  // If something happens before we initialize, that's a failure
33  m_Progress = -1;
34 }
35 
37 {
38  // Wait for thread to end
40 
41  // The StructuredClone destructor references a JSContext created by our
42  // ScriptInterface, so explicitly clean it up before ScriptInterface
43  m_MapData.reset();
44 
45  // Cleanup ScriptInterface
46  delete m_ScriptInterface;
47 }
48 
49 void CMapGeneratorWorker::Initialize(const VfsPath& scriptFile, const std::string& settings)
50 {
52 
53  // Set progress to positive value
54  m_Progress = 1;
55  m_ScriptPath = scriptFile;
56  m_Settings = settings;
57 
58  // Launch the worker thread
59  int ret = pthread_create(&m_WorkerThread, NULL, &RunThread, this);
60  ENSURE(ret == 0);
61 }
62 
64 {
65  debug_SetThreadName("MapGenerator");
66  g_Profiler2.RegisterCurrentThread("MapGenerator");
67 
68  CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(data);
69 
71 
72  // Run map generation scripts
73  if ((!self->Run()) || (self->m_Progress > 0))
74  {
75  // Don't leave progress in an unknown state, if generator failed, set it to -1
76  CScopeLock lock(self->m_WorkerMutex);
77  self->m_Progress = -1;
78  }
79 
80  // At this point the random map scripts are done running, so the thread has no further purpose
81  // and can die. The data will be stored in m_MapData already if successful, or m_Progress
82  // will contain an error value on failure.
83 
84  return NULL;
85 }
86 
88 {
89  m_ScriptInterface->SetCallbackData(static_cast<void*> (this));
90 
91  // Replace RNG with a seeded deterministic function
94 
95  // Functions for RMS
96  m_ScriptInterface->RegisterFunction<bool, std::wstring, CMapGeneratorWorker::LoadLibrary>("LoadLibrary");
97  m_ScriptInterface->RegisterFunction<void, CScriptValRooted, CMapGeneratorWorker::ExportMap>("ExportMap");
98  m_ScriptInterface->RegisterFunction<void, int, CMapGeneratorWorker::SetProgress>("SetProgress");
99  m_ScriptInterface->RegisterFunction<void, CMapGeneratorWorker::MaybeGC>("MaybeGC");
100  m_ScriptInterface->RegisterFunction<std::vector<std::string>, CMapGeneratorWorker::GetCivData>("GetCivData");
101 
102  // Parse settings
103  CScriptValRooted settingsVal = m_ScriptInterface->ParseJSON(m_Settings);
104  if (settingsVal.undefined())
105  {
106  LOGERROR(L"CMapGeneratorWorker::Run: Failed to parse settings");
107  return false;
108  }
109 
110  // Init RNG seed
111  uint32_t seed;
112  if (!m_ScriptInterface->GetProperty(settingsVal.get(), "Seed", seed))
113  { // No seed specified
114  LOGWARNING(L"CMapGeneratorWorker::Run: No seed value specified - using 0");
115  seed = 0;
116  }
117 
118  m_MapGenRNG.seed((int32_t)seed);
119 
120  // Copy settings to global variable
121  if (!m_ScriptInterface->SetProperty(m_ScriptInterface->GetGlobalObject(), "g_MapSettings", settingsVal))
122  {
123  LOGERROR(L"CMapGeneratorWorker::Run: Failed to define g_MapSettings");
124  return false;
125  }
126 
127  // Load RMS
128  LOGMESSAGE(L"Loading RMS '%ls'", m_ScriptPath.string().c_str());
130  {
131  LOGERROR(L"CMapGeneratorWorker::Run: Failed to load RMS '%ls'", m_ScriptPath.string().c_str());
132  return false;
133  }
134 
135  return true;
136 }
137 
139 {
141  return m_Progress;
142 }
143 
144 shared_ptr<ScriptInterface::StructuredClone> CMapGeneratorWorker::GetResults()
145 {
147  return m_MapData;
148 }
149 
150 bool CMapGeneratorWorker::LoadLibrary(void* cbdata, std::wstring name)
151 {
152  CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(cbdata);
153 
154  return self->LoadScripts(name);
155 }
156 
158 {
159  CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(cbdata);
160 
161  // Copy results
162  CScopeLock lock(self->m_WorkerMutex);
163  self->m_MapData = self->m_ScriptInterface->WriteStructuredClone(data.get());
164  self->m_Progress = 0;
165 }
166 
167 void CMapGeneratorWorker::SetProgress(void* cbdata, int progress)
168 {
169  CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(cbdata);
170 
171  // Copy data
172  CScopeLock lock(self->m_WorkerMutex);
173  self->m_Progress = progress;
174 }
175 
177 {
178  CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(cbdata);
179  self->m_ScriptInterface->MaybeGC();
180 }
181 
182 std::vector<std::string> CMapGeneratorWorker::GetCivData(void* UNUSED(cbdata))
183 {
184  VfsPath path(L"civs/");
185  VfsPaths pathnames;
186 
187  std::vector<std::string> data;
188 
189  // Load all JSON files in civs directory
190  Status ret = vfs::GetPathnames(g_VFS, path, L"*.json", pathnames);
191  if (ret == INFO::OK)
192  {
193  for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
194  {
195  // Load JSON file
196  CVFSFile file;
197  PSRETURN ret = file.Load(g_VFS, *it);
198  if (ret != PSRETURN_OK)
199  {
200  LOGERROR(L"CMapGeneratorWorker::GetCivData: Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
201  }
202  else
203  {
204  data.push_back(file.DecodeUTF8()); // assume it's UTF-8
205  }
206  }
207  }
208  else
209  {
210  // Some error reading directory
211  wchar_t error[200];
212  LOGERROR(L"CMapGeneratorWorker::GetCivData: Error reading directory '%ls': %ls", path.string().c_str(), StatusDescription(ret, error, ARRAY_SIZE(error)));
213  }
214 
215  return data;
216 
217 }
218 
219 bool CMapGeneratorWorker::LoadScripts(const std::wstring& libraryName)
220 {
221  // Ignore libraries that are already loaded
222  if (m_LoadedLibraries.find(libraryName) != m_LoadedLibraries.end())
223  {
224  return true;
225  }
226 
227  // Mark this as loaded, to prevent it recursively loading itself
228  m_LoadedLibraries.insert(libraryName);
229 
230  VfsPath path = L"maps/random/" + libraryName + L"/";
231  VfsPaths pathnames;
232 
233  // Load all scripts in mapgen directory
234  Status ret = vfs::GetPathnames(g_VFS, path, L"*.js", pathnames);
235  if (ret == INFO::OK)
236  {
237  for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
238  {
239  LOGMESSAGE(L"Loading map generator script '%ls'", it->string().c_str());
240 
242  {
243  LOGERROR(L"CMapGeneratorWorker::LoadScripts: Failed to load script '%ls'", it->string().c_str());
244  return false;
245  }
246  }
247  }
248  else
249  {
250  // Some error reading directory
251  wchar_t error[200];
252  LOGERROR(L"CMapGeneratorWorker::LoadScripts: Error reading scripts in directory '%ls': %ls", path.string().c_str(), StatusDescription(ret, error, ARRAY_SIZE(error)));
253  return false;
254  }
255 
256  return true;
257 }
258 
259 //////////////////////////////////////////////////////////////////////////////////
260 //////////////////////////////////////////////////////////////////////////////////
261 
263 {
264 }
265 
267 {
268  delete m_Worker;
269 }
270 
271 void CMapGenerator::GenerateMap(const VfsPath& scriptFile, const std::string& settings)
272 {
273  m_Worker->Initialize(scriptFile, settings);
274 }
275 
277 {
278  return m_Worker->GetProgress();
279 }
280 
281 shared_ptr<ScriptInterface::StructuredClone> CMapGenerator::GetResults()
282 {
283  return m_Worker->GetResults();
284 }
int GetProgress()
Get status of the map generator thread.
CStr DecodeUTF8() const
Returns contents of a UTF-8 encoded file as a string with optional BOM removed.
Definition: Filesystem.cpp:153
bool ReplaceNondeterministicRNG(boost::rand48 &rng)
Replace the default JS random number geenrator with a seeded, network-sync&#39;d one. ...
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
const Status OK
Definition: status.h:386
#define LOGERROR
Definition: CLogger.h:35
void SetCallbackData(void *cbdata)
const PSRETURN PSRETURN_OK
Definition: Errors.h:103
Reads a file, then gives read-only access to the contents.
Definition: Filesystem.h:69
static void SetProgress(void *cbdata, int progress)
CMapGeneratorWorker * m_Worker
Definition: MapGenerator.h:69
shared_ptr< ScriptInterface::StructuredClone > GetResults()
Get random map data, according to this format: http://trac.wildfiregames.com/wiki/Random_Map_Generato...
#define LOGMESSAGE
Definition: CLogger.h:32
static void MaybeGC(void *cbdata)
static std::vector< std::string > GetCivData(void *cbdata)
Locks a CMutex over this object&#39;s lifetime.
Definition: ThreadUtil.h:73
boost::rand48 m_MapGenRNG
Definition: MapGenerator.h:131
bool LoadGlobalScripts()
Load global scripts that most script contexts need, located in the /globalscripts directory...
CScriptValRooted ParseJSON(const std::string &string_utf8)
Parse a UTF-8-encoded JSON string.
#define ARRAY_SIZE(name)
static void * RunThread(void *data)
LIB_API void debug_SetThreadName(const char *name)
inform the debugger of the current thread&#39;s name.
Definition: bdbg.cpp:126
#define LOGWARNING
Definition: CLogger.h:34
shared_ptr< ScriptInterface::StructuredClone > m_MapData
Definition: MapGenerator.h:130
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
static void ExportMap(void *cbdata, CScriptValRooted data)
int pthread_create(pthread_t *thread_id, const void *attr, void *(*func)(void *), void *arg)
Definition: wpthread.cpp:636
CProfiler2 g_Profiler2
Definition: Profiler2.cpp:35
u32 PSRETURN
Definition: Errors.h:75
Definition: path.h:75
void RegisterCurrentThread(const std::string &name)
Call in any thread to enable the profiler in that thread.
Definition: Profiler2.cpp:241
const String & string() const
Definition: path.h:123
bool LoadGlobalScriptFile(const VfsPath &path)
Load and execute the given script in the global scope.
ScriptInterface * m_ScriptInterface
Definition: MapGenerator.h:133
std::set< std::wstring > m_LoadedLibraries
Definition: MapGenerator.h:129
std::string m_Settings
Definition: MapGenerator.h:135
int GetProgress()
Get status of the map generator thread.
pthread_t m_WorkerThread
Definition: MapGenerator.h:141
static bool LoadLibrary(void *cbdata, std::wstring name)
i64 Status
Error handling system.
Definition: status.h:171
Random map generator worker thread.
Definition: MapGenerator.h:82
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.
void GenerateMap(const VfsPath &scriptFile, const std::string &settings)
Start the map generator thread.
PSRETURN Load(const PIVFS &vfs, const VfsPath &filename)
Returns either PSRETURN_OK or PSRETURN_CVFSFile_LoadFailed.
Definition: Filesystem.cpp:117
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
std::vector< VfsPath > VfsPaths
Definition: vfs_path.h:42
JSBool error(JSContext *cx, uintN argc, jsval *vp)
shared_ptr< ScriptInterface::StructuredClone > GetResults()
Get random map data, according to this format: http://trac.wildfiregames.com/wiki/Random_Map_Generato...
jsval get() const
Returns the current value (or JSVAL_VOID if uninitialised).
Definition: ScriptVal.cpp:45
Abstraction around a SpiderMonkey JSContext.
int pthread_join(pthread_t thread, void **value_ptr)
Definition: wpthread.cpp:679
bool SetProperty(jsval obj, const char *name, const T &value, bool constant=false, bool enumerate=true)
Set the named property on the given object.
bool LoadScripts(const std::wstring &libraryName)
Load all scripts of the given library.
PIVFS g_VFS
Definition: Filesystem.cpp:30
void Initialize(const VfsPath &scriptFile, const std::string &settings)
Start the map generator thread.
static shared_ptr< ScriptRuntime > CreateRuntime(int runtimeSize=DEFAULT_RUNTIME_SIZE)
Returns a runtime, which can used to initialise any number of ScriptInterfaces contexts.
Status GetPathnames(const PIVFS &fs, const VfsPath &path, const wchar_t *filter, VfsPaths &pathnames)
Definition: vfs_util.cpp:40
#define RMS_RUNTIME_SIZE
void MaybeGC()
MaybeGC tries to determine whether garbage collection in cx&#39;s runtime would free up enough memory to ...