diff --git a/binaries/data/mods/public/gui/common/functions_utility_error.js b/binaries/data/mods/public/gui/common/functions_utility_error.js index 9cc7b27609..7ab9796037 100644 --- a/binaries/data/mods/public/gui/common/functions_utility_error.js +++ b/binaries/data/mods/public/gui/common/functions_utility_error.js @@ -17,7 +17,7 @@ function cancelOnError(msg) { Engine.PushGuiPage("page_msgbox.xml", { width: 400, - height: 300, + height: 200, message: '[font="serif-bold-18"]' + msg + '[/font]', title: "Loading Aborted", mode: 2 diff --git a/binaries/data/mods/public/maps/random/cantabrian_highlands.js b/binaries/data/mods/public/maps/random/cantabrian_highlands.js index 889a3f5f15..d11e74a87b 100644 --- a/binaries/data/mods/public/maps/random/cantabrian_highlands.js +++ b/binaries/data/mods/public/maps/random/cantabrian_highlands.js @@ -179,6 +179,8 @@ for (var i=1; i<=numPlayers; i++) } } +RMS.SetProgress(5); + // create lakes log("Creating lakes..."); placer = new ClumpPlacer(mapArea * 0.009, 0.8, 0.1, 0); @@ -194,6 +196,8 @@ createAreas( round(1.3 * numPlayers) ); +RMS.SetProgress(22); + // create bumps log("Creating bumps..."); placer = new ClumpPlacer(10, 0.3, 0.06, 0); @@ -205,6 +209,8 @@ createAreas( mapArea/1000 ); +RMS.SetProgress(25); + // create hills log("Creating hills..."); placer = new ClumpPlacer(mapArea * 0.0015, 0.2, 0.1, 0); @@ -220,6 +226,8 @@ createAreas( 2 * numPlayers ); +RMS.SetProgress(30); + // create forests log("Creating forests..."); placer = new ClumpPlacer(mapArea * 0.002, 0.1, 0.1, 0); @@ -234,6 +242,8 @@ createAreas( 6 * numPlayers ); +RMS.SetProgress(53); + // create dirt patches log("Creating dirt patches..."); var sizes = [8,14,20]; @@ -293,6 +303,8 @@ createObjectGroups(group, 0, // 2 * numPlayers, 50 // ); +RMS.SetProgress(60); + // create small decorative rocks log("Creating large decorative rocks..."); group = new SimpleGroup( @@ -360,6 +372,8 @@ createObjectGroups(group, undefined, mapArea/90 ); +RMS.SetProgress(80); + // create large grass tufts log("Creating large grass tufts..."); group = new SimpleGroup( @@ -370,6 +384,8 @@ createObjectGroups(group, undefined, mapArea/900 ); +RMS.SetProgress(87); + // create bushes log("Creating bushes..."); group = new SimpleGroup( @@ -386,7 +402,7 @@ group = new SimpleGroup( [new SimpleObject(aReeds, 5,10, 0,1.5, -PI/8,PI/8)] ); createObjectGroups(group, undefined, - [new BorderTileClassConstraint(clWater, 3, 0), new StayInTileClassConstraint(clWater, 1)], + [new BorderTileClassConstraint(clWater, 3, 0), stayClasses(clWater, 1)], 10 * numPlayers, 100 ); diff --git a/binaries/data/mods/public/maps/random/neareastern_badlands.js b/binaries/data/mods/public/maps/random/neareastern_badlands.js index 4ddefa79b8..2c1e867fa5 100644 --- a/binaries/data/mods/public/maps/random/neareastern_badlands.js +++ b/binaries/data/mods/public/maps/random/neareastern_badlands.js @@ -137,6 +137,8 @@ for (var i=0; i < numPlayers; i++) createObjectGroup(group, 0, avoidClasses(clBaseResource,1)); } +RMS.SetProgress(5); + // create patches log("Creating sand patches..."); var placer = new ClumpPlacer(30, 0.2, 0.1, 0); @@ -146,6 +148,8 @@ createAreas(placer, [painter, paintClass(clPatch)], mapArea/600 ); +RMS.SetProgress(24); + log("Creating dirt patches..."); placer = new ClumpPlacer(10, 0.2, 0.1, 0); painter = new TerrainPainter([tSand, tDirt]); @@ -154,6 +158,8 @@ createAreas(placer, [painter, paintClass(clPatch)], mapArea/600 ); +RMS.SetProgress(32); + // create the oasis (roughly 4% of map area) log("Creating oasis..."); placer = new ClumpPlacer(mapArea * 0.04, 0.6, 0.1, 0, mapSize/2, mapSize/2); @@ -161,6 +167,8 @@ painter = new LayeredPainter([[tSand, pForest], tShore, tWaterDeep], [6,1]); elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, -11, 5); createArea(placer, [painter, elevationPainter, paintClass(clForest)], null); +RMS.SetProgress(51); + // create hills log("Creating level 1 hills..."); placer = new ClumpPlacer(150, 0.25, 0.1, 0.3); @@ -174,6 +182,8 @@ createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill1)], mapArea/3800, 100 ); +RMS.SetProgress(70); + log("Creating small level 1 hills..."); placer = new ClumpPlacer(60, 0.25, 0.1, 0.3); terrainPainter = new LayeredPainter( @@ -186,6 +196,8 @@ createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill1)], mapArea/2800, 100 ); +RMS.SetProgress(81); + log("Creating level 2 hills..."); placer = new ClumpPlacer(60, 0.2, 0.1, 0.9); terrainPainter = new LayeredPainter( @@ -194,10 +206,12 @@ terrainPainter = new LayeredPainter( ); elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1); createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill2)], - [avoidClasses(clHill2, 1), new StayInTileClassConstraint(clHill1, 0)], + [avoidClasses(clHill2, 1), stayClasses(clHill1, 0)], mapArea/2800, 200 ); +RMS.SetProgress(91); + log("Creating level 3 hills..."); placer = new ClumpPlacer(25, 0.2, 0.1, 0.9); terrainPainter = new LayeredPainter( @@ -206,7 +220,7 @@ terrainPainter = new LayeredPainter( ); elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1); createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill3)], - [avoidClasses(clHill3, 1), new StayInTileClassConstraint(clHill2, 0)], + [avoidClasses(clHill3, 1), stayClasses(clHill2, 0)], mapArea/9000, 300 ); @@ -237,6 +251,8 @@ createObjectGroups(group, 0, mapArea/4000, 100 ); +RMS.SetProgress(97); + // create decorative rocks for hills log("Creating decorative rocks..."); group = new SimpleGroup([new SimpleObject(aDecorativeRock, 1,1, 0,0)], true); diff --git a/binaries/data/mods/public/maps/random/rmgen/mapgen.js b/binaries/data/mods/public/maps/random/rmgen/mapgen.js index 85b4a49094..112f7e2cd0 100644 --- a/binaries/data/mods/public/maps/random/rmgen/mapgen.js +++ b/binaries/data/mods/public/maps/random/rmgen/mapgen.js @@ -32,21 +32,20 @@ var g_Camera = { function InitMap() { - if (g_MapSettings === undefined || g_MapSettings == {}) - { // If settings missing, warn and use some defaults - warn("InitMapGen: settings missing"); - g_MapSettings = { - Size : 13, - BaseTerrain: "grass1_spring", - BaseHeight: 0, - PlayerData : [ {}, {} ] - }; + if (g_MapSettings === undefined) + { + // Should never get this far, failed settings would abort prior to loading scripts + error("InitMapGen: settings missing"); } // Create new map log("Creating new map..."); var terrain = createTerrain(g_MapSettings.BaseTerrain); + // XXX: Temporary hack to keep typed arrays from complaining about invalid arguments, + // until SpiderMonkey gets upgraded + g_MapSettings.Size = Math.floor(g_MapSettings.Size); + g_Map = new Map(g_MapSettings.Size * TILES_PER_PATCH, g_MapSettings.BaseHeight); g_Map.initTerrain(terrain); } diff --git a/source/graphics/MapGenerator.cpp b/source/graphics/MapGenerator.cpp index 40db65b063..1f5cd52f5f 100644 --- a/source/graphics/MapGenerator.cpp +++ b/source/graphics/MapGenerator.cpp @@ -27,71 +27,152 @@ #define RMS_RUNTIME_SIZE 96 * 1024 * 1024 -CMapGenerator::CMapGenerator() : m_ScriptInterface("RMS", "MapGenerator", ScriptInterface::CreateRuntime(RMS_RUNTIME_SIZE)) +CMapGeneratorWorker::CMapGeneratorWorker() { - m_ScriptInterface.SetCallbackData(static_cast (this)); - - // Replace RNG with a seeded deterministic function - m_ScriptInterface.ReplaceNondeterministicFunctions(m_MapGenRNG); - - // functions for RMS - m_ScriptInterface.RegisterFunction("LoadLibrary"); - m_ScriptInterface.RegisterFunction("ExportMap"); + // If something happens before we initialize, that's a failure + m_Progress = -1; } -bool CMapGenerator::GenerateMap(const VfsPath& scriptFile, const CScriptValRooted& settings) +CMapGeneratorWorker::~CMapGeneratorWorker() { + // Wait for thread to end + pthread_join(m_WorkerThread, NULL); +} + +void CMapGeneratorWorker::Initialize(const VfsPath& scriptFile, const std::string& settings) +{ + CScopeLock lock(m_WorkerMutex); + + // Set progress to positive value + m_Progress = 1; + m_ScriptPath = scriptFile; + m_Settings = settings; + + // Launch the worker thread + int ret = pthread_create(&m_WorkerThread, NULL, &RunThread, this); + debug_assert(ret == 0); +} + +void* CMapGeneratorWorker::RunThread(void *data) +{ + debug_SetThreadName("MapGenerator"); + + CMapGeneratorWorker* self = static_cast(data); + + self->m_ScriptInterface = new ScriptInterface("RMS", "MapGenerator", ScriptInterface::CreateRuntime(RMS_RUNTIME_SIZE)); + + // Run map generation scripts + if (!self->Run()) + { + // Failed: set progress to error + CScopeLock lock(self->m_WorkerMutex); + self->m_Progress = -1; + } + + // 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. + + // Cleanup ScriptInterface + SAFE_DELETE(self->m_ScriptInterface); + + return NULL; +} + +bool CMapGeneratorWorker::Run() +{ + m_ScriptInterface->SetCallbackData(static_cast (this)); + + // Replace RNG with a seeded deterministic function + m_ScriptInterface->ReplaceNondeterministicFunctions(m_MapGenRNG); + + // Functions for RMS + m_ScriptInterface->RegisterFunction("LoadLibrary"); + m_ScriptInterface->RegisterFunction("ExportMap"); + m_ScriptInterface->RegisterFunction("SetProgress"); + m_ScriptInterface->RegisterFunction("MaybeGC"); + + // Parse settings + CScriptValRooted settingsVal = m_ScriptInterface->ParseJSON(m_Settings); + if (settingsVal.undefined()) + { + LOGERROR(L"CMapGeneratorWorker::Initialize: Failed to parse settings"); + return false; + } + // Init RNG seed uint32 seed; - if (!m_ScriptInterface.GetProperty(settings.get(), "Seed", seed)) + if (!m_ScriptInterface->GetProperty(settingsVal.get(), "Seed", seed)) { // No seed specified - LOGWARNING(L"GenerateMap: No seed value specified - using 0"); + LOGWARNING(L"CMapGeneratorWorker::Initialize: No seed value specified - using 0"); seed = 0; } m_MapGenRNG.seed(seed); - // Copy settings to script context - if (!m_ScriptInterface.SetProperty(m_ScriptInterface.GetGlobalObject(), "g_MapSettings", settings)) + // Copy settings to global variable + if (!m_ScriptInterface->SetProperty(m_ScriptInterface->GetGlobalObject(), "g_MapSettings", settingsVal)) + { + LOGERROR(L"CMapGeneratorWorker::Initialize: Failed to define g_MapSettings"); return false; + } // Load RMS - LOGMESSAGE(L"Loading RMS '%ls'", scriptFile.string().c_str()); - if (!m_ScriptInterface.LoadGlobalScriptFile(scriptFile)) + LOGMESSAGE(L"Loading RMS '%ls'", m_ScriptPath.string().c_str()); + if (!m_ScriptInterface->LoadGlobalScriptFile(m_ScriptPath)) { - LOGERROR(L"Failed to load RMS '%ls'", scriptFile.string().c_str()); + LOGERROR(L"CMapGeneratorWorker::Initialize: Failed to load RMS '%ls'", m_ScriptPath.string().c_str()); return false; } return true; } -ScriptInterface& CMapGenerator::GetScriptInterface() +int CMapGeneratorWorker::GetProgress() { - return m_ScriptInterface; + CScopeLock lock(m_WorkerMutex); + return m_Progress; } -CScriptValRooted& CMapGenerator::GetMapData() +shared_ptr CMapGeneratorWorker::GetResults() { + CScopeLock lock(m_WorkerMutex); return m_MapData; } -bool CMapGenerator::LoadLibrary(void* cbdata, std::wstring name) +bool CMapGeneratorWorker::LoadLibrary(void* cbdata, std::wstring name) { - CMapGenerator* self = static_cast (cbdata); + CMapGeneratorWorker* self = static_cast(cbdata); return self->LoadScripts(name); } -void CMapGenerator::ExportMap(void* cbdata, CScriptValRooted data) +void CMapGeneratorWorker::ExportMap(void* cbdata, CScriptValRooted data) { - CMapGenerator* self = static_cast (cbdata); + CMapGeneratorWorker* self = static_cast(cbdata); // Copy results - self->m_MapData = data; + CScopeLock lock(self->m_WorkerMutex); + self->m_MapData = self->m_ScriptInterface->WriteStructuredClone(data.get()); + self->m_Progress = 0; } -bool CMapGenerator::LoadScripts(const std::wstring& libraryName) +void CMapGeneratorWorker::SetProgress(void* cbdata, int progress) +{ + CMapGeneratorWorker* self = static_cast(cbdata); + + // Copy data + CScopeLock lock(self->m_WorkerMutex); + self->m_Progress = progress; +} + +void CMapGeneratorWorker::MaybeGC(void* cbdata) +{ + CMapGeneratorWorker* self = static_cast(cbdata); + self->m_ScriptInterface->MaybeGC(); +} + +bool CMapGeneratorWorker::LoadScripts(const std::wstring& libraryName) { // Ignore libraries that are already loaded if (m_LoadedLibraries.find(libraryName) != m_LoadedLibraries.end()) @@ -114,7 +195,7 @@ bool CMapGenerator::LoadScripts(const std::wstring& libraryName) { LOGMESSAGE(L"Loading map generator script '%ls'", it->string().c_str()); - if (!m_ScriptInterface.LoadGlobalScriptFile(*it)) + if (!m_ScriptInterface->LoadGlobalScriptFile(*it)) { LOGERROR(L"Failed to load script '%ls'", it->string().c_str()); return false; @@ -123,3 +204,32 @@ bool CMapGenerator::LoadScripts(const std::wstring& libraryName) return true; } + + + + + + +CMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker()) +{ +} + +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(); +} diff --git a/source/graphics/MapGenerator.h b/source/graphics/MapGenerator.h index a441e47581..2c33ef746a 100644 --- a/source/graphics/MapGenerator.h +++ b/source/graphics/MapGenerator.h @@ -18,40 +18,125 @@ #ifndef INCLUDED_MAPGENERATOR #define INCLUDED_MAPGENERATOR -#include "ps/CStr.h" #include "ps/FileIo.h" +#include "ps/ThreadUtil.h" #include "scriptinterface/ScriptInterface.h" #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 { public: - // constructor CMapGenerator(); + ~CMapGenerator(); - // return success of map generation - bool GenerateMap(const VfsPath& scriptFile, const CScriptValRooted& settings); + /** + * 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); - // accessors - ScriptInterface& GetScriptInterface(); + /** + * Get status of the map generator thread + * + * @return Progress percentage 1-100 if active, 0 when finished, or -1 when + */ + int GetProgress(); - CScriptValRooted& GetMapData(); + /** + * 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(); - // callbacks for script functions - static bool LoadLibrary(void* cbdata, std::wstring name); - - static void ExportMap(void* cbdata, CScriptValRooted data); - private: + CMapGeneratorWorker* m_Worker; - bool LoadScripts(const std::wstring& libraryName); - - ScriptInterface m_ScriptInterface; - CScriptValRooted m_MapData; - std::set m_LoadedLibraries; - boost::rand48 m_MapGenRNG; }; +/** + * 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 - jsvals can't be used across threads/runtimes + */ +class CMapGeneratorWorker +{ +public: + CMapGeneratorWorker(); + ~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 when + */ + 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: +// Mapgen + + /** + * Load all scripts of the given library + * + * @param libraryName String specifying name of the library (subfolder of ../maps/random/) + */ + bool LoadScripts(const std::wstring& libraryName); + + // callbacks for script functions + static bool LoadLibrary(void* cbdata, std::wstring name); + static void ExportMap(void* cbdata, CScriptValRooted data); + static void SetProgress(void* cbdata, int progress); + static void MaybeGC(void* cbdata); + + std::set m_LoadedLibraries; + shared_ptr m_MapData; + boost::rand48 m_MapGenRNG; + int m_Progress; + ScriptInterface* m_ScriptInterface; + VfsPath m_ScriptPath; + std::string m_Settings; + +// Thread + static void* RunThread(void* data); + bool Run(); + + pthread_t m_WorkerThread; + CMutex m_WorkerMutex; +}; + + #endif //INCLUDED_MAPGENERATOR diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index d3602af0e6..3575921883 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -49,7 +49,7 @@ CMapReader::CMapReader() - : xml_reader(0), m_PatchesPerSide(0) + : xml_reader(0), m_PatchesPerSide(0), m_MapGen(0) { cur_terrain_tex = 0; // important - resets generator state @@ -161,7 +161,7 @@ void CMapReader::LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& RegMemFun(this, &CMapReader::LoadPlayerSettings, L"CMapReader::LoadPlayerSettings", 50); // load map generator with random map script - RegMemFun(this, &CMapReader::GenerateMap, L"CMapReader::GenerateMap", 2000); + RegMemFun(this, &CMapReader::GenerateMap, L"CMapReader::GenerateMap", 5000); // parse RMS results into terrain structure RegMemFun(this, &CMapReader::ParseTerrain, L"CMapReader::ParseTerrain", 500); @@ -1037,8 +1037,7 @@ int CMapReader::ReadXML() // finished or failed if (ret <= 0) { - delete xml_reader; - xml_reader = 0; + SAFE_DELETE(xml_reader); } return ret; @@ -1067,28 +1066,54 @@ int CMapReader::LoadRMSettings() int CMapReader::GenerateMap() { - TIMER(L"GenerateMap"); + if (!m_MapGen) + { + // Initialize map generator + m_MapGen = new CMapGenerator(); - CMapGenerator mapGen; + VfsPath scriptPath; + + if (m_ScriptFile.length()) + scriptPath = L"maps/random/"+m_ScriptFile; - VfsPath scriptPath; - - if (m_ScriptFile.length()) - scriptPath = L"maps/random/"+m_ScriptFile; - - // Copy map settings from simulator to mapgen context - CScriptValRooted scriptSettings(mapGen.GetScriptInterface().GetContext(), mapGen.GetScriptInterface().CloneValueFromOtherContext(pSimulation2->GetScriptInterface(), m_ScriptSettings.get())); - - // Try to generate map - if (!mapGen.GenerateMap(scriptPath, scriptSettings)) - { // RMS failed - return to main menu - throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details."); + // Stringify settings to pass across threads + std::string scriptSettings = pSimulation2->GetScriptInterface().StringifyJSON(m_ScriptSettings.get()); + + // Try to generate map + m_MapGen->GenerateMap(scriptPath, scriptSettings); } - // Copy data from mapgen to simulator context - m_MapData = CScriptValRooted(pSimulation2->GetScriptInterface().GetContext(), pSimulation2->GetScriptInterface().CloneValueFromOtherContext(mapGen.GetScriptInterface(), mapGen.GetMapData().get())); + // Check status + int progress = m_MapGen->GetProgress(); + if (progress < 0) + { + // RMS failed - return to main menu + throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details."); + } + else if (progress == 0) + { + // Finished, get results as StructuredClone object, which must be read to obtain the JS val + shared_ptr results = m_MapGen->GetResults(); - return 0; + // Parse data into simulation context + CScriptValRooted data(pSimulation2->GetScriptInterface().GetContext(), pSimulation2->GetScriptInterface().ReadStructuredClone(results)); + if (data.undefined()) + { + // RMS failed - return to main menu + throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details."); + } + else + { + m_MapData = data; + } + } + else + { + // Still working + } + + // return progress + return progress; }; @@ -1312,3 +1337,17 @@ int CMapReader::ParseCamera() return 0; } + +CMapReader::~CMapReader() +{ + // Cleaup objects + if (xml_reader) + { + delete xml_reader; + } + + if (m_MapGen) + { + delete m_MapGen; + } +} diff --git a/source/graphics/MapReader.h b/source/graphics/MapReader.h index e53472a177..1a2ba3598f 100644 --- a/source/graphics/MapReader.h +++ b/source/graphics/MapReader.h @@ -38,8 +38,8 @@ class CTerrainTextureEntry; class CScriptValRooted; class ScriptInterface; class CGameView; - class CXMLReader; +class CMapGenerator; class CMapReader : public CMapIO { @@ -48,6 +48,8 @@ class CMapReader : public CMapIO public: // constructor CMapReader(); + ~CMapReader(); + // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful void LoadMap(const VfsPath& pathname, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CSimulation2*, int playerID); @@ -119,6 +121,8 @@ private: CScriptValRooted m_ScriptSettings; CScriptValRooted m_MapData; + CMapGenerator* m_MapGen; + // state latched by LoadMap and held until DelayedLoadFinished CFileUnpacker unpacker; CTerrain* pTerrain; diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 7441ccc9c4..dbc21b67fc 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -1026,8 +1026,8 @@ bool Autostart(const CmdLineArgs& args) else { // Problem with JSON file - CStrW msg = L"Error reading random map script \"" + scriptPath + L"\".\nCheck application log for details."; - throw PSERROR_Game_World_MapLoadFailed(msg.ToUTF8().c_str()); + LOGERROR(L"Error reading random map script '%ls'", scriptPath.c_str()); + throw PSERROR_Game_World_MapLoadFailed("Error reading random map script.\nCheck application log for details."); } // Get optional map size argument (default 12) @@ -1162,9 +1162,11 @@ bool Autostart(const CmdLineArgs& args) void CancelLoad(const CStrW& message) { + //Cancel loader LDR_Cancel(); - // Call the cancelOnError GUI function, but only if it exists + // Call the cancelOnError GUI function, defined in ..gui/common/functions_utility_error.js + // So all GUI pages that load games should include this script if (g_GUI && g_GUI->HasPages()) { JSContext* cx = g_ScriptingHost.getContext(); diff --git a/source/ps/Loader.cpp b/source/ps/Loader.cpp index 92dac41ccb..607f67e23e 100644 --- a/source/ps/Loader.cpp +++ b/source/ps/Loader.cpp @@ -144,17 +144,12 @@ void LDR_EndRegistering() // note: no special notification will be returned by LDR_ProgressiveLoad. void LDR_Cancel() { - // note: calling during registering doesn't make sense - that - // should be an atomic sequence of begin, register [..], end. - debug_assert(state == LOADING); - // the queue doesn't need to be emptied now; that'll happen during the // next LDR_StartRegistering. for now, it is sufficient to set the // state, so that LDR_ProgressiveLoad is a no-op. state = IDLE; } - // helper routine for LDR_ProgressiveLoad. // tries to prevent starting a long task when at the end of a timeslice. static bool HaveTimeForNextTask(double time_left, double time_budget, int estimated_duration_ms) @@ -269,7 +264,7 @@ LibError LDR_ProgressiveLoad(double time_budget, wchar_t* description, size_t ma // .. failed; abort. loading will continue when we're called in // the next iteration of the main loop. // rationale: bail immediately instead of remembering the first - // error that came up so we report can all errors that happen. + // error that came up so we can report all errors that happen. else if(status < 0) { ret = (LibError)status; diff --git a/source/ps/World.cpp b/source/ps/World.cpp index 0d95387b96..a5bc71b1c3 100644 --- a/source/ps/World.cpp +++ b/source/ps/World.cpp @@ -89,7 +89,7 @@ void CWorld::RegisterInit(const CStrW& mapFile, int playerID) { delete reader; LOGERROR(L"Failed to load scenario %ls: %hs", mapfilename.string().c_str(), err.what()); - throw PSERROR_Game_World_MapLoadFailed("Failed to load scenario"); + throw PSERROR_Game_World_MapLoadFailed("Failed to load scenario.\nCheck application log for details."); } } }