Implemented synchronous chunk loading; optimized cChunkStay interface for speed (though still unused ;)

git-svn-id: http://mc-server.googlecode.com/svn/trunk@336 0a769ca7-a7f5-676a-18bf-c427514a06d6
master
madmaxoft@gmail.com 2012-02-28 10:45:53 +00:00
parent bccd52530f
commit 230f98a774
8 changed files with 342 additions and 152 deletions

View File

@ -60,15 +60,15 @@ cWSSCompact::~cWSSCompact()
bool cWSSCompact::LoadChunk(const cChunkCoords & a_Chunk)
{
cPAKFile * f = LoadPAKFile(a_Chunk);
if (f == NULL)
AString ChunkData;
int UncompressedSize = 0;
if (!GetChunkData(a_Chunk, UncompressedSize, ChunkData))
{
// For some reason we couldn't locate the file
LOG("Cannot locate a proper PAK file for chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
// The reason for failure is already printed in GetChunkData()
return false;
}
return f->LoadChunk(a_Chunk, m_World);
return LoadChunkFromData(a_Chunk, UncompressedSize, ChunkData, m_World);
}
@ -77,6 +77,8 @@ bool cWSSCompact::LoadChunk(const cChunkCoords & a_Chunk)
bool cWSSCompact::SaveChunk(const cChunkCoords & a_Chunk)
{
cCSLock Lock(m_CS);
cPAKFile * f = LoadPAKFile(a_Chunk);
if (f == NULL)
{
@ -93,6 +95,8 @@ bool cWSSCompact::SaveChunk(const cChunkCoords & a_Chunk)
cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkCoords & a_Chunk)
{
// ASSUMES that m_CS has been locked
// We need to retain this weird conversion code, because some edge chunks are in the wrong PAK file
const int LayerX = (int)(floorf((float)a_Chunk.m_ChunkX / 32.0f));
const int LayerZ = (int)(floorf((float)a_Chunk.m_ChunkZ / 32.0f));
@ -136,6 +140,121 @@ cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkCoords & a_Chunk)
bool cWSSCompact::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data)
{
cCSLock Lock(m_CS);
cPAKFile * f = LoadPAKFile(a_Chunk);
if (f == NULL)
{
return false;
}
return f->GetChunkData(a_Chunk, a_UncompressedSize, a_Data);
}
/*
// TODO: Rewrite saving to use the same principles as loading
bool cWSSCompact::SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data)
{
cCSLock Lock(m_CS);
cPAKFile * f = LoadPAKFile(a_Chunk);
if (f == NULL)
{
return false;
}
return f->SetChunkData(a_Chunk, a_UncompressedSize, a_Data);
}
*/
bool cWSSCompact::EraseChunkData(const cChunkCoords & a_Chunk)
{
cCSLock Lock(m_CS);
cPAKFile * f = LoadPAKFile(a_Chunk);
if (f == NULL)
{
return false;
}
return f->EraseChunkData(a_Chunk);
}
void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World)
{
// Load chests
Json::Value AllChests = a_Value.get("Chests", Json::nullValue);
if (!AllChests.empty())
{
for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr )
{
Json::Value & Chest = *itr;
cChestEntity * ChestEntity = new cChestEntity(0,0,0, a_World);
if (!ChestEntity->LoadFromJson( Chest ) )
{
LOGERROR("ERROR READING CHEST FROM JSON!" );
delete ChestEntity;
}
else
{
a_BlockEntities.push_back( ChestEntity );
}
} // for itr - AllChests[]
}
// Load furnaces
Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue);
if( !AllFurnaces.empty() )
{
for( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr )
{
Json::Value & Furnace = *itr;
cFurnaceEntity * FurnaceEntity = new cFurnaceEntity(0,0,0, a_World);
if( !FurnaceEntity->LoadFromJson( Furnace ) )
{
LOGERROR("ERROR READING FURNACE FROM JSON!" );
delete FurnaceEntity;
}
else
{
a_BlockEntities.push_back( FurnaceEntity );
}
} // for itr - AllFurnaces[]
}
// Load signs
Json::Value AllSigns = a_Value.get("Signs", Json::nullValue);
if( !AllSigns.empty() )
{
for( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr )
{
Json::Value & Sign = *itr;
cSignEntity * SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0,0,0, a_World);
if ( !SignEntity->LoadFromJson( Sign ) )
{
LOGERROR("ERROR READING SIGN FROM JSON!" );
delete SignEntity;
}
else
{
a_BlockEntities.push_back( SignEntity );
}
} // for itr - AllSigns[]
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWSSCompact::cPAKFile
@ -214,7 +333,7 @@ cWSSCompact::cPAKFile::~cPAKFile()
bool cWSSCompact::cPAKFile::LoadChunk(const cChunkCoords & a_Chunk, cWorld * a_World)
bool cWSSCompact::cPAKFile::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data)
{
int ChunkX = a_Chunk.m_ChunkX;
int ChunkZ = a_Chunk.m_ChunkZ;
@ -235,7 +354,9 @@ bool cWSSCompact::cPAKFile::LoadChunk(const cChunkCoords & a_Chunk, cWorld * a_W
return false;
}
return LoadChunk(a_Chunk, Offset, Header, a_World);
a_UncompressedSize = Header->m_UncompressedSize;
a_Data.assign(m_DataContents, Offset, Header->m_CompressedSize);
return true;
}
@ -259,39 +380,36 @@ bool cWSSCompact::cPAKFile::SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_W
bool cWSSCompact::cPAKFile::LoadChunk(const cChunkCoords & a_Chunk, int a_Offset, sChunkHeader * a_Header, cWorld * a_World)
bool cWSSCompact::LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World)
{
// Crude data integrity check:
if (a_Header->m_UncompressedSize < cChunk::c_BlockDataSize)
if (a_UncompressedSize < cChunk::c_BlockDataSize)
{
LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d out of %d needed), erasing",
LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
a_Header->m_UncompressedSize, cChunk::c_BlockDataSize
a_UncompressedSize, cChunk::c_BlockDataSize
);
EraseChunk(a_Chunk);
m_NumDirty++;
EraseChunkData(a_Chunk);
return false;
}
// Decompress the data:
AString UncompressedData;
int errorcode = UncompressString(m_DataContents.data() + a_Offset, a_Header->m_CompressedSize, UncompressedData, a_Header->m_UncompressedSize);
int errorcode = UncompressString(a_Data.data(), a_Data.size(), UncompressedData, a_UncompressedSize);
if (errorcode != Z_OK)
{
LOGERROR("Error %d decompressing data for chunk [%d, %d] from file \"%s\"",
LOGERROR("Error %d decompressing data for chunk [%d, %d]",
errorcode,
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
m_FileName.c_str()
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
);
return false;
}
if (a_Header->m_UncompressedSize != (int)UncompressedData.size())
if (a_UncompressedSize != (int)UncompressedData.size())
{
LOGWARNING("Uncompressed data size differs (exp %d, got %d) for chunk [%d, %d] from file \"%s\"",
a_Header->m_UncompressedSize, UncompressedData.size(),
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
m_FileName.c_str()
LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]",
a_UncompressedSize, UncompressedData.size(),
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
);
return false;
}
@ -299,13 +417,15 @@ bool cWSSCompact::cPAKFile::LoadChunk(const cChunkCoords & a_Chunk, int a_Offset
cEntityList Entities;
cBlockEntityList BlockEntities;
if (a_Header->m_UncompressedSize > cChunk::c_BlockDataSize ) // We gots some extra data :D
if (a_UncompressedSize > cChunk::c_BlockDataSize)
{
Json::Value root; // will contain the root value after parsing.
Json::Reader reader;
if ( !reader.parse( UncompressedData.data() + cChunk::c_BlockDataSize, root, false ) )
{
LOGERROR("Failed to parse trailing JSON!");
LOGERROR("Failed to parse trailing JSON in chunk [%d, %d]!",
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
);
}
else
{
@ -322,7 +442,7 @@ bool cWSSCompact::cPAKFile::LoadChunk(const cChunkCoords & a_Chunk, int a_Offset
void cWSSCompact::cPAKFile::EraseChunk(const cChunkCoords & a_Chunk)
bool cWSSCompact::cPAKFile::EraseChunkData(const cChunkCoords & a_Chunk)
{
int ChunkX = a_Chunk.m_ChunkX;
int ChunkZ = a_Chunk.m_ChunkZ;
@ -334,10 +454,12 @@ void cWSSCompact::cPAKFile::EraseChunk(const cChunkCoords & a_Chunk)
m_DataContents.erase(Offset, (*itr)->m_CompressedSize);
delete *itr;
itr = m_ChunkHeaders.erase(itr);
return;
return true;
}
Offset += (*itr)->m_CompressedSize;
}
return false;
}
@ -376,7 +498,7 @@ bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld
}
// Erase any existing data for the chunk:
EraseChunk(a_Chunk);
EraseChunkData(a_Chunk);
// Save the header:
sChunkHeader * Header = new sChunkHeader;
@ -438,70 +560,3 @@ void cWSSCompact::cPAKFile::SynchronizeFile(void)
void cWSSCompact::cPAKFile::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World)
{
// Load chests
Json::Value AllChests = a_Value.get("Chests", Json::nullValue);
if (!AllChests.empty())
{
for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr )
{
Json::Value & Chest = *itr;
cChestEntity * ChestEntity = new cChestEntity(0,0,0, a_World);
if (!ChestEntity->LoadFromJson( Chest ) )
{
LOGERROR("ERROR READING CHEST FROM JSON!" );
delete ChestEntity;
}
else
{
a_BlockEntities.push_back( ChestEntity );
}
} // for itr - AllChests[]
}
// Load furnaces
Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue);
if( !AllFurnaces.empty() )
{
for( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr )
{
Json::Value & Furnace = *itr;
cFurnaceEntity * FurnaceEntity = new cFurnaceEntity(0,0,0, a_World);
if( !FurnaceEntity->LoadFromJson( Furnace ) )
{
LOGERROR("ERROR READING FURNACE FROM JSON!" );
delete FurnaceEntity;
}
else
{
a_BlockEntities.push_back( FurnaceEntity );
}
} // for itr - AllFurnaces[]
}
// Load signs
Json::Value AllSigns = a_Value.get("Signs", Json::nullValue);
if( !AllSigns.empty() )
{
for( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr )
{
Json::Value & Sign = *itr;
cSignEntity * SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0,0,0, a_World);
if ( !SignEntity->LoadFromJson( Sign ) )
{
LOGERROR("ERROR READING SIGN FROM JSON!" );
delete SignEntity;
}
else
{
a_BlockEntities.push_back( SignEntity );
}
} // for itr - AllSigns[]
}
}

View File

@ -37,6 +37,10 @@ protected:
cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ);
~cPAKFile();
bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data);
bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data);
bool EraseChunkData(const cChunkCoords & a_Chunk);
bool SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World);
bool LoadChunk(const cChunkCoords & a_Chunk, cWorld * a_World);
@ -55,19 +59,32 @@ protected:
int m_NumDirty; // Number of chunks that were written into m_DataContents but not into the file
bool LoadChunk(const cChunkCoords & a_Chunk, int a_Offset, sChunkHeader * a_Header, cWorld * a_World);
void EraseChunk(const cChunkCoords & a_Chunk); // Erases the chunk data from m_DataContents and updates m_ChunkHeaders
bool SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World); // Saves the chunk to m_DataContents, updates headers and m_NumDirty
void SynchronizeFile(void); // Writes m_DataContents along with the headers to file, resets m_NumDirty
void LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World);
} ;
typedef std::list<cPAKFile *> cPAKFiles;
cCriticalSection m_CS;
cPAKFiles m_PAKFiles; // A MRU cache of PAK files
/// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache
cPAKFile * LoadPAKFile(const cChunkCoords & a_Chunk);
/// Gets chunk data from the correct file; locks CS as needed
bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data);
/// Sets chunk data to the correct file; locks CS as needed
bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data);
/// Erases chunk data from the correct file; locks CS as needed
bool EraseChunkData(const cChunkCoords & a_Chunk);
/// Loads the chunk from the data (no locking needed)
bool LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World);
void LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World);
// cWSSchema overrides:
virtual bool LoadChunk(const cChunkCoords & a_Chunk) override;
virtual bool SaveChunk(const cChunkCoords & a_Chunk) override;

View File

@ -335,7 +335,8 @@ bool cWorldStorage::LoadOneChunk(void)
}
HasMore = (m_LoadQueue.size() > 0);
}
if (ShouldLoad && !LoadChunk(cChunkCoords(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ)))
if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ))
{
if (ToLoad.m_Generate)
{
@ -389,22 +390,26 @@ bool cWorldStorage::SaveOneChunk(void)
bool cWorldStorage::LoadChunk(const cChunkCoords & a_Chunk)
bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
if (m_World->IsChunkValid(a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ))
if (m_World->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ))
{
// Already loaded (can happen, since the queue is async)
return true;
}
if (m_SaveSchema->LoadChunk(a_Chunk))
cChunkCoords Coords(a_ChunkX, a_ChunkY, a_ChunkZ);
// First try the schema that is used for saving
if (m_SaveSchema->LoadChunk(Coords))
{
return true;
}
// If it didn't have the chunk, try all the other schemas:
for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr)
{
if (((*itr) != m_SaveSchema) && (*itr)->LoadChunk(a_Chunk))
if (((*itr) != m_SaveSchema) && (*itr)->LoadChunk(Coords))
{
return true;
}

View File

@ -100,6 +100,9 @@ public:
void QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate); // Queues the chunk for loading; if not loaded, the chunk will be generated if a_Generate is true
void QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
/// Loads the chunk specified; returns true on success, false on failure
bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void UnqueueSave(const cChunkCoords & a_Chunk);
@ -150,9 +153,6 @@ protected:
/// Saves one chunk from the queue (if any queued); returns true if there are more chunks in the save queue
bool SaveOneChunk(void);
/// Loads the chunk specified; returns true on success, false on failure
bool LoadChunk(const cChunkCoords & a_Chunk);
} ;

View File

@ -106,12 +106,12 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ
cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
{
cCSLock Lock(m_CSLayers);
// No need to lock m_CSLayers, since it's already 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
return cChunkPtr();
return NULL;
}
cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
@ -132,12 +132,12 @@ cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
{
cCSLock Lock(m_CSLayers);
// No need to lock m_CSLayers, since it's already 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
return cChunkPtr();
return NULL;
}
cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
@ -157,6 +157,23 @@ cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, 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 );
if (Layer == NULL)
{
// An error must have occurred, since layers are automatically created if they don't exist
return NULL;
}
return Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
}
void cChunkMap::BroadcastToChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const cPacket & a_Packet, cClientHandle * a_Exclude)
{
// Broadcasts a_Packet to all clients in the chunk where block [x, y, z] is, except to client a_Exclude
@ -261,7 +278,7 @@ void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ)
void cChunkMap::ChunkDataLoaded(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ);
if (Chunk == NULL)
{
return;
@ -277,7 +294,7 @@ void cChunkMap::ChunkDataLoaded(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const
void cChunkMap::ChunkDataGenerated(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ);
if (Chunk == NULL)
{
return;
@ -329,13 +346,7 @@ bool cChunkMap::GetChunkBlocks(int a_ChunkX, int a_ChunkY, int a_ChunkZ, char *
bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
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
return false;
}
cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ);
return (Chunk != NULL) && Chunk->IsValid();
}
@ -405,6 +416,7 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
{
int ChunkX = a_BlockList.front().ChunkX;
int ChunkZ = a_BlockList.front().ChunkZ;
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
@ -477,7 +489,7 @@ char cChunkMap::GetBlock(int a_X, int a_Y, int a_Z)
AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ );
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
return Chunk->GetBlock(a_X, a_Y, a_Z);
@ -653,7 +665,7 @@ void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1,
bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
cChunkPtr Chunk = GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
if (Chunk == NULL)
{
return false;
@ -697,7 +709,7 @@ void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client, const cChunkCoo
bool cChunkMap::SendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@ -754,6 +766,43 @@ void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, 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_ChunkY, int a_ChunkZ)
{
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
if (Chunk == NULL)
{
// Internal error
return false;
}
if (Chunk->IsValid())
{
// Already loaded
return true;
}
}
return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkY, 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_ChunkY, itr->m_ChunkZ);
} // for itr - a_Chunks[]
}
void cChunkMap::UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
{
cCSLock Lock(m_CSLayers);
@ -771,16 +820,19 @@ void cChunkMap::UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, c
void cChunkMap::ChunkStay(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Stay)
void cChunkMap::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr)
{
cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
if (Chunk == NULL)
{
return;
continue;
}
Chunk->Stay(a_Stay);
}
}
@ -934,7 +986,7 @@ void cChunkMap::cChunkLayer::UnloadUnusedChunks(void)
{
if ((m_Chunks[i] != NULL) && (m_Chunks[i]->CanUnload()))
{
// The chunk destructor calls our GetChunk() while removing its entities
// The cChunk destructor calls our GetChunk() while removing its entities
// so we still need to be able to return the chunk. Therefore we first delete, then NULLify
// Doing otherwise results in bug http://forum.mc-server.org/showthread.php?tid=355
delete m_Chunks[i];
@ -975,7 +1027,8 @@ void cChunkMap::ChunkValidated(void)
// cChunkStay:
cChunkStay::cChunkStay(cWorld * a_World) :
m_World(a_World)
m_World(a_World),
m_IsEnabled(false)
{
}
@ -994,17 +1047,11 @@ cChunkStay::~cChunkStay()
void cChunkStay::Clear(void)
{
cChunkCoordsList Chunks;
if (m_IsEnabled)
{
cCSLock Lock(m_CS);
std::swap(Chunks, m_Chunks);
Disable();
}
// Un-"stay" all chunks:
for (cChunkCoordsList::const_iterator itr = Chunks.begin(); itr != Chunks.end(); ++itr)
{
m_World->ChunkStay(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ, false);
} // for itr - Chunks[]
m_Chunks.clear();
}
@ -1013,16 +1060,17 @@ void cChunkStay::Clear(void)
void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
cCSLock Lock(m_CS);
ASSERT(!m_IsEnabled);
for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
{
if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
{
// Already "stayed"
// Already present
return;
}
} // for itr - Chunks[]
m_World->ChunkStay(a_ChunkX, a_ChunkY, a_ChunkZ);
m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
}
@ -1031,13 +1079,14 @@ void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
cCSLock Lock(m_CS);
ASSERT(!m_IsEnabled);
for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
{
if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
{
// Found, un-"stay"
m_World->ChunkStay(a_ChunkX, a_ChunkY, a_ChunkZ, false);
m_Chunks.erase(itr);
return;
}
} // for itr - Chunks[]
@ -1046,3 +1095,27 @@ void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
void cChunkStay::Enable(void)
{
ASSERT(!m_IsEnabled);
m_World->ChunksStay(*this, true);
m_IsEnabled = true;
}
void cChunkStay::Disable(void)
{
ASSERT(m_IsEnabled);
m_World->ChunksStay(*this, false);
m_IsEnabled = false;
}

View File

@ -83,10 +83,16 @@ public:
/// Touches the chunk, causing it to be loaded or generated
void TouchChunk(int a_ChunkX, int a_ChunkY, 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_ChunkY, int a_ChunkZ);
/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid()
void LoadChunks(const cChunkCoordsList & a_Chunks);
void UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
/// Marks (a_Stay == true) or unmarks (a_Stay == false) a chunk as non-unloadable; to be used only by cChunkStay!
void ChunkStay(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Stay = true);
/// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable; to be used only by cChunkStay!
void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true);
void Tick( float a_Dt, MTRand & a_TickRand );
@ -172,6 +178,7 @@ private:
cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid
cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate
cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate
};
@ -179,7 +186,9 @@ private:
/** Makes chunks stay loaded until this object is cleared or destroyed
Works by setting internal flags in the cChunk that it should not be unloaded
Works by setting internal flags in the cChunk that it should not be unloaded.
To optimize for speed, cChunkStay has an Enabled flag, it will "stay" the chunks only when enabled and it will refuse manipulations when enabled
The object itself is not made thread-safe, it's supposed to be used from a single thread only.
*/
class cChunkStay
{
@ -192,11 +201,18 @@ public:
void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void Enable(void);
void Disable(void);
// Allow cChunkStay be passed to functions expecting a const cChunkCoordsList &
operator const cChunkCoordsList(void) const {return m_Chunks; }
protected:
cWorld * m_World;
cCriticalSection m_CS;
bool m_IsEnabled;
cChunkCoordsList m_Chunks;
} ;

View File

@ -1288,6 +1288,24 @@ void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
}
void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks)
{
m_ChunkMap->LoadChunks(a_Chunks);
}
void cWorld::UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
{
m_ChunkMap->UpdateSign(a_X, a_Y, a_Z, a_Line1, a_Line2, a_Line3, a_Line4);
@ -1297,9 +1315,9 @@ void cWorld::UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, cons
void cWorld::ChunkStay(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Stay)
void cWorld::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay)
{
m_ChunkMap->ChunkStay(a_ChunkX, a_ChunkY, a_ChunkZ, a_Stay);
m_ChunkMap->ChunksStay(a_Chunks, a_Stay);
}

View File

@ -127,10 +127,16 @@ public:
/// Touches the chunk, causing it to be loaded or generated
void TouchChunk(int a_ChunkX, int a_ChunkY, 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_ChunkY, int a_ChunkZ);
/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid()
void LoadChunks(const cChunkCoordsList & a_Chunks);
void UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
/// Marks (a_Stay == true) or unmarks (a_Stay == false) a chunk as non-unloadable; to be used only by cChunkStay!
void ChunkStay(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Stay = true);
/// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay!
void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true);
// TODO: Export to Lua
bool DoWithEntity( int a_UniqueID, cEntityCallback & a_Callback );