From 4242431407459e91f725de9cb7fb1e2ffade216d Mon Sep 17 00:00:00 2001 From: 9caihezi <9caihezi@gmail.com> Date: Tue, 9 Jan 2018 00:37:10 +1100 Subject: Rewrite cClientHandle::HandleRightClick (#4089) * Add hand parameter to distinguish main hand/off hand. * Add a new function cClientHandle::HandleUseItem to separate the functionality of using an item without a target block. This matches the protocol with client version >= 1.9 * Always actively update the status of a block if the placement fails (by out of reach or rejected by plugin). * Do not call plugin callback CallHookPlayerRightClick(-1, 255, -1, -1, 0, 0, 0) when using item. The CallHookPlayerUsingItem will still be called. Now at most one of CallHookPlayerRightClick, CallHookPlayerUsingBlock, CallHookPlayerUsingItem and CallHookPlayerEating will be called based on the type of action (not including the used version of callbacks). * Do not count using item as BlockInteractionsRate check (Using item takes time). * Now we can open chests(etc.) when sneaking as long as the player's hand is empty. This is what vanilla server does. --- src/Blocks/BlockHandler.h | 3 +- src/ClientHandle.cpp | 278 +++++++++++++++++++---------------------- src/ClientHandle.h | 3 +- src/Protocol/Protocol_1_11.cpp | 2 +- src/Protocol/Protocol_1_12.cpp | 2 +- src/Protocol/Protocol_1_8.cpp | 12 +- src/Protocol/Protocol_1_9.cpp | 27 +++- src/Protocol/Protocol_1_9.h | 4 + 8 files changed, 173 insertions(+), 158 deletions(-) (limited to 'src') diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h index d027d3cdc..7fbc15ff5 100644 --- a/src/Blocks/BlockHandler.h +++ b/src/Blocks/BlockHandler.h @@ -81,7 +81,8 @@ public: returns true if the use was successful, return false to use the block as a "normal" block */ virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { return false; } - /** Called when a right click to this block is cancelled */ + /** Called when a right click to this block is cancelled. + It forces the server to send the real state of a block to the client to prevent client assuming the operation is successfull */ virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) {} /** Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents */ diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 8470e7de6..3a07de7f1 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1395,186 +1395,113 @@ void cClientHandle::FinishDigAnimation() -void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem) -{ - // TODO: Rewrite this function - +void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, eHand a_Hand) +{ + // This function handles three actions: + // (1) Place a block; + // (2) "Use" a block: Interactive with the block, like opening a chest/crafting table/furnace; + // (3) Use the held item targeting a block. E.g. farming. + // + // Sneaking player will not use the block if hand is not empty. + // Frozen player can do nothing. + // In Game Mode Spectator, player cannot use item or place block, but can interactive with some block depending on cBlockInfo::IsUseableBySpectator(BlockType) + // + // If the action failed, we need to send an update of the placed block or inventory to the client. + // + // Actions rejected by plugin will not lead to other attempts. + // E.g., when opening a chest with a dirt in hand, if the plugin rejects opening the chest, the dirt will not be placed. + + // TODO: We are still consuming the items in main hand. Remove this override when the off-hand consumption is handled correctly. + a_Hand = eHand::hMain; + const cItem & HeldItem = (a_Hand == eHand::hOff) ? m_Player->GetInventory().GetShieldSlot() : m_Player->GetEquippedItem(); + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(HeldItem.m_ItemType); + + // TODO: This distance should be calculated from the point that the cursor pointing at, instead of the center of the block // Distance from the block's center to the player's eye height - double dist = (Vector3d(a_BlockX, a_BlockY, a_BlockZ) + Vector3d(0.5, 0.5, 0.5) - m_Player->GetEyePosition()).Length(); - LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s; dist: %.02f", - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str(), dist + double Dist = (Vector3d(a_BlockX, a_BlockY, a_BlockZ) + Vector3d(0.5, 0.5, 0.5) - m_Player->GetEyePosition()).Length(); + LOGD("HandleRightClick: {%d, %d, %d}, face %d, Hand: %d, HeldItem: %s; Dist: %.02f", + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Hand, ItemToFullString(HeldItem).c_str(), Dist ); // Check the reach distance: // _X 2014-11-25: I've maxed at 5.26 with a Survival client and 5.78 with a Creative client in my tests - double maxDist = m_Player->IsGameModeCreative() ? 5.78 : 5.26; - bool AreRealCoords = (dist <= maxDist); - + double MaxDist = m_Player->IsGameModeCreative() ? 5.78 : 5.26; + bool IsWithinReach = (Dist <= MaxDist); cWorld * World = m_Player->GetWorld(); - - if ( - (a_BlockFace != BLOCK_FACE_NONE) && // The client is interacting with a specific block - IsValidBlock(a_HeldItem.m_ItemType) && - !AreRealCoords - ) - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - if ((a_BlockX != -1) && (a_BlockY >= 0) && (a_BlockZ != -1)) - { - if (cChunkDef::IsValidHeight(a_BlockY)) - { - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, *m_Player); - } - if (cChunkDef::IsValidHeight(a_BlockY + 1)) - { - World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, *m_Player); // 2 block high things - } - if (cChunkDef::IsValidHeight(a_BlockY - 1)) - { - World->SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, *m_Player); // 2 block high things - } - } - m_Player->GetInventory().SendEquippedSlot(); - return; - } - - if (!AreRealCoords) - { - a_BlockFace = BLOCK_FACE_NONE; - } - cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); - if (m_Player->IsFrozen() || PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) - { - // A plugin doesn't agree with the action, replace the block on the client and quit: - if (AreRealCoords) - { - cChunkInterface ChunkInterface(World->GetChunkMap()); - BLOCKTYPE BlockType = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType); - BlockHandler->OnCancelRightClick(ChunkInterface, *World, *m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - if (a_BlockFace != BLOCK_FACE_NONE) - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - if (cChunkDef::IsValidHeight(a_BlockY)) - { - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, *m_Player); - } - if (cChunkDef::IsValidHeight(a_BlockY + 1)) - { - World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, *m_Player); // 2 block high things - } - if (cChunkDef::IsValidHeight(a_BlockY - 1)) - { - World->SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, *m_Player); // 2 block high things - } - m_Player->GetInventory().SendEquippedSlot(); - } - } - return; - } - - m_NumBlockChangeInteractionsThisTick++; - - if (!CheckBlockInteractionsRate()) - { - Kick("Too many blocks were placed / interacted with per unit time - hacked client?"); - return; - } - - const cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); - - if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1)) - { - // Only compare ItemType, not meta (torches have different metas) - // The -1 check is there because sometimes the client sends -1 instead of the held item - // Ref.: https://forum.cuberite.org/thread-549-post-4502.html#pid4502 - LOGWARN("Player %s tried to place a block that was not equipped (exp %d, got %d)", - m_Username.c_str(), Equipped.m_ItemType, a_HeldItem.m_ItemType - ); - - // Let's send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block - 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); - } - return; - } - if (AreRealCoords) + bool Success = false; + if (IsWithinReach && !m_Player->IsFrozen()) { BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType); - if (BlockHandler->IsUseable() && !m_Player->IsCrouched() && (!m_Player->IsGameModeSpectator() || cBlockInfo::IsUseableBySpectator(BlockType))) + bool Placeable = ItemHandler->IsPlaceable() && !m_Player->IsGameModeSpectator(); + bool BlockUsable = BlockHandler->IsUseable() && (!m_Player->IsGameModeSpectator() || cBlockInfo::IsUseableBySpectator(BlockType)); + + if (BlockUsable && !(m_Player->IsCrouched() && !HeldItem.IsEmpty())) { + // use a block + cChunkInterface ChunkInterface(World->GetChunkMap()); if (!PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) { - cChunkInterface ChunkInterface(World->GetChunkMap()); if (BlockHandler->OnUse(ChunkInterface, *World, *m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) { // block use was successful, we're done PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); - return; + Success = true; } } + else + { + // TODO: OnCancelRightClick seems to do the same thing with updating blocks at the end of this function. Need to double check + // A plugin doesn't agree with the action, replace the block on the client and quit: + BlockHandler->OnCancelRightClick(ChunkInterface, *World, *m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + } } - } - - // Players, who spectate cannot use their items - if (m_Player->IsGameModeSpectator()) - { - return; - } - - short EquippedDamage = Equipped.m_ItemDamage; - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType); - - if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE)) - { - if (!ItemHandler->OnPlayerPlace(*World, *m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) - { - // Placement failed, bail out - return; - } - } - else if ((ItemHandler->IsFood() || ItemHandler->IsDrinkable(EquippedDamage))) - { - if ( - (m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only creative or hungry players can eat - ItemHandler->IsFood() && - (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE) // Golden apple is a special case, it is used instead of eaten - ) + else if (Placeable) { - // The player is satiated or in creative, and trying to eat - return; + // TODO: Double check that we don't need this for using item and for packet out of range + m_NumBlockChangeInteractionsThisTick++; + if (!CheckBlockInteractionsRate()) + { + Kick("Too many blocks were placed / interacted with per unit time - hacked client?"); + return; + } + if (!PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + // place a block + Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + } } - m_Player->StartEating(); - if (m_Player->IsFrozen() || PlgMgr->CallHookPlayerEating(*m_Player)) + else { - // A plugin won't let us eat, abort (send the proper packets to the client, too): - m_Player->AbortEating(); + // Use an item in hand with a target block + if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + // All plugins agree with using the item + cBlockInServerPluginInterface PluginInterface(*World); + ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + Success = true; + } } } - else + if (!Success) { - if (m_Player->IsFrozen() || PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + // Update the target block including the block above and below for 2 block high things + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + for (int y = a_BlockY - 1; y <= a_BlockY + 1; y++) { - // A plugin doesn't agree with using the item, abort - return; + if (cChunkDef::IsValidHeight(y)) + { + World->SendBlockTo(a_BlockX, y, a_BlockZ, *m_Player); + } } - cBlockInServerPluginInterface PluginInterface(*World); - ItemHandler->OnItemUse(World, m_Player, PluginInterface, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); - } - // Charge bow when it's in slot off-hand / shield - if ((a_BlockFace == BLOCK_FACE_NONE) && (m_Player->GetInventory().GetShieldSlot().m_ItemType == E_ITEM_BOW)) - { - m_Player->StartChargingBow(); + // TODO: Send corresponding slot based on hand + m_Player->GetInventory().SendEquippedSlot(); } } @@ -1810,6 +1737,61 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick) +void cClientHandle::HandleUseItem(eHand a_Hand) +{ + // Use the held item without targeting a block: eating, drinking, charging a bow, using buckets + // In version 1.8.x, this function shares the same packet id with HandleRightClick. + // In version >= 1.9, there is a new packet id for "Use Item". + + // TODO: We are still consuming the items in main hand. Remove this override when the off-hand consumption is handled correctly. + a_Hand = eHand::hMain; + const cItem & HeldItem = (a_Hand == eHand::hOff) ? m_Player->GetInventory().GetShieldSlot() : m_Player->GetEquippedItem(); + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(HeldItem.m_ItemType); + cWorld * World = m_Player->GetWorld(); + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); + + LOGD("HandleUseItem: Hand: %d; HeldItem: %s", a_Hand, ItemToFullString(HeldItem).c_str()); + + // Use item in main / off hand + // TODO: do we need to sync the current inventory with client if it fails? + if (m_Player->IsFrozen() || m_Player->IsGameModeSpectator()) + { + return; + } + + if (ItemHandler->IsFood() || ItemHandler->IsDrinkable(HeldItem.m_ItemDamage)) + { + if ( + ItemHandler->IsFood() && + (m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only non-creative or hungry players can eat + (HeldItem.m_ItemType != E_ITEM_GOLDEN_APPLE) // Golden apple is a special case, it is used instead of eaten + ) + { + // The player is satiated or in creative, and trying to eat + return; + } + if (!PlgMgr->CallHookPlayerEating(*m_Player)) + { + m_Player->StartEating(); + } + } + else + { + // Use an item in hand without a target block + if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, -1, 255, -1, BLOCK_FACE_NONE, 0, 0, 0)) + { + // All plugins agree with using the item + cBlockInServerPluginInterface PluginInterface(*World); + ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, -1, 255, -1, BLOCK_FACE_NONE); + PlgMgr->CallHookPlayerUsedItem(*m_Player, -1, 255, -1, BLOCK_FACE_NONE, 0, 0, 0); + } + } +} + + + + + void cClientHandle::HandleRespawn(void) { if (m_Player == nullptr) diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 42e2ee10a..78da199a7 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -356,7 +356,7 @@ public: // tolua_export void HandlePluginMessage (const AString & a_Channel, const AString & a_Message); void HandleRespawn (void); - void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem); + void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, eHand a_Hand); void HandleSlotSelected (Int16 a_SlotNum); void HandleSpectate (const cUUID & a_PlayerUUID); void HandleSteerVehicle (float Forward, float Sideways); @@ -368,6 +368,7 @@ public: // tolua_export ); void HandleUnmount (void); void HandleUseEntity (UInt32 a_TargetEntityID, bool a_IsLeftClick); + void HandleUseItem (eHand a_Hand); void HandleWindowClick (UInt8 a_WindowID, Int16 a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem); void HandleWindowClose (UInt8 a_WindowID); diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp index c0c8815e8..f5df4e646 100644 --- a/src/Protocol/Protocol_1_11.cpp +++ b/src/Protocol/Protocol_1_11.cpp @@ -550,7 +550,7 @@ void cProtocol_1_11_0::HandlePacketBlockPlace(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, CursorX); HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, CursorY); HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, CursorZ); - m_Client->HandleRightClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), FloorC(CursorX * 16), FloorC(CursorY * 16), FloorC(CursorZ * 16), m_Client->GetPlayer()->GetEquippedItem()); + m_Client->HandleRightClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), FloorC(CursorX * 16), FloorC(CursorY * 16), FloorC(CursorZ * 16), HandIntToEnum(Hand)); } diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index b804dd351..a3d1952ed 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -363,7 +363,7 @@ void cProtocol_1_12::HandlePacketBlockPlace(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, CursorX); HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, CursorY); HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, CursorZ); - m_Client->HandleRightClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), FloorC(CursorX * 16), FloorC(CursorY * 16), FloorC(CursorZ * 16), m_Client->GetPlayer()->GetEquippedItem()); + m_Client->HandleRightClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), FloorC(CursorX * 16), FloorC(CursorY * 16), FloorC(CursorZ * 16), HandIntToEnum(Hand)); } diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index 4568ad8cb..41e168c95 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -2329,13 +2329,21 @@ void cProtocol_1_8_0::HandlePacketBlockPlace(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadBEInt8, Int8, Face); - cItem Item; + cItem Item; // Ignored ReadItem(a_ByteBuffer, Item, 3); HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, CursorX); HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, CursorY); HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, CursorZ); - m_Client->HandleRightClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), CursorX, CursorY, CursorZ, m_Client->GetPlayer()->GetEquippedItem()); + eBlockFace blockFace = FaceIntToBlockFace(Face); + if (blockFace == eBlockFace::BLOCK_FACE_NONE) + { + m_Client->HandleUseItem(eHand::hMain); + } + else + { + m_Client->HandleRightClick(BlockX, BlockY, BlockZ, blockFace, CursorX, CursorY, CursorZ, eHand::hMain); + } } diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 2b66cc7fa..3e600cefd 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -66,6 +66,7 @@ static const Int16 SLOT_NUM_OUTSIDE = -999; /** Value for main hand in Hand parameter for Protocol 1.9. */ static const UInt32 MAIN_HAND = 0; +static const UInt32 OFF_HAND = 1; @@ -2384,7 +2385,7 @@ void cProtocol_1_9_0::HandlePacketBlockPlace(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, CursorX); HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, CursorY); HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, CursorZ); - m_Client->HandleRightClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), CursorX, CursorY, CursorZ, m_Client->GetPlayer()->GetEquippedItem()); + m_Client->HandleRightClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), CursorX, CursorY, CursorZ, HandIntToEnum(Hand)); } @@ -2784,10 +2785,9 @@ void cProtocol_1_9_0::HandlePacketUseEntity(cByteBuffer & a_ByteBuffer) void cProtocol_1_9_0::HandlePacketUseItem(cByteBuffer & a_ByteBuffer) { - HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt64, Hand); + HANDLE_READ(a_ByteBuffer, ReadVarInt, Int32, Hand); - // Didn't click a block - emulate old values used with place block of -1, -1, -1 (and BLOCK_FACE_NONE). - m_Client->HandleRightClick(-1, 255, -1, BLOCK_FACE_NONE, 0, 0, 0, m_Client->GetPlayer()->GetEquippedItem()); + m_Client->HandleUseItem(HandIntToEnum(Hand)); } @@ -3267,6 +3267,25 @@ eBlockFace cProtocol_1_9_0::FaceIntToBlockFace(Int32 a_BlockFace) +eHand cProtocol_1_9_0::HandIntToEnum(Int32 a_Hand) +{ + // Convert hand parameter into eHand enum + switch (a_Hand) + { + case MAIN_HAND: return eHand::hMain; + case OFF_HAND: return eHand::hOff; + default: + { + ASSERT(!"Unknown hand value"); + return eHand::hMain; + } + } +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cProtocol_1_9_0::cPacketizer: diff --git a/src/Protocol/Protocol_1_9.h b/src/Protocol/Protocol_1_9.h index d08b76755..a7fd137a9 100644 --- a/src/Protocol/Protocol_1_9.h +++ b/src/Protocol/Protocol_1_9.h @@ -255,6 +255,10 @@ protected: If the received value doesn't match any of our eBlockFace constants, BLOCK_FACE_NONE is returned. */ eBlockFace FaceIntToBlockFace(Int32 a_FaceInt); + /** Converts the hand parameter received by the protocol into eHand constants. + If the received value doesn't match any of the know value, raise an assertion fail or return hMain. */ + eHand HandIntToEnum(Int32 a_Hand); + /** Writes the item data into a packet. */ void WriteItem(cPacketizer & a_Pkt, const cItem & a_Item); -- cgit v1.2.3