summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Chunk.cpp50
-rw-r--r--src/Chunk.h44
-rw-r--r--src/ChunkMap.cpp75
-rw-r--r--src/ChunkMap.h9
-rw-r--r--src/Generating/ChunkGenerator.cpp3
-rw-r--r--src/Generating/ChunkGenerator.h4
-rw-r--r--src/World.cpp38
-rw-r--r--src/World.h14
-rw-r--r--src/WorldStorage/WorldStorage.cpp11
9 files changed, 141 insertions, 107 deletions
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 9fb178bc3..caace5024 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -71,7 +71,7 @@ cChunk::cChunk(
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP,
cAllocationPool<cChunkData::sChunkSection> & a_Pool
) :
- m_IsValid(false),
+ m_Presence(cpInvalid),
m_IsLightValid(false),
m_IsDirty(false),
m_IsSaving(false),
@@ -165,11 +165,22 @@ cChunk::~cChunk()
-void cChunk::SetValid(void)
+void cChunk::SetPresence(cChunk::ePresence a_Presence)
{
- m_IsValid = true;
-
- m_World->GetChunkMap()->ChunkValidated();
+ m_Presence = a_Presence;
+ if (a_Presence == cpPresent)
+ {
+ m_World->GetChunkMap()->ChunkValidated();
+ }
+}
+
+
+
+
+
+void cChunk::SetShouldGenerateIfLoadFailed(bool a_ShouldGenerateIfLoadFailed)
+{
+ m_ShouldGenerateIfLoadFailed = a_ShouldGenerateIfLoadFailed;
}
@@ -178,6 +189,9 @@ void cChunk::SetValid(void)
void cChunk::MarkRegenerating(void)
{
+ // Set as queued again:
+ SetPresence(cpQueued);
+
// Tell all clients attached to this chunk that they want this chunk:
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
{
@@ -191,7 +205,11 @@ void cChunk::MarkRegenerating(void)
bool cChunk::CanUnload(void)
{
- return m_LoadedByClient.empty() && !m_IsDirty && (m_StayCount == 0);
+ return
+ m_LoadedByClient.empty() && // The chunk is not used by any client
+ !m_IsDirty && // The chunk has been saved properly or hasn't been touched since the load / gen
+ (m_StayCount == 0) && // The chunk is not in a ChunkStay
+ (m_Presence != cpQueued) ; // The chunk is not queued for loading / generating (otherwise multi-load / multi-gen could occur)
}
@@ -223,7 +241,7 @@ void cChunk::MarkSaved(void)
void cChunk::MarkLoaded(void)
{
m_IsDirty = false;
- SetValid();
+ SetPresence(cpPresent);
}
@@ -232,12 +250,17 @@ void cChunk::MarkLoaded(void)
void cChunk::MarkLoadFailed(void)
{
- if (m_IsValid)
+ ASSERT(m_Presence == cpQueued);
+
+ // If the chunk is marked as needed, generate it:
+ if (m_ShouldGenerateIfLoadFailed)
{
- return;
+ m_World->GetGenerator().QueueGenerateChunk(m_PosX, m_PosZ, false);
+ }
+ else
+ {
+ m_Presence = cpInvalid;
}
-
- m_HasLoadFailed = true;
}
@@ -246,6 +269,8 @@ void cChunk::MarkLoadFailed(void)
void cChunk::GetAllData(cChunkDataCallback & a_Callback)
{
+ ASSERT(m_Presence == cpPresent);
+
a_Callback.HeightMap(&m_HeightMap);
a_Callback.BiomeData(&m_BiomeMap);
@@ -272,6 +297,7 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData)
{
ASSERT(a_SetChunkData.IsHeightMapValid());
ASSERT(a_SetChunkData.AreBiomesValid());
+ ASSERT(IsQueued());
memcpy(m_BiomeMap, a_SetChunkData.GetBiomes(), sizeof(m_BiomeMap));
memcpy(m_HeightMap, a_SetChunkData.GetHeightMap(), sizeof(m_HeightMap));
@@ -317,7 +343,7 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData)
CreateBlockEntities();
// Set the chunk data as valid. This may be needed for some simulators that perform actions upon block adding (Vaporize)
- SetValid();
+ SetPresence(cpPresent);
// Wake up all simulators for their respective blocks:
WakeUpSimulators();
diff --git a/src/Chunk.h b/src/Chunk.h
index 0f4928b90..81862f2e7 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -66,6 +66,14 @@ class cChunk :
public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef
{
public:
+ /** Represents the presence state of the chunk */
+ enum ePresence
+ {
+ cpInvalid, /**< The chunk is not present at all and is not queued in the loader / generator */
+ cpQueued, /**< The chunk is not present, but is queued for loading / generation */
+ cpPresent, /**< The chunk is present */
+ };
+
cChunk(
int a_ChunkX, int a_ChunkZ, // Chunk coords
cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects
@@ -75,11 +83,25 @@ public:
cChunk(cChunk & other);
~cChunk();
- bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated)
- void SetValid(void); // Also wakes up any calls to cChunkMap::GetHeight()
- void MarkRegenerating(void); // Marks all clients attached to this chunk as wanting this chunk
- bool IsDirty(void) const {return m_IsDirty; } // Returns true if the chunk has changed since it was last saved
- bool HasLoadFailed(void) const {return m_HasLoadFailed; } // Returns true if the chunk failed to load and hasn't been generated since then
+ /** Returns true iff the chunk block data is valid (loaded / generated) */
+ bool IsValid(void) const {return (m_Presence == cpPresent); }
+
+ /** Returns true iff the chunk is in the queue for loading / generating */
+ bool IsQueued(void) const {return (m_Presence == cpQueued); }
+
+ /** Sets the chunk's presence.
+ Wakes up any calls to cChunkMap::GetHeight() when setting to cpPresent. */
+ void SetPresence(ePresence a_Presence);
+
+ /** Called to indicate whether the chunk should be queued in the generator if it fails to load. Set by cChunkMap::GetChunk(). */
+ void SetShouldGenerateIfLoadFailed(bool a_ShouldGenerateIfLoadFailed);
+
+ /** Marks all clients attached to this chunk as wanting this chunk. Also sets presence to cpQueued. */
+ void MarkRegenerating(void);
+
+ /** Returns true iff the chunk has changed since it was last saved. */
+ bool IsDirty(void) const {return m_IsDirty; }
+
bool CanUnload(void);
bool IsLightValid(void) const {return m_IsLightValid; }
@@ -94,7 +116,10 @@ public:
void MarkSaving(void); // Marks the chunk as being saved.
void MarkSaved(void); // Marks the chunk as saved, if it didn't change from the last call to MarkSaving()
void MarkLoaded(void); // Marks the chunk as freshly loaded. Fails if the chunk is already valid
- void MarkLoadFailed(void); // Marks the chunk as failed to load. Ignored is the chunk is already valid
+
+ /** Marks the chunk as failed to load.
+ If m_ShouldGenerateIfLoadFailed is set, queues the chunk for generating. */
+ void MarkLoadFailed(void);
/** Gets all chunk data, calls the a_Callback's methods for each data type */
void GetAllData(cChunkDataCallback & a_Callback);
@@ -434,7 +459,12 @@ private:
typedef std::vector<sSetBlockQueueItem> sSetBlockQueueVector;
- bool m_IsValid; // True if the chunk is loaded / generated
+ /** Holds the presence status of the chunk - if it is present, or in the loader / generator queue, or unloaded */
+ ePresence m_Presence;
+
+ /** If the chunk fails to load, should it be queued in the generator or reset back to invalid? */
+ bool m_ShouldGenerateIfLoadFailed;
+
bool m_IsLightValid; // True if the blocklight and skylight are calculated
bool m_IsDirty; // True if the chunk has changed since it was last saved
bool m_IsSaving; // True if the chunk is being saved
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 8c765c8c9..50066e539 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -145,10 +145,9 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ)
cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkZ)
{
- // No need to lock m_CSLayers, since it's already locked by the operation that called us
- ASSERT(m_CSLayers.IsLockedByCurrentThread());
+ ASSERT(m_CSLayers.IsLockedByCurrentThread()); // m_CSLayers should already be locked by the operation that called us
- cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ);
+ cChunkLayer * Layer = GetLayerForChunk(a_ChunkX, a_ChunkZ);
if (Layer == NULL)
{
// An error must have occurred, since layers are automatically created if they don't exist
@@ -160,8 +159,10 @@ cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkZ)
{
return NULL;
}
- if (!(Chunk->IsValid()))
+ if (!Chunk->IsValid() && !Chunk->IsQueued())
{
+ Chunk->SetPresence(cChunk::cpQueued);
+ Chunk->SetShouldGenerateIfLoadFailed(true);
m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ, true);
}
return Chunk;
@@ -171,10 +172,11 @@ cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkZ)
-cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkZ)
+cChunkPtr cChunkMap::GetChunkNoGen(int a_ChunkX, int a_ChunkZ)
{
- // No need to lock m_CSLayers, since it's already locked by the operation that called us
- cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ);
+ ASSERT(m_CSLayers.IsLockedByCurrentThread()); // m_CSLayers should already be locked by the operation that called us
+
+ cChunkLayer * Layer = GetLayerForChunk(a_ChunkX, a_ChunkZ);
if (Layer == NULL)
{
// An error must have occurred, since layers are automatically created if they don't exist
@@ -186,8 +188,9 @@ cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkZ)
{
return NULL;
}
- if (!(Chunk->IsValid()))
+ if (!Chunk->IsValid() && !Chunk->IsQueued())
{
+ Chunk->SetPresence(cChunk::cpQueued);
m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ, false);
}
@@ -200,7 +203,8 @@ cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkZ)
cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkZ)
{
- // No need to lock m_CSLayers, since it's already locked by the operation that called us
+ ASSERT(m_CSLayers.IsLockedByCurrentThread()); // m_CSLayers should already be locked by the operation that called us
+
cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ);
if (Layer == NULL)
{
@@ -1009,6 +1013,17 @@ bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blo
+bool cChunkMap::IsChunkQueued(int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
+ return (Chunk != NULL) && Chunk->IsQueued();
+}
+
+
+
+
+
bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
@@ -2332,48 +2347,6 @@ void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkZ)
-/// Loads the chunk synchronously, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before)
-bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkZ)
-{
- {
- cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
- if (Chunk == NULL)
- {
- // Internal error
- return false;
- }
- if (Chunk->IsValid())
- {
- // Already loaded
- return true;
- }
- if (Chunk->HasLoadFailed())
- {
- // Already tried loading and it failed
- return false;
- }
- }
- return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkZ);
-}
-
-
-
-
-
-/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid()
-void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks)
-{
- for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr)
- {
- LoadChunk(itr->m_ChunkX, itr->m_ChunkZ);
- } // for itr - a_Chunks[]
-}
-
-
-
-
-
void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 8c564d0de..7354536d4 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -133,6 +133,9 @@ public:
/** Copies the chunk's blocktypes into a_Blocks; returns true if successful */
bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks);
+ /** Returns true iff the chunk is in the loader / generator queue. */
+ bool IsChunkQueued(int a_ChunkX, int a_ChunkZ);
+
bool IsChunkValid (int a_ChunkX, int a_ChunkZ);
bool HasChunkAnyClients (int a_ChunkX, int a_ChunkZ);
int GetHeight (int a_BlockX, int a_BlockZ); // Waits for the chunk to get loaded / generated
@@ -278,12 +281,6 @@ public:
/** Touches the chunk, causing it to be loaded or generated */
void TouchChunk(int a_ChunkX, int a_ChunkZ);
- /** Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) */
- bool LoadChunk(int a_ChunkX, int a_ChunkZ);
-
- /** Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() */
- void LoadChunks(const cChunkCoordsList & a_Chunks);
-
/** 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 4fa9729ec..d8dbb4a3a 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -283,7 +283,8 @@ void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ)
{
ASSERT(m_PluginInterface != NULL);
ASSERT(m_ChunkSink != NULL);
-
+ ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ));
+
cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ);
m_PluginInterface->CallHookChunkGenerating(ChunkDesc);
m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc);
diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h
index e880a6766..190d9e616 100644
--- a/src/Generating/ChunkGenerator.h
+++ b/src/Generating/ChunkGenerator.h
@@ -106,6 +106,10 @@ public:
If this callback returns false, the chunk is not generated.
*/
virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) = 0;
+
+ /** Called to check whether the specified chunk is in the queued state.
+ Currently used only in Debug-mode asserts. */
+ virtual bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) = 0;
} ;
diff --git a/src/World.cpp b/src/World.cpp
index 2a3336dee..e669f6fa0 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -2374,6 +2374,8 @@ void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkZ)
void cWorld::QueueSetChunkData(const cSetChunkDataPtr & a_SetChunkData)
{
+ ASSERT(IsChunkQueued(a_SetChunkData->GetChunkX(), a_SetChunkData->GetChunkZ()));
+
// Validate biomes, if needed:
if (!a_SetChunkData->AreBiomesValid())
{
@@ -2463,6 +2465,15 @@ bool cWorld::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockT
+bool cWorld::IsChunkQueued(int a_ChunkX, int a_ChunkZ) const
+{
+ return m_ChunkMap->IsChunkQueued(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
bool cWorld::IsChunkValid(int a_ChunkX, int a_ChunkZ) const
{
return m_ChunkMap->IsChunkValid(a_ChunkX, a_ChunkZ);
@@ -2787,24 +2798,6 @@ void cWorld::TouchChunk(int a_ChunkX, int a_ChunkZ)
-bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkZ)
-{
- return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkZ);
-}
-
-
-
-
-
-void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks)
-{
- m_ChunkMap->LoadChunks(a_Chunks);
-}
-
-
-
-
-
void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
{
m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkZ);
@@ -3520,6 +3513,15 @@ bool cWorld::cChunkGeneratorCallbacks::IsChunkValid(int a_ChunkX, int a_ChunkZ)
+bool cWorld::cChunkGeneratorCallbacks::IsChunkQueued(int a_ChunkX, int a_ChunkZ)
+{
+ return m_World->IsChunkQueued(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
bool cWorld::cChunkGeneratorCallbacks::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ)
{
return m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ);
diff --git a/src/World.h b/src/World.h
index 49932ac9d..6316c6811 100644
--- a/src/World.h
+++ b/src/World.h
@@ -279,7 +279,12 @@ public:
/** Gets the chunk's blocks, only the block types */
bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes);
- bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const;
+ /** Returns true iff the chunk is in the loader / generator queue. */
+ bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) const;
+
+ /** Returns true iff the chunk is present and valid. */
+ bool IsChunkValid(int a_ChunkX, int a_ChunkZ) const;
+
bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const;
/** Queues a task to unload unused chunks onto the tick thread. The prefferred way of unloading*/
@@ -358,12 +363,6 @@ public:
/** Touches the chunk, causing it to be loaded or generated */
void TouchChunk(int a_ChunkX, int a_ChunkZ);
- /** Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) */
- bool LoadChunk(int a_ChunkX, int a_ChunkZ);
-
- /** Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() */
- void LoadChunks(const cChunkCoordsList & a_Chunks);
-
/** Marks the chunk as failed-to-load: */
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);
@@ -822,6 +821,7 @@ private:
virtual void OnChunkGenerated (cChunkDesc & a_ChunkDesc) override;
virtual bool IsChunkValid (int a_ChunkX, int a_ChunkZ) override;
virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) override;
+ virtual bool IsChunkQueued (int a_ChunkX, int a_ChunkZ) override;
// cPluginInterface overrides:
virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) override;
diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp
index 667a28470..899b0beb3 100644
--- a/src/WorldStorage/WorldStorage.cpp
+++ b/src/WorldStorage/WorldStorage.cpp
@@ -143,6 +143,8 @@ size_t cWorldStorage::GetSaveQueueLength(void)
void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ, bool a_Generate)
{
+ ASSERT(m_World->IsChunkQueued(a_ChunkX, a_ChunkZ));
+
m_LoadQueue.EnqueueItem(sChunkLoad(a_ChunkX, a_ChunkZ, a_Generate));
m_Event.Set();
}
@@ -153,6 +155,8 @@ void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ, bool a_Generate)
void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ)
{
+ ASSERT(m_World->IsChunkValid(a_ChunkX, a_ChunkZ));
+
m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkZ));
m_Event.Set();
}
@@ -244,6 +248,7 @@ bool cWorldStorage::LoadOneChunk(void)
{
sChunkLoad ToLoad(0, 0, false);
bool ShouldLoad = m_LoadQueue.TryDequeueItem(ToLoad);
+
if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ))
{
if (ToLoad.m_Generate)
@@ -285,11 +290,7 @@ bool cWorldStorage::SaveOneChunk(void)
bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkZ)
{
- if (m_World->IsChunkValid(a_ChunkX, a_ChunkZ))
- {
- // Already loaded (can happen, since the queue is async)
- return true;
- }
+ ASSERT(m_World->IsChunkQueued(a_ChunkX, a_ChunkZ));
cChunkCoords Coords(a_ChunkX, a_ChunkZ);