1
0
forked from 0ad/0ad

Random maps generated in their own thread, loading GUI is updated with progress.

Fixes some bugs in game loader error handling.

This was SVN commit r9220.
This commit is contained in:
historic_bruno 2011-04-10 05:31:18 +00:00
parent 469d0fe5c5
commit 942a45372c
11 changed files with 356 additions and 90 deletions

View File

@ -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

View File

@ -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
);

View File

@ -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);

View File

@ -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);
}

View File

@ -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<void*> (this));
// Replace RNG with a seeded deterministic function
m_ScriptInterface.ReplaceNondeterministicFunctions(m_MapGenRNG);
// functions for RMS
m_ScriptInterface.RegisterFunction<bool, std::wstring, CMapGenerator::LoadLibrary>("LoadLibrary");
m_ScriptInterface.RegisterFunction<void, CScriptValRooted, CMapGenerator::ExportMap>("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<CMapGeneratorWorker*>(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<void*> (this));
// Replace RNG with a seeded deterministic function
m_ScriptInterface->ReplaceNondeterministicFunctions(m_MapGenRNG);
// Functions for RMS
m_ScriptInterface->RegisterFunction<bool, std::wstring, CMapGeneratorWorker::LoadLibrary>("LoadLibrary");
m_ScriptInterface->RegisterFunction<void, CScriptValRooted, CMapGeneratorWorker::ExportMap>("ExportMap");
m_ScriptInterface->RegisterFunction<void, int, CMapGeneratorWorker::SetProgress>("SetProgress");
m_ScriptInterface->RegisterFunction<void, CMapGeneratorWorker::MaybeGC>("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<ScriptInterface::StructuredClone> 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<CMapGenerator*> (cbdata);
CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(cbdata);
return self->LoadScripts(name);
}
void CMapGenerator::ExportMap(void* cbdata, CScriptValRooted data)
void CMapGeneratorWorker::ExportMap(void* cbdata, CScriptValRooted data)
{
CMapGenerator* self = static_cast<CMapGenerator*> (cbdata);
CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(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<CMapGeneratorWorker*>(cbdata);
// Copy data
CScopeLock lock(self->m_WorkerMutex);
self->m_Progress = progress;
}
void CMapGeneratorWorker::MaybeGC(void* cbdata)
{
CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(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<ScriptInterface::StructuredClone> CMapGenerator::GetResults()
{
return m_Worker->GetResults();
}

View File

@ -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 <boost/random/linear_congruential.hpp>
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<ScriptInterface::StructuredClone> 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<std::wstring> 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<ScriptInterface::StructuredClone> 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<std::wstring> m_LoadedLibraries;
shared_ptr<ScriptInterface::StructuredClone> 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

View File

@ -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<ScriptInterface::StructuredClone> 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;
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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.");
}
}
}