From 26ac146f41091dc070d8075f5fc9de25b5a22578 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 17 Apr 2020 11:36:37 +0200 Subject: More Vector3 in cBlockHandler (#4644) * cBlockHandler.OnUpdate uses Vector3 params. Also slightly changed how block ticking works. --- src/Bindings/DeprecatedBindings.cpp | 38 ++++++++ src/Blocks/BlockCauldron.h | 38 ++++++-- src/Blocks/BlockCocoaPod.h | 10 +- src/Blocks/BlockFarmland.h | 36 +++++--- src/Blocks/BlockFluid.h | 57 ++++++++---- src/Blocks/BlockGrass.h | 54 ++++++----- src/Blocks/BlockHandler.cpp | 8 +- src/Blocks/BlockHandler.h | 19 +++- src/Blocks/BlockLeaves.h | 176 ++++++++++++++++++------------------ src/Blocks/BlockPlant.h | 17 ++-- src/Blocks/BlockPortal.h | 18 +++- src/Blocks/BlockRedstoneOre.h | 9 +- src/Blocks/BlockSapling.h | 21 +++-- src/Blocks/BlockVine.h | 18 +++- src/Blocks/CMakeLists.txt | 1 + src/Blocks/WorldInterface.h | 4 + src/Chunk.cpp | 49 +++------- src/Chunk.h | 16 ++-- src/ChunkMap.cpp | 20 ++-- src/ChunkMap.h | 8 +- src/World.cpp | 6 +- src/World.h | 13 ++- 22 files changed, 389 insertions(+), 247 deletions(-) (limited to 'src') diff --git a/src/Bindings/DeprecatedBindings.cpp b/src/Bindings/DeprecatedBindings.cpp index 63b5919ac..11b15a8c8 100644 --- a/src/Bindings/DeprecatedBindings.cpp +++ b/src/Bindings/DeprecatedBindings.cpp @@ -518,6 +518,43 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S) +/** function: cWorld:SetNextBlockTick */ +static int tolua_cWorld_SetNextBlockTick(lua_State * tolua_S) +{ + cLuaState LuaState(tolua_S); + + if ( + !LuaState.CheckParamUserType(1, "cWorld") || + !LuaState.CheckParamNumber(2, 4) || + !LuaState.CheckParamEnd(5) + ) + { + return 0; + } + + cWorld * Self = nullptr; + int BlockX = 0; + int BlockY = 0; + int BlockZ = 0; + + if (!LuaState.GetStackValues(1, Self, BlockX, BlockY, BlockZ)) + { + tolua_error(LuaState, "Failed to read parameters", nullptr); + } + if (Self == nullptr) + { + tolua_error(LuaState, "invalid 'self' in function 'SetNextBlockTick'", nullptr); + } + Self->SetNextBlockToTick({BlockX, BlockY, BlockZ}); + LOGWARNING("Warning: 'cWorld:SetNextBlockTick' function is deprecated. Please use 'cWorld:SetNextBlockToTick' instead."); + LuaState.LogStackTrace(0); + return 1; +} + + + + + void DeprecatedBindings::Bind(lua_State * tolua_S) { tolua_beginmodule(tolua_S, nullptr); @@ -558,6 +595,7 @@ void DeprecatedBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cWorld"); + tolua_function(tolua_S, "SetNextBlockTick", tolua_cWorld_SetNextBlockTick); tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines); tolua_endmodule(tolua_S); diff --git a/src/Blocks/BlockCauldron.h b/src/Blocks/BlockCauldron.h index 37f274f18..28c1dfda8 100644 --- a/src/Blocks/BlockCauldron.h +++ b/src/Blocks/BlockCauldron.h @@ -19,11 +19,19 @@ public: { } + + + + virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override { return cItem(E_ITEM_CAULDRON, 1, 0); } + + + + virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override { NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}); @@ -86,29 +94,45 @@ public: return true; } + + + + virtual bool IsUseable() override { return true; } - virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + + + + + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - if (!a_WorldInterface.IsWeatherWetAt(BlockX, BlockZ) || (a_RelY != a_WorldInterface.GetHeight(BlockX, BlockZ))) + auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos); + if (!a_WorldInterface.IsWeatherWetAtXYZ(WorldPos.addedY(1))) { // It's not raining at our current location or we do not have a direct view of the sky - // We cannot eat the rain :( return; } - NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + auto Meta = a_Chunk.GetMeta(a_RelPos); if (Meta < 3) { - a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta + 1); + a_Chunk.SetMeta(a_RelPos, Meta + 1); } } + + + + virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override { UNUSED(a_Meta); diff --git a/src/Blocks/BlockCocoaPod.h b/src/Blocks/BlockCocoaPod.h index 11190e0ba..74b7c3caa 100644 --- a/src/Blocks/BlockCocoaPod.h +++ b/src/Blocks/BlockCocoaPod.h @@ -38,11 +38,17 @@ public: - virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { if (GetRandomProvider().RandBool(0.20)) { - Grow(a_Chunk, {a_RelX, a_RelY, a_RelZ}); + Grow(a_Chunk, a_RelPos); } } diff --git a/src/Blocks/BlockFarmland.h b/src/Blocks/BlockFarmland.h index 20898b5e3..e5a55fcdd 100644 --- a/src/Blocks/BlockFarmland.h +++ b/src/Blocks/BlockFarmland.h @@ -41,26 +41,32 @@ public: - virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { - NIBBLETYPE BlockMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + auto BlockMeta = a_Chunk.GetMeta(a_RelPos); - if (IsWaterInNear(a_Chunk, a_RelX, a_RelY, a_RelZ)) + if (IsWaterInNear(a_Chunk, a_RelPos)) { // Water was found, set block meta to 7 - a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, 7); + a_Chunk.FastSetBlock(a_RelPos, m_BlockType, 7); return; } // Water wasn't found, de-hydrate block: if (BlockMeta > 0) { - a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_FARMLAND, --BlockMeta); + a_Chunk.FastSetBlock(a_RelPos, E_BLOCK_FARMLAND, --BlockMeta); return; } // Farmland too dry. If nothing is growing on top, turn back to dirt: - BLOCKTYPE UpperBlock = (a_RelY >= cChunkDef::Height - 1) ? static_cast(E_BLOCK_AIR) : a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ); + auto UpperBlock = cChunkDef::IsValidHeight(a_RelPos.y + 1) ? a_Chunk.GetBlock(a_RelPos.addedY(1)) : E_BLOCK_AIR; switch (UpperBlock) { case E_BLOCK_BEETROOTS: @@ -75,7 +81,7 @@ public: } default: { - a_Chunk.SetBlock({a_RelX, a_RelY, a_RelZ}, E_BLOCK_DIRT, 0); + a_Chunk.SetBlock(a_RelPos, E_BLOCK_DIRT, 0); break; } } @@ -111,11 +117,12 @@ public: - bool IsWaterInNear(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) + /** Returns true if there's either a water source block close enough to hydrate the specified position, or it's raining there. */ + bool IsWaterInNear(cChunk & a_Chunk, const Vector3i a_RelPos) { - if (a_Chunk.GetWorld()->IsWeatherWetAt(a_RelX, a_RelZ)) + if (a_Chunk.GetWorld()->IsWeatherWetAtXYZ(a_RelPos)) { - // Rain hydrates farmland, too, except in Desert biomes. + // Rain hydrates farmland, too return true; } @@ -123,9 +130,8 @@ public: // Ref.: https://minecraft.gamepedia.com/Farmland#Hydration // TODO: Rewrite this to use the chunk and its neighbors directly cBlockArea Area; - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - if (!Area.Read(*a_Chunk.GetWorld(), BlockX - 4, BlockX + 4, a_RelY, a_RelY + 1, BlockZ - 4, BlockZ + 4)) + auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos); + if (!Area.Read(*a_Chunk.GetWorld(), WorldPos - Vector3i(4, 0, 4), WorldPos + Vector3i(4, 1, 4))) { // Too close to the world edge, cannot check surroundings return false; @@ -144,6 +150,10 @@ public: return false; } + + + + virtual bool CanSustainPlant(BLOCKTYPE a_Plant) override { return ( diff --git a/src/Blocks/BlockFluid.h b/src/Blocks/BlockFluid.h index 36f363a92..e856dba38 100644 --- a/src/Blocks/BlockFluid.h +++ b/src/Blocks/BlockFluid.h @@ -80,6 +80,10 @@ public: return 0; } + + + + virtual bool CanSustainPlant(BLOCKTYPE a_Plant) override { return ( @@ -109,33 +113,47 @@ public: { } - /** Called to tick the block */ - virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + + + + + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { if (a_Chunk.GetWorld()->ShouldLavaSpawnFire()) { // Try to start up to 5 fires: for (int i = 0; i < 5; i++) { - TryStartFireNear(a_RelX, a_RelY, a_RelZ, a_Chunk); + TryStartFireNear(a_RelPos, a_Chunk); } } } + + + + /** Tries to start a fire near the lava at given coords. Returns true if fire started. */ - static bool TryStartFireNear(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) + static bool TryStartFireNear(const Vector3i a_RelPos, cChunk & a_Chunk) { - // Pick a block next to this lava block: + // Pick a random block next to this lava block: int rnd = a_Chunk.GetWorld()->GetTickRandomNumber(cChunkDef::NumBlocks * 8) / 7; int x = (rnd % 3) - 1; // -1 .. 1 int y = ((rnd / 4) % 4) - 1; // -1 .. 2 int z = ((rnd / 16) % 3) - 1; // -1 .. 1 + auto Pos = a_RelPos + Vector3i(x, y, z); // Check if it's fuel: BLOCKTYPE BlockType; if ( - ((a_RelY + y < 0) || (a_RelY + y >= cChunkDef::Height)) || - !a_Chunk.UnboundedRelGetBlockType(a_RelX + x, a_RelY + y, a_RelZ + z, BlockType) || + !cChunkDef::IsValidHeight(Pos.y) || + !a_Chunk.UnboundedRelGetBlockType(Pos, BlockType) || !cFireSimulator::IsFuel(BlockType) ) { @@ -143,10 +161,7 @@ public: } // Try to set it on fire: - static struct - { - int x, y, z; - } CrossCoords[] = + static Vector3i CrossCoords[] = { {-1, 0, 0}, { 1, 0, 0}, @@ -155,31 +170,37 @@ public: { 0, 0, -1}, { 0, 0, 1}, } ; - int RelX = a_RelX + x; - int RelY = a_RelY + y; - int RelZ = a_RelZ + z; for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++) { + auto NeighborPos = Pos + CrossCoords[i]; if ( - ((RelY + CrossCoords[i].y >= 0) && (RelY + CrossCoords[i].y < cChunkDef::Height)) && - a_Chunk.UnboundedRelGetBlockType(RelX + CrossCoords[i].x, RelY + CrossCoords[i].y, RelZ + CrossCoords[i].z, BlockType) && + cChunkDef::IsValidHeight(NeighborPos.y) && + a_Chunk.UnboundedRelGetBlockType(NeighborPos, BlockType) && (BlockType == E_BLOCK_AIR) ) { - // This is an air block next to a fuel next to lava, light it up: - a_Chunk.UnboundedRelSetBlock(RelX + CrossCoords[i].x, RelY + CrossCoords[i].y, RelZ + CrossCoords[i].z, E_BLOCK_FIRE, 0); + // This is an air block next to a fuel next to lava, light the fuel block up: + a_Chunk.UnboundedRelSetBlock(NeighborPos, E_BLOCK_FIRE, 0); return true; } } // for i - CrossCoords[] return false; } + + + + virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override { UNUSED(a_Meta); return 4; } + + + + virtual bool CanSustainPlant(BLOCKTYPE a_Plant) override { return false; diff --git a/src/Blocks/BlockGrass.h b/src/Blocks/BlockGrass.h index 0e54cb092..374fcceed 100644 --- a/src/Blocks/BlockGrass.h +++ b/src/Blocks/BlockGrass.h @@ -39,34 +39,39 @@ public: - virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { - // Make sure that there is enough light at the source block to spread if (!a_Chunk.GetWorld()->IsChunkLighted(a_Chunk.GetPosX(), a_Chunk.GetPosZ())) { a_Chunk.GetWorld()->QueueLightChunk(a_Chunk.GetPosX(), a_Chunk.GetPosZ()); return; } - else if ((a_RelY < cChunkDef::Height - 1)) + auto AbovePos = a_RelPos.addedY(1); + if (cChunkDef::IsValidHeight(AbovePos.y)) { - BLOCKTYPE above = a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ); - - // Grass turns back to dirt when the block above it is not transparent or water. - // It does not turn to dirt when a snow layer is above. - if ((above != E_BLOCK_SNOW) && - (!cBlockInfo::IsTransparent(above) || IsBlockWater(above))) + // Grass turns back to dirt when the block Above it is not transparent or water. + // It does not turn to dirt when a snow layer is Above. + auto Above = a_Chunk.GetBlock(AbovePos); + if ( + (Above != E_BLOCK_SNOW) && + (!cBlockInfo::IsTransparent(Above) || IsBlockWater(Above))) { - a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_DIRT, E_META_DIRT_NORMAL); + a_Chunk.FastSetBlock(a_RelPos, E_BLOCK_DIRT, E_META_DIRT_NORMAL); return; } - NIBBLETYPE light = std::max(a_Chunk.GetBlockLight(a_RelX, a_RelY + 1, a_RelZ), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(a_RelX, a_RelY + 1, a_RelZ))); - // Source block is not bright enough to spread + // Make sure that there is enough light at the source block to spread + auto light = std::max(a_Chunk.GetBlockLight(AbovePos), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(AbovePos))); if (light < 9) { return; } - } // Grass spreads to adjacent dirt blocks: @@ -79,37 +84,36 @@ public: BLOCKTYPE DestBlock; NIBBLETYPE DestMeta; - if (!cChunkDef::IsValidHeight(a_RelY + OfsY)) + auto Pos = a_RelPos + Vector3i(OfsX, OfsY, OfsZ); + if (!cChunkDef::IsValidHeight(Pos.y)) { // Y Coord out of range continue; } - Vector3i pos(a_RelX + OfsX, a_RelY + OfsY, a_RelZ + OfsZ); - auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(pos); + auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(Pos); if (chunk == nullptr) { // Unloaded chunk continue; } - chunk->GetBlockTypeMeta(pos, DestBlock, DestMeta); + chunk->GetBlockTypeMeta(Pos, DestBlock, DestMeta); if ((DestBlock != E_BLOCK_DIRT) || (DestMeta != E_META_DIRT_NORMAL)) { // Not a regular dirt block continue; } - auto abovePos = pos.addedY(1); - BLOCKTYPE above = chunk->GetBlock(abovePos); - NIBBLETYPE light = std::max(chunk->GetBlockLight(abovePos), chunk->GetTimeAlteredLight(chunk->GetSkyLight(abovePos))); + BLOCKTYPE Above = chunk->GetBlock(AbovePos); + NIBBLETYPE light = std::max(chunk->GetBlockLight(AbovePos), chunk->GetTimeAlteredLight(chunk->GetSkyLight(AbovePos))); if ((light > 4) && - cBlockInfo::IsTransparent(above) && - (!IsBlockLava(above)) && - (!IsBlockWaterOrIce(above)) + cBlockInfo::IsTransparent(Above) && + (!IsBlockLava(Above)) && + (!IsBlockWaterOrIce(Above)) ) { - auto absPos = chunk->RelativeToAbsolute(pos); + auto absPos = chunk->RelativeToAbsolute(Pos); if (!cRoot::Get()->GetPluginManager()->CallHookBlockSpread(*chunk->GetWorld(), absPos.x, absPos.y, absPos.z, ssGrassSpread)) { - chunk->FastSetBlock(pos, E_BLOCK_GRASS, 0); + chunk->FastSetBlock(Pos, E_BLOCK_GRASS, 0); } } } // for i - repeat twice diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index b3c777b05..f1df4db44 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -421,7 +421,13 @@ bool cBlockHandler::GetPlacementBlockTypeMeta( -void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ) +void cBlockHandler::OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos +) { } diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h index 75f6610ce..f4a7fc674 100644 --- a/src/Blocks/BlockHandler.h +++ b/src/Blocks/BlockHandler.h @@ -30,8 +30,14 @@ public: virtual ~cBlockHandler() {} /** Called when the block gets ticked either by a random tick or by a queued tick. - Note that the coords are chunk-relative! */ - virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ); + Note that the coords in a_RelPos are chunk-relative! */ + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_BlockPluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ); /** Returns the relative bounding box that must be entity-free in order for the block to be placed. a_XM, a_XP, etc. stand for the @@ -108,7 +114,14 @@ public: static void NeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_NeighborPos, eBlockFace a_WhichNeighbor); /** Called when the player starts digging the block. */ - virtual void OnDigging(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {} + virtual void OnDigging( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cPlayer & a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ + ) + { + } /** Called if the user right clicks the block and the block is useable returns true if the use was successful, return false to use the block as a "normal" block */ diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h index 065c5ec19..c936e0405 100644 --- a/src/Blocks/BlockLeaves.h +++ b/src/Blocks/BlockLeaves.h @@ -10,29 +10,88 @@ // Leaves can be this many blocks that away (inclusive) from the log not to decay #define LEAVES_CHECK_DISTANCE 6 -#define PROCESS_NEIGHBOR(x, y, z) \ - do { \ - switch (a_Area.GetBlockType(x, y, z)) \ - { \ - case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, static_cast(E_BLOCK_SPONGE + i + 1)); break; \ - case E_BLOCK_LOG: return true; \ - case E_BLOCK_NEW_LEAVES: a_Area.SetBlockType(x, y, z, static_cast(E_BLOCK_SPONGE + i + 1)); break; \ - case E_BLOCK_NEW_LOG: return true; \ - } \ - } while (false) -bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); - - -class cBlockLeavesHandler : +class cBlockLeavesHandler: public cBlockHandler { + using Super = cBlockHandler; + + /** Returns true if the area contains a continous path from the specified block to a log block entirely made out of leaves blocks. */ + static bool HasNearLog(cBlockArea & a_Area, const Vector3i a_BlockPos) + { + // Filter the blocks into a {leaves, log, other (air)} set: + auto * Types = a_Area.GetBlockTypes(); + for (size_t i = a_Area.GetBlockCount() - 1; i > 0; i--) + { + switch (Types[i]) + { + case E_BLOCK_LEAVES: + case E_BLOCK_LOG: + case E_BLOCK_NEW_LEAVES: + case E_BLOCK_NEW_LOG: + { + break; + } + default: + { + Types[i] = E_BLOCK_AIR; + break; + } + } + } // for i - Types[] + + // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block: + // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log + a_Area.SetBlockType(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_SPONGE); + for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++) + { + auto ProcessNeighbor = [&a_Area, i](int cbx, int cby, int cbz) -> bool + { + switch (a_Area.GetBlockType(cbx, cby, cbz)) + { + case E_BLOCK_LEAVES: a_Area.SetBlockType(cbx, cby, cbz, static_cast(E_BLOCK_SPONGE + i + 1)); break; + case E_BLOCK_LOG: return true; + case E_BLOCK_NEW_LEAVES: a_Area.SetBlockType(cbx, cby, cbz, static_cast(E_BLOCK_SPONGE + i + 1)); break; + case E_BLOCK_NEW_LOG: return true; + } + return false; + }; + for (int y = std::max(a_BlockPos.y - i, 0); y <= std::min(a_BlockPos.y + i, cChunkDef::Height - 1); y++) + { + for (int z = a_BlockPos.z - i; z <= a_BlockPos.z + i; z++) + { + for (int x = a_BlockPos.x - i; x <= a_BlockPos.x + i; x++) + { + if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i) + { + continue; + } + if ( + ProcessNeighbor(x - 1, y, z) || + ProcessNeighbor(x + 1, y, z) || + ProcessNeighbor(x, y, z - 1) || + ProcessNeighbor(x, y, z + 1) || + ProcessNeighbor(x, y + 1, z) || + ProcessNeighbor(x, y - 1, z) + ) + { + return true; + } + } // for x + } // for z + } // for y + } // for i - BFS iterations + return false; + } + + public: - cBlockLeavesHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + + cBlockLeavesHandler(BLOCKTYPE a_BlockType): + Super(a_BlockType) { } @@ -104,30 +163,34 @@ public: - virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { - NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + auto Meta = a_Chunk.GetMeta(a_RelPos); if ((Meta & 0x04) != 0) { // Player-placed leaves, don't decay return; } - if ((Meta & 0x8) == 0) + if ((Meta & 0x08) == 0) { // These leaves have been checked for decay lately and nothing around them changed return; } // Get the data around the leaves: - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + auto worldPos = a_Chunk.RelativeToAbsolute(a_RelPos); cBlockArea Area; if (!Area.Read( *a_Chunk.GetWorld(), - BlockX - LEAVES_CHECK_DISTANCE, BlockX + LEAVES_CHECK_DISTANCE, - a_RelY - LEAVES_CHECK_DISTANCE, a_RelY + LEAVES_CHECK_DISTANCE, - BlockZ - LEAVES_CHECK_DISTANCE, BlockZ + LEAVES_CHECK_DISTANCE, + worldPos - Vector3i(LEAVES_CHECK_DISTANCE, LEAVES_CHECK_DISTANCE, LEAVES_CHECK_DISTANCE), + worldPos + Vector3i(LEAVES_CHECK_DISTANCE, LEAVES_CHECK_DISTANCE, LEAVES_CHECK_DISTANCE), cBlockArea::baTypes) ) { @@ -135,15 +198,15 @@ public: return; } - if (HasNearLog(Area, BlockX, a_RelY, BlockZ)) + if (HasNearLog(Area, worldPos)) { // Wood found, the leaves stay; unset the check bit - a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta ^ 0x08, true, false); + a_Chunk.SetMeta(a_RelPos, Meta ^ 0x08, true, false); return; } // Decay the leaves: - a_ChunkInterface.DropBlockAsPickups({BlockX, a_RelY, BlockZ}); + a_ChunkInterface.DropBlockAsPickups(worldPos); } @@ -156,62 +219,3 @@ public: return 7; } } ; - - - - - -bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Filter the blocks into a {leaves, log, other (air)} set: - BLOCKTYPE * Types = a_Area.GetBlockTypes(); - for (size_t i = a_Area.GetBlockCount() - 1; i > 0; i--) - { - switch (Types[i]) - { - case E_BLOCK_LEAVES: - case E_BLOCK_LOG: - case E_BLOCK_NEW_LEAVES: - case E_BLOCK_NEW_LOG: - { - break; - } - default: - { - Types[i] = E_BLOCK_AIR; - break; - } - } - } // for i - Types[] - - // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block: - // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations - a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE); - for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++) - { - for (int y = std::max(a_BlockY - i, 0); y <= std::min(a_BlockY + i, cChunkDef::Height - 1); y++) - { - for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++) - { - for (int x = a_BlockX - i; x <= a_BlockX + i; x++) - { - if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i) - { - continue; - } - PROCESS_NEIGHBOR(x - 1, y, z); - PROCESS_NEIGHBOR(x + 1, y, z); - PROCESS_NEIGHBOR(x, y, z - 1); - PROCESS_NEIGHBOR(x, y, z + 1); - PROCESS_NEIGHBOR(x, y + 1, z); - PROCESS_NEIGHBOR(x, y - 1, z); - } // for x - } // for z - } // for y - } // for i - BFS iterations - return false; -} - - - - diff --git a/src/Blocks/BlockPlant.h b/src/Blocks/BlockPlant.h index 589619b26..b0760528c 100644 --- a/src/Blocks/BlockPlant.h +++ b/src/Blocks/BlockPlant.h @@ -26,20 +26,25 @@ public: - virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { - Vector3i relPos(a_RelX, a_RelY, a_RelZ); - auto action = CanGrow(a_Chunk, relPos); - switch (action) + auto Action = CanGrow(a_Chunk, a_RelPos); + switch (Action) { case paGrowth: { - Grow(a_Chunk, relPos); + Grow(a_Chunk, a_RelPos); break; } case paDeath: { - a_ChunkInterface.DigBlock(a_WorldInterface, a_Chunk.RelativeToAbsolute(relPos)); + a_ChunkInterface.DigBlock(a_WorldInterface, a_Chunk.RelativeToAbsolute(a_RelPos)); break; } case paStay: break; // do nothing diff --git a/src/Blocks/BlockPortal.h b/src/Blocks/BlockPortal.h index 852a231b6..3f5d87155 100644 --- a/src/Blocks/BlockPortal.h +++ b/src/Blocks/BlockPortal.h @@ -46,18 +46,26 @@ public: - virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { + // Spawn zombie pigmen with a 0.05% chance: if (GetRandomProvider().RandBool(0.9995)) { return; } + auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos); + a_WorldInterface.SpawnMob(WorldPos.x, WorldPos.y, WorldPos.z, mtZombiePigman, false); + } + + - int PosX = a_Chunk.GetPosX() * cChunkDef::Width + a_RelX; - int PosZ = a_Chunk.GetPosZ() * cChunkDef::Width + a_RelZ; - a_WorldInterface.SpawnMob(PosX, a_RelY, PosZ, mtZombiePigman, false); - } virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { diff --git a/src/Blocks/BlockRedstoneOre.h b/src/Blocks/BlockRedstoneOre.h index cb6f73733..4b570ab73 100644 --- a/src/Blocks/BlockRedstoneOre.h +++ b/src/Blocks/BlockRedstoneOre.h @@ -12,9 +12,10 @@ class cBlockRedstoneOreHandler : public cBlockOreHandler { using Super = cBlockOreHandler; + public: - using Super::Super; + using Super::Super; // Inherit constructor from base virtual bool OnUse( cChunkInterface & a_ChunkInterface, @@ -55,19 +56,19 @@ class cBlockGlowingRedstoneOreHandler: public cBlockOreHandler { using Super = cBlockOreHandler; + public: - using Super::Super; + using Super::Super; // Inherit constructor from base virtual void OnUpdate( cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, - int a_RelX, int a_RelY, int a_RelZ + const Vector3i a_RelPos ) override { - const Vector3i a_RelPos{a_RelX, a_RelY, a_RelZ}; auto BlockPos = a_Chunk.RelativeToAbsolute(a_RelPos); a_ChunkInterface.SetBlock(BlockPos, E_BLOCK_REDSTONE_ORE, 0); } diff --git a/src/Blocks/BlockSapling.h b/src/Blocks/BlockSapling.h index b66587288..370353882 100644 --- a/src/Blocks/BlockSapling.h +++ b/src/Blocks/BlockSapling.h @@ -43,26 +43,31 @@ public: - virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { - NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); - NIBBLETYPE Light = std::max(a_Chunk.GetBlockLight(a_RelX, a_RelY, a_RelZ), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(a_RelX, a_RelY, a_RelZ))); + auto Meta = a_Chunk.GetMeta(a_RelPos); + auto Light = std::max(a_Chunk.GetBlockLight(a_RelPos), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(a_RelPos))); // Only grow if we have the right amount of light if (Light > 8) { auto & random = GetRandomProvider(); // Only grow if we are in the right growth stage and have the right amount of space around them. - if (((Meta & 0x08) != 0) && random.RandBool(0.45) && CanGrowAt(a_Chunk, a_RelX, a_RelY, a_RelZ, Meta)) + if (((Meta & 0x08) != 0) && random.RandBool(0.45) && CanGrowAt(a_Chunk, a_RelPos.x, a_RelPos.y, a_RelPos.z, Meta)) { - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - a_Chunk.GetWorld()->GrowTree(BlockX, a_RelY, BlockZ); + auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos); + a_Chunk.GetWorld()->GrowTree(WorldPos.x, WorldPos.y, WorldPos.z); } // Only move to the next growth stage if we haven't gone there yet else if (((Meta & 0x08) == 0) && random.RandBool(0.45)) { - a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta | 0x08); + a_Chunk.SetMeta(a_RelPos, Meta | 0x08); } } } diff --git a/src/Blocks/BlockVine.h b/src/Blocks/BlockVine.h index 9c3c1c53e..f8328f046 100644 --- a/src/Blocks/BlockVine.h +++ b/src/Blocks/BlockVine.h @@ -221,25 +221,33 @@ public: - virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + virtual void OnUpdate( + cChunkInterface & a_ChunkInterface, + cWorldInterface & a_WorldInterface, + cBlockPluginInterface & a_PluginInterface, + cChunk & a_Chunk, + const Vector3i a_RelPos + ) override { UNUSED(a_ChunkInterface); UNUSED(a_WorldInterface); // Vine cannot grow down if at the bottom: - if (a_RelY < 1) + auto GrowPos = a_RelPos.addedY(-1); + if (!cChunkDef::IsValidHeight(GrowPos.y)) { return; } // Grow one block down, if possible: BLOCKTYPE Block; - a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY - 1, a_RelZ, Block); + a_Chunk.UnboundedRelGetBlockType(GrowPos, Block); if (Block == E_BLOCK_AIR) { - if (!a_BlockPluginInterface.CallHookBlockSpread(a_RelX + a_Chunk.GetPosX() * cChunkDef::Width, a_RelY - 1, a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width, ssVineSpread)) + auto WorldPos = a_Chunk.RelativeToAbsolute(GrowPos); + if (!a_PluginInterface.CallHookBlockSpread(WorldPos.x, WorldPos.y, WorldPos.z, ssVineSpread)) { - a_Chunk.UnboundedRelSetBlock(a_RelX, a_RelY - 1, a_RelZ, E_BLOCK_VINES, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + a_Chunk.UnboundedRelSetBlock(GrowPos, E_BLOCK_VINES, a_Chunk.GetMeta(a_RelPos)); } } } diff --git a/src/Blocks/CMakeLists.txt b/src/Blocks/CMakeLists.txt index 24d0a20a6..dea14b7f1 100644 --- a/src/Blocks/CMakeLists.txt +++ b/src/Blocks/CMakeLists.txt @@ -47,6 +47,7 @@ SET (HDRS BlockFurnace.h BlockGlass.h BlockGlowstone.h + BlockGrass.h BlockGravel.h BlockHandler.h BlockHopper.h diff --git a/src/Blocks/WorldInterface.h b/src/Blocks/WorldInterface.h index b4dcfa4a9..0967e2bb8 100644 --- a/src/Blocks/WorldInterface.h +++ b/src/Blocks/WorldInterface.h @@ -73,6 +73,10 @@ public: /** Returns true if it is raining or storming at the specified location. This takes into account biomes. */ virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) = 0; + /** Returns true if it is raining or storming at the specified location, + and the rain reaches the specified block position. */ + virtual bool IsWeatherWetAtXYZ(Vector3i a_Pos) = 0; + /** Returns or sets the minumim or maximum netherportal width */ virtual int GetMinNetherPortalWidth(void) const = 0; virtual int GetMaxNetherPortalWidth(void) const = 0; diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 17e130cdd..5d0c5e0e6 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -71,9 +71,6 @@ cChunk::cChunk( m_World(a_World), m_ChunkMap(a_ChunkMap), m_ChunkData(a_Pool), - m_BlockTickX(0), - m_BlockTickY(0), - m_BlockTickZ(0), m_NeighborXM(a_NeighborXM), m_NeighborXP(a_NeighborXP), m_NeighborZM(a_NeighborZM), @@ -724,13 +721,13 @@ void cChunk::Tick(std::chrono::milliseconds a_Dt) -void cChunk::TickBlock(int a_RelX, int a_RelY, int a_RelZ) +void cChunk::TickBlock(const Vector3i a_RelPos) { - cBlockHandler * Handler = BlockHandler(GetBlock(a_RelX, a_RelY, a_RelZ)); + cBlockHandler * Handler = BlockHandler(GetBlock(a_RelPos)); ASSERT(Handler != nullptr); // Happenned on server restart, FS #243 cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*this->GetWorld()); - Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, a_RelX, a_RelY, a_RelZ); + Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, a_RelPos); } @@ -839,43 +836,27 @@ void cChunk::CheckBlocks() void cChunk::TickBlocks(void) { - // Tick dem blocks - // _X: We must limit the random number or else we get a nasty int overflow bug - https://forum.cuberite.org/thread-457.html - int RandomX = m_World->GetTickRandomNumber(0x00ffffff); - int RandomY = m_World->GetTickRandomNumber(0x00ffffff); - int RandomZ = m_World->GetTickRandomNumber(0x00ffffff); - int TickX = m_BlockTickX; - int TickY = m_BlockTickY; - int TickZ = m_BlockTickZ; - cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*this->GetWorld()); - // This for loop looks disgusting, but it actually does a simple thing - first processes m_BlockTick, then adds random to it - // This is so that SetNextBlockTick() works - for (int i = 0; i < 50; i++, - - // This weird construct (*2, then /2) is needed, - // otherwise the blocktick distribution is too biased towards even coords! - - TickX = (TickX + RandomX) % (Width * 2), - TickY = (TickY + RandomY) % (Height * 2), - TickZ = (TickZ + RandomZ) % (Width * 2), - m_BlockTickX = TickX / 2, - m_BlockTickY = TickY / 2, - m_BlockTickZ = TickZ / 2 - ) + // Tick random blocks, but the first one should be m_BlockToTick (so that SetNextBlockToTick() works) + auto Idx = cChunkDef::MakeIndexNoCheck(m_BlockToTick); + for (int i = 0; i < 50; ++i) { - - if (m_BlockTickY > cChunkDef::GetHeight(m_HeightMap, m_BlockTickX, m_BlockTickZ)) + auto Pos = cChunkDef::IndexToCoordinate(static_cast(Idx)); + Idx = m_World->GetTickRandomNumber(cChunkDef::NumBlocks - 1); + if (Pos.y > cChunkDef::GetHeight(m_HeightMap, Pos.x, Pos.z)) { continue; // It's all air up here } - cBlockHandler * Handler = BlockHandler(GetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ)); + cBlockHandler * Handler = BlockHandler(GetBlock(Pos)); ASSERT(Handler != nullptr); // Happenned on server restart, FS #243 - Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, m_BlockTickX, m_BlockTickY, m_BlockTickZ); - } // for i - tickblocks + Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, Pos); + } // for i + + // Set a new random coord for the next tick: + m_BlockToTick = cChunkDef::IndexToCoordinate(static_cast(Idx)); } diff --git a/src/Chunk.h b/src/Chunk.h index 7ba12f5ef..bb6f2e5b5 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -147,7 +147,7 @@ public: void Tick(std::chrono::milliseconds a_Dt); /** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */ - void TickBlock(int a_RelX, int a_RelY, int a_RelZ); + void TickBlock(const Vector3i a_RelPos); int GetPosX(void) const { return m_PosX; } int GetPosZ(void) const { return m_PosZ; } @@ -375,12 +375,12 @@ public: m_IsSaving = false; } - /** Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call */ - inline void SetNextBlockTick(int a_RelX, int a_RelY, int a_RelZ) + /** Causes the specified block to be ticked on the next Tick() call. + Plugins can use this via the cWorld:SetNextBlockToTick() API. + Only one block coord per chunk may be set, a second call overwrites the first call */ + inline void SetNextBlockToTick(const Vector3i a_RelPos) { - m_BlockTickX = a_RelX; - m_BlockTickY = a_RelY; - m_BlockTickZ = a_RelZ; + m_BlockToTick = a_RelPos; } inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const @@ -629,7 +629,9 @@ private: cChunkDef::HeightMap m_HeightMap; cChunkDef::BiomeMap m_BiomeMap; - int m_BlockTickX, m_BlockTickY, m_BlockTickZ; + /** Relative coords of the block to tick first in the next Tick() call. + Plugins can use this to force a tick in a specific block, using cWorld:SetNextBlockToTick() API. */ + Vector3i m_BlockToTick; cChunk * m_NeighborXM; // Neighbor at [X - 1, Z] cChunk * m_NeighborXP; // Neighbor at [X + 1, Z] diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index fd4987bb4..1713173f8 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -2030,16 +2030,16 @@ int cChunkMap::GrowPlantAt(Vector3i a_BlockPos, int a_NumStages) -void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +void cChunkMap::SetNextBlockToTick(const Vector3i a_BlockPos) { - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + auto ChunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto RelPos = cChunkDef::AbsoluteToRelative(a_BlockPos, ChunkPos); cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); + auto Chunk = GetChunkNoLoad(ChunkPos); if (Chunk != nullptr) { - Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); + Chunk->SetNextBlockToTick(RelPos); } } @@ -2100,17 +2100,17 @@ void cChunkMap::Tick(std::chrono::milliseconds a_Dt) -void cChunkMap::TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +void cChunkMap::TickBlock(const Vector3i a_BlockPos) { + auto ChunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto RelPos = cChunkDef::AbsoluteToRelative(a_BlockPos, ChunkPos); cCSLock Lock(m_CSChunks); - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); + auto Chunk = GetChunkNoLoad(ChunkPos); if ((Chunk == nullptr) || !Chunk->IsValid()) { return; } - Chunk->TickBlock(a_BlockX, a_BlockY, a_BlockZ); + Chunk->TickBlock(RelPos); } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 5c0aa7688..7096e3d70 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -373,8 +373,10 @@ public: Returns the number of stages the plant has grown, 0 if not a plant. */ int GrowPlantAt(Vector3i a_BlockPos, int a_NumStages = 1); - /** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */ - void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Causes the specified block to be ticked on the next Tick() call. + Plugins can use this via the cWorld:SetNextBlockToTick() API. + Only one block coord per chunk may be set, a second call overwrites the first call */ + void SetNextBlockToTick(const Vector3i a_BlockPos); /** Make a Mob census, of all mobs, their family, their chunk and their distance to closest player */ void CollectMobCensus(cMobCensus & a_ToFill); @@ -385,7 +387,7 @@ public: void Tick(std::chrono::milliseconds a_Dt); /** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */ - void TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + void TickBlock(const Vector3i a_BlockPos); void UnloadUnusedChunks(void); void SaveAllChunks(void); diff --git a/src/World.cpp b/src/World.cpp index 0cbe0e445..eaed47e9c 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -590,9 +590,9 @@ bool cWorld::IsWeatherWetAtXYZ(Vector3i a_Pos) -void cWorld::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +void cWorld::SetNextBlockToTick(const Vector3i a_BlockPos) { - return m_ChunkMap->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); + return m_ChunkMap->SetNextBlockToTick(a_BlockPos); } @@ -3195,7 +3195,7 @@ void cWorld::TickQueuedBlocks(void) if (Block->TicksToWait <= 0) { // TODO: Handle the case when the chunk is already unloaded - m_ChunkMap->TickBlock(Block->X, Block->Y, Block->Z); + m_ChunkMap->TickBlock({Block->X, Block->Y, Block->Z}); delete Block; // We don't have to remove it from the vector, this will happen automatically on the next tick } else diff --git a/src/World.h b/src/World.h index 973ccc117..2d956bf49 100644 --- a/src/World.h +++ b/src/World.h @@ -1043,11 +1043,9 @@ public: return (IsWeatherWet() && !IsBiomeNoDownfall(Biome) && !IsBiomeCold(Biome)); } - /** Returns true if the specified location has wet weather (rain or storm), - using the same logic as IsWeatherWetAt, except that any rain-blocking blocks - above the specified position will block the precipitation and this function - will return false. */ - virtual bool IsWeatherWetAtXYZ(Vector3i a_Pos); + /** Returns true if it is raining or storming at the specified location, + and the rain reaches (the bottom of) the specified block position. */ + virtual bool IsWeatherWetAtXYZ(Vector3i a_Pos) override; /** Returns the seed of the world. */ int GetSeed(void) { return m_Generator.GetSeed(); } @@ -1058,8 +1056,9 @@ public: cWorldStorage & GetStorage (void) { return m_Storage; } cChunkMap * GetChunkMap (void) { return m_ChunkMap.get(); } - /** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */ - void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export + /** Causes the specified block to be ticked on the next Tick() call. + Only one block coord per chunk may be set, a second call overwrites the first call */ + void SetNextBlockToTick(const Vector3i a_BlockPos); // tolua_export int GetMaxSugarcaneHeight(void) const { return m_MaxSugarcaneHeight; } // tolua_export int GetMaxCactusHeight (void) const { return m_MaxCactusHeight; } // tolua_export -- cgit v1.2.3