2012-06-14 09:06:06 -04:00
# include "Globals.h"
2012-09-23 16:14:04 -04:00
# include "ChunkGenerator.h"
2013-11-27 02:40:59 -05:00
# include "inifile/iniFile.h"
2013-01-25 05:12:29 -05:00
# include "ChunkDesc.h"
# include "ComposableGenerator.h"
2013-04-27 09:38:40 -04:00
# include "Noise3DGenerator.h"
2014-01-10 16:22:54 -05:00
# include "../MersenneTwister.h"
2012-06-14 09:06:06 -04:00
/// If the generation queue size exceeds this number, a warning will be output
2013-11-16 13:55:49 -05:00
const unsigned int QUEUE_WARNING_LIMIT = 1000 ;
2012-06-14 09:06:06 -04:00
/// If the generation queue size exceeds this number, chunks with no clients will be skipped
2013-11-16 13:55:49 -05:00
const unsigned int QUEUE_SKIP_LIMIT = 500 ;
2012-06-14 09:06:06 -04:00
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 09:06:06 -04:00
// cChunkGenerator:
2013-01-25 05:12:29 -05:00
cChunkGenerator : : cChunkGenerator ( void ) :
super ( " cChunkGenerator " ) ,
2014-01-10 16:22:54 -05:00
m_Generator ( NULL ) ,
m_PluginInterface ( NULL ) ,
m_ChunkSink ( NULL )
2012-06-14 09:06:06 -04:00
{
}
cChunkGenerator : : ~ cChunkGenerator ( )
{
Stop ( ) ;
}
2014-01-10 16:22:54 -05:00
bool cChunkGenerator : : Start ( cPluginInterface & a_PluginInterface , cChunkSink & a_ChunkSink , cIniFile & a_IniFile )
2012-06-14 09:06:06 -04:00
{
2014-01-10 16:22:54 -05:00
m_PluginInterface = & a_PluginInterface ;
m_ChunkSink = & a_ChunkSink ;
2012-06-14 09:06:06 -04:00
MTRand rnd ;
m_Seed = a_IniFile . GetValueSetI ( " Seed " , " Seed " , rnd . randInt ( ) ) ;
2013-01-25 05:12:29 -05:00
AString GeneratorName = a_IniFile . GetValueSet ( " Generator " , " Generator " , " Composable " ) ;
2013-11-16 13:55:49 -05:00
2013-04-27 09:38:40 -04:00
if ( NoCaseCompare ( GeneratorName , " Noise3D " ) = = 0 )
2013-01-24 07:15:36 -05:00
{
2013-04-27 09:38:40 -04:00
m_Generator = new cNoise3DGenerator ( * this ) ;
2013-01-24 07:15:36 -05:00
}
2012-06-14 09:06:06 -04:00
else
{
2013-01-25 05:12:29 -05:00
if ( NoCaseCompare ( GeneratorName , " composable " ) ! = 0 )
2012-06-14 09:06:06 -04:00
{
2013-01-25 05:12:29 -05:00
LOGWARN ( " [Generator]::Generator value \" %s \" not recognized, using \" Composable \" . " , GeneratorName . c_str ( ) ) ;
2012-06-14 09:06:06 -04:00
}
2013-01-25 05:12:29 -05:00
m_Generator = new cComposableGenerator ( * this ) ;
2012-06-14 09:06:06 -04:00
}
2013-01-25 05:12:29 -05:00
if ( m_Generator = = NULL )
2012-06-14 09:06:06 -04:00
{
2013-01-25 05:12:29 -05:00
LOGERROR ( " Generator could not start, aborting the server " ) ;
return false ;
2012-06-14 09:06:06 -04:00
}
2013-11-16 13:55:49 -05:00
2014-01-10 16:22:54 -05:00
m_Generator - > Initialize ( a_IniFile ) ;
2013-11-16 13:55:49 -05:00
2013-01-25 05:12:29 -05:00
return super : : Start ( ) ;
2012-06-14 09:06:06 -04:00
}
2013-01-25 05:12:29 -05:00
void cChunkGenerator : : Stop ( void )
2012-06-14 09:06:06 -04:00
{
2013-01-25 05:12:29 -05:00
m_ShouldTerminate = true ;
m_Event . Set ( ) ;
m_evtRemoved . Set ( ) ; // Wake up anybody waiting for empty queue
Wait ( ) ;
2012-06-14 09:06:06 -04:00
2013-01-25 05:12:29 -05:00
delete m_Generator ;
m_Generator = NULL ;
2012-06-14 09:06:06 -04:00
}
void cChunkGenerator : : QueueGenerateChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
{
cCSLock Lock ( m_CS ) ;
2013-11-16 13:55:49 -05:00
2012-06-14 09:06:06 -04:00
// 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[]
2013-11-16 13:55:49 -05:00
2012-06-14 09:06:06 -04:00
// Add to queue, issue a warning if too many:
if ( m_Queue . size ( ) > = QUEUE_WARNING_LIMIT )
{
2014-03-12 13:34:50 -04:00
LOGWARN ( " WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! ( " SIZE_T_FMT " ) " , a_ChunkX , a_ChunkZ , m_Queue . size ( ) ) ;
2012-06-14 09:06:06 -04:00
}
m_Queue . push_back ( cChunkCoords ( a_ChunkX , a_ChunkY , a_ChunkZ ) ) ;
}
2013-11-16 13:55:49 -05:00
2012-06-14 09:06:06 -04:00
m_Event . Set ( ) ;
}
void cChunkGenerator : : GenerateBiomes ( int a_ChunkX , int a_ChunkZ , cChunkDef : : BiomeMap & a_BiomeMap )
{
2013-01-25 05:12:29 -05:00
if ( m_Generator ! = NULL )
2012-09-01 17:37:41 -04:00
{
2013-01-25 05:12:29 -05:00
m_Generator - > GenerateBiomes ( a_ChunkX , a_ChunkZ , a_BiomeMap ) ;
2012-09-01 17:37:41 -04:00
}
2012-06-14 09:06:06 -04: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 )
{
2013-01-25 05:12:29 -05:00
ASSERT ( m_Generator ! = NULL ) ;
return m_Generator - > GetBiomeAt ( a_BlockX , a_BlockZ ) ;
}
BLOCKTYPE cChunkGenerator : : 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 )
{
2014-03-11 17:16:08 -04:00
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 ( ) ) ;
2013-01-25 05:12:29 -05:00
return BlockStringToType ( a_Default ) ;
}
return Block ;
2012-06-14 09:06:06 -04:00
}
void cChunkGenerator : : Execute ( void )
{
2012-07-29 13:51:04 -04: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)
2013-11-16 13:55:49 -05:00
2012-06-14 09:06:06 -04:00
while ( ! m_ShouldTerminate )
{
cCSLock Lock ( m_CS ) ;
2014-01-24 18:56:19 -05:00
while ( m_Queue . empty ( ) )
2012-06-14 09:06:06 -04:00
{
2012-07-29 13:51:04 -04: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 09:06:06 -04:00
cCSUnlock Unlock ( Lock ) ;
m_Event . Wait ( ) ;
if ( m_ShouldTerminate )
{
return ;
}
2012-07-29 13:51:04 -04:00
NumChunksGenerated = 0 ;
GenerationStart = clock ( ) ;
LastReportTick = clock ( ) ;
2012-06-14 09:06:06 -04:00
}
2013-11-16 13:55:49 -05:00
2014-01-24 18:56:19 -05:00
if ( m_Queue . empty ( ) )
{
2014-01-25 09:42:26 -05:00
// Sometimes the queue remains empty
// If so, we can't do any front() operations on it!
2014-01-24 18:56:19 -05:00
continue ;
}
2012-06-14 09:06:06 -04: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 13:51:04 -04: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 09:06:06 -04:00
// Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set
2014-01-10 16:22:54 -05:00
if ( ( coords . m_ChunkY = = 0 ) & & m_ChunkSink - > IsChunkValid ( coords . m_ChunkX , coords . m_ChunkZ ) )
2012-06-14 09:06:06 -04:00
{
LOGD ( " Chunk [%d, %d] already generated, skipping generation " , coords . m_ChunkX , coords . m_ChunkZ ) ;
// Already generated, ignore request
continue ;
}
2013-11-16 13:55:49 -05:00
2014-01-10 16:22:54 -05:00
if ( SkipEnabled & & ! m_ChunkSink - > HasChunkAnyClients ( coords . m_ChunkX , coords . m_ChunkZ ) )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
LOGWARNING ( " Chunk generator overloaded, skipping chunk [%d, %d] " , coords . m_ChunkX , coords . m_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
continue ;
}
2013-11-16 13:55:49 -05:00
2012-06-14 09:06:06 -04:00
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 ) ;
2013-11-16 13:55:49 -05:00
2012-07-29 13:51:04 -04:00
NumChunksGenerated + + ;
2012-06-14 09:06:06 -04:00
} // while (!bStop)
}
void cChunkGenerator : : DoGenerate ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
2014-01-10 16:22:54 -05:00
ASSERT ( m_PluginInterface ! = NULL ) ;
ASSERT ( m_ChunkSink ! = NULL ) ;
2013-02-08 15:57:42 -05:00
cChunkDesc ChunkDesc ( a_ChunkX , a_ChunkZ ) ;
2014-01-10 16:22:54 -05:00
m_PluginInterface - > CallHookChunkGenerating ( ChunkDesc ) ;
2013-02-08 11:01:44 -05:00
m_Generator - > DoGenerate ( a_ChunkX , a_ChunkZ , ChunkDesc ) ;
2014-01-10 16:22:54 -05:00
m_PluginInterface - > CallHookChunkGenerated ( ChunkDesc ) ;
2013-11-16 13:55:49 -05:00
2013-05-05 15:56:45 -04:00
# ifdef _DEBUG
// Verify that the generator has produced valid data:
ChunkDesc . VerifyHeightmap ( ) ;
# endif
2013-11-16 13:55:49 -05:00
2014-01-10 16:22:54 -05:00
m_ChunkSink - > OnChunkGenerated ( ChunkDesc ) ;
2012-06-14 09:06:06 -04:00
}
2013-01-25 05:12:29 -05:00
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-01-25 05:12:29 -05:00
// cChunkGenerator::cGenerator:
cChunkGenerator : : cGenerator : : cGenerator ( cChunkGenerator & a_ChunkGenerator ) :
m_ChunkGenerator ( a_ChunkGenerator )
{
}
2014-01-10 16:22:54 -05:00
void cChunkGenerator : : cGenerator : : Initialize ( cIniFile & a_IniFile )
2013-01-25 05:12:29 -05:00
{
UNUSED ( a_IniFile ) ;
}
EMCSBiome cChunkGenerator : : cGenerator : : GetBiomeAt ( int a_BlockX , int a_BlockZ )
{
cChunkDef : : BiomeMap Biomes ;
int Y = 0 ;
int ChunkX , ChunkZ ;
2014-01-10 16:22:54 -05:00
cChunkDef : : AbsoluteToRelative ( a_BlockX , Y , a_BlockZ , ChunkX , ChunkZ ) ;
2013-01-25 05:12:29 -05:00
GenerateBiomes ( ChunkX , ChunkZ , Biomes ) ;
return cChunkDef : : GetBiome ( Biomes , a_BlockX , a_BlockZ ) ;
}