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:
parent
5fa45182e8
commit
868cd94ee9
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
} ;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()),
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
41
src/Chunk.h
41
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)
|
||||
{
|
||||
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;
|
||||
|
@ -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++)
|
||||
{
|
||||
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)
|
||||
for (size_t Y = 0; Y != cChunkDef::NumSections; Y++)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Store[Y].reset();
|
||||
|
||||
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,624 +69,166 @@ 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)
|
||||
{
|
||||
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
|
||||
}
|
||||
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))
|
||||
{
|
||||
ASSERT(!"cChunkData::SetMeta(): index out of range!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto Idxs = IndicesFromRelPos(a_RelPos);
|
||||
if (m_Sections[Idxs.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]);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE cChunkData::GetBlockLight(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_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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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);
|
||||
if (IsCompressed(ElementCount))
|
||||
{
|
||||
return cChunkDef::ExpandNibble(Section->data(), Indices.Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*Section)[Indices.Index];
|
||||
}
|
||||
}
|
||||
|
||||
return UnpackDefaultValue<ElementCount>(DefaultValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::FillMetas(NIBBLETYPE a_Value)
|
||||
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 needed, allocate any missing sections
|
||||
if (a_Value != 0x00)
|
||||
{
|
||||
for (auto & Section : m_Sections)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
if (a_Value == UnpackDefaultValue<ElementCount>(DefaultValue))
|
||||
{
|
||||
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;
|
||||
Section = cpp20::make_unique_for_overwrite<Type>();
|
||||
std::fill(Section->begin(), Section->end(), DefaultValue);
|
||||
}
|
||||
|
||||
// The section doesn't exist, find out if it is needed:
|
||||
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, static_cast<NIBBLETYPE>(0)))
|
||||
if (IsCompressed(ElementCount))
|
||||
{
|
||||
// No need for the section, the data is all zeroes
|
||||
continue;
|
||||
cChunkDef::PackNibble(Section->data(), Indices.Index, a_Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*Section)[Indices.Index] = a_Value;
|
||||
}
|
||||
}
|
||||
|
||||
// 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[]
|
||||
|
||||
|
||||
|
||||
|
||||
template<class ElementType, size_t ElementCount, ElementType DefaultValue>
|
||||
void ChunkDataStore<ElementType, ElementCount, DefaultValue>::SetSection(const ElementType (& a_Source)[ElementCount], const size_t a_Y)
|
||||
{
|
||||
auto & Section = Store[a_Y];
|
||||
const auto SourceEnd = std::end(a_Source);
|
||||
|
||||
if (Section != nullptr)
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<class ElementType, size_t ElementCount, ElementType DefaultValue>
|
||||
void ChunkDataStore<ElementType, ElementCount, DefaultValue>::SetAll(const ElementType (& a_Source)[cChunkDef::NumSections * ElementCount])
|
||||
{
|
||||
for (size_t Y = 0; Y != cChunkDef::NumSections; Y++)
|
||||
{
|
||||
SetSection(*reinterpret_cast<const ElementType (*)[ElementCount]>(a_Source + Y * ElementCount), Y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ChunkBlockData::Assign(const ChunkBlockData & a_Other)
|
||||
{
|
||||
m_Blocks.Assign(a_Other.m_Blocks);
|
||||
m_Metas.Assign(a_Other.m_Metas);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetSkyLight(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_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.SetAll(a_BlockSource);
|
||||
m_Metas.SetAll(a_MetaSource);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UInt32 cChunkData::NumPresentSections() const
|
||||
void ChunkBlockData::SetSection(const SectionType & a_BlockSource, const SectionMetaType & a_MetaSource, const size_t a_Y)
|
||||
{
|
||||
UInt32 Ret = 0U;
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
if (m_Sections[i] != nullptr)
|
||||
{
|
||||
++Ret;
|
||||
}
|
||||
}
|
||||
return Ret;
|
||||
m_Blocks.SetSection(a_BlockSource, a_Y);
|
||||
m_Metas.SetSection(a_MetaSource, a_Y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData::sChunkSection * cChunkData::Allocate(void)
|
||||
void ChunkLightData::Assign(const ChunkLightData & a_Other)
|
||||
{
|
||||
return m_Pool.Allocate();
|
||||
m_BlockLights.Assign(a_Other.m_BlockLights);
|
||||
m_SkyLights.Assign(a_Other.m_SkyLights);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::Free(cChunkData::sChunkSection * a_Section)
|
||||
void ChunkLightData::SetAll(const cChunkDef::BlockNibbles & a_BlockLightSource, const cChunkDef::BlockNibbles & a_SkyLightSource)
|
||||
{
|
||||
m_Pool.Free(a_Section);
|
||||
m_BlockLights.SetAll(a_BlockLightSource);
|
||||
m_SkyLights.SetAll(a_SkyLightSource);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::ZeroSection(cChunkData::sChunkSection * a_Section) const
|
||||
void ChunkLightData::SetSection(const SectionType & a_BlockLightSource, const SectionType & a_SkyLightSource, const size_t a_Y)
|
||||
{
|
||||
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));
|
||||
m_BlockLights.SetSection(a_BlockLightSource, a_Y);
|
||||
m_SkyLights.SetSection(a_SkyLightSource, a_Y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template struct ChunkDataStore<BLOCKTYPE, ChunkBlockData::SectionBlockCount, ChunkBlockData::DefaultValue>;
|
||||
template struct ChunkDataStore<NIBBLETYPE, ChunkBlockData::SectionMetaCount, ChunkLightData::DefaultBlockLightValue>;
|
||||
template struct ChunkDataStore<NIBBLETYPE, ChunkLightData::SectionLightCount, ChunkLightData::DefaultSkyLightValue>;
|
||||
|
239
src/ChunkData.h
239
src/ChunkData.h
@ -9,132 +9,159 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "AllocationPool.h"
|
||||
#include "FunctionRef.h"
|
||||
#include "ChunkDef.h"
|
||||
|
||||
|
||||
class cChunkData
|
||||
|
||||
|
||||
|
||||
template <class ElementType, size_t ElementCount, ElementType DefaultValue>
|
||||
struct ChunkDataStore
|
||||
{
|
||||
using Type = std::array<ElementType, ElementCount>;
|
||||
|
||||
/** Copy assign from another ChunkDataStore. */
|
||||
void Assign(const ChunkDataStore<ElementType, ElementCount, DefaultValue> & a_Other);
|
||||
|
||||
/** Gets one value at the given position.
|
||||
Returns DefaultValue if the section is not allocated. */
|
||||
ElementType Get(Vector3i a_Position) const;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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);
|
||||
|
||||
/** 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]);
|
||||
|
||||
/** Contains all the sections this ChunkDataStore manages. */
|
||||
std::unique_ptr<Type> Store[cChunkDef::NumSections];
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ChunkBlockData
|
||||
{
|
||||
public:
|
||||
|
||||
static const int SectionHeight = 16;
|
||||
static const size_t NumSections = (cChunkDef::Height / SectionHeight);
|
||||
static const size_t SectionBlockCount = SectionHeight * cChunkDef::Width * cChunkDef::Width;
|
||||
static constexpr size_t SectionBlockCount = cChunkDef::SectionHeight * cChunkDef::Width * cChunkDef::Width;
|
||||
static constexpr size_t SectionMetaCount = SectionBlockCount / 2;
|
||||
|
||||
struct sChunkSection
|
||||
{
|
||||
BLOCKTYPE m_BlockTypes[SectionBlockCount];
|
||||
NIBBLETYPE m_BlockMetas[SectionBlockCount / 2];
|
||||
NIBBLETYPE m_BlockLight[SectionBlockCount / 2];
|
||||
NIBBLETYPE m_BlockSkyLight[SectionBlockCount / 2];
|
||||
};
|
||||
static constexpr BLOCKTYPE DefaultValue = 0x00;
|
||||
static constexpr NIBBLETYPE DefaultMetaValue = 0x00;
|
||||
|
||||
cChunkData(cAllocationPool<sChunkSection> & a_Pool);
|
||||
cChunkData(cChunkData && a_Other);
|
||||
~cChunkData();
|
||||
|
||||
cChunkData & operator = (cChunkData && a_Other)
|
||||
{
|
||||
Assign(std::move(a_Other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Copy assign from another cChunkData */
|
||||
void Assign(const cChunkData & a_Other);
|
||||
|
||||
/** Move assign from another cChunkData */
|
||||
void Assign(cChunkData && a_Other);
|
||||
|
||||
BLOCKTYPE GetBlock(Vector3i a_RelPos) const;
|
||||
void SetBlock(Vector3i a_RelPos, BLOCKTYPE a_Block);
|
||||
|
||||
NIBBLETYPE GetMeta(Vector3i a_RelPos) const;
|
||||
bool SetMeta(Vector3i a_RelPos, NIBBLETYPE a_Nibble);
|
||||
|
||||
NIBBLETYPE GetBlockLight(Vector3i a_RelPos) const;
|
||||
|
||||
NIBBLETYPE GetSkyLight(Vector3i a_RelPos) const;
|
||||
|
||||
/** Return a pointer to the chunk section or nullptr if all air */
|
||||
const sChunkSection * GetSection(size_t a_SectionNum) const;
|
||||
|
||||
/** Returns a bitmask of chunk sections which are currently stored. */
|
||||
UInt16 GetSectionBitmask() const;
|
||||
|
||||
/** Clears all data */
|
||||
void Clear();
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Copies the metadata into the specified flat array. */
|
||||
void CopyMetas(NIBBLETYPE * a_Dest) const;
|
||||
|
||||
/** Copies the block light data into the specified flat array. */
|
||||
void CopyBlockLight(NIBBLETYPE * a_Dest) const;
|
||||
|
||||
/** Copies the skylight data into the specified flat array. */
|
||||
void CopySkyLight (NIBBLETYPE * a_Dest) const;
|
||||
|
||||
/** Fills the chunk with the specified block. */
|
||||
void FillBlockTypes(BLOCKTYPE a_Value);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** Returns the number of sections present (i.e. non-air). */
|
||||
UInt32 NumPresentSections() const;
|
||||
using SectionType = BLOCKTYPE[SectionBlockCount];
|
||||
using SectionMetaType = NIBBLETYPE[SectionMetaCount];
|
||||
|
||||
private:
|
||||
|
||||
sChunkSection * m_Sections[NumSections];
|
||||
ChunkDataStore<BLOCKTYPE, SectionBlockCount, DefaultValue> m_Blocks;
|
||||
ChunkDataStore<NIBBLETYPE, SectionMetaCount, DefaultMetaValue> m_Metas;
|
||||
|
||||
cAllocationPool<sChunkSection> & m_Pool;
|
||||
public:
|
||||
|
||||
/** Allocates a new section. Entry-point to custom allocators. */
|
||||
sChunkSection * Allocate(void);
|
||||
using BlockArray = decltype(m_Blocks)::Type;
|
||||
using MetaArray = decltype(m_Metas)::Type;
|
||||
|
||||
/** Frees the specified section, previously allocated using Allocate().
|
||||
Note that a_Section may be nullptr. */
|
||||
void Free(sChunkSection * a_Section);
|
||||
void Assign(const ChunkBlockData & a_Other);
|
||||
|
||||
/** Sets the data in the specified section to their default values. */
|
||||
void ZeroSection(sChunkSection * a_Section) 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); }
|
||||
|
||||
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); }
|
||||
|
||||
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); }
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ChunkLightData
|
||||
{
|
||||
public:
|
||||
|
||||
static constexpr size_t SectionLightCount = (cChunkDef::SectionHeight * cChunkDef::Width * cChunkDef::Width) / 2;
|
||||
|
||||
static constexpr NIBBLETYPE DefaultBlockLightValue = 0x00;
|
||||
static constexpr NIBBLETYPE DefaultSkyLightValue = 0xFF;
|
||||
|
||||
using SectionType = NIBBLETYPE[SectionLightCount];
|
||||
|
||||
private:
|
||||
|
||||
ChunkDataStore<NIBBLETYPE, SectionLightCount, DefaultBlockLightValue> m_BlockLights;
|
||||
ChunkDataStore<NIBBLETYPE, SectionLightCount, DefaultSkyLightValue> m_SkyLights;
|
||||
|
||||
public:
|
||||
|
||||
using LightArray = decltype(m_BlockLights)::Type;
|
||||
|
||||
void Assign(const ChunkLightData & a_Other);
|
||||
|
||||
NIBBLETYPE GetBlockLight(Vector3i a_Position) const { return m_BlockLights.Get(a_Position); }
|
||||
NIBBLETYPE GetSkyLight(Vector3i a_Position) const { return m_SkyLights.Get(a_Position); }
|
||||
|
||||
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>;
|
||||
|
@ -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
|
||||
|
||||
ChunkBlockData m_BlockData;
|
||||
ChunkLightData m_LightData;
|
||||
|
||||
private:
|
||||
|
||||
virtual void ChunkData(const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData) override
|
||||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
cListAllocationPool<cChunkData::sChunkSection> m_Pool;
|
||||
cChunkData m_Data;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void ChunkData(const cChunkData & a_ChunkBuffer) override
|
||||
{
|
||||
m_Data.Assign(a_ChunkBuffer);
|
||||
m_BlockData.Assign(a_BlockData);
|
||||
m_LightData.Assign(a_LightData);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
120
src/ChunkDef.h
120
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;
|
||||
}
|
||||
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
class PortalSearchCallback : public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
// Iterate through all of the blocks in the chunk
|
||||
for (unsigned int i = 0; i < cChunkDef::NumBlocks; i++)
|
||||
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)
|
||||
{
|
||||
if (blocks[i] == E_BLOCK_NETHER_PORTAL)
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual void ChunkData(const ChunkBlockData & a_BlockData, const ChunkLightData &) override
|
||||
{
|
||||
Vector3i Coordinate = cChunkDef::IndexToCoordinate(i);
|
||||
if (Coordinate.y >= m_MaxY)
|
||||
for (size_t Y = 0; Y < cChunkDef::NumSections; ++Y)
|
||||
{
|
||||
const auto Blocks = a_BlockData.GetSection(Y);
|
||||
if (Blocks == nullptr)
|
||||
{
|
||||
// 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);
|
||||
// Iterate through all of the blocks in the chunk:
|
||||
for (size_t i = 0; i < ChunkBlockData::SectionBlockCount; i++)
|
||||
{
|
||||
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
|
||||
else if ((PortalLoc - m_Position).SqrLength() < (m_PortalLoc - m_Position).SqrLength())
|
||||
{
|
||||
if ((PortalLoc - m_Position).SqrLength() < (m_PortalLoc - m_Position).SqrLength())
|
||||
{
|
||||
m_FoundPortal = true;
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
for (size_t SectionIdx = 0; SectionIdx < cChunkData::NumSections; ++SectionIdx)
|
||||
{
|
||||
auto Section = a_Data.GetSection(SectionIdx);
|
||||
if (Section != nullptr)
|
||||
{
|
||||
a_Func(*Section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
std::pair<UInt16, size_t> GetSectionBitmask(const ChunkBlockData & a_BlockData, const ChunkLightData & a_LightData)
|
||||
{
|
||||
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 };
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
for (size_t BlockIdx = 0; BlockIdx != cChunkData::SectionBlockCount; ++BlockIdx)
|
||||
const bool BlocksExist = Blocks != nullptr;
|
||||
const bool MetasExist = Metas != nullptr;
|
||||
|
||||
for (size_t BlockIdx = 0; BlockIdx != ChunkBlockData::SectionBlockCount; ++BlockIdx)
|
||||
{
|
||||
BLOCKTYPE BlockType = a_Section.m_BlockTypes[BlockIdx] & 0xFF;
|
||||
NIBBLETYPE BlockMeta = a_Section.m_BlockMetas[BlockIdx / 2] >> ((BlockIdx & 1) * 4) & 0x0f;
|
||||
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)
|
||||
{
|
||||
m_Packet.WriteBuf(a_Section.m_BlockLight, sizeof(a_Section.m_BlockLight));
|
||||
if (BlockLights == nullptr)
|
||||
{
|
||||
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)
|
||||
{
|
||||
m_Packet.WriteBuf(a_Section.m_BlockSkyLight, sizeof(a_Section.m_BlockSkyLight));
|
||||
if (SkyLights == nullptr)
|
||||
{
|
||||
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)
|
||||
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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
);
|
||||
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)
|
||||
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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
);
|
||||
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)
|
||||
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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
);
|
||||
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)
|
||||
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));
|
||||
WriteSectionDataSeamless<&Palette477>(a_Section, BitsPerEntry);
|
||||
}
|
||||
);
|
||||
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);
|
||||
|
@ -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);
|
||||
|
@ -9,12 +9,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include "FunctionRef.h"
|
||||
#include "ChunkDef.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cClientHandle;
|
||||
class cObjective;
|
||||
class cTeam;
|
||||
class cWorld;
|
||||
|
@ -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[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
)
|
||||
{
|
||||
|
136
src/World.cpp
136
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));
|
||||
}
|
||||
|
||||
|
||||
|
22
src/World.h
22
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. */
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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,31 +103,29 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) override
|
||||
{
|
||||
for (int RelX = 0; RelX < cChunkDef::Width; RelX++)
|
||||
virtual void HeightMap(const cChunkDef::HeightMap & a_HeightMap) override
|
||||
{
|
||||
for (int RelZ = 0; RelZ < cChunkDef::Width; RelZ++)
|
||||
{
|
||||
int Height = cChunkDef::GetHeight(*a_HeightMap, RelX, RelZ);
|
||||
mVanillaHeightMap[(RelZ << 4) | RelX] = Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override
|
||||
for (int RelX = 0; RelX < cChunkDef::Width; RelX++)
|
||||
{
|
||||
memcpy(mBiomes, a_BiomeMap, sizeof(mBiomes));
|
||||
for (size_t i = 0; i < ARRAYCOUNT(mBiomes); i++)
|
||||
Heights[RelX + RelZ * cChunkDef::Width] = cChunkDef::GetHeight(a_HeightMap, RelX, RelZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual void BiomeMap(const cChunkDef::BiomeMap & a_BiomeMap) override
|
||||
{
|
||||
if ((*a_BiomeMap)[i] < 255)
|
||||
for (size_t i = 0; i < ARRAYCOUNT(Biomes); i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
#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 (BlockLights != nullptr)
|
||||
{
|
||||
aWriter.AddByteArray("BlockLight", reinterpret_cast<const char *>(BlockLights->data()), BlockLights->size());
|
||||
}
|
||||
else
|
||||
{
|
||||
aWriter.AddByteArray("BlockLight", ChunkLightData::SectionLightCount, ChunkLightData::DefaultBlockLightValue);
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -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")))
|
||||
{
|
||||
// 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 biome data", a_RawChunkData);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Height map too:
|
||||
if (!LoadHeightMapFromNBT(Data.HeightMap, a_NBT, a_NBT.FindChildByName(Level, "HeightMap")))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
const auto * const BiomeData = a_NBT.GetData(a_TagIdx);
|
||||
for (size_t i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
|
||||
{
|
||||
if (BiomeData[i] > std::byte(EMCSBiome::biMaxVariantBiome))
|
||||
{
|
||||
// Unassigned biomes:
|
||||
return false;
|
||||
}
|
||||
|
||||
a_BiomeMap[i] = static_cast<EMCSBiome>(BiomeData[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkDef::BiomeMap * cWSSAnvil::LoadVanillaBiomeMapFromNBT(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_ByteArray))
|
||||
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) != 16 * 16)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// Unassigned biomes
|
||||
return nullptr;
|
||||
}
|
||||
(*a_BiomeMap)[i] = static_cast<EMCSBiome>(VanillaBiomeData[i]);
|
||||
}
|
||||
return a_BiomeMap;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx)
|
||||
const auto * const HeightData = a_NBT.GetData(a_TagIdx);
|
||||
for (int RelZ = 0; RelZ < cChunkDef::Width; RelZ++)
|
||||
{
|
||||
if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_IntArray))
|
||||
for (int RelX = 0; RelX < cChunkDef::Width; RelX++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (a_NBT.GetDataLength(a_TagIdx) != sizeof(*a_BiomeMap))
|
||||
{
|
||||
// The biomes stored don't match in size
|
||||
return nullptr;
|
||||
}
|
||||
const auto * BiomeData = a_NBT.GetData(a_TagIdx);
|
||||
for (size_t i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++)
|
||||
{
|
||||
(*a_BiomeMap)[i] = static_cast<EMCSBiome>(GetBEInt(&BiomeData[i * 4]));
|
||||
if ((*a_BiomeMap)[i] == 0xff)
|
||||
{
|
||||
// Unassigned biomes
|
||||
return nullptr;
|
||||
|
||||
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))
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
{
|
||||
// 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.SetBlockLight(SrcNibbleBuffer);
|
||||
|
||||
buffer.SetAll(SrcNibbleBuffer, SrcNibbleBuffer);
|
||||
|
||||
TEST_EQUAL(buffer.GetBlockLight({ 6, 2, 1 }), 0xe);
|
||||
|
||||
memset(SrcNibbleBuffer, 0x00, sizeof(SrcNibbleBuffer));
|
||||
SrcNibbleBuffer[(6 + (1 * 16) + (2 * 16 * 16)) / 2] = 0xe;
|
||||
buffer.SetSkyLight(SrcNibbleBuffer);
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
// test following segment (lights)
|
||||
ChunkLightData buffer;
|
||||
|
||||
NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2];
|
||||
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);
|
||||
|
||||
memset(SrcNibbleBuffer, 0xff, 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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -7,41 +7,50 @@
|
||||
|
||||
|
||||
|
||||
/** 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)
|
||||
{
|
||||
constexpr auto SectionCount = std::extent_v<OutType> / 16;
|
||||
|
||||
for (size_t Y = 0; Y != 16; Y++)
|
||||
{
|
||||
const auto Section = (Data.*Getter)(Y);
|
||||
static_assert(SectionCount == std::tuple_size<std::remove_pointer_t<decltype(Section)>>::value, "Output array has wrong size");
|
||||
|
||||
if (Section == nullptr)
|
||||
{
|
||||
std::fill_n(Out + Y * SectionCount, SectionCount, Default);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(Section->begin(), Section->end(), Out + Y * SectionCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Performs the entire Copies test. */
|
||||
static void test()
|
||||
static void Test()
|
||||
{
|
||||
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);
|
||||
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()
|
||||
)
|
||||
|
@ -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()
|
||||
)
|
34
tests/ChunkData/Creatable.cpp
Normal file
34
tests/ChunkData/Creatable.cpp
Normal file
@ -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()
|
||||
);
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user