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
This commit is contained in:
@@ -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<int, int> ChunkCoord;
|
||||
typedef std::list< ChunkCoord > ChunkCoordList;
|
||||
@@ -16,45 +18,20 @@ 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( 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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -63,97 +40,106 @@ cChunkGenerator::cChunkGenerator( cChunkMap* a_pChunkMap )
|
||||
|
||||
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;
|
||||
Stop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkGenerator::GenerateChunk( int a_X, int a_Z )
|
||||
bool cChunkGenerator::Start(cWorld * a_World, const AString & a_WorldGeneratorName)
|
||||
{
|
||||
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_World = a_World;
|
||||
|
||||
m_pState->m_Semaphore.Signal();
|
||||
if (a_WorldGeneratorName.compare("Test") == 0 )
|
||||
{
|
||||
m_pWorldGenerator = new cWorldGenerator_Test();
|
||||
}
|
||||
else // Default
|
||||
{
|
||||
m_pWorldGenerator = new cWorldGenerator();
|
||||
}
|
||||
|
||||
return super::Start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkGenerator::GenerateThread( void* a_Params )
|
||||
void cChunkGenerator::Stop(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;
|
||||
mShouldTerminate = true;
|
||||
m_Event.Set();
|
||||
Wait();
|
||||
|
||||
delete m_pWorldGenerator;
|
||||
m_pWorldGenerator = NULL;
|
||||
}
|
||||
|
||||
while (!m_pState->bStop)
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkGenerator::GenerateChunk(int a_ChunkX, 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)
|
||||
{
|
||||
cCSLock Lock(CriticalSection);
|
||||
if (GenerateQueue.size() == 0)
|
||||
if ((itr->m_ChunkX == a_ChunkX) && (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_ChunkZ));
|
||||
|
||||
m_Event.Set();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkGenerator::Execute(void)
|
||||
{
|
||||
while (!mShouldTerminate)
|
||||
{
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user