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