2012-06-14 13:06:06 +00:00
#include "Globals.h"
2012-09-23 20:14:04 +00:00
#include "ChunkGenerator.h"
2012-09-23 22:09:57 +00:00
#include "../World.h"
2012-09-23 20:14:04 +00:00
#include "../../iniFile/iniFile.h"
2012-06-14 13:06:06 +00:00
#include "BioGen.h"
#include "HeiGen.h"
#include "CompoGen.h"
#include "StructGen.h"
#include "FinishGen.h"
2012-09-23 22:09:57 +00:00
#include "../Root.h"
#include "../PluginManager.h"
#include "../LuaChunk.h"
2012-07-19 21:49:27 +00:00
#include "Ravines.h"
2012-07-22 18:51:38 +00:00
#include "Caves.h"
2012-06-14 13:06:06 +00:00
/// If the generation queue size exceeds this number, a warning will be output
const int QUEUE_WARNING_LIMIT = 1000 ;
/// If the generation queue size exceeds this number, chunks with no clients will be skipped
const int QUEUE_SKIP_LIMIT = 500 ;
static BLOCKTYPE GetIniBlock ( cIniFile & a_IniFile , const AString & a_SectionName , const AString & a_ValueName , const AString & a_Default )
{
AString BlockType = a_IniFile . GetValueSet ( a_SectionName , a_ValueName , a_Default );
BLOCKTYPE Block = BlockStringToType ( BlockType );
if ( Block < 0 )
{
LOGWARN ( "[&s].%s Could not parse block value \" %s \" . Using default: \" %s \" ." , a_SectionName . c_str (), a_ValueName . c_str (), BlockType . c_str (), a_Default . c_str ());
return BlockStringToType ( a_Default );
}
return Block ;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cChunkGenerator:
cChunkGenerator :: cChunkGenerator ( void )
: super ( "cChunkGenerator" )
, m_World ( NULL )
, m_BiomeGen ( NULL )
, m_HeightGen ( NULL )
, m_CompositionGen ( NULL )
{
}
cChunkGenerator ::~ cChunkGenerator ()
{
Stop ();
}
bool cChunkGenerator :: Start ( cWorld * a_World , cIniFile & a_IniFile )
{
MTRand rnd ;
m_World = a_World ;
m_Seed = a_IniFile . GetValueSetI ( "Seed" , "Seed" , rnd . randInt ());
InitBiomeGen ( a_IniFile );
InitHeightGen ( a_IniFile );
InitCompositionGen ( a_IniFile );
InitStructureGens ( a_IniFile );
InitFinishGens ( a_IniFile );
a_IniFile . WriteFile ();
return super :: Start ();
}
void cChunkGenerator :: Stop ( void )
{
m_ShouldTerminate = true ;
m_Event . Set ();
m_evtRemoved . Set (); // Wake up anybody waiting for empty queue
Wait ();
// Delete the generating composition:
for ( cFinishGenList :: const_iterator itr = m_FinishGens . begin (); itr != m_FinishGens . end (); ++ itr )
{
delete * itr ;
}
m_FinishGens . clear ();
for ( cStructureGenList :: const_iterator itr = m_StructureGens . begin (); itr != m_StructureGens . end (); ++ itr )
{
delete * itr ;
}
m_StructureGens . clear ();
delete m_CompositionGen ;
m_CompositionGen = NULL ;
delete m_HeightGen ;
m_HeightGen = NULL ;
delete m_BiomeGen ;
m_BiomeGen = NULL ;
}
void cChunkGenerator :: InitBiomeGen ( cIniFile & a_IniFile )
{
AString BiomeGenName = a_IniFile . GetValueSet ( "Generator" , "BiomeGen" , "" );
if ( BiomeGenName . empty ())
{
LOGWARN ( "[Generator]::BiomeGen value not found in world.ini, using \" DistortedVoronoi \" ." );
BiomeGenName = "DistortedVoronoi" ;
}
bool CacheOffByDefault = false ;
if ( NoCaseCompare ( BiomeGenName , "constant" ) == 0 )
{
AString Biome = a_IniFile . GetValueSet ( "Generator" , "ConstantBiome" , "Plains" );
EMCSBiome b = StringToBiome ( Biome );
if ( b == - 1 )
{
LOGWARN ( "[Generator]::ConstantBiome value \" %s \" not recognized, using \" Plains \" ." , Biome . c_str ());
b = biPlains ;
}
m_BiomeGen = new cBioGenConstant ( b );
CacheOffByDefault = true ; // we're generating faster than a cache would retrieve data :)
}
else if ( NoCaseCompare ( BiomeGenName , "checkerboard" ) == 0 )
{
int BiomeSize = a_IniFile . GetValueSetI ( "Generator" , "CheckerboardBiomeSize" , 64 );
AString Biomes = a_IniFile . GetValueSet ( "Generator" , "CheckerBoardBiomes" , "" );
m_BiomeGen = new cBioGenCheckerboard ( BiomeSize , Biomes );
CacheOffByDefault = true ; // we're (probably) generating faster than a cache would retrieve data
}
else if ( NoCaseCompare ( BiomeGenName , "voronoi" ) == 0 )
{
int CellSize = a_IniFile . GetValueSetI ( "Generator" , "VoronoiCellSize" , 64 );
AString Biomes = a_IniFile . GetValueSet ( "Generator" , "VoronoiBiomes" , "" );
m_BiomeGen = new cBioGenVoronoi ( m_Seed , CellSize , Biomes );
}
else
{
if ( NoCaseCompare ( BiomeGenName , "distortedvoronoi" ) != 0 )
{
LOGWARNING ( "Unknown BiomeGen \" %s \" , using \" DistortedVoronoi \" instead." , BiomeGenName . c_str ());
}
int CellSize = a_IniFile . GetValueSetI ( "Generator" , "DistortedVoronoiCellSize" , 96 );
AString Biomes = a_IniFile . GetValueSet ( "Generator" , "DistortedVoronoiBiomes" , "" );
m_BiomeGen = new cBioGenDistortedVoronoi ( m_Seed , CellSize , Biomes );
}
// Add a cache, if requested:
int CacheSize = a_IniFile . GetValueSetI ( "Generator" , "BiomeGenCacheSize" , CacheOffByDefault ? 0 : 64 );
if ( CacheSize > 0 )
{
if ( CacheSize < 4 )
{
LOGWARNING ( "Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d" ,
CacheSize , 4
);
CacheSize = 4 ;
}
LOGINFO ( "Using a cache for biomegen of size %d." , CacheSize );
m_BiomeGen = new cBioGenCache ( m_BiomeGen , CacheSize );
}
}
void cChunkGenerator :: InitHeightGen ( cIniFile & a_IniFile )
{
AString HeightGenName = a_IniFile . GetValueSet ( "Generator" , "HeightGen" , "" );
if ( HeightGenName . empty ())
{
LOGWARN ( "[Generator]::HeightGen value not found in world.ini, using \" Biomal \" ." );
HeightGenName = "Biomal" ;
}
bool CacheOffByDefault = false ;
if ( NoCaseCompare ( HeightGenName , "flat" ) == 0 )
{
int Height = a_IniFile . GetValueSetI ( "Generator" , "FlatHeight" , 5 );
m_HeightGen = new cHeiGenFlat ( Height );
CacheOffByDefault = true ; // We're generating faster than a cache would retrieve data
}
else if ( NoCaseCompare ( HeightGenName , "classic" ) == 0 )
{
// These used to be in terrain.ini, but now they are in world.ini (so that multiple worlds can have different values):
float HeightFreq1 = ( float ) a_IniFile . GetValueSetF ( "Generator" , "ClassicHeightFreq1" , 0.1 );
float HeightFreq2 = ( float ) a_IniFile . GetValueSetF ( "Generator" , "ClassicHeightFreq2" , 1.0 );
float HeightFreq3 = ( float ) a_IniFile . GetValueSetF ( "Generator" , "ClassicHeightFreq3" , 2.0 );
float HeightAmp1 = ( float ) a_IniFile . GetValueSetF ( "Generator" , "ClassicHeightAmp1" , 1.0 );
float HeightAmp2 = ( float ) a_IniFile . GetValueSetF ( "Generator" , "ClassicHeightAmp2" , 0.5 );
float HeightAmp3 = ( float ) a_IniFile . GetValueSetF ( "Generator" , "ClassicHeightAmp3" , 0.5 );
m_HeightGen = new cHeiGenClassic ( m_Seed , HeightFreq1 , HeightAmp1 , HeightFreq2 , HeightAmp2 , HeightFreq3 , HeightAmp3 );
}
else // "biomal" or <not found>
{
if ( NoCaseCompare ( HeightGenName , "biomal" ) != 0 )
{
LOGWARN ( "Unknown HeightGen \" %s \" , using \" Biomal \" instead." , HeightGenName . c_str ());
}
m_HeightGen = new cHeiGenBiomal ( m_Seed , * m_BiomeGen );
}
// Add a cache, if requested:
int CacheSize = a_IniFile . GetValueSetI ( "Generator" , "HeightGenCacheSize" , CacheOffByDefault ? 0 : 64 );
if ( CacheSize > 0 )
{
if ( CacheSize < 4 )
{
LOGWARNING ( "Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d" ,
CacheSize , 4
);
CacheSize = 4 ;
}
LOGINFO ( "Using a cache for Heightgen of size %d." , CacheSize );
m_HeightGen = new cHeiGenCache ( m_HeightGen , CacheSize );
}
}
void cChunkGenerator :: InitCompositionGen ( cIniFile & a_IniFile )
{
AString CompoGenName = a_IniFile . GetValueSet ( "Generator" , "CompositionGen" , "" );
if ( CompoGenName . empty ())
{
LOGWARN ( "[Generator]::CompositionGen value not found in world.ini, using \" Biomal \" ." );
CompoGenName = "Biomal" ;
}
if ( NoCaseCompare ( CompoGenName , "sameblock" ) == 0 )
{
2012-07-27 21:00:11 +00:00
int Block = GetIniBlock ( a_IniFile , "Generator" , "SameBlockType" , "stone" );
2012-06-14 13:06:06 +00:00
bool Bedrocked = ( a_IniFile . GetValueSetI ( "Generator" , "SameBlockBedrocked" , 1 ) != 0 );
m_CompositionGen = new cCompoGenSameBlock (( BLOCKTYPE ) Block , Bedrocked );
}
else if ( NoCaseCompare ( CompoGenName , "debugbiomes" ) == 0 )
{
m_CompositionGen = new cCompoGenDebugBiomes ;
}
else if ( NoCaseCompare ( CompoGenName , "classic" ) == 0 )
{
int SeaLevel = a_IniFile . GetValueSetI ( "Generator" , "ClassicSeaLevel" , 60 );
int BeachHeight = a_IniFile . GetValueSetI ( "Generator" , "ClassicBeachHeight" , 2 );
int BeachDepth = a_IniFile . GetValueSetI ( "Generator" , "ClassicBeachDepth" , 4 );
BLOCKTYPE BlockTop = GetIniBlock ( a_IniFile , "Generator" , "ClassicBlockTop" , "grass" );
BLOCKTYPE BlockMiddle = GetIniBlock ( a_IniFile , "Generator" , "ClassicBlockMiddle" , "dirt" );
BLOCKTYPE BlockBottom = GetIniBlock ( a_IniFile , "Generator" , "ClassicBlockBottom" , "stone" );
BLOCKTYPE BlockBeach = GetIniBlock ( a_IniFile , "Generator" , "ClassicBlockBeach" , "sand" );
BLOCKTYPE BlockBeachBottom = GetIniBlock ( a_IniFile , "Generator" , "ClassicBlockBeachBottom" , "sandstone" );
2012-10-14 17:05:00 +00:00
BLOCKTYPE BlockSea = GetIniBlock ( a_IniFile , "Generator" , "ClassicBlockSea" , "stationarywater" );
2012-06-14 13:06:06 +00:00
m_CompositionGen = new cCompoGenClassic (
SeaLevel , BeachHeight , BeachDepth , BlockTop , BlockMiddle , BlockBottom , BlockBeach ,
BlockBeachBottom , BlockSea
);
}
else
{
if ( NoCaseCompare ( CompoGenName , "biomal" ) != 0 )
{
LOGWARN ( "Unknown CompositionGen \" %s \" , using \" biomal \" instead." , CompoGenName . c_str ());
}
int SeaLevel = a_IniFile . GetValueSetI ( "Generator" , "BiomalSeaLevel" , 62 );
m_CompositionGen = new cCompoGenBiomal ( m_Seed , SeaLevel );
}
}
void cChunkGenerator :: InitStructureGens ( cIniFile & a_IniFile )
{
AString Structures = a_IniFile . GetValueSet ( "Generator" , "Structures" , "Trees,MarbleCaves,OreNests" );
AStringVector Str = StringSplit ( Structures , "," );
for ( AStringVector :: const_iterator itr = Str . begin (); itr != Str . end (); ++ itr )
{
if ( NoCaseCompare ( * itr , "trees" ) == 0 )
{
m_StructureGens . push_back ( new cStructGenTrees ( m_Seed , m_BiomeGen , m_HeightGen , m_CompositionGen ));
}
else if ( NoCaseCompare ( * itr , "marblecaves" ) == 0 )
{
m_StructureGens . push_back ( new cStructGenMarbleCaves ( m_Seed ));
}
2012-07-22 18:51:38 +00:00
else if ( NoCaseCompare ( * itr , "dualridgecaves" ) == 0 )
{
float Threshold = ( float ) a_IniFile . GetValueSetF ( "Generator" , "DualRidgeCavesThreshold" , 0.3 );
m_StructureGens . push_back ( new cStructGenDualRidgeCaves ( m_Seed , Threshold ));
}
2012-06-14 13:06:06 +00:00
else if ( NoCaseCompare ( * itr , "orenests" ) == 0 )
{
m_StructureGens . push_back ( new cStructGenOreNests ( m_Seed ));
}
2012-07-19 21:49:27 +00:00
else if ( NoCaseCompare ( * itr , "ravines" ) == 0 )
{
m_StructureGens . push_back ( new cStructGenRavines ( m_Seed , 128 ));
}
2012-07-27 16:47:55 +00:00
//*
2012-07-22 18:51:38 +00:00
// TODO: Not implemented yet; need a name
2012-07-27 16:47:55 +00:00
else if ( NoCaseCompare ( * itr , "wormnestcaves" ) == 0 )
2012-07-22 18:51:38 +00:00
{
m_StructureGens . push_back ( new cStructGenWormNestCaves ( m_Seed ));
}
2012-07-27 16:47:55 +00:00
//*/
2012-06-14 13:06:06 +00:00
else
{
LOGWARNING ( "Unknown structure generator: \" %s \" . Ignoring." , itr -> c_str ());
}
} // for itr - Str[]
}
void cChunkGenerator :: InitFinishGens ( cIniFile & a_IniFile )
{
2012-07-27 21:00:11 +00:00
AString Structures = a_IniFile . GetValueSet ( "Generator" , "Finishers" , "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava" );
2012-06-14 13:06:06 +00:00
AStringVector Str = StringSplit ( Structures , "," );
for ( AStringVector :: const_iterator itr = Str . begin (); itr != Str . end (); ++ itr )
{
if ( NoCaseCompare ( * itr , "SprinkleFoliage" ) == 0 )
{
m_FinishGens . push_back ( new cFinishGenSprinkleFoliage ( m_Seed ));
}
else if ( NoCaseCompare ( * itr , "Snow" ) == 0 )
{
m_FinishGens . push_back ( new cFinishGenSnow );
}
else if ( NoCaseCompare ( * itr , "Ice" ) == 0 )
{
m_FinishGens . push_back ( new cFinishGenIce );
}
2012-07-03 19:16:09 +00:00
else if ( NoCaseCompare ( * itr , "Lilypads" ) == 0 )
{
m_FinishGens . push_back ( new cFinishGenLilypads ( m_Seed ));
}
2012-07-27 21:00:11 +00:00
else if ( NoCaseCompare ( * itr , "BottomLava" ) == 0 )
{
int BottomLavaLevel = a_IniFile . GetValueSetI ( "Generator" , "BottomLavaLevel" , 10 );
m_FinishGens . push_back ( new cFinishGenBottomLava ( BottomLavaLevel ));
}
2012-10-21 20:06:31 +00:00
else if ( NoCaseCompare ( * itr , "PreSimulator" ) == 0 )
{
m_FinishGens . push_back ( new cFinishGenPreSimulator );
}
2012-06-14 13:06:06 +00:00
} // for itr - Str[]
}
void cChunkGenerator :: QueueGenerateChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
{
cCSLock Lock ( m_CS );
// Check if it is already in the queue:
for ( cChunkCoordsList :: iterator itr = m_Queue . begin (); itr != m_Queue . end (); ++ itr )
{
if (( itr -> m_ChunkX == a_ChunkX ) && ( itr -> m_ChunkY == a_ChunkY ) && ( itr -> m_ChunkZ == a_ChunkZ ))
{
// Already in the queue, bail out
return ;
}
} // for itr - m_Queue[]
// Add to queue, issue a warning if too many:
if ( m_Queue . size () >= QUEUE_WARNING_LIMIT )
{
LOGWARN ( "WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%i)" , a_ChunkX , a_ChunkZ , m_Queue . size ());
}
m_Queue . push_back ( cChunkCoords ( a_ChunkX , a_ChunkY , a_ChunkZ ));
}
m_Event . Set ();
}
void cChunkGenerator :: GenerateBiomes ( int a_ChunkX , int a_ChunkZ , cChunkDef :: BiomeMap & a_BiomeMap )
{
2012-09-01 21:37:41 +00:00
if ( m_BiomeGen != NULL ) // Quick fix for generator deinitializing before the world storage finishes loading
{
m_BiomeGen -> GenBiomes ( a_ChunkX , a_ChunkZ , a_BiomeMap );
}
2012-06-14 13:06:06 +00:00
}
void cChunkGenerator :: WaitForQueueEmpty ( void )
{
cCSLock Lock ( m_CS );
while ( ! m_ShouldTerminate && ! m_Queue . empty ())
{
cCSUnlock Unlock ( Lock );
m_evtRemoved . Wait ();
}
}
int cChunkGenerator :: GetQueueLength ( void )
{
cCSLock Lock ( m_CS );
return ( int ) m_Queue . size ();
}
EMCSBiome cChunkGenerator :: 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 );
m_BiomeGen -> GenBiomes ( ChunkX , ChunkZ , Biomes );
return cChunkDef :: GetBiome ( Biomes , a_BlockX , a_BlockZ );
}
void cChunkGenerator :: Execute ( void )
{
2012-07-29 17:51:04 +00:00
// To be able to display performance information, the generator counts the chunks generated.
// When the queue gets empty, the count is reset, so that waiting for the queue is not counted into the total time.
int NumChunksGenerated = 0 ; // Number of chunks generated since the queue was last empty
clock_t GenerationStart = clock (); // Clock tick when the queue started to fill
clock_t LastReportTick = clock (); // Clock tick of the last report made (so that performance isn't reported too often)
2012-06-14 13:06:06 +00:00
while ( ! m_ShouldTerminate )
{
cCSLock Lock ( m_CS );
while ( m_Queue . size () == 0 )
{
2012-07-29 17:51:04 +00:00
if (( NumChunksGenerated > 16 ) && ( clock () - LastReportTick > CLOCKS_PER_SEC ))
{
LOG ( "Chunk generator performance: %.2f ch/s (%d ch total)" ,
( double ) NumChunksGenerated * CLOCKS_PER_SEC / ( clock () - GenerationStart ),
NumChunksGenerated
);
}
2012-06-14 13:06:06 +00:00
cCSUnlock Unlock ( Lock );
m_Event . Wait ();
if ( m_ShouldTerminate )
{
return ;
}
2012-07-29 17:51:04 +00:00
NumChunksGenerated = 0 ;
GenerationStart = clock ();
LastReportTick = clock ();
2012-06-14 13:06:06 +00:00
}
cChunkCoords coords = m_Queue . front (); // Get next coord from queue
m_Queue . erase ( m_Queue . begin () ); // Remove coordinate from queue
bool SkipEnabled = ( m_Queue . size () > QUEUE_SKIP_LIMIT );
Lock . Unlock (); // Unlock ASAP
m_evtRemoved . Set ();
2012-07-29 17:51:04 +00:00
// Display perf info once in a while:
if (( NumChunksGenerated > 16 ) && ( clock () - LastReportTick > 2 * CLOCKS_PER_SEC ))
{
LOG ( "Chunk generator performance: %.2f ch/s (%d ch total)" ,
( double ) NumChunksGenerated * CLOCKS_PER_SEC / ( clock () - GenerationStart ),
NumChunksGenerated
);
LastReportTick = clock ();
}
2012-06-14 13:06:06 +00:00
// 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_ChunkY , 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_ChunkY , coords . m_ChunkZ ))
{
LOGWARNING ( "Chunk generator overloaded, skipping chunk [%d, %d, %d]" , coords . m_ChunkX , coords . m_ChunkY , coords . m_ChunkZ );
continue ;
}
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 );
2012-07-29 17:51:04 +00:00
NumChunksGenerated ++ ;
2012-06-14 13:06:06 +00:00
} // while (!bStop)
}
void cChunkGenerator :: DoGenerate ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
cChunkDef :: BiomeMap BiomeMap ;
cChunkDef :: BlockTypes BlockTypes ;
cChunkDef :: BlockNibbles BlockMeta ;
cChunkDef :: HeightMap HeightMap ;
cEntityList Entities ;
cBlockEntityList BlockEntities ;
cLuaChunk LuaChunk ( BlockTypes , BlockMeta , HeightMap , BiomeMap );
2012-06-14 17:30:37 +00:00
if ( cRoot :: Get () -> GetPluginManager () -> CallHookChunkGenerating ( m_World , a_ChunkX , a_ChunkZ , & LuaChunk ) )
2012-06-14 13:06:06 +00:00
{
// A plugin interrupted generation, handle something plugin specific
if ( LuaChunk . IsUsingDefaultBiomes () )
{
m_BiomeGen -> GenBiomes ( a_ChunkX , a_ChunkZ , BiomeMap );
}
if ( LuaChunk . IsUsingDefaultComposition () )
{
m_CompositionGen -> ComposeTerrain ( a_ChunkX , a_ChunkZ , BlockTypes , BlockMeta , HeightMap , BiomeMap , Entities , BlockEntities );
}
if ( LuaChunk . IsUsingDefaultStructures () )
{
for ( cStructureGenList :: iterator itr = m_StructureGens . begin (); itr != m_StructureGens . end (); ++ itr )
{
( * itr ) -> GenStructures ( a_ChunkX , a_ChunkZ , BlockTypes , BlockMeta , HeightMap , Entities , BlockEntities );
} // for itr - m_StructureGens[]
}
if ( LuaChunk . IsUsingDefaultFinish () )
{
for ( cFinishGenList :: iterator itr = m_FinishGens . begin (); itr != m_FinishGens . end (); ++ itr )
{
( * itr ) -> GenFinish ( a_ChunkX , a_ChunkZ , BlockTypes , BlockMeta , HeightMap , BiomeMap , Entities , BlockEntities );
} // for itr - m_FinishGens[]
}
}
else
{
// Use the composed generator:
m_BiomeGen -> GenBiomes ( a_ChunkX , a_ChunkZ , BiomeMap );
m_HeightGen -> GenHeightMap ( a_ChunkX , a_ChunkZ , HeightMap );
m_CompositionGen -> ComposeTerrain ( a_ChunkX , a_ChunkZ , BlockTypes , BlockMeta , HeightMap , BiomeMap , Entities , BlockEntities );
for ( cStructureGenList :: iterator itr = m_StructureGens . begin (); itr != m_StructureGens . end (); ++ itr )
{
( * itr ) -> GenStructures ( a_ChunkX , a_ChunkZ , BlockTypes , BlockMeta , HeightMap , Entities , BlockEntities );
} // for itr - m_StructureGens[]
for ( cFinishGenList :: iterator itr = m_FinishGens . begin (); itr != m_FinishGens . end (); ++ itr )
{
( * itr ) -> GenFinish ( a_ChunkX , a_ChunkZ , BlockTypes , BlockMeta , HeightMap , BiomeMap , Entities , BlockEntities );
} // for itr - m_FinishGens[]
}
m_World -> SetChunkData (
a_ChunkX , a_ChunkY , a_ChunkZ ,
BlockTypes , BlockMeta ,
NULL , NULL , // We don't have lighting, chunk will be lighted when needed
& HeightMap , & BiomeMap ,
Entities , BlockEntities ,
true
);
cRoot :: Get () -> GetPluginManager () -> CallHook ( cPluginManager :: E_PLUGIN_CHUNK_GENERATED , 3 , m_World , a_ChunkX , a_ChunkZ );
}