1
0

Prepare ChunkData for BlockState storage (#5105)

* Rename ChunkData Creatable test

* Add missing Y-check in RedstoneWireHandler

* Remove ChunkDef.h dependency in Scoreboard

* Prepare ChunkData for BlockState storage

+ Split chunk block, meta, block & sky light storage
+ Load the height map from disk
- Reduce duplicated code in ChunkData
- Remove saving MCSBiomes, there aren't any
- Remove the allocation pool, ref #4315, #3864

* fixed build

* fixed test

* fixed the debug compile

Co-authored-by: 12xx12 <44411062+12xx12@users.noreply.github.com>
This commit is contained in:
Tiger Wang 2021-03-05 13:03:55 +00:00 committed by GitHub
parent 5fa45182e8
commit 868cd94ee9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1106 additions and 2203 deletions

View File

@ -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);
}
};

View File

@ -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

View File

@ -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;
} ;

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -13,37 +13,37 @@
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;
return
{
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))
};
}
bool IsCompressed(const size_t ElementCount)
{
return ElementCount != ChunkBlockData::SectionBlockCount;
}
template <size_t ElementCount, typename ValueType>
ValueType UnpackDefaultValue(const ValueType DefaultValue)
{
if (IsCompressed(ElementCount))
{
return DefaultValue & 0xF;
}
return DefaultValue;
}
} // namespace (anonymous)
@ -51,54 +51,16 @@ namespace
cChunkData::cChunkData(cAllocationPool<cChunkData::sChunkSection> & a_Pool):
m_Sections(),
m_Pool(a_Pool)
template<class ElementType, size_t ElementCount, ElementType DefaultValue>
void ChunkDataStore<ElementType, ElementCount, DefaultValue>::Assign(const ChunkDataStore<ElementType, ElementCount, DefaultValue> & a_Other)
{
}
cChunkData::cChunkData(cChunkData && a_Other):
m_Pool(a_Other.m_Pool)
{
for (size_t i = 0; i < NumSections; i++)
for (size_t Y = 0; Y != cChunkDef::NumSections; Y++)
{
m_Sections[i] = a_Other.m_Sections[i];
a_Other.m_Sections[i] = nullptr;
}
}
Store[Y].reset();
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)
if (const auto & Other = a_Other.Store[Y]; Other != nullptr)
{
m_Sections[i] = Allocate();
*m_Sections[i] = *a_Other.m_Sections[i];
Store[Y] = std::make_unique<Type>(*Other);
}
}
}
@ -107,47 +69,65 @@ void cChunkData::Assign(const cChunkData & a_Other)
void cChunkData::Assign(cChunkData && a_Other)
template<class ElementType, size_t ElementCount, ElementType DefaultValue>
ElementType ChunkDataStore<ElementType, ElementCount, DefaultValue>::Get(const Vector3i a_Position) const
{
if (&a_Other == this)
const auto Indices = IndicesFromRelPos(a_Position);
const auto & Section = Store[Indices.Section];
if (Section != nullptr)
{
return;
if (IsCompressed(ElementCount))
{
return cChunkDef::ExpandNibble(Section->data(), Indices.Index);
}
else
{
return (*Section)[Indices.Index];
}
}
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;
}
return UnpackDefaultValue<ElementCount>(DefaultValue);
}
BLOCKTYPE cChunkData::GetBlock(Vector3i a_RelPos) 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
{
if (!cChunkDef::IsValidRelPos(a_RelPos))
return Store[a_Y].get();
}
template<class ElementType, size_t ElementCount, ElementType DefaultValue>
void ChunkDataStore<ElementType, ElementCount, DefaultValue>::Set(const Vector3i a_Position, const ElementType a_Value)
{
const auto Indices = IndicesFromRelPos(a_Position);
auto & Section = Store[Indices.Section];
if (Section == nullptr)
{
return E_BLOCK_AIR; // Coordinates are outside outside the world, so this must be an air block
if (a_Value == UnpackDefaultValue<ElementCount>(DefaultValue))
{
return;
}
Section = cpp20::make_unique_for_overwrite<Type>();
std::fill(Section->begin(), Section->end(), DefaultValue);
}
auto Idxs = IndicesFromRelPos(a_RelPos);
if (m_Sections[Idxs.Section] != nullptr)
if (IsCompressed(ElementCount))
{
return m_Sections[Idxs.Section]->m_BlockTypes[Idxs.Index];
cChunkDef::PackNibble(Section->data(), Indices.Index, a_Value);
}
else
{
return 0;
(*Section)[Indices.Index] = a_Value;
}
}
@ -155,576 +135,100 @@ BLOCKTYPE cChunkData::GetBlock(Vector3i a_RelPos) const
void cChunkData::SetBlock(Vector3i a_RelPos, BLOCKTYPE a_Block)
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 (!cChunkDef::IsValidRelPos(a_RelPos))
{
ASSERT(!"cChunkData::SetMeta(): index out of range!");
return;
}
auto & Section = Store[a_Y];
const auto SourceEnd = std::end(a_Source);
auto Idxs = IndicesFromRelPos(a_RelPos);
if (m_Sections[Idxs.Section] == nullptr)
if (Section != nullptr)
{
if (a_Block == 0x00)
{
return;
}
m_Sections[Idxs.Section] = Allocate();
if (m_Sections[Idxs.Section] == nullptr)
{
ASSERT(!"Failed to allocate a new section in Chunkbuffer");
return;
}
ZeroSection(m_Sections[Idxs.Section]);
std::copy(a_Source, SourceEnd, Section->begin());
}
else if (std::any_of(a_Source, SourceEnd, [](const auto Value) { return Value != DefaultValue; }))
{
Section = cpp20::make_unique_for_overwrite<Type>();
std::copy(a_Source, SourceEnd, Section->begin());
}
m_Sections[Idxs.Section]->m_BlockTypes[Idxs.Index] = a_Block;
}
NIBBLETYPE cChunkData::GetMeta(Vector3i a_RelPos) const
template<class ElementType, size_t ElementCount, ElementType DefaultValue>
void ChunkDataStore<ElementType, ElementCount, DefaultValue>::SetAll(const ElementType (& a_Source)[cChunkDef::NumSections * ElementCount])
{
if (cChunkDef::IsValidRelPos(a_RelPos))
for (size_t Y = 0; Y != cChunkDef::NumSections; Y++)
{
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;
}
SetSection(*reinterpret_cast<const ElementType (*)[ElementCount]>(a_Source + Y * ElementCount), Y);
}
// Coordinates are outside outside the world, so it must be an air block with a blank meta
return 0;
}
bool cChunkData::SetMeta(Vector3i a_RelPos, NIBBLETYPE a_Nibble)
void ChunkBlockData::Assign(const ChunkBlockData & a_Other)
{
if (!cChunkDef::IsValidRelPos(a_RelPos))
{
ASSERT(!"cChunkData::SetMeta(): index out of range!");
return false;
}
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)
{
ASSERT(!"Failed to allocate a new section in Chunkbuffer");
return false;
}
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;
m_Blocks.Assign(a_Other.m_Blocks);
m_Metas.Assign(a_Other.m_Metas);
}
NIBBLETYPE cChunkData::GetBlockLight(Vector3i a_RelPos) const
void ChunkBlockData::SetAll(const cChunkDef::BlockTypes & a_BlockSource, const cChunkDef::BlockNibbles & a_MetaSource)
{
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;
m_Blocks.SetAll(a_BlockSource);
m_Metas.SetAll(a_MetaSource);
}
NIBBLETYPE cChunkData::GetSkyLight(Vector3i a_RelPos) const
void ChunkBlockData::SetSection(const SectionType & a_BlockSource, const SectionMetaType & a_MetaSource, const size_t a_Y)
{
if (cChunkDef::IsValidRelPos(a_RelPos))
{
auto Idxs = IndicesFromRelPos(a_RelPos);
if (m_Sections[Idxs.Section] != nullptr)
{
return (m_Sections[Idxs.Section]->m_BlockSkyLight[Idxs.Index / 2] >> ((Idxs.Index & 1) * 4)) & 0x0f;
}
else
{
return 0xF;
}
}
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
return 0;
m_Blocks.SetSection(a_BlockSource, a_Y);
m_Metas.SetSection(a_MetaSource, a_Y);
}
const cChunkData::sChunkSection * cChunkData::GetSection(size_t a_SectionNum) const
void ChunkLightData::Assign(const ChunkLightData & a_Other)
{
if (a_SectionNum < NumSections)
{
return m_Sections[a_SectionNum];
}
ASSERT(!"cChunkData::GetSection: section index out of range");
return nullptr;
m_BlockLights.Assign(a_Other.m_BlockLights);
m_SkyLights.Assign(a_Other.m_SkyLights);
}
UInt16 cChunkData::GetSectionBitmask() const
void ChunkLightData::SetAll(const cChunkDef::BlockNibbles & a_BlockLightSource, const cChunkDef::BlockNibbles & a_SkyLightSource)
{
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;
m_BlockLights.SetAll(a_BlockLightSource);
m_SkyLights.SetAll(a_SkyLightSource);
}
void cChunkData::Clear()
void ChunkLightData::SetSection(const SectionType & a_BlockLightSource, const SectionType & a_SkyLightSource, const size_t a_Y)
{
for (size_t i = 0; i < NumSections; ++i)
{
if (m_Sections[i] != nullptr)
{
Free(m_Sections[i]);
m_Sections[i] = nullptr;
}
}
m_BlockLights.SetSection(a_BlockLightSource, a_Y);
m_SkyLights.SetSection(a_SkyLightSource, a_Y);
}
void cChunkData::CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx, size_t a_Length) 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);
}
}
}
}
void cChunkData::CopyMetas(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_BlockMetas, sizeof(m_Sections[i]->m_BlockMetas));
}
else
{
memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockMetas));
}
}
}
void cChunkData::CopyBlockLight(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_BlockLight, sizeof(m_Sections[i]->m_BlockLight));
}
else
{
memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockLight));
}
}
}
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));
}
}
}
void cChunkData::FillBlockTypes(BLOCKTYPE a_Value)
{
// 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_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);
}
}
}
for (auto Section : m_Sections)
{
if (Section != nullptr)
{
std::fill(std::begin(Section->m_BlockTypes), std::end(Section->m_BlockTypes), a_Value);
}
}
}
void cChunkData::FillMetas(NIBBLETYPE a_Value)
{
// 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);
}
}
}
void cChunkData::FillBlockLight(NIBBLETYPE a_Value)
{
// 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_BlockMetas), std::end(Section->m_BlockMetas), 0x00);
std::fill(std::begin(Section->m_BlockSkyLight), std::end(Section->m_BlockSkyLight), 0xff);
}
}
}
NIBBLETYPE NewLight = static_cast<NIBBLETYPE>((a_Value << 4) | a_Value);
for (auto Section : m_Sections)
{
if (Section != nullptr)
{
std::fill(std::begin(Section->m_BlockLight), std::end(Section->m_BlockLight), NewLight);
}
}
}
void cChunkData::FillSkyLight(NIBBLETYPE a_Value)
{
// If needed, allocate any missing sections
if (a_Value != 0x0f)
{
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);
}
}
}
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)
{
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[]
}
void cChunkData::SetBlockLight(const NIBBLETYPE * a_Src)
{
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 *