Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/RandomMapLogger.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/random/rmgen/RandomMapLogger.js (revision 23454)
+++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/RandomMapLogger.js (revision 23455)
@@ -1,45 +1,47 @@
function RandomMapLogger()
{
this.lastTime = undefined;
- this.startTime = Engine.GetMicroseconds();
+ this.startTime = Engine.GetMicroseconds ? Engine.GetMicroseconds() : 0;
this.prefix = ""; // seems noisy
- this.printDirectly(
- this.prefix +
- "Generating " + g_MapSettings.Name +
- " of size " + g_MapSettings.Size +
- " and " + getNumPlayers() + " players.\n");
+ // Don't print in test cases
+ if (g_MapSettings.Name)
+ this.printDirectly(
+ this.prefix +
+ "Generating " + g_MapSettings.Name +
+ " of size " + g_MapSettings.Size +
+ " and " + (g_MapSettings.PlayerData.length - 1) + " players.\n");
}
RandomMapLogger.prototype.printDirectly = function(string)
{
log(string);
print(string);
};
RandomMapLogger.prototype.print = function(string)
{
this.printDuration();
this.printDirectly(this.prefix + string + "...");
this.lastTime = Engine.GetMicroseconds();
};
RandomMapLogger.prototype.printDuration = function()
{
if (!this.lastTime)
return;
this.printDurationDirectly("", this.lastTime);
this.lastTime = Engine.GetMicroseconds();
};
RandomMapLogger.prototype.close = function()
{
this.printDuration();
this.printDurationDirectly(this.prefix + "Total map generation time:", this.startTime);
};
RandomMapLogger.prototype.printDurationDirectly = function(text, startTime)
{
this.printDirectly(text + " " + ((Engine.GetMicroseconds() - startTime) / 1000000).toFixed(6) + "s.\n");
};
Index: ps/trunk/binaries/data/mods/public/maps/random/tests/test_TileClass.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/random/tests/test_TileClass.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/maps/random/tests/test_TileClass.js (revision 23455)
@@ -0,0 +1,91 @@
+Engine.LoadLibrary("rmgen");
+
+var g_MapSettings = { "Size": 512 };
+var g_Map = new RandomMap(0, "blackness");
+
+// Test that that it checks by value, not by reference
+{
+ let tileClass = new TileClass(2);
+ let reference1 = new Vector2D(1, 1);
+ let reference2 = new Vector2D(1, 1);
+ tileClass.add(reference1);
+ TS_ASSERT(tileClass.has(reference2));
+}
+
+// Test out-of-bounds
+{
+ let tileClass = new TileClass(32);
+ let absentPoints = [
+ new Vector2D(0, 0),
+ new Vector2D(0, 1),
+ new Vector2D(1, 0),
+ new Vector2D(-1, -1),
+ new Vector2D(2048, 0),
+ new Vector2D(0, NaN),
+ new Vector2D(0, Infinity)
+ ];
+
+ for (let point of absentPoints)
+ TS_ASSERT(!tileClass.has(point));
+}
+
+// Test multi-insertion
+{
+ let tileClass = new TileClass(32);
+ let point = new Vector2D(1, 1);
+
+ tileClass.add(point);
+ tileClass.add(point);
+ TS_ASSERT_EQUALS(tileClass.countMembersInRadius(point, 0), 1);
+
+ // Still one point remaining
+ tileClass.remove(point);
+ tileClass.remove(point);
+ TS_ASSERT(!tileClass.has(point));
+}
+
+// Test multi-insertion removal
+{
+ let tileClass = new TileClass(32);
+ let point = new Vector2D(2, 7);
+
+ tileClass.add(point);
+ tileClass.add(point);
+ tileClass.remove(point);
+ TS_ASSERT(tileClass.has(point));
+
+ tileClass.remove(point);
+ TS_ASSERT(!tileClass.has(point));
+}
+
+// Test multi-insertion removal
+{
+ let tileClass = new TileClass(55);
+ let point = new Vector2D(5, 4);
+
+ for (let i = 0; i < 50; ++i)
+ tileClass.add(point);
+
+ tileClass.remove(point);
+ TS_ASSERT(tileClass.has(point));
+
+ tileClass.remove(point);
+ TS_ASSERT(tileClass.has(point));
+}
+
+// Test getters
+{
+ let tileClass = new TileClass(88);
+ let point = new Vector2D(5, 1);
+ tileClass.add(point);
+
+ let point2 = new Vector2D(4, 9);
+ tileClass.add(point2);
+
+ TS_ASSERT_EQUALS(tileClass.countMembersInRadius(point, 1), 1);
+ TS_ASSERT_EQUALS(tileClass.countMembersInRadius(point, 100), 2);
+
+ TS_ASSERT_EQUALS(tileClass.countNonMembersInRadius(point, 1), 4);
+ TS_ASSERT_EQUALS(tileClass.countNonMembersInRadius(point, 2), 12);
+ TS_ASSERT_EQUALS(tileClass.countNonMembersInRadius(point, 3), 28);
+}
Property changes on: ps/trunk/binaries/data/mods/public/maps/random/tests/test_TileClass.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/graphics/MapGenerator.cpp
===================================================================
--- ps/trunk/source/graphics/MapGenerator.cpp (revision 23454)
+++ ps/trunk/source/graphics/MapGenerator.cpp (revision 23455)
@@ -1,435 +1,440 @@
-/* Copyright (C) 2019 Wildfire Games.
+/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "MapGenerator.h"
#include "graphics/MapIO.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "lib/external_libraries/libsdl.h"
#include "lib/status.h"
#include "lib/timer.h"
#include "lib/file/vfs/vfs_path.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "ps/FileIo.h"
#include "ps/Profile.h"
#include "ps/scripting/JSInterface_VFS.h"
#include "scriptinterface/ScriptRuntime.h"
#include "scriptinterface/ScriptConversions.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/helpers/MapEdgeTiles.h"
#include
#include
// TODO: what's a good default? perhaps based on map size
#define RMS_RUNTIME_SIZE 96 * 1024 * 1024
extern bool IsQuitRequested();
static bool
MapGeneratorInterruptCallback(JSContext* UNUSED(cx))
{
// This may not use SDL_IsQuitRequested(), because it runs in a thread separate to SDL, see SDL_PumpEvents
if (IsQuitRequested())
{
LOGWARNING("Quit requested!");
return false;
}
return true;
}
-CMapGeneratorWorker::CMapGeneratorWorker()
+CMapGeneratorWorker::CMapGeneratorWorker(ScriptInterface* scriptInterface) :
+ m_ScriptInterface(scriptInterface)
{
// If something happens before we initialize, that's a failure
m_Progress = -1;
}
CMapGeneratorWorker::~CMapGeneratorWorker()
{
// Wait for thread to end
- m_WorkerThread.join();
+ if (m_WorkerThread.joinable())
+ m_WorkerThread.join();
}
void CMapGeneratorWorker::Initialize(const VfsPath& scriptFile, const std::string& settings)
{
std::lock_guard lock(m_WorkerMutex);
// Set progress to positive value
m_Progress = 1;
m_ScriptPath = scriptFile;
m_Settings = settings;
// Launch the worker thread
m_WorkerThread = std::thread(RunThread, this);
}
void* CMapGeneratorWorker::RunThread(CMapGeneratorWorker* self)
{
debug_SetThreadName("MapGenerator");
g_Profiler2.RegisterCurrentThread("MapGenerator");
shared_ptr mapgenRuntime = ScriptInterface::CreateRuntime(g_ScriptRuntime, RMS_RUNTIME_SIZE);
// Enable the script to be aborted
JS_SetInterruptCallback(mapgenRuntime->m_rt, MapGeneratorInterruptCallback);
self->m_ScriptInterface = new ScriptInterface("Engine", "MapGenerator", mapgenRuntime);
// Run map generation scripts
if (!self->Run() || self->m_Progress > 0)
{
// Don't leave progress in an unknown state, if generator failed, set it to -1
std::lock_guard lock(self->m_WorkerMutex);
self->m_Progress = -1;
}
SAFE_DELETE(self->m_ScriptInterface);
// At this point the random map scripts are done running, so the thread has no further purpose
// and can die. The data will be stored in m_MapData already if successful, or m_Progress
// will contain an error value on failure.
return NULL;
}
bool CMapGeneratorWorker::Run()
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
- m_ScriptInterface->SetCallbackData(static_cast (this));
-
- // Replace RNG with a seeded deterministic function
- m_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG);
-
- RegisterScriptFunctions();
-
// Parse settings
JS::RootedValue settingsVal(cx);
if (!m_ScriptInterface->ParseJSON(m_Settings, &settingsVal) && settingsVal.isUndefined())
{
LOGERROR("CMapGeneratorWorker::Run: Failed to parse settings");
return false;
}
// Prevent unintentional modifications to the settings object by random map scripts
if (!m_ScriptInterface->FreezeObject(settingsVal, true))
{
LOGERROR("CMapGeneratorWorker::Run: Failed to deepfreeze settings");
return false;
}
// Init RNG seed
u32 seed = 0;
if (!m_ScriptInterface->HasProperty(settingsVal, "Seed") ||
!m_ScriptInterface->GetProperty(settingsVal, "Seed", seed))
LOGWARNING("CMapGeneratorWorker::Run: No seed value specified - using 0");
- m_MapGenRNG.seed(seed);
+ InitScriptInterface(seed);
+
+ RegisterScriptFunctions_MapGenerator();
// Copy settings to global variable
JS::RootedValue global(cx, m_ScriptInterface->GetGlobalObject());
if (!m_ScriptInterface->SetProperty(global, "g_MapSettings", settingsVal, true, true))
{
LOGERROR("CMapGeneratorWorker::Run: Failed to define g_MapSettings");
return false;
}
// Load RMS
LOGMESSAGE("Loading RMS '%s'", m_ScriptPath.string8());
if (!m_ScriptInterface->LoadGlobalScriptFile(m_ScriptPath))
{
LOGERROR("CMapGeneratorWorker::Run: Failed to load RMS '%s'", m_ScriptPath.string8());
return false;
}
return true;
}
-void CMapGeneratorWorker::RegisterScriptFunctions()
+void CMapGeneratorWorker::InitScriptInterface(const u32 seed)
{
+ m_ScriptInterface->SetCallbackData(static_cast(this));
+
+ m_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG);
+ m_MapGenRNG.seed(seed);
+
// VFS
JSI_VFS::RegisterScriptFunctions_Maps(*m_ScriptInterface);
// Globalscripts may use VFS script functions
m_ScriptInterface->LoadGlobalScripts();
// File loading
m_ScriptInterface->RegisterFunction("LoadLibrary");
m_ScriptInterface->RegisterFunction("LoadHeightmapImage");
m_ScriptInterface->RegisterFunction("LoadMapTerrain");
- // Progression and profiling
- m_ScriptInterface->RegisterFunction("SetProgress");
- m_ScriptInterface->RegisterFunction("GetMicroseconds");
- m_ScriptInterface->RegisterFunction("ExportMap");
-
- // Template functions
- m_ScriptInterface->RegisterFunction("GetTemplate");
- m_ScriptInterface->RegisterFunction("TemplateExists");
- m_ScriptInterface->RegisterFunction, std::string, bool, CMapGeneratorWorker::FindTemplates>("FindTemplates");
- m_ScriptInterface->RegisterFunction, std::string, bool, CMapGeneratorWorker::FindActorTemplates>("FindActorTemplates");
-
// Engine constants
// Length of one tile of the terrain grid in metres.
// Useful to transform footprint sizes to the tilegrid coordinate system.
m_ScriptInterface->SetGlobal("TERRAIN_TILE_SIZE", static_cast(TERRAIN_TILE_SIZE));
// Number of impassable tiles at the map border
m_ScriptInterface->SetGlobal("MAP_BORDER_WIDTH", static_cast(MAP_EDGE_TILES));
}
+void CMapGeneratorWorker::RegisterScriptFunctions_MapGenerator()
+{
+ // Template functions
+ m_ScriptInterface->RegisterFunction("GetTemplate");
+ m_ScriptInterface->RegisterFunction("TemplateExists");
+ m_ScriptInterface->RegisterFunction, std::string, bool, CMapGeneratorWorker::FindTemplates>("FindTemplates");
+ m_ScriptInterface->RegisterFunction, std::string, bool, CMapGeneratorWorker::FindActorTemplates>("FindActorTemplates");
+
+ // Progression and profiling
+ m_ScriptInterface->RegisterFunction("SetProgress");
+ m_ScriptInterface->RegisterFunction("GetMicroseconds");
+ m_ScriptInterface->RegisterFunction("ExportMap");
+}
+
int CMapGeneratorWorker::GetProgress()
{
std::lock_guard lock(m_WorkerMutex);
return m_Progress;
}
double CMapGeneratorWorker::GetMicroseconds(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
{
return JS_Now();
}
shared_ptr CMapGeneratorWorker::GetResults()
{
std::lock_guard lock(m_WorkerMutex);
return m_MapData;
}
bool CMapGeneratorWorker::LoadLibrary(ScriptInterface::CxPrivate* pCxPrivate, const VfsPath& name)
{
CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
return self->LoadScripts(name);
}
void CMapGeneratorWorker::ExportMap(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data)
{
CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
// Copy results
std::lock_guard lock(self->m_WorkerMutex);
self->m_MapData = self->m_ScriptInterface->WriteStructuredClone(data);
self->m_Progress = 0;
}
void CMapGeneratorWorker::SetProgress(ScriptInterface::CxPrivate* pCxPrivate, int progress)
{
CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
// Copy data
std::lock_guard lock(self->m_WorkerMutex);
if (progress >= self->m_Progress)
self->m_Progress = progress;
else
LOGWARNING("The random map script tried to reduce the loading progress from %d to %d", self->m_Progress, progress);
}
CParamNode CMapGeneratorWorker::GetTemplate(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName)
{
CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
const CParamNode& templateRoot = self->m_TemplateLoader.GetTemplateFileData(templateName).GetChild("Entity");
if (!templateRoot.IsOk())
LOGERROR("Invalid template found for '%s'", templateName.c_str());
return templateRoot;
}
bool CMapGeneratorWorker::TemplateExists(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName)
{
CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
return self->m_TemplateLoader.TemplateExists(templateName);
}
std::vector CMapGeneratorWorker::FindTemplates(ScriptInterface::CxPrivate* pCxPrivate, const std::string& path, bool includeSubdirectories)
{
CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
return self->m_TemplateLoader.FindTemplates(path, includeSubdirectories, SIMULATION_TEMPLATES);
}
std::vector CMapGeneratorWorker::FindActorTemplates(ScriptInterface::CxPrivate* pCxPrivate, const std::string& path, bool includeSubdirectories)
{
CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
return self->m_TemplateLoader.FindTemplates(path, includeSubdirectories, ACTOR_TEMPLATES);
}
bool CMapGeneratorWorker::LoadScripts(const VfsPath& libraryName)
{
// Ignore libraries that are already loaded
if (m_LoadedLibraries.find(libraryName) != m_LoadedLibraries.end())
return true;
// Mark this as loaded, to prevent it recursively loading itself
m_LoadedLibraries.insert(libraryName);
VfsPath path = VfsPath(L"maps/random/") / libraryName / VfsPath();
VfsPaths pathnames;
// Load all scripts in mapgen directory
Status ret = vfs::GetPathnames(g_VFS, path, L"*.js", pathnames);
if (ret == INFO::OK)
{
for (const VfsPath& p : pathnames)
{
LOGMESSAGE("Loading map generator script '%s'", p.string8());
if (!m_ScriptInterface->LoadGlobalScriptFile(p))
{
LOGERROR("CMapGeneratorWorker::LoadScripts: Failed to load script '%s'", p.string8());
return false;
}
}
}
else
{
// Some error reading directory
wchar_t error[200];
LOGERROR("CMapGeneratorWorker::LoadScripts: Error reading scripts in directory '%s': %s", path.string8(), utf8_from_wstring(StatusDescription(ret, error, ARRAY_SIZE(error))));
return false;
}
return true;
}
JS::Value CMapGeneratorWorker::LoadHeightmap(ScriptInterface::CxPrivate* pCxPrivate, const VfsPath& filename)
{
std::vector heightmap;
if (LoadHeightmapImageVfs(filename, heightmap) != INFO::OK)
{
LOGERROR("Could not load heightmap file '%s'", filename.string8());
return JS::UndefinedValue();
}
CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
JSContext* cx = self->m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue returnValue(cx);
ToJSVal_vector(cx, &returnValue, heightmap);
return returnValue;
}
// See CMapReader::UnpackTerrain, CMapReader::ParseTerrain for the reordering
JS::Value CMapGeneratorWorker::LoadMapTerrain(ScriptInterface::CxPrivate* pCxPrivate, const VfsPath& filename)
{
CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
JSContext* cx = self->m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
if (!VfsFileExists(filename))
{
self->m_ScriptInterface->ReportError(
("Terrain file \"" + filename.string8() + "\" does not exist!").c_str());
return JS::UndefinedValue();
}
CFileUnpacker unpacker;
unpacker.Read(filename, "PSMP");
if (unpacker.GetVersion() < CMapIO::FILE_READ_VERSION)
{
self->m_ScriptInterface->ReportError(
("Could not load terrain file \"" + filename.string8() + "\" too old version!").c_str());
return JS::UndefinedValue();
}
// unpack size
ssize_t patchesPerSide = (ssize_t)unpacker.UnpackSize();
size_t verticesPerSide = patchesPerSide * PATCH_SIZE + 1;
// unpack heightmap
std::vector heightmap;
heightmap.resize(SQR(verticesPerSide));
unpacker.UnpackRaw(&heightmap[0], SQR(verticesPerSide) * sizeof(u16));
// unpack texture names
size_t textureCount = unpacker.UnpackSize();
std::vector textureNames;
textureNames.reserve(textureCount);
for (size_t i = 0; i < textureCount; ++i)
{
CStr texturename;
unpacker.UnpackString(texturename);
textureNames.push_back(texturename);
}
// unpack texture IDs per tile
ssize_t tilesPerSide = patchesPerSide * PATCH_SIZE;
std::vector tiles;
tiles.resize(size_t(SQR(tilesPerSide)));
unpacker.UnpackRaw(&tiles[0], sizeof(CMapIO::STileDesc) * tiles.size());
// reorder by patches and store and save texture IDs per tile
std::vector textureIDs;
for (ssize_t x = 0; x < tilesPerSide; ++x)
{
size_t patchX = x / PATCH_SIZE;
size_t offX = x % PATCH_SIZE;
for (ssize_t y = 0; y < tilesPerSide; ++y)
{
size_t patchY = y / PATCH_SIZE;
size_t offY = y % PATCH_SIZE;
// m_Priority and m_Tex2Index unused
textureIDs.push_back(tiles[(patchY * patchesPerSide + patchX) * SQR(PATCH_SIZE) + (offY * PATCH_SIZE + offX)].m_Tex1Index);
}
}
JS::RootedValue returnValue(cx);
ScriptInterface::CreateObject(
cx,
&returnValue,
"height", heightmap,
"textureNames", textureNames,
"textureIDs", textureIDs);
return returnValue;
}
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
-CMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker())
+CMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker(nullptr))
{
}
CMapGenerator::~CMapGenerator()
{
delete m_Worker;
}
void CMapGenerator::GenerateMap(const VfsPath& scriptFile, const std::string& settings)
{
m_Worker->Initialize(scriptFile, settings);
}
int CMapGenerator::GetProgress()
{
return m_Worker->GetProgress();
}
shared_ptr CMapGenerator::GetResults()
{
return m_Worker->GetResults();
}
Index: ps/trunk/source/graphics/MapGenerator.h
===================================================================
--- ps/trunk/source/graphics/MapGenerator.h (revision 23454)
+++ ps/trunk/source/graphics/MapGenerator.h (revision 23455)
@@ -1,242 +1,248 @@
-/* Copyright (C) 2019 Wildfire Games.
+/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_MAPGENERATOR
#define INCLUDED_MAPGENERATOR
#include "lib/posix/posix_pthread.h"
#include "ps/FileIo.h"
#include "ps/TemplateLoader.h"
#include "scriptinterface/ScriptInterface.h"
#include
#include
#include
#include
#include
class CMapGeneratorWorker;
/**
* Random map generator interface. Initialized by CMapReader and then checked
* periodically during loading, until it's finished (progress value is 0).
*
* The actual work is performed by CMapGeneratorWorker in a separate thread.
*/
class CMapGenerator
{
NONCOPYABLE(CMapGenerator);
public:
CMapGenerator();
~CMapGenerator();
/**
* Start the map generator thread
*
* @param scriptFile The VFS path for the script, e.g. "maps/random/latium.js"
* @param settings JSON string containing settings for the map generator
*/
void GenerateMap(const VfsPath& scriptFile, const std::string& settings);
/**
* Get status of the map generator thread
*
* @return Progress percentage 1-100 if active, 0 when finished, or -1 on error
*/
int GetProgress();
/**
* Get random map data, according to this format:
* http://trac.wildfiregames.com/wiki/Random_Map_Generator_Internals#Dataformat
*
* @return StructuredClone containing map data
*/
shared_ptr GetResults();
private:
CMapGeneratorWorker* m_Worker;
};
/**
* Random map generator worker thread.
* (This is run in a thread so that the GUI remains responsive while loading)
*
* Thread-safety:
* - Initialize and constructor/destructor must be called from the main thread.
* - ScriptInterface created and destroyed by thread
* - StructuredClone used to return JS map data - JS:Values can't be used across threads/runtimes.
*/
class CMapGeneratorWorker
{
public:
- CMapGeneratorWorker();
+ CMapGeneratorWorker(ScriptInterface* scriptInterface);
~CMapGeneratorWorker();
/**
* Start the map generator thread
*
* @param scriptFile The VFS path for the script, e.g. "maps/random/latium.js"
* @param settings JSON string containing settings for the map generator
*/
void Initialize(const VfsPath& scriptFile, const std::string& settings);
/**
* Get status of the map generator thread
*
* @return Progress percentage 1-100 if active, 0 when finished, or -1 on error
*/
int GetProgress();
/**
* Get random map data, according to this format:
* http://trac.wildfiregames.com/wiki/Random_Map_Generator_Internals#Dataformat
*
* @return StructuredClone containing map data
*/
shared_ptr GetResults();
+ /**
+ * Set initial seed, callback data.
+ * Expose functions, globals and classes defined in this class relevant to the map and test scripts.
+ */
+ void InitScriptInterface(const u32 seed);
+
private:
/**
- * Expose functions defined in this class to the script.
+ * Expose functions defined in this class that are relevant to mapscripts but not the tests.
*/
- void RegisterScriptFunctions();
+ void RegisterScriptFunctions_MapGenerator();
/**
* Load all scripts of the given library
*
* @param libraryName VfsPath specifying name of the library (subfolder of ../maps/random/)
* @return true if all scripts ran successfully, false if there's an error
*/
bool LoadScripts(const VfsPath& libraryName);
/**
* Recursively load all script files in the given folder.
*/
static bool LoadLibrary(ScriptInterface::CxPrivate* pCxPrivate, const VfsPath& name);
/**
* Finalize map generation and pass results from the script to the engine.
*/
static void ExportMap(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data);
/**
* Load an image file and return it as a height array.
*/
static JS::Value LoadHeightmap(ScriptInterface::CxPrivate* pCxPrivate, const VfsPath& src);
/**
* Load an Atlas terrain file (PMP) returning textures and heightmap.
*/
static JS::Value LoadMapTerrain(ScriptInterface::CxPrivate* pCxPrivate, const VfsPath& filename);
/**
* Sets the map generation progress, which is one of multiple stages determining the loading screen progress.
*/
static void SetProgress(ScriptInterface::CxPrivate* pCxPrivate, int progress);
/**
* Microseconds since the epoch.
*/
static double GetMicroseconds(ScriptInterface::CxPrivate* pCxPrivate);
/**
* Return the template data of the given template name.
*/
static CParamNode GetTemplate(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName);
/**
* Check whether the given template exists.
*/
static bool TemplateExists(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName);
/**
* Returns all template names of simulation entity templates.
*/
static std::vector FindTemplates(ScriptInterface::CxPrivate* pCxPrivate, const std::string& path, bool includeSubdirectories);
/**
* Returns all template names of actors.
*/
static std::vector FindActorTemplates(ScriptInterface::CxPrivate* pCxPrivate, const std::string& path, bool includeSubdirectories);
/**
* Perform map generation in an independent thread.
*/
static void* RunThread(CMapGeneratorWorker* self);
/**
* Perform the map generation.
*/
bool Run();
/**
* Currently loaded script librarynames.
*/
std::set m_LoadedLibraries;
/**
* Result of the mapscript generation including terrain, entities and environment settings.
*/
shared_ptr m_MapData;
/**
* Deterministic random number generator.
*/
boost::rand48 m_MapGenRNG;
/**
* Current map generation progress.
*/
int m_Progress;
/**
* Provides the script context.
*/
ScriptInterface* m_ScriptInterface;
/**
* Map generation script to run.
*/
VfsPath m_ScriptPath;
/**
* Map and simulation settings chosen in the gamesetup stage.
*/
std::string m_Settings;
/**
* Backend to loading template data.
*/
CTemplateLoader m_TemplateLoader;
/**
* Holds the mapgeneration thread identifier.
*/
std::thread m_WorkerThread;
/**
* Avoids thread synchronization issues.
*/
std::mutex m_WorkerMutex;
};
#endif //INCLUDED_MAPGENERATOR
Index: ps/trunk/source/graphics/tests/test_MapGenerator.h
===================================================================
--- ps/trunk/source/graphics/tests/test_MapGenerator.h (nonexistent)
+++ ps/trunk/source/graphics/tests/test_MapGenerator.h (revision 23455)
@@ -0,0 +1,54 @@
+/* Copyright (C) 2020 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "graphics/MapGenerator.h"
+#include "ps/Filesystem.h"
+#include "simulation2/system/ComponentTest.h"
+
+class TestMapGenerator : public CxxTest::TestSuite
+{
+public:
+ void setUp()
+ {
+ g_VFS = CreateVfs();
+ g_VFS->Mount(L"", DataDir() / "mods" / "mod", VFS_MOUNT_MUST_EXIST);
+ g_VFS->Mount(L"", DataDir() / "mods" / "public", VFS_MOUNT_MUST_EXIST, 1); // ignore directory-not-found errors
+ CXeromyces::Startup();
+ }
+
+ void tearDown()
+ {
+ CXeromyces::Terminate();
+ g_VFS.reset();
+ }
+
+ void test_mapgen_scripts()
+ {
+ VfsPaths paths;
+ TS_ASSERT_OK(vfs::GetPathnames(g_VFS, L"maps/random/tests/", L"test_*.js", paths));
+
+ for (const VfsPath& path : paths)
+ {
+ ScriptInterface scriptInterface("Engine", "MapGenerator", g_ScriptRuntime);
+ ScriptTestSetup(scriptInterface);
+
+ CMapGeneratorWorker worker(&scriptInterface);
+ worker.InitScriptInterface(0);
+ scriptInterface.LoadGlobalScriptFile(path);
+ }
+ }
+};
Property changes on: ps/trunk/source/graphics/tests/test_MapGenerator.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property