From 885d8287125439047ca2318f8e349b8da279e612 Mon Sep 17 00:00:00 2001 From: Lukas Pioch Date: Fri, 7 Jul 2017 09:31:45 +0200 Subject: [PATCH] Added bed entity (#3823) * Added bed entity * Export cBedEntity to lua * Set color of bed through item damage value * Added bed entity to APIDoc * NBT: Added loading and saving * Crafting recipes for the colored beds --- .../Plugins/APIDump/Classes/BlockEntities.lua | 40 ++++++++++++ Server/Plugins/APIDump/Classes/World.lua | 29 +++++++++ Server/crafting.txt | 22 ++++++- src/Bindings/AllToLua.pkg | 1 + src/Bindings/CMakeLists.txt | 1 + src/Bindings/ManualBindings_World.cpp | 1 + src/BlockEntities/BedEntity.cpp | 56 +++++++++++++++++ src/BlockEntities/BedEntity.h | 45 ++++++++++++++ src/BlockEntities/BlockEntity.cpp | 3 + src/BlockEntities/CMakeLists.txt | 2 + src/Blocks/BlockBed.cpp | 62 ++++++++++++++++++- src/Blocks/BlockBed.h | 21 ++++--- src/Blocks/BlockEntity.h | 2 +- src/Blocks/BlockHandler.cpp | 11 +++- src/Blocks/BlockHandler.h | 4 ++ src/Chunk.cpp | 12 ++++ src/Chunk.h | 5 ++ src/ChunkMap.cpp | 18 ++++++ src/ChunkMap.h | 6 ++ src/Protocol/Protocol_1_11.cpp | 12 ++++ src/Protocol/Protocol_1_12.cpp | 27 ++++++++ src/Protocol/Protocol_1_12.h | 1 + src/World.cpp | 9 +++ src/World.h | 3 + src/WorldStorage/NBTChunkSerializer.cpp | 14 +++++ src/WorldStorage/NBTChunkSerializer.h | 2 + src/WorldStorage/WSSAnvil.cpp | 28 +++++++++ src/WorldStorage/WSSAnvil.h | 1 + 28 files changed, 425 insertions(+), 13 deletions(-) create mode 100644 src/BlockEntities/BedEntity.cpp create mode 100644 src/BlockEntities/BedEntity.h diff --git a/Server/Plugins/APIDump/Classes/BlockEntities.lua b/Server/Plugins/APIDump/Classes/BlockEntities.lua index 5f1289f1e..eac70c1c8 100644 --- a/Server/Plugins/APIDump/Classes/BlockEntities.lua +++ b/Server/Plugins/APIDump/Classes/BlockEntities.lua @@ -155,6 +155,46 @@ return }, Inherits = "cBlockEntityWithItems", }, + cBedEntity = + { + Desc = [[ + A bed entity is a {{cBlockEntity}} descendant that represents a bed in the world. + ]], + Functions = + { + GetColor = + { + Returns = + { + { + Type = "number", + }, + }, + Notes = "Returns the color of the bed.", + }, + SetColor = + { + Params = + { + { + Type = "number", + }, + }, + Notes = "Set the color of the bed. A value between 0 - 15. The wool meta color values can be used.", + }, + IsPillowBlock = + { + Returns = + { + { + Type = "boolean", + }, + }, + Notes = "Returns true if this is the pillow block, it has then the meta 8.", + }, + }, + Inherits = "cBlockEntity", + }, cBlockEntity = { Desc = [[ diff --git a/Server/Plugins/APIDump/Classes/World.lua b/Server/Plugins/APIDump/Classes/World.lua index 1614886fd..429d32ed6 100644 --- a/Server/Plugins/APIDump/Classes/World.lua +++ b/Server/Plugins/APIDump/Classes/World.lua @@ -505,6 +505,35 @@ function OnAllChunksAvailable() All return values from the callbacks are i }, Notes = "If there is a beacon at the specified coords, calls the CallbackFunction with the {{cBeaconEntity}} parameter representing the beacon. The CallbackFunction has the following signature:
function Callback({{cBeaconEntity|BeaconEntity}})
The function returns false if there is no beacon, or if there is, it returns the bool value that the callback has returned.", }, + DoWithBedAt = + { + Params = + { + { + Name = "BlockX", + Type = "number", + }, + { + Name = "BlockY", + Type = "number", + }, + { + Name = "BlockZ", + Type = "number", + }, + { + Name = "CallbackFunction", + Type = "function", + }, + }, + Returns = + { + { + Type = "boolean", + }, + }, + Notes = "If there is a bed at the specified coords, calls the CallbackFunction with the {{cBedEntity}} parameter representing the bed. The CallbackFunction has the following signature:
function Callback({{cBedEntity|cBedEntity}})
The function returns false if there is no bed, or if there is, it returns the bool value that the callback has returned.", + }, DoWithBlockEntityAt = { Params = diff --git a/Server/crafting.txt b/Server/crafting.txt index e74725921..caac966ae 100644 --- a/Server/crafting.txt +++ b/Server/crafting.txt @@ -383,7 +383,6 @@ AcaciaFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | AcaciaPlanks, 2:1, 2:2 Anvil = IronBlock, 1:1, 2:1, 3:1 | IronIngot, 2:2, 1:3, 2:3, 3:3 ArmorStand = Stick, 1:1, 1:3, 2:1, 2:2, 3:1, 3:3 | StoneSlab, 2:3 Beacon = Glass, 1:1, 1:2, 2:1, 3:1, 3:2 | Obsidian, 1:3, 2:3, 3:3 | NetherStar, 2:2 -Bed = Planks^-1, 1:2, 2:2, 3:2 | Wool^-1, 1:1, 2:1, 3:1 BirchFence, 3 = BirchPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 BirchFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | BirchPlanks, 2:1, 2:2 Bookandquill = Book, * | feather, * | inksac, * @@ -949,3 +948,24 @@ FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * + + + +#******************************************************# +# Bed different colors +Bed^0 = Planks^-1, 1:2, 2:2, 3:2 | Wool^0, 1:1, 2:1, 3:1 +Bed^1 = Planks^-1, 1:2, 2:2, 3:2 | Wool^1, 1:1, 2:1, 3:1 +Bed^2 = Planks^-1, 1:2, 2:2, 3:2 | Wool^2, 1:1, 2:1, 3:1 +Bed^3 = Planks^-1, 1:2, 2:2, 3:2 | Wool^3, 1:1, 2:1, 3:1 +Bed^4 = Planks^-1, 1:2, 2:2, 3:2 | Wool^4, 1:1, 2:1, 3:1 +Bed^5 = Planks^-1, 1:2, 2:2, 3:2 | Wool^5, 1:1, 2:1, 3:1 +Bed^6 = Planks^-1, 1:2, 2:2, 3:2 | Wool^6, 1:1, 2:1, 3:1 +Bed^7 = Planks^-1, 1:2, 2:2, 3:2 | Wool^7, 1:1, 2:1, 3:1 +Bed^8 = Planks^-1, 1:2, 2:2, 3:2 | Wool^8, 1:1, 2:1, 3:1 +Bed^9 = Planks^-1, 1:2, 2:2, 3:2 | Wool^9, 1:1, 2:1, 3:1 +Bed^10 = Planks^-1, 1:2, 2:2, 3:2 | Wool^10, 1:1, 2:1, 3:1 +Bed^11 = Planks^-1, 1:2, 2:2, 3:2 | Wool^11, 1:1, 2:1, 3:1 +Bed^12 = Planks^-1, 1:2, 2:2, 3:2 | Wool^12, 1:1, 2:1, 3:1 +Bed^13 = Planks^-1, 1:2, 2:2, 3:2 | Wool^13, 1:1, 2:1, 3:1 +Bed^14 = Planks^-1, 1:2, 2:2, 3:2 | Wool^14, 1:1, 2:1, 3:1 +Bed^15 = Planks^-1, 1:2, 2:2, 3:2 | Wool^15, 1:1, 2:1, 3:1 diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index b59af7374..31c914bcd 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -100,6 +100,7 @@ $cfile "../Mobs/Monster.h" // Block entities: $cfile "../BlockEntities/BlockEntity.h" $cfile "../BlockEntities/BeaconEntity.h" +$cfile "../BlockEntities/BedEntity.h" $cfile "../BlockEntities/BlockEntityWithItems.h" $cfile "../BlockEntities/BrewingstandEntity.h" $cfile "../BlockEntities/ChestEntity.h" diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index 64db464f2..e50db12c3 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -66,6 +66,7 @@ set(BINDING_DEPENDENCIES ../BiomeDef.h ../BlockArea.h ../BlockEntities/BeaconEntity.h + ../BlockEntities/BedEntity.h ../BlockEntities/BlockEntity.h ../BlockEntities/BlockEntityWithItems.h ../BlockEntities/BrewingstandEntity.h diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp index f24e5ac34..7bf4d3f80 100644 --- a/src/Bindings/ManualBindings_World.cpp +++ b/src/Bindings/ManualBindings_World.cpp @@ -639,6 +639,7 @@ void cManualBindings::BindWorld(lua_State * tolua_S) tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay); tolua_function(tolua_S, "DoExplosionAt", tolua_cWorld_DoExplosionAt); tolua_function(tolua_S, "DoWithBeaconAt", DoWithXYZ); + tolua_function(tolua_S, "DoWithBedAt", DoWithXYZ); tolua_function(tolua_S, "DoWithBlockEntityAt", DoWithXYZ); tolua_function(tolua_S, "DoWithBrewingstandAt", DoWithXYZ); tolua_function(tolua_S, "DoWithChestAt", DoWithXYZ); diff --git a/src/BlockEntities/BedEntity.cpp b/src/BlockEntities/BedEntity.cpp new file mode 100644 index 000000000..b8f61c049 --- /dev/null +++ b/src/BlockEntities/BedEntity.cpp @@ -0,0 +1,56 @@ + +// BedEntity.cpp + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BedEntity.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../ClientHandle.h" +#include "../Blocks/BlockBed.h" + +cBedEntity::cBedEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, short a_Color): + Super(a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Color(a_Color) +{ + ASSERT(a_BlockType == E_BLOCK_BED); +} + + + + + +void cBedEntity::CopyFrom(const cBlockEntity & a_Src) +{ + Super::CopyFrom(a_Src); + auto & src = reinterpret_cast(a_Src); + m_Color = src.m_Color; +} + + + + + +void cBedEntity::SendTo(cClientHandle & a_Client) +{ + a_Client.SendUpdateBlockEntity(*this); +} + + + + + +void cBedEntity::SetColor(short a_Color) +{ + m_Color = a_Color; + int posX = m_PosX; + int posY = m_PosY; + int posZ = m_PosZ; + + // 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, [posX, posY, posZ](cWorld & a_World) + { + a_World.BroadcastBlockEntity(posX, posY, posZ); + }); +} diff --git a/src/BlockEntities/BedEntity.h b/src/BlockEntities/BedEntity.h new file mode 100644 index 000000000..8caa205c4 --- /dev/null +++ b/src/BlockEntities/BedEntity.h @@ -0,0 +1,45 @@ + +// BedEntity.h + +#pragma once + +#include "BlockEntity.h" + + + + + +// tolua_begin + +class cBedEntity : + public cBlockEntity +{ + typedef cBlockEntity Super; +public: + // tolua_end + + BLOCKENTITY_PROTODEF(cBedEntity) + + cBedEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, short a_Color = E_META_WOOL_RED); + + // tolua_begin + + /** Returns the color of the bed */ + short GetColor(void) const { return m_Color; } + + /** Set the color of the bed. */ + void SetColor(short a_Color); + + /** Returns true if this is the pillow block, it has then the meta 8. */ + bool IsPillowBlock(void) { return ((m_BlockMeta & 0x08) == 0x08); } + + // tolua_end + + // cBlockEntity overrides: + virtual void CopyFrom(const cBlockEntity & a_Src) override; + virtual bool UsedBy(cPlayer * a_Player) override { return false; } + virtual void SendTo(cClientHandle & a_Client) override; + +private: + short m_Color; +}; // tolua_export diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp index f60eb5622..f2380734a 100644 --- a/src/BlockEntities/BlockEntity.cpp +++ b/src/BlockEntities/BlockEntity.cpp @@ -5,6 +5,7 @@ #include "Globals.h" #include "BeaconEntity.h" +#include "BedEntity.h" #include "BlockEntity.h" #include "BrewingstandEntity.h" #include "ChestEntity.h" @@ -42,6 +43,7 @@ bool cBlockEntity::IsBlockEntityBlockType(BLOCKTYPE a_BlockType) switch (a_BlockType) { case E_BLOCK_BEACON: + case E_BLOCK_BED: case E_BLOCK_BREWING_STAND: case E_BLOCK_CHEST: case E_BLOCK_COMMAND_BLOCK: @@ -75,6 +77,7 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE switch (a_BlockType) { case E_BLOCK_BEACON: return new cBeaconEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_BED: return new cBedEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_BREWING_STAND: return new cBrewingstandEntity(a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_CHEST: return new cChestEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_COMMAND_BLOCK: return new cCommandBlockEntity(a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World); diff --git a/src/BlockEntities/CMakeLists.txt b/src/BlockEntities/CMakeLists.txt index d6bf6355c..93033931c 100644 --- a/src/BlockEntities/CMakeLists.txt +++ b/src/BlockEntities/CMakeLists.txt @@ -4,6 +4,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../") SET (SRCS BeaconEntity.cpp + BedEntity.cpp BlockEntity.cpp BlockEntityWithItems.cpp BrewingstandEntity.cpp @@ -25,6 +26,7 @@ SET (SRCS SET (HDRS BeaconEntity.h + BedEntity.h BlockEntity.h BlockEntityWithItems.h BrewingstandEntity.h diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp index adc01c158..d6112f853 100644 --- a/src/Blocks/BlockBed.cpp +++ b/src/Blocks/BlockBed.cpp @@ -1,3 +1,6 @@ + +// BlockBed.cpp + #include "Globals.h" #include "BlockBed.h" @@ -7,6 +10,7 @@ #include "../BoundingBox.h" #include "../Mobs/Monster.h" #include "../Entities/Entity.h" +#include "../BlockEntities/BedEntity.h" @@ -16,14 +20,19 @@ void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInt { NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ); - Vector3i Direction = MetaDataToDirection( OldMeta & 0x3); + Vector3i ThisPos(a_BlockX, a_BlockY, a_BlockZ); + Vector3i Direction = MetaDataToDirection(OldMeta & 0x3); if (OldMeta & 0x8) { // Was pillow if (a_ChunkInterface.GetBlock(ThisPos - Direction) == E_BLOCK_BED) { + // First replace the bed with air a_ChunkInterface.FastSetBlock(ThisPos - Direction, E_BLOCK_AIR, 0); + + // Then destroy the bed entity + Vector3i PillowPos(ThisPos - Direction); + a_ChunkInterface.SetBlock(PillowPos.x, PillowPos.y, PillowPos.z, E_BLOCK_AIR, 0); } } else @@ -31,7 +40,12 @@ void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInt // Was foot end if (a_ChunkInterface.GetBlock(ThisPos + Direction) == E_BLOCK_BED) { + // First replace the bed with air a_ChunkInterface.FastSetBlock(ThisPos + Direction, E_BLOCK_AIR, 0); + + // Then destroy the bed entity + Vector3i FootPos(ThisPos + Direction); + a_ChunkInterface.SetBlock(FootPos.x, FootPos.y, FootPos.z, E_BLOCK_AIR, 0); } } } @@ -169,4 +183,48 @@ bool cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface +void cBlockBedHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange) +{ + class cBedColor : + public cBedCallback + { + public: + short m_Color; + cBedColor(short a_Color) : + m_Color(a_Color) + { + } + + virtual bool Item(cBedEntity * a_Bed) override + { + a_Bed->SetColor(m_Color); + return true; + } + }; + cBedColor BedCallback(a_Player->GetEquippedItem().m_ItemDamage); + a_Player->GetWorld()->DoWithBedAt(a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), BedCallback); +} + + + + + +void cBlockBedHandler::ConvertToPickups(cEntity * a_Digger, cItems & a_Pickups, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + class cBedColor : + public cBedCallback + { + public: + short m_Color = E_META_WOOL_RED; + + virtual bool Item(cBedEntity * a_Bed) override + { + m_Color = a_Bed->GetColor(); + return true; + } + }; + cBedColor BedCallback; + a_Digger->GetWorld()->DoWithBedAt(a_BlockX, a_BlockY, a_BlockZ, BedCallback); + a_Pickups.Add(cItem(E_ITEM_BED, 1, BedCallback.m_Color)); +} diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h index cfb02ceeb..a9a3d0e20 100644 --- a/src/Blocks/BlockBed.h +++ b/src/Blocks/BlockBed.h @@ -1,10 +1,14 @@ +// BlockBed.h + #pragma once +#include "BlockEntity.h" #include "BlockHandler.h" #include "MetaRotator.h" -#include "Item.h" #include "ChunkInterface.h" +#include "../World.h" +#include "../Entities/Entity.h" class cPlayer; @@ -14,15 +18,16 @@ class cWorldInterface; class cBlockBedHandler : - public cMetaRotator + public cMetaRotator { public: cBlockBedHandler(BLOCKTYPE a_BlockType) - : cMetaRotator(a_BlockType) + : cMetaRotator(a_BlockType) { } virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override; + 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; virtual bool IsUseable(void) override @@ -30,11 +35,9 @@ public: return true; } - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to zero - a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0)); - } + virtual void ConvertToPickups(cItems & Pickups, NIBBLETYPE Meta) override {} + + virtual void ConvertToPickups(cEntity * a_Digger, cItems & a_Pickups, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ) override; // Bed specific helper functions static NIBBLETYPE RotationToMetaData(double a_Rotation) @@ -77,6 +80,8 @@ public: a_ChunkInterface.SetBlockMeta(a_BedPosition.x, a_BedPosition.y, a_BedPosition.z, Meta); } + virtual void OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange) override; + virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override { UNUSED(a_Meta); diff --git a/src/Blocks/BlockEntity.h b/src/Blocks/BlockEntity.h index 06cd860c5..5b1267ec9 100644 --- a/src/Blocks/BlockEntity.h +++ b/src/Blocks/BlockEntity.h @@ -2,7 +2,7 @@ #pragma once #include "BlockHandler.h" - +#include "ChunkInterface.h" diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index 36b20088c..41e3561d1 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -463,7 +463,6 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac { case E_BLOCK_ACACIA_DOOR: case E_BLOCK_ACTIVE_COMPARATOR: - case E_BLOCK_BED: case E_BLOCK_BEETROOTS: case E_BLOCK_BIRCH_DOOR: case E_BLOCK_BREWING_STAND: @@ -512,9 +511,19 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac ConvertToPickups(Pickups, Meta); break; } + case E_BLOCK_BED: + { + // Need to access the bed entity to get the color for the item damage + ConvertToPickups(a_Digger, Pickups, Meta, a_BlockX, a_BlockY, a_BlockZ); + } default: Pickups.Add(m_BlockType, 1, Meta); break; } } + else if (m_BlockType == E_BLOCK_BED) + { + // Need to access the bed entity to get the color for the item damage + ConvertToPickups(a_Digger, Pickups, Meta, a_BlockX, a_BlockY, a_BlockZ); + } else { ConvertToPickups(Pickups, Meta); diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h index 24552fb46..625def7d8 100644 --- a/src/Blocks/BlockHandler.h +++ b/src/Blocks/BlockHandler.h @@ -80,6 +80,10 @@ public: /** Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents */ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta); + /** Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents. + Overloaded method with coords and digger, for blocks that needs to access the block entity, e.g. a bed */ + virtual void ConvertToPickups(cEntity * a_Digger, cItems & a_Pickups, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ) {} + /** Handles the dropping, but not destruction, of a block based on what ConvertTo(Verbatim)Pickups() returns, including the spawning of pickups and alertion of plugins @param a_Digger The entity causing the drop; it may be nullptr @param a_CanDrop Informs the handler whether the block should be dropped at all. One example when this is false is when stone is destroyed by hand diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 99f643437..8a13fd20e 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -13,6 +13,7 @@ #include "zlib/zlib.h" #include "Defines.h" #include "BlockEntities/BeaconEntity.h" +#include "BlockEntities/BedEntity.h" #include "BlockEntities/BrewingstandEntity.h" #include "BlockEntities/ChestEntity.h" #include "BlockEntities/CommandBlockEntity.h" @@ -1404,6 +1405,7 @@ void cChunk::CreateBlockEntities(void) switch (BlockType) { case E_BLOCK_BEACON: + case E_BLOCK_BED: case E_BLOCK_TRAPPED_CHEST: case E_BLOCK_CHEST: case E_BLOCK_COMMAND_BLOCK: @@ -1537,6 +1539,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, switch (a_BlockType) { case E_BLOCK_BEACON: + case E_BLOCK_BED: case E_BLOCK_TRAPPED_CHEST: case E_BLOCK_CHEST: case E_BLOCK_COMMAND_BLOCK: @@ -2265,6 +2268,15 @@ bool cChunk::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCal +bool cChunk::DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback & a_Callback) +{ + return GenericDoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + bool cChunk::DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback) { diff --git a/src/Chunk.h b/src/Chunk.h index 3a0d37768..b5dcd7b91 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -30,6 +30,7 @@ class cServer; class cPlayer; class cChunkMap; class cBeaconEntity; +class cBedEntity; class cBrewingstandEntity; class cBoundingBox; class cChestEntity; @@ -53,6 +54,7 @@ class cSetChunkData; typedef std::list cClientHandleList; typedef cItemCallback cEntityCallback; typedef cItemCallback cBeaconCallback; +typedef cItemCallback cBedCallback; typedef cItemCallback cBrewingstandCallback; typedef cItemCallback cChestCallback; typedef cItemCallback cDispenserCallback; @@ -317,6 +319,9 @@ public: /** Calls the callback for the brewingstand at the specified coords; returns false if there's no brewingstand at those coords, true if found */ bool DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback); // Lua-acessible + /** Calls the callback for the bed at the specified coords; returns false if there's no bed at those coords, true if found */ + bool DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback & a_Callback); // Lua-acessible + /** Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found */ bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 019746b9d..f04af4340 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -2042,6 +2042,24 @@ bool cChunkMap::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeacon +bool cChunkMap::DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSChunks); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithBedAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + bool cChunkMap::DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback) { int ChunkX, ChunkZ; diff --git a/src/ChunkMap.h b/src/ChunkMap.h index e7694e907..7c57f9669 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -19,6 +19,7 @@ class cChunkStay; class cChunk; class cPlayer; class cBeaconEntity; +class cBedEntity; class cBrewingstandEntity; class cChestEntity; class cDispenserEntity; @@ -44,6 +45,7 @@ typedef cChunk * cChunkPtr; typedef cItemCallback cEntityCallback; typedef cItemCallback cBlockEntityCallback; typedef cItemCallback cBeaconCallback; +typedef cItemCallback cBedCallback; typedef cItemCallback cBrewingstandCallback; typedef cItemCallback cChestCallback; typedef cItemCallback cDispenserCallback; @@ -277,6 +279,10 @@ public: Returns false if there's no beacon at those coords, true if found. */ bool DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & a_Callback); // Lua-acessible + /** Calls the callback for the bed at the specified coords. + Returns false if there's no bed at those coords, true if found. */ + bool DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback & a_Callback); // Lua-acessible + /** Calls the callback for the brewingstand at the specified coords; returns false if there's no brewingstand at those coords, true if found */ bool DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback); // Lua-acessible diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp index 8f9a0b0b9..e4aaa8f6f 100644 --- a/src/Protocol/Protocol_1_11.cpp +++ b/src/Protocol/Protocol_1_11.cpp @@ -30,6 +30,7 @@ Implements the 1.11 protocol classes: #include "../Mobs/IncludeAllMonsters.h" #include "../BlockEntities/BeaconEntity.h" +#include "../BlockEntities/BedEntity.h" #include "../BlockEntities/CommandBlockEntity.h" #include "../BlockEntities/MobHeadEntity.h" #include "../BlockEntities/MobSpawnerEntity.h" @@ -421,6 +422,17 @@ void cProtocol_1_11_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity break; } + case E_BLOCK_BED: + { + auto & BedEntity = reinterpret_cast(a_BlockEntity); + Writer.AddInt("x", BedEntity.GetPosX()); + Writer.AddInt("y", BedEntity.GetPosY()); + Writer.AddInt("z", BedEntity.GetPosZ()); + Writer.AddInt("color", BedEntity.GetColor()); + Writer.AddString("id", "Bed"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though + break; + } + case E_BLOCK_COMMAND_BLOCK: { auto & CommandBlockEntity = reinterpret_cast(a_BlockEntity); diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index a0f2b9aa7..904b31ca0 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -1269,6 +1269,33 @@ void cProtocol_1_12::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +void cProtocol_1_12::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x09); // Update tile entity packet + Pkt.WritePosition64(a_BlockEntity.GetPosX(), a_BlockEntity.GetPosY(), a_BlockEntity.GetPosZ()); + + Byte Action = 0; + switch (a_BlockEntity.GetBlockType()) + { + case E_BLOCK_MOB_SPAWNER: Action = 1; break; // Update mob spawner spinny mob thing + case E_BLOCK_COMMAND_BLOCK: Action = 2; break; // Update command block text + case E_BLOCK_BEACON: Action = 3; break; // Update beacon entity + case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity + case E_BLOCK_FLOWER_POT: Action = 5; break; // Update flower pot + case E_BLOCK_BED: Action = 11; break; // Update bed color + default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break; + } + Pkt.WriteBEUInt8(Action); + + WriteBlockEntity(Pkt, a_BlockEntity); +} + + + + + void cProtocol_1_12::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) { ASSERT(m_State == 3); // In game mode? diff --git a/src/Protocol/Protocol_1_12.h b/src/Protocol/Protocol_1_12.h index f61b713ae..814932e79 100644 --- a/src/Protocol/Protocol_1_12.h +++ b/src/Protocol/Protocol_1_12.h @@ -59,6 +59,7 @@ public: virtual void SendTeleportEntity(const cEntity & a_Entity) override; virtual void SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override; virtual void SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override; + virtual void SendUpdateBlockEntity(cBlockEntity & a_BlockEntity) override; protected: virtual bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) override; virtual void HandlePacketBlockPlace(cByteBuffer & a_ByteBuffer) override; diff --git a/src/World.cpp b/src/World.cpp index da41b22db..fa2116597 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1439,6 +1439,15 @@ bool cWorld::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCal +bool cWorld::DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback & a_Callback) +{ + return m_ChunkMap->DoWithBedAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + bool cWorld::DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback) { return m_ChunkMap->DoWithBrewingstandAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); diff --git a/src/World.h b/src/World.h index 3af766166..bae7adbb9 100644 --- a/src/World.h +++ b/src/World.h @@ -536,6 +536,9 @@ public: /** Calls the callback for the beacon at the specified coords; returns false if there's no beacon at those coords, true if found */ bool DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & a_Callback); // Exported in ManualBindings.cpp + /** Calls the callback for the bed at the specified coords; returns false if there's no bed at those coords, true if found */ + bool DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback & a_Callback); // Exported in ManualBindings.cpp + /** Calls the callback for the brewingstand at the specified coords; returns false if there's no brewingstand at those coords, true if found */ bool DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback); // Lua-acessible diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index d98fc7bb5..d61e61879 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -11,6 +11,7 @@ #include "FastNBT.h" #include "../BlockEntities/BeaconEntity.h" +#include "../BlockEntities/BedEntity.h" #include "../BlockEntities/BrewingstandEntity.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/CommandBlockEntity.h" @@ -197,6 +198,18 @@ void cNBTChunkSerializer::AddBeaconEntity(cBeaconEntity * a_Entity) +void cNBTChunkSerializer::AddBedEntity(cBedEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Bed"); + m_Writer.AddInt("color", a_Entity->GetColor()); + m_Writer.EndCompound(); +} + + + + + void cNBTChunkSerializer::AddBrewingstandEntity(cBrewingstandEntity * a_Brewingstand) { m_Writer.BeginCompound(""); @@ -970,6 +983,7 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) switch (a_Entity->GetBlockType()) { case E_BLOCK_BEACON: AddBeaconEntity (reinterpret_cast (a_Entity)); break; + case E_BLOCK_BED: AddBedEntity (reinterpret_cast (a_Entity)); break; case E_BLOCK_BREWING_STAND: AddBrewingstandEntity(reinterpret_cast(a_Entity)); break; case E_BLOCK_CHEST: AddChestEntity (reinterpret_cast (a_Entity), a_Entity->GetBlockType()); break; case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity(reinterpret_cast(a_Entity)); break; diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h index 08433c3ec..087d17277 100644 --- a/src/WorldStorage/NBTChunkSerializer.h +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -21,6 +21,7 @@ class cEntity; class cBlockEntity; class cBoat; class cBeaconEntity; +class cBedEntity; class cBrewingstandEntity; class cChestEntity; class cCommandBlockEntity; @@ -98,6 +99,7 @@ protected: // Block entities: void AddBasicTileEntity (cBlockEntity * a_Entity, const char * a_EntityTypeID); void AddBeaconEntity (cBeaconEntity * a_Entity); + void AddBedEntity (cBedEntity * a_Entity); void AddBrewingstandEntity(cBrewingstandEntity * a_Brewingstand); void AddChestEntity (cChestEntity * a_Entity, BLOCKTYPE a_ChestType); void AddDispenserEntity (cDispenserEntity * a_Entity); diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index d5ae388bb..8b8a0482e 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -19,6 +19,7 @@ #include "../Root.h" #include "../BlockEntities/BeaconEntity.h" +#include "../BlockEntities/BedEntity.h" #include "../BlockEntities/BrewingstandEntity.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/CommandBlockEntity.h" @@ -694,6 +695,7 @@ cBlockEntity * cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a { // Specific entity loaders: case E_BLOCK_BEACON: return LoadBeaconFromNBT (a_NBT, a_Tag, a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_BED: return LoadBedFromNBT (a_NBT, a_Tag, a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ); case E_BLOCK_BREWING_STAND: return LoadBrewingstandFromNBT(a_NBT, a_Tag, a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ); case E_BLOCK_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ); case E_BLOCK_COMMAND_BLOCK: return LoadCommandBlockFromNBT(a_NBT, a_Tag, a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ); @@ -976,6 +978,32 @@ cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagI +cBlockEntity * cWSSAnvil::LoadBedFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Check if the data has a proper type: + static const AStringVector expectedTypes({ "Bed", "minecraft:bed" }); + if (!CheckBlockEntityType(a_NBT, a_TagIdx, expectedTypes, a_BlockX, a_BlockY, a_BlockZ)) + { + return nullptr; + } + + // Use color red as default + short Color = E_META_WOOL_RED; + + int ColorIDx = a_NBT.FindChildByName(a_TagIdx, "color"); + if (ColorIDx >= 0) + { + Color = static_cast(a_NBT.GetInt(ColorIDx)); + } + + auto Bed = cpp14::make_unique(a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, m_World, Color); + return Bed.release(); +} + + + + + cBlockEntity * cWSSAnvil::LoadBrewingstandFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ) { // Check if the data has a proper type: diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index b8d2d8508..12acbbcff 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -156,6 +156,7 @@ protected: bool CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const AStringVector & a_ExpectedTypes, int a_BlockX, int a_BlockY, int a_BlockZ); cBlockEntity * LoadBeaconFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadBedFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ); cBlockEntity * LoadBrewingstandFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ); cBlockEntity * LoadChestFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ); cBlockEntity * LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ);