diff options
Diffstat (limited to '')
39 files changed, 1034 insertions, 2131 deletions
diff --git a/src/AllocationPool.h b/src/AllocationPool.h deleted file mode 100644 index 0af56ce2f..000000000 --- a/src/AllocationPool.h +++ /dev/null @@ -1,187 +0,0 @@ - -#pragma once - - - - - -template <class T> -class cAllocationPool -{ -public: - class cStarvationCallbacks - { - public: - virtual ~cStarvationCallbacks() {} - - /** Is called when the reserve buffer starts to be used */ - virtual void OnStartUsingReserve() = 0; - - /** Is called once the reserve buffer has returned to normal size */ - virtual void OnEndUsingReserve() = 0; - - /** Is called when the allocation pool is unable to allocate memory. Will be repeatedly - called if it does not free sufficient memory */ - virtual void OnOutOfReserve() = 0; - }; - - virtual ~cAllocationPool() {} - - /** Allocates a pointer to T */ - virtual T * Allocate() = 0; - - /** Frees the pointer passed in a_ptr, invalidating it */ - virtual void Free(T * a_ptr) = 0; - - /** Two pools compare equal if memory allocated by one can be freed by the other */ - bool IsEqual(const cAllocationPool & a_Other) const noexcept - { - return ((this == &a_Other) || DoIsEqual(a_Other) || a_Other.DoIsEqual(*this)); - } - - friend bool operator == (const cAllocationPool & a_Lhs, const cAllocationPool & a_Rhs) - { - return a_Lhs.IsEqual(a_Rhs); - } - - friend bool operator != (const cAllocationPool & a_Lhs, const cAllocationPool & a_Rhs) - { - return !a_Lhs.IsEqual(a_Rhs); - } - -private: - virtual bool DoIsEqual(const cAllocationPool & a_Other) const noexcept = 0; -}; - - - - - -/** Allocates memory storing unused elements in a linked list. Keeps at least NumElementsInReserve -elements in the list unless malloc fails so that the program has a reserve to handle OOM. */ -template <class T> -class cListAllocationPool: - public cAllocationPool<T> -{ -public: - - cListAllocationPool(std::unique_ptr<typename cAllocationPool<T>::cStarvationCallbacks> a_Callbacks, size_t a_MinElementsInReserve, size_t a_MaxElementsInReserve) : - m_MinElementsInReserve(a_MinElementsInReserve), - m_MaxElementsInReserve(a_MaxElementsInReserve), - m_Callbacks(std::move(a_Callbacks)) - { - for (size_t i = 0; i < m_MinElementsInReserve; i++) - { - void * space = malloc(sizeof(T)); - if (space == nullptr) - { - m_Callbacks->OnStartUsingReserve(); - break; - } - m_FreeList.push_front(space); - } - } - - - virtual ~cListAllocationPool() override - { - while (!m_FreeList.empty()) - { - free(m_FreeList.front()); - m_FreeList.pop_front(); - } - } - - - virtual T * Allocate() override - { - if (m_FreeList.size() <= m_MinElementsInReserve) - { - void * space = malloc(sizeof(T)); - if (space != nullptr) - { - #if defined(_MSC_VER) && !defined(NDEBUG) - // The debugging-new that is set up using macros in Globals.h doesn't support the placement-new syntax used here. - // Temporarily disable the macro - #pragma push_macro("new") - #undef new - #endif - - return new(space) T; - - #if defined(_MSC_VER) && !defined(NDEBUG) - // Re-enable the debugging-new macro - #pragma pop_macro("new") - #endif - } - else if (m_FreeList.size() == m_MinElementsInReserve) - { - m_Callbacks->OnStartUsingReserve(); - } - else if (m_FreeList.empty()) - { - m_Callbacks->OnOutOfReserve(); - // Try again until the memory is avalable - return Allocate(); - } - } - // placement new, used to initalize the object - - #if defined(_MSC_VER) && !defined(NDEBUG) - // The debugging-new that is set up using macros in Globals.h doesn't support the placement-new syntax used here. - // Temporarily disable the macro - #pragma push_macro("new") - #undef new - #endif - - T * ret = new (m_FreeList.front()) T; - - #if defined(_MSC_VER) && !defined(NDEBUG) - // Re-enable the debugging-new macro - #pragma pop_macro("new") - #endif - - m_FreeList.pop_front(); - return ret; - } - - - virtual void Free(T * a_ptr) override - { - if (a_ptr == nullptr) - { - return; - } - - a_ptr->~T(); // placement destruct. - - if (m_FreeList.size() >= m_MaxElementsInReserve) - { - free(a_ptr); - return; - } - - m_FreeList.push_front(a_ptr); - if (m_FreeList.size() == m_MinElementsInReserve) - { - m_Callbacks->OnEndUsingReserve(); - } - } - -private: - /** The minimum number of elements to keep in the free list before malloc fails */ - size_t m_MinElementsInReserve; - /** Maximum free list size before returning memory to the OS */ - size_t m_MaxElementsInReserve; - std::list<void *> m_FreeList; - std::unique_ptr<typename cAllocationPool<T>::cStarvationCallbacks> m_Callbacks; - - virtual bool DoIsEqual(const cAllocationPool<T> & a_Other) const noexcept override - { - return (dynamic_cast<const cListAllocationPool<T>*>(&a_Other) != nullptr); - } -}; - - - - diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp index 1e70fc9ba..e9cd60242 100644 --- a/src/BlockArea.cpp +++ b/src/BlockArea.cpp @@ -2789,7 +2789,7 @@ bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ) -void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer) +void cBlockArea::cChunkReader::ChunkData(const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData) { int SizeY = m_Area.m_Size.y; int MinY = m_Origin.y; @@ -2848,7 +2848,7 @@ void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer) { int InChunkX = BaseX + x; int AreaX = OffX + x; - m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlock({ InChunkX, InChunkY, InChunkZ }); + m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockData.GetBlock({ InChunkX, InChunkY, InChunkZ }); } // for x } // for z } // for y @@ -2869,7 +2869,7 @@ void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer) { int InChunkX = BaseX + x; int AreaX = OffX + x; - m_Area.m_BlockMetas[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetMeta({ InChunkX, InChunkY, InChunkZ }); + m_Area.m_BlockMetas[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockData.GetMeta({ InChunkX, InChunkY, InChunkZ }); } // for x } // for z } // for y @@ -2890,7 +2890,7 @@ void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer) { int InChunkX = BaseX + x; int AreaX = OffX + x; - m_Area.m_BlockLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlockLight({ InChunkX, InChunkY, InChunkZ }); + m_Area.m_BlockLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_LightData.GetBlockLight({ InChunkX, InChunkY, InChunkZ }); } // for x } // for z } // for y @@ -2911,7 +2911,7 @@ void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer) { int InChunkX = BaseX + x; int AreaX = OffX + x; - m_Area.m_BlockSkyLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetSkyLight({ InChunkX, InChunkY, InChunkZ }); + m_Area.m_BlockSkyLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_LightData.GetSkyLight({ InChunkX, InChunkY, InChunkZ }); } // for x } // for z } // for y diff --git a/src/BlockArea.h b/src/BlockArea.h index 50bb54a4c..b672a18ec 100644 --- a/src/BlockArea.h +++ b/src/BlockArea.h @@ -449,7 +449,7 @@ protected: // cChunkDataCallback overrides: virtual bool Coords(int a_ChunkX, int a_ChunkZ) override; - virtual void ChunkData(const cChunkData & a_BlockTypes) override; + virtual void ChunkData(const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData) override; virtual void BlockEntity(cBlockEntity * a_BlockEntity) override; } ; diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index 3891af247..127227180 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -856,6 +856,34 @@ bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count) +bool cByteBuffer::WriteBuf(size_t a_Count, unsigned char a_Value) +{ + CHECK_THREAD + CheckValid(); + PUTBYTES(a_Count); + ASSERT(m_BufferSize >= m_ReadPos); + size_t BytesToEndOfBuffer = m_BufferSize - m_WritePos; + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + memset(m_Buffer + m_WritePos, a_Value, BytesToEndOfBuffer); + a_Count -= BytesToEndOfBuffer; + m_WritePos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + memset(m_Buffer + m_WritePos, a_Value, a_Count); + m_WritePos += a_Count; + } + return true; +} + + + + + bool cByteBuffer::ReadSome(ContiguousByteBuffer & a_String, size_t a_Count) { CHECK_THREAD diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h index f730c594e..7b589f341 100644 --- a/src/ByteBuffer.h +++ b/src/ByteBuffer.h @@ -113,6 +113,9 @@ public: /** Writes a_Count bytes into a_Buffer; returns true if successful */ bool WriteBuf(const void * a_Buffer, size_t a_Count); + /** Writes a_Count bytes into a_Buffer; returns true if successful */ + bool WriteBuf(size_t a_Count, unsigned char a_Value); + /** Reads a_Count bytes into a_String; returns true if successful */ bool ReadSome(ContiguousByteBuffer & a_String, size_t a_Count); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a3332caa..e9945fa54 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,7 +57,6 @@ target_sources( Root.cpp Scoreboard.cpp Server.cpp - SetChunkData.cpp SpawnPrepare.cpp Statistics.cpp StringCompression.cpp @@ -68,7 +67,6 @@ target_sources( World.cpp main.cpp - AllocationPool.h BiomeDef.h BlockArea.h BlockInServerPluginInterface.h diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 94bb425e1..6e1273999 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -54,20 +54,17 @@ cChunk::cChunk( int a_ChunkX, int a_ChunkZ, - cChunkMap * a_ChunkMap, cWorld * a_World, - cAllocationPool<cChunkData::sChunkSection> & a_Pool + cChunkMap * a_ChunkMap, cWorld * a_World ): m_Presence(cpInvalid), m_IsLightValid(false), m_IsDirty(false), m_IsSaving(false), - m_HasLoadFailed(false), m_StayCount(0), m_PosX(a_ChunkX), m_PosZ(a_ChunkZ), m_World(a_World), m_ChunkMap(a_ChunkMap), - m_ChunkData(a_Pool), m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()), m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()), m_RedstoneSimulatorData(a_World->GetRedstoneSimulator()->CreateChunkData()), @@ -271,7 +268,7 @@ void cChunk::MarkLoadFailed(void) MarkDirty(); // The chunk is always needed, generate it: - m_World->GetGenerator().QueueGenerateChunk({m_PosX, m_PosZ}, false); + m_World->GetGenerator().QueueGenerateChunk({ m_PosX, m_PosZ }, false); } @@ -282,12 +279,10 @@ void cChunk::GetAllData(cChunkDataCallback & a_Callback) const { ASSERT(m_Presence == cpPresent); - a_Callback.HeightMap(&m_HeightMap); - a_Callback.BiomeData(&m_BiomeMap); - a_Callback.LightIsValid(m_IsLightValid); - - a_Callback.ChunkData(m_ChunkData); + a_Callback.ChunkData(m_BlockData, m_LightData); + a_Callback.HeightMap(m_HeightMap); + a_Callback.BiomeMap(m_BiomeMap); for (const auto & Entity : m_Entities) { @@ -304,27 +299,25 @@ void cChunk::GetAllData(cChunkDataCallback & a_Callback) const -void cChunk::SetAllData(cSetChunkData & a_SetChunkData) +void cChunk::SetAllData(SetChunkData && a_SetChunkData) { - ASSERT(a_SetChunkData.IsHeightMapValid()); - ASSERT(a_SetChunkData.AreBiomesValid()); + std::copy(a_SetChunkData.HeightMap, a_SetChunkData.HeightMap + std::size(a_SetChunkData.HeightMap), m_HeightMap); + std::copy(a_SetChunkData.BiomeMap, a_SetChunkData.BiomeMap + std::size(a_SetChunkData.BiomeMap), m_BiomeMap); - memcpy(m_BiomeMap, a_SetChunkData.GetBiomes(), sizeof(m_BiomeMap)); - memcpy(m_HeightMap, a_SetChunkData.GetHeightMap(), sizeof(m_HeightMap)); - - m_ChunkData.Assign(std::move(a_SetChunkData.GetChunkData())); - m_IsLightValid = a_SetChunkData.IsLightValid(); + m_BlockData = std::move(a_SetChunkData.BlockData); + m_LightData = std::move(a_SetChunkData.LightData); + m_IsLightValid = a_SetChunkData.IsLightValid; // Entities need some extra steps to destroy, so here we're keeping the old ones. // Move the entities already in the chunk, including player entities, so that we don't lose any: - a_SetChunkData.GetEntities().insert( - a_SetChunkData.GetEntities().end(), + a_SetChunkData.Entities.insert( + a_SetChunkData.Entities.end(), std::make_move_iterator(m_Entities.begin()), std::make_move_iterator(m_Entities.end()) ); // Store the augmented result: - m_Entities = std::move(a_SetChunkData.GetEntities()); + m_Entities = std::move(a_SetChunkData.Entities); // Set all the entity variables again: for (const auto & Entity : m_Entities) @@ -335,7 +328,7 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData) } // Clear the block entities present - either the loader / saver has better, or we'll create empty ones: - m_BlockEntities = std::move(a_SetChunkData.GetBlockEntities()); + m_BlockEntities = std::move(a_SetChunkData.BlockEntities); // Check that all block entities have a valid blocktype at their respective coords (DEBUG-mode only): #ifndef NDEBUG @@ -357,15 +350,8 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData) // Set the chunk data as valid. This may be needed for some simulators that perform actions upon block adding (Vaporize) SetPresence(cpPresent); - if (a_SetChunkData.ShouldMarkDirty()) - { - MarkDirty(); - } - // Wake up all simulators for their respective blocks: WakeUpSimulators(); - - m_HasLoadFailed = false; } @@ -380,8 +366,7 @@ void cChunk::SetLight( // TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation. // Postponing until we see how bad it is :) - m_ChunkData.SetBlockLight(a_BlockLight); - m_ChunkData.SetSkyLight(a_SkyLight); + m_LightData.SetAll(a_BlockLight, a_SkyLight); MarkDirty(); m_IsLightValid = true; @@ -391,15 +376,6 @@ void cChunk::SetLight( -void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes) -{ - m_ChunkData.CopyBlockTypes(a_BlockTypes); -} - - - - - void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) { if ((a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas)) != (cBlockArea::baTypes | cBlockArea::baMetas)) @@ -1261,19 +1237,18 @@ void cChunk::WakeUpSimulators(void) auto * LavaSimulator = m_World->GetLavaSimulator(); auto * RedstoneSimulator = m_World->GetRedstoneSimulator(); - for (size_t SectionIdx = 0; SectionIdx != cChunkData::NumSections; ++SectionIdx) + for (size_t SectionIdx = 0; SectionIdx != cChunkDef::NumSections; ++SectionIdx) { - const auto * Section = m_ChunkData.GetSection(SectionIdx); + const auto * Section = m_BlockData.GetSection(SectionIdx); if (Section == nullptr) { continue; } - for (size_t BlockIdx = 0; BlockIdx != cChunkData::SectionBlockCount; ++BlockIdx) + for (size_t BlockIdx = 0; BlockIdx != ChunkBlockData::SectionBlockCount; ++BlockIdx) { - const auto BlockType = Section->m_BlockTypes[BlockIdx]; - auto Position = cChunkDef::IndexToCoordinate(BlockIdx); - Position.y += static_cast<int>(SectionIdx * cChunkData::SectionHeight); + const auto BlockType = (*Section)[BlockIdx]; + const auto Position = cChunkDef::IndexToCoordinate(BlockIdx + SectionIdx * ChunkBlockData::SectionBlockCount); RedstoneSimulator->AddBlock(*this, Position, BlockType); WaterSimulator->AddBlock(*this, Position, BlockType); @@ -1323,7 +1298,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT ASSERT(IsValid()); const BLOCKTYPE OldBlockType = GetBlock(a_RelX, a_RelY, a_RelZ); - const BLOCKTYPE OldBlockMeta = m_ChunkData.GetMeta({ a_RelX, a_RelY, a_RelZ }); + const BLOCKTYPE OldBlockMeta = m_BlockData.GetMeta({ a_RelX, a_RelY, a_RelZ }); if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta)) { return; @@ -1341,7 +1316,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT MarkDirty(); } - m_ChunkData.SetBlock({ a_RelX, a_RelY, a_RelZ }, a_BlockType); + m_BlockData.SetBlock({ a_RelX, a_RelY, a_RelZ }, a_BlockType); // Queue block to be sent only if ... if ( @@ -1357,7 +1332,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta)); } - m_ChunkData.SetMeta({ a_RelX, a_RelY, a_RelZ }, a_BlockMeta); + m_BlockData.SetMeta({ a_RelX, a_RelY, a_RelZ }, a_BlockMeta); // ONLY recalculate lighting if it's necessary! if ( @@ -2092,9 +2067,9 @@ void cChunk::GetBlockTypeMeta(Vector3i a_RelPos, BLOCKTYPE & a_BlockType, NIBBLE void cChunk::GetBlockInfo(Vector3i a_RelPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) const { a_BlockType = GetBlock(a_RelPos); - a_Meta = m_ChunkData.GetMeta(a_RelPos); - a_SkyLight = m_ChunkData.GetSkyLight(a_RelPos); - a_BlockLight = m_ChunkData.GetBlockLight(a_RelPos); + a_Meta = m_BlockData.GetMeta(a_RelPos); + a_SkyLight = m_LightData.GetSkyLight(a_RelPos); + a_BlockLight = m_LightData.GetBlockLight(a_RelPos); } diff --git a/src/Chunk.h b/src/Chunk.h index ec5407480..96f1b2cb8 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -37,7 +37,8 @@ class cBlockArea; class cFluidSimulatorData; class cMobCensus; class cMobSpawner; -class cSetChunkData; + +struct SetChunkData; typedef std::list<cClientHandle *> cClientHandleList; @@ -59,8 +60,7 @@ public: cChunk( int a_ChunkX, int a_ChunkZ, // Chunk coords - cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects - cAllocationPool<cChunkData::sChunkSection> & a_Pool + cChunkMap * a_ChunkMap, cWorld * a_World // Parent objects ); cChunk(const cChunk & Other) = delete; ~cChunk(); @@ -113,16 +113,13 @@ public: /** Sets all chunk data as either loaded from the storage or generated. BlockLight and BlockSkyLight are optional, if not present, chunk will be marked as unlighted. Modifies the BlockEntity list in a_SetChunkData - moves the block entities into the chunk. */ - void SetAllData(cSetChunkData & a_SetChunkData); + void SetAllData(SetChunkData && a_SetChunkData); void SetLight( const cChunkDef::BlockNibbles & a_BlockLight, const cChunkDef::BlockNibbles & a_SkyLight ); - /** Copies m_BlockData into a_BlockTypes, only the block types */ - void GetBlockTypes(BLOCKTYPE * a_BlockTypes); - /** Writes the specified cBlockArea at the coords specified. Note that the coords may extend beyond the chunk! */ void WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); @@ -160,8 +157,8 @@ public: FastSetBlock(a_RelPos.x, a_RelPos.y, a_RelPos.z, a_BlockType, a_BlockMeta); } - BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const { return m_ChunkData.GetBlock({ a_RelX, a_RelY, a_RelZ }); } - BLOCKTYPE GetBlock(Vector3i a_RelCoords) const { return m_ChunkData.GetBlock(a_RelCoords); } + BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const { return m_BlockData.GetBlock({ a_RelX, a_RelY, a_RelZ }); } + BLOCKTYPE GetBlock(Vector3i a_RelCoords) const { return m_BlockData.GetBlock(a_RelCoords); } void GetBlockTypeMeta(Vector3i a_RelPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const @@ -370,42 +367,36 @@ public: inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const { - return m_ChunkData.GetMeta({ a_RelX, a_RelY, a_RelZ }); + return m_BlockData.GetMeta({ a_RelX, a_RelY, a_RelZ }); } - NIBBLETYPE GetMeta(Vector3i a_RelPos) const { return m_ChunkData.GetMeta(a_RelPos); } + NIBBLETYPE GetMeta(Vector3i a_RelPos) const { return m_BlockData.GetMeta(a_RelPos); } void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { SetMeta({ a_RelX, a_RelY, a_RelZ }, a_Meta); } - /** Set a meta value, with the option of not informing the client and / or not marking dirty. - Used for setting metas that are of little value for saving to disk and / or for sending to the client, - such as leaf decay flags. */ inline void SetMeta(Vector3i a_RelPos, NIBBLETYPE a_Meta) { - bool hasChanged = m_ChunkData.SetMeta(a_RelPos, a_Meta); - if (hasChanged) - { - MarkDirty(); - m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelPos.x, a_RelPos.y, a_RelPos.z, GetBlock(a_RelPos), a_Meta)); - } + m_BlockData.SetMeta(a_RelPos, a_Meta); + MarkDirty(); + m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelPos.x, a_RelPos.y, a_RelPos.z, GetBlock(a_RelPos), a_Meta)); } /** Light alterations based on time */ NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const; /** Get the level of artificial light illuminating the block (0 - 15) */ - inline NIBBLETYPE GetBlockLight(Vector3i a_RelPos) const { return m_ChunkData.GetBlockLight(a_RelPos); } - inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const { return m_ChunkData.GetBlockLight({ a_RelX, a_RelY, a_RelZ }); } + inline NIBBLETYPE GetBlockLight(Vector3i a_RelPos) const { return m_LightData.GetBlockLight(a_RelPos); } + inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const { return m_LightData.GetBlockLight({ a_RelX, a_RelY, a_RelZ }); } /** Get the level of sky light illuminating the block (0 - 15) independent of daytime. */ - inline NIBBLETYPE GetSkyLight(Vector3i a_RelPos) const { return m_ChunkData.GetSkyLight(a_RelPos); } - inline NIBBLETYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) const { return m_ChunkData.GetSkyLight({ a_RelX, a_RelY, a_RelZ }); } + inline NIBBLETYPE GetSkyLight(Vector3i a_RelPos) const { return m_LightData.GetSkyLight(a_RelPos); } + inline NIBBLETYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) const { return m_LightData.GetSkyLight({ a_RelX, a_RelY, a_RelZ }); } /** Get the level of sky light illuminating the block (0 - 15), taking daytime into a account. */ - inline NIBBLETYPE GetSkyLightAltered(Vector3i a_RelPos) const { return GetTimeAlteredLight(m_ChunkData.GetSkyLight(a_RelPos)); } + inline NIBBLETYPE GetSkyLightAltered(Vector3i a_RelPos) const { return GetTimeAlteredLight(m_LightData.GetSkyLight(a_RelPos)); } inline NIBBLETYPE GetSkyLightAltered(int a_RelX, int a_RelY, int a_RelZ) const { return GetSkyLightAltered({ a_RelX, a_RelY, a_RelZ }); } /** Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) @@ -578,7 +569,6 @@ private: bool m_IsLightValid; // True if the blocklight and skylight are calculated bool m_IsDirty; // True if the chunk has changed since it was last saved bool m_IsSaving; // True if the chunk is being saved - bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then std::queue<Vector3i> m_ToTickBlocks; sSetBlockVector m_PendingSendBlocks; ///< Blocks that have changed and need to be sent to all clients @@ -595,7 +585,8 @@ private: cWorld * m_World; cChunkMap * m_ChunkMap; - cChunkData m_ChunkData; + ChunkBlockData m_BlockData; + ChunkLightData m_LightData; cChunkDef::HeightMap m_HeightMap; cChunkDef::BiomeMap m_BiomeMap; diff --git a/src/ChunkData.cpp b/src/ChunkData.cpp index a480c2c66..a820f043b 100644 --- a/src/ChunkData.cpp +++ b/src/ChunkData.cpp @@ -13,437 +13,121 @@ namespace { - /** Returns true if all a_Array's elements between [0] and [a_NumElements - 1] are equal to a_Value. */ - template <typename T> - bool IsAllValue(const T * a_Array, size_t a_NumElements, T a_Value) + struct SectionIndices { - for (size_t i = 0; i < a_NumElements; i++) - { - if (a_Array[i] != a_Value) - { - return false; - } - } - return true; - } - - - - - - struct sSectionIndices - { - int Section = 0; // Index into m_Sections - int Index = 0; // Index into a single sChunkSection + size_t Section = 0; // Index into m_Sections + size_t Index = 0; // Index into a single sChunkSection }; - sSectionIndices IndicesFromRelPos(Vector3i a_RelPos) + inline SectionIndices IndicesFromRelPos(const Vector3i a_RelPos) { ASSERT(cChunkDef::IsValidRelPos(a_RelPos)); - sSectionIndices Ret; - Ret.Section = a_RelPos.y / cChunkData::SectionHeight; - Ret.Index = cChunkDef::MakeIndexNoCheck(a_RelPos.x, a_RelPos.y % cChunkData::SectionHeight, a_RelPos.z); - return Ret; - } -} // namespace (anonymous) - - - - - -cChunkData::cChunkData(cAllocationPool<cChunkData::sChunkSection> & a_Pool): - m_Sections(), - m_Pool(a_Pool) -{ -} - - - - - -cChunkData::cChunkData(cChunkData && a_Other): - m_Pool(a_Other.m_Pool) -{ - for (size_t i = 0; i < NumSections; i++) - { - m_Sections[i] = a_Other.m_Sections[i]; - a_Other.m_Sections[i] = nullptr; - } -} - - - - - -cChunkData::~cChunkData() -{ - Clear(); -} - - - - -void cChunkData::Assign(const cChunkData & a_Other) -{ - // If assigning to self, no-op - if (&a_Other == this) - { - return; - } - - Clear(); - for (size_t i = 0; i < NumSections; ++i) - { - if (a_Other.m_Sections[i] != nullptr) + return { - m_Sections[i] = Allocate(); - *m_Sections[i] = *a_Other.m_Sections[i]; - } - } -} - - - - - -void cChunkData::Assign(cChunkData && a_Other) -{ - if (&a_Other == this) - { - return; - } - - if (m_Pool != a_Other.m_Pool) - { - // Cannot transfer the memory, do a copy instead - const cChunkData & CopyOther = a_Other; - Assign(CopyOther); - return; - } - - Clear(); - for (size_t i = 0; i < NumSections; i++) - { - m_Sections[i] = a_Other.m_Sections[i]; - a_Other.m_Sections[i] = nullptr; - } -} - - - - - -BLOCKTYPE cChunkData::GetBlock(Vector3i a_RelPos) const -{ - if (!cChunkDef::IsValidRelPos(a_RelPos)) - { - return E_BLOCK_AIR; // Coordinates are outside outside the world, so this must be an air block + static_cast<size_t>(a_RelPos.y / cChunkDef::SectionHeight), + static_cast<size_t>(cChunkDef::MakeIndexNoCheck(a_RelPos.x, a_RelPos.y % cChunkDef::SectionHeight, a_RelPos.z)) + }; } - auto Idxs = IndicesFromRelPos(a_RelPos); - if (m_Sections[Idxs.Section] != nullptr) - { - return m_Sections[Idxs.Section]->m_BlockTypes[Idxs.Index]; - } - else - { - return 0; - } -} - - - - -void cChunkData::SetBlock(Vector3i a_RelPos, BLOCKTYPE a_Block) -{ - if (!cChunkDef::IsValidRelPos(a_RelPos)) + bool IsCompressed(const size_t ElementCount) { - ASSERT(!"cChunkData::SetMeta(): index out of range!"); - return; + return ElementCount != ChunkBlockData::SectionBlockCount; } - auto Idxs = IndicesFromRelPos(a_RelPos); - if (m_Sections[Idxs.Section] == nullptr) + template <size_t ElementCount, typename ValueType> + ValueType UnpackDefaultValue(const ValueType DefaultValue) { - if (a_Block == 0x00) - { - return; - } - m_Sections[Idxs.Section] = Allocate(); - if (m_Sections[Idxs.Section] == nullptr) + if (IsCompressed(ElementCount)) { - ASSERT(!"Failed to allocate a new section in Chunkbuffer"); - return; + return DefaultValue & 0xF; } - ZeroSection(m_Sections[Idxs.Section]); - } - m_Sections[Idxs.Section]->m_BlockTypes[Idxs.Index] = a_Block; -} - - - - -NIBBLETYPE cChunkData::GetMeta(Vector3i a_RelPos) const -{ - if (cChunkDef::IsValidRelPos(a_RelPos)) - { - auto Idxs = IndicesFromRelPos(a_RelPos); - if (m_Sections[Idxs.Section] != nullptr) - { - return (m_Sections[Idxs.Section]->m_BlockMetas[Idxs.Index / 2] >> ((Idxs.Index & 1) * 4)) & 0x0f; - } - else - { - return 0; - } + return DefaultValue; } - // Coordinates are outside outside the world, so it must be an air block with a blank meta - return 0; -} +} // namespace (anonymous) -bool cChunkData::SetMeta(Vector3i a_RelPos, NIBBLETYPE a_Nibble) +template<class ElementType, size_t ElementCount, ElementType DefaultValue> +void ChunkDataStore<ElementType, ElementCount, DefaultValue>::Assign(const ChunkDataStore<ElementType, ElementCount, DefaultValue> & a_Other) { - if (!cChunkDef::IsValidRelPos(a_RelPos)) + for (size_t Y = 0; Y != cChunkDef::NumSections; Y++) { - ASSERT(!"cChunkData::SetMeta(): index out of range!"); - return false; - } + Store[Y].reset(); - auto Idxs = IndicesFromRelPos(a_RelPos); - if (m_Sections[Idxs.Section] == nullptr) - { - if ((a_Nibble & 0xf) == 0x00) - { - return false; - } - m_Sections[Idxs.Section] = Allocate(); - if (m_Sections[Idxs.Section] == nullptr) + if (const auto & Other = a_Other.Store[Y]; Other != nullptr) { - ASSERT(!"Failed to allocate a new section in Chunkbuffer"); - return false; + Store[Y] = std::make_unique<Type>(*Other); } - ZeroSection(m_Sections[Idxs.Section]); } - NIBBLETYPE oldval = m_Sections[Idxs.Section]->m_BlockMetas[Idxs.Index / 2] >> ((Idxs.Index & 1) * 4) & 0xf; - m_Sections[Idxs.Section]->m_BlockMetas[Idxs.Index / 2] = static_cast<NIBBLETYPE>( - (m_Sections[Idxs.Section]->m_BlockMetas[Idxs.Index / 2] & (0xf0 >> ((Idxs.Index & 1) * 4))) | // The untouched nibble - ((a_Nibble & 0x0f) << ((Idxs.Index & 1) * 4)) // The nibble being set - ); - return oldval != a_Nibble; } -NIBBLETYPE cChunkData::GetBlockLight(Vector3i a_RelPos) const +template<class ElementType, size_t ElementCount, ElementType DefaultValue> +ElementType ChunkDataStore<ElementType, ElementCount, DefaultValue>::Get(const Vector3i a_Position) const { - if (cChunkDef::IsValidRelPos(a_RelPos)) - { - auto Idxs = IndicesFromRelPos(a_RelPos); - if (m_Sections[Idxs.Section] != nullptr) - { - return (m_Sections[Idxs.Section]->m_BlockLight[Idxs.Index / 2] >> ((Idxs.Index & 1) * 4)) & 0x0f; - } - else - { - return 0; - } - } - ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!"); - return 0; -} - + const auto Indices = IndicesFromRelPos(a_Position); + const auto & Section = Store[Indices.Section]; - - - -NIBBLETYPE cChunkData::GetSkyLight(Vector3i a_RelPos) const -{ - if (cChunkDef::IsValidRelPos(a_RelPos)) + if (Section != nullptr) { - auto Idxs = IndicesFromRelPos(a_RelPos); - if (m_Sections[Idxs.Section] != nullptr) + if (IsCompressed(ElementCount)) { - return (m_Sections[Idxs.Section]->m_BlockSkyLight[Idxs.Index / 2] >> ((Idxs.Index & 1) * 4)) & 0x0f; + return cChunkDef::ExpandNibble(Section->data(), Indices.Index); } else { - return 0xF; + return (*Section)[Indices.Index]; } } - ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!"); - return 0; -} - - - - - -const cChunkData::sChunkSection * cChunkData::GetSection(size_t a_SectionNum) const -{ - if (a_SectionNum < NumSections) - { - return m_Sections[a_SectionNum]; - } - ASSERT(!"cChunkData::GetSection: section index out of range"); - return nullptr; -} - - - - - -UInt16 cChunkData::GetSectionBitmask() const -{ - static_assert(NumSections <= 16U, "cChunkData::GetSectionBitmask needs a bigger data type"); - UInt16 Res = 0U; - for (size_t i = 0U; i < NumSections; ++i) - { - Res |= ((m_Sections[i] != nullptr) << i); - } - return Res; -} - - - - -void cChunkData::Clear() -{ - for (size_t i = 0; i < NumSections; ++i) - { - if (m_Sections[i] != nullptr) - { - Free(m_Sections[i]); - m_Sections[i] = nullptr; - } - } + return UnpackDefaultValue<ElementCount>(DefaultValue); } -void cChunkData::CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx, size_t a_Length) const +template<class ElementType, size_t ElementCount, ElementType DefaultValue> +typename ChunkDataStore<ElementType, ElementCount, DefaultValue>::Type * ChunkDataStore<ElementType, ElementCount, DefaultValue>::GetSection(const size_t a_Y) const { - size_t ToSkip = a_Idx; - - for (size_t i = 0; i < NumSections; i++) - { - size_t StartPos = 0; - if (ToSkip > 0) - { - StartPos = std::min(ToSkip, +SectionBlockCount); - ToSkip -= StartPos; - } - if (StartPos < SectionBlockCount) - { - size_t ToCopy = std::min(+SectionBlockCount - StartPos, a_Length); - a_Length -= ToCopy; - if (m_Sections[i] != nullptr) - { - BLOCKTYPE * blockbuffer = m_Sections[i]->m_BlockTypes; - memcpy(&a_Dest[(i * SectionBlockCount) + StartPos - a_Idx], blockbuffer + StartPos, sizeof(BLOCKTYPE) * ToCopy); - } - else - { - memset(&a_Dest[(i * SectionBlockCount) + StartPos - a_Idx], 0, sizeof(BLOCKTYPE) * ToCopy); - } - } - } + return Store[a_Y].get(); } -void cChunkData::CopyMetas(NIBBLETYPE * a_Dest) const +template<class ElementType, size_t ElementCount, ElementType DefaultValue> +void ChunkDataStore<ElementType, ElementCount, DefaultValue>::Set(const Vector3i a_Position, const ElementType a_Value) { - for (size_t i = 0; i < NumSections; i++) - { - if (m_Sections[i] != nullptr) - { - memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockMetas, sizeof(m_Sections[i]->m_BlockMetas)); - } - else - { - memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockMetas)); - } - } -} - - - - + const auto Indices = IndicesFromRelPos(a_Position); + auto & Section = Store[Indices.Section]; -void cChunkData::CopyBlockLight(NIBBLETYPE * a_Dest) const -{ - for (size_t i = 0; i < NumSections; i++) + if (Section == nullptr) { - if (m_Sections[i] != nullptr) - { - memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockLight, sizeof(m_Sections[i]->m_BlockLight)); - } - else + if (a_Value == UnpackDefaultValue<ElementCount>(DefaultValue)) { - memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockLight)); + return; } - } -} - - - - -void cChunkData::CopySkyLight(NIBBLETYPE * a_Dest) const -{ - for (size_t i = 0; i < NumSections; i++) - { - if (m_Sections[i] != nullptr) - { - memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockSkyLight, sizeof(m_Sections[i]->m_BlockSkyLight)); - } - else - { - memset(&a_Dest[i * SectionBlockCount / 2], 0xff, sizeof(m_Sections[i]->m_BlockSkyLight)); - } + Section = cpp20::make_unique_for_overwrite<Type>(); + std::fill(Section->begin(), Section->end(), DefaultValue); } -} - - - - -void cChunkData::FillBlockTypes(BLOCKTYPE a_Value) -{ - // If needed, allocate any missing sections - if (a_Value != 0x00) + if (IsCompressed(ElementCount)) { - for (auto & Section : m_Sections) - { - if (Section == nullptr) - { - Section = Allocate(); - std::fill(std::begin(Section->m_BlockMetas), std::end(Section->m_BlockMetas), 0x00); - std::fill(std::begin(Section->m_BlockLight), std::end(Section->m_BlockLight), 0x00); - std::fill(std::begin(Section->m_BlockSkyLight), std::end(Section->m_BlockSkyLight), 0xff); - } - } + cChunkDef::PackNibble(Section->data(), Indices.Index, a_Value); } - - for (auto Section : m_Sections) + else { - if (Section != nullptr) - { - std::fill(std::begin(Section->m_BlockTypes), std::end(Section->m_BlockTypes), a_Value); - } + (*Section)[Indices.Index] = a_Value; } } @@ -451,61 +135,20 @@ void cChunkData::FillBlockTypes(BLOCKTYPE a_Value) -void cChunkData::FillMetas(NIBBLETYPE a_Value) +template<class ElementType, size_t ElementCount, ElementType DefaultValue> +void ChunkDataStore<ElementType, ElementCount, DefaultValue>::SetSection(const ElementType (& a_Source)[ElementCount], const size_t a_Y) { - // If needed, allocate any missing sections - if (a_Value != 0x00) - { - for (auto & Section : m_Sections) - { - if (Section == nullptr) - { - Section = Allocate(); - std::fill(std::begin(Section->m_BlockTypes), std::end(Section->m_BlockTypes), 0x00); - std::fill(std::begin(Section->m_BlockLight), std::end(Section->m_BlockLight), 0x00); - std::fill(std::begin(Section->m_BlockSkyLight), std::end(Section->m_BlockSkyLight), 0xff); - } - } - } - - NIBBLETYPE NewMeta = static_cast<NIBBLETYPE>((a_Value << 4) | a_Value); - for (auto Section : m_Sections) - { - if (Section != nullptr) - { - std::fill(std::begin(Section->m_BlockMetas), std::end(Section->m_BlockMetas), NewMeta); - } - } -} - - + auto & Section = Store[a_Y]; + const auto SourceEnd = std::end(a_Source); - - -void cChunkData::FillBlockLight(NIBBLETYPE a_Value) -{ - // If needed, allocate any missing sections - if (a_Value != 0x00) + if (Section != nullptr) { - for (auto & Section : m_Sections) - { - if (Section == nullptr) - { - Section = Allocate(); - std::fill(std::begin(Section->m_BlockTypes), std::end(Section->m_BlockTypes), 0x00); - std::fill(std::begin(Section->m_BlockMetas), std::end(Section->m_BlockMetas), 0x00); - std::fill(std::begin(Section->m_BlockSkyLight), std::end(Section->m_BlockSkyLight), 0xff); - } - } + std::copy(a_Source, SourceEnd, Section->begin()); } - - NIBBLETYPE NewLight = static_cast<NIBBLETYPE>((a_Value << 4) | a_Value); - for (auto Section : m_Sections) + else if (std::any_of(a_Source, SourceEnd, [](const auto Value) { return Value != DefaultValue; })) { - if (Section != nullptr) - { - std::fill(std::begin(Section->m_BlockLight), std::end(Section->m_BlockLight), NewLight); - } + Section = cpp20::make_unique_for_overwrite<Type>(); + std::copy(a_Source, SourceEnd, Section->begin()); } } @@ -513,30 +156,12 @@ void cChunkData::FillBlockLight(NIBBLETYPE a_Value) -void cChunkData::FillSkyLight(NIBBLETYPE a_Value) +template<class ElementType, size_t ElementCount, ElementType DefaultValue> +void ChunkDataStore<ElementType, ElementCount, DefaultValue>::SetAll(const ElementType (& a_Source)[cChunkDef::NumSections * ElementCount]) { - // If needed, allocate any missing sections - if (a_Value != 0x0f) + for (size_t Y = 0; Y != cChunkDef::NumSections; Y++) { - for (auto & Section : m_Sections) - { - if (Section == nullptr) - { - Section = Allocate(); - std::fill(std::begin(Section->m_BlockTypes), std::end(Section->m_BlockTypes), 0x00); - std::fill(std::begin(Section->m_BlockMetas), std::end(Section->m_BlockMetas), 0x00); - std::fill(std::begin(Section->m_BlockLight), std::end(Section->m_BlockLight), 0x00); - } - } - } - - NIBBLETYPE NewSkyLight = static_cast<NIBBLETYPE>((a_Value << 4) | a_Value); - for (auto Section : m_Sections) - { - if (Section != nullptr) - { - std::fill(std::begin(Section->m_BlockSkyLight), std::end(Section->m_BlockSkyLight), NewSkyLight); - } + SetSection(*reinterpret_cast<const ElementType (*)[ElementCount]>(a_Source + Y * ElementCount), Y); } } @@ -544,187 +169,66 @@ void cChunkData::FillSkyLight(NIBBLETYPE a_Value) -void cChunkData::SetBlockTypes(const BLOCKTYPE * a_Src) -{ - ASSERT(a_Src != nullptr); - - for (size_t i = 0; i < NumSections; i++) - { - // If the section is already allocated, copy the data into it: - if (m_Sections[i] != nullptr) - { - memcpy(m_Sections[i]->m_BlockTypes, &a_Src[i * SectionBlockCount], sizeof(m_Sections[i]->m_BlockTypes)); - continue; - } - - // The section doesn't exist, find out if it is needed: - if (IsAllValue(a_Src + i * SectionBlockCount, SectionBlockCount, static_cast<BLOCKTYPE>(0))) - { - // No need for the section, the data is all-air - continue; - } - - // Allocate the section and copy the data into it: - m_Sections[i] = Allocate(); - memcpy(m_Sections[i]->m_BlockTypes, &a_Src[i * SectionBlockCount], sizeof(m_Sections[i]->m_BlockTypes)); - memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas)); - memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight)); - memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight)); - } // for i - m_Sections[] -} - - - - - -void cChunkData::SetMetas(const NIBBLETYPE * a_Src) +void ChunkBlockData::Assign(const ChunkBlockData & a_Other) { - ASSERT(a_Src != nullptr); - - for (size_t i = 0; i < NumSections; i++) - { - // If the section is already allocated, copy the data into it: - if (m_Sections[i] != nullptr) - { - memcpy(m_Sections[i]->m_BlockMetas, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockMetas)); - continue; - } - - // The section doesn't exist, find out if it is needed: - if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, static_cast<NIBBLETYPE>(0))) - { - // No need for the section, the data is all zeroes - continue; - } - - // Allocate the section and copy the data into it: - m_Sections[i] = Allocate(); - memcpy(m_Sections[i]->m_BlockMetas, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockMetas)); - memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes)); - memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight)); - memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight)); - } // for i - m_Sections[] + m_Blocks.Assign(a_Other.m_Blocks); + m_Metas.Assign(a_Other.m_Metas); } -void cChunkData::SetBlockLight(const NIBBLETYPE * a_Src) +void ChunkBlockData::SetAll(const cChunkDef::BlockTypes & a_BlockSource, const cChunkDef::BlockNibbles & a_MetaSource) { - if (a_Src == nullptr) - { - return; - } - - for (size_t i = 0; i < NumSections; i++) - { - // If the section is already allocated, copy the data into it: - if (m_Sections[i] != nullptr) - { - memcpy(m_Sections[i]->m_BlockLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockLight)); - continue; - } - - // The section doesn't exist, find out if it is needed: - if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, static_cast<NIBBLETYPE>(0))) - { - // No need for the section, the data is all zeroes - continue; - } - - // Allocate the section and copy the data into it: - m_Sections[i] = Allocate(); - memcpy(m_Sections[i]->m_BlockLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockLight)); - memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes)); - memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas)); - memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight)); - } // for i - m_Sections[] + m_Blocks.SetAll(a_BlockSource); + m_Metas.SetAll(a_MetaSource); } -void cChunkData::SetSkyLight(const NIBBLETYPE * a_Src) +void ChunkBlockData::SetSection(const SectionType & a_BlockSource, const SectionMetaType & a_MetaSource, const size_t a_Y) { - if (a_Src == nullptr) - { - return; - } - - for (size_t i = 0; i < NumSections; i++) - { - // If the section is already allocated, copy the data into it: - if (m_Sections[i] != nullptr) - { - memcpy(m_Sections[i]->m_BlockSkyLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockSkyLight)); - continue; - } - - // The section doesn't exist, find out if it is needed: - if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, static_cast<NIBBLETYPE>(0xff))) - { - // No need for the section, the data is all zeroes - continue; - } - - // Allocate the section and copy the data into it: - m_Sections[i] = Allocate(); - memcpy(m_Sections[i]->m_BlockSkyLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockSkyLight)); - memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes)); - memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas)); - memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight)); - } // for i - m_Sections[] + m_Blocks.SetSection(a_BlockSource, a_Y); + m_Metas.SetSection(a_MetaSource, a_Y); } -UInt32 cChunkData::NumPresentSections() const +void ChunkLightData::Assign(const ChunkLightData & a_Other) { - UInt32 Ret = 0U; - for (size_t i = 0; i < NumSections; i++) - { - if (m_Sections[i] != nullptr) - { - ++Ret; - } - } - return Ret; + m_BlockLights.Assign(a_Other.m_BlockLights); + m_SkyLights.Assign(a_Other.m_SkyLights); } -cChunkData::sChunkSection * cChunkData::Allocate(void) +void ChunkLightData::SetAll(const cChunkDef::BlockNibbles & a_BlockLightSource, const cChunkDef::BlockNibbles & a_SkyLightSource) { - return m_Pool.Allocate(); + m_BlockLights.SetAll(a_BlockLightSource); + m_SkyLights.SetAll(a_SkyLightSource); } -void cChunkData::Free(cChunkData::sChunkSection * a_Section) +void ChunkLightData::SetSection(const SectionType & a_BlockLightSource, const SectionType & a_SkyLightSource, const size_t a_Y) { - m_Pool.Free(a_Section); + m_BlockLights.SetSection(a_BlockLightSource, a_Y); + m_SkyLights.SetSection(a_SkyLightSource, a_Y); } -void cChunkData::ZeroSection(cChunkData::sChunkSection * a_Section) const -{ - memset(a_Section->m_BlockTypes, 0x00, sizeof(a_Section->m_BlockTypes)); - memset(a_Section->m_BlockMetas, 0x00, sizeof(a_Section->m_BlockMetas)); - memset(a_Section->m_BlockLight, 0x00, sizeof(a_Section->m_BlockLight)); - memset(a_Section->m_BlockSkyLight, 0xff, sizeof(a_Section->m_BlockSkyLight)); -} - - - - +template struct ChunkDataStore<BLOCKTYPE, ChunkBlockData::SectionBlockCount, ChunkBlockData::DefaultValue>; +template struct ChunkDataStore<NIBBLETYPE, ChunkBlockData::SectionMetaCount, ChunkLightData::DefaultBlockLightValue>; +template struct ChunkDataStore<NIBBLETYPE, ChunkLightData::SectionLightCount, ChunkLightData::DefaultSkyLightValue>; diff --git a/src/ChunkData.h b/src/ChunkData.h index 3b4c9c867..a984b752e 100644 --- a/src/ChunkData.h +++ b/src/ChunkData.h @@ -9,132 +9,159 @@ #pragma once +#include "FunctionRef.h" +#include "ChunkDef.h" -#include <cstring> -#include "AllocationPool.h" -#include "ChunkDef.h" -class cChunkData + +template <class ElementType, size_t ElementCount, ElementType DefaultValue> +struct ChunkDataStore { -public: + using Type = std::array<ElementType, ElementCount>; - static const int SectionHeight = 16; - static const size_t NumSections = (cChunkDef::Height / SectionHeight); - static const size_t SectionBlockCount = SectionHeight * cChunkDef::Width * cChunkDef::Width; + /** Copy assign from another ChunkDataStore. */ + void Assign(const ChunkDataStore<ElementType, ElementCount, DefaultValue> & a_Other); - struct sChunkSection - { - BLOCKTYPE m_BlockTypes[SectionBlockCount]; - NIBBLETYPE m_BlockMetas[SectionBlockCount / 2]; - NIBBLETYPE m_BlockLight[SectionBlockCount / 2]; - NIBBLETYPE m_BlockSkyLight[SectionBlockCount / 2]; - }; + /** Gets one value at the given position. + Returns DefaultValue if the section is not allocated. */ + ElementType Get(Vector3i a_Position) const; - cChunkData(cAllocationPool<sChunkSection> & a_Pool); - cChunkData(cChunkData && a_Other); - ~cChunkData(); + /** Returns a raw pointer to the internal representation of the specified section. + Will be nullptr if the section is not allocated. */ + Type * GetSection(size_t a_Y) const; - cChunkData & operator = (cChunkData && a_Other) - { - Assign(std::move(a_Other)); - return *this; - } + /** Sets one value at the given position. + Allocates a section if needed for the operation. */ + void Set(Vector3i a_Position, ElementType a_Value); + + /** Copies the data from the specified flat section array into the internal representation. + Allocates a section if needed for the operation. */ + void SetSection(const ElementType (& a_Source)[ElementCount], size_t a_Y); - /** Copy assign from another cChunkData */ - void Assign(const cChunkData & a_Other); + /** Copies the data from the specified flat array into the internal representation. + Allocates sections that are needed for the operation. */ + void SetAll(const ElementType (& a_Source)[cChunkDef::NumSections * ElementCount]); - /** Move assign from another cChunkData */ - void Assign(cChunkData && a_Other); + /** Contains all the sections this ChunkDataStore manages. */ + std::unique_ptr<Type> Store[cChunkDef::NumSections]; +}; + + + + + +class ChunkBlockData +{ +public: - BLOCKTYPE GetBlock(Vector3i a_RelPos) const; - void SetBlock(Vector3i a_RelPos, BLOCKTYPE a_Block); + static constexpr size_t SectionBlockCount = cChunkDef::SectionHeight * cChunkDef::Width * cChunkDef::Width; + static constexpr size_t SectionMetaCount = SectionBlockCount / 2; - NIBBLETYPE GetMeta(Vector3i a_RelPos) const; - bool SetMeta(Vector3i a_RelPos, NIBBLETYPE a_Nibble); + static constexpr BLOCKTYPE DefaultValue = 0x00; + static constexpr NIBBLETYPE DefaultMetaValue = 0x00; - NIBBLETYPE GetBlockLight(Vector3i a_RelPos) const; + using SectionType = BLOCKTYPE[SectionBlockCount]; + using SectionMetaType = NIBBLETYPE[SectionMetaCount]; - NIBBLETYPE GetSkyLight(Vector3i a_RelPos) const; +private: - /** Return a pointer to the chunk section or nullptr if all air */ - const sChunkSection * GetSection(size_t a_SectionNum) const; + ChunkDataStore<BLOCKTYPE, SectionBlockCount, DefaultValue> m_Blocks; + ChunkDataStore<NIBBLETYPE, SectionMetaCount, DefaultMetaValue> m_Metas; - /** Returns a bitmask of chunk sections which are currently stored. */ - UInt16 GetSectionBitmask() const; +public: - /** Clears all data */ - void Clear(); + using BlockArray = decltype(m_Blocks)::Type; + using MetaArray = decltype(m_Metas)::Type; - /** Copies the blocktype data into the specified flat array. - Optionally, only a part of the data is copied, as specified by the a_Idx and a_Length parameters. */ - void CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx = 0, size_t a_Length = cChunkDef::NumBlocks) const; + void Assign(const ChunkBlockData & a_Other); - /** Copies the metadata into the specified flat array. */ - void CopyMetas(NIBBLETYPE * a_Dest) const; + BLOCKTYPE GetBlock(Vector3i a_Position) const { return m_Blocks.Get(a_Position); } + NIBBLETYPE GetMeta(Vector3i a_Position) const { return m_Metas.Get(a_Position); } - /** Copies the block light data into the specified flat array. */ - void CopyBlockLight(NIBBLETYPE * a_Dest) const; + BlockArray * GetSection(size_t a_Y) const { return m_Blocks.GetSection(a_Y); } + MetaArray * GetMetaSection(size_t a_Y) const { return m_Metas.GetSection(a_Y); } - /** Copies the skylight data into the specified flat array. */ - void CopySkyLight (NIBBLETYPE * a_Dest) const; + void SetBlock(Vector3i a_Position, BLOCKTYPE a_Block) { m_Blocks.Set(a_Position, a_Block); } + void SetMeta(Vector3i a_Position, NIBBLETYPE a_Meta) { m_Metas.Set(a_Position, a_Meta); } - /** Fills the chunk with the specified block. */ - void FillBlockTypes(BLOCKTYPE a_Value); + void SetAll(const cChunkDef::BlockTypes & a_BlockSource, const cChunkDef::BlockNibbles & a_MetaSource); + void SetSection(const SectionType & a_BlockSource, const SectionMetaType & a_MetaSource, size_t a_Y); +}; - /** Fills the chunk with the specified meta value. */ - void FillMetas (NIBBLETYPE a_Value); - /** Fills the chunk with the specified block light. */ - void FillBlockLight(NIBBLETYPE a_Value); - /** Fills the chunk with the specified sky light. */ - void FillSkyLight (NIBBLETYPE a_Value); - /** Copies the blocktype data from the specified flat array into the internal representation. - Allocates sections that are needed for the operation. - Requires that a_Src is a valid pointer. */ - void SetBlockTypes(const BLOCKTYPE * a_Src); - /** Copies the metadata from the specified flat array into the internal representation. - Allocates sectios that are needed for the operation. - Requires that a_Src is a valid pointer. */ - void SetMetas(const NIBBLETYPE * a_Src); +class ChunkLightData +{ +public: - /** Copies the blocklight data from the specified flat array into the internal representation. - Allocates sectios that are needed for the operation. - Allows a_Src to be nullptr, in which case it doesn't do anything. */ - void SetBlockLight(const NIBBLETYPE * a_Src); + static constexpr size_t SectionLightCount = (cChunkDef::SectionHeight * cChunkDef::Width * cChunkDef::Width) / 2; - /** Copies the skylight data from the specified flat array into the internal representation. - Allocates sectios that are needed for the operation. - Allows a_Src to be nullptr, in which case it doesn't do anything. */ - void SetSkyLight(const NIBBLETYPE * a_Src); + static constexpr NIBBLETYPE DefaultBlockLightValue = 0x00; + static constexpr NIBBLETYPE DefaultSkyLightValue = 0xFF; - /** Returns the number of sections present (i.e. non-air). */ - UInt32 NumPresentSections() const; + using SectionType = NIBBLETYPE[SectionLightCount]; private: - sChunkSection * m_Sections[NumSections]; + ChunkDataStore<NIBBLETYPE, SectionLightCount, DefaultBlockLightValue> m_BlockLights; + ChunkDataStore<NIBBLETYPE, SectionLightCount, DefaultSkyLightValue> m_SkyLights; + +public: - cAllocationPool<sChunkSection> & m_Pool; + using LightArray = decltype(m_BlockLights)::Type; - /** Allocates a new section. Entry-point to custom allocators. */ - sChunkSection * Allocate(void); + void Assign(const ChunkLightData & a_Other); - /** Frees the specified section, previously allocated using Allocate(). - Note that a_Section may be nullptr. */ - void Free(sChunkSection * a_Section); + NIBBLETYPE GetBlockLight(Vector3i a_Position) const { return m_BlockLights.Get(a_Position); } + NIBBLETYPE GetSkyLight(Vector3i a_Position) const { return m_SkyLights.Get(a_Position); } - /** Sets the data in the specified section to their default values. */ - void ZeroSection(sChunkSection * a_Section) const; + LightArray * GetBlockLightSection(size_t a_Y) const { return m_BlockLights.GetSection(a_Y); } + LightArray * GetSkyLightSection(size_t a_Y) const { return m_SkyLights.GetSection(a_Y); } + void SetAll(const cChunkDef::BlockNibbles & a_BlockLightSource, const cChunkDef::BlockNibbles & a_SkyLightSource); + void SetSection(const SectionType & a_BlockLightSource, const SectionType & a_SkyLightSource, size_t a_Y); }; +namespace ChunkDef +{ + using cForEachSectionCallback = cFunctionRef<void( + size_t, + ChunkBlockData::BlockArray *, + ChunkBlockData::MetaArray *, + ChunkLightData::LightArray *, + ChunkLightData::LightArray *)>; + + /** Invokes the callback functor for every chunk section containing at least one present block or light section data. + This is used to collect all data for all sections. */ + inline void ForEachSection(const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, cForEachSectionCallback a_Callback) + { + for (size_t Y = 0; Y < cChunkDef::NumSections; ++Y) + { + const auto Blocks = a_BlockData.GetSection(Y); + const auto Metas = a_BlockData.GetMetaSection(Y); + const auto BlockLights = a_LightData.GetBlockLightSection(Y); + const auto SkyLights = a_LightData.GetSkyLightSection(Y); + + if ((Blocks != nullptr) || (Metas != nullptr) || (BlockLights != nullptr) || (SkyLights != nullptr)) + { + a_Callback(Y, Blocks, Metas, BlockLights, SkyLights); + } + } + } +} + + + + +extern template struct ChunkDataStore<BLOCKTYPE, ChunkBlockData::SectionBlockCount, ChunkBlockData::DefaultValue>; +extern template struct ChunkDataStore<NIBBLETYPE, ChunkBlockData::SectionMetaCount, ChunkLightData::DefaultBlockLightValue>; +extern template struct ChunkDataStore<NIBBLETYPE, ChunkLightData::SectionLightCount, ChunkLightData::DefaultSkyLightValue>; diff --git a/src/ChunkDataCallback.h b/src/ChunkDataCallback.h index 0f120bfb1..49b3f1e2b 100644 --- a/src/ChunkDataCallback.h +++ b/src/ChunkDataCallback.h @@ -29,17 +29,17 @@ public: If false is returned, the chunk is skipped. */ virtual bool Coords(int a_ChunkX, int a_ChunkZ) { UNUSED(a_ChunkX); UNUSED(a_ChunkZ); return true; } - /** Called once to provide heightmap data */ - virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) { UNUSED(a_HeightMap); } - - /** Called once to provide biome data */ - virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) { UNUSED(a_BiomeMap); } - /** Called once to let know if the chunk lighting is valid. Return value is ignored */ virtual void LightIsValid(bool a_IsLightValid) { UNUSED(a_IsLightValid); } - /** Called once to export block info */ - virtual void ChunkData(const cChunkData & a_Buffer) { UNUSED(a_Buffer); } + /** Called once to export block data. */ + virtual void ChunkData(const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData) { UNUSED(a_BlockData); UNUSED(a_LightData); } + + /** Called once to provide heightmap data. */ + virtual void HeightMap(const cChunkDef::HeightMap & a_HeightMap) { UNUSED(a_HeightMap); } + + /** Called once to provide biome data. */ + virtual void BiomeMap(const cChunkDef::BiomeMap & a_BiomeMap) { UNUSED(a_BiomeMap); } /** Called for each entity in the chunk */ virtual void Entity(cEntity * a_Entity) { UNUSED(a_Entity); } @@ -51,87 +51,20 @@ public: - -/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer */ -class cChunkDataArrayCollector : - public cChunkDataCallback -{ -public: - - // Must be unsigned char instead of BLOCKTYPE or NIBBLETYPE, because it houses both. - unsigned char m_BlockData[cChunkDef::BlockDataSize]; - -protected: - - virtual void ChunkData(const cChunkData & a_ChunkBuffer) override - { - a_ChunkBuffer.CopyBlockTypes(m_BlockData); - a_ChunkBuffer.CopyMetas(m_BlockData + cChunkDef::NumBlocks); - a_ChunkBuffer.CopyBlockLight(m_BlockData + 3 * cChunkDef::NumBlocks / 2); - a_ChunkBuffer.CopySkyLight(m_BlockData + 2 * cChunkDef::NumBlocks); - } -}; - - - - - -/** A simple implementation of the cChunkDataCallback interface that collects all block data into separate buffers */ -class cChunkDataSeparateCollector : - public cChunkDataCallback -{ -public: - - cChunkDef::BlockTypes m_BlockTypes; - cChunkDef::BlockNibbles m_BlockMetas; - cChunkDef::BlockNibbles m_BlockLight; - cChunkDef::BlockNibbles m_BlockSkyLight; - -protected: - - virtual void ChunkData(const cChunkData & a_ChunkBuffer) override - { - a_ChunkBuffer.CopyBlockTypes(m_BlockTypes); - a_ChunkBuffer.CopyMetas(m_BlockMetas); - a_ChunkBuffer.CopyBlockLight(m_BlockLight); - a_ChunkBuffer.CopySkyLight(m_BlockSkyLight); - } -} ; - - - - /** A simple implementation of the cChunkDataCallback interface that just copies the cChunkData */ class cChunkDataCopyCollector : public cChunkDataCallback { public: - struct MemCallbacks: - cAllocationPool<cChunkData::sChunkSection>::cStarvationCallbacks - { - virtual void OnStartUsingReserve() override {} - virtual void OnEndUsingReserve() override {} - virtual void OnOutOfReserve() override {} - }; - - cChunkDataCopyCollector(): - m_Pool(std::make_unique<MemCallbacks>(), 0, cChunkData::NumSections), // Keep 1 chunk worth of reserve - m_Data(m_Pool) - { - } + ChunkBlockData m_BlockData; + ChunkLightData m_LightData; - cListAllocationPool<cChunkData::sChunkSection> m_Pool; - cChunkData m_Data; +private: -protected: - - virtual void ChunkData(const cChunkData & a_ChunkBuffer) override + virtual void ChunkData(const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData) override { - m_Data.Assign(a_ChunkBuffer); + m_BlockData.Assign(a_BlockData); + m_LightData.Assign(a_LightData); } }; - - - - diff --git a/src/ChunkDef.h b/src/ChunkDef.h index 1e59f9fb7..9e97d53fc 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -129,13 +129,14 @@ private: class cChunkDef { public: + // Chunk dimensions: static const int Width = 16; static const int Height = 256; static const int NumBlocks = Width * Height * Width; - /** If the data is collected into a single buffer, how large it needs to be: */ - static const int BlockDataSize = cChunkDef::NumBlocks * 2 + (cChunkDef::NumBlocks / 2); // 2.5 * numblocks + static const int SectionHeight = 16; + static const size_t NumSections = (cChunkDef::Height / SectionHeight); /** The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the highest non-air block in the column */ typedef HEIGHTTYPE HeightMap[Width * Width]; @@ -151,12 +152,6 @@ public: /** The type used for block data in nibble format, AXIS_ORDER ordering */ typedef NIBBLETYPE BlockNibbles[NumBlocks / 2]; - /** The storage wrapper used for compressed blockdata residing in RAMz */ - typedef std::vector<BLOCKTYPE> COMPRESSED_BLOCKTYPE; - - /** The storage wrapper used for compressed nibbledata residing in RAMz */ - typedef std::vector<NIBBLETYPE> COMPRESSED_NIBBLETYPE; - /** Converts absolute block coords into relative (chunk + block) coords: */ inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ) @@ -169,9 +164,6 @@ public: } - - - /** Converts the specified absolute position into a relative position within its chunk. Use BlockToChunk to query the chunk coords. */ inline static Vector3i AbsoluteToRelative(Vector3i a_BlockPosition) @@ -181,9 +173,6 @@ public: } - - - /** Converts the absolute coords into coords relative to the specified chunk. */ inline static Vector3i AbsoluteToRelative(Vector3i a_BlockPosition, cChunkCoords a_ChunkPos) { @@ -191,8 +180,6 @@ public: } - - /** Converts relative block coordinates into absolute coordinates with a known chunk location */ inline static Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition, cChunkCoords a_ChunkCoords) { @@ -204,21 +191,20 @@ public: } - - - /** Validates a height-coordinate. Returns false if height-coordiante is out of height bounds */ inline static bool IsValidHeight(int a_Height) { return ((a_Height >= 0) && (a_Height < Height)); } + /** Validates a width-coordinate. Returns false if width-coordiante is out of width bounds */ inline static bool IsValidWidth(int a_Width) { return ((a_Width >= 0) && (a_Width < Width)); } + /** Validates a chunk relative coordinate. Returns false if the coordiante is out of bounds for a chunk. */ inline static bool IsValidRelPos(Vector3i a_RelPos) { @@ -229,6 +215,7 @@ public: ); } + /** Converts absolute block coords to chunk coords: */ inline static void BlockToChunk(int a_X, int a_Z, int & a_ChunkX, int & a_ChunkZ) { @@ -239,6 +226,7 @@ public: a_ChunkZ = ChunkCoords.m_ChunkZ; } + /** The Y coordinate of a_Pos is ignored */ inline static cChunkCoords BlockToChunk(const Vector3i a_Position) { @@ -273,14 +261,12 @@ public: } - inline static int MakeIndexNoCheck(Vector3i a_RelPos) { return MakeIndexNoCheck(a_RelPos.x, a_RelPos.y, a_RelPos.z); } - inline static Vector3i IndexToCoordinate(size_t index) { #if AXIS_ORDER == AXIS_ORDER_XZY @@ -370,115 +356,33 @@ public: } - static NIBBLETYPE GetNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int a_BlockIdx, bool a_IsSkyLightNibble = false) - { - if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks)) - { - if (static_cast<size_t>(a_BlockIdx / 2) >= a_Buffer.size()) - { - return (a_IsSkyLightNibble ? 0xff : 0); - } - return (a_Buffer[static_cast<size_t>(a_BlockIdx / 2)] >> ((a_BlockIdx & 1) * 4)) & 0x0f; - } - ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!"); - return 0; - } - - - static NIBBLETYPE GetNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int x, int y, int z, bool a_IsSkyLightNibble = false) - { - if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1)) - { - size_t Index = static_cast<size_t>(MakeIndexNoCheck(x, y, z)); - if ((Index / 2) >= a_Buffer.size()) - { - return (a_IsSkyLightNibble ? 0xff : 0); - } - return ExpandNibble(a_Buffer, Index); - } - ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!"); - return 0; - } - - - static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, Vector3i a_RelPos) - { - if (IsValidRelPos(a_RelPos)) - { - auto Index = MakeIndexNoCheck(a_RelPos); - return (a_Buffer[static_cast<size_t>(Index / 2)] >> ((Index & 1) * 4)) & 0x0f; - } - ASSERT(!"Coords out of chunk range!"); - return 0; - } - - static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z) { if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1)) { int Index = MakeIndexNoCheck(x, y, z); - return (a_Buffer[static_cast<size_t>(Index / 2)] >> ((Index & 1) * 4)) & 0x0f; + return ExpandNibble(a_Buffer, static_cast<size_t>(Index)); } ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!"); return 0; } - static void SetNibble(COMPRESSED_NIBBLETYPE & a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble) + inline static void PackNibble(NIBBLETYPE * const a_Buffer, const size_t a_Index, const NIBBLETYPE a_Nibble) { - if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks)) - { - ASSERT(!"cChunkDef::SetNibble(): index out of range!"); - return; - } - if (static_cast<size_t>(a_BlockIdx / 2) >= a_Buffer.size()) - { - a_Buffer.resize(static_cast<size_t>((a_BlockIdx / 2) + 1)); - } - a_Buffer[static_cast<size_t>(a_BlockIdx / 2)] = PackNibble(a_Buffer, static_cast<size_t>(a_BlockIdx), a_Nibble); - } + ASSERT((a_Nibble & 0xF) == a_Nibble); // Only the lower bits should be set - - static void SetNibble(COMPRESSED_NIBBLETYPE & a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble) - { - if ( - (x >= Width) || (x < 0) || - (y >= Height) || (y < 0) || - (z >= Width) || (z < 0) - ) - { - ASSERT(!"cChunkDef::SetNibble(): index out of range!"); - return; - } - - size_t Index = static_cast<size_t>(MakeIndexNoCheck(x, y, z)); - if ((Index / 2) >= a_Buffer.size()) - { - a_Buffer.resize(((Index / 2) + 1)); - } - a_Buffer[(Index / 2)] = PackNibble(a_Buffer, Index, a_Nibble); - } - - -private: - - - inline static NIBBLETYPE PackNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t a_Index, NIBBLETYPE a_Nibble) - { - return static_cast<NIBBLETYPE>( + a_Buffer[a_Index / 2] = static_cast<NIBBLETYPE>( (a_Buffer[a_Index / 2] & (0xf0 >> ((a_Index & 1) * 4))) | // The untouched nibble ((a_Nibble & 0x0f) << ((a_Index & 1) * 4)) // The nibble being set ); } - inline static NIBBLETYPE ExpandNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t a_Index) + inline static NIBBLETYPE ExpandNibble(const NIBBLETYPE * const a_Buffer, const size_t a_Index) { return (a_Buffer[a_Index / 2] >> ((a_Index & 1) * 4)) & 0x0f; } - - } ; diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index c1d9b78ab..dbd6f8cf3 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -30,12 +30,7 @@ // cChunkMap: cChunkMap::cChunkMap(cWorld * a_World) : - m_World(a_World), - m_Pool( - std::make_unique<cListAllocationPool<cChunkData::sChunkSection>>( - std::make_unique<cStarvationCallbacks>(), 1600u, 5000u - ) - ) + m_World(a_World) { } @@ -43,25 +38,12 @@ cChunkMap::cChunkMap(cWorld * a_World) : -cChunkMap::~cChunkMap() -{ - // Explicitly destroy all chunks, so that they're guaranteed to be - // destroyed before other internals. This fixes crashes on stopping the server. - // because the chunk destructor deletes entities and those may access the chunkmap. - // Also, the cChunkData destructor accesses the chunkMap's allocator. - m_Chunks.clear(); -} - - - - - cChunk & cChunkMap::ConstructChunk(int a_ChunkX, int a_ChunkZ) { // If not exists insert. Then, return the chunk at these coordinates: return m_Chunks.try_emplace( { a_ChunkX, a_ChunkZ }, - a_ChunkX, a_ChunkZ, this, m_World, *m_Pool + a_ChunkX, a_ChunkZ, this, m_World ).first->second; } @@ -232,15 +214,15 @@ void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) -void cChunkMap::SetChunkData(cSetChunkData & a_SetChunkData) +void cChunkMap::SetChunkData(struct SetChunkData && a_SetChunkData) { - int ChunkX = a_SetChunkData.GetChunkX(); - int ChunkZ = a_SetChunkData.GetChunkZ(); + const int ChunkX = a_SetChunkData.Chunk.m_ChunkX; + const int ChunkZ = a_SetChunkData.Chunk.m_ChunkZ; { cCSLock Lock(m_CSChunks); const auto Chunk = FindChunk(ChunkX, ChunkZ); ASSERT(Chunk != nullptr); // Chunk cannot have unloaded since it is marked as queued - Chunk->SetAllData(a_SetChunkData); + Chunk->SetAllData(std::move(a_SetChunkData)); // Notify relevant ChunkStays: cChunkStays ToBeDisabled; @@ -310,22 +292,6 @@ bool cChunkMap::GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callb -bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) -{ - cCSLock Lock(m_CSChunks); - const auto Chunk = FindChunk(a_ChunkX, a_ChunkZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) - { - return false; - } - Chunk->GetBlockTypes(a_BlockTypes); - return true; -} - - - - - bool cChunkMap::IsChunkQueued(int a_ChunkX, int a_ChunkZ) const { cCSLock Lock(m_CSChunks); diff --git a/src/ChunkMap.h b/src/ChunkMap.h index de1f29754..bafadc818 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -34,10 +34,11 @@ class cFlowerPotEntity; class cBlockArea; class cMobCensus; class cMobSpawner; -class cSetChunkData; class cBoundingBox; class cDeadlockDetect; +struct SetChunkData; + typedef std::list<cClientHandle *> cClientHandleList; using cEntityCallback = cFunctionRef<bool(cEntity &)>; using cBeaconCallback = cFunctionRef<bool(cBeaconEntity &)>; @@ -65,7 +66,6 @@ class cChunkMap public: cChunkMap(cWorld * a_World); - ~cChunkMap(); /** Sends the block entity, if it is at the coords specified, to a_Client */ void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); @@ -99,7 +99,7 @@ public: If MarkDirty is set, the chunk is set as dirty (used after generating) Modifies the BlockEntity list in a_SetChunkData - moves the block entities into the chunk. */ - void SetChunkData(cSetChunkData & a_SetChunkData); + void SetChunkData(SetChunkData && a_SetChunkData); void ChunkLighted( int a_ChunkX, int a_ChunkZ, @@ -111,9 +111,6 @@ public: Returns true if the chunk was reported successfully, false if not (chunk not present or callback failed). */ bool GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback) const; - /** Copies the chunk's blocktypes into a_Blocks; returns true if successful */ - bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks); - /** Returns true iff the chunk is in the loader / generator queue. */ bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) const; @@ -404,24 +401,6 @@ private: // The chunkstay can (de-)register itself using AddChunkStay() and DelChunkStay() friend class cChunkStay; - - class cStarvationCallbacks - : public cAllocationPool<cChunkData::sChunkSection>::cStarvationCallbacks - { - virtual void OnStartUsingReserve() override - { - LOG("Using backup memory buffer"); - } - virtual void OnEndUsingReserve() override - { - LOG("Stoped using backup memory buffer"); - } - virtual void OnOutOfReserve() override - { - LOG("Out of Memory"); - } - }; - typedef std::list<cChunkStay *> cChunkStays; mutable cCriticalSection m_CSChunks; @@ -437,8 +416,6 @@ private: /** The cChunkStay descendants that are currently enabled in this chunkmap */ cChunkStays m_ChunkStays; - std::unique_ptr<cAllocationPool<cChunkData::sChunkSection> > m_Pool; - /** Returns or creates and returns a chunk pointer corresponding to the given chunk coordinates. Emplaces this chunk in the chunk map. */ cChunk & ConstructChunk(int a_ChunkX, int a_ChunkZ); diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp index 0a7f58bc7..e40714472 100644 --- a/src/ChunkSender.cpp +++ b/src/ChunkSender.cpp @@ -238,7 +238,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, const WeakClients & a_C } // Send: - m_Serializer.SendToClients(a_ChunkX, a_ChunkZ, m_Data, m_BiomeMap, Clients); + m_Serializer.SendToClients(a_ChunkX, a_ChunkZ, m_BlockData, m_LightData, m_BiomeMap, Clients); for (const auto & Client : Clients) { @@ -276,7 +276,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, const WeakClients & a_C }); } } - m_Data.Clear(); + m_BlockEntities.clear(); m_EntityIDs.clear(); } @@ -303,14 +303,14 @@ void cChunkSender::Entity(cEntity * a_Entity) -void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) +void cChunkSender::BiomeMap(const cChunkDef::BiomeMap & a_BiomeMap) { for (size_t i = 0; i < ARRAYCOUNT(m_BiomeMap); i++) { - if ((*a_BiomeMap)[i] < 255) + if (a_BiomeMap[i] < 255) { // Normal MC biome, copy as-is: - m_BiomeMap[i] = static_cast<unsigned char>((*a_BiomeMap)[i]); + m_BiomeMap[i] = static_cast<unsigned char>(a_BiomeMap[i]); } else { @@ -319,7 +319,3 @@ void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) } } // for i - m_BiomeMap[] } - - - - diff --git a/src/ChunkSender.h b/src/ChunkSender.h index 43246ad64..7fe47c360 100644 --- a/src/ChunkSender.h +++ b/src/ChunkSender.h @@ -47,7 +47,7 @@ class cChunkSender; -class cChunkSender: +class cChunkSender final : public cIsThread, public cChunkDataCopyCollector { @@ -125,7 +125,7 @@ protected: // cChunkDataCollector overrides: // (Note that they are called while the ChunkMap's CS is locked - don't do heavy calculations here!) - virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) override; + virtual void BiomeMap (const cChunkDef::BiomeMap & a_BiomeMap) override; virtual void Entity (cEntity * a_Entity) override; virtual void BlockEntity (cBlockEntity * a_Entity) override; diff --git a/src/Globals.h b/src/Globals.h index bc868b637..0a5607c2f 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -293,6 +293,12 @@ namespace cpp20 { return std::unique_ptr<T>(new std::remove_extent_t<T>[a_Size]); } + + template <class T> + std::enable_if_t<!std::is_array_v<T>, std::unique_ptr<T>> make_unique_for_overwrite() + { + return std::unique_ptr<T>(new T); + } } diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp index 0632bbd23..015bfb68a 100644 --- a/src/LightingThread.cpp +++ b/src/LightingThread.cpp @@ -17,25 +17,25 @@ class cReader : public cChunkDataCallback { - virtual void ChunkData(const cChunkData & a_ChunkBuffer) override + virtual void ChunkData(const ChunkBlockData & a_BlockData, const ChunkLightData &) override { BLOCKTYPE * OutputRows = m_BlockTypes; int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3; - for (size_t i = 0; i != cChunkData::NumSections; ++i) + for (size_t i = 0; i != cChunkDef::NumSections; ++i) { - auto * Section = a_ChunkBuffer.GetSection(i); + const auto Section = a_BlockData.GetSection(i); if (Section == nullptr) { // Skip to the next section - OutputIdx += 9 * cChunkData::SectionHeight * cChunkDef::Width; + OutputIdx += 9 * cChunkDef::SectionHeight * cChunkDef::Width; continue; } - for (size_t OffsetY = 0; OffsetY != cChunkData::SectionHeight; ++OffsetY) + for (size_t OffsetY = 0; OffsetY != cChunkDef::SectionHeight; ++OffsetY) { for (size_t Z = 0; Z != cChunkDef::Width; ++Z) { - auto InPtr = Section->m_BlockTypes + Z * cChunkDef::Width + OffsetY * cChunkDef::Width * cChunkDef::Width; + auto InPtr = Section->data() + Z * cChunkDef::Width + OffsetY * cChunkDef::Width * cChunkDef::Width; std::copy_n(InPtr, cChunkDef::Width, OutputRows + OutputIdx * cChunkDef::Width); OutputIdx += 3; @@ -48,7 +48,7 @@ class cReader : } // BlockTypes() - virtual void HeightMap(const cChunkDef::HeightMap * a_Heightmap) override + virtual void HeightMap(const cChunkDef::HeightMap & a_Heightmap) override { // Copy the entire heightmap, distribute it into the 3x3 chunk blob: typedef struct {HEIGHTTYPE m_Row[16]; } ROW; @@ -64,11 +64,11 @@ class cReader : // Find the highest block in the entire chunk, use it as a base for m_MaxHeight: HEIGHTTYPE MaxHeight = m_MaxHeight; - for (size_t i = 0; i < ARRAYCOUNT(*a_Heightmap); i++) + for (size_t i = 0; i < ARRAYCOUNT(a_Heightmap); i++) { - if ((*a_Heightmap)[i] > MaxHeight) + if (a_Heightmap[i] > MaxHeight) { - MaxHeight = (*a_Heightmap)[i]; + MaxHeight = a_Heightmap[i]; } } m_MaxHeight = MaxHeight; diff --git a/src/NetherPortalScanner.cpp b/src/NetherPortalScanner.cpp index 69a009fa2..9c5c0929a 100644 --- a/src/NetherPortalScanner.cpp +++ b/src/NetherPortalScanner.cpp @@ -48,37 +48,73 @@ cNetherPortalScanner::cNetherPortalScanner(cEntity & a_MovingEntity, cWorld & a_ void cNetherPortalScanner::OnChunkAvailable(int a_ChunkX, int a_ChunkZ) { - cChunkDef::BlockTypes blocks; - m_World.GetChunkBlockTypes(a_ChunkX, a_ChunkZ, blocks); - - // Iterate through all of the blocks in the chunk - for (unsigned int i = 0; i < cChunkDef::NumBlocks; i++) + class PortalSearchCallback : public cChunkDataCallback { - if (blocks[i] == E_BLOCK_NETHER_PORTAL) + public: + + PortalSearchCallback(const int a_ChunkX, const int a_ChunkZ, bool & a_FoundPortal, Vector3i & a_PortalLoc, const Vector3d a_Position, const int a_MaxY) : + m_ChunkX(a_ChunkX), + m_ChunkZ(a_ChunkZ), + m_FoundPortal(a_FoundPortal), + m_PortalLoc(a_PortalLoc), + m_Position(a_Position), + m_MaxY(a_MaxY) { - Vector3i Coordinate = cChunkDef::IndexToCoordinate(i); - if (Coordinate.y >= m_MaxY) - { - // This is above the map, don't consider it. - continue; - } + } - Vector3d PortalLoc = Vector3d(Coordinate.x + a_ChunkX * cChunkDef::Width, Coordinate.y, Coordinate.z + a_ChunkZ * cChunkDef::Width); - if (!m_FoundPortal) - { - m_FoundPortal = true; - m_PortalLoc = PortalLoc; - } - else + private: + + virtual void ChunkData(const ChunkBlockData & a_BlockData, const ChunkLightData &) override + { + for (size_t Y = 0; Y < cChunkDef::NumSections; ++Y) { - if ((PortalLoc - m_Position).SqrLength() < (m_PortalLoc - m_Position).SqrLength()) + const auto Blocks = a_BlockData.GetSection(Y); + if (Blocks == nullptr) + { + continue; + } + + // Iterate through all of the blocks in the chunk: + for (size_t i = 0; i < ChunkBlockData::SectionBlockCount; i++) { - m_FoundPortal = true; - m_PortalLoc = PortalLoc; + if ((*Blocks)[i] != E_BLOCK_NETHER_PORTAL) + { + continue; + } + + Vector3i Coordinate = cChunkDef::IndexToCoordinate(i + Y * ChunkBlockData::SectionBlockCount); + if (Coordinate.y >= m_MaxY) + { + // This is above the map, don't consider it: + continue; + } + + Vector3d PortalLoc = Vector3d(Coordinate.x + m_ChunkX * cChunkDef::Width, Coordinate.y, Coordinate.z + m_ChunkZ * cChunkDef::Width); + if (!m_FoundPortal) + { + m_FoundPortal = true; + m_PortalLoc = PortalLoc; + } + else if ((PortalLoc - m_Position).SqrLength() < (m_PortalLoc - m_Position).SqrLength()) + { + // Found a closer portal, use that instead: + m_PortalLoc = PortalLoc; + } } } } - } + + int m_ChunkX, m_ChunkZ; + + bool & m_FoundPortal; + Vector3i & m_PortalLoc; + const Vector3d m_Position; + + const int m_MaxY; + } Callback(a_ChunkX, a_ChunkZ, m_FoundPortal, m_PortalLoc, m_Position, m_MaxY); + + [[maybe_unused]] const bool Result = m_World.GetChunkData({ a_ChunkX, a_ChunkZ }, Callback); + ASSERT(Result); } diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index ec7fa6648..80a94d024 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -14,26 +14,22 @@ -/** Calls the given function with every present chunk section. */ -template <class Func> -void ForEachSection(const cChunkData & a_Data, Func a_Func) +namespace { - for (size_t SectionIdx = 0; SectionIdx < cChunkData::NumSections; ++SectionIdx) + std::pair<UInt16, size_t> GetSectionBitmask(const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData) { - auto Section = a_Data.GetSection(SectionIdx); - if (Section != nullptr) - { - a_Func(*Section); - } - } -} - - + size_t Present = 0; + UInt16 Mask = 0; + ChunkDef::ForEachSection(a_BlockData, a_LightData, [&Present, &Mask](const auto Y, auto, auto, auto, auto) + { + Present++; + Mask |= (1 << Y); + }); + return { Mask, Present }; + } -namespace -{ auto PaletteLegacy(const BLOCKTYPE a_BlockType, const NIBBLETYPE a_Meta) { return (a_BlockType << 4) | a_Meta; @@ -72,7 +68,7 @@ cChunkDataSerializer::cChunkDataSerializer(const eDimension a_Dimension) : -void cChunkDataSerializer::SendToClients(const int a_ChunkX, const int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData, const ClientHandles & a_SendTo) +void cChunkDataSerializer::SendToClients(const int a_ChunkX, const int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap, const ClientHandles & a_SendTo) { for (const auto & Client : a_SendTo) { @@ -80,14 +76,14 @@ void cChunkDataSerializer::SendToClients(const int a_ChunkX, const int a_ChunkZ, { case cProtocol::Version::v1_8_0: { - Serialize(Client, a_ChunkX, a_ChunkZ, a_Data, a_BiomeData, CacheVersion::v47); + Serialize(Client, a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap, CacheVersion::v47); continue; } case cProtocol::Version::v1_9_0: case cProtocol::Version::v1_9_1: case cProtocol::Version::v1_9_2: { - Serialize(Client, a_ChunkX, a_ChunkZ, a_Data, a_BiomeData, CacheVersion::v107); + Serialize(Client, a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap, CacheVersion::v107); continue; } case cProtocol::Version::v1_9_4: @@ -98,23 +94,23 @@ void cChunkDataSerializer::SendToClients(const int a_ChunkX, const int a_ChunkZ, case cProtocol::Version::v1_12_1: case cProtocol::Version::v1_12_2: { - Serialize(Client, a_ChunkX, a_ChunkZ, a_Data, a_BiomeData, CacheVersion::v110); + Serialize(Client, a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap, CacheVersion::v110); continue; } case cProtocol::Version::v1_13: { - Serialize(Client, a_ChunkX, a_ChunkZ, a_Data, a_BiomeData, CacheVersion::v393); // This version didn't last very long xD + Serialize(Client, a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap, CacheVersion::v393); // This version didn't last very long xD continue; } case cProtocol::Version::v1_13_1: case cProtocol::Version::v1_13_2: { - Serialize(Client, a_ChunkX, a_ChunkZ, a_Data, a_BiomeData, CacheVersion::v401); + Serialize(Client, a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap, CacheVersion::v401); continue; } case cProtocol::Version::v1_14: { - Serialize(Client, a_ChunkX, a_ChunkZ, a_Data, a_BiomeData, CacheVersion::v477); + Serialize(Client, a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap, CacheVersion::v477); continue; } } @@ -132,7 +128,7 @@ void cChunkDataSerializer::SendToClients(const int a_ChunkX, const int a_ChunkZ, -inline void cChunkDataSerializer::Serialize(const ClientHandles::value_type & a_Client, const int a_ChunkX, const int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData, const CacheVersion a_CacheVersion) +inline void cChunkDataSerializer::Serialize(const ClientHandles::value_type & a_Client, const int a_ChunkX, const int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap, const CacheVersion a_CacheVersion) { auto & Cache = m_Cache[static_cast<size_t>(a_CacheVersion)]; if (Cache.Engaged) @@ -146,32 +142,32 @@ inline void cChunkDataSerializer::Serialize(const ClientHandles::value_type & a_ { case CacheVersion::v47: { - Serialize47(a_ChunkX, a_ChunkZ, a_Data, a_BiomeData); + Serialize47(a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap); break; } case CacheVersion::v107: { - Serialize107(a_ChunkX, a_ChunkZ, a_Data, a_BiomeData); + Serialize107(a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap); break; } case CacheVersion::v110: { - Serialize110(a_ChunkX, a_ChunkZ, a_Data, a_BiomeData); + Serialize110(a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap); break; } case CacheVersion::v393: { - Serialize393<&Palette393>(a_ChunkX, a_ChunkZ, a_Data, a_BiomeData); + Serialize393<&Palette393>(a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap); break; } case CacheVersion::v401: { - Serialize393<&Palette401>(a_ChunkX, a_ChunkZ, a_Data, a_BiomeData); + Serialize393<&Palette401>(a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap); break; } case CacheVersion::v477: { - Serialize477(a_ChunkX, a_ChunkZ, a_Data, a_BiomeData); + Serialize477(a_ChunkX, a_ChunkZ, a_BlockData, a_LightData, a_BiomeMap); break; } } @@ -185,177 +181,189 @@ inline void cChunkDataSerializer::Serialize(const ClientHandles::value_type & a_ -inline void cChunkDataSerializer::Serialize47(const int a_ChunkX, const int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData) +inline void cChunkDataSerializer::Serialize47(const int a_ChunkX, const int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap) { // This function returns the fully compressed packet (including packet size), not the raw packet! + const auto Bitmask = GetSectionBitmask(a_BlockData, a_LightData); + // Create the packet: m_Packet.WriteVarInt32(0x21); // Packet id (Chunk Data packet) m_Packet.WriteBEInt32(a_ChunkX); m_Packet.WriteBEInt32(a_ChunkZ); m_Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag - m_Packet.WriteBEUInt16(a_Data.GetSectionBitmask()); + m_Packet.WriteBEUInt16(Bitmask.first); // Write the chunk size: const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - UInt32 ChunkSize = ( - a_Data.NumPresentSections() * cChunkData::SectionBlockCount * 3 + // Blocks and lighting + const size_t ChunkSize = ( + Bitmask.second * (ChunkBlockData::SectionBlockCount * 2 + ChunkLightData::SectionLightCount * 2) + // Blocks and lighting BiomeDataSize // Biome data ); - m_Packet.WriteVarInt32(ChunkSize); + m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSize)); // Chunk written as seperate arrays of (blocktype + meta), blocklight and skylight // each array stores all present sections of the same kind packed together // Write the block types to the packet: - ForEachSection(a_Data, [&](const cChunkData::sChunkSection & a_Section) + ChunkDef::ForEachSection(a_BlockData, a_LightData, [this](auto, const auto Blocks, const auto Metas, auto, auto) + { + const bool BlocksExist = Blocks != nullptr; + const bool MetasExist = Metas != nullptr; + + for (size_t BlockIdx = 0; BlockIdx != ChunkBlockData::SectionBlockCount; ++BlockIdx) { - for (size_t BlockIdx = 0; BlockIdx != cChunkData::SectionBlockCount; ++BlockIdx) - { - BLOCKTYPE BlockType = a_Section.m_BlockTypes[BlockIdx] & 0xFF; - NIBBLETYPE BlockMeta = a_Section.m_BlockMetas[BlockIdx / 2] >> ((BlockIdx & 1) * 4) & 0x0f; - m_Packet.WriteBEUInt8(static_cast<unsigned char>(BlockType << 4) | BlockMeta); - m_Packet.WriteBEUInt8(static_cast<unsigned char>(BlockType >> 4)); - } + BLOCKTYPE BlockType = BlocksExist ? (*Blocks)[BlockIdx] : 0; + NIBBLETYPE BlockMeta = MetasExist ? cChunkDef::ExpandNibble(Metas->data(), BlockIdx) : 0; + m_Packet.WriteBEUInt8(static_cast<unsigned char>(BlockType << 4) | BlockMeta); + m_Packet.WriteBEUInt8(static_cast<unsigned char>(BlockType >> 4)); } - ); + }); // Write the block lights: - ForEachSection(a_Data, [&](const cChunkData::sChunkSection & a_Section) + ChunkDef::ForEachSection(a_BlockData, a_LightData, [this](auto, auto, auto, const auto BlockLights, auto) + { + if (BlockLights == nullptr) { - m_Packet.WriteBuf(a_Section.m_BlockLight, sizeof(a_Section.m_BlockLight)); + m_Packet.WriteBuf(ChunkLightData::SectionLightCount, ChunkLightData::DefaultBlockLightValue); } - ); + else + { + m_Packet.WriteBuf(BlockLights->data(), BlockLights->size()); + } + }); // Write the sky lights: - ForEachSection(a_Data, [&](const cChunkData::sChunkSection & a_Section) + ChunkDef::ForEachSection(a_BlockData, a_LightData, [this](auto, auto, auto, auto, const auto SkyLights) + { + if (SkyLights == nullptr) { - m_Packet.WriteBuf(a_Section.m_BlockSkyLight, sizeof(a_Section.m_BlockSkyLight)); + m_Packet.WriteBuf(ChunkLightData::SectionLightCount, ChunkLightData::DefaultSkyLightValue); } - ); + else + { + m_Packet.WriteBuf(SkyLights->data(), SkyLights->size()); + } + }); // Write the biome data: - m_Packet.WriteBuf(a_BiomeData, BiomeDataSize); + m_Packet.WriteBuf(a_BiomeMap, BiomeDataSize); } -inline void cChunkDataSerializer::Serialize107(const int a_ChunkX, const int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData) +inline void cChunkDataSerializer::Serialize107(const int a_ChunkX, const int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap) { // This function returns the fully compressed packet (including packet size), not the raw packet! + // Below variables tagged static because of https://developercommunity.visualstudio.com/content/problem/367326 + + static constexpr UInt8 BitsPerEntry = 13; + static constexpr size_t ChunkSectionDataArraySize = (ChunkBlockData::SectionBlockCount * BitsPerEntry) / 8 / 8; // Convert from bit count to long count + + const auto Bitmask = GetSectionBitmask(a_BlockData, a_LightData); // Create the packet: m_Packet.WriteVarInt32(0x20); // Packet id (Chunk Data packet) m_Packet.WriteBEInt32(a_ChunkX); m_Packet.WriteBEInt32(a_ChunkZ); m_Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag - m_Packet.WriteVarInt32(a_Data.GetSectionBitmask()); - // Write the chunk size: - const UInt8 BitsPerEntry = 13; - const size_t ChunkSectionDataArraySize = (cChunkData::SectionBlockCount * BitsPerEntry) / 8 / 8; // Convert from bit count to long count + m_Packet.WriteVarInt32(Bitmask.first); + size_t ChunkSectionSize = ( 1 + // Bits per block - set to 13, so the global palette is used and the palette has a length of 0 1 + // Palette length 2 + // Data array length VarInt - 2 bytes for the current value ChunkSectionDataArraySize * 8 + // Actual block data - multiplied by 8 because first number is longs - cChunkData::SectionBlockCount / 2 // Block light + ChunkLightData::SectionLightCount // Block light ); if (m_Dimension == dimOverworld) { // Sky light is only sent in the overworld. - ChunkSectionSize += cChunkData::SectionBlockCount / 2; + ChunkSectionSize += ChunkLightData::SectionLightCount; } const size_t BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - size_t ChunkSize = ( - ChunkSectionSize * a_Data.NumPresentSections() + + const size_t ChunkSize = ( + ChunkSectionSize * Bitmask.second + BiomeDataSize ); + + // Write the chunk size: m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSize)); // Write each chunk section... - ForEachSection(a_Data, [&](const cChunkData::sChunkSection & a_Section) - { - m_Packet.WriteBEUInt8(BitsPerEntry); - m_Packet.WriteVarInt32(0); // Palette length is 0 - m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize)); - WriteSectionDataSeamless<&PaletteLegacy>(a_Section, BitsPerEntry); - - // Write lighting: - m_Packet.WriteBuf(a_Section.m_BlockLight, sizeof(a_Section.m_BlockLight)); - if (m_Dimension == dimOverworld) - { - // Skylight is only sent in the overworld; the nether and end do not use it - m_Packet.WriteBuf(a_Section.m_BlockSkyLight, sizeof(a_Section.m_BlockSkyLight)); - } - } - ); + ChunkDef::ForEachSection(a_BlockData, a_LightData, [this](auto, const auto Blocks, const auto Metas, const auto BlockLights, const auto SkyLights) + { + m_Packet.WriteBEUInt8(BitsPerEntry); + m_Packet.WriteVarInt32(0); // Palette length is 0 + m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize)); + WriteBlockSectionSeamless<&PaletteLegacy>(Blocks, Metas, BitsPerEntry); + WriteLightSectionGrouped(BlockLights, SkyLights); + }); // Write the biome data - m_Packet.WriteBuf(a_BiomeData, BiomeDataSize); + m_Packet.WriteBuf(a_BiomeMap, BiomeDataSize); } -inline void cChunkDataSerializer::Serialize110(const int a_ChunkX, const int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData) +inline void cChunkDataSerializer::Serialize110(const int a_ChunkX, const int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap) { // This function returns the fully compressed packet (including packet size), not the raw packet! + // Below variables tagged static because of https://developercommunity.visualstudio.com/content/problem/367326 + + static constexpr UInt8 BitsPerEntry = 13; + static constexpr size_t ChunkSectionDataArraySize = (ChunkBlockData::SectionBlockCount * BitsPerEntry) / 8 / 8; // Convert from bit count to long count + + const auto Bitmask = GetSectionBitmask(a_BlockData, a_LightData); // Create the packet: m_Packet.WriteVarInt32(0x20); // Packet id (Chunk Data packet) m_Packet.WriteBEInt32(a_ChunkX); m_Packet.WriteBEInt32(a_ChunkZ); m_Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag - m_Packet.WriteVarInt32(a_Data.GetSectionBitmask()); - // Write the chunk size: - const UInt8 BitsPerEntry = 13; - const size_t ChunkSectionDataArraySize = (cChunkData::SectionBlockCount * BitsPerEntry) / 8 / 8; // Convert from bit count to long count + m_Packet.WriteVarInt32(Bitmask.first); + size_t ChunkSectionSize = ( 1 + // Bits per block - set to 13, so the global palette is used and the palette has a length of 0 1 + // Palette length 2 + // Data array length VarInt - 2 bytes for the current value ChunkSectionDataArraySize * 8 + // Actual block data - multiplied by 8 because first number is longs - cChunkData::SectionBlockCount / 2 // Block light + ChunkLightData::SectionLightCount // Block light ); if (m_Dimension == dimOverworld) { // Sky light is only sent in the overworld. - ChunkSectionSize += cChunkData::SectionBlockCount / 2; + ChunkSectionSize += ChunkLightData::SectionLightCount; } const size_t BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - size_t ChunkSize = ( - ChunkSectionSize * a_Data.NumPresentSections() + + const size_t ChunkSize = ( + ChunkSectionSize * Bitmask.second + BiomeDataSize ); + + // Write the chunk size: m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSize)); // Write each chunk section... - ForEachSection(a_Data, [&](const cChunkData::sChunkSection & a_Section) - { - m_Packet.WriteBEUInt8(BitsPerEntry); - m_Packet.WriteVarInt32(0); // Palette length is 0 - m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize)); - WriteSectionDataSeamless<&PaletteLegacy>(a_Section, BitsPerEntry); - - // Write lighting: - m_Packet.WriteBuf(a_Section.m_BlockLight, sizeof(a_Section.m_BlockLight)); - if (m_Dimension == dimOverworld) - { - // Skylight is only sent in the overworld; the nether and end do not use it - m_Packet.WriteBuf(a_Section.m_BlockSkyLight, sizeof(a_Section.m_BlockSkyLight)); - } - } - ); + ChunkDef::ForEachSection(a_BlockData, a_LightData, [this](auto, const auto Blocks, const auto Metas, const auto BlockLights, const auto SkyLights) + { + m_Packet.WriteBEUInt8(BitsPerEntry); + m_Packet.WriteVarInt32(0); // Palette length is 0 + m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize)); + WriteBlockSectionSeamless<&PaletteLegacy>(Blocks, Metas, BitsPerEntry); + WriteLightSectionGrouped(BlockLights, SkyLights); + }); // Write the biome data - m_Packet.WriteBuf(a_BiomeData, BiomeDataSize); + m_Packet.WriteBuf(a_BiomeMap, BiomeDataSize); // Identify 1.9.4's tile entity list as empty m_Packet.WriteBEUInt8(0); @@ -366,61 +374,58 @@ inline void cChunkDataSerializer::Serialize110(const int a_ChunkX, const int a_C template <auto Palette> -inline void cChunkDataSerializer::Serialize393(const int a_ChunkX, const int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData) +inline void cChunkDataSerializer::Serialize393(const int a_ChunkX, const int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap) { // This function returns the fully compressed packet (including packet size), not the raw packet! + // Below variables tagged static because of https://developercommunity.visualstudio.com/content/problem/367326 + + static constexpr UInt8 BitsPerEntry = 14; + static constexpr size_t ChunkSectionDataArraySize = (ChunkBlockData::SectionBlockCount * BitsPerEntry) / 8 / 8; + + const auto Bitmask = GetSectionBitmask(a_BlockData, a_LightData); // Create the packet: m_Packet.WriteVarInt32(0x22); // Packet id (Chunk Data packet) m_Packet.WriteBEInt32(a_ChunkX); m_Packet.WriteBEInt32(a_ChunkZ); m_Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag - m_Packet.WriteVarInt32(a_Data.GetSectionBitmask()); + m_Packet.WriteVarInt32(Bitmask.first); - // Write the chunk size in bytes: - const UInt8 BitsPerEntry = 14; - const size_t ChunkSectionDataArraySize = (cChunkData::SectionBlockCount * BitsPerEntry) / 8 / 8; size_t ChunkSectionSize = ( 1 + // Bits per entry, BEUInt8, 1 byte m_Packet.GetVarIntSize(static_cast<UInt32>(ChunkSectionDataArraySize)) + // Field containing "size of whole section", VarInt32, variable size ChunkSectionDataArraySize * 8 + // Actual section data, lots of bytes (multiplier 1 long = 8 bytes) - cChunkData::SectionBlockCount / 2 // Size of blocklight which is always sent + ChunkLightData::SectionLightCount // Size of blocklight which is always sent ); if (m_Dimension == dimOverworld) { // Sky light is only sent in the overworld. - ChunkSectionSize += cChunkData::SectionBlockCount / 2; + ChunkSectionSize += ChunkLightData::SectionLightCount; } const size_t BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - size_t ChunkSize = ( - ChunkSectionSize * a_Data.NumPresentSections() + + const size_t ChunkSize = ( + ChunkSectionSize * Bitmask.second + BiomeDataSize * 4 // Biome data now BE ints ); + + // Write the chunk size in bytes: m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSize)); // Write each chunk section... - ForEachSection(a_Data, [&](const cChunkData::sChunkSection & a_Section) - { - m_Packet.WriteBEUInt8(BitsPerEntry); - m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize)); - WriteSectionDataSeamless<Palette>(a_Section, BitsPerEntry); - - // Write lighting: - m_Packet.WriteBuf(a_Section.m_BlockLight, sizeof(a_Section.m_BlockLight)); - if (m_Dimension == dimOverworld) - { - // Skylight is only sent in the overworld; the nether and end do not use it - m_Packet.WriteBuf(a_Section.m_BlockSkyLight, sizeof(a_Section.m_BlockSkyLight)); - } - } - ); + ChunkDef::ForEachSection(a_BlockData, a_LightData, [this](auto, const auto Blocks, const auto Metas, const auto BlockLights, const auto SkyLights) + { + m_Packet.WriteBEUInt8(BitsPerEntry); + m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize)); + WriteBlockSectionSeamless<Palette>(Blocks, Metas, BitsPerEntry); + WriteLightSectionGrouped(BlockLights, SkyLights); + }); // Write the biome data for (size_t i = 0; i != BiomeDataSize; i++) { - m_Packet.WriteBEUInt32(static_cast<UInt32>(a_BiomeData[i]) & 0xff); + m_Packet.WriteBEUInt32(static_cast<UInt32>(a_BiomeMap[i])); } // Identify 1.9.4's tile entity list as empty @@ -431,16 +436,22 @@ inline void cChunkDataSerializer::Serialize393(const int a_ChunkX, const int a_C -inline void cChunkDataSerializer::Serialize477(const int a_ChunkX, const int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData) +inline void cChunkDataSerializer::Serialize477(const int a_ChunkX, const int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap) { // This function returns the fully compressed packet (including packet size), not the raw packet! + // Below variables tagged static because of https://developercommunity.visualstudio.com/content/problem/367326 + + static constexpr UInt8 BitsPerEntry = 14; + static constexpr size_t ChunkSectionDataArraySize = (ChunkBlockData::SectionBlockCount * BitsPerEntry) / 8 / 8; + + const auto Bitmask = GetSectionBitmask(a_BlockData, a_LightData); // Create the packet: m_Packet.WriteVarInt32(0x21); // Packet id (Chunk Data packet) m_Packet.WriteBEInt32(a_ChunkX); m_Packet.WriteBEInt32(a_ChunkZ); m_Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag - m_Packet.WriteVarInt32(a_Data.GetSectionBitmask()); + m_Packet.WriteVarInt32(Bitmask.first); { cFastNBTWriter Writer; @@ -451,9 +462,6 @@ inline void cChunkDataSerializer::Serialize477(const int a_ChunkX, const int a_C m_Packet.Write(Writer.GetResult().data(), Writer.GetResult().size()); } - // Write the chunk size in bytes: - const UInt8 BitsPerEntry = 14; - const size_t ChunkSectionDataArraySize = (cChunkData::SectionBlockCount * BitsPerEntry) / 8 / 8; const size_t ChunkSectionSize = ( 2 + // Block count, BEInt16, 2 bytes 1 + // Bits per entry, BEUInt8, 1 byte @@ -463,25 +471,26 @@ inline void cChunkDataSerializer::Serialize477(const int a_ChunkX, const int a_C const size_t BiomeDataSize = cChunkDef::Width * cChunkDef::Width; const size_t ChunkSize = ( - ChunkSectionSize * a_Data.NumPresentSections() + + ChunkSectionSize * Bitmask.second + BiomeDataSize * 4 // Biome data now BE ints ); + + // Write the chunk size in bytes: m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSize)); // Write each chunk section... - ForEachSection(a_Data, [&](const cChunkData::sChunkSection & a_Section) - { - m_Packet.WriteBEInt16(-1); - m_Packet.WriteBEUInt8(BitsPerEntry); - m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize)); - WriteSectionDataSeamless<&Palette477>(a_Section, BitsPerEntry); - } - ); + ChunkDef::ForEachSection(a_BlockData, a_LightData, [this](auto, const auto Blocks, const auto Metas, auto, auto) + { + m_Packet.WriteBEInt16(-1); + m_Packet.WriteBEUInt8(BitsPerEntry); + m_Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize)); + WriteBlockSectionSeamless<&Palette477>(Blocks, Metas, BitsPerEntry); + }); // Write the biome data for (size_t i = 0; i != BiomeDataSize; i++) { - m_Packet.WriteBEUInt32(static_cast<UInt32>(a_BiomeData[i]) & 0xff); + m_Packet.WriteBEUInt32(a_BiomeMap[i]); } // Identify 1.9.4's tile entity list as empty @@ -493,7 +502,7 @@ inline void cChunkDataSerializer::Serialize477(const int a_ChunkX, const int a_C template <auto Palette> -inline void cChunkDataSerializer::WriteSectionDataSeamless(const cChunkData::sChunkSection & a_Section, const UInt8 a_BitsPerEntry) +inline void cChunkDataSerializer::WriteBlockSectionSeamless(const ChunkBlockData::BlockArray * a_Blocks, const ChunkBlockData::MetaArray * a_Metas, const UInt8 a_BitsPerEntry) { // https://wiki.vg/Chunk_Format#Data_structure @@ -503,10 +512,13 @@ inline void cChunkDataSerializer::WriteSectionDataSeamless(const cChunkData::sCh UInt64 Buffer = 0; // A buffer to compose multiple smaller bitsizes into one 64-bit number unsigned char BitIndex = 0; // The bit-position in Buffer that represents where to write next - for (size_t Index = 0; Index != cChunkData::SectionBlockCount; Index++) + const bool BlocksExist = a_Blocks != nullptr; + const bool MetasExist = a_Metas != nullptr; + + for (size_t Index = 0; Index != ChunkBlockData::SectionBlockCount; Index++) { - const BLOCKTYPE BlockType = a_Section.m_BlockTypes[Index]; - const NIBBLETYPE BlockMeta = (a_Section.m_BlockMetas[Index / 2] >> ((Index % 2) * 4)) & 0x0f; + const BLOCKTYPE BlockType = BlocksExist ? (*a_Blocks)[Index] : 0; + const NIBBLETYPE BlockMeta = MetasExist ? cChunkDef::ExpandNibble(a_Metas->data(), Index) : 0; const auto Value = Palette(BlockType, BlockMeta); // Write as much as possible of Value, starting from BitIndex, into Buffer: @@ -530,7 +542,7 @@ inline void cChunkDataSerializer::WriteSectionDataSeamless(const cChunkData::sCh } } - static_assert((cChunkData::SectionBlockCount % 64) == 0, "Section must fit wholly into a 64-bit long array"); + static_assert((ChunkBlockData::SectionBlockCount % 64) == 0, "Section must fit wholly into a 64-bit long array"); ASSERT(BitIndex == 0); ASSERT(Buffer == 0); } @@ -539,6 +551,36 @@ inline void cChunkDataSerializer::WriteSectionDataSeamless(const cChunkData::sCh +inline void cChunkDataSerializer::WriteLightSectionGrouped(const ChunkLightData::LightArray * const a_BlockLights, const ChunkLightData::LightArray * const a_SkyLights) +{ + // Write lighting: + if (a_BlockLights == nullptr) + { + m_Packet.WriteBuf(ChunkLightData::SectionLightCount, ChunkLightData::DefaultBlockLightValue); + } + else + { + m_Packet.WriteBuf(a_BlockLights->data(), a_BlockLights->size()); + } + + // Skylight is only sent in the overworld; the nether and end do not use it: + if (m_Dimension == dimOverworld) + { + if (a_SkyLights == nullptr) + { + m_Packet.WriteBuf(ChunkLightData::SectionLightCount, ChunkLightData::DefaultSkyLightValue); + } + else + { + m_Packet.WriteBuf(a_SkyLights->data(), a_SkyLights->size()); + } + } +} + + + + + inline void cChunkDataSerializer::CompressPacketInto(ChunkDataCache & a_Cache) { m_Compressor.ReadFrom(m_Packet); diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h index 47d92e2ee..d440098f6 100644 --- a/src/Protocol/ChunkDataSerializer.h +++ b/src/Protocol/ChunkDataSerializer.h @@ -49,25 +49,28 @@ public: /** For each client, serializes the chunk into their protocol version and sends it. Parameters are the coordinates of the chunk to serialise, and the data and biome data read from the chunk. */ - void SendToClients(int a_ChunkX, int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData, const ClientHandles & a_SendTo); + void SendToClients(int a_ChunkX, int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap, const ClientHandles & a_SendTo); private: /** Serialises the given chunk, storing the result into the given cache entry, and sends the data. If the cache entry is already present, simply re-uses it. */ - inline void Serialize(const ClientHandles::value_type & a_Client, int a_ChunkX, int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData, CacheVersion a_CacheVersion); + inline void Serialize(const ClientHandles::value_type & a_Client, int a_ChunkX, int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap, CacheVersion a_CacheVersion); - inline void Serialize47 (int a_ChunkX, int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData); // Release 1.8 - inline void Serialize107(int a_ChunkX, int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData); // Release 1.9 - inline void Serialize110(int a_ChunkX, int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData); // Release 1.9.4 + inline void Serialize47 (int a_ChunkX, int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap); // Release 1.8 + inline void Serialize107(int a_ChunkX, int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap); // Release 1.9 + inline void Serialize110(int a_ChunkX, int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap); // Release 1.9.4 template <auto Palette> - inline void Serialize393(int a_ChunkX, int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData); // Release 1.13 - 1.13.2 - inline void Serialize477(int a_ChunkX, int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData); // Release 1.14 - 1.14.4 + inline void Serialize393(int a_ChunkX, int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap); // Release 1.13 - 1.13.2 + inline void Serialize477(int a_ChunkX, int a_ChunkZ, const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData, const unsigned char * a_BiomeMap); // Release 1.14 - 1.14.4 /** Writes all blocks in a chunk section into a series of Int64. Writes start from the bit directly subsequent to the previous write's end, possibly crossing over to the next Int64. */ template <auto Palette> - inline void WriteSectionDataSeamless(const cChunkData::sChunkSection & a_Section, const UInt8 a_BitsPerEntry); + inline void WriteBlockSectionSeamless(const ChunkBlockData::BlockArray * a_Blocks, const ChunkBlockData::MetaArray * a_Metas, UInt8 a_BitsPerEntry); + + /** Copies all lights in a chunk section into the packet, block light followed immediately by sky light. */ + inline void WriteLightSectionGrouped(const ChunkLightData::LightArray * a_BlockLights, const ChunkLightData::LightArray * a_SkyLights); /** Finalises the data, compresses it if required, and stores it into cache. */ inline void CompressPacketInto(ChunkDataCache & a_Cache); diff --git a/src/Scoreboard.h b/src/Scoreboard.h index 523598578..3127ac15b 100644 --- a/src/Scoreboard.h +++ b/src/Scoreboard.h @@ -9,12 +9,13 @@ #pragma once +#include "FunctionRef.h" + -#include "FunctionRef.h" -#include "ChunkDef.h" +class cClientHandle; class cObjective; class cTeam; class cWorld; diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp deleted file mode 100644 index 8f850dec4..000000000 --- a/src/SetChunkData.cpp +++ /dev/null @@ -1,157 +0,0 @@ - -// SetChunkData.cpp - -// Implements the cSetChunkData class used for sending loaded / generated chunk - -#include "Globals.h" -#include "SetChunkData.h" -#include "BlockEntities/BlockEntity.h" -#include "Entities/Entity.h" - -namespace -{ -struct sMemCallbacks: - cAllocationPool<cChunkData::sChunkSection>::cStarvationCallbacks -{ - virtual void OnStartUsingReserve() override {} - virtual void OnEndUsingReserve() override {} - virtual void OnOutOfReserve() override {} -}; -} // namespace (anonymous) - - - - - -cSetChunkData::cSetChunkData(int a_ChunkX, int a_ChunkZ, bool a_ShouldMarkDirty) : - m_ChunkX(a_ChunkX), - m_ChunkZ(a_ChunkZ), - m_Pool(std::make_unique<sMemCallbacks>(), 0u, cChunkData::NumSections), - m_ChunkData(m_Pool), - m_IsLightValid(false), - m_IsHeightMapValid(false), - m_AreBiomesValid(false), - m_ShouldMarkDirty(a_ShouldMarkDirty) -{ -} - - - - - -cSetChunkData::cSetChunkData( - int a_ChunkX, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMetas, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_SkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap * a_Biomes, - cEntityList && a_Entities, - cBlockEntities && a_BlockEntities, - bool a_ShouldMarkDirty -) : - cSetChunkData(a_ChunkX, a_ChunkZ, a_ShouldMarkDirty) -{ - // Check the params' validity: - ASSERT(a_BlockTypes != nullptr); - ASSERT(a_BlockMetas != nullptr); - - // Copy block types and metas: - m_ChunkData.SetBlockTypes(a_BlockTypes); - m_ChunkData.SetMetas(a_BlockMetas); - - // Copy lights, if both given: - if ((a_BlockLight != nullptr) && (a_SkyLight != nullptr)) - { - m_ChunkData.SetBlockLight(a_BlockLight); - m_ChunkData.SetSkyLight(a_SkyLight); - m_IsLightValid = true; - } - - // Copy the heightmap, if available: - if (a_HeightMap != nullptr) - { - memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap)); - m_IsHeightMapValid = true; - } - - // Copy biomes, if available: - if (a_Biomes != nullptr) - { - memcpy(m_Biomes, a_Biomes, sizeof(m_Biomes)); - m_AreBiomesValid = true; - } - - // Move entities and blockentities: - m_Entities = std::move(a_Entities); - m_BlockEntities = std::move(a_BlockEntities); -} - - - - - -void cSetChunkData::CalculateHeightMap(void) -{ - // Find the heighest present section in the chunk - size_t MaxSection = 0; - for (size_t i = cChunkData::NumSections - 1; i != 0; --i) - { - if (m_ChunkData.GetSection(i) != nullptr) - { - MaxSection = i; - break; - } - } - const int MaxHeight = static_cast<int>(MaxSection + 1) * cChunkData::SectionHeight - 1; - - for (int x = 0; x < cChunkDef::Width; x++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = MaxHeight; y > -1; y--) - { - if (m_ChunkData.GetBlock({x, y, z}) != E_BLOCK_AIR) - { - m_HeightMap[x + z * cChunkDef::Width] = static_cast<HEIGHTTYPE>(y); - break; - } - } // for y - } // for z - } // for x - m_IsHeightMapValid = true; -} - - - - - -void cSetChunkData::RemoveInvalidBlockEntities(void) -{ - for (cBlockEntities::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end();) - { - auto & BlockEntity = itr->second; - BLOCKTYPE EntityBlockType = BlockEntity->GetBlockType(); - BLOCKTYPE WorldBlockType = m_ChunkData.GetBlock({BlockEntity->GetRelX(), BlockEntity->GetPosY(), BlockEntity->GetRelZ()}); - if (EntityBlockType != WorldBlockType) - { - // Bad blocktype, remove the block entity: - FLOGD("Block entity blocktype mismatch at {0}: entity for blocktype {1}({2}) in block {3}({4}). Deleting the block entity.", - BlockEntity->GetPos(), - ItemTypeToString(EntityBlockType), EntityBlockType, - ItemTypeToString(WorldBlockType), WorldBlockType - ); - itr = m_BlockEntities.erase(itr); - } - else - { - // Good blocktype, keep the block entity: - ++itr; - } - } // for itr - m_BlockEntities[] -} - - - - diff --git a/src/SetChunkData.h b/src/SetChunkData.h index 1744d3b78..8a4a8b995 100644 --- a/src/SetChunkData.h +++ b/src/SetChunkData.h @@ -1,7 +1,7 @@ // SetChunkData.h -// Declares the cSetChunkData class used for sending loaded / generated chunk data into cWorld +// Defines the SetChunkData struct that contains the data for a loaded / generated chunk, ready to be set #pragma once @@ -12,94 +12,25 @@ -class cSetChunkData +struct SetChunkData { -public: - /** Constructs a new instance with empty data. - Allocates new buffers for the block data. - Prefer to use this constructor, then fill the object with data and then send it to cWorld, as this will - reduce the copying required to queue the set operation. */ - cSetChunkData(int a_ChunkX, int a_ChunkZ, bool a_ShouldMarkDirty); + /** Initialise the structure with chunk coordinates. + The caller is responsible for initialising the remaining members. */ + SetChunkData(const cChunkCoords a_Chunk) : + Chunk(a_Chunk) + { + } - /** Constructs a new instance based on data existing elsewhere, will copy all the memory. Prefer to use the - other constructor as much as possible. - Will move the entity list and blockentities into the internal storage, and invalidate a_Entities and - a_BlockEntities. - When passing an lvalue, a_Entities and a_BlockEntities must be explicitly converted to an rvalue beforehand - with std::move(). - a_BlockTypes and a_BlockMetas must always be valid. - If either of the light arrays are nullptr, the chunk data will be marked as not having any light at all and - will be scheduled for re-lighting once it is set into the chunkmap. - If a_Biomes is not valid, the internal flag is set and the world will calculate the biomes using the chunk - generator when setting the chunk data. - If a_HeightMap is not assigned, the world will calculate the heightmap based on the blocktypes when setting - the chunk data. */ - cSetChunkData( - int a_ChunkX, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMetas, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_SkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap * a_Biomes, - cEntityList && a_Entities, - cBlockEntities && a_BlockEntities, - bool a_ShouldMarkDirty - ); + cChunkCoords Chunk; - int GetChunkX(void) const { return m_ChunkX; } - int GetChunkZ(void) const { return m_ChunkZ; } + ChunkBlockData BlockData; + ChunkLightData LightData; - /** Returns the internal storage of block types, metas and lighting. */ - cChunkData & GetChunkData(void) { return m_ChunkData; } + cChunkDef::BiomeMap BiomeMap; + cChunkDef::HeightMap HeightMap; - /** Returns the internal storage for heightmap, read-only. */ - const cChunkDef::HeightMap & GetHeightMap(void) const { return m_HeightMap; } + cEntityList Entities; + cBlockEntities BlockEntities; - /** Returns the internal storage for biomes, read-write. */ - cChunkDef::BiomeMap & GetBiomes(void) { return m_Biomes; } - - /** Returns the internal storage for entities, read-write. */ - cEntityList & GetEntities(void) { return m_Entities; } - - /** Returns the internal storage for block entities, read-write. */ - cBlockEntities & GetBlockEntities(void) { return m_BlockEntities; } - - /** Returns whether both light arrays stored in this object are valid. */ - bool IsLightValid(void) const { return m_IsLightValid; } - - /** Returns whether the heightmap stored in this object is valid. */ - bool IsHeightMapValid(void) const { return m_IsHeightMapValid; } - - /** Returns whether the biomes stored in this object are valid. */ - bool AreBiomesValid(void) const { return m_AreBiomesValid; } - - /** Returns whether the chunk should be marked as dirty after its data is set. - Used by the generator to save chunks after generating. */ - bool ShouldMarkDirty(void) const { return m_ShouldMarkDirty; } - - /** Marks the biomes stored in this object as valid. */ - void MarkBiomesValid(void) { m_AreBiomesValid = true; } - - /** Calculates the heightmap based on the contained blocktypes and marks it valid. */ - void CalculateHeightMap(void); - - /** Removes the block entities that don't have a proper blocktype at their corresponding coords. */ - void RemoveInvalidBlockEntities(void); - -protected: - int m_ChunkX; - int m_ChunkZ; - - cListAllocationPool<cChunkData::sChunkSection> m_Pool; - cChunkData m_ChunkData; - cChunkDef::HeightMap m_HeightMap; - cChunkDef::BiomeMap m_Biomes; - cEntityList m_Entities; - cBlockEntities m_BlockEntities; - - bool m_IsLightValid; - bool m_IsHeightMapValid; - bool m_AreBiomesValid; - bool m_ShouldMarkDirty; + bool IsLightValid; }; diff --git a/src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h b/src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h index db622520a..70a546b0e 100644 --- a/src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h +++ b/src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h @@ -160,6 +160,7 @@ namespace RedstoneWireHandler if ( // IsYMTerracingBlocked (i.e. check block above lower terracing position, a.k.a. just the plain adjacent) (!cBlockInfo::IsSolid(LateralBlock) || cBlockInfo::IsTransparent(LateralBlock)) && + (Adjacent.y > 0) && (NeighbourChunk->GetBlock(Adjacent + OffsetYM) == E_BLOCK_REDSTONE_WIRE) // Only terrace YM with another wire ) { diff --git a/src/World.cpp b/src/World.cpp index 1ade4d69c..d2aab1ac4 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -962,17 +962,6 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La // Call the plugins cPluginManager::Get()->CallHookWorldTick(*this, a_Dt, a_LastTickDurationMSec); - // Set any chunk data that has been queued for setting: - cSetChunkDataPtrs SetChunkDataQueue; - { - cCSLock Lock(m_CSSetChunkDataQueue); - std::swap(SetChunkDataQueue, m_SetChunkDataQueue); - } - for (cSetChunkDataPtrs::iterator itr = SetChunkDataQueue.begin(), end = SetChunkDataQueue.end(); itr != end; ++itr) - { - SetChunkData(**itr); - } // for itr - SetChunkDataQueue[] - m_WorldAge += a_Dt; if (m_IsDaylightCycleEnabled) @@ -997,6 +986,7 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La } } + TickQueuedChunkDataSets(); TickQueuedBlocks(); m_ChunkMap.Tick(a_Dt); TickMobs(a_Dt); @@ -1151,6 +1141,48 @@ void cWorld::TickMobs(std::chrono::milliseconds a_Dt) +void cWorld::TickQueuedChunkDataSets() +{ + decltype(m_SetChunkDataQueue) SetChunkDataQueue; + { + cCSLock Lock(m_CSSetChunkDataQueue); + SetChunkDataQueue = std::move(m_SetChunkDataQueue); + } + + // Set any chunk data that has been queued for setting: + for (auto & Item : SetChunkDataQueue) + { + // A copy of the chunk coordinates since we're moving Item. + const auto Chunk = Item.Chunk; + + // Set the data: + m_ChunkMap.SetChunkData(std::move(Item)); + + // If a client is requesting this chunk, send it to them: + cChunkSender & ChunkSender = m_ChunkSender; + DoWithChunk( + Chunk.m_ChunkX, Chunk.m_ChunkZ, + [&ChunkSender](cChunk & a_Chunk) + { + if (a_Chunk.HasAnyClients()) + { + ChunkSender.QueueSendChunkTo( + a_Chunk.GetPosX(), + a_Chunk.GetPosZ(), + cChunkSender::Priority::Medium, + a_Chunk.GetAllClients() + ); + } + return true; + } + ); + } // for itr - SetChunkDataQueue[] +} + + + + + void cWorld::TickQueuedEntityAdditions(void) { decltype(m_EntitiesToAdd) EntitiesToAdd; @@ -2236,22 +2268,8 @@ void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) -void cWorld::QueueSetChunkData(cSetChunkDataPtr a_SetChunkData) +void cWorld::QueueSetChunkData(struct SetChunkData && a_SetChunkData) { - // Validate biomes, if needed: - if (!a_SetChunkData->AreBiomesValid()) - { - // The biomes are not assigned, get them from the generator: - m_Generator.GenerateBiomes({a_SetChunkData->GetChunkX(), a_SetChunkData->GetChunkZ()}, a_SetChunkData->GetBiomes()); - a_SetChunkData->MarkBiomesValid(); - } - - // Validate heightmap, if needed: - if (!a_SetChunkData->IsHeightMapValid()) - { - a_SetChunkData->CalculateHeightMap(); - } - // Store a copy of the data in the queue: // TODO: If the queue is too large, wait for it to get processed. Not likely, though. cCSLock Lock(m_CSSetChunkDataQueue); @@ -2262,39 +2280,6 @@ void cWorld::QueueSetChunkData(cSetChunkDataPtr a_SetChunkData) -void cWorld::SetChunkData(cSetChunkData & a_SetChunkData) -{ - ASSERT(a_SetChunkData.AreBiomesValid()); - ASSERT(a_SetChunkData.IsHeightMapValid()); - - m_ChunkMap.SetChunkData(a_SetChunkData); - - // If a client is requesting this chunk, send it to them: - int ChunkX = a_SetChunkData.GetChunkX(); - int ChunkZ = a_SetChunkData.GetChunkZ(); - cChunkSender & ChunkSender = m_ChunkSender; - DoWithChunk( - ChunkX, ChunkZ, - [&ChunkSender] (cChunk & a_Chunk) -> bool - { - if (a_Chunk.HasAnyClients()) - { - ChunkSender.QueueSendChunkTo( - a_Chunk.GetPosX(), - a_Chunk.GetPosZ(), - cChunkSender::Priority::Medium, - a_Chunk.GetAllClients() - ); - } - return true; - } - ); -} - - - - - void cWorld::ChunkLighted( int a_ChunkX, int a_ChunkZ, const cChunkDef::BlockNibbles & a_BlockLight, @@ -2317,15 +2302,6 @@ bool cWorld::GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback -bool cWorld::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) -{ - return m_ChunkMap.GetChunkBlockTypes(a_ChunkX, a_ChunkZ, a_BlockTypes); -} - - - - - bool cWorld::IsChunkQueued(int a_ChunkX, int a_ChunkZ) const { return m_ChunkMap.IsChunkQueued(a_ChunkX, a_ChunkZ); @@ -3260,16 +3236,20 @@ void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc cChunkDef::BlockNibbles BlockMetas; a_ChunkDesc.CompressBlockMetas(BlockMetas); - auto SetChunkData = std::make_unique<cSetChunkData>( - a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), - a_ChunkDesc.GetBlockTypes(), BlockMetas, - nullptr, nullptr, // We don't have lighting, chunk will be lighted when needed - &a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(), - std::move(a_ChunkDesc.GetEntities()), std::move(a_ChunkDesc.GetBlockEntities()), - true - ); - SetChunkData->RemoveInvalidBlockEntities(); - m_World->QueueSetChunkData(std::move(SetChunkData)); + struct SetChunkData Data({ a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ() }); + { + Data.BlockData.SetAll(a_ChunkDesc.GetBlockTypes(), BlockMetas); + + std::copy(a_ChunkDesc.GetBiomeMap(), a_ChunkDesc.GetBiomeMap() + std::size(a_ChunkDesc.GetBiomeMap()), Data.BiomeMap); + std::copy(a_ChunkDesc.GetHeightMap(), a_ChunkDesc.GetHeightMap() + std::size(a_ChunkDesc.GetHeightMap()), Data.HeightMap); + + Data.Entities = std::move(a_ChunkDesc.GetEntities()); + Data.BlockEntities = std::move(a_ChunkDesc.GetBlockEntities()); + + Data.IsLightValid = false; + } + + m_World->QueueSetChunkData(std::move(Data)); } diff --git a/src/World.h b/src/World.h index 9d68ff47d..ec114cedc 100644 --- a/src/World.h +++ b/src/World.h @@ -49,14 +49,12 @@ class cHopperEntity; class cNoteEntity; class cMobHeadEntity; class cCompositeChat; -class cSetChunkData; class cDeadlockDetect; class cUUID; -typedef std::list< cPlayer * > cPlayerList; +struct SetChunkData; -typedef std::unique_ptr<cSetChunkData> cSetChunkDataPtr; -typedef std::vector<cSetChunkDataPtr> cSetChunkDataPtrs; +typedef std::list< cPlayer * > cPlayerList; @@ -240,8 +238,8 @@ public: void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); /** Puts the chunk data into a queue to be set into the chunkmap in the tick thread. - If the chunk data doesn't contain valid biomes, the biomes are calculated before adding the data into the queue. */ - void QueueSetChunkData(cSetChunkDataPtr a_SetChunkData); + Modifies the a_SetChunkData - moves the entities contained in it into the queue. */ + void QueueSetChunkData(SetChunkData && a_SetChunkData); void ChunkLighted( int a_ChunkX, int a_ChunkZ, @@ -253,9 +251,6 @@ public: Returns true if the chunk was reported successfully, false if not (chunk not present or callback failed). */ bool GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback) const; - /** Gets the chunk's blocks, only the block types */ - bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes); - /** Returns true iff the chunk is in the loader / generator queue. */ bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) const; @@ -1263,7 +1258,7 @@ private: cCriticalSection m_CSSetChunkDataQueue; /** Queue for the chunk data to be set into m_ChunkMap by the tick thread. Protected by m_CSSetChunkDataQueue */ - cSetChunkDataPtrs m_SetChunkDataQueue; + std::vector<SetChunkData> m_SetChunkDataQueue; void Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); @@ -1273,6 +1268,9 @@ private: /** Handles the mob spawning / moving / destroying each tick */ void TickMobs(std::chrono::milliseconds a_Dt); + /** Sets the chunk data queued in the m_SetChunkDataQueue queue into their chunk. */ + void TickQueuedChunkDataSets(); + /** Adds the entities queued in the m_EntitiesToAdd queue into their chunk. If the entity was a player, he is also added to the m_Players list. */ void TickQueuedEntityAdditions(void); @@ -1307,10 +1305,6 @@ private: /** Sets mob spawning values if nonexistant to their dimension specific defaults */ void InitializeAndLoadMobSpawningValues(cIniFile & a_IniFile); - /** Sets the specified chunk data into the chunkmap. Called in the tick thread. - Modifies the a_SetChunkData - moves the entities contained in it into the chunk. */ - void SetChunkData(cSetChunkData & a_SetChunkData); - /** Checks if the sapling at the specified block coord is a part of a large-tree sapling (2x2). If so, adjusts the coords so that they point to the northwest (XM ZM) corner of the sapling area and returns true. Returns false if not a part of large-tree sapling. */ diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp index d5b9fd0f7..df93e21e4 100644 --- a/src/WorldStorage/FastNBT.cpp +++ b/src/WorldStorage/FastNBT.cpp @@ -637,6 +637,18 @@ void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, +void cFastNBTWriter::AddByteArray(const AString & a_Name, size_t a_NumElements, unsigned char a_Value) +{ + TagCommon(a_Name, TAG_ByteArray); + UInt32 len = htonl(static_cast<UInt32>(a_NumElements)); + m_Result.append(reinterpret_cast<const std::byte *>(&len), 4); + m_Result.append(a_NumElements, std::byte(a_Value)); +} + + + + + void cFastNBTWriter::AddIntArray(const AString & a_Name, const Int32 * a_Value, size_t a_NumElements) { TagCommon(a_Name, TAG_IntArray); diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h index 0df520e21..d9c388179 100644 --- a/src/WorldStorage/FastNBT.h +++ b/src/WorldStorage/FastNBT.h @@ -340,6 +340,7 @@ public: void AddDouble (const AString & a_Name, double a_Value); void AddString (const AString & a_Name, std::string_view a_Value); void AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements); + void AddByteArray(const AString & a_Name, size_t a_NumElements, unsigned char a_Value); void AddIntArray (const AString & a_Name, const Int32 * a_Value, size_t a_NumElements); void AddByteArray(const AString & a_Name, const AString & a_Value) diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 2542cd2da..714c65a91 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -53,16 +53,14 @@ /** Collects and stores the chunk data via the cChunkDataCallback interface */ -class SerializerCollector: +class SerializerCollector final : public cChunkDataCopyCollector { public: // The data collected from the chunk: - cChunkDef::BiomeMap mBiomes; - UInt8 mVanillaBiomes[cChunkDef::Width * cChunkDef::Width]; - int mVanillaHeightMap[cChunkDef::Width * cChunkDef::Width]; - bool mBiomesAreValid; + UInt8 Biomes[cChunkDef::Width * cChunkDef::Width]; + int Heights[cChunkDef::Width * cChunkDef::Width]; /** True if a tag has been opened in the callbacks and not yet closed. */ bool mIsTagOpen; @@ -84,7 +82,6 @@ public: SerializerCollector(cFastNBTWriter & aWriter): - mBiomesAreValid(false), mIsTagOpen(false), mHasHadEntity(false), mHasHadBlockEntity(false), @@ -106,14 +103,13 @@ public: - virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) override + virtual void HeightMap(const cChunkDef::HeightMap & a_HeightMap) override { - for (int RelX = 0; RelX < cChunkDef::Width; RelX++) + for (int RelZ = 0; RelZ < cChunkDef::Width; RelZ++) { - for (int RelZ = 0; RelZ < cChunkDef::Width; RelZ++) + for (int RelX = 0; RelX < cChunkDef::Width; RelX++) { - int Height = cChunkDef::GetHeight(*a_HeightMap, RelX, RelZ); - mVanillaHeightMap[(RelZ << 4) | RelX] = Height; + Heights[RelX + RelZ * cChunkDef::Width] = cChunkDef::GetHeight(a_HeightMap, RelX, RelZ); } } } @@ -122,15 +118,14 @@ public: - virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override + virtual void BiomeMap(const cChunkDef::BiomeMap & a_BiomeMap) override { - memcpy(mBiomes, a_BiomeMap, sizeof(mBiomes)); - for (size_t i = 0; i < ARRAYCOUNT(mBiomes); i++) + for (size_t i = 0; i < ARRAYCOUNT(Biomes); i++) { - if ((*a_BiomeMap)[i] < 255) + if (a_BiomeMap[i] < 255) { // Normal MC biome, copy as-is: - mVanillaBiomes[i] = static_cast<Byte>((*a_BiomeMap)[i]); + Biomes[i] = static_cast<Byte>(a_BiomeMap[i]); } else { @@ -139,7 +134,6 @@ public: return; } } // for i - mBiomeMap[] - mBiomesAreValid = true; } @@ -252,13 +246,6 @@ public: mWriter.EndList(); } - // If light not valid, reset it to defaults: - if (!mIsLightValid) - { - m_Data.FillBlockLight(0x00); - m_Data.FillSkyLight(0x0f); - } - // Check if "Entity" and "TileEntities" lists exists. MCEdit requires this. if (!mHasHadEntity) { @@ -1187,40 +1174,57 @@ void NBTChunkSerializer::Serialize(const cWorld & aWorld, cChunkCoords aCoords, ASSERT(Result); serializer.Finish(); // Close NBT tags - // Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray): - if (serializer.mBiomesAreValid) - { - aWriter.AddByteArray("Biomes", reinterpret_cast<const char *>(serializer.mVanillaBiomes), ARRAYCOUNT(serializer.mVanillaBiomes)); - aWriter.AddIntArray ("MCSBiomes", reinterpret_cast<const int *>(serializer.mBiomes), ARRAYCOUNT(serializer.mBiomes)); - } + // Save biomes: + aWriter.AddByteArray("Biomes", reinterpret_cast<const char *>(serializer.Biomes), ARRAYCOUNT(serializer.Biomes)); // Save heightmap (Vanilla require this): - aWriter.AddIntArray("HeightMap", reinterpret_cast<const int *>(serializer.mVanillaHeightMap), ARRAYCOUNT(serializer.mVanillaHeightMap)); + aWriter.AddIntArray("HeightMap", reinterpret_cast<const int *>(serializer.Heights), ARRAYCOUNT(serializer.Heights)); // Save blockdata: aWriter.BeginList("Sections", TAG_Compound); - for (size_t Y = 0; Y != cChunkData::NumSections; ++Y) + ChunkDef::ForEachSection(serializer.m_BlockData, serializer.m_LightData, [&aWriter](const auto Y, const auto Blocks, const auto Metas, const auto BlockLights, const auto SkyLights) { - auto section = serializer.m_Data.GetSection(Y); - if (section == nullptr) + aWriter.BeginCompound(""); + + if (Blocks != nullptr) { - continue; + aWriter.AddByteArray("Blocks", reinterpret_cast<const char *>(Blocks->data()), Blocks->size()); + } + else + { + aWriter.AddByteArray("Blocks", ChunkBlockData::SectionBlockCount, ChunkBlockData::DefaultValue); } - aWriter.BeginCompound(""); - aWriter.AddByteArray("Blocks", reinterpret_cast<const char *>(section->m_BlockTypes), ARRAYCOUNT(section->m_BlockTypes)); - aWriter.AddByteArray("Data", reinterpret_cast<const char *>(section->m_BlockMetas), ARRAYCOUNT(section->m_BlockMetas)); + if (Metas != nullptr) + { + aWriter.AddByteArray("Data", reinterpret_cast<const char *>(Metas->data()), Metas->size()); + } + else + { + aWriter.AddByteArray("Data", ChunkBlockData::SectionMetaCount, ChunkBlockData::DefaultMetaValue); + } + + if (BlockLights != nullptr) + { + aWriter.AddByteArray("BlockLight", reinterpret_cast<const char *>(BlockLights->data()), BlockLights->size()); + } + else + { + aWriter.AddByteArray("BlockLight", ChunkLightData::SectionLightCount, ChunkLightData::DefaultBlockLightValue); + } - #ifdef DEBUG_SKYLIGHT - aWriter.AddByteArray("BlockLight", reinterpret_cast<const char *>(section->m_BlockSkyLight), ARRAYCOUNT(section->m_BlockSkyLight)); - #else - aWriter.AddByteArray("BlockLight", reinterpret_cast<const char *>(section->m_BlockLight), ARRAYCOUNT(section->m_BlockLight)); - #endif + if (SkyLights != nullptr) + { + aWriter.AddByteArray("SkyLight", reinterpret_cast<const char *>(SkyLights->data()), SkyLights->size()); + } + else + { + aWriter.AddByteArray("SkyLight", ChunkLightData::SectionLightCount, ChunkLightData::DefaultSkyLightValue); + } - aWriter.AddByteArray("SkyLight", reinterpret_cast<const char *>(section->m_BlockSkyLight), ARRAYCOUNT(section->m_BlockSkyLight)); aWriter.AddByte("Y", static_cast<unsigned char>(Y)); aWriter.EndCompound(); - } + }); aWriter.EndList(); // "Sections" // Store the information that the lighting is valid. diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 78320d636..e2a9b1539 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -353,16 +353,7 @@ Compression::Result cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk) bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT, const ContiguousByteBufferView a_RawChunkData) { - // The data arrays, in MCA-native y / z / x ordering (will be reordered for the final chunk data) - cChunkDef::BlockTypes BlockTypes; - cChunkDef::BlockNibbles MetaData; - cChunkDef::BlockNibbles BlockLight; - cChunkDef::BlockNibbles SkyLight; - - memset(BlockTypes, 0, sizeof(BlockTypes)); - memset(MetaData, 0, sizeof(MetaData)); - memset(SkyLight, 0xff, sizeof(SkyLight)); // By default, data not present in the NBT means air, which means full skylight - memset(BlockLight, 0x00, sizeof(BlockLight)); + struct SetChunkData Data(a_Chunk); // Load the blockdata, blocklight and skylight: int Level = a_NBT.FindChildByName(0, "Level"); @@ -371,12 +362,14 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Missing NBT tag: Level", a_RawChunkData); return false; } + int Sections = a_NBT.FindChildByName(Level, "Sections"); if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List)) { ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Missing NBT tag: Sections", a_RawChunkData); return false; } + eTagType SectionsType = a_NBT.GetChildrenType(Sections); if ((SectionsType != TAG_Compound) && (SectionsType != TAG_End)) { @@ -385,39 +378,56 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT } for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child)) { - int y = 0; - int SectionY = a_NBT.FindChildByName(Child, "Y"); - if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte)) + const int SectionYTag = a_NBT.FindChildByName(Child, "Y"); + if ((SectionYTag < 0) || (a_NBT.GetType(SectionYTag) != TAG_Byte)) { - continue; + ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "NBT tag missing or has wrong: Y", a_RawChunkData); + return false; } - y = a_NBT.GetByte(SectionY); - if ((y < 0) || (y > 15)) + + const int Y = a_NBT.GetByte(SectionYTag); + if ((Y < 0) || (Y > static_cast<int>(cChunkDef::NumSections - 1))) { - continue; + ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "NBT tag exceeds chunk bounds: Y", a_RawChunkData); + return false; + } + + const auto + BlockData = GetSectionData(a_NBT, Child, "Blocks", ChunkBlockData::SectionBlockCount), + MetaData = GetSectionData(a_NBT, Child, "Data", ChunkBlockData::SectionMetaCount), + BlockLightData = GetSectionData(a_NBT, Child, "BlockLight", ChunkLightData::SectionLightCount), + SkyLightData = GetSectionData(a_NBT, Child, "SkyLight", ChunkLightData::SectionLightCount); + if ((BlockData != nullptr) && (MetaData != nullptr) && (SkyLightData != nullptr) && (BlockLightData != nullptr)) + { + Data.BlockData.SetSection(*reinterpret_cast<const ChunkBlockData::SectionType *>(BlockData), *reinterpret_cast<const ChunkBlockData::SectionMetaType *>(MetaData), static_cast<size_t>(Y)); + Data.LightData.SetSection(*reinterpret_cast<const ChunkLightData::SectionType *>(BlockLightData), *reinterpret_cast<const ChunkLightData::SectionType *>(SkyLightData), static_cast<size_t>(Y)); + } + else + { + ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Missing chunk block/light data", a_RawChunkData); + return false; } - CopyNBTData(a_NBT, Child, "Blocks", reinterpret_cast<char *>(&(BlockTypes[y * 4096])), 4096); - CopyNBTData(a_NBT, Child, "Data", reinterpret_cast<char *>(&(MetaData[y * 2048])), 2048); - CopyNBTData(a_NBT, Child, "SkyLight", reinterpret_cast<char *>(&(SkyLight[y * 2048])), 2048); - CopyNBTData(a_NBT, Child, "BlockLight", reinterpret_cast<char *>(&(BlockLight[y * 2048])), 2048); } // for itr - LevelSections[] - // Load the biomes from NBT, if present and valid. First try MCS-style, then Vanilla-style: - cChunkDef::BiomeMap BiomeMap; - cChunkDef::BiomeMap * Biomes = LoadBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "MCSBiomes")); - if (Biomes == nullptr) + // Load the biomes from NBT, if present and valid: + if (!LoadBiomeMapFromNBT(Data.BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "Biomes"))) + { + ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Missing chunk biome data", a_RawChunkData); + return false; + } + + // Height map too: + if (!LoadHeightMapFromNBT(Data.HeightMap, a_NBT, a_NBT.FindChildByName(Level, "HeightMap"))) { - // MCS-style biomes not available, load vanilla-style: - Biomes = LoadVanillaBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "Biomes")); + ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Missing chunk height data", a_RawChunkData); + return false; } // Load the entities from NBT: - cEntityList Entities; - cBlockEntities BlockEntities; - LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities")); - LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"), BlockTypes, MetaData); + LoadEntitiesFromNBT (Data.Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities")); + LoadBlockEntitiesFromNBT(Data.BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"), Data.BlockData); - bool IsLightValid = (a_NBT.FindChildByName(Level, "MCSIsLightValid") > 0); + Data.IsLightValid = (a_NBT.FindChildByName(Level, "MCSIsLightValid") > 0); /* // Uncomment this block for really cool stuff :) @@ -455,16 +465,7 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT } // for y //*/ - auto SetChunkData = std::make_unique<cSetChunkData>( - a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, - BlockTypes, MetaData, - IsLightValid ? BlockLight : nullptr, - IsLightValid ? SkyLight : nullptr, - nullptr, Biomes, - std::move(Entities), std::move(BlockEntities), - false - ); - m_World->QueueSetChunkData(std::move(SetChunkData)); + m_World->QueueSetChunkData(std::move(Data)); return true; } @@ -472,69 +473,66 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT -void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, size_t a_Length) +bool cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap & a_BiomeMap, const cParsedNBT & a_NBT, const int a_TagIdx) { - int Child = a_NBT.FindChildByName(a_Tag, a_ChildName); - if ((Child >= 0) && (a_NBT.GetType(Child) == TAG_ByteArray) && (a_NBT.GetDataLength(Child) == a_Length)) + if ( + (a_TagIdx < 0) || + (a_NBT.GetType(a_TagIdx) != TAG_ByteArray) || + (a_NBT.GetDataLength(a_TagIdx) != std::size(a_BiomeMap)) + ) { - memcpy(a_Destination, a_NBT.GetData(Child), a_Length); + return false; } -} - - - - -cChunkDef::BiomeMap * cWSSAnvil::LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx) -{ - if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_ByteArray)) - { - return nullptr; - } - if (a_NBT.GetDataLength(a_TagIdx) != 16 * 16) + const auto * const BiomeData = a_NBT.GetData(a_TagIdx); + for (size_t i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) { - // The biomes stored don't match in size - return nullptr; - } - const unsigned char * VanillaBiomeData = reinterpret_cast<const unsigned char *>(a_NBT.GetData(a_TagIdx)); - for (size_t i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++) - { - if ((VanillaBiomeData)[i] == 0xff) + if (BiomeData[i] > std::byte(EMCSBiome::biMaxVariantBiome)) { - // Unassigned biomes - return nullptr; + // Unassigned biomes: + return false; } - (*a_BiomeMap)[i] = static_cast<EMCSBiome>(VanillaBiomeData[i]); + + a_BiomeMap[i] = static_cast<EMCSBiome>(BiomeData[i]); } - return a_BiomeMap; + + return true; } -cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx) +bool cWSSAnvil::LoadHeightMapFromNBT(cChunkDef::HeightMap & a_HeightMap, const cParsedNBT & a_NBT, const int a_TagIdx) { - if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_IntArray)) + if ( + (a_TagIdx < 0) || + (a_NBT.GetType(a_TagIdx) != TAG_IntArray) || + (a_NBT.GetDataLength(a_TagIdx) != (4 * std::size(a_HeightMap))) + ) { - return nullptr; - } - if (a_NBT.GetDataLength(a_TagIdx) != sizeof(*a_BiomeMap)) - { - // The biomes stored don't match in size - return nullptr; + return false; } - const auto * BiomeData = a_NBT.GetData(a_TagIdx); - for (size_t i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++) + + const auto * const HeightData = a_NBT.GetData(a_TagIdx); + for (int RelZ = 0; RelZ < cChunkDef::Width; RelZ++) { - (*a_BiomeMap)[i] = static_cast<EMCSBiome>(GetBEInt(&BiomeData[i * 4])); - if ((*a_BiomeMap)[i] == 0xff) + for (int RelX = 0; RelX < cChunkDef::Width; RelX++) { - // Unassigned biomes - return nullptr; + const int Index = 4 * (RelX + RelZ * cChunkDef::Width); + const int Height = GetBEInt(HeightData + Index); + + if (Height > std::numeric_limits<HEIGHTTYPE>::max()) + { + // Invalid data: + return false; + } + + cChunkDef::SetHeight(a_HeightMap, RelX, RelZ, static_cast<HEIGHTTYPE>(Height)); } } - return a_BiomeMap; + + return true; } @@ -575,7 +573,7 @@ void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entities, const cParsedNBT & -void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntities & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) +void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntities & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, const ChunkBlockData & a_BlockData) { if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List)) { @@ -596,11 +594,11 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntities & a_BlockEntities, const LOGWARNING("Bad block entity, missing the coords. Will be ignored."); continue; } - auto relPos = cChunkDef::AbsoluteToRelative(absPos); + const auto relPos = cChunkDef::AbsoluteToRelative(absPos); // Load the proper BlockEntity type based on the block type: - BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, relPos); - NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, relPos); + const auto BlockType = a_BlockData.GetBlock(relPos); + const auto BlockMeta = a_BlockData.GetMeta(relPos); OwnedBlockEntity Entity; try @@ -3990,6 +3988,20 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, ContiguousB +const std::byte * cWSSAnvil::GetSectionData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, size_t a_Length) +{ + int Child = a_NBT.FindChildByName(a_Tag, a_ChildName); + if ((Child >= 0) && (a_NBT.GetType(Child) == TAG_ByteArray) && (a_NBT.GetDataLength(Child) == a_Length)) + { + return a_NBT.GetData(Child); + } + return nullptr; +} + + + + + bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const ContiguousByteBufferView a_Data) { if (!OpenFile(false)) diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 1751c6761..d37bf2d25 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -24,6 +24,7 @@ class cMonster; class cProjectileEntity; class cHangingEntity; class cUUID; +class ChunkBlockData; @@ -106,6 +107,9 @@ protected: /** Gets chunk data from the correct file; locks file CS as needed */ bool GetChunkData(const cChunkCoords & a_Chunk, ContiguousByteBuffer & a_Data); + /** Copies a_Length bytes of data from the specified NBT Tag's Child into the a_Destination buffer */ + const std::byte * GetSectionData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, size_t a_Length); + /** Sets chunk data into the correct file; locks file CS as needed */ bool SetChunkData(const cChunkCoords & a_Chunk, ContiguousByteBufferView a_Data); @@ -119,17 +123,17 @@ protected: a_RawChunkData is the raw (compressed) chunk data, used for offloading when chunk loading fails. */ bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT, ContiguousByteBufferView a_RawChunkData); - /** Loads the chunk's biome map from vanilla-format; returns a_BiomeMap if biomes present and valid, nullptr otherwise */ - cChunkDef::BiomeMap * LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); + /** Loads the chunk's biome map into a_BiomeMap if biomes present and valid; returns false otherwise. */ + bool LoadBiomeMapFromNBT(cChunkDef::BiomeMap & a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); - /** Loads the chunk's biome map from MCS format; returns a_BiomeMap if biomes present and valid, nullptr otherwise */ - cChunkDef::BiomeMap * LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); + /** Loads the chunk's height map into a_HeightMap if heights present and valid; returns false otherwise. */ + bool LoadHeightMapFromNBT(cChunkDef::HeightMap & a_HeightMap, const cParsedNBT & a_NBT, int a_TagIdx); /** Loads the chunk's entities from NBT data (a_Tag is the Level\\Entities list tag; may be -1) */ void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_Tag); /** Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1) */ - void LoadBlockEntitiesFromNBT(cBlockEntities & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); + void LoadBlockEntitiesFromNBT(cBlockEntities & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, const ChunkBlockData & a_BlockData); /** Loads the data for a block entity from the specified NBT tag. Returns the loaded block entity, or nullptr upon failure. */ @@ -298,9 +302,6 @@ protected: /** Gets the correct MCA file either from cache or from disk, manages the m_MCAFiles cache; assumes m_CS is locked */ cMCAFile * LoadMCAFile(const cChunkCoords & a_Chunk); - /** Copies a_Length bytes of data from the specified NBT Tag's Child into the a_Destination buffer */ - void CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, size_t a_Length); - // cWSSchema overrides: virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; virtual bool SaveChunk(const cChunkCoords & a_Chunk) override; diff --git a/tests/ChunkData/ArraytoCoord.cpp b/tests/ChunkData/ArraytoCoord.cpp index 1fdaf3365..89fd8f731 100644 --- a/tests/ChunkData/ArraytoCoord.cpp +++ b/tests/ChunkData/ArraytoCoord.cpp @@ -8,100 +8,99 @@ /** Performs the entire ArrayToCoords test. */ static void test() { - - class cMockAllocationPool - : public cAllocationPool<cChunkData::sChunkSection> - { - virtual cChunkData::sChunkSection * Allocate() override - { - return new cChunkData::sChunkSection(); - } - - virtual void Free(cChunkData::sChunkSection * a_Ptr) override - { - delete a_Ptr; - } - - virtual bool DoIsEqual(const cAllocationPool<cChunkData::sChunkSection> &) const noexcept override - { - return false; - } - } Pool; { - - // Test first segment - cChunkData buffer(Pool); + // Test first segment (blocks) + ChunkBlockData buffer; BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]; memset(SrcBlockBuffer, 0x00, sizeof(SrcBlockBuffer)); SrcBlockBuffer[7 + (4 * 16) + (5 * 16 * 16)] = 0xcd; - buffer.SetBlockTypes(SrcBlockBuffer); - TEST_EQUAL(buffer.GetBlock({ 7, 5, 4 }), 0xcd); NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; memset(SrcNibbleBuffer, 0x00, sizeof(SrcNibbleBuffer)); SrcNibbleBuffer[(6 + (1 * 16) + (2 * 16 * 16)) / 2] = 0xe; - buffer.SetMetas(SrcNibbleBuffer); + + buffer.SetAll(SrcBlockBuffer, SrcNibbleBuffer); + + TEST_EQUAL(buffer.GetBlock({ 7, 5, 4 }), 0xcd); TEST_EQUAL(buffer.GetMeta({ 6, 2, 1 }), 0xe); + } - memset(SrcNibbleBuffer, 0x00, sizeof(SrcNibbleBuffer)); - SrcNibbleBuffer[(6 + (1 * 16) + (2 * 16 * 16)) / 2] = 0xe; - buffer.SetBlockLight(SrcNibbleBuffer); - TEST_EQUAL(buffer.GetBlockLight({ 6, 2, 1 }), 0xe); + { + // Test first segment (lights) + ChunkLightData buffer; + NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; memset(SrcNibbleBuffer, 0x00, sizeof(SrcNibbleBuffer)); SrcNibbleBuffer[(6 + (1 * 16) + (2 * 16 * 16)) / 2] = 0xe; - buffer.SetSkyLight(SrcNibbleBuffer); + + buffer.SetAll(SrcNibbleBuffer, SrcNibbleBuffer); + + TEST_EQUAL(buffer.GetBlockLight({ 6, 2, 1 }), 0xe); TEST_EQUAL(buffer.GetSkyLight({ 6, 2, 1 }), 0xe); } { - // test following segment - cChunkData buffer(Pool); + // test following segment (blocks) + ChunkBlockData buffer; BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]; memset(SrcBlockBuffer, 0x00, sizeof(SrcBlockBuffer)); SrcBlockBuffer[7 + (4 * 16) + (24 * 16 * 16)] = 0xcd; - buffer.SetBlockTypes(SrcBlockBuffer); - TEST_EQUAL(buffer.GetBlock({ 7, 24, 4 }), 0xcd); NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; memset(SrcNibbleBuffer, 0x00, sizeof(SrcNibbleBuffer)); SrcNibbleBuffer[(6 + (1 * 16) + (24 * 16 * 16)) / 2] = 0xe; - buffer.SetMetas(SrcNibbleBuffer); + + buffer.SetAll(SrcBlockBuffer, SrcNibbleBuffer); + + TEST_EQUAL(buffer.GetBlock({ 7, 24, 4 }), 0xcd); TEST_EQUAL(buffer.GetMeta({ 6, 24, 1 }), 0xe); + } - memset(SrcNibbleBuffer, 0x00, sizeof(SrcNibbleBuffer)); - SrcNibbleBuffer[(6 + 1 * 16 + 24 * 16 * 16) / 2] = 0xe; - buffer.SetBlockLight(SrcNibbleBuffer); - TEST_EQUAL(buffer.GetBlockLight({ 6, 24, 1 }), 0xe); + { + // test following segment (lights) + ChunkLightData buffer; - memset(SrcNibbleBuffer, 0xff, sizeof(SrcNibbleBuffer)); + NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; + memset(SrcNibbleBuffer, 0x00, sizeof(SrcNibbleBuffer)); SrcNibbleBuffer[(6 + (1 * 16) + (24 * 16 * 16)) / 2] = 0xe; - buffer.SetSkyLight(SrcNibbleBuffer); + + buffer.SetAll(SrcNibbleBuffer, SrcNibbleBuffer); + + TEST_EQUAL(buffer.GetBlockLight({ 6, 24, 1 }), 0xe); TEST_EQUAL(buffer.GetSkyLight({ 6, 24, 1 }), 0xe); } { - // test zeros - cChunkData buffer(Pool); + // test zeros (blocks) + ChunkBlockData buffer; BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]; memset(SrcBlockBuffer, 0x00, sizeof(SrcBlockBuffer)); - buffer.SetBlockTypes(SrcBlockBuffer); - TEST_EQUAL(buffer.GetBlock({ 7, 24, 4 }), 0x00); NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; memset(SrcNibbleBuffer, 0x00, sizeof(SrcNibbleBuffer)); - buffer.SetMetas(SrcNibbleBuffer); + + buffer.SetAll(SrcBlockBuffer, SrcNibbleBuffer); + + TEST_EQUAL(buffer.GetBlock({ 7, 24, 4 }), 0x00); TEST_EQUAL(buffer.GetMeta({ 6, 24, 1 }), 0x0); + } + + { + // test zeros (lights) + ChunkLightData buffer; + NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; memset(SrcNibbleBuffer, 0x00, sizeof(SrcNibbleBuffer)); - buffer.SetBlockLight(SrcNibbleBuffer); - TEST_EQUAL(buffer.GetBlockLight({ 6, 24, 1 }), 0x0); - memset(SrcNibbleBuffer, 0xff, sizeof(SrcNibbleBuffer)); - buffer.SetSkyLight(SrcNibbleBuffer); + NIBBLETYPE SrcNibbleBuffer2[16 * 16 * 256 / 2]; + memset(SrcNibbleBuffer2, 0xff, sizeof(SrcNibbleBuffer2)); + + buffer.SetAll(SrcNibbleBuffer, SrcNibbleBuffer2); + + TEST_EQUAL(buffer.GetBlockLight({ 6, 24, 1 }), 0x0); TEST_EQUAL(buffer.GetSkyLight({ 6, 24, 1 }), 0xf); } } diff --git a/tests/ChunkData/CMakeLists.txt b/tests/ChunkData/CMakeLists.txt index 705d46b35..8c2de41a1 100644 --- a/tests/ChunkData/CMakeLists.txt +++ b/tests/ChunkData/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(ChunkBuffer ${PROJECT_SOURCE_DIR}/src/ChunkData.cpp ${PROJECT_SOURCE target_link_libraries(ChunkBuffer PUBLIC fmt::fmt) -add_executable(creatable-exe creatable.cpp) +add_executable(creatable-exe Creatable.cpp) target_link_libraries(creatable-exe ChunkBuffer) add_test(NAME creatable-test COMMAND creatable-exe) @@ -20,19 +20,11 @@ add_executable(arraystocoords-exe ArraytoCoord.cpp) target_link_libraries(arraystocoords-exe ChunkBuffer) add_test(NAME arraystocoords-test COMMAND arraystocoords-exe) -add_executable(copyblocks-exe CopyBlocks.cpp) -target_link_libraries(copyblocks-exe ChunkBuffer) -add_test(NAME copyblocks-test COMMAND copyblocks-exe) - - - - # Put all test projects into a separate folder: set_target_properties( arraystocoords-exe coordinates-exe copies-exe - copyblocks-exe creatable-exe PROPERTIES FOLDER Tests/ChunkData ) diff --git a/tests/ChunkData/Coordinates.cpp b/tests/ChunkData/Coordinates.cpp index 97c3f3a15..3e7949662 100644 --- a/tests/ChunkData/Coordinates.cpp +++ b/tests/ChunkData/Coordinates.cpp @@ -9,26 +9,8 @@ /** Performs the entire cChunkData coordinates test. */ static void test() { - class cMockAllocationPool - : public cAllocationPool<cChunkData::sChunkSection> { - virtual cChunkData::sChunkSection * Allocate() override - { - return new cChunkData::sChunkSection(); - } - - virtual void Free(cChunkData::sChunkSection * a_Ptr) override - { - delete a_Ptr; - } - - virtual bool DoIsEqual(const cAllocationPool<cChunkData::sChunkSection> &) const noexcept override - { - return false; - } - } Pool; - { - cChunkData buffer(Pool); + ChunkBlockData buffer; // Empty chunks buffer.SetBlock({ 0, 0, 0 }, 0xAB); @@ -44,6 +26,26 @@ static void test() TEST_EQUAL(buffer.GetBlock({ 0, 32, 0 }), 0x0); TEST_EQUAL(buffer.GetMeta({ 0, 48, 0 }), 0x0); + // Out of range GetBlock + TEST_ASSERTS( + buffer.GetBlock({ -1, 0, 0 }); + ); + TEST_ASSERTS( + buffer.GetBlock({ 0, -1, 0 }); + ); + TEST_ASSERTS( + buffer.GetBlock({ 0, 0, -1 }); + ); + TEST_ASSERTS( + buffer.GetBlock({ 256, 0, 0 }); + ); + TEST_ASSERTS( + buffer.GetBlock({ 0, 256, 0 }); + ); + TEST_ASSERTS( + buffer.GetBlock({ 0, 0, 256 }); + ); + // Out of range SetBlock TEST_ASSERTS( buffer.SetBlock({ -1, 0, 0 }, 0) @@ -63,6 +65,27 @@ static void test() TEST_ASSERTS( buffer.SetBlock({ 0, 0, 256 }, 0) ); + + // Out of range GetMeta + TEST_ASSERTS( + buffer.GetMeta({ -1, 0, 0 }); + ); + TEST_ASSERTS( + buffer.GetMeta({ 0, -1, 0 }); + ); + TEST_ASSERTS( + buffer.GetMeta({ 0, 0, -1 }); + ); + TEST_ASSERTS( + buffer.GetMeta({ 256, 0, 0 }); + ); + TEST_ASSERTS( + buffer.GetMeta({ 0, 256, 0 }); + ); + TEST_ASSERTS( + buffer.GetMeta({ 0, 0, 256 }); + ); + // Out of range SetMeta TEST_ASSERTS( buffer.SetMeta({ -1, 0, 0 }, 0) @@ -82,26 +105,10 @@ static void test() TEST_ASSERTS( buffer.SetMeta({ 0, 0, 256 }, 0) ); - - // Reading out of range blocks should return air - TEST_EQUAL(buffer.GetBlock({ -1, 0, 0 }), 0); - TEST_EQUAL(buffer.GetBlock({ 0, -1, 0 }), 0); - TEST_EQUAL(buffer.GetBlock({ 0, 0, -1 }), 0); - TEST_EQUAL(buffer.GetBlock({ 256, 0, 0 }), 0); - TEST_EQUAL(buffer.GetBlock({ 0, 256, 0 }), 0); - TEST_EQUAL(buffer.GetBlock({ 0, 0, 256 }), 0); - - // Reading out of range metas should return 0 - TEST_EQUAL(buffer.GetMeta({ -1, 0, 0 }), 0); - TEST_EQUAL(buffer.GetMeta({ 0, -1, 0 }), 0); - TEST_EQUAL(buffer.GetMeta({ 0, 0, -1 }), 0); - TEST_EQUAL(buffer.GetMeta({ 256, 0, 0 }), 0); - TEST_EQUAL(buffer.GetMeta({ 0, 256, 0 }), 0); - TEST_EQUAL(buffer.GetMeta({ 0, 0, 256 }), 0); } { - cChunkData buffer(Pool); + ChunkBlockData buffer; // Zero's buffer.SetBlock({ 0, 0, 0 }, 0x0); @@ -118,9 +125,9 @@ static void test() { // Operator = - cChunkData buffer(Pool); + ChunkBlockData buffer; buffer.SetBlock({ 0, 0, 0 }, 0x42); - cChunkData copy(Pool); + ChunkBlockData copy; copy = std::move(buffer); TEST_EQUAL(copy.GetBlock({ 0, 0, 0 }), 0x42); } diff --git a/tests/ChunkData/Copies.cpp b/tests/ChunkData/Copies.cpp index 9b9cbbaf1..3555d4301 100644 --- a/tests/ChunkData/Copies.cpp +++ b/tests/ChunkData/Copies.cpp @@ -7,41 +7,50 @@ -/** Performs the entire Copies test. */ -static void test() +/** Helper that copies a data store into a contiguous flat array, filling in a default value for sections that aren't present. */ +template <class StoreType, typename GetType, typename DefaultType, typename OutType> +static void CopyAll(const StoreType & Data, GetType Getter, DefaultType Default, OutType & Out) { - LOGD("Test started"); + constexpr auto SectionCount = std::extent_v<OutType> / 16; - class cMockAllocationPool - : public cAllocationPool<cChunkData::sChunkSection> + for (size_t Y = 0; Y != 16; Y++) { - virtual cChunkData::sChunkSection * Allocate() override - { - return new cChunkData::sChunkSection(); - } + const auto Section = (Data.*Getter)(Y); + static_assert(SectionCount == std::tuple_size<std::remove_pointer_t<decltype(Section)>>::value, "Output array has wrong size"); - virtual void Free(cChunkData::sChunkSection * a_Ptr) override + if (Section == nullptr) { - delete a_Ptr; + std::fill_n(Out + Y * SectionCount, SectionCount, Default); } - - virtual bool DoIsEqual(const cAllocationPool<cChunkData::sChunkSection>&) const noexcept override + else { - return false; + std::copy(Section->begin(), Section->end(), Out + Y * SectionCount); } - } Pool; + } +} + + + + + +/** Performs the entire Copies test. */ +static void Test() +{ + LOGD("Test started"); + { - cChunkData buffer(Pool); + ChunkBlockData buffer; buffer.SetBlock({ 3, 1, 4 }, 0xDE); buffer.SetMeta({ 3, 1, 4 }, 0xA); - cChunkData copy(Pool); + ChunkBlockData copy; copy.Assign(buffer); TEST_EQUAL(copy.GetBlock({ 3, 1, 4 }), 0xDE); TEST_EQUAL(copy.GetMeta({ 3, 1, 4 }), 0xA); BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]; + NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]{}; for (int i = 0; i < 16 * 16 * 256; i += 4) { SrcBlockBuffer[i + 0] = 0xde; @@ -50,20 +59,21 @@ static void test() SrcBlockBuffer[i + 3] = 0xef; } - buffer.SetBlockTypes(SrcBlockBuffer); + buffer.SetAll(SrcBlockBuffer, SrcNibbleBuffer); BLOCKTYPE DstBlockBuffer[16 * 16 * 256]; - buffer.CopyBlockTypes(DstBlockBuffer); + CopyAll(buffer, &ChunkBlockData::GetSection, ChunkBlockData::DefaultValue, DstBlockBuffer); TEST_EQUAL(memcmp(SrcBlockBuffer, DstBlockBuffer, (16 * 16 * 256) - 1), 0); memset(SrcBlockBuffer, 0x00, 16 * 16 * 256); - buffer.SetBlockTypes(SrcBlockBuffer); - buffer.CopyBlockTypes(DstBlockBuffer); + buffer.SetAll(SrcBlockBuffer, SrcNibbleBuffer); + CopyAll(buffer, &ChunkBlockData::GetSection, ChunkBlockData::DefaultValue, DstBlockBuffer); TEST_EQUAL(memcmp(SrcBlockBuffer, DstBlockBuffer, (16 * 16 * 256) - 1), 0); } { - cChunkData buffer(Pool); + ChunkBlockData buffer; + BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]{}; NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; for (int i = 0; i < 16 * 16 * 256 / 2; i += 4) { @@ -73,19 +83,19 @@ static void test() SrcNibbleBuffer[i + 3] = 0xef; } - buffer.SetMetas(SrcNibbleBuffer); + buffer.SetAll(SrcBlockBuffer, SrcNibbleBuffer); NIBBLETYPE DstNibbleBuffer[16 * 16 * 256/ 2]; - buffer.CopyMetas(DstNibbleBuffer); + CopyAll(buffer, &ChunkBlockData::GetMetaSection, ChunkBlockData::DefaultMetaValue, DstNibbleBuffer); TEST_EQUAL(memcmp(SrcNibbleBuffer, DstNibbleBuffer, (16 * 16 * 256 / 2) - 1), 0); memset(SrcNibbleBuffer, 0x00, 16 * 16 * 256 /2); - buffer.SetMetas(SrcNibbleBuffer); - buffer.CopyMetas(DstNibbleBuffer); + buffer.SetAll(SrcBlockBuffer, SrcNibbleBuffer); + CopyAll(buffer, &ChunkBlockData::GetMetaSection, ChunkBlockData::DefaultMetaValue, DstNibbleBuffer); TEST_EQUAL(memcmp(SrcNibbleBuffer, DstNibbleBuffer, (16 * 16 * 256 / 2) - 1), 0); } { - cChunkData buffer(Pool); + ChunkLightData buffer; NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; for (int i = 0; i < 16 * 16 * 256 / 2; i += 4) @@ -96,19 +106,19 @@ static void test() SrcNibbleBuffer[i + 3] = 0xef; } - buffer.SetBlockLight(SrcNibbleBuffer); + buffer.SetAll(SrcNibbleBuffer, SrcNibbleBuffer); NIBBLETYPE DstNibbleBuffer[16 * 16 * 256 / 2]; - buffer.CopyBlockLight(DstNibbleBuffer); + CopyAll(buffer, &ChunkLightData::GetBlockLightSection, ChunkLightData::DefaultBlockLightValue, DstNibbleBuffer); TEST_EQUAL(memcmp(SrcNibbleBuffer, DstNibbleBuffer, (16 * 16 * 256 /2) - 1), 0); memset(SrcNibbleBuffer, 0x00, 16 * 16 * 256 /2); - buffer.SetBlockLight(SrcNibbleBuffer); - buffer.CopyBlockLight(DstNibbleBuffer); + buffer.SetAll(SrcNibbleBuffer, SrcNibbleBuffer); + CopyAll(buffer, &ChunkLightData::GetBlockLightSection, ChunkLightData::DefaultBlockLightValue, DstNibbleBuffer); TEST_EQUAL(memcmp(SrcNibbleBuffer, DstNibbleBuffer, (16 * 16 * 256 /2) - 1), 0); } { - cChunkData buffer(Pool); + ChunkLightData buffer; NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; for (int i = 0; i < 16 * 16 * 256 / 2; i += 4) @@ -119,38 +129,44 @@ static void test() SrcNibbleBuffer[i + 3] = 0xef; } - buffer.SetSkyLight(SrcNibbleBuffer); + buffer.SetAll(SrcNibbleBuffer, SrcNibbleBuffer); NIBBLETYPE DstNibbleBuffer[16 * 16 * 256/ 2]; - buffer.CopySkyLight(DstNibbleBuffer); + CopyAll(buffer, &ChunkLightData::GetSkyLightSection, ChunkLightData::DefaultSkyLightValue, DstNibbleBuffer); TEST_EQUAL(memcmp(SrcNibbleBuffer, DstNibbleBuffer, (16 * 16 * 256 / 2) - 1), 0); memset(SrcNibbleBuffer, 0xFF, 16 * 16 * 256 / 2); - buffer.SetSkyLight(SrcNibbleBuffer); - buffer.CopySkyLight(DstNibbleBuffer); + buffer.SetAll(SrcNibbleBuffer, SrcNibbleBuffer); + CopyAll(buffer, &ChunkLightData::GetSkyLightSection, ChunkLightData::DefaultSkyLightValue, DstNibbleBuffer); TEST_EQUAL(memcmp(SrcNibbleBuffer, DstNibbleBuffer, (16 * 16 * 256 / 2) - 1), 0); } { - cChunkData buffer(Pool); + ChunkBlockData buffer; BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]; memset(SrcBlockBuffer, 0x00, 16 * 16 * 256); BLOCKTYPE DstBlockBuffer[16 * 16 * 256]; - buffer.CopyBlockTypes(DstBlockBuffer); + CopyAll(buffer, &ChunkBlockData::GetSection, ChunkBlockData::DefaultValue, DstBlockBuffer); TEST_EQUAL(memcmp(SrcBlockBuffer, DstBlockBuffer, (16 * 16 * 256) - 1), 0); NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; memset(SrcNibbleBuffer, 0x00, 16 * 16 * 256 / 2); NIBBLETYPE DstNibbleBuffer[16 * 16 * 256 / 2]; - buffer.CopyMetas(DstNibbleBuffer); + CopyAll(buffer, &ChunkBlockData::GetMetaSection, ChunkBlockData::DefaultMetaValue, DstNibbleBuffer); TEST_EQUAL(memcmp(SrcNibbleBuffer, DstNibbleBuffer, (16 * 16 * 256 / 2) - 1), 0); + } + + { + ChunkLightData buffer; + NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; memset(SrcNibbleBuffer, 0x00, 16 * 16 * 256 / 2); - buffer.CopyBlockLight(DstNibbleBuffer); + NIBBLETYPE DstNibbleBuffer[16 * 16 * 256 / 2]; + CopyAll(buffer, &ChunkLightData::GetBlockLightSection, ChunkLightData::DefaultBlockLightValue, DstNibbleBuffer); TEST_EQUAL(memcmp(SrcNibbleBuffer, DstNibbleBuffer, (16 * 16 * 256 / 2) - 1), 0); memset(SrcNibbleBuffer, 0xFF, 16 * 16 * 256 / 2); - buffer.CopySkyLight(DstNibbleBuffer); + CopyAll(buffer, &ChunkLightData::GetSkyLightSection, ChunkLightData::DefaultSkyLightValue, DstNibbleBuffer); TEST_EQUAL(memcmp(SrcNibbleBuffer, DstNibbleBuffer, (16 * 16 * 256 / 2) - 1), 0); } } @@ -160,5 +176,5 @@ static void test() IMPLEMENT_TEST_MAIN("ChunkData Copies", - test() + Test() ) diff --git a/tests/ChunkData/CopyBlocks.cpp b/tests/ChunkData/CopyBlocks.cpp deleted file mode 100644 index c86a9e754..000000000 --- a/tests/ChunkData/CopyBlocks.cpp +++ /dev/null @@ -1,98 +0,0 @@ - -// CopyBlocks.cpp - -// Implements the test for cChunkData::CopyBlockTypes() range copying - - - - - -#include "Globals.h" -#include "../TestHelpers.h" -#include "ChunkData.h" - - - - - -/** Performs the entire CopyBlocks test. */ -static void test() -{ - // Set up a cChunkData with known contents - all blocks 0x01, all metas 0x02: - class cMockAllocationPool - : public cAllocationPool<cChunkData::sChunkSection> - { - virtual cChunkData::sChunkSection * Allocate() override - { - return new cChunkData::sChunkSection(); - } - - virtual void Free(cChunkData::sChunkSection * a_Ptr) override - { - delete a_Ptr; - } - - virtual bool DoIsEqual(const cAllocationPool<cChunkData::sChunkSection> &) const noexcept override - { - return false; - } - } Pool; - cChunkData Data(Pool); - cChunkDef::BlockTypes BlockTypes; - cChunkDef::BlockNibbles BlockMetas; - memset(BlockTypes, 0x01, sizeof(BlockTypes)); - memset(BlockMetas, 0x02, sizeof(BlockMetas)); - Data.SetBlockTypes(BlockTypes); - Data.SetMetas(BlockMetas); - - // Try to read varying amounts of blocktypes from the cChunkData. - // Verify that the exact amount of memory is copied, by copying to a larger buffer and checking its boundaries - BLOCKTYPE TestBuffer[5 * cChunkDef::NumBlocks]; - size_t WritePosIdx = 2 * cChunkDef::NumBlocks; - BLOCKTYPE * WritePosition = &TestBuffer[WritePosIdx]; - memset(TestBuffer, 0x03, sizeof(TestBuffer)); - size_t LastReportedStep = 1; - for (size_t idx = 0; idx < 5000; idx += 73) - { - if (idx / 500 != LastReportedStep) - { - printf("Testing index %u...\n", static_cast<unsigned>(idx)); - LastReportedStep = idx / 500; - } - - for (size_t len = 3; len < 700; len += 13) - { - Data.CopyBlockTypes(WritePosition, idx, len); - - // Verify the data copied: - for (size_t i = 0; i < len; i++) - { - TEST_EQUAL(WritePosition[i], 0x01); - } - // Verify the space before the copied data hasn't been changed: - for (size_t i = 0; i < WritePosIdx; i++) - { - TEST_EQUAL(TestBuffer[i], 0x03); - } - // Verify the space after the copied data hasn't been changed: - for (size_t i = WritePosIdx + idx + len; i < ARRAYCOUNT(TestBuffer); i++) - { - TEST_EQUAL(TestBuffer[i], 0x03); - } - - // Re-initialize the buffer for the next test: - for (size_t i = 0; i < len; i++) - { - WritePosition[i] = 0x03; - } - } // for len - } // for idx -} - - - - - -IMPLEMENT_TEST_MAIN("ChunkData CopyBlocks", - test() -) diff --git a/tests/ChunkData/Creatable.cpp b/tests/ChunkData/Creatable.cpp new file mode 100644 index 000000000..278885b9d --- /dev/null +++ b/tests/ChunkData/Creatable.cpp @@ -0,0 +1,34 @@ + +#include "Globals.h" +#include "../TestHelpers.h" +#include "ChunkData.h" + + + + + +static void Test() +{ + LOGD("Test started"); + + ChunkBlockData train; + ChunkLightData buffer; + + /* + + /-|===D + / |===D + /---| + / | + ====/=====|=============== + + */ +} + + + + + +IMPLEMENT_TEST_MAIN("ChunkData Creatable", + Test() +); diff --git a/tests/ChunkData/creatable.cpp b/tests/ChunkData/creatable.cpp deleted file mode 100644 index 9767c31b4..000000000 --- a/tests/ChunkData/creatable.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -#include "Globals.h" -#include "ChunkData.h" - -int main(int argc, char** argv) -{ - LOGD("Test started"); - - class cMockAllocationPool - : public cAllocationPool<cChunkData::sChunkSection> - { - virtual cChunkData::sChunkSection * Allocate() override - { - return new cChunkData::sChunkSection(); - } - - virtual void Free(cChunkData::sChunkSection * a_Ptr) override - { - delete a_Ptr; - } - - virtual bool DoIsEqual(const cAllocationPool<cChunkData::sChunkSection> &) const noexcept override - { - return false; - } - } Pool; - cChunkData buffer(Pool); - return 0; -} |