From 310c1076a7acce6e4dd4c2611527b65fe86fe351 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Fri, 14 Dec 2012 22:38:30 +0000 Subject: [PATCH] cChunk now tracks its neighbors as direct pointers; used in UnboundedRelGetBlock et al. git-svn-id: http://mc-server.googlecode.com/svn/trunk@1073 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/Chunk.cpp | 120 +++++++++++++++++++++++++++++++++++++++++++- source/Chunk.h | 17 +++++-- source/ChunkMap.cpp | 76 +++++++++++++++++++++++++++- source/ChunkMap.h | 24 +++++++-- 4 files changed, 224 insertions(+), 13 deletions(-) diff --git a/source/Chunk.cpp b/source/Chunk.cpp index f297e0bbd..5bfe28e41 100644 --- a/source/Chunk.cpp +++ b/source/Chunk.cpp @@ -55,7 +55,11 @@ sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_Bloc /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cChunk: -cChunk::cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World) +cChunk::cChunk( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, + cChunkMap * a_ChunkMap, cWorld * a_World, + cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP +) : m_PosX( a_ChunkX ) , m_PosY( a_ChunkY ) , m_PosZ( a_ChunkZ ) @@ -69,8 +73,27 @@ cChunk::cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, , m_IsDirty(false) , m_IsSaving(false) , m_StayCount(0) + , m_NeighborXM(a_NeighborXM) + , m_NeighborXP(a_NeighborXP) + , m_NeighborZM(a_NeighborZM) + , m_NeighborZP(a_NeighborZP) { - // LOGINFO("### new cChunk (%i, %i) at %p, thread 0x%x ###", a_X, a_Z, this, GetCurrentThreadId()); + if (a_NeighborXM != NULL) + { + a_NeighborXM->m_NeighborXP = this; + } + if (a_NeighborXP != NULL) + { + a_NeighborXP->m_NeighborXM = this; + } + if (a_NeighborZM != NULL) + { + a_NeighborZM->m_NeighborZP = this; + } + if (a_NeighborZP != NULL) + { + a_NeighborZP->m_NeighborZM = this; + } } @@ -102,6 +125,23 @@ cChunk::~cChunk() (*itr)->Destroy(); } m_Entities.clear(); + + if (m_NeighborXM != NULL) + { + m_NeighborXM->m_NeighborXP = NULL; + } + if (m_NeighborXP != NULL) + { + m_NeighborXP->m_NeighborXM = NULL; + } + if (m_NeighborZM != NULL) + { + m_NeighborZM->m_NeighborZP = NULL; + } + if (m_NeighborZP != NULL) + { + m_NeighborZP->m_NeighborZM = NULL; + } } @@ -772,9 +812,11 @@ bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE { if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) { + LOGWARNING("UnboundedRelGetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); return false; } + // Is it in this chunk? if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) { int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); @@ -782,6 +824,26 @@ bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockMeta = GetMeta(BlockIdx); return true; } + + // Not in this chunk, try walking the neighbors first: + if ((a_RelX < 0) && (m_NeighborXM != NULL)) + { + return m_NeighborXM->UnboundedRelGetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + } + if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL)) + { + return m_NeighborXP->UnboundedRelGetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + } + if ((a_RelZ < 0) && (m_NeighborZM != NULL)) + { + return m_NeighborZM->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta); + } + if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL)) + { + return m_NeighborZP->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta); + } + + // Neighbors not available, use the chunkmap to locate the chunk: return m_ChunkMap->LockedGetBlock( m_PosX * cChunkDef::Width + a_RelX, ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, @@ -796,11 +858,38 @@ bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { + if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) + { + LOGWARNING("UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); + return false; + } + + // Is it in this chunk? if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) { SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); return true; } + + // Not in this chunk, try walking the neighbors first: + if ((a_RelX < 0) && (m_NeighborXM != NULL)) + { + return m_NeighborXM->UnboundedRelSetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + } + if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL)) + { + return m_NeighborXP->UnboundedRelSetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + } + if ((a_RelZ < 0) && (m_NeighborZM != NULL)) + { + return m_NeighborZM->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta); + } + if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL)) + { + return m_NeighborZP->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta); + } + + // Neighbors not available, use the chunkmap to locate the chunk: return m_ChunkMap->LockedSetBlock( m_PosX * cChunkDef::Width + a_RelX, ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, @@ -815,11 +904,38 @@ bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { + if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) + { + LOGWARNING("UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); + return false; + } + + // Is it in this chunk? if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) { FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); return true; } + + // Not in this chunk, try walking the neighbors first: + if ((a_RelX < 0) && (m_NeighborXM != NULL)) + { + return m_NeighborXM->UnboundedRelFastSetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + } + if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL)) + { + return m_NeighborXP->UnboundedRelFastSetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + } + if ((a_RelZ < 0) && (m_NeighborZM != NULL)) + { + return m_NeighborZM->UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta); + } + if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL)) + { + return m_NeighborZP->UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta); + } + + // Neighbors not available, use the chunkmap to locate the chunk: return m_ChunkMap->LockedFastSetBlock( m_PosX * cChunkDef::Width + a_RelX, ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, diff --git a/source/Chunk.h b/source/Chunk.h index eaa4f82b8..c285a26e3 100644 --- a/source/Chunk.h +++ b/source/Chunk.h @@ -59,7 +59,11 @@ class cChunk : public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef { public: - cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World); + cChunk( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords + cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects + cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks + ); ~cChunk(); bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated) @@ -267,6 +271,11 @@ private: cChunkDef::BiomeMap m_BiomeMap; int m_BlockTickX, m_BlockTickY, m_BlockTickZ; + + cChunk * m_NeighborXM; // Neighbor at [X - 1, Z] + cChunk * m_NeighborXP; // Neighbor at [X + 1, Z] + cChunk * m_NeighborZM; // Neighbor at [X, Z - 1] + cChunk * m_NeighborZP; // Neighbor at [X, Z + 1] void RemoveBlockEntity( cBlockEntity* a_BlockEntity ); void AddBlockEntity( cBlockEntity* a_BlockEntity ); @@ -303,13 +312,13 @@ private: /// Checks if a leaves block at the specified coords has a log up to 4 blocks away connected by other leaves blocks (false if no log) bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); - /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() + /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick() bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() + /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick() bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() + /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick() bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); }; diff --git a/source/ChunkMap.cpp b/source/ChunkMap.cpp index 9c93f3e08..61ca51ab0 100644 --- a/source/ChunkMap.cpp +++ b/source/ChunkMap.cpp @@ -84,7 +84,38 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ) -cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ ) +cChunkMap::cChunkLayer * cChunkMap::FindLayerForChunk(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 FindLayer(LayerX, LayerZ); +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::FindLayer(int a_LayerX, int a_LayerZ) +{ + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) + { + return *itr; + } + } // for itr - m_Layers[] + + // Not found + return NULL; +} + + + + + +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))); @@ -98,6 +129,8 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ cChunkPtr cChunkMap::GetChunk( 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 + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); if (Layer == NULL) { @@ -224,6 +257,22 @@ bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLO +cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + cChunkLayer * Layer = FindLayerForChunk(a_ChunkX, a_ChunkZ); + if (Layer == NULL) + { + return NULL; + } + return Layer->FindChunk(a_ChunkX, a_ChunkZ); +} + + + + + void cChunkMap::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); @@ -1786,7 +1835,11 @@ cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_Ch int Index = LocalX + LocalZ * LAYER_SIZE; if (m_Chunks[Index] == NULL) { - m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld()); + cChunk * neixm = (LocalX > 0) ? m_Chunks[Index - 1] : m_Parent->FindChunk(a_ChunkX - 1, a_ChunkZ); + cChunk * neixp = (LocalX < LAYER_SIZE - 1) ? m_Chunks[Index + 1] : m_Parent->FindChunk(a_ChunkX + 1, a_ChunkZ); + cChunk * neizm = (LocalZ > 0) ? m_Chunks[Index - LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ - 1); + cChunk * neizp = (LocalZ < LAYER_SIZE - 1) ? m_Chunks[Index + LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ + 1); + m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp); } return m_Chunks[Index]; } @@ -1795,6 +1848,25 @@ cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_Ch +cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ) +{ + 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 NULL; + } + + int Index = LocalX + LocalZ * LAYER_SIZE; + return m_Chunks[Index]; +} + + + + + void cChunkMap::cChunkLayer::Tick(float a_Dt, MTRand & a_TickRand) { for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) diff --git a/source/ChunkMap.h b/source/ChunkMap.h index 38d16d0c4..5dd4778ce 100644 --- a/source/ChunkMap.h +++ b/source/ChunkMap.h @@ -264,6 +264,9 @@ private: /// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ); + /// Returns the specified chunk, or NULL if not created yet + cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); + int GetX(void) const {return m_LayerX; } int GetZ(void) const {return m_LayerZ; } @@ -288,12 +291,20 @@ private: }; typedef std::list cChunkLayerList; - // TODO: Use smart pointers for cChunkLayerList as well, so that ticking and saving needn't lock the entire layerlist - // This however means that cChunkLayer needs to interlock its m_Chunks[] - cChunkLayer * GetLayerForChunk( int a_ChunkX, int a_ChunkZ ); // Creates the layer if it doesn't already exist - cChunkLayer * GetLayer( int a_LayerX, int a_LayerZ ); // Creates the layer if it doesn't already exist - void RemoveLayer( cChunkLayer* a_Layer ); + /// Finds the cChunkLayer object responsible for the specified chunk; returns NULL if not found. Assumes m_CSLayers is locked. + cChunkLayer * FindLayerForChunk(int a_ChunkX, int a_ChunkZ); + + /// Returns the specified cChunkLayer object; returns NULL if not found. Assumes m_CSLayers is locked. + cChunkLayer * FindLayer(int a_LayerX, int a_LayerZ); + + /// Returns the cChunkLayer object responsible for the specified chunk; creates it if not found. + cChunkLayer * GetLayerForChunk (int a_ChunkX, int a_ChunkZ); + + /// Returns the specified cChunkLayer object; creates it if not found. + cChunkLayer * GetLayer(int a_LayerX, int a_LayerZ); + + void RemoveLayer(cChunkLayer * a_Layer); cCriticalSection m_CSLayers; cChunkLayerList m_Layers; @@ -313,6 +324,9 @@ private: /// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSLayers is locked. To be called only from cChunkMap. + cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); };