#include "Globals.h" #include "cChunkGenerator.h" #include "cChunkMap.h" #include "cChunk.h" #include "cWorld.h" #include "cMCLogger.h" typedef std::pair ChunkCoord; typedef std::list< ChunkCoord > ChunkCoordList; /// If the generation queue size exceeds this number, a warning will be output #define QUEUE_WARNING_LIMIT 1000 struct cChunkGenerator::sChunkGeneratorState { cCriticalSection m_CriticalSection; // For protecting the variables in this struct ChunkCoordList GenerateQueue; ChunkCoord CurrentlyGeneratingCoords; cChunk* pCurrentlyGenerating; bool bCurrentlyGenerating; cSemaphore m_Semaphore; cThread * pThread; bool bStop; sChunkGeneratorState(void) : m_Semaphore(1, 0) , pThread( 0 ) , bStop( false ) , bCurrentlyGenerating( false ) , pCurrentlyGenerating( false ) {} }; cChunkGenerator::cChunkGenerator( cChunkMap* a_pChunkMap ) : m_pState( new sChunkGeneratorState ) , m_pChunkMap( a_pChunkMap ) { m_pState->pThread = new cThread( GenerateThread, this, "cChunkGenerator::GenerateThread" ); m_pState->pThread->Start( true ); } cChunkGenerator::~cChunkGenerator() { m_pState->bStop = true; m_pState->m_Semaphore.Signal(); // Signal so thread can continue and exit delete m_pState->pThread; delete m_pState; } void cChunkGenerator::GenerateChunk( int a_X, int a_Z ) { cCSLock Lock(&m_pState->m_CriticalSection); if (m_pState->bCurrentlyGenerating) { if ((m_pState->CurrentlyGeneratingCoords.first == a_X) && (m_pState->CurrentlyGeneratingCoords.second == a_Z)) { return; // Already generating this chunk, so ignore } } m_pState->GenerateQueue.remove( ChunkCoord(a_X, a_Z) ); if (m_pState->GenerateQueue.size() >= QUEUE_WARNING_LIMIT) { LOGWARN("WARNING: Adding chunk (%i, %i) to generation queue; Queue is too big! (%i)", a_X, a_Z, m_pState->GenerateQueue.size() ); } m_pState->GenerateQueue.push_back( ChunkCoord(a_X, a_Z) ); Lock.Unlock(); m_pState->m_Semaphore.Signal(); } void cChunkGenerator::GenerateThread( void* a_Params ) { // Cache some values for easy access (they are all references/pointers) cChunkGenerator * self = (cChunkGenerator*)a_Params; sChunkGeneratorState * m_pState = self->m_pState; ChunkCoordList & GenerateQueue = m_pState->GenerateQueue; cChunkMap & ChunkMap = *self->m_pChunkMap; cCriticalSection * CriticalSection = &m_pState->m_CriticalSection; cSemaphore & Semaphore = m_pState->m_Semaphore; while (!m_pState->bStop) { cCSLock Lock(CriticalSection); if (GenerateQueue.size() == 0) { cCSUnlock Unlock(Lock); Semaphore.Wait(); } if (m_pState->bStop) break; ChunkCoord coord = *GenerateQueue.begin(); // Get next coord from queue GenerateQueue.erase( GenerateQueue.begin() ); // Remove coordinate from queue m_pState->bCurrentlyGenerating = true; m_pState->CurrentlyGeneratingCoords = coord; Lock.Unlock(); // Unlock ASAP ChunkMap.GetWorld()->LockChunks(); if( ChunkMap.GetChunk( coord.first, 0, coord.second ) ) // Make sure it has not been loaded in the meantime. Don't want to generate the same chunk twice { // This is possible when forcing the server to generate a chunk in the main thread ChunkMap.GetWorld()->UnlockChunks(); continue; } ChunkMap.GetWorld()->UnlockChunks(); LOGINFO("cChunkGenerator generating chunk %i %i", coord.first, coord.second ); cChunk* Chunk = new cChunk( coord.first, 0, coord.second, ChunkMap.GetWorld() ); Lock.Lock(); m_pState->pCurrentlyGenerating = Chunk; Lock.Unlock(); Chunk->Initialize(); // Generate the chunk ChunkMap.GetWorld()->LockChunks(); ChunkMap.AddChunk( Chunk ); ChunkMap.GetWorld()->UnlockChunks(); Lock.Lock(); m_pState->bCurrentlyGenerating = false; m_pState->pCurrentlyGenerating = 0; Lock.Unlock(); } // while (!bStop) }