From 2ab8d2bd98004e40a915e1c9eca764d32115d6c5 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 9 Dec 2014 10:43:40 +0100 Subject: Added a RemoveIf() function to cQueue --- src/OSSupport/Queue.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h index 8d096fe29..82f221453 100644 --- a/src/OSSupport/Queue.h +++ b/src/OSSupport/Queue.h @@ -163,6 +163,29 @@ public: return false; } + + /** Removes all items for which the predicate returns true. */ + template + void RemoveIf(Predicate a_Predicate) + { + cCSLock Lock(m_CS); + for (auto itr = m_Contents.begin(); itr != m_Contents.end();) + { + if (a_Predicate(*itr)) + { + auto itr2 = itr; + ++itr2; + m_Contents.erase(itr); + m_evtRemoved.Set(); + itr = itr2; + } + else + { + ++itr; + } + } // for itr - m_Contents[] + } + private: /// The contents of the queue QueueType m_Contents; -- cgit v1.2.3 From 563f41af65dbe48ac8fa59a045b3a38b22d00266 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 10 Dec 2014 22:27:48 +0100 Subject: Lighting thread: skip chunks that are already lit. --- src/LightingThread.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp index ae5e746e7..01ba24105 100644 --- a/src/LightingThread.cpp +++ b/src/LightingThread.cpp @@ -236,6 +236,16 @@ void cLightingThread::Execute(void) void cLightingThread::LightChunk(cLightingChunkStay & a_Item) { + // If the chunk is already lit, skip it: + if (m_World->IsChunkLighted(a_Item.m_ChunkX, a_Item.m_ChunkZ)) + { + if (a_Item.m_CallbackAfter != nullptr) + { + a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ); + } + return; + } + cChunkDef::BlockNibbles BlockLight, SkyLight; ReadChunks(a_Item.m_ChunkX, a_Item.m_ChunkZ); -- cgit v1.2.3 From fcd3d1bfedbfbe535f406e3db933486bad913ab4 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 10 Dec 2014 22:35:16 +0100 Subject: Added a cWorld:PrepareChunk function. It prepares the chunk - loads or generates it and lights it. The spawn prepare process uses this function. --- src/ChunkDef.h | 23 +++++++++- src/ChunkMap.cpp | 97 +++++++++++++++++++++++++++++++++++++++ src/ChunkMap.h | 14 ++++++ src/Generating/ChunkGenerator.cpp | 47 ++++++++++--------- src/Generating/ChunkGenerator.h | 47 +++++++++++++++---- src/World.cpp | 13 +++++- src/World.h | 6 +++ src/WorldStorage/WorldStorage.cpp | 58 +++++++++++++++++------ src/WorldStorage/WorldStorage.h | 6 +-- 9 files changed, 260 insertions(+), 51 deletions(-) diff --git a/src/ChunkDef.h b/src/ChunkDef.h index 260aace58..8f1d416ad 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -410,7 +410,7 @@ typedef std::list cChunkCoordsWithBoolList; -/// Interface class used as a callback for operations that involve chunk coords +/** Interface class used as a callback for operations that involve chunk coords */ class cChunkCoordCallback { public: @@ -424,6 +424,27 @@ public: +/** Provides storage for a set of chunk coords together with a callback. +Used for chunk queues that notify about processed items. */ +class cChunkCoordsWithCallback +{ +public: + cChunkCoordsWithCallback(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback): + m_ChunkX(a_ChunkX), + m_ChunkZ(a_ChunkZ), + m_Callback(a_Callback) + { + } + + int m_ChunkX; + int m_ChunkZ; + cChunkCoordCallback * m_Callback; +}; + + + + + /** Generic template that can store any kind of data together with a triplet of 3 coords*/ template class cCoordWithData { diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 8a8f17a1b..222a756b1 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -2349,6 +2349,103 @@ void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkZ) +void cChunkMap::PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); + + // If the chunk is not prepared, queue it in the lighting thread, that will do all the needed processing: + if ((Chunk == nullptr) || !Chunk->IsValid() || !Chunk->IsLightValid()) + { + m_World->GetLightingThread().QueueChunk(a_ChunkX, a_ChunkZ, a_Callback); + return; + } + + // The chunk is present and lit, just call the callback: + if (a_Callback != nullptr) + { + a_Callback->Call(a_ChunkX, a_ChunkZ); + } +} + + + + + +bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); + if (Chunk == nullptr) + { + // Generic error while getting the chunk - out of memory? + return false; + } + + // Try loading the chunk: + if ((Chunk == nullptr) || (!Chunk->IsValid())) + { + class cPrepareLoadCallback: public cChunkCoordCallback + { + public: + cPrepareLoadCallback(cWorld & a_World, cChunkMap & a_ChunkMap, cChunkCoordCallback * a_Callback): + m_World(a_World), + m_ChunkMap(a_ChunkMap), + m_Callback(a_Callback) + { + } + + // cChunkCoordCallback override: + virtual void Call(int a_CBChunkX, int a_CBChunkZ) override + { + // The chunk has been loaded or an error occurred, check if it's valid now: + cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_CBChunkX, a_CBChunkZ); + + if (CBChunk == nullptr) + { + // An error occurred, but we promised to call the callback, so call it even when there's no real chunk data: + if (m_Callback != nullptr) + { + m_Callback->Call(a_CBChunkX, a_CBChunkZ); + } + return; + } + + // If the chunk is not valid, queue it in the generator: + if (!CBChunk->IsValid()) + { + m_World.GetGenerator().QueueGenerateChunk(a_CBChunkX, a_CBChunkZ, false, m_Callback); + return; + } + + // The chunk was loaded, call the callback: + if (m_Callback != nullptr) + { + m_Callback->Call(a_CBChunkX, a_CBChunkZ); + } + } + + protected: + cWorld & m_World; + cChunkMap & m_ChunkMap; + cChunkCoordCallback * m_Callback; + }; + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ, new cPrepareLoadCallback(*m_World, *this, a_Callback)); + return true; + } + + // The chunk is valid, just call the callback: + if (a_Callback != nullptr) + { + a_Callback->Call(a_ChunkX, a_ChunkZ); + } + return true; +} + + + + + void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 51be5cb3d..6a858b06d 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -280,6 +280,20 @@ public: /** Touches the chunk, causing it to be loaded or generated */ void TouchChunk(int a_ChunkX, int a_ChunkZ); + + /** Queues the chunk for preparing - making sure that it's generated and lit. + The specified chunk is queued to be loaded or generated, and lit if needed. + The specified callback is called after the chunk has been prepared. If there's no preparation to do, only the callback is called. + It is legal to call without the callback. */ + void PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); // Lua-accessible + + /** Queues the chunk for generating. + First attempts to load the chunk from the storage. If that fails, queues the chunk for generating. + The specified callback is called after the chunk has been loaded / generated. + It is legal to call without the callback. + Returns true if successful, false if not (possibly an out-of-memory error). + If the return value is true, the callback was / will be called. */ + bool GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); // Lua-accessible /** Marks the chunk as failed-to-load */ void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ); diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index d2e7b47b4..854aac60d 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -110,29 +110,19 @@ void cChunkGenerator::Stop(void) -void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate) +void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback) { ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ)); { cCSLock Lock(m_CS); - // Check if it is already in the queue: - for (cChunkCoordsWithBoolList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr) - { - 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! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size()); } - m_Queue.push_back(cChunkCoordsWithBool(a_ChunkX, a_ChunkZ, a_ForceGenerate)); + m_Queue.push_back(cQueueItem{a_ChunkX, a_ChunkZ, a_ForceGenerate, a_Callback}); } m_Event.Set(); @@ -242,9 +232,9 @@ void cChunkGenerator::Execute(void) continue; } - cChunkCoordsWithBool coords = m_Queue.front(); // Get next coord from queue + cQueueItem 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 coordinate from queue + m_Queue.erase(m_Queue.begin()); // Remove the item from the queue Lock.Unlock(); // Unlock ASAP m_evtRemoved.Set(); @@ -258,22 +248,35 @@ void cChunkGenerator::Execute(void) LastReportTick = clock(); } - if (!coords.m_ForceGenerate && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ)) + // Skip the chunk if it's already generated and regeneration is not forced: + if (!item.m_ForceGenerate && m_ChunkSink->IsChunkValid(item.m_ChunkX, item.m_ChunkZ)) { - LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ); - // Already generated, ignore request + LOGD("Chunk [%d, %d] already generated, skipping generation", item.m_ChunkX, item.m_ChunkZ); + if (item.m_Callback != nullptr) + { + item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ); + } continue; } - if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ)) + // Skip the chunk if the generator is overloaded: + if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_ChunkX, item.m_ChunkZ)) { - LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); + LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ); + if (item.m_Callback != nullptr) + { + item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ); + } continue; } - LOGD("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); - DoGenerate(coords.m_ChunkX, coords.m_ChunkZ); - + // Generate the chunk: + LOGD("Generating chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ); + DoGenerate(item.m_ChunkX, item.m_ChunkZ); + if (item.m_Callback != nullptr) + { + item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ); + } NumChunksGenerated++; } // while (!bStop) } diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h index 190d9e616..4af486ae1 100644 --- a/src/Generating/ChunkGenerator.h +++ b/src/Generating/ChunkGenerator.h @@ -119,8 +119,12 @@ public: bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile); void Stop(void); - /// Queues the chunk for generation; removes duplicate requests - void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate); + /** Queues the chunk for generation + If a-ForceGenerate is set, the chunk is regenerated even if the data is already present in the chunksink. + a_Callback is called after the chunk is generated. If the chunk was already present, the callback is still called, even if not regenerating. + It is legal to set the callback to nullptr, no callback is called then. + If the generator becomes overloaded and skips this chunk, the callback is still called. */ + void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback = nullptr); /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); @@ -131,22 +135,46 @@ public: int GetSeed(void) const { return m_Seed; } - /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome + /** Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome */ EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); - /// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. + /** Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. */ static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default); private: + struct cQueueItem + { + /** The chunk coords */ + int m_ChunkX, m_ChunkZ; + + /** Force the regeneration of an already existing chunk */ + bool m_ForceGenerate; + + /** Callback to call after generating.*/ + cChunkCoordCallback * m_Callback; + }; + + typedef std::list cGenQueue; + + + /** Seed used for the generator. */ int m_Seed; - cCriticalSection m_CS; - cChunkCoordsWithBoolList m_Queue; - cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate - cEvent m_evtRemoved; ///< Set when an item is removed from the queue + /** CS protecting access to the queue. */ + cCriticalSection m_CS; + + /** Queue of the chunks to be generated. Protected against multithreaded access by m_CS. */ + cGenQueue m_Queue; + + /** Set when an item is added to the queue or the thread should terminate. */ + cEvent m_Event; + + /** Set when an item is removed from the queue. */ + cEvent m_evtRemoved; - cGenerator * m_Generator; ///< The actual generator engine used to generate chunks + /** The actual generator engine used to generate chunks. */ + cGenerator * m_Generator; /** The plugin interface that may modify the generated chunks */ cPluginInterface * m_PluginInterface; @@ -158,6 +186,7 @@ private: // cIsThread override: virtual void Execute(void) override; + /** Generates the specified chunk and sets it into the chunksink. */ void DoGenerate(int a_ChunkX, int a_ChunkZ); }; diff --git a/src/World.cpp b/src/World.cpp index e73dcb915..ff5a2cf35 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -115,7 +115,7 @@ public: { int chunkX, chunkZ; DecodeChunkCoords(i, chunkX, chunkZ); - m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this); + m_World.PrepareChunk(chunkX, chunkZ, this); } // for i // Wait for the lighting thread to prepare everything. Event is set in the Call() callback: @@ -2907,6 +2907,15 @@ void cWorld::TouchChunk(int a_ChunkX, int a_ChunkZ) +void cWorld::PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter) +{ + m_ChunkMap->PrepareChunk(a_ChunkX, a_ChunkZ, a_CallAfter); +} + + + + + void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ) { m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkZ); @@ -3017,7 +3026,7 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ) { - m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkZ); + m_ChunkMap->GenerateChunk(a_ChunkX, a_ChunkZ); } diff --git a/src/World.h b/src/World.h index d64be208e..c87ecd8c7 100644 --- a/src/World.h +++ b/src/World.h @@ -375,6 +375,12 @@ public: /** Touches the chunk, causing it to be loaded or generated */ void TouchChunk(int a_ChunkX, int a_ChunkZ); + + /** Queues the chunk for preparing - making sure that it's generated and lit. + The specified chunk is queued to be loaded or generated, and lit if needed. + The specified callback is called after the chunk has been prepared. If there's no preparation to do, only the callback is called. + It is legal to call with no callback. */ + void PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); /** Marks the chunk as failed-to-load: */ void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ); diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp index 31318ee67..c7a295175 100644 --- a/src/WorldStorage/WorldStorage.cpp +++ b/src/WorldStorage/WorldStorage.cpp @@ -140,11 +140,11 @@ size_t cWorldStorage::GetSaveQueueLength(void) -void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ) +void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) { ASSERT(m_World->IsChunkQueued(a_ChunkX, a_ChunkZ)); - m_LoadQueue.EnqueueItem(cChunkCoords(a_ChunkX, a_ChunkZ)); + m_LoadQueue.EnqueueItem(cChunkCoordsWithCallback(a_ChunkX, a_ChunkZ, a_Callback)); m_Event.Set(); } @@ -152,11 +152,11 @@ void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ) -void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ) +void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) { ASSERT(m_World->IsChunkValid(a_ChunkX, a_ChunkZ)); - m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkZ)); + m_SaveQueue.EnqueueItem(cChunkCoordsWithCallback(a_ChunkX, a_ChunkZ, a_Callback)); m_Event.Set(); } @@ -166,7 +166,11 @@ void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ) void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkZ) { - m_LoadQueue.Remove(cChunkCoords(a_ChunkX, a_ChunkZ)); + m_LoadQueue.RemoveIf([=](cChunkCoordsWithCallback & a_Item) + { + return (a_Item.m_ChunkX == a_ChunkX) && (a_Item.m_ChunkZ == a_ChunkZ); + } + ); } @@ -175,7 +179,11 @@ void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkZ) void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk) { - m_SaveQueue.Remove(a_Chunk); + m_SaveQueue.RemoveIf([=](cChunkCoordsWithCallback & a_Item) + { + return (a_Item.m_ChunkX == a_Chunk.m_ChunkX) && (a_Item.m_ChunkZ == a_Chunk.m_ChunkZ); + } + ); } @@ -244,14 +252,23 @@ void cWorldStorage::Execute(void) bool cWorldStorage::LoadOneChunk(void) { - cChunkCoords ToLoad(0, 0); + // Dequeue an item, bail out if there's none left: + cChunkCoordsWithCallback ToLoad(0, 0, nullptr); bool ShouldLoad = m_LoadQueue.TryDequeueItem(ToLoad); + if (!ShouldLoad) + { + return false; + } - if (ShouldLoad) + // Load the chunk: + bool res = LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ); + + // Call the callback, if specified: + if (ToLoad.m_Callback != nullptr) { - return LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ); + ToLoad.m_Callback->Call(ToLoad.m_ChunkX, ToLoad.m_ChunkZ); } - return false; + return res; } @@ -260,17 +277,30 @@ bool cWorldStorage::LoadOneChunk(void) bool cWorldStorage::SaveOneChunk(void) { - cChunkCoords ToSave(0, 0); + // Dequeue one chunk to save: + cChunkCoordsWithCallback ToSave(0, 0, nullptr); bool ShouldSave = m_SaveQueue.TryDequeueItem(ToSave); - if (ShouldSave && m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ)) + if (!ShouldSave) + { + return false; + } + + // Save the chunk, if it's valid: + if (m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ)) { m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ); - if (m_SaveSchema->SaveChunk(ToSave)) + if (m_SaveSchema->SaveChunk(cChunkCoords(ToSave.m_ChunkX, ToSave.m_ChunkZ))) { m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ); } } - return ShouldSave; + + // Call the callback, if specified: + if (ToSave.m_Callback != nullptr) + { + ToSave.m_Callback->Call(ToSave.m_ChunkX, ToSave.m_ChunkZ); + } + return true; } diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index fc7e9f84c..e9ba2a8e2 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -25,7 +25,7 @@ // fwd: class cWorld; -typedef cQueue cChunkCoordsQueue; +typedef cQueue cChunkCoordsQueue; @@ -64,8 +64,8 @@ public: cWorldStorage(void); ~cWorldStorage(); - void QueueLoadChunk(int a_ChunkX, int a_ChunkZ); - void QueueSaveChunk(int a_ChunkX, int a_ChunkZ); + void QueueLoadChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr); + void QueueSaveChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr); void UnqueueLoad(int a_ChunkX, int a_ChunkZ); void UnqueueSave(const cChunkCoords & a_Chunk); -- cgit v1.2.3