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) bool cWSSCompact::LoadChunk(const cChunkCoords & a_Chunk)
{ {
cPAKFile * f = LoadPAKFile(a_Chunk); AString ChunkData;
if (f == NULL) int UncompressedSize = 0;
if (!GetChunkData(a_Chunk, UncompressedSize, ChunkData))
{ {
// For some reason we couldn't locate the file // The reason for failure is already printed in GetChunkData()
LOG("Cannot locate a proper PAK file for chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false; 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) bool cWSSCompact::SaveChunk(const cChunkCoords & a_Chunk)
{ {
cCSLock Lock(m_CS);
cPAKFile * f = LoadPAKFile(a_Chunk); cPAKFile * f = LoadPAKFile(a_Chunk);
if (f == NULL) if (f == NULL)
{ {
@ -93,6 +95,8 @@ bool cWSSCompact::SaveChunk(const cChunkCoords & a_Chunk)
cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(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 // 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 LayerX = (int)(floorf((float)a_Chunk.m_ChunkX / 32.0f));
const int LayerZ = (int)(floorf((float)a_Chunk.m_ChunkZ / 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 // 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 ChunkX = a_Chunk.m_ChunkX;
int ChunkZ = a_Chunk.m_ChunkZ; int ChunkZ = a_Chunk.m_ChunkZ;
@ -235,7 +354,9 @@ bool cWSSCompact::cPAKFile::LoadChunk(const cChunkCoords & a_Chunk, cWorld * a_W
return false; 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: // 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_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
a_Header->m_UncompressedSize, cChunk::c_BlockDataSize a_UncompressedSize, cChunk::c_BlockDataSize
); );
EraseChunk(a_Chunk); EraseChunkData(a_Chunk);
m_NumDirty++;
return false; return false;
} }
// Decompress the data: // Decompress the data:
AString UncompressedData; 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) 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, errorcode,
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
m_FileName.c_str()
); );
return false; 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\"", LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]",
a_Header->m_UncompressedSize, UncompressedData.size(), a_UncompressedSize, UncompressedData.size(),
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
m_FileName.c_str()
); );
return false; return false;
} }
@ -299,13 +417,15 @@ bool cWSSCompact::cPAKFile::LoadChunk(const cChunkCoords & a_Chunk, int a_Offset
cEntityList Entities; cEntityList Entities;
cBlockEntityList BlockEntities; 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::Value root; // will contain the root value after parsing.
Json::Reader reader; Json::Reader reader;
if ( !reader.parse( UncompressedData.data() + cChunk::c_BlockDataSize, root, false ) ) 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 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 ChunkX = a_Chunk.m_ChunkX;
int ChunkZ = a_Chunk.m_ChunkZ; 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); m_DataContents.erase(Offset, (*itr)->m_CompressedSize);
delete *itr; delete *itr;
itr = m_ChunkHeaders.erase(itr); itr = m_ChunkHeaders.erase(itr);
return; return true;
} }
Offset += (*itr)->m_CompressedSize; 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: // Erase any existing data for the chunk:
EraseChunk(a_Chunk); EraseChunkData(a_Chunk);
// Save the header: // Save the header:
sChunkHeader * Header = new sChunkHeader; 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(const AString & a_FileName, int a_LayerX, int a_LayerZ);
~cPAKFile(); ~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 SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World);
bool LoadChunk(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 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); 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 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 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; typedef std::list<cPAKFile *> cPAKFiles;
cCriticalSection m_CS;
cPAKFiles m_PAKFiles; // A MRU cache of PAK files 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 /// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache
cPAKFile * LoadPAKFile(const cChunkCoords & a_Chunk); 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: // cWSSchema overrides:
virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; virtual bool LoadChunk(const cChunkCoords & a_Chunk) override;
virtual bool SaveChunk(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); 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) 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) // Already loaded (can happen, since the queue is async)
return true; 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; 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) 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; 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 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); 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 UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void UnqueueSave(const cChunkCoords & a_Chunk); 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 /// Saves one chunk from the queue (if any queued); returns true if there are more chunks in the save queue
bool SaveOneChunk(void); 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 ) 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 ); cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ );
if (Layer == NULL) if (Layer == NULL)
{ {
// An error must have occurred, since layers are automatically created if they don't exist // 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); 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 ) 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 ); cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ );
if (Layer == NULL) if (Layer == NULL)
{ {
// An error must have occurred, since layers are automatically created if they don't exist // 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); 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) 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 // 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) 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); 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) if (Chunk == NULL)
{ {
return; 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) 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); 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) if (Chunk == NULL)
{ {
return; 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) bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{ {
cCSLock Lock(m_CSLayers); cCSLock Lock(m_CSLayers);
cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, 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);
return (Chunk != NULL) && Chunk->IsValid(); return (Chunk != NULL) && Chunk->IsValid();
} }
@ -405,6 +416,7 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
{ {
int ChunkX = a_BlockList.front().ChunkX; int ChunkX = a_BlockList.front().ChunkX;
int ChunkZ = a_BlockList.front().ChunkZ; int ChunkZ = a_BlockList.front().ChunkZ;
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid()) 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 ); AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ );
cCSLock Lock(m_CSLayers); 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()) if ((Chunk != NULL) && Chunk->IsValid())
{ {
return Chunk->GetBlock(a_X, a_Y, a_Z); 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) bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
{ {
cCSLock Lock(m_CSLayers); 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) if (Chunk == NULL)
{ {
return false; 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) bool cChunkMap::SendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
{ {
cCSLock Lock(m_CSLayers); 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()) if ((Chunk == NULL) || !Chunk->IsValid())
{ {
return false; 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) 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); cCSLock Lock(m_CSLayers);
@ -771,15 +820,18 @@ 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); 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)
if (Chunk == NULL)
{ {
return; cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
if (Chunk == NULL)
{
continue;
}
Chunk->Stay(a_Stay);
} }
Chunk->Stay(a_Stay);
} }
@ -856,7 +908,7 @@ cChunkMap::cChunkLayer::~cChunkLayer()
cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
{ {
// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check // Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check
const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE;
const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE;
@ -934,7 +986,7 @@ void cChunkMap::cChunkLayer::UnloadUnusedChunks(void)
{ {
if ((m_Chunks[i] != NULL) && (m_Chunks[i]->CanUnload())) 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 // 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 // Doing otherwise results in bug http://forum.mc-server.org/showthread.php?tid=355
delete m_Chunks[i]; delete m_Chunks[i];
@ -975,7 +1027,8 @@ void cChunkMap::ChunkValidated(void)
// cChunkStay: // cChunkStay:
cChunkStay::cChunkStay(cWorld * a_World) : 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) void cChunkStay::Clear(void)
{ {
cChunkCoordsList Chunks; if (m_IsEnabled)
{ {
cCSLock Lock(m_CS); Disable();
std::swap(Chunks, m_Chunks);
} }
m_Chunks.clear();
// 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[]
} }
@ -1013,16 +1060,17 @@ void cChunkStay::Clear(void)
void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ) 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) 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)) if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
{ {
// Already "stayed" // Already present
return; return;
} }
} // for itr - Chunks[] } // 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) 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) 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)) if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
{ {
// Found, un-"stay" // Found, un-"stay"
m_World->ChunkStay(a_ChunkX, a_ChunkY, a_ChunkZ, false); m_Chunks.erase(itr);
return; return;
} }
} // for itr - Chunks[] } // 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 /// Touches the chunk, causing it to be loaded or generated
void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); 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); 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! /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable; to be used only by cChunkStay!
void ChunkStay(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Stay = true); void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true);
void Tick( float a_Dt, MTRand & a_TickRand ); void Tick( float a_Dt, MTRand & a_TickRand );
@ -170,8 +176,9 @@ private:
cWorld * m_World; cWorld * m_World;
cChunkPtr GetChunk ( int a_ChunkX, int a_ChunkY, int a_ChunkZ ); // Also queues the chunk for loading / generating if not valid 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 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 /** 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 class cChunkStay
{ {
@ -192,11 +201,18 @@ public:
void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ); void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void Remove(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: protected:
cWorld * m_World; cWorld * m_World;
cCriticalSection m_CS; bool m_IsEnabled;
cChunkCoordsList m_Chunks; 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) 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); 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 /// Touches the chunk, causing it to be loaded or generated
void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); 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); 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! /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay!
void ChunkStay(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Stay = true); void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true);
// TODO: Export to Lua // TODO: Export to Lua
bool DoWithEntity( int a_UniqueID, cEntityCallback & a_Callback ); bool DoWithEntity( int a_UniqueID, cEntityCallback & a_Callback );