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:
parent
469d0fe5c5
commit
942a45372c
@ -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
|
||||
|
@ -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
|
||||
);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user