1
0

Decoupled cChunkGenerator from cWorld and cRoot.

Now the chunk generator can be used by other projects without depending on the two hugest structures in MCS.
This commit is contained in:
madmaxoft 2014-01-10 22:22:54 +01:00
parent 712e6e0bb2
commit cea997426b
10 changed files with 188 additions and 50 deletions

View File

@ -2,13 +2,11 @@
#include "Globals.h"
#include "ChunkGenerator.h"
#include "../World.h"
#include "inifile/iniFile.h"
#include "../Root.h"
#include "../Bindings/PluginManager.h"
#include "ChunkDesc.h"
#include "ComposableGenerator.h"
#include "Noise3DGenerator.h"
#include "../MersenneTwister.h"
@ -29,8 +27,9 @@ const unsigned int QUEUE_SKIP_LIMIT = 500;
cChunkGenerator::cChunkGenerator(void) :
super("cChunkGenerator"),
m_World(NULL),
m_Generator(NULL)
m_Generator(NULL),
m_PluginInterface(NULL),
m_ChunkSink(NULL)
{
}
@ -47,10 +46,12 @@ cChunkGenerator::~cChunkGenerator()
bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile)
bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile)
{
m_PluginInterface = &a_PluginInterface;
m_ChunkSink = &a_ChunkSink;
MTRand rnd;
m_World = a_World;
m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt());
AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable");
@ -73,7 +74,7 @@ bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile)
return false;
}
m_Generator->Initialize(a_World, a_IniFile);
m_Generator->Initialize(a_IniFile);
return super::Start();
}
@ -237,14 +238,14 @@ void cChunkGenerator::Execute(void)
}
// Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set
if ((coords.m_ChunkY == 0) && m_World->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
if ((coords.m_ChunkY == 0) && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
{
LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
// Already generated, ignore request
continue;
}
if (SkipEnabled && !m_World->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
{
LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
continue;
@ -253,9 +254,6 @@ void cChunkGenerator::Execute(void)
LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
// Save the chunk right after generating, so that we don't have to generate it again on next run
m_World->GetStorage().QueueSaveChunk(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
NumChunksGenerated++;
} // while (!bStop)
}
@ -265,27 +263,20 @@ void cChunkGenerator::Execute(void)
void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
ASSERT(m_PluginInterface != NULL);
ASSERT(m_ChunkSink != NULL);
cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ);
cRoot::Get()->GetPluginManager()->CallHookChunkGenerating(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc);
m_PluginInterface->CallHookChunkGenerating(ChunkDesc);
m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc);
cRoot::Get()->GetPluginManager()->CallHookChunkGenerated(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc);
m_PluginInterface->CallHookChunkGenerated(ChunkDesc);
#ifdef _DEBUG
// Verify that the generator has produced valid data:
ChunkDesc.VerifyHeightmap();
#endif
cChunkDef::BlockNibbles BlockMetas;
ChunkDesc.CompressBlockMetas(BlockMetas);
m_World->SetChunkData(
a_ChunkX, a_ChunkZ,
ChunkDesc.GetBlockTypes(), BlockMetas,
NULL, NULL, // We don't have lighting, chunk will be lighted when needed
&ChunkDesc.GetHeightMap(), &ChunkDesc.GetBiomeMap(),
ChunkDesc.GetEntities(), ChunkDesc.GetBlockEntities(),
true
);
m_ChunkSink->OnChunkGenerated(ChunkDesc);
}
@ -304,9 +295,8 @@ cChunkGenerator::cGenerator::cGenerator(cChunkGenerator & a_ChunkGenerator) :
void cChunkGenerator::cGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
void cChunkGenerator::cGenerator::Initialize(cIniFile & a_IniFile)
{
m_World = a_World;
UNUSED(a_IniFile);
}
@ -319,7 +309,7 @@ EMCSBiome cChunkGenerator::cGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
cChunkDef::BiomeMap Biomes;
int Y = 0;
int ChunkX, ChunkZ;
cWorld::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, Y, ChunkZ);
cChunkDef::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, ChunkZ);
GenerateBiomes(ChunkX, ChunkZ, Biomes);
return cChunkDef::GetBiome(Biomes, a_BlockX, a_BlockZ);
}

View File

@ -26,7 +26,6 @@ If the generator queue is overloaded, the generator skips chunks with no clients
// fwd:
class cWorld;
class cIniFile;
class cChunkDesc;
@ -40,7 +39,7 @@ class cChunkGenerator :
typedef cIsThread super;
public:
/// The interface that a class has to implement to become a generator
/** The interface that a class has to implement to become a generator */
class cGenerator
{
public:
@ -48,7 +47,7 @@ public:
virtual ~cGenerator() {} ; // Force a virtual destructor
/// Called to initialize the generator on server startup.
virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile);
virtual void Initialize(cIniFile & a_IniFile);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
@ -61,14 +60,59 @@ public:
protected:
cChunkGenerator & m_ChunkGenerator;
cWorld * m_World;
} ;
/** The interface through which the plugins are called for their OnChunkGenerating / OnChunkGenerated hooks. */
class cPluginInterface
{
public:
// Force a virtual destructor
virtual ~cPluginInterface() {}
/** Called when the chunk is about to be generated.
The generator may be partly or fully overriden by the implementation
*/
virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) = 0;
/** Called after the chunk is generated, before it is handed to the chunk sink.
a_ChunkDesc contains the generated chunk data. Implementation may modify this data.
*/
virtual void CallHookChunkGenerated(cChunkDesc & a_ChunkDesc) = 0;
} ;
/** The interface through which the generated chunks are handed to the cWorld or whoever created us. */
class cChunkSink
{
public:
// Force a virtual destructor
virtual ~cChunkSink() {}
/** Called after the chunk has been generated
The interface may store the chunk, send it over network, whatever.
The chunk is not expected to be modified, but the generator will survive if the implementation
changes the data within. All changes are ignored, though.
*/
virtual void OnChunkGenerated(cChunkDesc & a_ChunkDesc) = 0;
/** Called just before the chunk generation is started,
to verify that it hasn't been generated in the meantime.
If this callback returns true, the chunk is not generated.
*/
virtual bool IsChunkValid(int a_ChunkX, int a_ChunkZ) = 0;
/** Called when the generator is overloaded to skip chunks that are no longer needed.
If this callback returns false, the chunk is not generated.
*/
virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) = 0;
} ;
cChunkGenerator (void);
~cChunkGenerator();
bool Start(cWorld * a_World, cIniFile & a_IniFile);
bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile);
void Stop(void);
/// Queues the chunk for generation; removes duplicate requests
@ -91,8 +135,6 @@ public:
private:
cWorld * m_World;
int m_Seed;
cCriticalSection m_CS;
@ -102,6 +144,13 @@ private:
cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
/** The plugin interface that may modify the generated chunks */
cPluginInterface * m_PluginInterface;
/** The destination where the generated chunks are sent */
cChunkSink * m_ChunkSink;
// cIsThread override:
virtual void Execute(void) override;

View File

@ -157,9 +157,9 @@ cComposableGenerator::~cComposableGenerator()
void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
void cComposableGenerator::Initialize(cIniFile & a_IniFile)
{
super::Initialize(a_World, a_IniFile);
super::Initialize(a_IniFile);
InitBiomeGen(a_IniFile);
InitHeightGen(a_IniFile);
@ -369,13 +369,14 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int Seed = m_ChunkGenerator.GetSeed();
AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator");
eDimension Dimension = StringToDimension(a_IniFile.GetValue("General", "Dimension", "Overworld"));
AStringVector Str = StringSplitAndTrim(Structures, ",");
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
// Finishers, alpha-sorted:
if (NoCaseCompare(*itr, "BottomLava") == 0)
{
int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10;
int DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10;
int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel));
}
@ -389,7 +390,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(*itr, "LavaSprings") == 0)
{
m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World));
m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, Dimension));
}
else if (NoCaseCompare(*itr, "Lilypads") == 0)
{
@ -409,7 +410,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(*itr, "WaterSprings") == 0)
{
m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World));
m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, Dimension));
}
} // for itr - Str[]
}

View File

@ -162,7 +162,7 @@ public:
cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
virtual ~cComposableGenerator();
virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;

View File

@ -520,7 +520,7 @@ void cFinishGenPreSimulator::StationarizeFluid(
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenFluidSprings:
cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World) :
cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension) :
m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other
m_HeightDistribution(255),
m_Fluid(a_Fluid)
@ -529,7 +529,7 @@ cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cI
AString SectionName = IsWater ? "WaterSprings" : "LavaSprings";
AString DefaultHeightDistribution;
int DefaultChance = 0;
switch (a_World.GetDimension())
switch (a_Dimension)
{
case dimNether:
{

View File

@ -164,7 +164,7 @@ class cFinishGenFluidSprings :
public cFinishGen
{
public:
cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World);
cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension);
protected:

View File

@ -150,10 +150,8 @@ cNoise3DGenerator::~cNoise3DGenerator()
void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
void cNoise3DGenerator::Initialize(cIniFile & a_IniFile)
{
m_World = a_World;
// Params:
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);

View File

@ -24,7 +24,7 @@ public:
cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator);
virtual ~cNoise3DGenerator();
virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;

View File

@ -10,6 +10,7 @@
#include "Root.h"
#include "inifile/iniFile.h"
#include "ChunkMap.h"
#include "Generating/ChunkDesc.h"
#include "OSSupport/Timer.h"
// Entities (except mobs):
@ -238,6 +239,7 @@ cWorld::cWorld(const AString & a_WorldName) :
m_SkyDarkness(0),
m_Weather(eWeather_Sunny),
m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
m_GeneratorCallbacks(*this),
m_TickThread(*this)
{
LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
@ -583,7 +585,7 @@ void cWorld::Start(void)
m_Lighting.Start(this);
m_Storage.Start(this, m_StorageSchema);
m_Generator.Start(this, IniFile);
m_Generator.Start(m_GeneratorCallbacks, m_GeneratorCallbacks, IniFile);
m_ChunkSender.Start(this);
m_TickThread.Start();
@ -2843,3 +2845,77 @@ void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld::cChunkGeneratorCallbacks:
cWorld::cChunkGeneratorCallbacks::cChunkGeneratorCallbacks(cWorld & a_World) :
m_World(&a_World)
{
}
void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc)
{
cChunkDef::BlockNibbles BlockMetas;
a_ChunkDesc.CompressBlockMetas(BlockMetas);
m_World->SetChunkData(
a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(),
a_ChunkDesc.GetBlockTypes(), BlockMetas,
NULL, NULL, // We don't have lighting, chunk will be lighted when needed
&a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(),
a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(),
true
);
// Save the chunk right after generating, so that we don't have to generate it again on next run
m_World->GetStorage().QueueSaveChunk(a_ChunkDesc.GetChunkX(), 0, a_ChunkDesc.GetChunkZ());
}
bool cWorld::cChunkGeneratorCallbacks::IsChunkValid(int a_ChunkX, int a_ChunkZ)
{
return m_World->IsChunkValid(a_ChunkX, a_ChunkZ);
}
bool cWorld::cChunkGeneratorCallbacks::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ)
{
return m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ);
}
void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerating(cChunkDesc & a_ChunkDesc)
{
cPluginManager::Get()->CallHookChunkGenerating(
m_World, a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), &a_ChunkDesc
);
}
void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_ChunkDesc)
{
cPluginManager::Get()->CallHookChunkGenerated(
m_World, a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), &a_ChunkDesc
);
}

View File

@ -637,6 +637,27 @@ private:
} ;
/** Implementation of the callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */
class cChunkGeneratorCallbacks :
public cChunkGenerator::cChunkSink,
public cChunkGenerator::cPluginInterface
{
cWorld * m_World;
// cChunkSink overrides:
virtual void OnChunkGenerated (cChunkDesc & a_ChunkDesc) override;
virtual bool IsChunkValid (int a_ChunkX, int a_ChunkZ) override;
virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) override;
// cPluginInterface overrides:
virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) override;
virtual void CallHookChunkGenerated (cChunkDesc & a_ChunkDesc) override;
public:
cChunkGeneratorCallbacks(cWorld & a_World);
} ;
AString m_WorldName;
AString m_IniFileName;
@ -714,6 +735,9 @@ private:
cChunkGenerator m_Generator;
/** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */
cChunkGeneratorCallbacks m_GeneratorCallbacks;
cChunkSender m_ChunkSender;
cLightingThread m_Lighting;
cTickThread m_TickThread;