forked from cuberite/cuberite
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Separated chunk generator from world / plugin interfaces.
The generator now only takes care of servicing synchronous "GetChunk(X, Y)" and "GetBiomes(X, Y)" requests.
- Loading branch information
Showing
27 changed files
with
782 additions
and
688 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
#include "Globals.h" | ||
#include "ChunkGeneratorThread.h" | ||
#include "Generating/ChunkGenerator.h" | ||
#include "Generating/ChunkDesc.h" | ||
|
||
|
||
|
||
|
||
|
||
/** If the generation queue size exceeds this number, a warning will be output */ | ||
const size_t QUEUE_WARNING_LIMIT = 1000; | ||
|
||
/** If the generation queue size exceeds this number, chunks with no clients will be skipped */ | ||
const size_t QUEUE_SKIP_LIMIT = 500; | ||
|
||
|
||
|
||
|
||
|
||
cChunkGeneratorThread::cChunkGeneratorThread(void) : | ||
Super("cChunkGeneratorThread"), | ||
m_Generator(nullptr), | ||
m_PluginInterface(nullptr), | ||
m_ChunkSink(nullptr) | ||
{ | ||
} | ||
|
||
|
||
|
||
|
||
|
||
cChunkGeneratorThread::~cChunkGeneratorThread() | ||
{ | ||
Stop(); | ||
} | ||
|
||
|
||
|
||
|
||
|
||
bool cChunkGeneratorThread::Initialize(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile) | ||
{ | ||
m_PluginInterface = &a_PluginInterface; | ||
m_ChunkSink = &a_ChunkSink; | ||
|
||
m_Generator = cChunkGenerator::CreateFromIniFile(a_IniFile); | ||
if (m_Generator == nullptr) | ||
{ | ||
LOGERROR("Generator could not start, aborting the server"); | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
|
||
|
||
|
||
|
||
void cChunkGeneratorThread::Stop(void) | ||
{ | ||
m_ShouldTerminate = true; | ||
m_Event.Set(); | ||
m_evtRemoved.Set(); // Wake up anybody waiting for empty queue | ||
Super::Stop(); | ||
m_Generator.reset(); | ||
} | ||
|
||
|
||
|
||
|
||
|
||
void cChunkGeneratorThread::QueueGenerateChunk( | ||
cChunkCoords a_Coords, | ||
bool a_ForceRegeneration, | ||
cChunkCoordCallback * a_Callback | ||
) | ||
{ | ||
ASSERT(m_ChunkSink->IsChunkQueued(a_Coords)); | ||
|
||
{ | ||
cCSLock Lock(m_CS); | ||
|
||
// Add to queue, issue a warning if too many: | ||
if (m_Queue.size() >= QUEUE_WARNING_LIMIT) | ||
{ | ||
LOGWARN("WARNING: Adding chunk %s to generation queue; Queue is too big! (%zu)", a_Coords.ToString().c_str(), m_Queue.size()); | ||
} | ||
m_Queue.push_back(QueueItem{a_Coords, a_ForceRegeneration, a_Callback}); | ||
} | ||
|
||
m_Event.Set(); | ||
} | ||
|
||
|
||
|
||
|
||
|
||
void cChunkGeneratorThread::GenerateBiomes(cChunkCoords a_Coords, cChunkDef::BiomeMap & a_BiomeMap) | ||
{ | ||
if (m_Generator != nullptr) | ||
{ | ||
m_Generator->GenerateBiomes(a_Coords.m_ChunkX, a_Coords.m_ChunkZ, a_BiomeMap); | ||
} | ||
} | ||
|
||
|
||
|
||
|
||
|
||
void cChunkGeneratorThread::WaitForQueueEmpty(void) | ||
{ | ||
cCSLock Lock(m_CS); | ||
while (!m_ShouldTerminate && !m_Queue.empty()) | ||
{ | ||
cCSUnlock Unlock(Lock); | ||
m_evtRemoved.Wait(); | ||
} | ||
} | ||
|
||
|
||
|
||
|
||
|
||
int cChunkGeneratorThread::GetQueueLength(void) const | ||
{ | ||
cCSLock Lock(m_CS); | ||
return static_cast<int>(m_Queue.size()); | ||
} | ||
|
||
|
||
|
||
|
||
|
||
int cChunkGeneratorThread::GetSeed() const | ||
{ | ||
return m_Generator->GetSeed(); | ||
} | ||
|
||
|
||
|
||
|
||
|
||
EMCSBiome cChunkGeneratorThread::GetBiomeAt(int a_BlockX, int a_BlockZ) | ||
{ | ||
ASSERT(m_Generator != nullptr); | ||
return m_Generator->GetBiomeAt(a_BlockX, a_BlockZ); | ||
} | ||
|
||
|
||
|
||
|
||
|
||
void cChunkGeneratorThread::Execute(void) | ||
{ | ||
// 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) | ||
|
||
while (!m_ShouldTerminate) | ||
{ | ||
cCSLock Lock(m_CS); | ||
while (m_Queue.empty()) | ||
{ | ||
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC)) | ||
{ | ||
/* LOG("Chunk generator performance: %.2f ch / sec (%d ch total)", | ||
static_cast<double>(NumChunksGenerated) * CLOCKS_PER_SEC/ (clock() - GenerationStart), | ||
NumChunksGenerated | ||
); */ | ||
} | ||
cCSUnlock Unlock(Lock); | ||
m_Event.Wait(); | ||
if (m_ShouldTerminate) | ||
{ | ||
return; | ||
} | ||
NumChunksGenerated = 0; | ||
GenerationStart = clock(); | ||
LastReportTick = clock(); | ||
} | ||
|
||
if (m_Queue.empty()) | ||
{ | ||
// Sometimes the queue remains empty | ||
// If so, we can't do any front() operations on it! | ||
continue; | ||
} | ||
|
||
auto item = m_Queue.front(); // Get next chunk from the queue | ||
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT); | ||
m_Queue.erase(m_Queue.begin()); // Remove the item from the queue | ||
Lock.Unlock(); // Unlock ASAP | ||
m_evtRemoved.Set(); | ||
|
||
// Display perf info once in a while: | ||
if ((NumChunksGenerated > 512) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC)) | ||
{ | ||
LOG("Chunk generator performance: %.2f ch / sec (%d ch total)", | ||
static_cast<double>(NumChunksGenerated) * CLOCKS_PER_SEC / (clock() - GenerationStart), | ||
NumChunksGenerated | ||
); | ||
LastReportTick = clock(); | ||
} | ||
|
||
// Skip the chunk if it's already generated and regeneration is not forced. Report as success: | ||
if (!item.m_ForceRegeneration && m_ChunkSink->IsChunkValid(item.m_Coords)) | ||
{ | ||
LOGD("Chunk %s already generated, skipping generation", item.m_Coords.ToString().c_str()); | ||
if (item.m_Callback != nullptr) | ||
{ | ||
item.m_Callback->Call(item.m_Coords, true); | ||
} | ||
continue; | ||
} | ||
|
||
// Skip the chunk if the generator is overloaded: | ||
if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_Coords)) | ||
{ | ||
LOGWARNING("Chunk generator overloaded, skipping chunk %s", item.m_Coords.ToString().c_str()); | ||
if (item.m_Callback != nullptr) | ||
{ | ||
item.m_Callback->Call(item.m_Coords, false); | ||
} | ||
continue; | ||
} | ||
|
||
// Generate the chunk: | ||
DoGenerate(item.m_Coords); | ||
if (item.m_Callback != nullptr) | ||
{ | ||
item.m_Callback->Call(item.m_Coords, true); | ||
} | ||
NumChunksGenerated++; | ||
} // while (!bStop) | ||
} | ||
|
||
|
||
|
||
|
||
|
||
void cChunkGeneratorThread::DoGenerate(cChunkCoords a_Coords) | ||
{ | ||
ASSERT(m_PluginInterface != nullptr); | ||
ASSERT(m_ChunkSink != nullptr); | ||
|
||
cChunkDesc ChunkDesc(a_Coords); | ||
m_PluginInterface->CallHookChunkGenerating(ChunkDesc); | ||
m_Generator->Generate(a_Coords.m_ChunkX, a_Coords.m_ChunkZ, ChunkDesc); | ||
m_PluginInterface->CallHookChunkGenerated(ChunkDesc); | ||
|
||
#ifdef _DEBUG | ||
// Verify that the generator has produced valid data: | ||
ChunkDesc.VerifyHeightmap(); | ||
#endif | ||
|
||
m_ChunkSink->OnChunkGenerated(ChunkDesc); | ||
} | ||
|
||
|
||
|
||
|
||
|
Oops, something went wrong.