1
0
cuberite-2a/src/BlockArea.cpp
Mattes D 01b8ed5295
Pulled the BlockID and BlockInfo headers from Globals.h. (#4591)
The BlockID.h file was removed from Globals.h and renamed to BlockType.h (main change)
The BlockInfo.h file was removed from Globals.h (main change)
The ENUM_BLOCK_ID and ENUM_ITEM_ID enum names were replaced with ENUM_BLOCK_TYPE and ENUM_ITEM_TYPE (cosmetics)
The various enums, such as eDimension, eDamageType and eExplosionSource were moved from BlockType.h to Defines.h, together with the helper functions for converting between them and strings (StringToDimension et al.) (minor)
Many inline functions were moved from headers to their respective cpp files, so that BlockType.h could be included only into the cpp file, rather than the header.
That broke our tests a bit, since they pick bits and pieces out of the main code and provide stubs for the rest; they had to be re-stubbed and re-verified.
eMonsterType values are no longer tied to E_ITEM_SPAWN_EGG_META_* values
2020-04-03 08:57:01 +02:00

3006 lines
68 KiB
C++

// BlockArea.cpp
// NOTE: compile.sh checks for this file in order to determine if this is the Cuberite folder.
// Please modify compile.sh if you want to rename or remove this file.
// This file was chosen arbitrarily and it's a good enough indicator we're in the Cuberite folder.
// Implements the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries
// The object also supports writing the blockdata back into cWorld, even into other coords
#include "Globals.h"
#include "BlockArea.h"
#include "OSSupport/GZipFile.h"
#include "Blocks/BlockHandler.h"
#include "ChunkData.h"
#include "BlockEntities/BlockEntity.h"
#include "Item.h"
#include "BlockInfo.h"
// Disable MSVC warnings: "conditional expression is constant"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4127)
#endif
typedef void (CombinatorFunc)(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta);
/** Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
This wild construct allows us to pass a function argument and still have it inlined by the compiler. */
template <bool MetasValid, CombinatorFunc Combinator>
void InternalMergeBlocks(
BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
int a_SizeX, int a_SizeY, int a_SizeZ,
int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ,
int a_DstOffX, int a_DstOffY, int a_DstOffZ,
int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ
)
{
UNUSED(a_SrcSizeY);
UNUSED(a_DstSizeY);
for (int y = 0; y < a_SizeY; y++)
{
int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ;
int DstBaseY = (y + a_DstOffY) * a_DstSizeX * a_DstSizeZ;
for (int z = 0; z < a_SizeZ; z++)
{
int SrcBaseZ = SrcBaseY + (z + a_SrcOffZ) * a_SrcSizeX;
int DstBaseZ = DstBaseY + (z + a_DstOffZ) * a_DstSizeX;
int SrcIdx = SrcBaseZ + a_SrcOffX;
int DstIdx = DstBaseZ + a_DstOffX;
for (int x = 0; x < a_SizeX; x++)
{
if (MetasValid)
{
Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
}
else
{
NIBBLETYPE FakeDestMeta = 0;
Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], FakeDestMeta, static_cast<NIBBLETYPE>(0));
}
++DstIdx;
++SrcIdx;
} // for x
} // for z
} // for y
}
/** Combinator used for cBlockArea::msOverwrite merging */
template <bool MetaValid>
void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
a_DstType = a_SrcType;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
/** Combinator used for cBlockArea::msFillAir merging */
template <bool MetaValid>
void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if (a_DstType == E_BLOCK_AIR)
{
a_DstType = a_SrcType;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
// "else" is the default, already in place
}
/** Combinator used for cBlockArea::msImprint merging */
template <bool MetaValid>
void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if (a_SrcType != E_BLOCK_AIR)
{
a_DstType = a_SrcType;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
// "else" is the default, already in place
}
/** Combinator used for cBlockArea::msLake merging */
template <bool MetaValid>
void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// Sponge is the NOP block
if (a_SrcType == E_BLOCK_SPONGE)
{
return;
}
// Air is always hollowed out
if (a_SrcType == E_BLOCK_AIR)
{
a_DstType = E_BLOCK_AIR;
if (MetaValid)
{
a_DstMeta = 0;
}
return;
}
// Water and lava are never overwritten
switch (a_DstType)
{
case E_BLOCK_WATER:
case E_BLOCK_STATIONARY_WATER:
case E_BLOCK_LAVA:
case E_BLOCK_STATIONARY_LAVA:
{
return;
}
}
// Water and lava always overwrite
switch (a_SrcType)
{
case E_BLOCK_WATER:
case E_BLOCK_STATIONARY_WATER:
case E_BLOCK_LAVA:
case E_BLOCK_STATIONARY_LAVA:
{
a_DstType = a_SrcType;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
return;
}
}
if (a_SrcType == E_BLOCK_STONE)
{
switch (a_DstType)
{
case E_BLOCK_DIRT:
case E_BLOCK_GRASS:
case E_BLOCK_MYCELIUM:
{
a_DstType = E_BLOCK_STONE;
if (MetaValid)
{
a_DstMeta = 0;
}
return;
}
}
}
// Everything else is left as it is
}
/** Combinator used for cBlockArea::msSpongePrint merging */
template <bool MetaValid>
void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// Sponge overwrites nothing, everything else overwrites anything
if (a_SrcType != E_BLOCK_SPONGE)
{
a_DstType = a_SrcType;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
}
/** Combinator used for cBlockArea::msDifference merging */
template <bool MetaValid>
void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
{
a_DstType = E_BLOCK_AIR;
if (MetaValid)
{
a_DstMeta = 0;
}
}
else
{
a_DstType = a_SrcType;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
}
/** Combinator used for cBlockArea::msSimpleCompare merging */
template <bool MetaValid>
void MergeCombinatorSimpleCompare(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
{
// The blocktypes are the same, and the blockmetas are not present or are the same
a_DstType = E_BLOCK_AIR;
}
else
{
// The blocktypes or blockmetas differ
a_DstType = E_BLOCK_STONE;
}
}
/** Combinator used for cBlockArea::msMask merging */
template <bool MetaValid>
void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// If the blocks are the same, keep the dest; otherwise replace with air
if ((a_SrcType != a_DstType) || !MetaValid || (a_SrcMeta != a_DstMeta))
{
a_DstType = E_BLOCK_AIR;
if (MetaValid)
{
a_DstMeta = 0;
}
}
}
// Re-enable previously disabled MSVC warnings
#ifdef _MSC_VER
#pragma warning(pop)
#endif
////////////////////////////////////////////////////////////////////////////////
// cBlockArea:
cBlockArea::cBlockArea(void) = default;
bool cBlockArea::IsValidDataTypeCombination(int a_DataTypes)
{
// BlockEntities require that BlockTypes be present, too
if ((a_DataTypes & baBlockEntities) != 0)
{
if ((a_DataTypes & baTypes) == 0)
{
return false;
}
}
// All other combinations are considered valid
return true;
}
void cBlockArea::Clear(void)
{
m_BlockTypes.reset();
m_BlockMetas.reset();
m_BlockLight.reset();
m_BlockSkyLight.reset();
m_BlockEntities.reset();
m_Origin.Set(0, 0, 0);
m_Size.Set(0, 0, 0);
}
void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
{
ASSERT(a_SizeX > 0);
ASSERT(a_SizeY > 0);
ASSERT(a_SizeZ > 0);
ASSERT(IsValidDataTypeCombination(a_DataTypes));
// Warn if the height is too much, but proceed with the creation:
if (a_SizeY > cChunkDef::Height)
{
LOGWARNING("Creating a cBlockArea with height larger than world height (%d). Continuing, but the area may misbehave.", a_SizeY);
}
Clear();
SetSize(a_SizeX, a_SizeY, a_SizeZ, a_DataTypes);
Fill(a_DataTypes, E_BLOCK_AIR);
}
void cBlockArea::Create(const Vector3i & a_Size, int a_DataTypes)
{
Create(a_Size.x, a_Size.y, a_Size.z, a_DataTypes);
}
void cBlockArea::SetWEOffset(int a_OffsetX, int a_OffsetY, int a_OffsetZ)
{
m_WEOffset.Set(a_OffsetX, a_OffsetY, a_OffsetZ);
}
void cBlockArea::SetWEOffset(const Vector3i & a_Offset)
{
m_WEOffset.Set(a_Offset.x, a_Offset.y, a_Offset.z);
}
void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
{
m_Origin.Set(a_OriginX, a_OriginY, a_OriginZ);
}
void cBlockArea::SetOrigin(const Vector3i & a_Origin)
{
m_Origin.Set(a_Origin.x, a_Origin.y, a_Origin.z);
}
bool cBlockArea::IsValidRelCoords(int a_RelX, int a_RelY, int a_RelZ) const
{
return (
(a_RelX >= 0) && (a_RelX < m_Size.x) &&
(a_RelY >= 0) && (a_RelY < m_Size.y) &&
(a_RelZ >= 0) && (a_RelZ < m_Size.z)
);
}
bool cBlockArea::IsValidRelCoords(const Vector3i & a_RelCoords) const
{
return IsValidRelCoords(a_RelCoords.x, a_RelCoords.y, a_RelCoords.z);
}
bool cBlockArea::IsValidCoords(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
return IsValidRelCoords(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z);
}
bool cBlockArea::IsValidCoords(const Vector3i & a_Coords) const
{
return IsValidRelCoords(a_Coords - m_Origin);
}
bool cBlockArea::Read(cForEachChunkProvider & a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
{
ASSERT(IsValidDataTypeCombination(a_DataTypes));
ASSERT(cChunkDef::IsValidHeight(a_MinBlockY));
ASSERT(cChunkDef::IsValidHeight(a_MaxBlockY));
ASSERT(a_MinBlockX <= a_MaxBlockX);
ASSERT(a_MinBlockY <= a_MaxBlockY);
ASSERT(a_MinBlockZ <= a_MaxBlockZ);
// Include the Max coords:
a_MaxBlockX += 1;
a_MaxBlockY += 1;
a_MaxBlockZ += 1;
// Allocate the needed memory:
Clear();
if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes))
{
return false;
}
m_Origin.Set(a_MinBlockX, a_MinBlockY, a_MinBlockZ);
cChunkReader Reader(*this);
// Convert block coords to chunks coords:
int MinChunkX, MaxChunkX;
int MinChunkZ, MaxChunkZ;
cChunkDef::AbsoluteToRelative(a_MinBlockX, a_MinBlockY, a_MinBlockZ, MinChunkX, MinChunkZ);
cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ);
// Query block data:
if (!a_ForEachChunkProvider.ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
{
Clear();
return false;
}
return true;
}
bool cBlockArea::Read(cForEachChunkProvider & a_ForEachChunkProvider, const cCuboid & a_Bounds, int a_DataTypes)
{
return Read(
a_ForEachChunkProvider,
a_Bounds.p1.x, a_Bounds.p2.x,
a_Bounds.p1.y, a_Bounds.p2.y,
a_Bounds.p1.z, a_Bounds.p2.z,
a_DataTypes
);
}
bool cBlockArea::Read(cForEachChunkProvider & a_ForEachChunkProvider, const Vector3i & a_Point1, const Vector3i & a_Point2, int a_DataTypes)
{
return Read(
a_ForEachChunkProvider,
a_Point1.x, a_Point2.x,
a_Point1.y, a_Point2.y,
a_Point1.z, a_Point2.z,
a_DataTypes
);
}
bool cBlockArea::Write(cForEachChunkProvider & a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
{
ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have?
ASSERT(cChunkDef::IsValidHeight(a_MinBlockY));
ASSERT(cChunkDef::IsValidHeight(a_MinBlockY + m_Size.y - 1));
return a_ForEachChunkProvider.WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
}
bool cBlockArea::Write(cForEachChunkProvider & a_ForEachChunkProvider, const Vector3i & a_MinCoords, int a_DataTypes)
{
return Write(
a_ForEachChunkProvider,
a_MinCoords.x, a_MinCoords.y, a_MinCoords.z,
a_DataTypes
);
}
void cBlockArea::CopyTo(cBlockArea & a_Into) const
{
if (&a_Into == this)
{
LOGWARNING("Trying to copy a cBlockArea into self, ignoring.");
return;
}
a_Into.Clear();
a_Into.SetSize(m_Size.x, m_Size.y, m_Size.z, GetDataTypes());
a_Into.m_Origin = m_Origin;
size_t BlockCount = GetBlockCount();
if (HasBlockTypes())
{
memcpy(a_Into.GetBlockTypes(), GetBlockTypes(), BlockCount * sizeof(BLOCKTYPE));
}
if (HasBlockMetas())
{
memcpy(a_Into.GetBlockMetas(), GetBlockMetas(), BlockCount * sizeof(NIBBLETYPE));
}
if (HasBlockLights())
{
memcpy(a_Into.GetBlockLight(), GetBlockLight(), BlockCount * sizeof(NIBBLETYPE));
}
if (HasBlockSkyLights())
{
memcpy(a_Into.GetBlockSkyLight(), GetBlockSkyLight(), BlockCount * sizeof(NIBBLETYPE));
}
if (HasBlockEntities())
{
ClearBlockEntities(*(a_Into.m_BlockEntities));
for (const auto & keyPair: *m_BlockEntities)
{
const auto & pos = keyPair.second->GetPos();
a_Into.m_BlockEntities->insert({keyPair.first, keyPair.second->Clone(pos)});
}
}
}
void cBlockArea::CopyFrom(const cBlockArea & a_From)
{
a_From.CopyTo(*this);
}
void cBlockArea::DumpToRawFile(const AString & a_FileName)
{
cFile f;
if (!f.Open(a_FileName, cFile::fmWrite))
{
LOGWARNING("cBlockArea: Cannot open file \"%s\" for raw dump", a_FileName.c_str());
return;
}
UInt32 SizeX = ntohl(static_cast<UInt32>(m_Size.x));
UInt32 SizeY = ntohl(static_cast<UInt32>(m_Size.y));
UInt32 SizeZ = ntohl(static_cast<UInt32>(m_Size.z));
f.Write(&SizeX, 4);
f.Write(&SizeY, 4);
f.Write(&SizeZ, 4);
unsigned char DataTypes = static_cast<unsigned char>(GetDataTypes());
f.Write(&DataTypes, 1);
size_t NumBlocks = GetBlockCount();
if (HasBlockTypes())
{
f.Write(GetBlockTypes(), NumBlocks * sizeof(BLOCKTYPE));
}
if (HasBlockMetas())
{
f.Write(GetBlockMetas(), NumBlocks);
}
if (HasBlockLights())
{
f.Write(GetBlockLight(), NumBlocks);
}
if (HasBlockSkyLights())
{
f.Write(GetBlockSkyLight(), NumBlocks);
}
}
void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
{
if (
(a_AddMinX + a_SubMaxX >= m_Size.x) ||
(a_AddMinY + a_SubMaxY >= m_Size.y) ||
(a_AddMinZ + a_SubMaxZ >= m_Size.z)
)
{
LOGWARNING("cBlockArea:Crop called with more croping than the dimensions: %d x %d x %d with cropping %d, %d and %d",
m_Size.x, m_Size.y, m_Size.z,
a_AddMinX + a_SubMaxX, a_AddMinY + a_SubMaxY, a_AddMinZ + a_SubMaxZ
);
return;
}
if (HasBlockTypes())
{
CropBlockTypes(a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
}
if (HasBlockMetas())
{
CropNibbles(m_BlockMetas, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
}
if (HasBlockLights())
{
CropNibbles(m_BlockLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
}
if (HasBlockSkyLights())
{
CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
}
if (HasBlockEntities())
{
auto maxX = m_Size.x - a_SubMaxX;
auto maxY = m_Size.y - a_SubMaxY;
auto maxZ = m_Size.z - a_SubMaxZ;
// Move and crop block Entities:
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto posX = be->GetPosX();
auto posY = be->GetPosY();
auto posZ = be->GetPosZ();
if (
(posX < a_AddMinX) || (posX >= maxX) ||
(posY < a_AddMinY) || (posY >= maxY) ||
(posZ < a_AddMinZ) || (posZ >= maxZ)
)
{
// The block entity is out of new coord range, remove it:
delete be;
}
else
{
// The block entity is within the new coords, recalculate its coords to match the new area:
posX -= a_AddMinX;
posY -= a_AddMinY;
posZ -= a_AddMinZ;
be->SetPos({posX, posY, posZ});
m_BlockEntities->insert({MakeIndex(posX, posY, posZ), std::move(be)});
}
}
}
m_Origin.Move(a_AddMinX, a_AddMinY, a_AddMinZ);
m_Size.x -= a_AddMinX + a_SubMaxX;
m_Size.y -= a_AddMinY + a_SubMaxY;
m_Size.z -= a_AddMinZ + a_SubMaxZ;
}
void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
{
if (HasBlockTypes())
{
ExpandBlockTypes(a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
}
if (HasBlockMetas())
{
ExpandNibbles(m_BlockMetas, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
}
if (HasBlockLights())
{
ExpandNibbles(m_BlockLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
}
if (HasBlockSkyLights())
{
ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
}
if (HasBlockEntities())
{
// Move block entities:
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto posX = be->GetPosX() + a_SubMinX;
auto posY = be->GetPosY() + a_SubMinY;
auto posZ = be->GetPosZ() + a_SubMinZ;
be->SetPos({posX, posY, posZ});
m_BlockEntities->insert({MakeIndex(posX, posY, posZ), std::move(be)});
}
}
m_Origin.Move(-a_SubMinX, -a_SubMinY, -a_SubMinZ);
m_Size.x += a_SubMinX + a_AddMaxX;
m_Size.y += a_SubMinY + a_AddMaxY;
m_Size.z += a_SubMinZ + a_AddMaxZ;
}
void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy)
{
const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas();
NIBBLETYPE * DstMetas = GetBlockMetas();
bool IsDummyMetas = ((SrcMetas == nullptr) || (DstMetas == nullptr));
if (IsDummyMetas)
{
MergeByStrategy<false>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas);
}
else
{
MergeByStrategy<true>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas);
}
}
void cBlockArea::Merge(const cBlockArea & a_Src, const Vector3i & a_RelMinCoords, eMergeStrategy a_Strategy)
{
Merge(a_Src, a_RelMinCoords.x, a_RelMinCoords.y, a_RelMinCoords.z, a_Strategy);
}
void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight)
{
if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
{
LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
__FUNCTION__, a_DataTypes, GetDataTypes()
);
a_DataTypes = a_DataTypes & GetDataTypes();
}
size_t BlockCount = GetBlockCount();
if ((a_DataTypes & baTypes) != 0)
{
for (size_t i = 0; i < BlockCount; i++)
{
m_BlockTypes[i] = a_BlockType;
}
}
if ((a_DataTypes & baMetas) != 0)
{
for (size_t i = 0; i < BlockCount; i++)
{
m_BlockMetas[i] = a_BlockMeta;
}
}
if ((a_DataTypes & baLight) != 0)
{
for (size_t i = 0; i < BlockCount; i++)
{
m_BlockLight[i] = a_BlockLight;
}
}
if ((a_DataTypes & baSkyLight) != 0)
{
for (size_t i = 0; i < BlockCount; i++)
{
m_BlockSkyLight[i] = a_BlockSkyLight;
}
}
// If the area contains block entities, remove those not matching and replace with whatever block entity block was filled
if (HasBlockEntities() && ((a_DataTypes & baTypes) != 0))
{
if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
{
RescanBlockEntities();
}
else
{
ClearBlockEntities(*m_BlockEntities);
}
}
}
void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
{
LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
__FUNCTION__, a_DataTypes, GetDataTypes()
);
a_DataTypes = a_DataTypes & GetDataTypes();
}
if ((a_DataTypes & baTypes) != 0)
{
for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
{
m_BlockTypes[MakeIndex(x, y, z)] = a_BlockType;
} // for x, z, y
}
if ((a_DataTypes & baMetas) != 0)
{
for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
{
m_BlockMetas[MakeIndex(x, y, z)] = a_BlockMeta;
} // for x, z, y
}
if ((a_DataTypes & baLight) != 0)
{
for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
{
m_BlockLight[MakeIndex(x, y, z)] = a_BlockLight;
} // for x, z, y
}
if ((a_DataTypes & baSkyLight) != 0)
{
for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
{
m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight;
} // for x, z, y
}
// If the area contains block entities, remove those in the affected cuboid and replace with whatever block entity block was filled:
if (HasBlockEntities() && ((a_DataTypes & baTypes) != 0))
{
if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
{
RescanBlockEntities();
}
else
{
ClearBlockEntities(*m_BlockEntities);
}
}
}
void cBlockArea::FillRelCuboid(const cCuboid & a_RelCuboid,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
FillRelCuboid(
a_RelCuboid.p1.x, a_RelCuboid.p2.x,
a_RelCuboid.p1.y, a_RelCuboid.p2.y,
a_RelCuboid.p1.z, a_RelCuboid.p2.z,
a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight
);
}
void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
// Bresenham-3D algorithm for drawing lines:
int dx = abs(a_RelX2 - a_RelX1);
int dy = abs(a_RelY2 - a_RelY1);
int dz = abs(a_RelZ2 - a_RelZ1);
int sx = (a_RelX1 < a_RelX2) ? 1 : -1;
int sy = (a_RelY1 < a_RelY2) ? 1 : -1;
int sz = (a_RelZ1 < a_RelZ2) ? 1 : -1;
if (dx >= std::max(dy, dz)) // x dominant
{
int yd = dy - dx / 2;
int zd = dz - dx / 2;
for (;;)
{
RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
if (a_RelX1 == a_RelX2)
{
break;
}
if (yd >= 0) // move along y
{
a_RelY1 += sy;
yd -= dx;
}
if (zd >= 0) // move along z
{
a_RelZ1 += sz;
zd -= dx;
}
// move along x
a_RelX1 += sx;
yd += dy;
zd += dz;
}
}
else if (dy >= std::max(dx, dz)) // y dominant
{
int xd = dx - dy / 2;
int zd = dz - dy / 2;
for (;;)
{
RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
if (a_RelY1 == a_RelY2)
{
break;
}
if (xd >= 0) // move along x
{
a_RelX1 += sx;
xd -= dy;
}
if (zd >= 0) // move along z
{
a_RelZ1 += sz;
zd -= dy;
}
// move along y
a_RelY1 += sy;
xd += dx;
zd += dz;
}
}
else
{
// z dominant
ASSERT(dz >= std::max(dx, dy));
int xd = dx - dz / 2;
int yd = dy - dz / 2;
for (;;)
{
RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
if (a_RelZ1 == a_RelZ2)
{
break;
}
if (xd >= 0) // move along x
{
a_RelX1 += sx;
xd -= dz;
}
if (yd >= 0) // move along y
{
a_RelY1 += sy;
yd -= dz;
}
// move along z
a_RelZ1 += sz;
xd += dx;
yd += dy;
}
} // if (which dimension is dominant)
}
void cBlockArea::RelLine(const Vector3i & a_Point1, const Vector3i & a_Point2,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
RelLine(
a_Point1.x, a_Point1.y, a_Point1.z,
a_Point2.x, a_Point2.y, a_Point2.z,
a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight
);
}
void cBlockArea::RotateCCW(void)
{
if (!HasBlockTypes())
{
LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
return;
}
if (!HasBlockMetas())
{
// There are no blockmetas to rotate, just use the NoMeta function
RotateCCWNoMeta();
return;
}
// We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
BLOCKARRAY NewTypes{ new BLOCKTYPE[GetBlockCount()] };
NIBBLEARRAY NewMetas{ new NIBBLETYPE[GetBlockCount()] };
for (int x = 0; x < m_Size.x; x++)
{
int NewZ = m_Size.x - x - 1;
for (int z = 0; z < m_Size.z; z++)
{
int NewX = z;
for (int y = 0; y < m_Size.y; y++)
{
auto NewIdx = MakeIndexForSize({ NewX, y, NewZ }, { m_Size.z, m_Size.y, m_Size.x });
auto OldIdx = MakeIndex(x, y, z);
NewTypes[NewIdx] = m_BlockTypes[OldIdx];
NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]);
} // for y
} // for z
} // for x
m_BlockTypes = std::move(NewTypes);
m_BlockMetas = std::move(NewMetas);
// Rotate the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = be->GetPosZ();
auto newY = be->GetPosY();
auto newZ = m_Size.x - be->GetPosX() - 1;
auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
std::swap(m_Size.x, m_Size.z);
}
void cBlockArea::RotateCW(void)
{
if (!HasBlockTypes())
{
LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
return;
}
if (!HasBlockMetas())
{
// There are no blockmetas to rotate, just use the NoMeta function
RotateCWNoMeta();
return;
}
// We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
BLOCKARRAY NewTypes{ new BLOCKTYPE[GetBlockCount()] };
NIBBLEARRAY NewMetas{ new NIBBLETYPE[GetBlockCount()] };
for (int x = 0; x < m_Size.x; x++)
{
int NewZ = x;
for (int z = 0; z < m_Size.z; z++)
{
int NewX = m_Size.z - z - 1;
for (int y = 0; y < m_Size.y; y++)
{
auto NewIdx = MakeIndexForSize({ NewX, y, NewZ }, { m_Size.z, m_Size.y, m_Size.x });
auto OldIdx = MakeIndex(x, y, z);
NewTypes[NewIdx] = m_BlockTypes[OldIdx];
NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]);
} // for y
} // for z
} // for x
m_BlockTypes = std::move(NewTypes);
m_BlockMetas = std::move(NewMetas);
// Rotate the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = m_Size.z - be->GetPosZ() - 1;
auto newY = be->GetPosY();
auto newZ = be->GetPosX();
auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
std::swap(m_Size.x, m_Size.z);
}
void cBlockArea::MirrorXY(void)
{
if (!HasBlockTypes())
{
LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
return;
}
if (!HasBlockMetas())
{
// There are no blockmetas to mirror, just use the NoMeta function
MirrorXYNoMeta();
return;
}
// We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
int HalfZ = m_Size.z / 2;
int MaxZ = m_Size.z - 1;
for (int y = 0; y < m_Size.y; y++)
{
for (int z = 0; z < HalfZ; z++)
{
for (int x = 0; x < m_Size.x; x++)
{
auto Idx1 = MakeIndex(x, y, z);
auto Idx2 = MakeIndex(x, y, MaxZ - z);
std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXY(m_BlockMetas[Idx1]);
NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXY(m_BlockMetas[Idx2]);
m_BlockMetas[Idx1] = Meta2;
m_BlockMetas[Idx2] = Meta1;
} // for x
} // for z
} // for y
// Mirror the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = be->GetPosX();
auto newY = be->GetPosY();
auto newZ = MaxZ - be->GetPosZ();
auto newIdx = MakeIndex(newX, newY, newZ);
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
}
void cBlockArea::MirrorXZ(void)
{
if (!HasBlockTypes())
{
LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
return;
}
if (!HasBlockMetas())
{
// There are no blockmetas to mirror, just use the NoMeta function
MirrorXZNoMeta();
return;
}
// We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
int HalfY = m_Size.y / 2;
int MaxY = m_Size.y - 1;
for (int y = 0; y < HalfY; y++)
{
for (int z = 0; z < m_Size.z; z++)
{
for (int x = 0; x < m_Size.x; x++)
{
auto Idx1 = MakeIndex(x, y, z);
auto Idx2 = MakeIndex(x, MaxY - y, z);
std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXZ(m_BlockMetas[Idx1]);
NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXZ(m_BlockMetas[Idx2]);
m_BlockMetas[Idx1] = Meta2;
m_BlockMetas[Idx2] = Meta1;
} // for x
} // for z
} // for y
// Mirror the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = be->GetPosX();
auto newY = MaxY - be->GetPosY();
auto newZ = be->GetPosZ();
auto newIdx = MakeIndex(newX, newY, newZ);
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
}
void cBlockArea::MirrorYZ(void)
{
if (!HasBlockTypes())
{
LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
return;
}
if (!HasBlockMetas())
{
// There are no blockmetas to mirror, just use the NoMeta function
MirrorYZNoMeta();
return;
}
// We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
int HalfX = m_Size.x / 2;
int MaxX = m_Size.x - 1;
for (int y = 0; y < m_Size.y; y++)
{
for (int z = 0; z < m_Size.z; z++)
{
for (int x = 0; x < HalfX; x++)
{
auto Idx1 = MakeIndex(x, y, z);
auto Idx2 = MakeIndex(MaxX - x, y, z);
std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]);
NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]);
m_BlockMetas[Idx1] = Meta2;
m_BlockMetas[Idx2] = Meta1;
} // for x
} // for z
} // for y
// Mirror the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = MaxX - be->GetPosX();
auto newY = be->GetPosY();
auto newZ = be->GetPosZ();
auto newIdx = MakeIndex(newX, newY, newZ);
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
}
void cBlockArea::RotateCCWNoMeta(void)
{
if (HasBlockTypes())
{
BLOCKARRAY NewTypes{ new BLOCKTYPE[GetBlockCount()] };
for (int x = 0; x < m_Size.x; x++)
{
int NewZ = m_Size.x - x - 1;
for (int z = 0; z < m_Size.z; z++)
{
int NewX = z;
for (int y = 0; y < m_Size.y; y++)
{
NewTypes[MakeIndexForSize({ NewX, y, NewZ }, { m_Size.z, m_Size.y, m_Size.x })] = m_BlockTypes[MakeIndex(x, y, z)];
} // for y
} // for z
} // for x
m_BlockTypes = std::move(NewTypes);
}
if (HasBlockMetas())
{
NIBBLEARRAY NewMetas{ new NIBBLETYPE[GetBlockCount()] };
for (int x = 0; x < m_Size.x; x++)
{
int NewZ = m_Size.x - x - 1;
for (int z = 0; z < m_Size.z; z++)
{
int NewX = z;
for (int y = 0; y < m_Size.y; y++)
{
NewMetas[MakeIndexForSize({ NewX, y, NewZ }, { m_Size.z, m_Size.y, m_Size.x })] = m_BlockMetas[MakeIndex(x, y, z)];
} // for y
} // for z
} // for x
m_BlockMetas = std::move(NewMetas);
}
// Rotate the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = be->GetPosZ();
auto newY = be->GetPosY();
auto newZ = m_Size.x - be->GetPosX() - 1;
auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
std::swap(m_Size.x, m_Size.z);
}
void cBlockArea::RotateCWNoMeta(void)
{
if (HasBlockTypes())
{
BLOCKARRAY NewTypes{ new BLOCKTYPE[GetBlockCount()] };
for (int z = 0; z < m_Size.z; z++)
{
int NewX = m_Size.z - z - 1;
for (int x = 0; x < m_Size.x; x++)
{
int NewZ = x;
for (int y = 0; y < m_Size.y; y++)
{
NewTypes[MakeIndexForSize({ NewX, y, NewZ }, { m_Size.z, m_Size.y, m_Size.x })] = m_BlockTypes[MakeIndex(x, y, z)];
} // for y
} // for x
} // for z
m_BlockTypes = std::move(NewTypes);
}
if (HasBlockMetas())
{
NIBBLEARRAY NewMetas{ new NIBBLETYPE[GetBlockCount()] };
for (int z = 0; z < m_Size.z; z++)
{
int NewX = m_Size.z - z - 1;
for (int x = 0; x < m_Size.x; x++)
{
int NewZ = x;
for (int y = 0; y < m_Size.y; y++)
{
NewMetas[MakeIndexForSize({ NewX, y, NewZ }, { m_Size.z, m_Size.y, m_Size.x })] = m_BlockMetas[MakeIndex(x, y, z)];
} // for y
} // for x
} // for z
m_BlockMetas = std::move(NewMetas);
}
// Rotate the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = m_Size.z - be->GetPosZ() - 1;
auto newY = be->GetPosY();
auto newZ = be->GetPosX();
auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
std::swap(m_Size.x, m_Size.z);
}
void cBlockArea::MirrorXYNoMeta(void)
{
int HalfZ = m_Size.z / 2;
int MaxZ = m_Size.z - 1;
if (HasBlockTypes())
{
for (int y = 0; y < m_Size.y; y++)
{
for (int z = 0; z < HalfZ; z++)
{
for (int x = 0; x < m_Size.x; x++)
{
std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, y, MaxZ - z)]);
} // for x
} // for z
} // for y
} // if (HasBlockTypes)
if (HasBlockMetas())
{
for (int y = 0; y < m_Size.y; y++)
{
for (int z = 0; z < HalfZ; z++)
{
for (int x = 0; x < m_Size.x; x++)
{
std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, y, MaxZ - z)]);
} // for x
} // for z
} // for y
} // if (HasBlockMetas)
// Mirror the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = be->GetPosX();
auto newY = be->GetPosY();
auto newZ = MaxZ - be->GetPosZ();
auto newIdx = MakeIndex(newX, newY, newZ);
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
}
void cBlockArea::MirrorXZNoMeta(void)
{
int HalfY = m_Size.y / 2;
int MaxY = m_Size.y - 1;
if (HasBlockTypes())
{
for (int y = 0; y < HalfY; y++)
{
for (int z = 0; z < m_Size.z; z++)
{
for (int x = 0; x < m_Size.x; x++)
{
std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, MaxY - y, z)]);
} // for x
} // for z
} // for y
} // if (HasBlockTypes)
if (HasBlockMetas())
{
for (int y = 0; y < HalfY; y++)
{
for (int z = 0; z < m_Size.z; z++)
{
for (int x = 0; x < m_Size.x; x++)
{
std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, MaxY - y, z)]);
} // for x
} // for z
} // for y
} // if (HasBlockMetas)
// Mirror the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = be->GetPosX();
auto newY = MaxY - be->GetPosY();
auto newZ = be->GetPosZ();
auto newIdx = MakeIndex(newX, newY, newZ);
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
}
void cBlockArea::MirrorYZNoMeta(void)
{
int HalfX = m_Size.x / 2;
int MaxX = m_Size.x - 1;
if (HasBlockTypes())
{
for (int y = 0; y < m_Size.y; y++)
{
for (int z = 0; z < m_Size.z; z++)
{
for (int x = 0; x < HalfX; x++)
{
std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(MaxX - x, y, z)]);
} // for x
} // for z
} // for y
} // if (HasBlockTypes)
if (HasBlockMetas())
{
for (int y = 0; y < m_Size.y; y++)
{
for (int z = 0; z < m_Size.z; z++)
{
for (int x = 0; x < HalfX; x++)
{
std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(MaxX - x, y, z)]);
} // for x
} // for z
} // for y
} // if (HasBlockMetas)
// Mirror the BlockEntities:
if (HasBlockEntities())
{
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (const auto & keyPair: oldBE)
{
auto & be = keyPair.second;
auto newX = MaxX - be->GetPosX();
auto newY = be->GetPosY();
auto newZ = be->GetPosZ();
auto newIdx = MakeIndex(newX, newY, newZ);
be->SetPos({newX, newY, newZ});
m_BlockEntities->insert({newIdx, std::move(be)});
}
}
}
void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
{
ASSERT(m_BlockTypes != nullptr);
auto idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
m_BlockTypes[idx] = a_BlockType;
// Update the block entities, if appropriate:
if (HasBlockEntities())
{
auto itr = m_BlockEntities->find(idx);
if (itr != m_BlockEntities->end())
{
if (itr->second->GetBlockType() == a_BlockType)
{
// The block entity is for the same block type, keep the current one
return;
}
m_BlockEntities->erase(itr);
}
if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
{
NIBBLETYPE meta = HasBlockMetas() ? m_BlockMetas[idx] : 0;
m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(a_BlockType, meta, {a_RelX, a_RelY, a_RelZ})});
}
}
}
void cBlockArea::SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
{
SetRelBlockType(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_BlockType);
}
void cBlockArea::SetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
{
SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockMeta, GetBlockMetas());
}
void cBlockArea::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta)
{
SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, GetBlockMetas());
}
void cBlockArea::SetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight)
{
SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockLight, GetBlockLight());
}
void cBlockArea::SetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight)
{
SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockLight, GetBlockLight());
}
void cBlockArea::SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight)
{
SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockSkyLight, GetBlockSkyLight());
}
void cBlockArea::SetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight)
{
SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockSkyLight, GetBlockSkyLight());
}
BLOCKTYPE cBlockArea::GetRelBlockType(int a_RelX, int a_RelY, int a_RelZ) const
{
if (m_BlockTypes == nullptr)
{
LOGWARNING("cBlockArea: BlockTypes have not been read!");
return E_BLOCK_AIR;
}
return m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)];
}
BLOCKTYPE cBlockArea::GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
return GetRelBlockType(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z);
}
NIBBLETYPE cBlockArea::GetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const
{
return GetRelNibble(a_RelX, a_RelY, a_RelZ, GetBlockMetas());
}
NIBBLETYPE cBlockArea::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
return GetNibble(a_BlockX, a_BlockY, a_BlockZ, GetBlockMetas());
}
NIBBLETYPE cBlockArea::GetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ) const
{
return GetRelNibble(a_RelX, a_RelY, a_RelZ, GetBlockLight());
}
NIBBLETYPE cBlockArea::GetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
return GetNibble(a_BlockX, a_BlockY, a_BlockZ, GetBlockLight());
}
NIBBLETYPE cBlockArea::GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const
{
return GetRelNibble(a_RelX, a_RelY, a_RelZ, GetBlockSkyLight());
}
NIBBLETYPE cBlockArea::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
return GetNibble(a_BlockX, a_BlockY, a_BlockZ, GetBlockSkyLight());
}
void cBlockArea::SetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
SetRelBlockTypeMeta(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_BlockType, a_BlockMeta);
}
void cBlockArea::SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
auto idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
if (m_BlockTypes == nullptr)
{
LOGWARNING("%s: BlockTypes not available but requested to be written to.", __FUNCTION__);
}
else
{
m_BlockTypes[idx] = a_BlockType;
}
if (m_BlockMetas == nullptr)
{
LOGWARNING("%s: BlockMetas not available but requested to be written to.", __FUNCTION__);
}
else
{
m_BlockMetas[idx] = a_BlockMeta;
}
// Update the block entities, if appropriate:
if (HasBlockEntities())
{
auto itr = m_BlockEntities->find(idx);
if (itr != m_BlockEntities->end())
{
if (itr->second->GetBlockType() == a_BlockType)
{
// The block entity is for the same block type, keep the current one
return;
}
m_BlockEntities->erase(itr);
}
if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
{
m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, {a_RelX, a_RelY, a_RelZ})});
}
}
}
void cBlockArea::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
{
return GetRelBlockTypeMeta(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_BlockType, a_BlockMeta);
}
void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
{
auto idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
if (m_BlockTypes == nullptr)
{
LOGWARNING("cBlockArea: BlockTypes have not been read!");
a_BlockType = E_BLOCK_AIR;
}
else
{
a_BlockType = m_BlockTypes[idx];
}
if (m_BlockMetas == nullptr)
{
LOGWARNING("cBlockArea: BlockMetas have not been read!");
a_BlockMeta = 0;
}
else
{
a_BlockMeta = m_BlockMetas[idx];
}
}
cCuboid cBlockArea::GetBounds(void) const
{
return cCuboid(
m_Origin,
m_Origin + m_Size - Vector3i(1, 1, 1)
);
}
size_t cBlockArea::CountNonAirBlocks(void) const
{
// Check if blocktypes are valid:
if (m_BlockTypes == nullptr)
{
LOGWARNING("%s: BlockTypes have not been read!", __FUNCTION__);
return 0;
}
// Count the blocks:
size_t res = 0;
for (int y = 0; y < m_Size.y; y++)
{
for (int z = 0; z < m_Size.z; z++)
{
for (int x = 0; x < m_Size.x; x++)
{
if (m_BlockTypes[MakeIndex(x, y, z)] != E_BLOCK_AIR)
{
++res;
}
} // for x
} // for z
} // for y
return res;
}
size_t cBlockArea::CountSpecificBlocks(BLOCKTYPE a_BlockType) const
{
// If blocktypes are not valid, log a warning and return zero occurences:
if (m_BlockTypes == nullptr)
{
LOGWARNING("%s: BlockTypes not available!", __FUNCTION__);
return 0;
}
// Count the blocks:
size_t num = GetBlockCount();
size_t res = 0;
for (size_t i = 0; i < num; i++)
{
if (m_BlockTypes[i] == a_BlockType)
{
res++;
}
}
return res;
}
size_t cBlockArea::CountSpecificBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) const
{
// If blocktypes are not valid, log a warning and return zero occurences:
if (m_BlockTypes == nullptr)
{
LOGWARNING("%s: BlockTypes not available!", __FUNCTION__);
return 0;
}
// If blockmetas are not valid, log a warning and count only blocktypes:
if (m_BlockMetas == nullptr)
{
LOGWARNING("%s: BlockMetas not available, comparing blocktypes only!", __FUNCTION__);
return CountSpecificBlocks(a_BlockType);
}
// Count the blocks:
size_t num = GetBlockCount();
size_t res = 0;
for (size_t i = 0; i < num; i++)
{
if ((m_BlockTypes[i] == a_BlockType) && (m_BlockMetas[i] == a_BlockMeta))
{
res++;
}
}
return res;
}
void cBlockArea::GetNonAirCropRelCoords(int & a_MinRelX, int & a_MinRelY, int & a_MinRelZ, int & a_MaxRelX, int & a_MaxRelY, int & a_MaxRelZ, BLOCKTYPE a_IgnoreBlockType)
{
// Check if blocktypes are valid:
if (m_BlockTypes == nullptr)
{
LOGWARNING("%s: BlockTypes have not been read!", __FUNCTION__);
a_MinRelX = 1;
a_MaxRelX = 0;
return;
}
// Walk all the blocks and find the min and max coords for the non-ignored ones:
int MaxX = 0, MinX = m_Size.x - 1;
int MaxY = 0, MinY = m_Size.y - 1;
int MaxZ = 0, MinZ = m_Size.z - 1;
for (int y = 0; y < m_Size.y; y++)
{
for (int z = 0; z < m_Size.z; z++)
{
for (int x = 0; x < m_Size.x; x++)
{
if (m_BlockTypes[MakeIndex(x, y, z)] == a_IgnoreBlockType)
{
continue;
}
// The block is not ignored, update any coords that need updating:
if (x < MinX)
{
MinX = x;
}
if (x > MaxX)
{
MaxX = x;
}
if (y < MinY)
{
MinY = y;
}
if (y > MaxY)
{
MaxY = y;
}
if (z < MinZ)
{
MinZ = z;
}
if (z > MaxZ)
{
MaxZ = z;
}
} // for x
} // for z
} // for y
// Assign to the output:
a_MinRelX = MinX;
a_MinRelY = MinY;
a_MinRelZ = MinZ;
a_MaxRelX = MaxX;
a_MaxRelY = MaxY;
a_MaxRelZ = MaxZ;
}
int cBlockArea::GetDataTypes(void) const
{
int res = 0;
if (m_BlockTypes != nullptr)
{
res |= baTypes;
}
if (m_BlockMetas != nullptr)
{
res |= baMetas;
}
if (m_BlockLight != nullptr)
{
res |= baLight;
}
if (m_BlockSkyLight != nullptr)
{
res |= baSkyLight;
}
if (m_BlockEntities != nullptr)
{
res |= baBlockEntities;
}
return res;
}
bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
{
ASSERT(IsValidDataTypeCombination(a_DataTypes));
BLOCKARRAY NewBlocks;
NIBBLEARRAY NewMetas;
NIBBLEARRAY NewLight;
NIBBLEARRAY NewSkyLight;
cBlockEntitiesPtr NewBlockEntities;
// Try to allocate the new storage
if ((a_DataTypes & baTypes) != 0)
{
NewBlocks.reset(new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ]);
if (NewBlocks == nullptr)
{
return false;
}
}
if ((a_DataTypes & baMetas) != 0)
{
NewMetas.reset(new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]);
if (NewMetas == nullptr)
{
return false;
}
}
if ((a_DataTypes & baLight) != 0)
{
NewLight.reset(new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]);
if (NewLight == nullptr)
{
return false;
}
}
if ((a_DataTypes & baSkyLight) != 0)
{
NewSkyLight.reset(new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]);
if (NewSkyLight == nullptr)
{
return false;
}
}
if ((a_DataTypes & baBlockEntities) != 0)
{
NewBlockEntities.reset(new cBlockEntities);
if (NewBlockEntities == nullptr)
{
return false;
}
}
// Commit changes
m_BlockTypes = std::move(NewBlocks);
m_BlockMetas = std::move(NewMetas);
m_BlockLight = std::move(NewLight);
m_BlockSkyLight = std::move(NewSkyLight);
m_BlockEntities = std::move(NewBlockEntities);
m_Size.Set(a_SizeX, a_SizeY, a_SizeZ);
return true;
}
size_t cBlockArea::MakeIndexForSize(Vector3i a_RelPos, Vector3i a_Size)
{
ASSERT(a_RelPos.x >= 0);
ASSERT(a_RelPos.x < a_Size.x);
ASSERT(a_RelPos.y >= 0);
ASSERT(a_RelPos.y < a_Size.y);
ASSERT(a_RelPos.z >= 0);
ASSERT(a_RelPos.z < a_Size.z);
return static_cast<size_t>(a_RelPos.x + a_RelPos.z * a_Size.x + a_RelPos.y * a_Size.x * a_Size.z);
}
bool cBlockArea::DoWithBlockEntityRelAt(int a_RelX, int a_RelY, int a_RelZ, cBlockEntityCallback a_Callback)
{
ASSERT(IsValidRelCoords(a_RelX, a_RelY, a_RelZ));
if (!HasBlockEntities())
{
return false;
}
auto idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
auto itr = m_BlockEntities->find(idx);
if (itr == m_BlockEntities->end())
{
return false;
}
return a_Callback(*itr->second);
}
bool cBlockArea::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback a_Callback)
{
return DoWithBlockEntityRelAt(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Callback);
}
bool cBlockArea::ForEachBlockEntity(cBlockEntityCallback a_Callback)
{
if (!HasBlockEntities())
{
return true;
}
for (auto & keyPair: *m_BlockEntities)
{
if (a_Callback(*keyPair.second))
{
return false;
}
}
return true;
}
cItems cBlockArea::PickupsFromBlock(Vector3i a_AbsPos, const cEntity * a_Digger, const cItem * a_Tool)
{
auto relPos = a_AbsPos - m_Origin;
BLOCKTYPE blockType;
NIBBLETYPE blockMeta;
GetRelBlockTypeMeta(relPos.x, relPos.y, relPos.z, blockType, blockMeta);
auto blockEntity = GetBlockEntityRel(relPos);
auto handler = BlockHandler(blockType);
return handler->ConvertToPickups(blockMeta, blockEntity, a_Digger, a_Tool);
}
void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
{
if (a_Array == nullptr)
{
LOGWARNING("cBlockArea: datatype has not been read!");
return;
}
a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value;
}
void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
{
SetRelNibble(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Value, a_Array);
}
NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const
{
if (a_Array == nullptr)
{
LOGWARNING("cBlockArea: datatype has not been read!");
return 16;
}
return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)];
}
NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const
{
return GetRelNibble(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Array);
}
void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
{
int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
BLOCKARRAY NewBlockTypes{ new BLOCKTYPE[NewSizeX * NewSizeY * NewSizeZ] };
size_t idx = 0;
for (int y = 0; y < NewSizeY; y++)
{
for (int z = 0; z < NewSizeZ; z++)
{
for (int x = 0; x < NewSizeX; x++)
{
auto OldIndex = MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ);
NewBlockTypes[idx++] = m_BlockTypes[OldIndex];
} // for x
} // for z
} // for y
m_BlockTypes = std::move(NewBlockTypes);
}
void cBlockArea::CropNibbles(NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
{
int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
NIBBLEARRAY NewNibbles{ new NIBBLETYPE[NewSizeX * NewSizeY * NewSizeZ] };
size_t idx = 0;
for (int y = 0; y < NewSizeY; y++)
{
for (int z = 0; z < NewSizeZ; z++)
{
for (int x = 0; x < NewSizeX; x++)
{
NewNibbles[idx++] = a_Array[MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ)];
} // for x
} // for z
} // for y
a_Array = std::move(NewNibbles);
}
void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
{
int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX;
int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY;
int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ;
size_t BlockCount = static_cast<size_t>(NewSizeX * NewSizeY * NewSizeZ);
BLOCKARRAY NewBlockTypes{ new BLOCKTYPE[BlockCount] };
memset(NewBlockTypes.get(), 0, BlockCount * sizeof(BLOCKTYPE));
size_t OldIndex = 0;
for (int y = 0; y < m_Size.y; y++)
{
int IndexBaseY = (y + a_SubMinY) * m_Size.x * m_Size.z;
for (int z = 0; z < m_Size.z; z++)
{
int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_Size.x;
auto idx = static_cast<size_t>(IndexBaseZ + a_SubMinX);
for (int x = 0; x < m_Size.x; x++)
{
NewBlockTypes[idx++] = m_BlockTypes[OldIndex++];
} // for x
} // for z
} // for y
m_BlockTypes = std::move(NewBlockTypes);
}
void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
{
int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX;
int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY;
int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ;
size_t BlockCount = static_cast<size_t>(NewSizeX * NewSizeY * NewSizeZ);
NIBBLEARRAY NewNibbles{ new NIBBLETYPE[BlockCount] };
memset(NewNibbles.get(), 0, BlockCount * sizeof(NIBBLETYPE));
size_t OldIndex = 0;
for (int y = 0; y < m_Size.y; y++)
{
int IndexBaseY = (y + a_SubMinY) * m_Size.x * m_Size.z;
for (int z = 0; z < m_Size.z; z++)
{
int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_Size.x;
auto idx = static_cast<size_t>(IndexBaseZ + a_SubMinX);
for (int x = 0; x < m_Size.x; x++)
{
NewNibbles[idx++] = a_Array[OldIndex++];
} // for x
} // for z
} // for y
a_Array = std::move(NewNibbles);
}
void cBlockArea::RelSetData(
int a_RelX, int a_RelY, int a_RelZ,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
if (!IsValidCoords(a_RelX, a_RelY, a_RelZ))
{
return;
}
auto Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
if ((a_DataTypes & baTypes) != 0)
{
m_BlockTypes[Index] = a_BlockType;
}
if ((a_DataTypes & baMetas) != 0)
{
m_BlockMetas[Index] = a_BlockMeta;
}
if ((a_DataTypes & baLight) != 0)
{
m_BlockLight[Index] = a_BlockLight;
}
if ((a_DataTypes & baSkyLight) != 0)
{
m_BlockSkyLight[Index] = a_BlockSkyLight;
}
// Update the block entities, if appropriate:
if (HasBlockEntities())
{
auto itr = m_BlockEntities->find(Index);
if (itr != m_BlockEntities->end())
{
if (itr->second->GetBlockType() == a_BlockType)
{
// The block entity is for the same block type, keep the current one
return;
}
// The block entity is for a different block type, remove it:
m_BlockEntities->erase(itr);
}
if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
{
// The block type should have a block entity attached to it, create an empty one:
m_BlockEntities->insert({Index, cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, {a_RelX, a_RelY, a_RelZ})});
}
}
}
template <bool MetasValid>
void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas)
{
// Block types are compulsory, block metas are optional
if (!HasBlockTypes() || !a_Src.HasBlockTypes())
{
LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
return;
}
// Dst is *this, Src is a_Src
int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading
int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing
int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy
int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading
int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing
int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy
int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading
int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing
int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy
[&]
{
switch (a_Strategy)
{
case cBlockArea::msOverwrite:
{
InternalMergeBlocks<MetasValid, MergeCombinatorOverwrite<MetasValid> >(
GetBlockTypes(), a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
return;
} // case msOverwrite
case cBlockArea::msFillAir:
{
InternalMergeBlocks<MetasValid, MergeCombinatorFillAir<MetasValid> >(
GetBlockTypes(), a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
return;
} // case msFillAir
case cBlockArea::msImprint:
{
InternalMergeBlocks<MetasValid, MergeCombinatorImprint<MetasValid> >(
GetBlockTypes(), a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
return;
} // case msImprint
case cBlockArea::msLake:
{
InternalMergeBlocks<MetasValid, MergeCombinatorLake<MetasValid> >(
GetBlockTypes(), a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
return;
} // case msLake
case cBlockArea::msSpongePrint:
{
InternalMergeBlocks<MetasValid, MergeCombinatorSpongePrint<MetasValid> >(
GetBlockTypes(), a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
return;
} // case msSpongePrint
case cBlockArea::msDifference:
{
InternalMergeBlocks<MetasValid, MergeCombinatorDifference<MetasValid> >(
GetBlockTypes(), a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
return;
} // case msDifference
case cBlockArea::msSimpleCompare:
{
InternalMergeBlocks<MetasValid, MergeCombinatorSimpleCompare<MetasValid> >(
GetBlockTypes(), a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
return;
} // case msSimpleCompare
case cBlockArea::msMask:
{
InternalMergeBlocks<MetasValid, MergeCombinatorMask<MetasValid> >(
GetBlockTypes(), a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
return;
} // case msMask
} // switch (a_Strategy)
UNREACHABLE("Unsupported block area merge strategy");
}();
if (HasBlockEntities())
{
if (a_Src.HasBlockEntities())
{
MergeBlockEntities(a_RelX, a_RelY, a_RelZ, a_Src);
}
else
{
RescanBlockEntities();
}
}
}
void cBlockArea::ClearBlockEntities(cBlockEntities & a_BlockEntities)
{
for (auto & keyPair: a_BlockEntities)
{
delete keyPair.second;
}
a_BlockEntities.clear();
}
void cBlockArea::MergeBlockEntities(int a_RelX, int a_RelY, int a_RelZ, const cBlockArea & a_Src)
{
// Only supported with both BlockEntities and BlockTypes (caller should check):
ASSERT(HasBlockTypes());
ASSERT(HasBlockEntities());
ASSERT(a_Src.HasBlockTypes());
ASSERT(a_Src.HasBlockEntities());
// Remove block entities that no longer match the block at their coords:
RemoveNonMatchingBlockEntities();
// Clone BEs from a_Src wherever a BE is missing:
for (int y = 0; y < m_Size.y; ++y) for (int z = 0; z < m_Size.z; ++z) for (int x = 0; x < m_Size.x; ++x)
{
auto idx = MakeIndex(x, y, z);
auto type = m_BlockTypes[idx];
if (!cBlockEntity::IsBlockEntityBlockType(type))
{
continue;
}
// This block should have a block entity, check that there is one:
auto itr = m_BlockEntities->find(idx);
if (itr != m_BlockEntities->end())
{
// There is one already
continue;
}
// Copy a BE from a_Src, if it exists there:
auto srcX = x + a_RelX;
auto srcY = y + a_RelY;
auto srcZ = z + a_RelZ;
if (a_Src.IsValidRelCoords(srcX, srcY, srcZ))
{
auto srcIdx = a_Src.MakeIndex(srcX, srcY, srcZ);
auto itrSrc = a_Src.m_BlockEntities->find(srcIdx);
if (itrSrc != a_Src.m_BlockEntities->end())
{
m_BlockEntities->insert({idx, itrSrc->second->Clone({x, y, z})});
continue;
}
}
// No BE found in a_Src, insert a new empty one:
NIBBLETYPE meta = HasBlockMetas() ? m_BlockMetas[idx] : 0;
m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(type, meta, {x, y, z})});
} // for x, z, y
}
void cBlockArea::RescanBlockEntities(void)
{
// Only supported with both BlockEntities and BlockTypes
if (!HasBlockEntities() || !HasBlockTypes())
{
return;
}
// Remove block entities that no longer match the block at their coords:
RemoveNonMatchingBlockEntities();
// Add block entities for all block types that should have a BE assigned to them:
for (int y = 0; y < m_Size.y; ++y) for (int z = 0; z < m_Size.z; ++z) for (int x = 0; x < m_Size.x; ++x)
{
auto idx = MakeIndex(x, y, z);
auto type = m_BlockTypes[idx];
if (!cBlockEntity::IsBlockEntityBlockType(type))
{
continue;
}
// This block should have a block entity, check that there is one:
auto itr = m_BlockEntities->find(idx);
if (itr != m_BlockEntities->end())
{
continue;
}
// Create a new BE for this block:
NIBBLETYPE meta = HasBlockMetas() ? m_BlockMetas[idx] : 0;
m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(type, meta, {x, y, z})});
} // for x, z, y
}
void cBlockArea::RemoveNonMatchingBlockEntities(void)
{
// Only supported with both BlockEntities and BlockTypes:
ASSERT(HasBlockTypes());
ASSERT(HasBlockEntities());
cBlockEntities oldBE;
std::swap(oldBE, *m_BlockEntities);
for (auto & keyPair: oldBE)
{
auto type = m_BlockTypes[static_cast<size_t>(keyPair.first)];
if (type == keyPair.second->GetBlockType())
{
m_BlockEntities->insert({keyPair.first, std::move(keyPair.second)});
}
else
{
delete keyPair.second;
}
}
}
cBlockEntity * cBlockArea::GetBlockEntityRel(Vector3i a_RelPos)
{
if (!HasBlockEntities())
{
return nullptr;
}
auto itr = m_BlockEntities->find(MakeIndex(a_RelPos));
return (itr == m_BlockEntities->end()) ? nullptr : itr->second;
}
////////////////////////////////////////////////////////////////////////////////
// cBlockArea::sBlockEntityDeleter:
void cBlockArea::sBlockEntitiesDeleter::operator () (cBlockEntities * a_BlockEntities)
{
if (a_BlockEntities != nullptr)
{
ClearBlockEntities(*a_BlockEntities);
delete a_BlockEntities;
}
}
////////////////////////////////////////////////////////////////////////////////
// cBlockArea::cChunkReader:
cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
m_Area(a_Area),
m_AreaBounds(cCuboid(a_Area.GetOrigin(), a_Area.GetOrigin() + a_Area.GetSize() - Vector3i(1, 1, 1))),
m_Origin(a_Area.m_Origin.x, a_Area.m_Origin.y, a_Area.m_Origin.z),
m_CurrentChunkX(0),
m_CurrentChunkZ(0)
{
}
void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc)
{
int SizeY = m_Area.m_Size.y;
int MinY = m_Origin.y;
// SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
// OffX, OffZ are the offsets of the current chunk data from the area origin
// BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
int SizeX = cChunkDef::Width;
int SizeZ = cChunkDef::Width;
int OffX, OffZ;
int BaseX, BaseZ;
OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
if (OffX < 0)
{
BaseX = -OffX;
SizeX += OffX; // SizeX is decreased, OffX is negative
OffX = 0;
}
else
{
BaseX = 0;
}
OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
if (OffZ < 0)
{
BaseZ = -OffZ;
SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
OffZ = 0;
}
else
{
BaseZ = 0;
}
// If the chunk extends beyond the area in the X or Z axis, cut off the Size:
if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
{
SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
}
if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
{
SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
}
for (int y = 0; y < SizeY; y++)
{
int ChunkY = MinY + y;
int AreaY = y;
for (int z = 0; z < SizeZ; z++)
{
int ChunkZ = BaseZ + z;
int AreaZ = OffZ + z;
for (int x = 0; x < SizeX; x++)
{
int ChunkX = BaseX + x;
int AreaX = OffX + x;
a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ);
} // for x
} // for z
} // for y
}
bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
{
m_CurrentChunkX = a_ChunkX;
m_CurrentChunkZ = a_ChunkZ;
return true;
}
void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer)
{
int SizeY = m_Area.m_Size.y;
int MinY = m_Origin.y;
// SizeX, SizeZ are the dimensions of the block data to copy from the current chunk (size of the geometric union)
// OffX, OffZ are the offsets of the current chunk data from the area origin
// BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
int SizeX = cChunkDef::Width;
int SizeZ = cChunkDef::Width;
int OffX, OffZ;
int BaseX, BaseZ;
OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
if (OffX < 0)
{
BaseX = -OffX;
SizeX += OffX; // SizeX is decreased, OffX is negative
OffX = 0;
}
else
{
BaseX = 0;
}
OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
if (OffZ < 0)
{
BaseZ = -OffZ;
SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
OffZ = 0;
}
else
{
BaseZ = 0;
}
// If the chunk extends beyond the area in the X or Z axis, cut off the Size:
if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
{
SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
}
if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
{
SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
}
// Copy the blocktypes:
if (m_Area.m_BlockTypes != nullptr)
{
for (int y = 0; y < SizeY; y++)
{
int InChunkY = MinY + y;
int AreaY = y;
for (int z = 0; z < SizeZ; z++)
{
int InChunkZ = BaseZ + z;
int AreaZ = OffZ + z;
for (int x = 0; x < SizeX; x++)
{
int InChunkX = BaseX + x;
int AreaX = OffX + x;
m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlock({ InChunkX, InChunkY, InChunkZ });
} // for x
} // for z
} // for y
}
// Copy the block metas:
if (m_Area.m_BlockMetas != nullptr)
{
for (int y = 0; y < SizeY; y++)
{
int InChunkY = MinY + y;
int AreaY = y;
for (int z = 0; z < SizeZ; z++)
{
int InChunkZ = BaseZ + z;
int AreaZ = OffZ + z;
for (int x = 0; x < SizeX; x++)
{
int InChunkX = BaseX + x;
int AreaX = OffX + x;
m_Area.m_BlockMetas[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetMeta({ InChunkX, InChunkY, InChunkZ });
} // for x
} // for z
} // for y
}
// Copy the blocklight:
if (m_Area.m_BlockLight != nullptr)
{
for (int y = 0; y < SizeY; y++)
{
int InChunkY = MinY + y;
int AreaY = y;
for (int z = 0; z < SizeZ; z++)
{
int InChunkZ = BaseZ + z;
int AreaZ = OffZ + z;
for (int x = 0; x < SizeX; x++)
{
int InChunkX = BaseX + x;
int AreaX = OffX + x;
m_Area.m_BlockLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlockLight({ InChunkX, InChunkY, InChunkZ });
} // for x
} // for z
} // for y
}
// Copy the skylight:
if (m_Area.m_BlockSkyLight != nullptr)
{
for (int y = 0; y < SizeY; y++)
{
int InChunkY = MinY + y;
int AreaY = y;
for (int z = 0; z < SizeZ; z++)
{
int InChunkZ = BaseZ + z;
int AreaZ = OffZ + z;
for (int x = 0; x < SizeX; x++)
{
int InChunkX = BaseX + x;
int AreaX = OffX + x;
m_Area.m_BlockSkyLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetSkyLight({ InChunkX, InChunkY, InChunkZ });
} // for x
} // for z
} // for y
}
}
void cBlockArea::cChunkReader::BlockEntity(cBlockEntity * a_BlockEntity)
{
if (!m_Area.HasBlockEntities())
{
return;
}
if (!m_AreaBounds.IsInside(a_BlockEntity->GetPos()))
{
return;
}
auto areaPos = a_BlockEntity->GetPos() - m_Area.m_Origin;
auto Idx = m_Area.MakeIndex(areaPos);
m_Area.m_BlockEntities->insert({Idx, a_BlockEntity->Clone(areaPos)});
}