From a999c5d845bd759c6d83b356c7b39e67473dc452 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sat, 10 Apr 2021 15:57:16 +0100 Subject: More cProtocol cleanup * Alpha sort functions * Simplify hand handling * Fix left handed mode client-side display --- src/Protocol/Protocol_1_9.cpp | 433 ++++++++++++++++++++---------------------- 1 file changed, 209 insertions(+), 224 deletions(-) (limited to 'src/Protocol/Protocol_1_9.cpp') diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 6b240b235..e6f774bdf 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -51,9 +51,11 @@ Implements the 1.9 protocol classes: -/** Value for main hand in Hand parameter for Protocol 1.9. */ -static const UInt32 MAIN_HAND = 0; -static const UInt32 OFF_HAND = 1; +// Value for main hand in Hand parameter for Protocol 1.9. +#define MAIN_HAND 0 + +// Value for left hand in MainHand parameter for Protocol 1.9. +#define LEFT_HAND 0 @@ -602,7 +604,7 @@ void cProtocol_1_9_0::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -UInt32 cProtocol_1_9_0::GetPacketID(cProtocol::ePacketType a_Packet) +UInt32 cProtocol_1_9_0::GetPacketID(cProtocol::ePacketType a_Packet) const { switch (a_Packet) { @@ -726,22 +728,22 @@ signed char cProtocol_1_9_0::GetProtocolEntityStatus(const EntityAnimation a_Ani -cProtocol::Version cProtocol_1_9_0::GetProtocolVersion() +UInt32 cProtocol_1_9_0::GetProtocolMobType(const eMonsterType a_MobType) const { - return Version::v1_9_0; + switch (a_MobType) + { + case mtShulker: return 69; + default: return Super::GetProtocolMobType(a_MobType); + } } -UInt32 cProtocol_1_9_0::GetProtocolMobType(const eMonsterType a_MobType) +cProtocol::Version cProtocol_1_9_0::GetProtocolVersion() const { - switch (a_MobType) - { - case mtShulker: return 69; - default: return Super::GetProtocolMobType(a_MobType); - } + return Version::v1_9_0; } @@ -824,7 +826,7 @@ void cProtocol_1_9_0::HandlePacketAnimation(cByteBuffer & a_ByteBuffer) { HANDLE_READ(a_ByteBuffer, ReadVarInt, Int32, Hand); - m_Client->HandleAnimation(0); // Packet exists solely for arm-swing notification + m_Client->HandleAnimation(Hand == MAIN_HAND); // Packet exists solely for arm-swing notification (main and off-hand). } @@ -862,7 +864,8 @@ 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, HandIntToEnum(Hand)); + + m_Client->HandleRightClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), CursorX, CursorY, CursorZ, Hand == MAIN_HAND); } @@ -899,12 +902,12 @@ void cProtocol_1_9_0::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, ChatFlags); HANDLE_READ(a_ByteBuffer, ReadBool, bool, ChatColors); HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, SkinParts); - HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, MainHand); + HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, MainHand); m_Client->SetLocale(Locale); m_Client->SetViewDistance(ViewDistance); m_Client->GetPlayer()->SetSkinParts(SkinParts); - m_Client->GetPlayer()->SetMainHand(static_cast(MainHand)); + m_Client->GetPlayer()->SetLeftHanded(MainHand == LEFT_HAND); // TODO: Handle chat flags and chat colors } @@ -964,7 +967,7 @@ void cProtocol_1_9_0::HandlePacketPlayerPos(cByteBuffer & a_ByteBuffer) if (m_IsTeleportIdConfirmed) { - m_Client->HandlePlayerPos(PosX, PosY, PosZ, IsOnGround); + m_Client->HandlePlayerMove(PosX, PosY, PosZ, IsOnGround); } } @@ -1065,6 +1068,7 @@ void cProtocol_1_9_0::HandlePacketUseEntity(cByteBuffer & a_ByteBuffer) case 0: { HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, Hand); + if (Hand == MAIN_HAND) // TODO: implement handling of off-hand actions; ignore them for now to avoid processing actions twice { m_Client->HandleUseEntity(EntityID, false); @@ -1102,7 +1106,7 @@ void cProtocol_1_9_0::HandlePacketUseItem(cByteBuffer & a_ByteBuffer) { HANDLE_READ(a_ByteBuffer, ReadVarInt, Int32, Hand); - m_Client->HandleUseItem(HandIntToEnum(Hand)); + m_Client->HandleUseItem(Hand == MAIN_HAND); } @@ -1193,7 +1197,7 @@ void cProtocol_1_9_0::HandlePacketWindowClick(cByteBuffer & a_ByteBuffer) -void cProtocol_1_9_0::ParseItemMetadata(cItem & a_Item, const ContiguousByteBufferView a_Metadata) +void cProtocol_1_9_0::ParseItemMetadata(cItem & a_Item, const ContiguousByteBufferView a_Metadata) const { // Parse into NBT: cParsedNBT NBT(a_Metadata); @@ -1397,25 +1401,6 @@ void cProtocol_1_9_0::ParseItemMetadata(cItem & a_Item, const ContiguousByteBuff -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; - } - } -} - - - - - void cProtocol_1_9_0::SendEntitySpawn(const cEntity & a_Entity, const UInt8 a_ObjectType, const Int32 a_ObjectData) { ASSERT(m_State == 3); // In game mode? @@ -1443,7 +1428,7 @@ void cProtocol_1_9_0::SendEntitySpawn(const cEntity & a_Entity, const UInt8 a_Ob -void cProtocol_1_9_0::WriteBlockEntity(cFastNBTWriter & a_Writer, const cBlockEntity & a_BlockEntity) +void cProtocol_1_9_0::WriteBlockEntity(cFastNBTWriter & a_Writer, const cBlockEntity & a_BlockEntity) const { a_Writer.AddInt("x", a_BlockEntity.GetPosX()); a_Writer.AddInt("y", a_BlockEntity.GetPosY()); @@ -1467,177 +1452,7 @@ void cProtocol_1_9_0::WriteBlockEntity(cFastNBTWriter & a_Writer, const cBlockEn -void cProtocol_1_9_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) -{ - short ItemType = a_Item.m_ItemType; - ASSERT(ItemType >= -1); // Check validity of packets in debug runtime - if (ItemType <= 0) - { - // Fix, to make sure no invalid values are sent. - ItemType = -1; - } - - if (a_Item.IsEmpty()) - { - a_Pkt.WriteBEInt16(-1); - return; - } - - if ((ItemType == E_ITEM_POTION) && ((a_Item.m_ItemDamage & 0x4000) != 0)) - { - // Ugly special case for splash potion ids which changed in 1.9; this can be removed when the new 1.9 ids are implemented - a_Pkt.WriteBEInt16(438); // minecraft:splash_potion - } - else - { - // Normal item - a_Pkt.WriteBEInt16(ItemType); - } - a_Pkt.WriteBEInt8(a_Item.m_ItemCount); - if ((ItemType == E_ITEM_POTION) || (ItemType == E_ITEM_SPAWN_EGG)) - { - // These items lost their metadata; if it is sent they don't render correctly. - a_Pkt.WriteBEInt16(0); - } - else - { - a_Pkt.WriteBEInt16(a_Item.m_ItemDamage); - } - - if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (ItemType != E_ITEM_FIREWORK_ROCKET) && (ItemType != E_ITEM_FIREWORK_STAR) && !a_Item.m_ItemColor.IsValid() && (ItemType != E_ITEM_POTION) && (ItemType != E_ITEM_SPAWN_EGG)) - { - a_Pkt.WriteBEInt8(0); - return; - } - - - // Send the enchantments and custom names: - cFastNBTWriter Writer; - if (a_Item.m_RepairCost != 0) - { - Writer.AddInt("RepairCost", a_Item.m_RepairCost); - } - if (!a_Item.m_Enchantments.IsEmpty()) - { - const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; - EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, Writer, TagName); - } - if (!a_Item.IsBothNameAndLoreEmpty() || a_Item.m_ItemColor.IsValid()) - { - Writer.BeginCompound("display"); - if (a_Item.m_ItemColor.IsValid()) - { - Writer.AddInt("color", static_cast(a_Item.m_ItemColor.m_Color)); - } - - if (!a_Item.IsCustomNameEmpty()) - { - Writer.AddString("Name", a_Item.m_CustomName); - } - if (!a_Item.IsLoreEmpty()) - { - Writer.BeginList("Lore", TAG_String); - - for (const auto & Line : a_Item.m_LoreTable) - { - Writer.AddString("", Line); - } - - Writer.EndList(); - } - 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, static_cast(a_Item.m_ItemType)); - } - if (a_Item.m_ItemType == E_ITEM_POTION) - { - // 1.9 potions use a different format. In the future (when only 1.9+ is supported) this should be its own class - AString PotionID = "empty"; // Fallback of "Uncraftable potion" for unhandled cases - - cEntityEffect::eType Type = cEntityEffect::GetPotionEffectType(a_Item.m_ItemDamage); - if (Type != cEntityEffect::effNoEffect) - { - switch (Type) - { - case cEntityEffect::effRegeneration: PotionID = "regeneration"; break; - case cEntityEffect::effSpeed: PotionID = "swiftness"; break; - case cEntityEffect::effFireResistance: PotionID = "fire_resistance"; break; - case cEntityEffect::effPoison: PotionID = "poison"; break; - case cEntityEffect::effInstantHealth: PotionID = "healing"; break; - case cEntityEffect::effNightVision: PotionID = "night_vision"; break; - case cEntityEffect::effWeakness: PotionID = "weakness"; break; - case cEntityEffect::effStrength: PotionID = "strength"; break; - case cEntityEffect::effSlowness: PotionID = "slowness"; break; - case cEntityEffect::effJumpBoost: PotionID = "leaping"; break; - case cEntityEffect::effInstantDamage: PotionID = "harming"; break; - case cEntityEffect::effWaterBreathing: PotionID = "water_breathing"; break; - case cEntityEffect::effInvisibility: PotionID = "invisibility"; break; - default: ASSERT(!"Unknown potion effect"); break; - } - if (cEntityEffect::GetPotionEffectIntensity(a_Item.m_ItemDamage) == 1) - { - PotionID = "strong_" + PotionID; - } - else if (a_Item.m_ItemDamage & 0x40) - { - // Extended potion bit - PotionID = "long_" + PotionID; - } - } - else - { - // Empty potions: Water bottles and other base ones - if (a_Item.m_ItemDamage == 0) - { - // No other bits set; thus it's a water bottle - PotionID = "water"; - } - else - { - switch (a_Item.m_ItemDamage & 0x3f) - { - case 0x00: PotionID = "mundane"; break; - case 0x10: PotionID = "awkward"; break; - case 0x20: PotionID = "thick"; break; - } - // Default cases will use "empty" from before. - } - } - - PotionID = "minecraft:" + PotionID; - - Writer.AddString("Potion", PotionID); - } - if (a_Item.m_ItemType == E_ITEM_SPAWN_EGG) - { - // Convert entity ID to the name. - eMonsterType MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(a_Item.m_ItemDamage); - if (MonsterType != eMonsterType::mtInvalidType) - { - Writer.BeginCompound("EntityTag"); - Writer.AddString("id", "minecraft:" + cMonster::MobTypeToVanillaNBT(MonsterType)); - Writer.EndCompound(); - } - } - - Writer.Finish(); - - const auto Result = Writer.GetResult(); - if (Result.empty()) - { - a_Pkt.WriteBEInt8(0); - return; - } - a_Pkt.WriteBuf(Result); -} - - - - - -void cProtocol_1_9_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity) +void cProtocol_1_9_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity) const { // Common metadata: Int8 Flags = 0; @@ -1692,7 +1507,7 @@ void cProtocol_1_9_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a a_Pkt.WriteBEUInt8(13); a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); - a_Pkt.WriteBEUInt8(static_cast(Player.GetMainHand())); + a_Pkt.WriteBEUInt8(Player.IsLeftHanded() ? 0 : 1); break; } case cEntity::etPickup: @@ -1859,7 +1674,177 @@ void cProtocol_1_9_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a -void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob) +void cProtocol_1_9_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) const +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + if (a_Item.IsEmpty()) + { + a_Pkt.WriteBEInt16(-1); + return; + } + + if ((ItemType == E_ITEM_POTION) && ((a_Item.m_ItemDamage & 0x4000) != 0)) + { + // Ugly special case for splash potion ids which changed in 1.9; this can be removed when the new 1.9 ids are implemented + a_Pkt.WriteBEInt16(438); // minecraft:splash_potion + } + else + { + // Normal item + a_Pkt.WriteBEInt16(ItemType); + } + a_Pkt.WriteBEInt8(a_Item.m_ItemCount); + if ((ItemType == E_ITEM_POTION) || (ItemType == E_ITEM_SPAWN_EGG)) + { + // These items lost their metadata; if it is sent they don't render correctly. + a_Pkt.WriteBEInt16(0); + } + else + { + a_Pkt.WriteBEInt16(a_Item.m_ItemDamage); + } + + if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (ItemType != E_ITEM_FIREWORK_ROCKET) && (ItemType != E_ITEM_FIREWORK_STAR) && !a_Item.m_ItemColor.IsValid() && (ItemType != E_ITEM_POTION) && (ItemType != E_ITEM_SPAWN_EGG)) + { + a_Pkt.WriteBEInt8(0); + return; + } + + + // Send the enchantments and custom names: + cFastNBTWriter Writer; + if (a_Item.m_RepairCost != 0) + { + Writer.AddInt("RepairCost", a_Item.m_RepairCost); + } + if (!a_Item.m_Enchantments.IsEmpty()) + { + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, Writer, TagName); + } + if (!a_Item.IsBothNameAndLoreEmpty() || a_Item.m_ItemColor.IsValid()) + { + Writer.BeginCompound("display"); + if (a_Item.m_ItemColor.IsValid()) + { + Writer.AddInt("color", static_cast(a_Item.m_ItemColor.m_Color)); + } + + if (!a_Item.IsCustomNameEmpty()) + { + Writer.AddString("Name", a_Item.m_CustomName); + } + if (!a_Item.IsLoreEmpty()) + { + Writer.BeginList("Lore", TAG_String); + + for (const auto & Line : a_Item.m_LoreTable) + { + Writer.AddString("", Line); + } + + Writer.EndList(); + } + 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, static_cast(a_Item.m_ItemType)); + } + if (a_Item.m_ItemType == E_ITEM_POTION) + { + // 1.9 potions use a different format. In the future (when only 1.9+ is supported) this should be its own class + AString PotionID = "empty"; // Fallback of "Uncraftable potion" for unhandled cases + + cEntityEffect::eType Type = cEntityEffect::GetPotionEffectType(a_Item.m_ItemDamage); + if (Type != cEntityEffect::effNoEffect) + { + switch (Type) + { + case cEntityEffect::effRegeneration: PotionID = "regeneration"; break; + case cEntityEffect::effSpeed: PotionID = "swiftness"; break; + case cEntityEffect::effFireResistance: PotionID = "fire_resistance"; break; + case cEntityEffect::effPoison: PotionID = "poison"; break; + case cEntityEffect::effInstantHealth: PotionID = "healing"; break; + case cEntityEffect::effNightVision: PotionID = "night_vision"; break; + case cEntityEffect::effWeakness: PotionID = "weakness"; break; + case cEntityEffect::effStrength: PotionID = "strength"; break; + case cEntityEffect::effSlowness: PotionID = "slowness"; break; + case cEntityEffect::effJumpBoost: PotionID = "leaping"; break; + case cEntityEffect::effInstantDamage: PotionID = "harming"; break; + case cEntityEffect::effWaterBreathing: PotionID = "water_breathing"; break; + case cEntityEffect::effInvisibility: PotionID = "invisibility"; break; + default: ASSERT(!"Unknown potion effect"); break; + } + if (cEntityEffect::GetPotionEffectIntensity(a_Item.m_ItemDamage) == 1) + { + PotionID = "strong_" + PotionID; + } + else if (a_Item.m_ItemDamage & 0x40) + { + // Extended potion bit + PotionID = "long_" + PotionID; + } + } + else + { + // Empty potions: Water bottles and other base ones + if (a_Item.m_ItemDamage == 0) + { + // No other bits set; thus it's a water bottle + PotionID = "water"; + } + else + { + switch (a_Item.m_ItemDamage & 0x3f) + { + case 0x00: PotionID = "mundane"; break; + case 0x10: PotionID = "awkward"; break; + case 0x20: PotionID = "thick"; break; + } + // Default cases will use "empty" from before. + } + } + + PotionID = "minecraft:" + PotionID; + + Writer.AddString("Potion", PotionID); + } + if (a_Item.m_ItemType == E_ITEM_SPAWN_EGG) + { + // Convert entity ID to the name. + eMonsterType MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(a_Item.m_ItemDamage); + if (MonsterType != eMonsterType::mtInvalidType) + { + Writer.BeginCompound("EntityTag"); + Writer.AddString("id", "minecraft:" + cMonster::MobTypeToVanillaNBT(MonsterType)); + Writer.EndCompound(); + } + } + + Writer.Finish(); + + const auto Result = Writer.GetResult(); + if (Result.empty()) + { + a_Pkt.WriteBEInt8(0); + return; + } + a_Pkt.WriteBuf(Result); +} + + + + + +void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob) const { // Living entity metadata if (a_Mob.HasCustomName()) @@ -2294,7 +2279,7 @@ void cProtocol_1_9_1::SendLogin(const cPlayer & a_Player, const cWorld & a_World -cProtocol::Version cProtocol_1_9_1::GetProtocolVersion() +cProtocol::Version cProtocol_1_9_1::GetProtocolVersion() const { return Version::v1_9_1; } @@ -2306,7 +2291,7 @@ cProtocol::Version cProtocol_1_9_1::GetProtocolVersion() //////////////////////////////////////////////////////////////////////////////// // cProtocol_1_9_2: -cProtocol::Version cProtocol_1_9_2::GetProtocolVersion() +cProtocol::Version cProtocol_1_9_2::GetProtocolVersion() const { return Version::v1_9_2; } @@ -2354,16 +2339,7 @@ void cProtocol_1_9_4::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, c -cProtocol::Version cProtocol_1_9_4::GetProtocolVersion() -{ - return Version::v1_9_4; -} - - - - - -UInt32 cProtocol_1_9_4::GetPacketID(cProtocol::ePacketType a_Packet) +UInt32 cProtocol_1_9_4::GetPacketID(cProtocol::ePacketType a_Packet) const { switch (a_Packet) { @@ -2376,3 +2352,12 @@ UInt32 cProtocol_1_9_4::GetPacketID(cProtocol::ePacketType a_Packet) default: return Super::GetPacketID(a_Packet); } } + + + + + +cProtocol::Version cProtocol_1_9_4::GetProtocolVersion() const +{ + return Version::v1_9_4; +} -- cgit v1.2.3