1
0
Fork 0

Rewritten chunk status to specify whether the chunk is in queue.

This fixes #1370.
This commit is contained in:
Mattes D 2014-09-05 22:16:48 +02:00
parent e15b8600a6
commit 137b021d26
9 changed files with 141 additions and 107 deletions

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
} ;

View File

@ -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);

View File

@ -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;

View File

@ -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);