diff options
Diffstat (limited to 'src')
72 files changed, 1279 insertions, 489 deletions
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 1890dcfe5..aa6ee05b3 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -677,6 +677,7 @@ void cLuaState::Push(Vector3i * a_Vector) void cLuaState::Push(void * a_Ptr) { + UNUSED(a_Ptr); ASSERT(IsValid()); // Investigate the cause of this - what is the callstack? diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 05dc9717e..a5247bbe6 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -2563,9 +2563,13 @@ static int tolua_cBlockArea_SaveToSchematicString(lua_State * tolua_S) return 0; } - AString Data = cSchematicFileSerializer::SaveToSchematicString(*self); - L.Push(Data); - return 1; + AString Data; + if (cSchematicFileSerializer::SaveToSchematicString(*self, Data)) + { + L.Push(Data); + return 1; + } + return 0; } diff --git a/src/BlockEntities/DispenserEntity.cpp b/src/BlockEntities/DispenserEntity.cpp index 374f3d6e3..cbfbb1b6a 100644 --- a/src/BlockEntities/DispenserEntity.cpp +++ b/src/BlockEntities/DispenserEntity.cpp @@ -116,7 +116,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) { double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); - m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 4, 0); // 4 seconds fuse, no initial velocity + m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 80, 0); // 80 ticks fuse, no initial velocity m_Contents.ChangeSlotCount(a_SlotNum, -1); } break; diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp index 399efcd9b..d1ecfdf7e 100644 --- a/src/BlockInfo.cpp +++ b/src/BlockInfo.cpp @@ -42,8 +42,6 @@ cBlockInfo::~cBlockInfo() cBlockInfo & cBlockInfo::Get(BLOCKTYPE a_Type) { - ASSERT(a_Type < 256); - return ms_Info[a_Type]; } @@ -271,6 +269,11 @@ void cBlockInfo::Initialize(void) ms_Info[E_BLOCK_VINES ].m_IsSnowable = false; ms_Info[E_BLOCK_WALLSIGN ].m_IsSnowable = false; ms_Info[E_BLOCK_WATER ].m_IsSnowable = false; + ms_Info[E_BLOCK_RAIL ].m_IsSnowable = false; + ms_Info[E_BLOCK_ACTIVATOR_RAIL ].m_IsSnowable = false; + ms_Info[E_BLOCK_POWERED_RAIL ].m_IsSnowable = false; + ms_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSnowable = false; + ms_Info[E_BLOCK_COBWEB ].m_IsSnowable = false; // Blocks that don't drop without a special tool: @@ -309,6 +312,10 @@ void cBlockInfo::Initialize(void) ms_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_RequiresSpecialTool = true; ms_Info[E_BLOCK_STONE_SLAB ].m_RequiresSpecialTool = true; ms_Info[E_BLOCK_VINES ].m_RequiresSpecialTool = true; + ms_Info[E_BLOCK_FURNACE ].m_RequiresSpecialTool = true; + ms_Info[E_BLOCK_LIT_FURNACE ].m_RequiresSpecialTool = true; + ms_Info[E_BLOCK_ANVIL ].m_RequiresSpecialTool = true; + ms_Info[E_BLOCK_ENCHANTMENT_TABLE ].m_RequiresSpecialTool = true; // Nonsolid blocks: diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h index caec2b56f..6daa94730 100644 --- a/src/Blocks/BlockBed.h +++ b/src/Blocks/BlockBed.h @@ -4,6 +4,7 @@ #include "BlockHandler.h" #include "ChunkInterface.h" #include "WorldInterface.h" +#include "MetaRotater.h" #include "../Entities/Player.h" @@ -11,11 +12,11 @@ class cBlockBedHandler : - public cBlockHandler + public cMetaRotater<cBlockHandler, 0x3, 0x02, 0x03, 0x00, 0x01, true> { public: cBlockBedHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + : cMetaRotater<cBlockHandler, 0x3, 0x02, 0x03, 0x00, 0x01,true>(a_BlockType) { } diff --git a/src/Blocks/BlockButton.h b/src/Blocks/BlockButton.h index 2db9b7ec7..740cbe3c4 100644 --- a/src/Blocks/BlockButton.h +++ b/src/Blocks/BlockButton.h @@ -2,16 +2,17 @@ #include "BlockHandler.h" #include "Chunk.h" +#include "MetaRotater.h" class cBlockButtonHandler : - public cBlockHandler + public cMetaRotater<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true> { public: cBlockButtonHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + : cMetaRotater<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>(a_BlockType) { } diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h index 02ecc4346..30588d8fc 100644 --- a/src/Blocks/BlockChest.h +++ b/src/Blocks/BlockChest.h @@ -4,17 +4,18 @@ #include "BlockEntity.h" #include "../BlockArea.h" #include "../Entities/Player.h" +#include "MetaRotater.h" class cBlockChestHandler : - public cBlockEntityHandler + public cMetaRotater<cBlockEntityHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true> { public: cBlockChestHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) + : cMetaRotater<cBlockEntityHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>(a_BlockType) { } diff --git a/src/Blocks/BlockComparator.h b/src/Blocks/BlockComparator.h index cea35a864..e570ff302 100644 --- a/src/Blocks/BlockComparator.h +++ b/src/Blocks/BlockComparator.h @@ -3,17 +3,18 @@ #include "BlockHandler.h" #include "BlockRedstoneRepeater.h" +#include "MetaRotater.h" class cBlockComparatorHandler : - public cBlockHandler + public cMetaRotater<cBlockHandler, 0x03, 0x00, 0x01, 0x02, 0x03, true> { public: cBlockComparatorHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + : cMetaRotater<cBlockHandler, 0x03, 0x00, 0x01, 0x02, 0x03, true>(a_BlockType) { } diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp index 0acf04852..4e38ef334 100644 --- a/src/Blocks/BlockDoor.cpp +++ b/src/Blocks/BlockDoor.cpp @@ -9,7 +9,7 @@ cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + : super(a_BlockType) { } diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h index 68be2c658..981774c17 100644 --- a/src/Blocks/BlockDoor.h +++ b/src/Blocks/BlockDoor.h @@ -4,13 +4,15 @@ #include "BlockHandler.h" #include "../Entities/Player.h" #include "Chunk.h" +#include "MetaRotater.h" class cBlockDoorHandler : - public cBlockHandler + public cMetaRotater<cBlockHandler, 0x03, 0x01, 0x02, 0x03, 0x00, true> { + typedef cMetaRotater<cBlockHandler, 0x03, 0x01, 0x02, 0x03, 0x00, true> super; public: cBlockDoorHandler(BLOCKTYPE a_BlockType); @@ -168,6 +170,60 @@ public: } + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + if (a_Meta & 0x08) + { + return a_Meta; + } + else + { + return super::MetaRotateCCW(a_Meta); + } + } + + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + if (a_Meta & 0x08) + { + return a_Meta; + } + else + { + return super::MetaRotateCW(a_Meta); + } + } + + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + if (a_Meta & 0x08) + { + return a_Meta; + } + else + { + return super::MetaMirrorXY(a_Meta); + } + } + + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + if (a_Meta & 0x08) + { + return a_Meta; + } + else + { + return super::MetaMirrorYZ(a_Meta); + } + } + } ; diff --git a/src/Blocks/BlockDropSpenser.h b/src/Blocks/BlockDropSpenser.h index 30d347ec9..7e0ad0e55 100644 --- a/src/Blocks/BlockDropSpenser.h +++ b/src/Blocks/BlockDropSpenser.h @@ -6,17 +6,18 @@ #pragma once #include "../Piston.h" +#include "MetaRotater.h" class cBlockDropSpenserHandler : - public cBlockEntityHandler + public cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04> { public: cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) : - cBlockEntityHandler(a_BlockType) + cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>(a_BlockType) { } @@ -34,6 +35,20 @@ public: a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch()); return true; } + + virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override + { + // Bit 0x08 is a flag. Lowest three bits are position. 0x08 == 1000 + NIBBLETYPE OtherMeta = a_Meta & 0x08; + // Mirrors defined by by a table. (Source, mincraft.gamepedia.com) 0x07 == 0111 + switch (a_Meta & 0x07) + { + case 0x00: return 0x01 + OtherMeta; // Down -> Up + case 0x01: return 0x00 + OtherMeta; // Up -> Down + } + // Not Facing Up or Down; No change. + return a_Meta; + } } ; diff --git a/src/Blocks/BlockEnderchest.h b/src/Blocks/BlockEnderchest.h index b4b0b995d..97cf484fb 100644 --- a/src/Blocks/BlockEnderchest.h +++ b/src/Blocks/BlockEnderchest.h @@ -2,17 +2,17 @@ #pragma once #include "BlockEntity.h" - +#include "MetaRotater.h" class cBlockEnderchestHandler : - public cBlockEntityHandler + public cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04> { public: cBlockEnderchestHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) + : cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>(a_BlockType) { } diff --git a/src/Blocks/BlockFenceGate.h b/src/Blocks/BlockFenceGate.h index 1abe1eecf..e3162bbd6 100644 --- a/src/Blocks/BlockFenceGate.h +++ b/src/Blocks/BlockFenceGate.h @@ -2,17 +2,17 @@ #pragma once #include "BlockHandler.h" - +#include "MetaRotater.h" class cBlockFenceGateHandler : - public cBlockHandler + public cMetaRotater<cBlockHandler, 0x03, 0x02, 0x03, 0x00, 0x01, true> { public: cBlockFenceGateHandler(BLOCKTYPE a_BlockType) : - cBlockHandler(a_BlockType) + cMetaRotater<cBlockHandler, 0x03, 0x02, 0x03, 0x00, 0x01, true>(a_BlockType) { } diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index c5165986c..aa97b2ca9 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -146,6 +146,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType); case E_BLOCK_NETHER_WART: return new cBlockNetherWartHandler (a_BlockType); + case E_BLOCK_NETHER_QUARTZ_ORE: return new cBlockOreHandler (a_BlockType); case E_BLOCK_NEW_LEAVES: return new cBlockNewLeavesHandler (a_BlockType); case E_BLOCK_NEW_LOG: return new cBlockSidewaysHandler (a_BlockType); case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType); diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h index 50c2e2ad5..3a3efb3cc 100644 --- a/src/Blocks/BlockHandler.h +++ b/src/Blocks/BlockHandler.h @@ -23,6 +23,8 @@ class cBlockHandler { public: cBlockHandler(BLOCKTYPE a_BlockType); + + 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! diff --git a/src/Blocks/BlockStairs.h b/src/Blocks/BlockStairs.h index c1887bc46..ea8405597 100644 --- a/src/Blocks/BlockStairs.h +++ b/src/Blocks/BlockStairs.h @@ -2,17 +2,17 @@ #pragma once #include "BlockHandler.h" - +#include "MetaRotater.h" class cBlockStairsHandler : - public cBlockHandler + public cMetaRotater<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true> { public: cBlockStairsHandler(BLOCKTYPE a_BlockType) : - cBlockHandler(a_BlockType) + cMetaRotater<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>(a_BlockType) { } @@ -25,6 +25,14 @@ public: BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ) override { + UNUSED(a_ChunkInterface); + UNUSED(a_BlockX); + UNUSED(a_BlockY); + UNUSED(a_BlockZ); + UNUSED(a_CursorX); + UNUSED(a_CursorY); + UNUSED(a_CursorZ); + UNUSED(a_BlockMeta); a_BlockType = m_BlockType; a_BlockMeta = RotationToMetaData(a_Player->GetYaw()); switch (a_BlockFace) @@ -96,54 +104,6 @@ public: } - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x03; // East -> North - case 0x01: return TopBits | 0x02; // West -> South - case 0x02: return TopBits | 0x00; // South -> East - case 0x03: return TopBits | 0x01; // North -> West - } - // Not reachable, but to avoid a compiler warning: - return 0; - } - - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x02; // East -> South - case 0x01: return TopBits | 0x03; // West -> North - case 0x02: return TopBits | 0x01; // South -> West - case 0x03: return TopBits | 0x00; // North -> East - } - // Not reachable, but to avoid a compiler warning: - return 0; - } - - - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x00; // East -> East - case 0x01: return TopBits | 0x01; // West -> West - case 0x02: return TopBits | 0x03; // South -> North - case 0x03: return TopBits | 0x02; // North -> South - } - // Not reachable, but to avoid a compiler warning: - return 0; - } - - virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override { // Toggle bit 3: @@ -151,20 +111,6 @@ public: } - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x01; // East -> West - case 0x01: return TopBits | 0x00; // West -> East - case 0x02: return TopBits | 0x02; // South -> South - case 0x03: return TopBits | 0x03; // North -> North - } - // Not reachable, but to avoid a compiler warning: - return 0; - } } ; diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h index 84bbb37ec..d32c77629 100644 --- a/src/Blocks/BlockTorch.h +++ b/src/Blocks/BlockTorch.h @@ -2,17 +2,17 @@ #include "BlockHandler.h" #include "../Chunk.h" - +#include "MetaRotater.h" class cBlockTorchHandler : - public cBlockHandler + public cMetaRotater<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2> { public: cBlockTorchHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + : cMetaRotater<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2>(a_BlockType) { } @@ -185,67 +185,6 @@ public: { return "step.wood"; } - - - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x01: return TopBits | 0x04; // East -> North - case 0x02: return TopBits | 0x03; // West -> South - case 0x03: return TopBits | 0x01; // South -> East - case 0x04: return TopBits | 0x02; // North -> West - default: return a_Meta; // Floor -> Floor - } - } - - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x01: return TopBits | 0x03; // East -> South - case 0x02: return TopBits | 0x04; // West -> North - case 0x03: return TopBits | 0x02; // South -> West - case 0x04: return TopBits | 0x01; // North -> East - default: return a_Meta; // Floor -> Floor - } - } - - - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x03: return TopBits | 0x04; // South -> North - case 0x04: return TopBits | 0x03; // North -> South - default: return a_Meta; // Keep the rest - } - } - - - // Mirroring around the XZ plane doesn't make sense for floor torches, - // the others stay the same, so let's keep all the metas the same. - // The base class does tht for us, no need to override MetaMirrorXZ() - - - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x01: return TopBits | 0x02; // East -> West - case 0x02: return TopBits | 0x01; // West -> East - default: return a_Meta; // Keep the rest - } - } } ; diff --git a/src/Blocks/BlockVine.h b/src/Blocks/BlockVine.h index d8c114284..708583e70 100644 --- a/src/Blocks/BlockVine.h +++ b/src/Blocks/BlockVine.h @@ -1,8 +1,7 @@ - #pragma once #include "BlockHandler.h" - +#include "MetaRotater.h" @@ -24,6 +23,10 @@ public: BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ) override { + UNUSED(a_Player); + UNUSED(a_CursorX); + UNUSED(a_CursorY); + UNUSED(a_CursorZ); // TODO: Disallow placement where the vine doesn't attach to something properly BLOCKTYPE BlockType = 0; NIBBLETYPE BlockMeta; @@ -162,11 +165,17 @@ public: return false; } - virtual void OnUpdate(cWorld * a_World, int X, int Y, int Z) + virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) { - if (a_World->GetBlock(X, Y - 1, Z) == E_BLOCK_AIR) + UNUSED(a_ChunkInterface); + UNUSED(a_WorldInterface); + UNUSED(a_BlockPluginInterface); + + BLOCKTYPE Block; + a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY - 1, a_RelZ, Block); + if (Block == E_BLOCK_AIR) { - a_World->SetBlock(X, Y - 1, Z, E_BLOCK_VINES, a_World->GetBlockMeta(X, Y, Z)); + a_Chunk.UnboundedRelSetBlock(a_RelX, a_RelY - 1, a_RelZ, E_BLOCK_VINES, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); } } @@ -180,8 +189,8 @@ public: { return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left } - - + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override { // Bits 2 and 4 stay, bits 1 and 3 swap @@ -194,6 +203,7 @@ public: // Bits 1 and 3 stay, bits 2 and 4 swap return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2)); } + } ; diff --git a/src/Blocks/MetaRotater.h b/src/Blocks/MetaRotater.h new file mode 100644 index 000000000..dde88e6db --- /dev/null +++ b/src/Blocks/MetaRotater.h @@ -0,0 +1,120 @@ +// MetaRotater.h + +// Provides a mixin for rotations and reflections + +#pragma once + +// MSVC generates warnings for the templated AssertIfNotMatched parameter conditions, so disable it: +#ifdef _MSC_VER + #pragma warning(disable: 4127) // Conditional expression is constant +#endif + + + + + +/* +Provides a mixin for rotations and reflections following the standard pattern of apply mask then use case. + +Usage: +Inherit from this class providing your base class as Base, the BitMask for the direction bits in bitmask and the masked value for the directions in North, East, South, West. There is also an aptional parameter AssertIfNotMatched. Set this if it is invalid for a block to exist in any other state. +*/ + +template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched = false> +class cMetaRotater : public Base +{ +public: + + cMetaRotater(BLOCKTYPE a_BlockType) : + Base(a_BlockType) + {} + + virtual ~cMetaRotater() {} + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override; + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override; + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override; + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override; +}; + + + + + +template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched> +NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCW(NIBBLETYPE a_Meta) +{ + NIBBLETYPE OtherMeta = a_Meta & (~BitMask); + switch (a_Meta & BitMask) + { + case South: return West | OtherMeta; + case West: return North | OtherMeta; + case North: return East | OtherMeta; + case East: return South | OtherMeta; + } + if (AssertIfNotMatched) + { + ASSERT(!"Invalid Meta value"); + } + return a_Meta; +} + + + + + +template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched> +NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCCW(NIBBLETYPE a_Meta) +{ + NIBBLETYPE OtherMeta = a_Meta & (~BitMask); + switch (a_Meta & BitMask) + { + case South: return East | OtherMeta; + case East: return North | OtherMeta; + case North: return West | OtherMeta; + case West: return South | OtherMeta; + } + if (AssertIfNotMatched) + { + ASSERT(!"Invalid Meta value"); + } + return a_Meta; +} + + + + + +template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched> +NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorXY(NIBBLETYPE a_Meta) +{ + NIBBLETYPE OtherMeta = a_Meta & (~BitMask); + switch (a_Meta & BitMask) + { + case South: return North | OtherMeta; + case North: return South | OtherMeta; + } + // Not Facing North or South; No change. + return a_Meta; +} + + + + + +template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched> +NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorYZ(NIBBLETYPE a_Meta) +{ + NIBBLETYPE OtherMeta = a_Meta & (~BitMask); + switch (a_Meta & BitMask) + { + case West: return East | OtherMeta; + case East: return West | OtherMeta; + } + // Not Facing East or West; No change. + return a_Meta; +} + + + + diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index d2d3beb97..96a135562 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -44,10 +44,10 @@ -#if 0 +#ifdef SELF_TEST /// Self-test of the VarInt-reading and writing code -class cByteBufferSelfTest +static class cByteBufferSelfTest { public: cByteBufferSelfTest(void) @@ -86,7 +86,7 @@ public: cByteBuffer buf(3); for (int i = 0; i < 1000; i++) { - int FreeSpace = buf.GetFreeSpace(); + size_t FreeSpace = buf.GetFreeSpace(); assert(buf.GetReadableSpace() == 0); assert(FreeSpace > 0); assert(buf.Write("a", 1)); @@ -171,21 +171,22 @@ cByteBuffer::~cByteBuffer() -bool cByteBuffer::Write(const char * a_Bytes, int a_Count) +bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count) { CHECK_THREAD; CheckValid(); // Store the current free space for a check after writing: - int CurFreeSpace = GetFreeSpace(); - int CurReadableSpace = GetReadableSpace(); - int WrittenBytes = 0; + size_t CurFreeSpace = GetFreeSpace(); + size_t CurReadableSpace = GetReadableSpace(); + size_t WrittenBytes = 0; if (CurFreeSpace < a_Count) { return false; } - int TillEnd = m_BufferSize - m_WritePos; + ASSERT(m_BufferSize >= m_WritePos); + size_t TillEnd = m_BufferSize - m_WritePos; if (TillEnd <= a_Count) { // Need to wrap around the ringbuffer end @@ -216,16 +217,20 @@ bool cByteBuffer::Write(const char * a_Bytes, int a_Count) -int cByteBuffer::GetFreeSpace(void) const +size_t cByteBuffer::GetFreeSpace(void) const { CHECK_THREAD; CheckValid(); if (m_WritePos >= m_DataStart) { // Wrap around the buffer end: + ASSERT(m_BufferSize >= m_WritePos); + ASSERT((m_BufferSize - m_WritePos + m_DataStart) >= 1); return m_BufferSize - m_WritePos + m_DataStart - 1; } // Single free space partition: + ASSERT(m_BufferSize >= m_WritePos); + ASSERT(m_BufferSize - m_WritePos >= 1); return m_DataStart - m_WritePos - 1; } @@ -234,10 +239,12 @@ int cByteBuffer::GetFreeSpace(void) const /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() -int cByteBuffer::GetUsedSpace(void) const +size_t cByteBuffer::GetUsedSpace(void) const { CHECK_THREAD; CheckValid(); + ASSERT(m_BufferSize >= GetFreeSpace()); + ASSERT((m_BufferSize - GetFreeSpace()) >= 1); return m_BufferSize - GetFreeSpace() - 1; } @@ -246,16 +253,18 @@ int cByteBuffer::GetUsedSpace(void) const /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) -int cByteBuffer::GetReadableSpace(void) const +size_t cByteBuffer::GetReadableSpace(void) const { CHECK_THREAD; CheckValid(); if (m_ReadPos > m_WritePos) { // Wrap around the buffer end: + ASSERT(m_BufferSize >= m_ReadPos); return m_BufferSize - m_ReadPos + m_WritePos; } // Single readable space partition: + ASSERT(m_WritePos >= m_ReadPos); return m_WritePos - m_ReadPos ; } @@ -263,7 +272,7 @@ int cByteBuffer::GetReadableSpace(void) const -bool cByteBuffer::CanReadBytes(int a_Count) const +bool cByteBuffer::CanReadBytes(size_t a_Count) const { CHECK_THREAD; CheckValid(); @@ -274,7 +283,7 @@ bool cByteBuffer::CanReadBytes(int a_Count) const -bool cByteBuffer::CanWriteBytes(int a_Count) const +bool cByteBuffer::CanWriteBytes(size_t a_Count) const { CHECK_THREAD; CheckValid(); @@ -650,15 +659,14 @@ bool cByteBuffer::WriteLEInt(int a_Value) -bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count) +bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count) { CHECK_THREAD; CheckValid(); - ASSERT(a_Count >= 0); NEEDBYTES(a_Count); char * Dst = (char *)a_Buffer; // So that we can do byte math - int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; - ASSERT(BytesToEndOfBuffer >= 0); // Sanity check + ASSERT(m_BufferSize >= m_ReadPos); + size_t BytesToEndOfBuffer = m_BufferSize - m_ReadPos; if (BytesToEndOfBuffer <= a_Count) { // Reading across the ringbuffer end, read the first part and adjust parameters: @@ -684,14 +692,14 @@ bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count) -bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count) +bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count) { CHECK_THREAD; CheckValid(); - ASSERT(a_Count >= 0); PUTBYTES(a_Count); char * Src = (char *)a_Buffer; // So that we can do byte math - int BytesToEndOfBuffer = m_BufferSize - m_WritePos; + ASSERT(m_BufferSize >= m_ReadPos); + size_t BytesToEndOfBuffer = m_BufferSize - m_WritePos; if (BytesToEndOfBuffer <= a_Count) { // Reading across the ringbuffer end, read the first part and adjust parameters: @@ -714,22 +722,22 @@ bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count) -bool cByteBuffer::ReadString(AString & a_String, int a_Count) +bool cByteBuffer::ReadString(AString & a_String, size_t a_Count) { CHECK_THREAD; CheckValid(); - ASSERT(a_Count >= 0); NEEDBYTES(a_Count); a_String.clear(); a_String.reserve(a_Count); - int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; - ASSERT(BytesToEndOfBuffer >= 0); // Sanity check + ASSERT(m_BufferSize >= m_ReadPos); + size_t BytesToEndOfBuffer = m_BufferSize - m_ReadPos; if (BytesToEndOfBuffer <= a_Count) { // Reading across the ringbuffer end, read the first part and adjust parameters: if (BytesToEndOfBuffer > 0) { a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer); + ASSERT(a_Count >= BytesToEndOfBuffer); a_Count -= BytesToEndOfBuffer; } m_ReadPos = 0; @@ -767,11 +775,10 @@ bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars) -bool cByteBuffer::SkipRead(int a_Count) +bool cByteBuffer::SkipRead(size_t a_Count) { CHECK_THREAD; CheckValid(); - ASSERT(a_Count >= 0); if (!CanReadBytes(a_Count)) { return false; @@ -809,6 +816,7 @@ bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes) size_t num = (a_NumBytes > sizeof(buf)) ? sizeof(buf) : a_NumBytes; VERIFY(ReadBuf(buf, num)); VERIFY(a_Dst.Write(buf, num)); + ASSERT(a_NumBytes >= num); a_NumBytes -= num; } return true; @@ -846,13 +854,15 @@ void cByteBuffer::ReadAgain(AString & a_Out) // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party CHECK_THREAD; CheckValid(); - int DataStart = m_DataStart; + size_t DataStart = m_DataStart; if (m_ReadPos < m_DataStart) { // Across the ringbuffer end, read the first part and adjust next part's start: + ASSERT(m_BufferSize >= m_DataStart); a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart); DataStart = 0; } + ASSERT(m_ReadPos >= DataStart); a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart); } @@ -860,7 +870,7 @@ void cByteBuffer::ReadAgain(AString & a_Out) -void cByteBuffer::AdvanceReadPos(int a_Count) +void cByteBuffer::AdvanceReadPos(size_t a_Count) { CHECK_THREAD; CheckValid(); diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h index cbce119b1..ed2e10a55 100644 --- a/src/ByteBuffer.h +++ b/src/ByteBuffer.h @@ -31,25 +31,25 @@ public: ~cByteBuffer(); /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not - bool Write(const char * a_Bytes, int a_Count); + bool Write(const char * a_Bytes, size_t a_Count); /// Returns the number of bytes that can be successfully written to the ringbuffer - int GetFreeSpace(void) const; + size_t GetFreeSpace(void) const; /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() - int GetUsedSpace(void) const; + size_t GetUsedSpace(void) const; /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) - int GetReadableSpace(void) const; + size_t GetReadableSpace(void) const; /// Returns the current data start index. For debugging purposes. int GetDataStart(void) const { return m_DataStart; } /// Returns true if the specified amount of bytes are available for reading - bool CanReadBytes(int a_Count) const; + bool CanReadBytes(size_t a_Count) const; /// Returns true if the specified amount of bytes are available for writing - bool CanWriteBytes(int a_Count) const; + bool CanWriteBytes(size_t a_Count) const; // Read the specified datatype and advance the read pointer; return true if successfully read: bool ReadChar (char & a_Value); @@ -92,19 +92,19 @@ public: bool WriteLEInt (int a_Value); /// Reads a_Count bytes into a_Buffer; returns true if successful - bool ReadBuf(void * a_Buffer, int a_Count); + bool ReadBuf(void * a_Buffer, size_t a_Count); /// Writes a_Count bytes into a_Buffer; returns true if successful - bool WriteBuf(const void * a_Buffer, int a_Count); + bool WriteBuf(const void * a_Buffer, size_t a_Count); /// Reads a_Count bytes into a_String; returns true if successful - bool ReadString(AString & a_String, int a_Count); + bool ReadString(AString & a_String, size_t a_Count); /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String bool ReadUTF16String(AString & a_String, int a_NumChars); /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer - bool SkipRead(int a_Count); + bool SkipRead(size_t a_Count); /// Reads all available data into a_Data void ReadAll(AString & a_Data); @@ -126,18 +126,18 @@ public: protected: char * m_Buffer; - int m_BufferSize; // Total size of the ringbuffer + size_t m_BufferSize; // Total size of the ringbuffer #ifdef _DEBUG volatile unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker #endif // _DEBUG - int m_DataStart; // Where the data starts in the ringbuffer - int m_WritePos; // Where the data ends in the ringbuffer - int m_ReadPos; // Where the next read will start in the ringbuffer + size_t m_DataStart; // Where the data starts in the ringbuffer + size_t m_WritePos; // Where the data ends in the ringbuffer + size_t m_ReadPos; // Where the next read will start in the ringbuffer /// Advances the m_ReadPos by a_Count bytes - void AdvanceReadPos(int a_Count); + void AdvanceReadPos(size_t a_Count); } ; diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 870568cdf..94c9f5f71 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -52,6 +52,9 @@ /** Maximum number of explosions to send this tick, server will start dropping if exceeded */ #define MAX_EXPLOSIONS_PER_TICK 20 +/** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */ +#define MAX_BLOCK_CHANGE_INTERACTIONS 20 + /** How many ticks before the socket is closed after the client is destroyed (#31) */ static const int TICKS_BEFORE_CLOSE = 20; @@ -96,8 +99,8 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) : m_ShouldCheckDownloaded(false), m_NumExplosionsThisTick(0), m_UniqueID(0), - m_Locale("en_GB"), - m_HasSentPlayerChunk(false) + m_HasSentPlayerChunk(false), + m_Locale("en_GB") { m_Protocol = new cProtocolRecognizer(this); @@ -555,12 +558,25 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString } else if (a_Channel == "REGISTER") { + if (HasPluginChannel(a_Channel)) + { + SendPluginMessage("UNREGISTER", a_Channel); + return; // Can't register again if already taken - kinda defeats the point of plugin messaging! + } + RegisterPluginChannels(BreakApartPluginChannels(a_Message)); } else if (a_Channel == "UNREGISTER") { UnregisterPluginChannels(BreakApartPluginChannels(a_Message)); } + else if (!HasPluginChannel(a_Channel)) + { + // Ignore if client sent something but didn't register the channel first + LOGD("Player %s sent a plugin message on channel \"%s\", but didn't REGISTER it first", GetUsername().c_str(), a_Channel.c_str()); + SendPluginMessage("UNREGISTER", a_Channel); + return; + } cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message); } @@ -687,6 +703,14 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status ); + m_NumBlockChangeInteractionsThisTick++; + + if (!CheckBlockInteractionsRate()) + { + Kick("Too many blocks were destroyed per unit time - hacked client?"); + return; + } + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status)) { @@ -694,12 +718,6 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); return; } - - if (!CheckBlockInteractionsRate()) - { - // Too many interactions per second, simply ignore. Probably a hacked client, so don't even send bak the block - return; - } switch (a_Status) { @@ -931,7 +949,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType); BlockHandler->OnCancelRightClick(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - if (a_BlockFace > -1) + if (a_BlockFace != BLOCK_FACE_NONE) { AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); @@ -942,7 +960,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e if (!CheckBlockInteractionsRate()) { - LOGD("Too many block interactions, aborting placement"); + Kick("Too many blocks were placed/interacted with per unit time - hacked client?"); return; } @@ -1453,7 +1471,7 @@ bool cClientHandle::HandleHandshake(const AString & a_Username) -void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID) +void cClientHandle::HandleEntityCrouch(int a_EntityID, bool a_IsCrouching) { if (a_EntityID != m_Player->GetUniqueID()) { @@ -1461,35 +1479,37 @@ void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID) return; } - switch (a_ActionID) + m_Player->SetCrouch(a_IsCrouching); +} + + + + + +void cClientHandle::HandleEntityLeaveBed(int a_EntityID) +{ + if (a_EntityID != m_Player->GetUniqueID()) { - case 1: // Crouch - { - m_Player->SetCrouch(true); - break; - } - case 2: // Uncrouch - { - m_Player->SetCrouch(false); - break; - } - case 3: // Leave bed - { - m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, 2); - break; - } - case 4: // Start sprinting - { - m_Player->SetSprint(true); - break; - } - case 5: // Stop sprinting - { - m_Player->SetSprint(false); - SendPlayerMaxSpeed(); - break; - } + // We should only receive entity actions from the entity that is performing the action + return; + } + + m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, 2); +} + + + + + +void cClientHandle::HandleEntitySprinting(int a_EntityID, bool a_IsSprinting) +{ + if (a_EntityID != m_Player->GetUniqueID()) + { + // We should only receive entity actions from the entity that is performing the action + return; } + + m_Player->SetSprint(a_IsSprinting); } @@ -1619,28 +1639,12 @@ bool cClientHandle::CheckBlockInteractionsRate(void) { ASSERT(m_Player != NULL); ASSERT(m_Player->GetWorld() != NULL); - /* - // TODO: _X 2012_11_01: This needs a total re-thinking and rewriting - int LastActionCnt = m_Player->GetLastBlockActionCnt(); - if ((m_Player->GetWorld()->GetTime() - m_Player->GetLastBlockActionTime()) < 0.1) - { - // Limit the number of block interactions per tick - m_Player->SetLastBlockActionTime(); //Player tried to interact with a block. Reset last block interation time. - m_Player->SetLastBlockActionCnt(LastActionCnt + 1); - if (m_Player->GetLastBlockActionCnt() > MAXBLOCKCHANGEINTERACTIONS) - { - // Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick - LOGWARN("Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked.", m_Username.c_str()); - Kick("You're a baaaaaad boy!"); - return false; - } - } - else + + if (m_NumBlockChangeInteractionsThisTick > MAX_BLOCK_CHANGE_INTERACTIONS) { - m_Player->SetLastBlockActionCnt(0); // Reset count - m_Player->SetLastBlockActionTime(); // Player tried to interact with a block. Reset last block interation time. + return false; } - */ + return true; } @@ -1713,8 +1717,9 @@ void cClientHandle::Tick(float a_Dt) } } - // Reset explosion counter: + // Reset explosion & block change counters: m_NumExplosionsThisTick = 0; + m_NumBlockChangeInteractionsThisTick = 0; } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 194533402..72b1c7d09 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -46,7 +46,6 @@ class cClientHandle : // tolua_export public cSocketThreads::cCallback { // tolua_export public: - static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick #if defined(ANDROID_NDK) static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini) @@ -188,7 +187,9 @@ public: void HandleChat (const AString & a_Message); void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem); void HandleDisconnect (const AString & a_Reason); - void HandleEntityAction (int a_EntityID, char a_ActionID); + void HandleEntityCrouch (int a_EntityID, bool a_IsCrouching); + void HandleEntityLeaveBed (int a_EntityID); + void HandleEntitySprinting (int a_EntityID, bool a_IsSprinting); /** Called when the protocol handshake has been received (for protocol versions that support it; otherwise the first instant when a username is received). @@ -319,6 +320,9 @@ private: /** Number of explosions sent this tick */ int m_NumExplosionsThisTick; + + /** Number of place or break interactions this tick */ + int m_NumBlockChangeInteractionsThisTick; static int s_ClientCount; int m_UniqueID; diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp index fb1a10cca..868182394 100644 --- a/src/CraftingRecipes.cpp +++ b/src/CraftingRecipes.cpp @@ -1,4 +1,4 @@ - + // CraftingRecipes.cpp // Interfaces to the cCraftingRecipes class representing the storage of crafting recipes @@ -762,9 +762,94 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti Recipe->m_Ingredients.push_back(*itrS); } Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end()); + + // We use Recipe instead of a_Recipe because we want the wildcard ingredients' slot numbers as well, which was just added previously + HandleFireworks(a_CraftingGrid, Recipe.get(), a_GridStride, a_OffsetX, a_OffsetY); + return Recipe.release(); } + +void cCraftingRecipes::HandleFireworks(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_OffsetX, int a_OffsetY) +{ + // TODO: add support for more than one dye in the recipe + // A manual and temporary solution (listing everything) is in crafting.txt for fade colours, but a programmatic solutions needs to be done for everything else + + if (a_Recipe->m_Result.m_ItemType == E_ITEM_FIREWORK_ROCKET) + { + for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr) + { + switch (itr->m_Item.m_ItemType) + { + case E_ITEM_FIREWORK_STAR: + { + // Result was a rocket, found a star - copy star data to rocket data + int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY); + a_Recipe->m_Result.m_FireworkItem.CopyFrom(a_CraftingGrid[GridID].m_FireworkItem); + break; + } + case E_ITEM_GUNPOWDER: + { + // Gunpowder - increase flight time + a_Recipe->m_Result.m_FireworkItem.m_FlightTimeInTicks += 20; + break; + } + case E_ITEM_PAPER: break; + default: LOG("Unexpected item in firework rocket a_Recipe, was the crafting file fireworks section changed?"); break; + } + } + } + else if (a_Recipe->m_Result.m_ItemType == E_ITEM_FIREWORK_STAR) + { + std::vector<int> DyeColours; + bool FoundStar = false; + + for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr) + { + switch (itr->m_Item.m_ItemType) + { + case E_ITEM_FIREWORK_STAR: + { + // Result was star, found another star - probably adding fade colours, but copy data over anyhow + FoundStar = true; + int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY); + a_Recipe->m_Result.m_FireworkItem.CopyFrom(a_CraftingGrid[GridID].m_FireworkItem); + break; + } + case E_ITEM_DYE: + { + int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY); + DyeColours.push_back(cFireworkItem::GetVanillaColourCodeFromDye(a_CraftingGrid[GridID].m_ItemDamage)); + break; + } + case E_ITEM_GUNPOWDER: break; + case E_ITEM_DIAMOND: a_Recipe->m_Result.m_FireworkItem.m_HasTrail = true; break; + case E_ITEM_GLOWSTONE_DUST: a_Recipe->m_Result.m_FireworkItem.m_HasFlicker = true; break; + + case E_ITEM_FIRE_CHARGE: a_Recipe->m_Result.m_FireworkItem.m_Type = 1; break; + case E_ITEM_GOLD_NUGGET: a_Recipe->m_Result.m_FireworkItem.m_Type = 2; break; + case E_ITEM_FEATHER: a_Recipe->m_Result.m_FireworkItem.m_Type = 4; break; + case E_ITEM_HEAD: a_Recipe->m_Result.m_FireworkItem.m_Type = 3; break; + default: LOG("Unexpected item in firework star a_Recipe, was the crafting file fireworks section changed?"); break; // ermahgerd BARD ardmins + } + } + + if (FoundStar && (!DyeColours.empty())) + { + // Found a star and a dye? Fade colours. + a_Recipe->m_Result.m_FireworkItem.m_FadeColours = DyeColours; + } + else if (!DyeColours.empty()) + { + // Only dye? Normal colours. + a_Recipe->m_Result.m_FireworkItem.m_Colours = DyeColours; + } + } +} + + + + diff --git a/src/CraftingRecipes.h b/src/CraftingRecipes.h index 9d92cbfab..90e41eddc 100644 --- a/src/CraftingRecipes.h +++ b/src/CraftingRecipes.h @@ -165,6 +165,9 @@ protected: /// Checks if the grid matches the specified recipe, offset by the specified offsets. Returns a matched cRecipe * if so, or NULL if not matching. Caller must delete the return value! cRecipe * MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY); + + /** Searches for anything firework related, and does the data setting if appropriate */ + void HandleFireworks(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_OffsetX, int a_OffsetY); } ; diff --git a/src/Entities/Floater.h b/src/Entities/Floater.h index 865d6dc50..f3b51d77b 100644 --- a/src/Entities/Floater.h +++ b/src/Entities/Floater.h @@ -11,7 +11,7 @@ class cFloater : public cEntity { - typedef cFloater super; + typedef cEntity super; public: //tolua_end diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index f52a7b6d9..7f38aa35a 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -1031,9 +1031,9 @@ cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) : -void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item) +void cMinecartWithChest::SetSlot(size_t a_Idx, const cItem & a_Item) { - ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items))); + ASSERT(a_Idx < ARRAYCOUNT(m_Items)); m_Items[a_Idx] = a_Item; } diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index 073e78953..ebdb576e0 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.h @@ -122,7 +122,7 @@ public: const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; } cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; } - void SetSlot(int a_Idx, const cItem & a_Item); + void SetSlot(size_t a_Idx, const cItem & a_Item); protected: @@ -193,4 +193,4 @@ public: CLASS_PROTODEF(cMinecartWithHopper); cMinecartWithHopper(double a_X, double a_Y, double a_Z); -} ;
\ No newline at end of file +} ; diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 42ee14cf3..ccdd151f3 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -10,18 +10,11 @@ #include "../BlockEntities/BlockEntity.h" #include "../GroupManager.h" #include "../Group.h" -#include "../ChatColor.h" -#include "../Item.h" -#include "../Tracer.h" #include "../Root.h" #include "../OSSupport/Timer.h" -#include "../MersenneTwister.h" #include "../Chunk.h" #include "../Items/ItemHandler.h" -#include "../Vector3d.h" -#include "../Vector3f.h" - #include "inifile/iniFile.h" #include "json/json.h" @@ -45,10 +38,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) , m_Inventory(*this) , m_CurrentWindow(NULL) , m_InventoryWindow(NULL) - , m_TimeLastPickupCheck(0.f) , m_Color('-') - , m_LastBlockActionTime(0) - , m_LastBlockActionCnt(0) , m_GameMode(eGameMode_NotSet) , m_IP("") , m_ClientHandle(a_Client) @@ -86,7 +76,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) m_LastPlayerListTime = t1.GetNowTime(); m_TimeLastTeleportPacket = 0; - m_TimeLastPickupCheck = 0; m_PlayerName = a_PlayerName; m_bDirtyPosition = true; // So chunks are streamed to player at spawn @@ -1047,27 +1036,6 @@ void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse) -void cPlayer::SetLastBlockActionTime() -{ - if (m_World != NULL) - { - m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f; - } -} - - - - - -void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) -{ - m_LastBlockActionCnt = a_LastBlockActionCnt; -} - - - - - void cPlayer::SetGameMode(eGameMode a_GameMode) { if ((a_GameMode < gmMin) || (a_GameMode >= gmMax)) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index a795bb9eb..f9404dfaf 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -50,7 +50,7 @@ public: /// Returns the curently equipped weapon; empty item if none virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } - /// Returns the currently equipped helmet; empty item if nonte + /// Returns the currently equipped helmet; empty item if none virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); } /// Returns the currently equipped chestplate; empty item if none @@ -165,11 +165,6 @@ public: // tolua_end void SetIP(const AString & a_IP); - - float GetLastBlockActionTime() { return m_LastBlockActionTime; } - int GetLastBlockActionCnt() { return m_LastBlockActionCnt; } - void SetLastBlockActionCnt( int ); - void SetLastBlockActionTime(); // Sets the current gamemode, doesn't check validity, doesn't send update packets to client void LoginSetGameMode(eGameMode a_GameMode); @@ -416,12 +411,8 @@ protected: cWindow * m_CurrentWindow; cWindow * m_InventoryWindow; - float m_TimeLastPickupCheck; - char m_Color; - float m_LastBlockActionTime; - int m_LastBlockActionCnt; eGameMode m_GameMode; AString m_IP; diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 03bc0c99d..12d2025ec 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -214,7 +214,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve -cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed) +cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed) { Vector3d Speed; if (a_Speed != NULL) @@ -231,8 +231,15 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed); case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed); case pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkFirework: return new cFireworkEntity (a_Creator, a_X, a_Y, a_Z ); - // TODO: the rest + case pkFirework: + { + if (a_Item.m_FireworkItem.m_Colours.empty()) + { + return NULL; + } + + return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, a_Item); + } } LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind); @@ -276,6 +283,7 @@ AString cProjectileEntity::GetMCAClassName(void) const case pkExpBottle: return "ThrownExpBottle"; case pkSplashPotion: return "ThrownPotion"; case pkWitherSkull: return "WitherSkull"; + case pkFirework: return "Firework"; case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this? } ASSERT(!"Unhandled projectile entity kind!"); @@ -693,8 +701,10 @@ void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_H /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cFireworkEntity : -cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z) : -super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item) : +super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25), + m_ExplodeTimer(0), + m_FireworkItem(a_Item) { } @@ -702,30 +712,20 @@ super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) { - if ((a_HitFace != BLOCK_FACE_BOTTOM) && (a_HitFace != BLOCK_FACE_NONE)) + int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width; + int PosY = POSY_TOINT; + + if ((PosY < 0) || (PosY >= cChunkDef::Height)) { - return; + goto setspeed; } - SetSpeed(0, 0, 0); - SetPosition(GetPosX(), GetPosY() - 0.5, GetPosZ()); - - m_IsInGround = true; - - BroadcastMovementUpdate(); -} - - - - - -void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ if (m_IsInGround) { - if (a_Chunk.GetBlock((int)GetPosX(), (int)GetPosY() + 1, (int)GetPosZ()) == E_BLOCK_AIR) + if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) == E_BLOCK_AIR) { m_IsInGround = false; } @@ -734,28 +734,35 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) return; } } + else + { + if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR) + { + OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM); + return; + } + } - Vector3d PerTickSpeed = GetSpeed() / 20; - Vector3d Pos = GetPosition(); +setspeed: + AddSpeedY(1); + AddPosition(GetSpeed() * (a_Dt / 1000)); +} - // Trace the tick's worth of movement as a line: - Vector3d NextPos = Pos + PerTickSpeed; - cProjectileTracerCallback TracerCallback(this); - if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) + + + + +void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks) { - // Something has been hit, abort all other processing - return; + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_FIREWORK_EXPLODE); + Destroy(); } - // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff - - // Update the position: - SetPosition(NextPos); - // Add slowdown and gravity effect to the speed: - Vector3d NewSpeed(GetSpeed()); - NewSpeed.y += 2; - NewSpeed *= TracerCallback.GetSlowdownCoeff(); - SetSpeed(NewSpeed); + m_ExplodeTimer++; } diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index e80592999..fac592d16 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -46,7 +46,7 @@ public: cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height); - static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL); + static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed = NULL); /// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace); @@ -305,13 +305,19 @@ public: CLASS_PROTODEF(cFireworkEntity); - cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z); + cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item); + const cItem & GetItem(void) const { return m_FireworkItem; } protected: // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +private: + + int m_ExplodeTimer; + cItem m_FireworkItem; // tolua_begin diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp index 339107b2e..02f31f5bb 100644 --- a/src/Entities/TNTEntity.cpp +++ b/src/Entities/TNTEntity.cpp @@ -8,10 +8,9 @@ -cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec) : +cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, int a_FuseTicks) : super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98), - m_Counter(0), - m_MaxFuseTime(a_FuseTimeInSec) + m_FuseTicks(a_FuseTicks) { } @@ -19,10 +18,9 @@ cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSe -cTNTEntity::cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec) : +cTNTEntity::cTNTEntity(const Vector3d & a_Pos, int a_FuseTicks) : super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), - m_Counter(0), - m_MaxFuseTime(a_FuseTimeInSec) + m_FuseTicks(a_FuseTicks) { } @@ -42,18 +40,27 @@ void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle) +void cTNTEntity::Explode(void) +{ + m_FuseTicks = 0; + Destroy(true); + LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ()); + m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this); +} + + + + + void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); BroadcastMovementUpdate(); - float delta_time = a_Dt / 1000; // Convert miliseconds to seconds - m_Counter += delta_time; - if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM + + m_FuseTicks -= 1; + if (m_FuseTicks <= 0) { - Destroy(true); - LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ()); - m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this); - return; + Explode(); } } diff --git a/src/Entities/TNTEntity.h b/src/Entities/TNTEntity.h index d1fcae766..116f5a8cb 100644 --- a/src/Entities/TNTEntity.h +++ b/src/Entities/TNTEntity.h @@ -16,19 +16,28 @@ public: // tolua_end CLASS_PROTODEF(cTNTEntity); - cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec); - cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec); + cTNTEntity(double a_X, double a_Y, double a_Z, int a_FuseTicks = 80); + cTNTEntity(const Vector3d & a_Pos, int a_FuseTicks = 80); // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - double GetCounterTime(void) const { return m_Counter; } // tolua_export - double GetMaxFuseTime(void) const { return m_MaxFuseTime; } // tolua_export + + // tolua_begin + + /** Explode the tnt */ + void Explode(void); + + /** Returns the fuse ticks until the tnt will explode */ + int GetFuseTicks(void) const { return m_FuseTicks; } + + /** Set the fuse ticks until the tnt will explode */ + void SetFuseTicks(int a_FuseTicks) { m_FuseTicks = a_FuseTicks; } + + // tolua_end protected: - double m_Counter; ///< How much time has elapsed since the object was created, in seconds - double m_MaxFuseTime; ///< How long the fuse is, in seconds + int m_FuseTicks; ///< How much ticks is left, while the tnt will explode }; // tolua_export diff --git a/src/Generating/MineShafts.cpp b/src/Generating/MineShafts.cpp index d9acc57bb..28dc37567 100644 --- a/src/Generating/MineShafts.cpp +++ b/src/Generating/MineShafts.cpp @@ -69,6 +69,8 @@ public: m_BoundingBox(a_BoundingBox) { } + + virtual ~cMineShaft() {} /// Returns true if this mineshaft intersects the specified cuboid bool DoesIntersect(const cCuboid & a_Other) diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp index a660285d1..4909587b1 100644 --- a/src/Generating/Trees.cpp +++ b/src/Generating/Trees.cpp @@ -595,7 +595,7 @@ void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise { break; } - ASSERT(LayerSize < ARRAYCOUNT(BigOs)); + ASSERT((size_t)LayerSize < ARRAYCOUNT(BigOs)); PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); h--; } diff --git a/src/Item.cpp b/src/Item.cpp index 9170006b6..61d57e763 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -1,4 +1,4 @@ - + #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Item.h" @@ -139,6 +139,16 @@ void cItem::GetJson(Json::Value & a_OutValue) const { a_OutValue["Lore"] = m_Lore; } + + if ((m_ItemType == E_ITEM_FIREWORK_ROCKET) || (m_ItemType == E_ITEM_FIREWORK_STAR)) + { + a_OutValue["Flicker"] = m_FireworkItem.m_HasFlicker; + a_OutValue["Trail"] = m_FireworkItem.m_HasTrail; + a_OutValue["Type"] = m_FireworkItem.m_Type; + a_OutValue["FlightTimeInTicks"] = m_FireworkItem.m_FlightTimeInTicks; + a_OutValue["Colours"] = m_FireworkItem.ColoursToString(m_FireworkItem); + a_OutValue["FadeColours"] = m_FireworkItem.FadeColoursToString(m_FireworkItem); + } } } @@ -157,6 +167,16 @@ void cItem::FromJson(const Json::Value & a_Value) m_Enchantments.AddFromString(a_Value.get("ench", "").asString()); m_CustomName = a_Value.get("Name", "").asString(); m_Lore = a_Value.get("Lore", "").asString(); + + if ((m_ItemType == E_ITEM_FIREWORK_ROCKET) || (m_ItemType == E_ITEM_FIREWORK_STAR)) + { + m_FireworkItem.m_HasFlicker = a_Value.get("Flicker", false).asBool(); + m_FireworkItem.m_HasTrail = a_Value.get("Trail", false).asBool(); + m_FireworkItem.m_Type = (NIBBLETYPE)a_Value.get("Type", 0).asInt(); + m_FireworkItem.m_FlightTimeInTicks = (short)a_Value.get("FlightTimeInTicks", 0).asInt(); + m_FireworkItem.ColoursFromString(a_Value.get("Colours", "").asString(), m_FireworkItem); + m_FireworkItem.FadeColoursFromString(a_Value.get("FadeColours", "").asString(), m_FireworkItem); + } } } diff --git a/src/Item.h b/src/Item.h index cc3b3c961..4bdfb12dd 100644 --- a/src/Item.h +++ b/src/Item.h @@ -11,6 +11,7 @@ #include "Defines.h" #include "Enchantments.h" +#include "WorldStorage/FireworksSerializer.h" @@ -38,7 +39,8 @@ public: m_ItemCount(0), m_ItemDamage(0), m_CustomName(""), - m_Lore("") + m_Lore(""), + m_FireworkItem() { } @@ -57,7 +59,8 @@ public: m_ItemDamage (a_ItemDamage), m_Enchantments(a_Enchantments), m_CustomName (a_CustomName), - m_Lore (a_Lore) + m_Lore (a_Lore), + m_FireworkItem() { if (!IsValidItem(m_ItemType)) { @@ -77,7 +80,8 @@ public: m_ItemDamage (a_CopyFrom.m_ItemDamage), m_Enchantments(a_CopyFrom.m_Enchantments), m_CustomName (a_CopyFrom.m_CustomName), - m_Lore (a_CopyFrom.m_Lore) + m_Lore (a_CopyFrom.m_Lore), + m_FireworkItem(a_CopyFrom.m_FireworkItem) { } @@ -90,6 +94,7 @@ public: m_Enchantments.Clear(); m_CustomName = ""; m_Lore = ""; + m_FireworkItem.EmptyData(); } @@ -115,7 +120,8 @@ public: (m_ItemDamage == a_Item.m_ItemDamage) && (m_Enchantments == a_Item.m_Enchantments) && (m_CustomName == a_Item.m_CustomName) && - (m_Lore == a_Item.m_Lore) + (m_Lore == a_Item.m_Lore) && + m_FireworkItem.IsEqualTo(a_Item.m_FireworkItem) ); } @@ -177,6 +183,8 @@ public: cEnchantments m_Enchantments; AString m_CustomName; AString m_Lore; + + cFireworkItem m_FireworkItem; }; // tolua_end diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h index ef3f37a7a..5b6c239cc 100644 --- a/src/Items/ItemHandler.h +++ b/src/Items/ItemHandler.h @@ -21,6 +21,9 @@ class cItemHandler public: cItemHandler(int a_ItemType); + // Force virtual destructor + virtual ~cItemHandler() {} + /// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir); diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h index cc7daeb08..18873e911 100644 --- a/src/Items/ItemLighter.h +++ b/src/Items/ItemLighter.h @@ -33,8 +33,8 @@ public: case E_BLOCK_TNT: { // Activate the TNT: - a_World->BroadcastSoundEffect("game.tnt.primed", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); - a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom + a_World->BroadcastSoundEffect("game.tnt.primed", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 1.0f); + a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5); // 80 ticks to boom a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0); break; } diff --git a/src/Items/ItemPickaxe.h b/src/Items/ItemPickaxe.h index bde7f0905..2a8e40daa 100644 --- a/src/Items/ItemPickaxe.h +++ b/src/Items/ItemPickaxe.h @@ -19,17 +19,13 @@ public: { switch(m_ItemType) { - case E_ITEM_WOODEN_PICKAXE: - case E_ITEM_GOLD_PICKAXE: - return 1; - case E_ITEM_STONE_PICKAXE: - return 2; - case E_ITEM_IRON_PICKAXE: - return 3; - case E_ITEM_DIAMOND_PICKAXE: - return 4; - default: - return 0; + case E_ITEM_WOODEN_PICKAXE: return 1; + case E_ITEM_GOLD_PICKAXE: return 1; + case E_ITEM_STONE_PICKAXE: return 2; + case E_ITEM_IRON_PICKAXE: return 3; + case E_ITEM_DIAMOND_PICKAXE: return 4; + + default: return 0; } } @@ -61,6 +57,10 @@ public: return PickaxeLevel() >= 2; } + case E_BLOCK_ANVIL: + case E_BLOCK_ENCHANTMENT_TABLE: + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: case E_BLOCK_COAL_ORE: case E_BLOCK_STONE: case E_BLOCK_COBBLESTONE: diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h index 46049f961..c6a4e714e 100644 --- a/src/Items/ItemThrowable.h +++ b/src/Items/ItemThrowable.h @@ -35,7 +35,7 @@ public: Vector3d Pos = a_Player->GetThrowStartPos(); Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff; - a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &Speed); + a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, a_Player->GetEquippedItem(), &Speed); return true; } @@ -127,13 +127,13 @@ public: return false; } + a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, a_Player->GetEquippedItem()); + if (!a_Player->IsGameModeCreative()) { a_Player->GetInventory().RemoveOneEquippedItem(); } - a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, 0); - return true; } diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index 4761103e5..c64360153 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -68,17 +68,28 @@ void cSheep::OnRightClicked(cPlayer & a_Player) void cSheep::Tick(float a_Dt, cChunk & a_Chunk) { - // The sheep should not move when he's eating so only handle the physics. + super::Tick(a_Dt, a_Chunk); + int PosX = POSX_TOINT; + int PosY = POSY_TOINT - 1; + int PosZ = POSZ_TOINT; + + if ((PosY <= 0) || (PosY > cChunkDef::Height)) + { + return; + } + if (m_TimeToStopEating > 0) { - HandlePhysics(a_Dt, a_Chunk); + m_bMovingToDestination = false; // The sheep should not move when he's eating m_TimeToStopEating--; + if (m_TimeToStopEating == 0) { - if (m_World->GetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ()) == E_BLOCK_GRASS) + if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) // Make sure grass hasn't been destroyed in the meantime { - // The sheep ate the grass so we change it to dirt. - m_World->SetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ(), E_BLOCK_DIRT, 0); + // The sheep ate the grass so we change it to dirt + m_World->SetBlock(PosX, PosY, PosZ, E_BLOCK_DIRT, 0); + GetWorld()->BroadcastSoundParticleEffect(2001, PosX, PosY, PosX, E_BLOCK_GRASS); m_IsSheared = false; m_World->BroadcastEntityMetadata(*this); } @@ -86,12 +97,11 @@ void cSheep::Tick(float a_Dt, cChunk & a_Chunk) } else { - super::Tick(a_Dt, a_Chunk); if (m_World->GetTickRandomNumber(600) == 1) { - if (m_World->GetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ()) == E_BLOCK_GRASS) + if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) { - m_World->BroadcastEntityStatus(*this, 10); + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_SHEEP_EATING); m_TimeToStopEating = 40; } } diff --git a/src/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h index fcd2ce11f..b2eb5950f 100644 --- a/src/OSSupport/SocketThreads.h +++ b/src/OSSupport/SocketThreads.h @@ -103,7 +103,7 @@ private: public: cSocketThread(cSocketThreads * a_Parent); - ~cSocketThread(); + virtual ~cSocketThread(); // All these methods assume parent's m_CS is locked bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; } diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index b5560f7c1..d3383bf0d 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -52,7 +52,7 @@ public: virtual ~cProtocol() {} /// Called when client sends some data - virtual void DataReceived(const char * a_Data, int a_Size) = 0; + virtual void DataReceived(const char * a_Data, size_t a_Size) = 0; // Sending stuff to clients (alphabetically sorted): virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 3980350f5..50ebb6d43 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -1186,7 +1186,7 @@ void cProtocol125::SendData(const char * a_Data, int a_Size) -void cProtocol125::DataReceived(const char * a_Data, int a_Size) +void cProtocol125::DataReceived(const char * a_Data, size_t a_Size) { if (!m_ReceivedData.Write(a_Data, a_Size)) { @@ -1375,7 +1375,16 @@ int cProtocol125::ParseEntityAction(void) { HANDLE_PACKET_READ(ReadBEInt, int, EntityID); HANDLE_PACKET_READ(ReadChar, char, ActionID); - m_Client->HandleEntityAction(EntityID, ActionID); + + switch (ActionID) + { + case 1: m_Client->HandleEntityCrouch(EntityID, true); break; // Crouch + case 2: m_Client->HandleEntityCrouch(EntityID, false); break; // Uncrouch + case 3: m_Client->HandleEntityLeaveBed(EntityID); break; // Leave Bed + case 4: m_Client->HandleEntitySprinting(EntityID, true); break; // Start sprinting + case 5: m_Client->HandleEntitySprinting(EntityID, false); break; // Stop sprinting + } + return PARSE_OK; } diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index 1d1484a60..aca24c03a 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -11,6 +11,7 @@ #include "Protocol.h" #include "../ByteBuffer.h" +#include "../Entities/Painting.h" @@ -24,7 +25,7 @@ public: cProtocol125(cClientHandle * a_Client); /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; + virtual void DataReceived(const char * a_Data, size_t a_Size) override; /// Sending stuff to clients (alphabetically sorted): virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; @@ -57,9 +58,17 @@ public: virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override; virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override; - virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override {} // This protocol doesn't support such message + virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override + { + // This protocol doesn't support such message + UNUSED(a_ID); + UNUSED(a_Scale); + } virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; - virtual void SendPaintingSpawn (const cPainting & a_Painting) override {}; + virtual void SendPaintingSpawn (const cPainting & a_Painting) override + { + UNUSED(a_Painting); + }; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; @@ -73,7 +82,12 @@ public: virtual void SendRespawn (void) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; - virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override {} // This protocol doesn't support such message + virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override + { + UNUSED(a_Name); + UNUSED(a_DisplayName); + UNUSED(a_Mode); + } // This protocol doesn't support such message virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override {} // This protocol doesn't support such message virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override {} // This protocol doesn't support such message virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index 1f9222a69..8df550c7b 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -108,7 +108,7 @@ cProtocol132::~cProtocol132() -void cProtocol132::DataReceived(const char * a_Data, int a_Size) +void cProtocol132::DataReceived(const char * a_Data, size_t a_Size) { if (m_IsEncrypted) { diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h index 89f4636f5..0702fbf5a 100644 --- a/src/Protocol/Protocol132.h +++ b/src/Protocol/Protocol132.h @@ -40,7 +40,7 @@ public: virtual ~cProtocol132(); /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; + virtual void DataReceived(const char * a_Data, size_t a_Size) override; // Sending commands (alphabetically sorted): virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; diff --git a/src/Protocol/Protocol16x.cpp b/src/Protocol/Protocol16x.cpp index cfa27b3c4..f6ec0a199 100644 --- a/src/Protocol/Protocol16x.cpp +++ b/src/Protocol/Protocol16x.cpp @@ -184,7 +184,16 @@ int cProtocol161::ParseEntityAction(void) HANDLE_PACKET_READ(ReadBEInt, int, EntityID); HANDLE_PACKET_READ(ReadChar, char, ActionID); HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal); - m_Client->HandleEntityAction(EntityID, ActionID); + + switch (ActionID) + { + case 1: m_Client->HandleEntityCrouch(EntityID, true); break; // Crouch + case 2: m_Client->HandleEntityCrouch(EntityID, false); break; // Uncrouch + case 3: m_Client->HandleEntityLeaveBed(EntityID); break; // Leave Bed + case 4: m_Client->HandleEntitySprinting(EntityID, true); break; // Start sprinting + case 5: m_Client->HandleEntitySprinting(EntityID, false); break; // Stop sprinting + } + return PARSE_OK; } diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 18646254f..8c800036e 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -98,7 +98,7 @@ cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAdd -void cProtocol172::DataReceived(const char * a_Data, int a_Size) +void cProtocol172::DataReceived(const char * a_Data, size_t a_Size) { if (m_IsEncrypted) { @@ -1244,7 +1244,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size) if (m_ReceivedData.GetReadableSpace() > 0) { AString AllData; - int OldReadableSpace = m_ReceivedData.GetReadableSpace(); + size_t OldReadableSpace = m_ReceivedData.GetReadableSpace(); m_ReceivedData.ReadAll(AllData); m_ReceivedData.ResetRead(); m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace); @@ -1366,7 +1366,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size) if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0)) { AString AllData; - int OldReadableSpace = m_ReceivedData.GetReadableSpace(); + size_t OldReadableSpace = m_ReceivedData.GetReadableSpace(); m_ReceivedData.ReadAll(AllData); m_ReceivedData.ResetRead(); m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace); @@ -1732,7 +1732,15 @@ void cProtocol172::HandlePacketEntityAction(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadBEInt, int, PlayerID); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Action); HANDLE_READ(a_ByteBuffer, ReadBEInt, int, JumpBoost); - m_Client->HandleEntityAction(PlayerID, Action); + + switch (Action) + { + case 1: m_Client->HandleEntityCrouch(PlayerID, true); break; // Crouch + case 2: m_Client->HandleEntityCrouch(PlayerID, false); break; // Uncrouch + case 3: m_Client->HandleEntityLeaveBed(PlayerID); break; // Leave Bed + case 4: m_Client->HandleEntitySprinting(PlayerID, true); break; // Start sprinting + case 5: m_Client->HandleEntitySprinting(PlayerID, false); break; // Stop sprinting + } } @@ -2071,36 +2079,47 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) // Load enchantments and custom display names from the NBT data: for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) { - if ( - (NBT.GetType(tag) == TAG_List) && - ( - (NBT.GetName(tag) == "ench") || - (NBT.GetName(tag) == "StoredEnchantments") - ) - ) + AString TagName = NBT.GetName(tag); + switch (NBT.GetType(tag)) { - EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag); - } - else if ((NBT.GetType(tag) == TAG_Compound) && (NBT.GetName(tag) == "display")) // Custom name and lore tag - { - for (int displaytag = NBT.GetFirstChild(tag); displaytag >= 0; displaytag = NBT.GetNextSibling(displaytag)) + case TAG_List: { - if ((NBT.GetType(displaytag) == TAG_String) && (NBT.GetName(displaytag) == "Name")) // Custon name tag + if ((TagName == "ench") || (TagName == "StoredEnchantments")) // Enchantments tags { - a_Item.m_CustomName = NBT.GetString(displaytag); + EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag); } - else if ((NBT.GetType(displaytag) == TAG_List) && (NBT.GetName(displaytag) == "Lore")) // Lore tag + break; + } + case TAG_Compound: + { + if (TagName == "display") // Custom name and lore tag { - AString Lore; - - for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings + for (int displaytag = NBT.GetFirstChild(tag); displaytag >= 0; displaytag = NBT.GetNextSibling(displaytag)) { - AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a newline, used internally by MCS to display a new line in the client; don't forget to c_str ;) + if ((NBT.GetType(displaytag) == TAG_String) && (NBT.GetName(displaytag) == "Name")) // Custon name tag + { + a_Item.m_CustomName = NBT.GetString(displaytag); + } + else if ((NBT.GetType(displaytag) == TAG_List) && (NBT.GetName(displaytag) == "Lore")) // Lore tag + { + AString Lore; + + for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings + { + AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a newline, used internally by MCS to display a new line in the client; don't forget to c_str ;) + } + + a_Item.m_Lore = Lore; + } } - - a_Item.m_Lore = Lore; } + else if ((TagName == "Fireworks") || (TagName == "Explosion")) + { + cFireworkItem::ParseFromNBT(a_Item.m_FireworkItem, NBT, tag, (ENUM_ITEM_ID)a_Item.m_ItemType); + } + break; } + default: LOGD("Unimplemented NBT data when parsing!"); break; } } } @@ -2264,7 +2283,7 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item) WriteByte (a_Item.m_ItemCount); WriteShort(a_Item.m_ItemDamage); - if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty()) + if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR)) { WriteShort(-1); return; @@ -2303,6 +2322,10 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item) } Writer.EndCompound(); } + if ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR)) + { + cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, Writer, (ENUM_ITEM_ID)a_Item.m_ItemType); + } Writer.Finish(); AString Compressed; CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); @@ -2479,10 +2502,22 @@ void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity) } case cEntity::etProjectile: { - if (((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow) + cProjectileEntity & Projectile = (cProjectileEntity &)a_Entity; + switch (Projectile.GetProjectileKind()) { - WriteByte(0x10); - WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); + case cProjectileEntity::pkArrow: + { + WriteByte(0x10); + WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); + break; + } + case cProjectileEntity::pkFirework: + { + WriteByte(0xA8); + WriteItem(((const cFireworkEntity &)a_Entity).GetItem()); + break; + } + default: break; } break; } diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 113501568..41163009e 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -56,7 +56,7 @@ public: cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); /** Called when client sends some data: */ - virtual void DataReceived(const char * a_Data, int a_Size) override; + virtual void DataReceived(const char * a_Data, size_t a_Size) override; /** Sending stuff to clients (alphabetically sorted): */ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 84b052146..3b9003e60 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -68,7 +68,7 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) -void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size) +void cProtocolRecognizer::DataReceived(const char * a_Data, size_t a_Size) { if (m_Protocol == NULL) { diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 6aaafedeb..d7fb8fad2 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -59,7 +59,7 @@ public: static AString GetVersionTextFromInt(int a_ProtocolVersion); /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; + virtual void DataReceived(const char * a_Data, size_t a_Size) override; /// Sending stuff to clients (alphabetically sorted): virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; diff --git a/src/Root.cpp b/src/Root.cpp index 78c94888d..69f18104e 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -593,7 +593,6 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac unsigned m_NameLength; const AString m_PlayerName; - cPlayerListCallback & m_Callback; virtual bool Item (cPlayer * a_pPlayer) { unsigned int Rating = RateCompareString (m_PlayerName, a_pPlayer->GetName()); @@ -615,18 +614,17 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac } public: - cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback) : + cCallback (const AString & a_PlayerName) : m_BestRating(0), m_NameLength(a_PlayerName.length()), m_PlayerName(a_PlayerName), - m_Callback(a_Callback), m_BestMatch(NULL), m_NumMatches(0) {} cPlayer * m_BestMatch; unsigned m_NumMatches; - } Callback (a_PlayerName, a_Callback); + } Callback (a_PlayerName); ForEachPlayer( Callback ); if (Callback.m_NumMatches == 1) diff --git a/src/Simulator/DelayedFluidSimulator.cpp b/src/Simulator/DelayedFluidSimulator.cpp index 3d2170e44..bc5158d95 100644 --- a/src/Simulator/DelayedFluidSimulator.cpp +++ b/src/Simulator/DelayedFluidSimulator.cpp @@ -20,7 +20,7 @@ bool cDelayedFluidSimulatorChunkData::cSlot::Add(int a_RelX, int a_RelY, int a_RelZ) { ASSERT(a_RelZ >= 0); - ASSERT(a_RelZ < ARRAYCOUNT(m_Blocks)); + ASSERT(a_RelZ < static_cast<int>(ARRAYCOUNT(m_Blocks))); cCoordWithIntVector & Blocks = m_Blocks[a_RelZ]; int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp index 4967c83f9..26712e6e6 100644 --- a/src/Simulator/FireSimulator.cpp +++ b/src/Simulator/FireSimulator.cpp @@ -334,21 +334,33 @@ void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_Rel for (size_t i = 0; i < ARRAYCOUNT(gNeighborCoords); i++) { BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_Chunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta)) + int X = a_RelX + gNeighborCoords[i].x; + int Z = a_RelZ + gNeighborCoords[i].z; + + cChunkPtr Neighbour = a_Chunk->GetRelNeighborChunkAdjustCoords(X, Z); + if (Neighbour == NULL) { - // Neighbor not accessible, ignore it continue; } + BlockType = Neighbour->GetBlock(X, a_RelY + gCrossCoords[i].y, Z); + if (!IsFuel(BlockType)) { continue; } + + if (BlockType == E_BLOCK_TNT) + { + int AbsX = X + Neighbour->GetPosX() * cChunkDef::Width; + int AbsZ = Z + Neighbour->GetPosZ() * cChunkDef::Width; + + m_World.SpawnPrimedTNT(AbsX, a_RelY + gNeighborCoords[i].y, AbsZ, 0); + Neighbour->SetBlock(X, a_RelY + gNeighborCoords[i].y, Z, E_BLOCK_AIR, 0); + return; + } + bool ShouldReplaceFuel = (m_World.GetTickRandomNumber(MAX_CHANCE_REPLACE_FUEL) < m_ReplaceFuelChance); - a_Chunk->UnboundedRelSetBlock( - a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, - ShouldReplaceFuel ? E_BLOCK_FIRE : E_BLOCK_AIR, 0 - ); + Neighbour->SetBlock(X, a_RelY + gNeighborCoords[i].y, Z, ShouldReplaceFuel ? E_BLOCK_FIRE : E_BLOCK_AIR, 0); } // for i - Coords[] } diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp index b1ac734d7..03e94e791 100644 --- a/src/Simulator/FloodyFluidSimulator.cpp +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -54,14 +54,23 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ) ); - NIBBLETYPE MyMeta = a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ); - if (!IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ))) + BLOCKTYPE MyBlock; NIBBLETYPE MyMeta; + a_Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, MyBlock, MyMeta); + + if (!IsAnyFluidBlock(MyBlock)) { // Can happen - if a block is scheduled for simulating and gets replaced in the meantime. FLOG(" BadBlockType exit"); return; } + // When in contact with water, lava should harden + if (HardenBlock(a_Chunk, a_RelX, a_RelY, a_RelZ, MyBlock, MyMeta)) + { + // Block was changed, bail out + return; + } + if (MyMeta != 0) { // Source blocks aren't checked for tributaries, others are. @@ -309,6 +318,8 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i a_NewMeta ); a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); + + HardenBlock(a_NearChunk, a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); } @@ -361,3 +372,56 @@ bool cFloodyFluidSimulator::CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX + +bool cFloodyFluidSimulator::HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) +{ + // Only lava blocks can harden + if (!IsBlockLava(a_BlockType)) + { + return false; + } + + bool ShouldHarden = false; + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + static const Vector3i Coords[] = + { + Vector3i( 1, 0, 0), + Vector3i(-1, 0, 0), + Vector3i( 0, 0, 1), + Vector3i( 0, 0, -1), + }; + for (size_t i = 0; i < ARRAYCOUNT(Coords); i++) + { + if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) + { + continue; + } + if (IsBlockWater(BlockType)) + { + ShouldHarden = true; + } + } // for i - Coords[] + + if (ShouldHarden) + { + if (a_Meta == 0) + { + // Source lava block + a_Chunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_OBSIDIAN, 0); + return true; + } + // Ignore last lava level + else if (a_Meta <= 4) + { + a_Chunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_COBBLESTONE, 0); + return true; + } + } + + return false; +} + + + diff --git a/src/Simulator/FloodyFluidSimulator.h b/src/Simulator/FloodyFluidSimulator.h index 5fd91b2b1..632de3bb2 100644 --- a/src/Simulator/FloodyFluidSimulator.h +++ b/src/Simulator/FloodyFluidSimulator.h @@ -47,6 +47,12 @@ protected: /** Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so. */ bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); + /** Checks if the specified block should harden (Water/Lava interaction) and if so, converts it to a suitable block. + * + * Returns whether the block was changed or not. + */ + bool HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta); + /** Spread water to neighbors. * * May be overridden to provide more sophisticated algorithms. diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index f377b0aa7..ca2ef4b1a 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -838,7 +838,7 @@ void cIncrementalRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { m_World.BroadcastSoundEffect("game.tnt.primed", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); - m_World.SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom + m_World.SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5); // 80 ticks to boom m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); } } diff --git a/src/Simulator/VanillaFluidSimulator.cpp b/src/Simulator/VanillaFluidSimulator.cpp index 5308d162b..78aff9d68 100644 --- a/src/Simulator/VanillaFluidSimulator.cpp +++ b/src/Simulator/VanillaFluidSimulator.cpp @@ -86,7 +86,7 @@ int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int { return Cost; } - if (!IsPassableForFluid(BlockType)) + if (!IsPassableForFluid(BlockType) && !IsBlockLiquid(BlockType)) { return Cost; } @@ -96,7 +96,7 @@ int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int { return Cost; } - if (IsPassableForFluid(BlockType)) + if (IsPassableForFluid(BlockType) || IsBlockLiquid(BlockType)) { // Path found, exit return a_Iteration; diff --git a/src/World.cpp b/src/World.cpp index f6d277663..a9db6bf00 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1726,10 +1726,10 @@ int cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType -void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff) +void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks, double a_InitialVelocityCoeff) { UNUSED(a_InitialVelocityCoeff); - cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTimeInSec); + cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTicks); TNT->Initialize(this); // TODO: Add a bit of speed in horiz and vert axes, based on the a_InitialVelocityCoeff } @@ -2980,9 +2980,9 @@ int cWorld::SpawnMobFinalize(cMonster * a_Monster) -int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed) +int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem a_Item, const Vector3d * a_Speed) { - cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Speed); + cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Item, a_Speed); if (Projectile == NULL) { return -1; @@ -3005,18 +3005,18 @@ void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Resul cCSLock Lock(m_CSPlayers); for (cPlayerList::iterator itr = m_Players.begin(), end = m_Players.end(); itr != end; ++itr) { - size_t LastSpace = a_Text.find_last_of(" "); //Find the position of the last space + size_t LastSpace = a_Text.find_last_of(" "); // Find the position of the last space - std::string LastWord = a_Text.substr(LastSpace + 1, a_Text.length()); //Find the last word - std::string PlayerName ((*itr)->GetName()); - std::size_t Found = PlayerName.find(LastWord); //Try to find last word in playername + AString LastWord = a_Text.substr(LastSpace + 1, a_Text.length()); // Find the last word + AString PlayerName ((*itr)->GetName()); + size_t Found = PlayerName.find(LastWord); // Try to find last word in playername - if (Found!=0) + if (Found == AString::npos) { - continue; //No match + continue; // No match } - a_Results.push_back((*itr)->GetName()); //Match! + a_Results.push_back(PlayerName); // Match! } } diff --git a/src/World.h b/src/World.h index 0a0939dd1..d48db5911 100644 --- a/src/World.h +++ b/src/World.h @@ -445,7 +445,7 @@ public: int SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward); /** Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided */ - void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff = 1); + void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTimeInSec = 80, double a_InitialVelocityCoeff = 1); // tolua_end @@ -708,7 +708,7 @@ public: int SpawnMobFinalize(cMonster* a_Monster); /** Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise */ - int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed = NULL); // tolua_export + int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem a_Item, const Vector3d * a_Speed = NULL); // tolua_export /** Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! */ int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); } diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h index 01a9ad274..1b8b09c21 100644 --- a/src/WorldStorage/FastNBT.h +++ b/src/WorldStorage/FastNBT.h @@ -309,7 +309,7 @@ protected: eTagType m_ItemType; // for TAG_List, the element type } ; - static const int MAX_STACK = 50; // Highliy doubtful that an NBT would be constructed this many levels deep + static const int MAX_STACK = 50; // Highly doubtful that an NBT would be constructed this many levels deep // These two fields emulate a stack. A raw array is used due to speed issues - no reallocations are allowed. sParent m_Stack[MAX_STACK]; diff --git a/src/WorldStorage/FireworksSerializer.cpp b/src/WorldStorage/FireworksSerializer.cpp new file mode 100644 index 000000000..1f05b470d --- /dev/null +++ b/src/WorldStorage/FireworksSerializer.cpp @@ -0,0 +1,252 @@ + +#include "Globals.h" +#include "FireworksSerializer.h" +#include "WorldStorage/FastNBT.h" + + + + + +void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFastNBTWriter & a_Writer, const ENUM_ITEM_ID a_Type) +{ + switch (a_Type) + { + case E_ITEM_FIREWORK_ROCKET: + { + a_Writer.BeginCompound("Fireworks"); + a_Writer.AddByte("Flight", a_FireworkItem.m_FlightTimeInTicks / 20); + a_Writer.BeginList("Explosions", TAG_Compound); + a_Writer.BeginCompound(""); + a_Writer.AddByte("Flicker", a_FireworkItem.m_HasFlicker); + a_Writer.AddByte("Trail", a_FireworkItem.m_HasTrail); + a_Writer.AddByte("Type", a_FireworkItem.m_Type); + a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size()); + a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size()); + a_Writer.EndCompound(); + a_Writer.EndList(); + a_Writer.EndCompound(); + break; + } + case E_ITEM_FIREWORK_STAR: + { + a_Writer.BeginCompound("Explosion"); + a_Writer.AddByte("Flicker", a_FireworkItem.m_HasFlicker); + a_Writer.AddByte("Trail", a_FireworkItem.m_HasTrail); + a_Writer.AddByte("Type", a_FireworkItem.m_Type); + if (!a_FireworkItem.m_Colours.empty()) + { + a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size()); + } + if (!a_FireworkItem.m_FadeColours.empty()) + { + a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size()); + } + a_Writer.EndCompound(); + break; + } + default: ASSERT(!"Unhandled firework item!"); break; + } +} + + + + + +void cFireworkItem::ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNBT & a_NBT, int a_TagIdx, const ENUM_ITEM_ID a_Type) +{ + if (a_TagIdx < 0) + { + return; + } + + switch (a_Type) + { + case E_ITEM_FIREWORK_STAR: + { + for (int explosiontag = a_NBT.GetFirstChild(a_TagIdx); explosiontag >= 0; explosiontag = a_NBT.GetNextSibling(explosiontag)) + { + eTagType TagType = a_NBT.GetType(explosiontag); + if (TagType == TAG_Byte) // Custon name tag + { + AString ExplosionName = a_NBT.GetName(explosiontag); + + if (ExplosionName == "Flicker") + { + a_FireworkItem.m_HasFlicker = (a_NBT.GetByte(explosiontag) == 1); + } + else if (ExplosionName == "Trail") + { + a_FireworkItem.m_HasTrail = (a_NBT.GetByte(explosiontag) == 1); + } + else if (ExplosionName == "Type") + { + a_FireworkItem.m_Type = a_NBT.GetByte(explosiontag); + } + } + else if (TagType == TAG_IntArray) + { + AString ExplosionName = a_NBT.GetName(explosiontag); + + if (ExplosionName == "Colors") + { + // Divide by four as data length returned in bytes + int DataLength = a_NBT.GetDataLength(explosiontag) / 4; + if (DataLength == 0) + { + continue; + } + + const int * ColourData = (const int *)(a_NBT.GetData(explosiontag)); + for (int i = 0; i < DataLength; i++) + { + a_FireworkItem.m_Colours.push_back(ntohl(ColourData[i])); + } + } + else if (ExplosionName == "FadeColors") + { + int DataLength = a_NBT.GetDataLength(explosiontag) / 4; + if (DataLength == 0) + { + continue; + } + + const int * FadeColourData = (const int *)(a_NBT.GetData(explosiontag)); + for (int i = 0; i < DataLength; i++) + { + a_FireworkItem.m_FadeColours.push_back(ntohl(FadeColourData[i])); + } + } + } + } + break; + } + case E_ITEM_FIREWORK_ROCKET: + { + for (int fireworkstag = a_NBT.GetFirstChild(a_TagIdx); fireworkstag >= 0; fireworkstag = a_NBT.GetNextSibling(fireworkstag)) + { + eTagType TagType = a_NBT.GetType(fireworkstag); + if (TagType == TAG_Byte) // Custon name tag + { + if (a_NBT.GetName(fireworkstag) == "Flight") + { + a_FireworkItem.m_FlightTimeInTicks = a_NBT.GetByte(fireworkstag) * 20; + } + } + else if ((TagType == TAG_List) && (a_NBT.GetName(fireworkstag) == "Explosions")) + { + int ExplosionsChild = a_NBT.GetFirstChild(fireworkstag); + if ((a_NBT.GetType(ExplosionsChild) == TAG_Compound) && (a_NBT.GetName(ExplosionsChild).empty())) + { + ParseFromNBT(a_FireworkItem, a_NBT, ExplosionsChild, E_ITEM_FIREWORK_STAR); + } + } + } + break; + } + default: ASSERT(!"Unhandled firework item!"); break; + } +} + + + + + +AString cFireworkItem::ColoursToString(const cFireworkItem & a_FireworkItem) +{ + AString Result; + + for (std::vector<int>::const_iterator itr = a_FireworkItem.m_Colours.begin(); itr != a_FireworkItem.m_Colours.end(); ++itr) + { + AppendPrintf(Result, "%i;", *itr); + } + + return Result; +} + + + + + +void cFireworkItem::ColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem) +{ + AStringVector Split = StringSplit(a_String, ";"); + + for (size_t itr = 0; itr < Split.size(); ++itr) + { + if (Split[itr].empty()) + { + continue; + } + + a_FireworkItem.m_Colours.push_back(atoi(Split[itr].c_str())); + } +} + + + + + +AString cFireworkItem::FadeColoursToString(const cFireworkItem & a_FireworkItem) +{ + AString Result; + + for (std::vector<int>::const_iterator itr = a_FireworkItem.m_FadeColours.begin(); itr != a_FireworkItem.m_FadeColours.end(); ++itr) + { + AppendPrintf(Result, "%i;", *itr); + } + + return Result; +} + + + + + +void cFireworkItem::FadeColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem) +{ + AStringVector Split = StringSplit(a_String, ";"); + + for (size_t itr = 0; itr < Split.size(); ++itr) + { + if (Split[itr].empty()) + { + continue; + } + + a_FireworkItem.m_FadeColours.push_back(atoi(Split[itr].c_str())); + } +} + + + + + +int cFireworkItem::GetVanillaColourCodeFromDye(short a_DyeMeta) +{ + /* + Colours are supposed to be calculated via: R << 16 + G << 8 + B + However, the RGB values fireworks use aren't the same as the ones for dyes (the ones listed in the MC Wiki) + Therefore, here is a list of numbers gotten via the Protocol Proxy + */ + + switch (a_DyeMeta) + { + case E_META_DYE_BLACK: return 0x1E1B1B; + case E_META_DYE_RED: return 0xB3312C; + case E_META_DYE_GREEN: return 0x3B511A; + case E_META_DYE_BROWN: return 0x51301A; + case E_META_DYE_BLUE: return 0x253192; + case E_META_DYE_PURPLE: return 0x7B2FBE; + case E_META_DYE_CYAN: return 0x287697; + case E_META_DYE_LIGHTGRAY: return 0xABABAB; + case E_META_DYE_GRAY: return 0x434343; + case E_META_DYE_PINK: return 0xD88198; + case E_META_DYE_LIGHTGREEN: return 0x41CD34; + case E_META_DYE_YELLOW: return 0xDECF2A; + case E_META_DYE_LIGHTBLUE: return 0x6689D3; + case E_META_DYE_MAGENTA: return 0xC354CD; + case E_META_DYE_ORANGE: return 0xEB8844; + case E_META_DYE_WHITE: return 0xF0F0F0; + default: ASSERT(!"Unhandled dye meta whilst trying to get colour code for fireworks!"); return 0; + } +} diff --git a/src/WorldStorage/FireworksSerializer.h b/src/WorldStorage/FireworksSerializer.h new file mode 100644 index 000000000..5b87bafdb --- /dev/null +++ b/src/WorldStorage/FireworksSerializer.h @@ -0,0 +1,92 @@ + +// FireworksSerializer.h + +// Declares the cFireworkItem class representing a firework or firework star + + + + + +#pragma once + +#include "Defines.h" + +class cFastNBTWriter; +class cParsedNBT; + + + + + +class cFireworkItem +{ +public: + cFireworkItem(void) : + m_HasFlicker(false), + m_HasTrail(false), + m_Type(0), + m_FlightTimeInTicks(0) + { + } + + inline void CopyFrom(const cFireworkItem & a_Item) + { + m_FlightTimeInTicks = a_Item.m_FlightTimeInTicks; + m_HasFlicker = a_Item.m_HasFlicker; + m_HasTrail = a_Item.m_HasTrail; + m_Type = a_Item.m_Type; + m_Colours = a_Item.m_Colours; + m_FadeColours = a_Item.m_FadeColours; + } + + inline void EmptyData(void) + { + m_FlightTimeInTicks = 0; + m_HasFlicker = false; + m_Type = 0; + m_HasTrail = false; + m_Colours.clear(); + m_FadeColours.clear(); + } + + inline bool IsEqualTo(const cFireworkItem & a_Item) const + { + return + ( + (m_FlightTimeInTicks == a_Item.m_FlightTimeInTicks) && + (m_HasFlicker == a_Item.m_HasFlicker) && + (m_HasTrail == a_Item.m_HasTrail) && + (m_Type == a_Item.m_Type) && + (m_Colours == a_Item.m_Colours) && + (m_FadeColours == a_Item.m_FadeColours) + ); + } + + /** Writes firework NBT data to a Writer object */ + static void WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFastNBTWriter & a_Writer, const ENUM_ITEM_ID a_Type); + + /** Reads NBT data from a NBT object and populates a FireworkItem with it */ + static void ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNBT & a_NBT, int a_TagIdx, const ENUM_ITEM_ID a_Type); + + /** Converts the firework's vector of colours into a string of values separated by a semicolon */ + static AString ColoursToString(const cFireworkItem & a_FireworkItem); + + /** Parses a string containing encoded firework colours and populates a FireworkItem with it */ + static void ColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem); + + /** Converts the firework's vector of fade colours into a string of values separated by a semicolon */ + static AString FadeColoursToString(const cFireworkItem & a_FireworkItem); + + /** Parses a string containing encoded firework fade colours and populates a FireworkItem with it */ + static void FadeColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem); + + /** Returns a colour code for fireworks used by the network code */ + static int GetVanillaColourCodeFromDye(short a_DyeMeta); + + bool m_HasFlicker; + bool m_HasTrail; + NIBBLETYPE m_Type; + short m_FlightTimeInTicks; + std::vector<int> m_Colours; + std::vector<int> m_FadeColours; +};
\ No newline at end of file diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 6d0e29958..4cf3c62d7 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -28,6 +28,7 @@ #include "../Entities/Minecart.h" #include "../Entities/Pickup.h" #include "../Entities/ProjectileEntity.h" +#include "../Entities/TNTEntity.h" #include "../Mobs/Monster.h" #include "../Mobs/Bat.h" @@ -91,11 +92,19 @@ void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AStrin } // Write the enchantments: - if (!a_Item.m_Enchantments.IsEmpty()) + if (!a_Item.m_Enchantments.IsEmpty() || ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR))) { - const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; m_Writer.BeginCompound("tag"); - EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName); + if ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR)) + { + cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, m_Writer, (ENUM_ITEM_ID)a_Item.m_ItemType); + } + + if (!a_Item.m_Enchantments.IsEmpty()) + { + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName); + } m_Writer.EndCompound(); } @@ -583,6 +592,18 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile) +void cNBTChunkSerializer::AddTNTEntity(cTNTEntity * a_TNT) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_TNT, "PrimedTnt"); + m_Writer.AddByte("Fuse", (unsigned char)a_TNT->GetFuseTicks()); + m_Writer.EndCompound(); +} + + + + + void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart) { m_Writer.BeginList("Items", TAG_Compound); @@ -662,7 +683,7 @@ void cNBTChunkSerializer::Entity(cEntity * a_Entity) case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break; case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break; case cEntity::etProjectile: AddProjectileEntity ((cProjectileEntity *)a_Entity); break; - case cEntity::etTNT: /* TODO */ break; + case cEntity::etTNT: AddTNTEntity ((cTNTEntity *) a_Entity); break; case cEntity::etExpOrb: /* TODO */ break; case cEntity::etItemFrame: /* TODO */ break; case cEntity::etPainting: /* TODO */ break; diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h index 8a9e18413..3b486d2bc 100644 --- a/src/WorldStorage/NBTChunkSerializer.h +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -41,6 +41,7 @@ class cMonster; class cPickup; class cItemGrid; class cProjectileEntity; +class cTNTEntity; @@ -107,6 +108,7 @@ protected: void AddMonsterEntity (cMonster * a_Monster); void AddPickupEntity (cPickup * a_Pickup); void AddProjectileEntity (cProjectileEntity * a_Projectile); + void AddTNTEntity (cTNTEntity * a_TNT); void AddMinecartChestContents(cMinecartWithChest * a_Minecart); diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp index a6ae8d8e0..b021aeb0c 100644 --- a/src/WorldStorage/SchematicFileSerializer.cpp +++ b/src/WorldStorage/SchematicFileSerializer.cpp @@ -103,7 +103,7 @@ bool cSchematicFileSerializer::SaveToSchematicFile(const cBlockArea & a_BlockAre -AString cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea) +bool cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out) { // Serialize into NBT data: AString NBT = SaveToSchematicNBT(a_BlockArea); @@ -114,14 +114,13 @@ AString cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_Blo } // Gzip the data: - AString Compressed; - int res = CompressStringGZIP(NBT.data(), NBT.size(), Compressed); + int res = CompressStringGZIP(NBT.data(), NBT.size(), a_Out); if (res != Z_OK) { LOG("%s: Cannot Gzip the area data NBT representation: %d", __FUNCTION__, res); return false; } - return Compressed; + return true; } diff --git a/src/WorldStorage/SchematicFileSerializer.h b/src/WorldStorage/SchematicFileSerializer.h index f6ce1c16c..05b6c74f4 100644 --- a/src/WorldStorage/SchematicFileSerializer.h +++ b/src/WorldStorage/SchematicFileSerializer.h @@ -36,8 +36,8 @@ public: static bool SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName); /** Saves the area into a string containing the .schematic file data. - Returns the data, or empty string if failed. */ - static AString SaveToSchematicString(const cBlockArea & a_BlockArea); + Returns true if successful, false on failure. The data is stored into a_Out. */ + static bool SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out); private: /** Loads the area from a schematic file uncompressed and parsed into a NBT tree. diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 680f2458f..eb159f28d 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -36,6 +36,7 @@ #include "../Entities/Minecart.h" #include "../Entities/Pickup.h" #include "../Entities/ProjectileEntity.h" +#include "../Entities/TNTEntity.h" @@ -663,6 +664,12 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_ { EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, a_NBT, EnchTag); } + + int FireworksTag = a_NBT.FindChildByName(TagTag, ((a_Item.m_ItemType == E_ITEM_FIREWORK_STAR) ? "Fireworks" : "Explosion")); + if (EnchTag > 0) + { + cFireworkItem::ParseFromNBT(a_Item.m_FireworkItem, a_NBT, FireworksTag, (ENUM_ITEM_ID)a_Item.m_ItemType); + } return true; } @@ -1231,6 +1238,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { LoadPigZombieFromNBT(a_Entities, a_NBT, a_EntityTagIdx); } + else if (strncmp(a_IDTag, "PrimedTnt", a_IDTagLength) == 0) + { + LoadTNTFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } // TODO: other entities } @@ -2167,6 +2178,28 @@ void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT +void cWSSAnvil::LoadTNTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cTNTEntity> TNT(new cTNTEntity(0.0, 0.0, 0.0, 0)); + if (!LoadEntityBaseFromNBT(*TNT.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Load Fuse Ticks: + int FuseTicks = a_NBT.FindChildByName(a_TagIdx, "Fuse"); + if (FuseTicks > 0) + { + TNT->SetFuseTicks((int) a_NBT.GetByte(FuseTicks)); + } + + a_Entities.push_back(TNT.release()); +} + + + + + bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx) { double Pos[3]; diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index b26345b13..fe93d16c3 100644 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -192,6 +192,7 @@ protected: void LoadWolfFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadZombieFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadPigZombieFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadTNTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); /// Loads entity common data from the NBT compound; returns true if successful bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx); |