1
0

Unify DoWithBlockEntity (#5168)

+ DoWith calls now broadcast the block entity and mark the chunk dirty
+ Add block entity change queue to synchronise BE updates with block updates
* Fixed a few incorrect assertions about BE type
- Remove manual overloads
This commit is contained in:
Tiger Wang 2021-03-28 14:40:57 +01:00 committed by GitHub
parent 5123850db0
commit 748b121703
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 550 additions and 1596 deletions

View File

@ -211,228 +211,6 @@ public:
/** Template for the bindings for the DoWithXYZAt(X, Y, Z) functions that don't need to check their coords. */
template <
class SELF,
class ITEM,
bool (SELF::*DoWithFn)(int, int, int, cFunctionRef<bool(ITEM &)>)
>
static int DoWithXYZ(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamNumber(2, 4) ||
!L.CheckParamFunction(5) ||
!L.CheckParamEnd(6)
)
{
return 0;
}
// Get parameters:
SELF * Self = nullptr;
int BlockX = 0;
int BlockY = 0;
int BlockZ = 0;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, FnRef);
if (Self == nullptr)
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #5");
}
// Call the DoWith function:
bool res = (Self->*DoWithFn)(BlockX, BlockY, BlockZ, [&](ITEM & a_Item)
{
bool ret = false;
L.Call(FnRef, &a_Item, cLuaState::Return, ret);
return ret;
}
);
// Push the result as the return value:
L.Push(res);
return 1;
}
/** Template for the bindings for the DoWithXYZAt(X, Y, Z) functions that need to check their coords. */
template <
class SELF,
class ITEM,
bool (SELF::*DoWithFn)(int, int, int, cFunctionRef<bool(ITEM &)>),
bool (SELF::*CoordCheckFn)(int, int, int) const
>
static int DoWithXYZ(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamNumber(2, 4) ||
!L.CheckParamFunction(5) ||
!L.CheckParamEnd(6)
)
{
return 0;
}
// Get parameters:
SELF * Self = nullptr;
int BlockX = 0;
int BlockY = 0;
int BlockZ = 0;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, FnRef);
if (Self == nullptr)
{
return L.ApiParamError("Invalid 'self'");
}
if (!FnRef.IsValid())
{
return L.ApiParamError("Expected a valid callback function for parameter #5");
}
if (!(Self->*CoordCheckFn)(BlockX, BlockY, BlockZ))
{
return L.FApiParamError("The provided coordinates ({0}) are not valid",
Vector3i{BlockX, BlockY, BlockZ}
);
}
// Call the DoWith function:
bool res = (Self->*DoWithFn)(BlockX, BlockY, BlockZ, [&](ITEM & a_Item)
{
bool ret = false;
L.Call(FnRef, &a_Item, cLuaState::Return, ret);
return ret;
}
);
// Push the result as the return value:
L.Push(res);
return 1;
}
template <
class Ty1,
class Ty2,
bool (Ty1::*ForEachFn)(int, int, cFunctionRef<bool(Ty2 &)>)
>
static int ForEachInChunk(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamNumber(2, 3) ||
!L.CheckParamFunction(4) ||
!L.CheckParamEnd(5)
)
{
return 0;
}
// Get parameters:
Ty1 * Self = nullptr;
int ChunkX = 0;
int ChunkZ = 0;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, ChunkX, ChunkZ, FnRef);
if (Self == nullptr)
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #4");
}
// Call the DoWith function:
bool res = (Self->*ForEachFn)(ChunkX, ChunkZ, [&](Ty2 & a_Item)
{
bool ret = false;
L.Call(FnRef, &a_Item, cLuaState::Return, ret);
return ret;
}
);
// Push the result as the return value:
L.Push(res);
return 1;
}
template <
class Ty1,
class Ty2,
bool (Ty1::*ForEachFn)(const cBoundingBox &, cFunctionRef<bool(Ty2 &)>)
>
static int ForEachInBox(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamUserType(2, "cBoundingBox") ||
!L.CheckParamFunction(3) ||
!L.CheckParamEnd(4)
)
{
return 0;
}
// Get the params:
Ty1 * Self = nullptr;
cBoundingBox * Box = nullptr;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, Box, FnRef);
if ((Self == nullptr) || (Box == nullptr))
{
LOGWARNING("Invalid world (%p) or boundingbox (%p)", static_cast<void *>(Self), static_cast<void *>(Box));
L.LogStackTrace();
return 0;
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2");
}
bool res = (Self->*ForEachFn)(*Box, [&](Ty2 & a_Item)
{
bool ret = false;
if (!L.Call(FnRef, &a_Item, cLuaState::Return, ret))
{
LOGWARNING("Failed to call Lua callback");
L.LogStackTrace();
return true; // Abort enumeration
}
return ret;
}
);
// Push the result as the return value:
L.Push(res);
return 1;
}
template <
class Ty1,
class Ty2,

View File

@ -15,6 +15,66 @@
/** Template for the bindings for the DoWithXYZAt(X, Y, Z) functions that need to check their coords. */
template <
class SELF,
class ITEM,
bool (SELF::*DoWithFn)(int, int, int, cFunctionRef<bool(ITEM &)>),
bool (SELF::*CoordCheckFn)(int, int, int) const
>
static int DoWithXYZ(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamNumber(2, 4) ||
!L.CheckParamFunction(5) ||
!L.CheckParamEnd(6)
)
{
return 0;
}
// Get parameters:
SELF * Self = nullptr;
int BlockX = 0;
int BlockY = 0;
int BlockZ = 0;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, FnRef);
if (Self == nullptr)
{
return L.ApiParamError("Invalid 'self'");
}
if (!FnRef.IsValid())
{
return L.ApiParamError("Expected a valid callback function for parameter #5");
}
if (!(Self->*CoordCheckFn)(BlockX, BlockY, BlockZ))
{
return L.FApiParamError("The provided coordinates ({0}) are not valid",
Vector3i{BlockX, BlockY, BlockZ}
);
}
// Call the DoWith function:
bool res = (Self->*DoWithFn)(BlockX, BlockY, BlockZ, [&](ITEM & a_Item)
{
bool ret = false;
L.Call(FnRef, &a_Item, cLuaState::Return, ret);
return ret;
}
);
// Push the result as the return value:
L.Push(res);
return 1;
}
/** Reads params that together form a Cuboid.
These can be:
- 6 numbers (MinX, MaxX, MinY, MaxY, MinZ, MaxZ)

View File

@ -12,6 +12,23 @@
#include "PluginLua.h"
#include "LuaChunkStay.h"
#include "BlockEntities/BeaconEntity.h"
#include "BlockEntities/BedEntity.h"
#include "BlockEntities/BrewingstandEntity.h"
#include "BlockEntities/ChestEntity.h"
#include "BlockEntities/CommandBlockEntity.h"
#include "BlockEntities/DispenserEntity.h"
#include "BlockEntities/DropSpenserEntity.h"
#include "BlockEntities/DropperEntity.h"
#include "BlockEntities/FlowerPotEntity.h"
#include "BlockEntities/FurnaceEntity.h"
#include "BlockEntities/HopperEntity.h"
#include "BlockEntities/MobHeadEntity.h"
#include "BlockEntities/NoteEntity.h"
/** Check that a Lua parameter is either a vector or 3 numbers in sequence
\param L The Lua state
@ -50,6 +67,183 @@ static bool GetStackVectorOr3Numbers(cLuaState & L, int a_Index, Vector3<T> & a_
/** Template for the bindings for the DoWithXYZAt(X, Y, Z) functions that don't need to check their coords. */
template <class BlockEntityType, BLOCKTYPE... BlockTypes>
static int DoWithBlockEntityAt(lua_State * tolua_S)
{
cLuaState L(tolua_S);
int OffsetIndex;
// Check params:
if (
!L.CheckParamSelf("cWorld") ||
!CheckParamVectorOr3Numbers(L, "Vector3<int>", 2, OffsetIndex) ||
!L.CheckParamFunction(OffsetIndex) ||
!L.CheckParamEnd(OffsetIndex + 1)
)
{
return 0;
}
cWorld * Self = nullptr;
Vector3i Position;
cLuaState::cRef FnRef;
// Get parameters:
if (
!L.GetStackValues(1, Self) ||
!GetStackVectorOr3Numbers(L, 2, Position) ||
!L.GetStackValues(OffsetIndex, FnRef)
)
{
return 0;
}
if (Self == nullptr)
{
return L.ApiParamError("Invalid 'self'");
}
if (!FnRef.IsValid())
{
return L.ApiParamError("Expected a valid callback function for parameter %i", OffsetIndex);
}
// Call the DoWith function:
bool res = Self->DoWithBlockEntityAt(Position, [&L, &FnRef](cBlockEntity & a_BlockEntity)
{
if constexpr (sizeof...(BlockTypes) != 0)
{
if (((a_BlockEntity.GetBlockType() != BlockTypes) && ...))
{
return false;
}
}
bool ret = false;
L.Call(FnRef, static_cast<BlockEntityType *>(&a_BlockEntity), cLuaState::Return, ret);
return ret;
});
// Push the result as the return value:
L.Push(res);
return 1;
}
template <
class Ty1,
class Ty2,
bool (Ty1::*ForEachFn)(const cBoundingBox &, cFunctionRef<bool(Ty2 &)>)
>
static int ForEachInBox(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamUserType(2, "cBoundingBox") ||
!L.CheckParamFunction(3) ||
!L.CheckParamEnd(4)
)
{
return 0;
}
// Get the params:
Ty1 * Self = nullptr;
cBoundingBox * Box = nullptr;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, Box, FnRef);
if ((Self == nullptr) || (Box == nullptr))
{
return L.ApiParamError("Invalid world (%p) or boundingbox (%p)", static_cast<void *>(Self), static_cast<void *>(Box));
}
if (!FnRef.IsValid())
{
return L.ApiParamError("Expected a valid callback function for parameter #2");
}
bool res = (Self->*ForEachFn)(*Box, [&](Ty2 & a_Item)
{
bool ret = false;
if (!L.Call(FnRef, &a_Item, cLuaState::Return, ret))
{
LOGWARNING("Failed to call Lua callback");
L.LogStackTrace();
return true; // Abort enumeration
}
return ret;
}
);
// Push the result as the return value:
L.Push(res);
return 1;
}
template <class BlockEntityType, BLOCKTYPE... BlockTypes>
static int ForEachBlockEntityInChunk(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamSelf("cWorld") ||
!L.CheckParamNumber(2, 3) ||
!L.CheckParamFunction(4) ||
!L.CheckParamEnd(5)
)
{
return 0;
}
// Get parameters:
cWorld * Self = nullptr;
int ChunkX = 0;
int ChunkZ = 0;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, ChunkX, ChunkZ, FnRef);
if (Self == nullptr)
{
return L.ApiParamError("Error in function call '#funcname#': Invalid 'self'");
}
if (!FnRef.IsValid())
{
return L.ApiParamError("Expected a valid callback function for parameter #4");
}
// Call the ForEach function:
bool res = Self->ForEachBlockEntityInChunk(ChunkX, ChunkZ, [&L, &FnRef](cBlockEntity & a_BlockEntity)
{
if constexpr (sizeof...(BlockTypes) != 0)
{
if (((a_BlockEntity.GetBlockType() != BlockTypes) && ...))
{
return false;
}
}
bool ret = false;
L.Call(FnRef, static_cast<BlockEntityType *>(&a_BlockEntity), cLuaState::Return, ret);
return ret;
});
// Push the result as the return value:
L.Push(res);
return 1;
}
static int tolua_cWorld_BroadcastBlockAction(lua_State * tolua_S)
{
/* Function signature:
@ -566,6 +760,53 @@ static int tolua_cWorld_FastSetBlock(lua_State * tolua_S)
static int tolua_cWorld_ForEachEntityInChunk(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamNumber(2, 3) ||
!L.CheckParamFunction(4) ||
!L.CheckParamEnd(5)
)
{
return 0;
}
// Get parameters:
cWorld * Self = nullptr;
int ChunkX = 0;
int ChunkZ = 0;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, ChunkX, ChunkZ, FnRef);
if (Self == nullptr)
{
return L.ApiParamError("Invalid 'self'");
}
if (!FnRef.IsValid())
{
return L.ApiParamError("Expected a valid callback function for parameter #4");
}
// Call the DoWith function:
bool res = Self->ForEachEntityInChunk(ChunkX, ChunkZ, [&](cEntity & a_Item)
{
bool ret = false;
L.Call(FnRef, &a_Item, cLuaState::Return, ret);
return ret;
}
);
// Push the result as the return value:
L.Push(res);
return 1;
}
static int tolua_cWorld_ForEachLoadedChunk(lua_State * tolua_S)
{
// Exported manually, because tolua doesn't support converting functions to functor types.
@ -1347,35 +1588,35 @@ void cManualBindings::BindWorld(lua_State * tolua_S)
tolua_function(tolua_S, "BroadcastParticleEffect", tolua_cWorld_BroadcastParticleEffect);
tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay);
tolua_function(tolua_S, "DoExplosionAt", tolua_cWorld_DoExplosionAt);
tolua_function(tolua_S, "DoWithBeaconAt", DoWithXYZ<cWorld, cBeaconEntity, &cWorld::DoWithBeaconAt>);
tolua_function(tolua_S, "DoWithBedAt", DoWithXYZ<cWorld, cBedEntity, &cWorld::DoWithBedAt>);
tolua_function(tolua_S, "DoWithBlockEntityAt", DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
tolua_function(tolua_S, "DoWithBrewingstandAt", DoWithXYZ<cWorld, cBrewingstandEntity, &cWorld::DoWithBrewingstandAt>);
tolua_function(tolua_S, "DoWithChestAt", DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
tolua_function(tolua_S, "DoWithCommandBlockAt", DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>);
tolua_function(tolua_S, "DoWithDispenserAt", DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
tolua_function(tolua_S, "DoWithDropSpenserAt", DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
tolua_function(tolua_S, "DoWithDropperAt", DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>);
tolua_function(tolua_S, "DoWithEntityByID", DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>);
tolua_function(tolua_S, "DoWithFlowerPotAt", DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>);
tolua_function(tolua_S, "DoWithFurnaceAt", DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
tolua_function(tolua_S, "DoWithHopperAt", DoWithXYZ<cWorld, cHopperEntity, &cWorld::DoWithHopperAt>);
tolua_function(tolua_S, "DoWithMobHeadAt", DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadAt>);
tolua_function(tolua_S, "DoWithBeaconAt", DoWithBlockEntityAt<cBeaconEntity, E_BLOCK_BEACON>);
tolua_function(tolua_S, "DoWithBedAt", DoWithBlockEntityAt<cBedEntity, E_BLOCK_BED>);
tolua_function(tolua_S, "DoWithBlockEntityAt", DoWithBlockEntityAt<cBlockEntity>);
tolua_function(tolua_S, "DoWithBrewingstandAt", DoWithBlockEntityAt<cBrewingstandEntity, E_BLOCK_BREWING_STAND>);
tolua_function(tolua_S, "DoWithChestAt", DoWithBlockEntityAt<cChestEntity, E_BLOCK_CHEST, E_BLOCK_TRAPPED_CHEST>);
tolua_function(tolua_S, "DoWithCommandBlockAt", DoWithBlockEntityAt<cCommandBlockEntity, E_BLOCK_COMMAND_BLOCK>);
tolua_function(tolua_S, "DoWithDispenserAt", DoWithBlockEntityAt<cDispenserEntity, E_BLOCK_DISPENSER>);
tolua_function(tolua_S, "DoWithDropSpenserAt", DoWithBlockEntityAt<cDropSpenserEntity, E_BLOCK_DISPENSER, E_BLOCK_DROPPER>);
tolua_function(tolua_S, "DoWithDropperAt", DoWithBlockEntityAt<cDropperEntity, E_BLOCK_DROPPER>);
tolua_function(tolua_S, "DoWithEntityByID", DoWithID<cWorld, cEntity, &cWorld::DoWithEntityByID>);
tolua_function(tolua_S, "DoWithFlowerPotAt", DoWithBlockEntityAt<cFlowerPotEntity, E_BLOCK_FLOWER_POT>);
tolua_function(tolua_S, "DoWithFurnaceAt", DoWithBlockEntityAt<cFurnaceEntity, E_BLOCK_FURNACE, E_BLOCK_LIT_FURNACE>);
tolua_function(tolua_S, "DoWithHopperAt", DoWithBlockEntityAt<cHopperEntity, E_BLOCK_HOPPER>);
tolua_function(tolua_S, "DoWithMobHeadAt", DoWithBlockEntityAt<cMobHeadEntity, E_BLOCK_HEAD>);
tolua_function(tolua_S, "DoWithNearestPlayer", tolua_cWorld_DoWithNearestPlayer);
tolua_function(tolua_S, "DoWithNoteBlockAt", DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
tolua_function(tolua_S, "DoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
tolua_function(tolua_S, "DoWithNoteBlockAt", DoWithBlockEntityAt<cNoteEntity, E_BLOCK_NOTE_BLOCK>);
tolua_function(tolua_S, "DoWithPlayer", DoWith<cWorld, cPlayer, &cWorld::DoWithPlayer>);
tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_cWorld_DoWithPlayerByUUID);
tolua_function(tolua_S, "FastSetBlock", tolua_cWorld_FastSetBlock);
tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
tolua_function(tolua_S, "ForEachBlockEntityInChunk", ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
tolua_function(tolua_S, "ForEachBrewingstandInChunk", ForEachInChunk<cWorld, cBrewingstandEntity, &cWorld::ForEachBrewingstandInChunk>);
tolua_function(tolua_S, "ForEachChestInChunk", ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
tolua_function(tolua_S, "ForEachEntity", ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
tolua_function(tolua_S, "ForEachEntityInBox", ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>);
tolua_function(tolua_S, "ForEachEntityInChunk", ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>);
tolua_function(tolua_S, "ForEachFurnaceInChunk", ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>);
tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith<cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
tolua_function(tolua_S, "ForEachBlockEntityInChunk", ForEachBlockEntityInChunk<cBlockEntity>);
tolua_function(tolua_S, "ForEachBrewingstandInChunk", ForEachBlockEntityInChunk<cBrewingstandEntity, E_BLOCK_BREWING_STAND>);
tolua_function(tolua_S, "ForEachChestInChunk", ForEachBlockEntityInChunk<cChestEntity, E_BLOCK_CHEST, E_BLOCK_TRAPPED_CHEST>);
tolua_function(tolua_S, "ForEachEntity", ForEach<cWorld, cEntity, &cWorld::ForEachEntity>);
tolua_function(tolua_S, "ForEachEntityInBox", ForEachInBox<cWorld, cEntity, &cWorld::ForEachEntityInBox>);
tolua_function(tolua_S, "ForEachEntityInChunk", tolua_cWorld_ForEachEntityInChunk);
tolua_function(tolua_S, "ForEachFurnaceInChunk", ForEachBlockEntityInChunk<cFurnaceEntity, E_BLOCK_FURNACE, E_BLOCK_LIT_FURNACE>);
tolua_function(tolua_S, "ForEachLoadedChunk", tolua_cWorld_ForEachLoadedChunk);
tolua_function(tolua_S, "ForEachPlayer", ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>);
tolua_function(tolua_S, "ForEachPlayer", ForEach<cWorld, cPlayer, &cWorld::ForEachPlayer>);
tolua_function(tolua_S, "GetBlock", tolua_cWorld_GetBlock);
tolua_function(tolua_S, "GetBlockBlockLight", tolua_cWorld_GetBlockBlockLight);
tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo);

View File

@ -56,12 +56,4 @@ void cBedEntity::SendTo(cClientHandle & a_Client)
void cBedEntity::SetColor(short a_Color)
{
m_Color = a_Color;
auto Pos = GetPos();
// If the bed entity is send immediately, the client (maybe) still has not the bed.
// Fix that by delaying the broadcast of the bed entity by a tick:
m_World->ScheduleTask(1, [Pos](cWorld & a_World)
{
a_World.BroadcastBlockEntity(Pos);
});
}

View File

@ -146,24 +146,25 @@ bool cChestEntity::UsedBy(cPlayer * a_Player)
void cChestEntity::ScanNeighbours()
{
// Callback for finding neighbouring chest:
auto FindNeighbour = [this](cChestEntity & a_Chest)
// Callback for finding neighbouring chest.
auto FindNeighbour = [this](cBlockEntity & a_BlockEntity)
{
if (a_Chest.GetBlockType() != m_BlockType)
if (a_BlockEntity.GetBlockType() != m_BlockType)
{
// Neighboring block is not the same type of chest
return true;
return false;
}
m_Neighbour = &a_Chest;
return false;
m_Neighbour = static_cast<cChestEntity *>(&a_BlockEntity);
return true;
};
// Scan horizontally adjacent blocks for any neighbouring chest of the same type:
if (
m_World->DoWithChestAt(m_Pos.x - 1, m_Pos.y, m_Pos.z, FindNeighbour) ||
m_World->DoWithChestAt(m_Pos.x + 1, m_Pos.y, m_Pos.z, FindNeighbour) ||
m_World->DoWithChestAt(m_Pos.x, m_Pos.y, m_Pos.z - 1, FindNeighbour) ||
m_World->DoWithChestAt(m_Pos.x, m_Pos.y, m_Pos.z + 1, FindNeighbour)
m_World->DoWithBlockEntityAt(m_Pos.addedX(-1), FindNeighbour) ||
m_World->DoWithBlockEntityAt(m_Pos.addedX(+1), FindNeighbour) ||
m_World->DoWithBlockEntityAt(m_Pos.addedZ(-1), FindNeighbour) ||
m_World->DoWithBlockEntityAt(m_Pos.addedX(+1), FindNeighbour)
)
{
m_Neighbour->m_Neighbour = this;

View File

@ -43,15 +43,6 @@ bool cCommandBlockEntity::UsedBy(cPlayer * a_Player)
void cCommandBlockEntity::SetCommand(const AString & a_Cmd)
{
m_Command = a_Cmd;
/*
Vanilla requires that the server send a Block Entity Update after a command has been set
Therefore, command blocks don't support on-the-fly (when window is open) updating of a command and therefore...
...the following code can't be put in UsedBy just before the window opens
Just documenting my experience in getting this to work :P
*/
m_World->BroadcastBlockEntity(GetPos());
}
@ -60,7 +51,6 @@ void cCommandBlockEntity::SetCommand(const AString & a_Cmd)
void cCommandBlockEntity::SetLastOutput(const AString & a_LastOut)
{
m_World->BroadcastBlockEntity(GetPos());
m_LastOutput = a_LastOut;
}
@ -180,7 +170,6 @@ void cCommandBlockEntity::Execute()
{
// Overwrite field
m_CmdBlock->SetLastOutput(cClientHandle::FormatChatPrefix(m_CmdBlock->GetWorld()->ShouldUseChatPrefixes(), "SUCCESS", cChatColor::Green, cChatColor::White) + a_Text);
m_CmdBlock->GetWorld()->BroadcastBlockEntity(m_CmdBlock->GetPos());
}
};

View File

@ -33,7 +33,6 @@ void cMobHeadEntity::SetType(const eMobHeadType & a_Type)
m_OwnerUUID = cUUID{};
}
m_Type = a_Type;
m_World->BroadcastBlockEntity(GetPos());
}
@ -43,7 +42,6 @@ void cMobHeadEntity::SetType(const eMobHeadType & a_Type)
void cMobHeadEntity::SetRotation(eMobHeadRotation a_Rotation)
{
m_Rotation = a_Rotation;
m_World->BroadcastBlockEntity(GetPos());
}
@ -70,8 +68,6 @@ void cMobHeadEntity::SetOwner(const cPlayer & a_Owner)
break;
}
}
m_World->BroadcastBlockEntity(GetPos());
}
@ -89,7 +85,6 @@ void cMobHeadEntity::SetOwner(const cUUID & a_OwnerUUID, const AString & a_Owner
m_OwnerName = a_OwnerName;
m_OwnerTexture = a_OwnerTexture;
m_OwnerTextureSignature = a_OwnerTextureSignature;
m_World->BroadcastBlockEntity(GetPos());
}

View File

@ -70,6 +70,7 @@ bool cMobSpawnerEntity::UsedBy(cPlayer * a_Player)
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
m_World->BroadcastBlockEntity(GetPos());
FLOGD("Changed monster spawner at {0} to type {1}.", GetPos(), cMonster::MobTypeToString(MonsterType));
return true;
}
@ -105,6 +106,7 @@ bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (m_SpawnDelay <= 0)
{
SpawnEntity();
m_World->BroadcastBlockEntity(GetPos());
return true;
}
else
@ -121,7 +123,6 @@ bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cMobSpawnerEntity::ResetTimer(void)
{
m_SpawnDelay = GetRandomProvider().RandInt<short>(m_MinSpawnDelay, m_MaxSpawnDelay);
m_World->BroadcastBlockEntity(GetPos());
}

View File

@ -1181,36 +1181,3 @@ extern AString ItemToFullString(const cItem & a_Item);
extern cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default);
// tolua_end
/** Base case for IsOneOf to handle empty template aguments. */
template <class = void>
bool IsOneOf(BLOCKTYPE a_BlockType)
{
return false;
}
/** Returns true if a_BlockType is equal to any of the variadic template arguments.
Some example usage:
\code
IsOneOf<>(E_BLOCK_AIR) == false
IsOneOf<E_BLOCK_AIR>(E_BLOCK_DIRT) == false
IsOneOf<E_BLOCK_AIR, E_BLOCK_DIRT>(E_BLOCK_DIRT) == true
\endcode
The implementation is ugly but it is equivalent to this C++17 fold expression:
\code
((a_BlockType == Types) || ...)
\endcode
Just written to be valid without fold expressions or SFINAE. */
template <BLOCKTYPE Head, BLOCKTYPE ... Tail>
bool IsOneOf(BLOCKTYPE a_BlockType)
{
return ((a_BlockType == Head) || (IsOneOf<Tail...>(a_BlockType)));
}

View File

@ -158,12 +158,12 @@ bool cBlockBedHandler::OnUse(
void cBlockBedHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange) const
{
a_Player.GetWorld()->DoWithBedAt(a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), [&](cBedEntity & a_Bed)
{
a_Bed.SetColor(a_Player.GetEquippedItem().m_ItemDamage);
return true;
}
);
a_Player.GetWorld()->DoWithBlockEntityAt(a_BlockChange.GetAbsolutePos(), [&a_Player](cBlockEntity & a_BlockEntity)
{
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_BED);
static_cast<cBedEntity &>(a_BlockEntity).SetColor(a_Player.GetEquippedItem().m_ItemDamage);
return false;
});
}

View File

@ -32,7 +32,7 @@ private:
) const override
{
AString WindowName = "Enchant";
a_WorldInterface.DoWithBlockEntityAt(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, [&WindowName](cBlockEntity & a_Entity)
a_WorldInterface.DoWithBlockEntityAt(a_BlockPos, [&WindowName](cBlockEntity & a_Entity)
{
if (a_Entity.GetBlockType() != E_BLOCK_ENCHANTMENT_TABLE)
{
@ -46,7 +46,7 @@ private:
WindowName = CustomName;
}
return true;
return false;
});
cWindow * Window = new cEnchantingWindow(a_BlockPos, std::move(WindowName));

View File

@ -26,18 +26,15 @@ private:
const Vector3i a_BlockPos
) const override
{
a_WorldInterface.DoWithBlockEntityAt(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, [](cBlockEntity & a_BlockEntity)
a_WorldInterface.DoWithBlockEntityAt(a_BlockPos, [](cBlockEntity & a_BlockEntity)
{
if (a_BlockEntity.GetBlockType() != E_BLOCK_NOTE_BLOCK)
{
return false;
}
auto & NoteEntity = static_cast<cNoteEntity &>(a_BlockEntity);
NoteEntity.MakeSound();
return true;
static_cast<cNoteEntity &>(a_BlockEntity).MakeSound();
return false;
});
}
};

View File

@ -4,13 +4,11 @@
#include "../FunctionRef.h"
#include "../Mobs/MonsterTypes.h"
class cBedEntity;
class cBlockEntity;
class cBroadcastInterface;
class cItems;
class cPlayer;
using cBedCallback = cFunctionRef<bool(cBedEntity &)>;
using cBlockEntityCallback = cFunctionRef<bool(cBlockEntity &)>;
using cPlayerListCallback = cFunctionRef<bool(cPlayer &)>;
using cEntityCallback = cFunctionRef<bool(cEntity &)>;
@ -32,10 +30,8 @@ public:
virtual void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData) = 0;
virtual bool DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback a_Callback) = 0;
/** Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found */
virtual bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback a_Callback) = 0;
virtual bool DoWithBlockEntityAt(Vector3i a_Position, cBlockEntityCallback a_Callback) = 0;
/** Spawns item pickups for each item in the list. May compress pickups if too many entities: */
virtual void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false) = 0;

View File

@ -12,21 +12,6 @@
#include "ClientHandle.h"
#include "Server.h"
#include "Defines.h"
#include "BlockEntities/BeaconEntity.h"
#include "BlockEntities/BedEntity.h"
#include "BlockEntities/BrewingstandEntity.h"
#include "BlockEntities/ChestEntity.h"
#include "BlockEntities/CommandBlockEntity.h"
#include "BlockEntities/DispenserEntity.h"
#include "BlockEntities/DropperEntity.h"
#include "BlockEntities/FlowerPotEntity.h"
#include "BlockEntities/FurnaceEntity.h"
#include "BlockEntities/HopperEntity.h"
#include "BlockEntities/JukeboxEntity.h"
#include "BlockEntities/MobHeadEntity.h"
#include "BlockEntities/MobSpawnerEntity.h"
#include "BlockEntities/NoteEntity.h"
#include "BlockEntities/SignEntity.h"
#include "Entities/Pickup.h"
#include "Item.h"
#include "Noise/Noise.h"
@ -468,7 +453,6 @@ void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock
auto clone = be->Clone({posX, posY, posZ});
clone->SetWorld(m_World);
AddBlockEntity(std::move(clone));
m_World->BroadcastBlockEntity({posX, posY, posZ});
}
}
}
@ -789,28 +773,34 @@ void cChunk::MoveEntityToNewChunk(OwnedEntity a_Entity)
void cChunk::BroadcastPendingBlockChanges(void)
{
if (m_PendingSendBlocks.empty())
if (const auto PendingBlocksCount = m_PendingSendBlocks.size(); PendingBlocksCount >= 10240)
{
return;
}
if (m_PendingSendBlocks.size() >= 10240)
{
// Resend the full chunk
for (auto ClientHandle : m_LoadedByClient)
// Resend the full chunk:
for (const auto ClientHandle : m_LoadedByClient)
{
m_World->ForceSendChunkTo(m_PosX, m_PosZ, cChunkSender::Priority::Medium, ClientHandle);
}
}
else
else if (PendingBlocksCount != 0)
{
// Only send block changes
for (auto ClientHandle : m_LoadedByClient)
// Send block changes:
for (const auto ClientHandle : m_LoadedByClient)
{
ClientHandle->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks);
}
}
// Send block entity changes:
for (const auto Entity : m_PendingSendBlockEntities)
{
for (const auto ClientHandle : m_LoadedByClient)
{
Entity->SendTo(*ClientHandle);
}
}
m_PendingSendBlocks.clear();
m_PendingSendBlockEntities.clear();
}
@ -823,12 +813,12 @@ void cChunk::CheckBlocks()
cBlockInServerPluginInterface PluginInterface(*m_World);
// Process a limited number of blocks since cBlockHandler::Check may queue more to tick
auto Count = m_ToTickBlocks.size();
auto Count = m_BlocksToCheck.size();
while (Count != 0)
{
const auto Pos = m_ToTickBlocks.front();
m_ToTickBlocks.pop();
const auto Pos = m_BlocksToCheck.front();
m_BlocksToCheck.pop();
Count--;
cBlockHandler::For(GetBlock(Pos)).Check(ChunkInterface, PluginInterface, Pos, *this);
@ -1265,8 +1255,8 @@ void cChunk::SetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_Blo
{
FastSetBlock(a_RelPos, a_BlockType, a_BlockMeta);
// Tick this block's neighbors via cBlockHandler::Check:
m_ToTickBlocks.push(a_RelPos);
// Queue a check of this block's neighbors:
m_BlocksToCheck.push(a_RelPos);
// Wake up the simulators for this block:
GetWorld()->GetSimulatorManager()->WakeUp(*this, a_RelPos);
@ -1370,22 +1360,26 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client)
{
const auto BlockEntity = GetBlockEntityRel({ a_RelX, a_RelY, a_RelZ });
if (a_Client == nullptr)
{
// Queue the block for all clients in the chunk (will be sent in Tick())
// Queue the block (entity) for all clients in the chunk (will be sent in BroadcastPendingBlockChanges()):
m_PendingSendBlocks.emplace_back(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ));
if (BlockEntity != nullptr)
{
m_PendingSendBlockEntities.push_back(BlockEntity);
}
return;
}
Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ));
const auto Position = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
a_Client->SendBlockChange(Position.x, Position.y, Position.z, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ));
// FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client:
cBlockEntity * Block = GetBlockEntity(wp.x, wp.y, wp.z);
if (Block != nullptr)
if (BlockEntity != nullptr)
{
Block->SendTo(*a_Client);
BlockEntity->SendTo(*a_Client);
}
}
@ -1508,39 +1502,12 @@ void cChunk::SetAreaBiome(int a_MinRelX, int a_MaxRelX, int a_MinRelZ, int a_Max
bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
{
// Also sends update packets to all clients in the chunk
auto Entity = GetBlockEntity(a_PosX, a_PosY, a_PosZ);
if (Entity == nullptr)
{
return false; // Not a block entity
}
if (
(Entity->GetBlockType() != E_BLOCK_WALLSIGN) &&
(Entity->GetBlockType() != E_BLOCK_SIGN_POST)
)
{
return false; // Not a sign
}
MarkDirty();
auto Sign = static_cast<cSignEntity *>(Entity);
Sign->SetLines(a_Line1, a_Line2, a_Line3, a_Line4);
m_World->BroadcastBlockEntity({a_PosX, a_PosY, a_PosZ});
return true;
}
void cChunk::RemoveBlockEntity(cBlockEntity * a_BlockEntity)
{
MarkDirty();
ASSERT(a_BlockEntity != nullptr);
auto idx = static_cast<size_t>(cChunkDef::MakeIndex(a_BlockEntity->GetRelX(), a_BlockEntity->GetPosY(), a_BlockEntity->GetRelZ()));
m_BlockEntities.erase(idx);
m_BlockEntities.erase(static_cast<size_t>(cChunkDef::MakeIndex(a_BlockEntity->GetRelX(), a_BlockEntity->GetPosY(), a_BlockEntity->GetRelZ())));
m_PendingSendBlockEntities.erase(std::remove(m_PendingSendBlockEntities.begin(), m_PendingSendBlockEntities.end(), a_BlockEntity), m_PendingSendBlockEntities.end());
}
@ -1745,125 +1712,19 @@ bool cChunk::DoWithEntityByID(UInt32 a_EntityID, cEntityCallback a_Callback, boo
template <class tyEntity, BLOCKTYPE... tBlocktype>
bool cChunk::GenericForEachBlockEntity(cFunctionRef<bool(tyEntity &)> a_Callback)
{
// The blockentity list is locked by the parent chunkmap's CS
for (auto & KeyPair : m_BlockEntities)
{
cBlockEntity * Block = KeyPair.second.get();
if (
(sizeof...(tBlocktype) == 0) || // Let empty list mean all block entities
(IsOneOf<tBlocktype...>(Block->GetBlockType()))
)
{
if (a_Callback(*static_cast<tyEntity *>(Block)))
{
return false;
}
}
} // for KeyPair - m_BlockEntitites[]
return true;
}
bool cChunk::ForEachBlockEntity(cBlockEntityCallback a_Callback)
{
return GenericForEachBlockEntity<cBlockEntity>(a_Callback);
}
bool cChunk::ForEachBrewingstand(cBrewingstandCallback a_Callback)
{
return GenericForEachBlockEntity<cBrewingstandEntity,
E_BLOCK_BREWING_STAND
>(a_Callback);
}
bool cChunk::ForEachChest(cChestCallback a_Callback)
{
return GenericForEachBlockEntity<cChestEntity,
E_BLOCK_CHEST
>(a_Callback);
}
bool cChunk::ForEachDispenser(cDispenserCallback a_Callback)
{
return GenericForEachBlockEntity<cDispenserEntity,
E_BLOCK_DISPENSER
>(a_Callback);
}
bool cChunk::ForEachDropper(cDropperCallback a_Callback)
{
return GenericForEachBlockEntity<cDropperEntity,
E_BLOCK_DROPPER
>(a_Callback);
}
bool cChunk::ForEachDropSpenser(cDropSpenserCallback a_Callback)
{
return GenericForEachBlockEntity<cDropSpenserEntity,
E_BLOCK_DISPENSER,
E_BLOCK_DROPPER
>(a_Callback);
}
bool cChunk::ForEachFurnace(cFurnaceCallback a_Callback)
{
return GenericForEachBlockEntity<cFurnaceEntity,
E_BLOCK_FURNACE,
E_BLOCK_LIT_FURNACE
>(a_Callback);
}
template <class tyEntity, BLOCKTYPE... tBlocktype>
bool cChunk::GenericDoWithBlockEntityAt(Vector3i a_Position, cFunctionRef<bool(tyEntity &)> a_Callback)
{
// The blockentity list is locked by the parent chunkmap's CS
cBlockEntity * Block = GetBlockEntityRel(a_Position);
if (Block == nullptr)
for (auto & KeyPair : m_BlockEntities)
{
return false; // No block entity here
if (a_Callback(*KeyPair.second))
{
return false;
}
}
if (
(sizeof...(tBlocktype) != 0) && // Let empty list mean all block entities
(!IsOneOf<tBlocktype...>(Block->GetBlockType()))
)
{
return false; // Not any of the given tBlocktypes
}
return !a_Callback(*static_cast<tyEntity *>(Block));
return true;
}
@ -1871,182 +1732,19 @@ bool cChunk::GenericDoWithBlockEntityAt(Vector3i a_Position, cFunctionRef<bool(t
bool cChunk::DoWithBlockEntityAt(Vector3i a_Position, cBlockEntityCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cBlockEntity>(a_Position, a_Callback);
}
bool cChunk::DoWithBeaconAt(Vector3i a_Position, cBeaconCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cBeaconEntity,
E_BLOCK_BEACON
>(a_Position, a_Callback);
}
bool cChunk::DoWithBedAt(Vector3i a_Position, cBedCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cBedEntity,
E_BLOCK_BED
>(a_Position, a_Callback);
}
bool cChunk::DoWithBrewingstandAt(Vector3i a_Position, cBrewingstandCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cBrewingstandEntity,
E_BLOCK_BREWING_STAND
>(a_Position, a_Callback);
}
bool cChunk::DoWithChestAt(Vector3i a_Position, cChestCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cChestEntity,
E_BLOCK_CHEST,
E_BLOCK_TRAPPED_CHEST
>(a_Position, a_Callback);
}
bool cChunk::DoWithDispenserAt(Vector3i a_Position, cDispenserCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cDispenserEntity,
E_BLOCK_DISPENSER
>(a_Position, a_Callback);
}
bool cChunk::DoWithDropperAt(Vector3i a_Position, cDropperCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cDropperEntity,
E_BLOCK_DROPPER
>(a_Position, a_Callback);
}
bool cChunk::DoWithDropSpenserAt(Vector3i a_Position, cDropSpenserCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cDropSpenserEntity,
E_BLOCK_DISPENSER,
E_BLOCK_DROPPER
>(a_Position, a_Callback);
}
bool cChunk::DoWithFurnaceAt(Vector3i a_Position, cFurnaceCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cFurnaceEntity,
E_BLOCK_FURNACE,
E_BLOCK_LIT_FURNACE
>(a_Position, a_Callback);
}
bool cChunk::DoWithHopperAt(Vector3i a_Position, cHopperCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cHopperEntity,
E_BLOCK_HOPPER
>(a_Position, a_Callback);
}
bool cChunk::DoWithNoteBlockAt(Vector3i a_Position, cNoteBlockCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cNoteEntity,
E_BLOCK_NOTE_BLOCK
>(a_Position, a_Callback);
}
bool cChunk::DoWithCommandBlockAt(Vector3i a_Position, cCommandBlockCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cCommandBlockEntity,
E_BLOCK_COMMAND_BLOCK
>(a_Position, a_Callback);
}
bool cChunk::DoWithMobHeadAt(Vector3i a_Position, cMobHeadCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cMobHeadEntity,
E_BLOCK_HEAD
>(a_Position, a_Callback);
}
bool cChunk::DoWithFlowerPotAt(Vector3i a_Position, cFlowerPotCallback a_Callback)
{
return GenericDoWithBlockEntityAt<cFlowerPotEntity,
E_BLOCK_FLOWER_POT
>(a_Position, a_Callback);
}
bool cChunk::GetSignLines(Vector3i a_Position, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
{
// The blockentity list is locked by the parent chunkmap's CS
auto Entity = GetBlockEntity(a_Position);