#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "cChunkMap.h" #include "cWorld.h" #include "cRoot.h" #include "cMakeDir.h" #include "cPlayer.h" #include "BlockID.h" #include "cItem.h" #include "cPickup.h" #ifndef _WIN32 #include // abs #endif #include "zlib.h" #include #define RECI_RAND_MAX (1.f/RAND_MAX) inline float fRadRand( float a_Radius ) { MTRand r1; return ((float)r1.rand() * RECI_RAND_MAX)*a_Radius - a_Radius*0.5f; } //////////////////////////////////////////////////////////////////////////////// // cChunkMap: cChunkMap::cChunkMap(cWorld * a_World ) : m_World( a_World ) { } cChunkMap::~cChunkMap() { cCSLock Lock(m_CSLayers); for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { delete *itr; } // for itr - m_Layers[] } void cChunkMap::RemoveLayer( cChunkLayer* a_Layer ) { cCSLock Lock(m_CSLayers); m_Layers.remove(a_Layer); } cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ) { cCSLock Lock(m_CSLayers); for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) { return *itr; } } // Not found, create new: cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this); if (Layer == NULL) { LOGERROR("cChunkMap: Cannot create new layer, server out of memory?"); return NULL; } m_Layers.push_back(Layer); return Layer; } cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ ) { const int LayerX = (int)(floorf((float)a_ChunkX / (float)(LAYER_SIZE))); const int LayerZ = (int)(floorf((float)a_ChunkZ / (float)(LAYER_SIZE))); return GetLayer( LayerX, LayerZ ); } cChunkPtr cChunkMap::GetChunk( 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 cChunkPtr(); } cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); if (!(Chunk->IsValid())) { m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); } return Chunk; } cChunkPtr cChunkMap::GetChunkNoGen( 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 cChunkPtr(); } cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); // TODO: Load, but do not generate, if not valid return Chunk; } void cChunkMap::BroadcastToChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, 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 cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); if (Chunk == NULL) { return; } // It's perfectly legal to broadcast packets even to invalid chunks! Chunk->Broadcast(a_Packet, a_Exclude); } void cChunkMap::BroadcastToChunkOfBlock(int a_X, int a_Y, int a_Z, 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 cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; BlockToChunk(a_X, a_Y, a_Z, ChunkX, ChunkZ); cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); if (Chunk == NULL) { return; } // It's perfectly legal to broadcast packets even to invalid chunks! Chunk->Broadcast(a_Packet, a_Exclude); } void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) { // a_Player rclked block entity at the coords specified, handle it cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; BlockToChunk(a_X, a_Y, a_Z, ChunkX, ChunkZ); cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; } Chunk->UseBlockEntity(a_Player, a_X, a_Y, a_Z); } void cChunkMap::MarkChunkDirty (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) || !Chunk->IsValid()) { return; } Chunk->MarkDirty(); } void cChunkMap::MarkChunkSaving(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) || !Chunk->IsValid()) { return; } Chunk->MarkSaving(); } void cChunkMap::MarkChunkSaved (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) || !Chunk->IsValid()) { return; } Chunk->MarkSaved(); } 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); if (Chunk == NULL) { return; } Chunk->SetAllData(a_BlockData, a_Entities, a_BlockEntities); Chunk->MarkLoaded(); } 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); if (Chunk == NULL) { return; } Chunk->SetAllData(a_BlockData, a_Entities, a_BlockEntities); // TODO: This has to go - lighting takes way too long to execute in a locked ChunkMap! Chunk->CalculateLighting(); Chunk->SetValid(); Chunk->MarkDirty(); } void cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback * a_Callback) { cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; } Chunk->GetAllData(a_Callback); } bool cChunkMap::GetChunkBlocks(int a_ChunkX, int a_ChunkY, int a_ChunkZ, char * a_Blocks) { cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; } Chunk->GetBlocks(a_Blocks); return true; } bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkY, int a_ChunkZ) { cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); return (Chunk != NULL) && Chunk->IsValid(); } bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) { cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); return (Chunk != NULL) && Chunk->HasAnyClients(); } void cChunkMap::SpreadChunkLighting(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) && Chunk->IsValid()) { // TODO: Rewrite this to call Chunk's lighting without any parameters Chunk->SpreadLight( Chunk->pGetSkyLight() ); Chunk->SpreadLight( Chunk->pGetLight() ); } } int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ) { cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ, BlockY = 0; AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); if (Chunk == NULL) { return 0; } // Wait for the chunk to become valid: while (!Chunk->IsValid()) { GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); // Re-queue (in case it managed to get unloaded before we caught it cCSUnlock Unlock(Lock); m_evtChunkValid.Wait(); } return Chunk->GetHeight(a_BlockX, a_BlockZ); } void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) { sSetBlockList Failed; // Process all items from a_BlockList, either successfully or by placing into Failed while (!a_BlockList.empty()) { int ChunkX = a_BlockList.front().ChunkX; int ChunkZ = a_BlockList.front().ChunkZ; cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) { if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) { Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); itr = a_BlockList.erase(itr); } else { ++itr; } } // for itr - a_BlockList[] } else { // The chunk is not valid, move all blocks within this chunk to Failed for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) { if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) { Failed.push_back(*itr); itr = a_BlockList.erase(itr); } else { ++itr; } } // for itr - a_BlockList[] } } // Return the failed: std::swap(Failed, a_BlockList); } void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player) { int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway int BlockY = (int)(a_Player->GetPosY()); int BlockZ = (int)(a_Player->GetPosZ()); int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y; AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1); int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1); cCSLock Lock(m_CSLayers); GetChunkNoGen(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player); // Check the neighboring chunks as well: GetChunkNoGen(OtherChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); GetChunkNoGen(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); GetChunkNoGen(ChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); GetChunkNoGen(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); } char cChunkMap::GetBlock(int a_X, int a_Y, int a_Z) { int ChunkX, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { return Chunk->GetBlock(a_X, a_Y, a_Z); } return 0; } char cChunkMap::GetBlockMeta(int a_X, int a_Y, int a_Z) { int ChunkX, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); if ((Chunk != NULL) && Chunk->IsValid() ) { // Although it is called GetLight(), it actually gets meta when passed the Meta field return Chunk->GetLight( Chunk->pGetMeta(), a_X, a_Y, a_Z ); } return 0; } void cChunkMap::SetBlockMeta(int a_X, int a_Y, int a_Z, char a_BlockMeta) { int ChunkX, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); if ((Chunk != NULL) && Chunk->IsValid() ) { // Although it is called SetLight(), it actually sets meta when passed the Meta field Chunk->SetLight( Chunk->pGetMeta(), a_X, a_Y, a_Z, a_BlockMeta ); Chunk->SendBlockTo( a_X, a_Y, a_Z, NULL ); } } void cChunkMap::SetBlock(int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta) { int ChunkX, ChunkZ, X = a_X, Y = a_Y, Z = a_Z; AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); if ((Chunk != NULL) && Chunk->IsValid()) { Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); } } bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z, cItem & a_PickupItem) { int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ; AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ ); cCSLock Lock(m_CSLayers); cChunkPtr DestChunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); if ((DestChunk == NULL) || !DestChunk->IsValid()) { return false; } DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0 ); m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z); if ( !a_PickupItem.IsEmpty() ) { cPickup * Pickup = new cPickup( a_X * 32 + 16 + (int)fRadRand(16.f), a_Y * 32 + 16 + (int)fRadRand(16.f), a_Z * 32 + 16 + (int)fRadRand(16.f), a_PickupItem ); Pickup->Initialize(m_World); } return true; } void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) { int ChunkX, ChunkZ; AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); if (Chunk->IsValid()) { Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle()); } } void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback) { cCSLock Lock(m_CSLayers); cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, a_ChunkY1, a_ChunkZ1); if (Chunk1 == NULL) { return; } cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, a_ChunkY2, a_ChunkZ2); if (Chunk2 == NULL) { return; } cClientHandleList Clients1(Chunk1->GetAllClients()); cClientHandleList Clients2(Chunk2->GetAllClients()); // Find "removed" clients: for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) { bool Found = false; for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) { if (*itr1 == *itr2) { Found = true; break; } } // for itr2 - Clients2[] if (!Found) { a_Callback.Removed(*itr1); } } // for itr1 - Clients1[] // Find "added" clients: for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) { bool Found = false; for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) { if (*itr1 == *itr2) { Found = true; break; } } // for itr1 - Clients1[] if (!Found) { a_Callback.Added(*itr2); } } // for itr2 - Clients2[] } 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); if (Chunk == NULL) { return false; } return Chunk->AddClient(a_Client); } void cChunkMap::RemoveChunkClient(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); if (Chunk == NULL) { return; } Chunk->RemoveClient(a_Client); } void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client, const cChunkCoordsList & a_Chunks) { cCSLock Lock(m_CSLayers); for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) { GetChunkNoGen(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ)->RemoveClient(a_Client); } } 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); if ((Chunk == NULL) || !Chunk->IsValid()) { return false; } Chunk->SendTo(a_Client); return true; } void cChunkMap::MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ) { cCSLock Lock(m_CSLayers); cChunkPtr OldChunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkY(), a_Entity->GetChunkZ()); if (OldChunk != NULL) { OldChunk->RemoveEntity(a_Entity); } cChunkPtr NewChunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); if (NewChunk != NULL) { NewChunk->AddEntity(a_Entity); } } void cChunkMap::RemoveEntityFromChunk(cEntity * a_Entity, 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) && !Chunk->IsValid()) { return; } Chunk->RemoveEntity(a_Entity); } void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) { cCSLock Lock(m_CSLayers); GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); } 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); int ChunkX, ChunkZ; BlockToChunk(a_X, a_Y, a_Z, ChunkX, ChunkZ); cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); if ((Chunk == NULL) || !Chunk->IsValid()) { return; } Chunk->UpdateSign(a_X, a_Y, a_Z, a_Line1, a_Line2, a_Line3, a_Line4); } void cChunkMap::Tick( float a_Dt, MTRand & a_TickRandom ) { cCSLock Lock(m_CSLayers); for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { (*itr)->Tick(a_Dt, a_TickRandom); } // for itr - m_Layers } void cChunkMap::UnloadUnusedChunks() { cCSLock Lock(m_CSLayers); for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { (*itr)->UnloadUnusedChunks(); } // for itr - m_Layers } void cChunkMap::SaveAllChunks(void) { cCSLock Lock(m_CSLayers); for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { (*itr)->Save(); } // for itr - m_Layers[] } //////////////////////////////////////////////////////////////////////////////// // cChunkMap::cChunkLayer: cChunkMap::cChunkLayer::cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent) : m_LayerX( a_LayerX ) , m_LayerZ( a_LayerZ ) , m_Parent( a_Parent ) , m_NumChunksLoaded( 0 ) { } 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; if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) { ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); return cChunkPtr(); } int Index = LocalX + LocalZ * LAYER_SIZE; if (m_Chunks[Index].get() == NULL) { m_Chunks[Index].reset(new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld())); } return m_Chunks[Index]; } void cChunkMap::cChunkLayer::Tick(float a_Dt, MTRand & a_TickRand) { for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) { // Only tick chunks that are valid and have clients: if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) { m_Chunks[i]->Tick(a_Dt, a_TickRand); } } // for i - m_Chunks[] } int cChunkMap::cChunkLayer::GetNumChunksLoaded(void) const { int NumChunks = 0; for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) { if (m_Chunks[i] != NULL) { NumChunks++; } } // for i - m_Chunks[] return NumChunks; } void cChunkMap::cChunkLayer::Save(void) { cWorld * World = m_Parent->GetWorld(); for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) { if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->IsDirty()) { World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosY(), m_Chunks[i]->GetPosZ()); } } // for i - m_Chunks[] } void cChunkMap::cChunkLayer::UnloadUnusedChunks(void) { for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) { if ((m_Chunks[i] != NULL) && (m_Chunks[i]->CanUnload())) { m_Chunks[i].reset(); } } // for i - m_Chunks[] } int cChunkMap::GetNumChunks(void) { cCSLock Lock(m_CSLayers); int NumChunks = 0; for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { NumChunks += (*itr)->GetNumChunksLoaded(); } return NumChunks; } void cChunkMap::ChunkValidated(void) { m_evtChunkValid.Set(); }