Fix cChunkMap issues below with coords below y=0 (#5397)

* return false in cChunkMap::GetBlockTypeMeta if requested height is invalid

* add checks to users of cWorld::GetBlockTypeMeta

* add checks for invalid height to cChunkMap::GetBlock and cChunkMap::GetBlockMeta

* add hle0 to CONTRIBUTORS

* Fix merge conflict with isValidHeight

* Add initialisation contract and fulfil it.

---------

Co-authored-by: Alexander Harkness <me@bearbin.net>
This commit is contained in:
hle0 2024-11-07 18:05:47 -05:00 committed by GitHub
parent 8ca73f1929
commit 33b9c5dc6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 130 additions and 49 deletions

View File

@ -48,8 +48,12 @@ void cBlockPistonHandler::ExtendPiston(Vector3i a_BlockPos, cWorld & a_World)
BLOCKTYPE pistonBlock;
NIBBLETYPE pistonMeta;
a_World.GetBlockTypeMeta(a_BlockPos, pistonBlock, pistonMeta);
a_World.BroadcastBlockAction(a_BlockPos, PistonExtendAction, pistonMeta, pistonBlock);
if (a_World.GetBlockTypeMeta(a_BlockPos, pistonBlock, pistonMeta))
{
a_World.BroadcastBlockAction(
a_BlockPos, PistonExtendAction, pistonMeta, pistonBlock
);
}
}
// Client expects the server to "play" the animation before setting the final blocks
@ -60,9 +64,12 @@ void cBlockPistonHandler::ExtendPiston(Vector3i a_BlockPos, cWorld & a_World)
{
BLOCKTYPE pistonBlock;
NIBBLETYPE pistonMeta;
World.GetBlockTypeMeta(a_BlockPos, pistonBlock, pistonMeta);
if ((pistonBlock != E_BLOCK_PISTON) && !IsSticky(pistonBlock))
if (
!World.GetBlockTypeMeta(a_BlockPos, pistonBlock, pistonMeta) ||
((pistonBlock != E_BLOCK_PISTON) && !IsSticky(pistonBlock))
)
{
// Ensure we operate on a piston to avoid spurious behaviour
// Note that the scheduled task may result in the block type of a_BlockPos changing
@ -104,17 +111,23 @@ void cBlockPistonHandler::RetractPiston(Vector3i a_BlockPos, cWorld & a_World)
{
BLOCKTYPE pistonBlock;
NIBBLETYPE pistonMeta;
a_World.GetBlockTypeMeta(a_BlockPos, pistonBlock, pistonMeta);
a_World.BroadcastBlockAction(a_BlockPos, PistonRetractAction, pistonMeta, pistonBlock);
if (a_World.GetBlockTypeMeta(a_BlockPos, pistonBlock, pistonMeta))
{
a_World.BroadcastBlockAction(
a_BlockPos, PistonRetractAction, pistonMeta, pistonBlock
);
}
}
a_World.ScheduleTask(1_tick, [a_BlockPos](cWorld & World)
{
BLOCKTYPE pistonBlock;
NIBBLETYPE pistonMeta;
World.GetBlockTypeMeta(a_BlockPos, pistonBlock, pistonMeta);
if ((pistonBlock != E_BLOCK_PISTON) && !IsSticky(pistonBlock))
if (
!World.GetBlockTypeMeta(a_BlockPos, pistonBlock, pistonMeta) ||
((pistonBlock != E_BLOCK_PISTON) && !IsSticky(pistonBlock))
)
{
// Ensure we operate on a piston to avoid spurious behaviour
// Note that the scheduled task may result in the block type of a_BlockPos changing
@ -189,19 +202,20 @@ void cBlockPistonHandler::PushBlocks(
NIBBLETYPE moveMeta;
for (auto & moveBlockPos : sortedBlocks)
{
a_World.GetBlockTypeMeta(moveBlockPos, moveBlock, moveMeta);
if (cBlockInfo::IsPistonBreakable(moveBlock))
if (a_World.GetBlockTypeMeta(moveBlockPos, moveBlock, moveMeta))
{
// Block is breakable, drop it:
a_World.DropBlockAsPickups(moveBlockPos, nullptr, nullptr);
}
else
{
// Not breakable, just move it
a_World.SetBlock(moveBlockPos, E_BLOCK_AIR, 0);
moveBlockPos += a_PushDir;
a_World.SetBlock(moveBlockPos, moveBlock, moveMeta);
if (cBlockInfo::IsPistonBreakable(moveBlock))
{
// Block is breakable, drop it:
a_World.DropBlockAsPickups(moveBlockPos, nullptr, nullptr);
}
else
{
// Not breakable, just move it
a_World.SetBlock(moveBlockPos, E_BLOCK_AIR, 0);
moveBlockPos += a_PushDir;
a_World.SetBlock(moveBlockPos, moveBlock, moveMeta);
}
}
}
}
@ -232,7 +246,10 @@ bool cBlockPistonHandler::CanPushBlock(
BLOCKTYPE currBlock;
NIBBLETYPE currMeta;
a_World.GetBlockTypeMeta(a_BlockPos, currBlock, currMeta);
if (!a_World.GetBlockTypeMeta(a_BlockPos, currBlock, currMeta))
{
return !a_RequirePushable;
}
if (currBlock == E_BLOCK_AIR)
{

View File

@ -474,6 +474,11 @@ void cChunkMap::CollectPickupsByEntity(cEntity & a_Entity)
BLOCKTYPE cChunkMap::GetBlock(Vector3i a_BlockPos) const
{
if (!cChunkDef::IsValidHeight(a_BlockPos))
{
return 0;
}
auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos);
auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos);
@ -493,6 +498,11 @@ BLOCKTYPE cChunkMap::GetBlock(Vector3i a_BlockPos) const
NIBBLETYPE cChunkMap::GetBlockMeta(Vector3i a_BlockPos) const
{
if (!cChunkDef::IsValidHeight(a_BlockPos))
{
return 0;
}
auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos);
auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos);
@ -585,6 +595,14 @@ void cChunkMap::SetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE
bool cChunkMap::GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
{
if (!cChunkDef::IsValidHeight(a_BlockPos))
{
// Initialise the params to fulfil our contract.
a_BlockType = 0;
a_BlockMeta = 0;
return false;
}
auto chunkCoord = cChunkDef::BlockToChunk(a_BlockPos);
auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkCoord);
@ -595,6 +613,10 @@ bool cChunkMap::GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, N
Chunk->GetBlockTypeMeta(relPos, a_BlockType, a_BlockMeta);
return true;
}
// Initialise the params to fulfil our contract.
a_BlockType = 0;
a_BlockMeta = 0;
return false;
}

View File

@ -115,6 +115,10 @@ public:
void SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_BlockMeta);
void SetBlock (Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
/** Get the block type and meta at the specified coords
Will always initialise a_BlockType and a_BlockMeta if called.
Returns false if the data could not be retrieved, either because the chunk is invalid or the height is invalid.
Otherwise, returns true. */
bool GetBlockTypeMeta (Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
bool GetBlockInfo (Vector3i, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) const;

View File

@ -1254,7 +1254,10 @@ void cClientHandle::HandleBlockDigStarted(Vector3i a_BlockPos, eBlockFace a_Bloc
BLOCKTYPE DiggingBlock;
NIBBLETYPE DiggingMeta;
m_Player->GetWorld()->GetBlockTypeMeta(a_BlockPos, DiggingBlock, DiggingMeta);
if (!m_Player->GetWorld()->GetBlockTypeMeta(a_BlockPos, DiggingBlock, DiggingMeta))
{
return;
}
if (
m_Player->IsGameModeCreative() &&
@ -1322,7 +1325,10 @@ void cClientHandle::HandleBlockDigFinished(Vector3i a_BlockPos, eBlockFace a_Blo
BLOCKTYPE DugBlock;
NIBBLETYPE DugMeta;
m_Player->GetWorld()->GetBlockTypeMeta(a_BlockPos, DugBlock, DugMeta);
if (!m_Player->GetWorld()->GetBlockTypeMeta(a_BlockPos, DugBlock, DugMeta))
{
return;
}
if (!m_Player->IsGameModeCreative())
{

View File

@ -2594,9 +2594,10 @@ bool cPlayer::IsInsideWater()
BLOCKTYPE Block;
NIBBLETYPE Meta;
m_World->GetBlockTypeMeta(EyePos, Block, Meta);
if ((Block != E_BLOCK_WATER) && (Block != E_BLOCK_STATIONARY_WATER))
if (
!m_World->GetBlockTypeMeta(GetEyePosition().Floor(), Block, Meta) ||
((Block != E_BLOCK_WATER) && (Block != E_BLOCK_STATIONARY_WATER))
)
{
return false;
}

View File

@ -27,7 +27,10 @@ public:
auto & World = *a_Player.GetWorld();
BLOCKTYPE HeadType;
NIBBLETYPE HeadMeta;
World.GetBlockTypeMeta(HeadPosition, HeadType, HeadMeta);
if (!World.GetBlockTypeMeta(HeadPosition, HeadType, HeadMeta))
{
return false;
}
// Vanilla only allows beds to be placed into air.
// Check if there is empty space for the "head" block:

View File

@ -29,7 +29,10 @@ public:
const auto TopPos = a_PlacePosition.addedY(1);
BLOCKTYPE TopType;
NIBBLETYPE TopMeta;
World.GetBlockTypeMeta(TopPos, TopType, TopMeta);
if (!World.GetBlockTypeMeta(TopPos, TopType, TopMeta))
{
return false;
}
if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(World, a_HeldItem, TopPos, TopMeta, a_ClickedBlockFace, false))
{

View File

@ -54,7 +54,10 @@ public:
{
BLOCKTYPE TopType;
NIBBLETYPE TopMeta;
World.GetBlockTypeMeta(UpperBlockPosition, TopType, TopMeta);
if (!World.GetBlockTypeMeta(UpperBlockPosition, TopType, TopMeta))
{
return false;
}
if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(World, a_HeldItem, UpperBlockPosition, TopMeta, a_ClickedBlockFace, false))
{

View File

@ -54,10 +54,12 @@ public:
// Cocoa (brown dye) can be planted on jungle logs:
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
a_World->GetBlockTypeMeta(a_ClickedBlockPos, BlockType, BlockMeta);
// Check if the block that the player clicked is a jungle log.
if ((BlockType != E_BLOCK_LOG) || ((BlockMeta & 0x03) != E_META_LOG_JUNGLE))
if (
!a_World->GetBlockTypeMeta(a_ClickedBlockPos, BlockType, BlockMeta) ||
((BlockType != E_BLOCK_LOG) || ((BlockMeta & 0x03) != E_META_LOG_JUNGLE))
)
{
return false;
}

View File

@ -35,8 +35,8 @@ public:
{
BLOCKTYPE FacingBlock;
NIBBLETYPE FacingMeta;
a_World->GetBlockTypeMeta(a_ClickedBlockPos, FacingBlock, FacingMeta);
if (FacingBlock == E_BLOCK_END_PORTAL_FRAME)
if (a_World->GetBlockTypeMeta(a_ClickedBlockPos, FacingBlock, FacingMeta) && (FacingBlock == E_BLOCK_END_PORTAL_FRAME))
{
// Fill the portal frame. E_META_END_PORTAL_EYE is the bit for holding the eye of ender.
if ((FacingMeta & E_META_END_PORTAL_FRAME_EYE) != E_META_END_PORTAL_FRAME_EYE)

View File

@ -46,7 +46,10 @@ public:
// Can only transform dirt or grass blocks:
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
a_World->GetBlockTypeMeta(a_ClickedBlockPos, BlockType, BlockMeta);
if (!a_World->GetBlockTypeMeta(a_ClickedBlockPos, BlockType, BlockMeta))
{
return false;
}
if ((BlockType != E_BLOCK_DIRT) && (BlockType != E_BLOCK_GRASS))
{
return false;

View File

@ -32,7 +32,10 @@ public:
{
BLOCKTYPE Block;
NIBBLETYPE BlockMeta;
a_World->GetBlockTypeMeta(a_ClickedBlockPos, Block, BlockMeta);
if (!a_World->GetBlockTypeMeta(a_ClickedBlockPos, Block, BlockMeta))
{
return false;
}
if ((Block == E_BLOCK_LEAVES) || (Block == E_BLOCK_NEW_LEAVES))
{

View File

@ -1065,9 +1065,12 @@ void cSlotAreaAnvil::OnTakeResult(cPlayer & a_Player)
BLOCKTYPE Block;
NIBBLETYPE BlockMeta;
a_Player.GetWorld()->GetBlockTypeMeta(BlockPos, Block, BlockMeta);
if (!a_Player.IsGameModeCreative() && (Block == E_BLOCK_ANVIL) && GetRandomProvider().RandBool(0.12))
if (
a_Player.GetWorld()->GetBlockTypeMeta(BlockPos, Block, BlockMeta) &&
!a_Player.IsGameModeCreative() && (Block == E_BLOCK_ANVIL) &&
GetRandomProvider().RandBool(0.12)
)
{
NIBBLETYPE Orientation = BlockMeta & 0x3;
NIBBLETYPE AnvilDamage = BlockMeta >> 2;

View File

@ -1539,8 +1539,7 @@ bool cWorld::GetLargeTreeAdjustment(Vector3i & a_BlockPos, NIBBLETYPE a_Meta)
{
NIBBLETYPE meta;
BLOCKTYPE type;
GetBlockTypeMeta(a_BlockPos.addedXZ(x, z), type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((meta & 0x07) == a_Meta);
IsLarge = IsLarge && GetBlockTypeMeta(a_BlockPos.addedXZ(x, z), type, meta) && (type == E_BLOCK_SAPLING) && ((meta & 0x07) == a_Meta);
}
}
@ -1557,8 +1556,7 @@ bool cWorld::GetLargeTreeAdjustment(Vector3i & a_BlockPos, NIBBLETYPE a_Meta)
{
NIBBLETYPE meta;
BLOCKTYPE type;
GetBlockTypeMeta(a_BlockPos.addedXZ(x, z), type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((meta & 0x07) == a_Meta);
IsLarge = IsLarge && GetBlockTypeMeta(a_BlockPos.addedXZ(x, z), type, meta) && (type == E_BLOCK_SAPLING) && ((meta & 0x07) == a_Meta);
}
}
@ -1576,8 +1574,7 @@ bool cWorld::GetLargeTreeAdjustment(Vector3i & a_BlockPos, NIBBLETYPE a_Meta)
{
NIBBLETYPE meta;
BLOCKTYPE type;
GetBlockTypeMeta(a_BlockPos.addedXZ(x, z), type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((meta & 0x07) == a_Meta);
IsLarge = IsLarge && GetBlockTypeMeta(a_BlockPos.addedXZ(x, z), type, meta) && (type == E_BLOCK_SAPLING) && ((meta & 0x07) == a_Meta);
}
}
@ -1596,8 +1593,7 @@ bool cWorld::GetLargeTreeAdjustment(Vector3i & a_BlockPos, NIBBLETYPE a_Meta)
{
NIBBLETYPE meta;
BLOCKTYPE type;
GetBlockTypeMeta(a_BlockPos.addedXZ(x, z), type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((meta & 0x07) == a_Meta);
IsLarge = IsLarge && GetBlockTypeMeta(a_BlockPos.addedXZ(x, z), type, meta) && (type == E_BLOCK_SAPLING) && ((meta & 0x07) == a_Meta);
}
}
@ -2044,7 +2040,10 @@ void cWorld::PlaceBlock(const Vector3i a_Position, const BLOCKTYPE a_BlockType,
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
GetBlockTypeMeta(a_Position, BlockType, BlockMeta);
if (!GetBlockTypeMeta(a_Position, BlockType, BlockMeta))
{
return;
}
SetBlock(a_Position, a_BlockType, a_BlockMeta);
@ -2070,7 +2069,10 @@ bool cWorld::DigBlock(Vector3i a_BlockPos, const cEntity * a_Digger)
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
GetBlockTypeMeta(a_BlockPos, BlockType, BlockMeta);
if (!GetBlockTypeMeta(a_BlockPos, BlockType, BlockMeta))
{
return false;
}
if (!m_ChunkMap.DigBlock(a_BlockPos))
{
@ -2617,7 +2619,11 @@ bool cWorld::IsTrapdoorOpen(int a_BlockX, int a_BlockY, int a_BlockZ)
{
BLOCKTYPE Block;
NIBBLETYPE Meta;
GetBlockTypeMeta({ a_BlockX, a_BlockY, a_BlockZ }, Block, Meta);
if (!GetBlockTypeMeta({ a_BlockX, a_BlockY, a_BlockZ }, Block, Meta))
{
return false;
}
if ((Block != E_BLOCK_TRAPDOOR) && (Block != E_BLOCK_IRON_TRAPDOOR))
{
return false;
@ -2634,7 +2640,11 @@ bool cWorld::SetTrapdoorOpen(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Op
{
BLOCKTYPE Block;
NIBBLETYPE Meta;
GetBlockTypeMeta({ a_BlockX, a_BlockY, a_BlockZ }, Block, Meta);
if (!GetBlockTypeMeta({ a_BlockX, a_BlockY, a_BlockZ }, Block, Meta))
{
return false;
}
if ((Block != E_BLOCK_TRAPDOOR) && (Block != E_BLOCK_IRON_TRAPDOOR))
{
return false;

View File

@ -109,6 +109,7 @@ static void TestParser5(void)
static void TestParser6(void)
{
cCompositeChat Msg;