From 4f17362aeb80e5339c58a5d3b0fbaeb88d9e701c Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Mon, 13 Feb 2012 21:47:03 +0000 Subject: Rewritten most of the code for multithreading; still not 100%, but getting there. If this commit proves to be too problematic, we can always undo it. git-svn-id: http://mc-server.googlecode.com/svn/trunk@251 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/cChunkGenerator.cpp | 162 +++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 88 deletions(-) (limited to 'source/cChunkGenerator.cpp') diff --git a/source/cChunkGenerator.cpp b/source/cChunkGenerator.cpp index 78853a26b..8755ea325 100644 --- a/source/cChunkGenerator.cpp +++ b/source/cChunkGenerator.cpp @@ -2,11 +2,13 @@ #include "Globals.h" #include "cChunkGenerator.h" -#include "cChunkMap.h" -#include "cChunk.h" #include "cWorld.h" +#include "cWorldGenerator.h" +#include "cWorldGenerator_Test.h" + + + -#include "cMCLogger.h" typedef std::pair ChunkCoord; typedef std::list< ChunkCoord > ChunkCoordList; @@ -16,144 +18,128 @@ typedef std::list< ChunkCoord > ChunkCoordList; /// If the generation queue size exceeds this number, a warning will be output -#define QUEUE_WARNING_LIMIT 1000 +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 = 50; -struct cChunkGenerator::sChunkGeneratorState +cChunkGenerator::cChunkGenerator(void) + : super("cChunkGenerator") + , m_World(NULL) + , m_pWorldGenerator(NULL) { - 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() +{ + Stop(); +} -cChunkGenerator::cChunkGenerator( cChunkMap* a_pChunkMap ) - : m_pState( new sChunkGeneratorState ) - , m_pChunkMap( a_pChunkMap ) +bool cChunkGenerator::Start(cWorld * a_World, const AString & a_WorldGeneratorName) { - m_pState->pThread = new cThread( GenerateThread, this, "cChunkGenerator::GenerateThread" ); - m_pState->pThread->Start( true ); + m_World = a_World; + + if (a_WorldGeneratorName.compare("Test") == 0 ) + { + m_pWorldGenerator = new cWorldGenerator_Test(); + } + else // Default + { + m_pWorldGenerator = new cWorldGenerator(); + } + + return super::Start(); } -cChunkGenerator::~cChunkGenerator() +void cChunkGenerator::Stop(void) { - m_pState->bStop = true; - - m_pState->m_Semaphore.Signal(); // Signal so thread can continue and exit - delete m_pState->pThread; - - delete m_pState; + mShouldTerminate = true; + m_Event.Set(); + Wait(); + + delete m_pWorldGenerator; + m_pWorldGenerator = NULL; } -void cChunkGenerator::GenerateChunk( int a_X, int a_Z ) +void cChunkGenerator::GenerateChunk(int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(&m_pState->m_CriticalSection); - - if (m_pState->bCurrentlyGenerating) + 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 ((m_pState->CurrentlyGeneratingCoords.first == a_X) && (m_pState->CurrentlyGeneratingCoords.second == a_Z)) + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) { - return; // Already generating this chunk, so ignore + // Already in the queue, bail out + return; } - } - - m_pState->GenerateQueue.remove( ChunkCoord(a_X, a_Z) ); - if (m_pState->GenerateQueue.size() >= QUEUE_WARNING_LIMIT) + } // 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_X, a_Z, m_pState->GenerateQueue.size() ); + LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%i)", a_ChunkX, a_ChunkZ, m_Queue.size()); } - m_pState->GenerateQueue.push_back( ChunkCoord(a_X, a_Z) ); - - Lock.Unlock(); + m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); - m_pState->m_Semaphore.Signal(); + m_Event.Set(); } -void cChunkGenerator::GenerateThread( void* a_Params ) +void cChunkGenerator::Execute(void) { - // 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) + while (!mShouldTerminate) { - cCSLock Lock(CriticalSection); - if (GenerateQueue.size() == 0) + cCSLock Lock(m_CS); + while (m_Queue.size() == 0) { cCSUnlock Unlock(Lock); - Semaphore.Wait(); + m_Event.Wait(); + if (mShouldTerminate) + { + return; + } } - 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; + 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 - 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(); + cChunkPtr Chunk = m_World->GetChunk(coords.m_ChunkX, 0, coords.m_ChunkZ); + if ((Chunk != NULL) && (Chunk->IsValid() || (SkipEnabled && !Chunk->HasAnyClient()))) + { + // Already generated / overload-skip, ignore request 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(); + LOG("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); + m_pWorldGenerator->GenerateChunk(Chunk); + + Chunk->SetValid(); } // while (!bStop) } -- cgit v1.2.3