From 230f98a774d956934e42cb4ba7b2cddcdc365676 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Tue, 28 Feb 2012 10:45:53 +0000 Subject: [PATCH] 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 --- source/WSSCompact.cpp | 243 ++++++++++++++++++++++++---------------- source/WSSCompact.h | 21 +++- source/WorldStorage.cpp | 15 ++- source/WorldStorage.h | 6 +- source/cChunkMap.cpp | 149 +++++++++++++++++------- source/cChunkMap.h | 28 ++++- source/cWorld.cpp | 22 +++- source/cWorld.h | 10 +- 8 files changed, 342 insertions(+), 152 deletions(-) diff --git a/source/WSSCompact.cpp b/source/WSSCompact.cpp index 3b0f6c2c..bb47ac0c 100644 --- a/source/WSSCompact.cpp +++ b/source/WSSCompact.cpp @@ -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[] - } -} - - - - diff --git a/source/WSSCompact.h b/source/WSSCompact.h index a2bfc4d2..1cb8d161 100644 --- a/source/WSSCompact.h +++ b/source/WSSCompact.h @@ -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 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; diff --git a/source/WorldStorage.cpp b/source/WorldStorage.cpp index 2d8d5bbc..17c58c54 100644 --- a/source/WorldStorage.cpp +++ b/source/WorldStorage.cpp @@ -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; } diff --git a/source/WorldStorage.h b/source/WorldStorage.h index 9c3a3877..689d5f1e 100644 --- a/source/WorldStorage.h +++ b/source/WorldStorage.h @@ -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); } ; diff --git a/source/cChunkMap.cpp b/source/cChunkMap.cpp index 140a3f5c..2f4d39ca 100644 --- a/source/cChunkMap.cpp +++ b/source/cChunkMap.cpp @@ -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,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); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) + for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) { - 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 ) { // 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 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())) { - // 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; +} + + + + diff --git a/source/cChunkMap.h b/source/cChunkMap.h index f22c6f04..1b7192a9 100644 --- a/source/cChunkMap.h +++ b/source/cChunkMap.h @@ -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 ); @@ -170,8 +176,9 @@ private: 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 GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ ); // Also queues the chunk for loading if not valid; doesn't generate + 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; } ; diff --git a/source/cWorld.cpp b/source/cWorld.cpp index 5cee37e4..e5947686 100644 --- a/source/cWorld.cpp +++ b/source/cWorld.cpp @@ -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); } diff --git a/source/cWorld.h b/source/cWorld.h index a6f2ce7f..a0dd435d 100644 --- a/source/cWorld.h +++ b/source/cWorld.h @@ -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 );