From 77731f80fb89182e8697d9daad6c8400e480fb6d Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Wed, 1 Aug 2018 00:21:44 +0100 Subject: Initial support for 1.13 clients + Can look at *shape* of world + Can look at *shape* of inventory --- src/Protocol/CMakeLists.txt | 2 + src/Protocol/ChunkDataSerializer.cpp | 138 +++++++++++ src/Protocol/ChunkDataSerializer.h | 6 +- src/Protocol/ProtocolRecognizer.cpp | 6 + src/Protocol/ProtocolRecognizer.h | 7 +- src/Protocol/Protocol_1_13.cpp | 456 +++++++++++++++++++++++++++++++++++ src/Protocol/Protocol_1_13.h | 58 +++++ src/Protocol/Protocol_1_9.h | 2 +- 8 files changed, 669 insertions(+), 6 deletions(-) create mode 100644 src/Protocol/Protocol_1_13.cpp create mode 100644 src/Protocol/Protocol_1_13.h diff --git a/src/Protocol/CMakeLists.txt b/src/Protocol/CMakeLists.txt index b9ccf0d33..127edd317 100644 --- a/src/Protocol/CMakeLists.txt +++ b/src/Protocol/CMakeLists.txt @@ -10,6 +10,7 @@ SET (SRCS Protocol_1_10.cpp Protocol_1_11.cpp Protocol_1_12.cpp + Protocol_1_13.cpp ProtocolRecognizer.cpp ) @@ -25,6 +26,7 @@ SET (HDRS Protocol_1_10.h Protocol_1_11.h Protocol_1_12.h + Protocol_1_13.h ProtocolRecognizer.h ) diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index 33905b8e8..23d5b19ae 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -66,6 +66,7 @@ const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int case RELEASE_1_8_0: Serialize47(data, a_ChunkX, a_ChunkZ); break; case RELEASE_1_9_0: Serialize107(data, a_ChunkX, a_ChunkZ); break; case RELEASE_1_9_4: Serialize110(data, a_ChunkX, a_ChunkZ); break; + case RELEASE_1_13: Serialize393(data, a_ChunkX, a_ChunkZ); break; // TODO: Other protocol versions may serialize the data differently; implement here default: @@ -440,3 +441,140 @@ void cChunkDataSerializer::Serialize110(AString & a_Data, int a_ChunkX, int a_Ch + +void cChunkDataSerializer::Serialize393(AString & a_Data, int a_ChunkX, int a_ChunkZ) +{ + // This function returns the fully compressed packet (including packet size), not the raw packet! + + // Create the packet: + cByteBuffer Packet(512 KiB); + Packet.WriteVarInt32(0x22); // Packet id (Chunk Data packet) + Packet.WriteBEInt32(a_ChunkX); + Packet.WriteBEInt32(a_ChunkZ); + Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag + Packet.WriteVarInt32(m_Data.GetSectionBitmask()); + + // Write the chunk size in bytes: + const size_t BitsPerEntry = 14; + const size_t Mask = (1 << BitsPerEntry) - 1; + const size_t ChunkSectionDataArraySize = (cChunkData::SectionBlockCount * BitsPerEntry) / 8 / 8; + size_t ChunkSectionSize = ( + 1 + // Bits per entry, BEUInt8, 1 byte + Packet.GetVarIntSize(static_cast(ChunkSectionDataArraySize)) + // Field containing "size of whole section", VarInt32, variable size + ChunkSectionDataArraySize * 8 + // Actual section data, lots of bytes (multiplier 1 long = 8 bytes) + cChunkData::SectionBlockCount / 2 // Size of blocklight which is always sent + ); + + if (m_Dimension == dimOverworld) + { + // Sky light is only sent in the overworld. + ChunkSectionSize += cChunkData::SectionBlockCount / 2; + } + + const size_t BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + size_t ChunkSize = ( + ChunkSectionSize * m_Data.NumPresentSections() + + BiomeDataSize * 4 // Biome data now BE ints + ); + Packet.WriteVarInt32(static_cast(ChunkSize)); + + // Write each chunk section... + ForEachSection(m_Data, [&](const cChunkData::sChunkSection & a_Section) + { + Packet.WriteBEUInt8(static_cast(BitsPerEntry)); + Packet.WriteVarInt32(static_cast(ChunkSectionDataArraySize)); + + UInt64 TempLong = 0; // Temporary value that will be stored into + UInt64 CurrentlyWrittenIndex = 0; // "Index" of the long that would be written to + + for (size_t Index = 0; Index < cChunkData::SectionBlockCount; Index++) + { + UInt64 Value = a_Section.m_BlockTypes[Index]; + /* + if (Index % 2 == 0) + { + Value |= a_Section.m_BlockMetas[Index / 2] & 0x0f; + } + else + { + Value |= a_Section.m_BlockMetas[Index / 2] >> 4; + } + */ + Value &= Mask; // It shouldn't go out of bounds, but it's still worth being careful + + // Painful part where we write data into the long array. Based off of the normal code. + size_t BitPosition = Index * BitsPerEntry; + size_t FirstIndex = BitPosition / 64; + size_t SecondIndex = ((Index + 1) * BitsPerEntry - 1) / 64; + size_t BitOffset = BitPosition % 64; + + if (FirstIndex != CurrentlyWrittenIndex) + { + // Write the current data before modifiying it. + Packet.WriteBEUInt64(TempLong); + TempLong = 0; + CurrentlyWrittenIndex = FirstIndex; + } + + TempLong |= (Value << BitOffset); + + if (FirstIndex != SecondIndex) + { + // Part of the data is now in the second long; write the first one first + Packet.WriteBEUInt64(TempLong); + CurrentlyWrittenIndex = SecondIndex; + + TempLong = (Value >> (64 - BitOffset)); + } + } + // The last long will generally not be written + Packet.WriteBEUInt64(TempLong); + + // Write lighting: + Packet.WriteBuf(a_Section.m_BlockLight, sizeof(a_Section.m_BlockLight)); + if (m_Dimension == dimOverworld) + { + // Skylight is only sent in the overworld; the nether and end do not use it + Packet.WriteBuf(a_Section.m_BlockSkyLight, sizeof(a_Section.m_BlockSkyLight)); + } + } + ); + + // Write the biome data + for (size_t i = 0; i != BiomeDataSize; i++) + { + Packet.WriteBEUInt32(static_cast(m_BiomeData[i]) & 0xff); + } + + // Identify 1.9.4's tile entity list as empty + Packet.WriteVarInt32(0); + + AString PacketData; + Packet.ReadAll(PacketData); + Packet.CommitRead(); + + if (PacketData.size() >= 256) + { + if (!cProtocol_1_9_0::CompressPacket(PacketData, a_Data)) + { + ASSERT(!"Packet compression failed."); + a_Data.clear(); + return; + } + } + else + { + cByteBuffer Buffer(20); + AString PostData; + + Buffer.WriteVarInt32(static_cast(Packet.GetUsedSpace() + 1)); + Buffer.WriteVarInt32(0); + Buffer.ReadAll(PostData); + Buffer.CommitRead(); + + a_Data.clear(); + a_Data.reserve(PostData.size() + PacketData.size()); + a_Data.append(PostData.data(), PostData.size()); + a_Data.append(PacketData.data(), PacketData.size()); + } +} diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h index 2f9c950de..7011d3e15 100644 --- a/src/Protocol/ChunkDataSerializer.h +++ b/src/Protocol/ChunkDataSerializer.h @@ -21,16 +21,18 @@ protected: Serializations m_Serializations; - void Serialize47(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.8 + void Serialize47 (AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.8 void Serialize107(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.9 void Serialize110(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.9.4 + void Serialize393(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.13 public: enum { - RELEASE_1_8_0 = 47, + RELEASE_1_8_0 = 47, RELEASE_1_9_0 = 107, RELEASE_1_9_4 = 110, + RELEASE_1_13 = 393, } ; cChunkDataSerializer( diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 9bfa0f105..d0f4c6f53 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -12,6 +12,7 @@ #include "Protocol_1_10.h" #include "Protocol_1_11.h" #include "Protocol_1_12.h" +#include "Protocol_1_13.h" #include "Packetizer.h" #include "../ClientHandle.h" #include "../Root.h" @@ -1150,6 +1151,11 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema m_Protocol = new cProtocol_1_12_2(m_Client, ServerAddress, ServerPort, NextState); return true; } + case PROTO_VERSION_1_13: + { + m_Protocol = new cProtocol_1_13(m_Client, ServerAddress, ServerPort, NextState); + return true; + } default: { LOGD("Client \"%s\" uses an unsupported protocol (lengthed, version %u (0x%x))", diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 59cdfa6de..185793f55 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -18,9 +18,9 @@ // Adjust these if a new protocol is added or an old one is removed: -#define MCS_CLIENT_VERSIONS "1.8.x, 1.9.x, 1.10.x, 1.11.x, 1.12.x" -#define MCS_PROTOCOL_VERSIONS "47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340" -#define MCS_LATEST_PROTOCOL_VERSION 340 +#define MCS_CLIENT_VERSIONS "1.8.x, 1.9.x, 1.10.x, 1.11.x, 1.12.x, 1.13" +#define MCS_PROTOCOL_VERSIONS "47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393" +#define MCS_LATEST_PROTOCOL_VERSION 393 @@ -45,6 +45,7 @@ public: PROTO_VERSION_1_12 = 335, PROTO_VERSION_1_12_1 = 338, PROTO_VERSION_1_12_2 = 340, + PROTO_VERSION_1_13 = 393 }; cProtocolRecognizer(cClientHandle * a_Client); diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp new file mode 100644 index 000000000..87a7ff6da --- /dev/null +++ b/src/Protocol/Protocol_1_13.cpp @@ -0,0 +1,456 @@ + +// Protocol_1_13.cpp + +/* +Implements the 1.13 protocol classes: +- release 1.13 protocol (#393) +*/ + +#include "Globals.h" +#include "Protocol_1_13.h" +#include "ProtocolRecognizer.h" +#include "ChunkDataSerializer.h" +#include "Packetizer.h" + +#include "../Entities/Boat.h" +#include "../Entities/Minecart.h" +#include "../Entities/Pickup.h" +#include "../Entities/Player.h" +#include "../Entities/ItemFrame.h" +#include "../Entities/ArrowEntity.h" +#include "../Entities/FireworkEntity.h" +#include "../Entities/SplashPotionEntity.h" + +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "../Bindings/PluginManager.h" + + + + + +#define HANDLE_READ(ByteBuf, Proc, Type, Var) \ + Type Var; \ + if (!ByteBuf.Proc(Var))\ + {\ + return;\ + } + + + + + +#define HANDLE_PACKET_READ(ByteBuf, Proc, Type, Var) \ + Type Var; \ + { \ + if (!ByteBuf.Proc(Var)) \ + { \ + ByteBuf.CheckValid(); \ + return false; \ + } \ + ByteBuf.CheckValid(); \ + } + + + + + +cProtocol_1_13::cProtocol_1_13(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) : + Super(a_Client, a_ServerAddress, a_ServerPort, a_State) +{ +} + + + + + +UInt32 cProtocol_1_13::GetPacketID(ePacketType a_PacketType) +{ + switch (a_PacketType) + { + case pktAttachEntity: return 0x40; + case pktBlockChanges: return 0x0f; + case pktCameraSetTo: return 0x3c; + case pktChatRaw: return 0x0e; + case pktCollectEntity: return 0x4f; + case pktDestroyEntity: return 0x35; + case pktDisconnectDuringGame: return 0x1b; + case pktEditSign: return 0x2c; + case pktEntityEffect: return 0x53; + case pktEntityEquipment: return 0x42; + case pktEntityHeadLook: return 0x39; + case pktEntityLook: return 0x2a; + case pktEntityMeta: return 0x3f; + case pktEntityProperties: return 0x52; + case pktEntityRelMove: return 0x28; + case pktEntityRelMoveLook: return 0x29; + case pktEntityStatus: return 0x1c; + case pktEntityVelocity: return 0x41; + case pktExperience: return 0x43; + case pktExplosion: return 0x1e; + case pktGameMode: return 0x20; + case pktHeldItemChange: return 0x3d; + case pktInventorySlot: return 0x17; + case pktJoinGame: return 0x25; + case pktKeepAlive: return 0x21; + case pktMapData: return 0x26; + case pktParticleEffect: return 0x24; + case pktPlayerAbilities: return 0x2e; + case pktPlayerList: return 0x30; + case pktPlayerMaxSpeed: return 0x52; + case pktPlayerMoveLook: return 0x32; + case pktPluginMessage: return 0x19; + case pktRemoveEntityEffect: return 0x36; + case pktRespawn: return 0x38; + case pktScoreboardObjective: return 0x45; + case pktSoundEffect: return 0x1a; + case pktSoundParticleEffect: return 0x23; + case pktSpawnPosition: return 0x49; + case pktTabCompletionResults: return 0x10; + case pktTeleportEntity: return 0x50; + case pktTimeUpdate: return 0x4a; + case pktTitle: return 0x4b; + case pktUnloadChunk: return 0x1f; + case pktUpdateHealth: return 0x44; + case pktUpdateScore: return 0x48; + case pktUpdateSign: return GetPacketID(pktUpdateBlockEntity); + case pktUseBed: return 0x33; + case pktWindowClose: return 0x13; + case pktWindowItems: return 0x15; + case pktWindowOpen: return 0x14; + case pktWindowProperty: return 0x16; + default: return Super::GetPacketID(a_PacketType); + } +} + + + + + +bool cProtocol_1_13::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) +{ + if (m_State != 3) + { + return Super::HandlePacket(a_ByteBuffer, a_PacketType); + } + + // Game + switch (a_PacketType) + { + case 0x00: HandleConfirmTeleport(a_ByteBuffer); return true; + case 0x05: HandlePacketTabComplete(a_ByteBuffer); return true; + case 0x02: HandlePacketChatMessage(a_ByteBuffer); return true; + case 0x03: HandlePacketClientStatus(a_ByteBuffer); return true; + case 0x04: HandlePacketClientSettings(a_ByteBuffer); return true; + case 0x06: break; // Confirm transaction - not used in Cuberite + case 0x07: HandlePacketEnchantItem(a_ByteBuffer); return true; + case 0x08: HandlePacketWindowClick(a_ByteBuffer); return true; + case 0x09: HandlePacketWindowClose(a_ByteBuffer); return true; + case 0x0a: HandlePacketPluginMessage(a_ByteBuffer); return true; + case 0x0d: HandlePacketUseEntity(a_ByteBuffer); return true; + case 0x0e: HandlePacketKeepAlive(a_ByteBuffer); return true; + case 0x0f: HandlePacketPlayer(a_ByteBuffer); return true; + case 0x10: HandlePacketPlayerPos(a_ByteBuffer); return true; + case 0x11: HandlePacketPlayerPosLook(a_ByteBuffer); return true; + case 0x12: HandlePacketPlayerLook(a_ByteBuffer); return true; + case 0x13: HandlePacketVehicleMove(a_ByteBuffer); return true; + case 0x14: HandlePacketBoatSteer(a_ByteBuffer); return true; + case 0x15: break; // Pick item - not yet implemented + case 0x16: break; // Craft Recipe Request - not yet implemented + case 0x17: HandlePacketPlayerAbilities(a_ByteBuffer); return true; + case 0x18: HandlePacketBlockDig(a_ByteBuffer); return true; + case 0x19: HandlePacketEntityAction(a_ByteBuffer); return true; + case 0x1a: HandlePacketSteerVehicle(a_ByteBuffer); return true; + case 0x1b: HandlePacketCraftingBookData(a_ByteBuffer); return true; + case 0x1d: break; // Resource pack status - not yet implemented + case 0x1e: HandlePacketAdvancementTab(a_ByteBuffer); return true; + case 0x21: HandlePacketSlotSelect(a_ByteBuffer); return true; + case 0x24: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true; + case 0x26: HandlePacketUpdateSign(a_ByteBuffer); return true; + case 0x27: HandlePacketAnimation(a_ByteBuffer); return true; + case 0x28: HandlePacketSpectate(a_ByteBuffer); return true; + case 0x29: HandlePacketBlockPlace(a_ByteBuffer); return true; + case 0x2a: HandlePacketUseItem(a_ByteBuffer); return true; + } + + return Super::HandlePacket(a_ByteBuffer, a_PacketType); +} + + + + + +void cProtocol_1_13::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) +{ + cServer * Server = cRoot::Get()->GetServer(); + AString ServerDescription = Server->GetDescription(); + auto NumPlayers = static_cast(Server->GetNumPlayers()); + auto MaxPlayers = static_cast(Server->GetMaxPlayers()); + AString Favicon = Server->GetFaviconData(); + cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon); + + // Version: + Json::Value Version; + Version["name"] = "Cuberite 1.13"; + Version["protocol"] = cProtocolRecognizer::PROTO_VERSION_1_13; + + // Players: + Json::Value Players; + Players["online"] = NumPlayers; + Players["max"] = MaxPlayers; + // TODO: Add "sample" + + // Description: + Json::Value Description; + Description["text"] = ServerDescription.c_str(); + + // Create the response: + Json::Value ResponseValue; + ResponseValue["version"] = Version; + ResponseValue["players"] = Players; + ResponseValue["description"] = Description; + m_Client->ForgeAugmentServerListPing(ResponseValue); + if (!Favicon.empty()) + { + ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str()); + } + + // Serialize the response into a packet: + Json::FastWriter Writer; + cPacketizer Pkt(*this, pktStatusResponse); + Pkt.WriteString(Writer.write(ResponseValue)); +} + + + + + +void cProtocol_1_13::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) +{ + HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Channel); + + // If the plugin channel is recognized vanilla, handle it directly: + if (Channel.substr(0, 15) == "minecraft:brand") + { + HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Brand); + m_Client->SetClientBrand(Brand); + + // Send back our brand, including the length: + SendPluginMessage("minecraft:brand", "\x08""Cuberite"); + return; + } + + // Read the plugin message and relay to clienthandle: + AString Data; + VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds + m_Client->HandlePluginMessage(Channel, Data); +} + + + + + +void cProtocol_1_13::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, pktBlockChange); + Pkt.WritePosition64(a_BlockX, a_BlockY, a_BlockZ); + Pkt.WriteVarInt32(static_cast(a_BlockType)); // TODO: Palette +} + + + + + +void cProtocol_1_13::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, pktBlockChanges); + Pkt.WriteBEInt32(a_ChunkX); + Pkt.WriteBEInt32(a_ChunkZ); + Pkt.WriteVarInt32(static_cast(a_Changes.size())); + for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) + { + Int16 Coords = static_cast(itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12)); + Pkt.WriteBEInt16(Coords); + Pkt.WriteVarInt32(static_cast(itr->m_BlockType)); // TODO: Palette + } // for itr - a_Changes[] +} + + + + + +void cProtocol_1_13::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + ASSERT(m_State == 3); // In game mode? + + const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_13, a_ChunkX, a_ChunkZ); + cCSLock Lock(m_CSPacket); + SendData(ChunkData.data(), ChunkData.size()); +} + + + + + +void cProtocol_1_13::SendMapData(const cMap & a_Map, int a_DataStartX, int a_DataStartY) +{ + // TODO +} + + + + + +void cProtocol_1_13::SendPaintingSpawn(const cPainting & a_Painting) +{ + // TODO +} + + + + + +void cProtocol_1_13::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_ParticleAmount) +{ + // TODO +} + + + + + +void cProtocol_1_13::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array a_Data) +{ + // TODO +} + + + + + +void cProtocol_1_13::SendPluginMessage(const AString & a_Channel, const AString & a_Message) +{ + // TODO +} + + + + + +void cProtocol_1_13::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) +{ + // TODO +} + + + + + +void cProtocol_1_13::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) +{ + // TODO +} + + + + + +void cProtocol_1_13::SendStatistics(const cStatManager & a_Manager) +{ + // TODO +} + + + + + +void cProtocol_1_13::SendTabCompletionResults(const AStringVector & a_Results) +{ + // TODO +} + + + + + +void cProtocol_1_13::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity) +{ + // TODO +} + + + + + +bool cProtocol_1_13::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes) +{ + HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt16, Int16, ItemType); + if (ItemType == -1) + { + // The item is empty, no more data follows + a_Item.Empty(); + return true; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt8, Int8, ItemCount); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = 0; // o no, no more damage in 1.13 + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + AString Metadata; + if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) + { + // No metadata + return true; + } + + ParseItemMetadata(a_Item, Metadata); + return true; +} + + + + + +void cProtocol_1_13::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; + } + + // Normal item + // TODO: use new item ids + a_Pkt.WriteBEInt16(ItemType); + a_Pkt.WriteBEInt8(a_Item.m_ItemCount); + + // TODO: NBT + a_Pkt.WriteBEInt8(0); +} + + + + + +void cProtocol_1_13::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity) +{ + // TODO: Investigate +} diff --git a/src/Protocol/Protocol_1_13.h b/src/Protocol/Protocol_1_13.h new file mode 100644 index 000000000..6eace0567 --- /dev/null +++ b/src/Protocol/Protocol_1_13.h @@ -0,0 +1,58 @@ + +// Protocol_1_13.h + +/* +Declares the 1.13 protocol classes: + - cProtocol_1_13 + - release 1.13 protocol (#393) +(others may be added later in the future for the 1.13 release series) +*/ + + + + + +#pragma once + +#include "Protocol_1_12.h" + + + + + +class cProtocol_1_13 : + public cProtocol_1_12_2 +{ + typedef cProtocol_1_12_2 Super; + +public: + cProtocol_1_13(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); + +protected: + + virtual UInt32 GetPacketID(ePacketType a_PacketType) override; + + // Packet receiving: + virtual bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) override; + virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override; + virtual void HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) override; + + // Packet sending: + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY) override; + virtual void SendPaintingSpawn (const cPainting & a_Painting) override; + 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_ParticleAmount) override; + virtual void SendParticleEffect (const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array a_Data) override; + virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; + virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; + virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; + virtual void SendStatistics (const cStatManager & a_Manager) override; + virtual void SendTabCompletionResults (const AStringVector & a_Results) override; + virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override; + + virtual bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes) override; + virtual void WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) override; + virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity) override; +}; diff --git a/src/Protocol/Protocol_1_9.h b/src/Protocol/Protocol_1_9.h index 99d65c77a..681035d80 100644 --- a/src/Protocol/Protocol_1_9.h +++ b/src/Protocol/Protocol_1_9.h @@ -248,7 +248,7 @@ protected: eHand HandIntToEnum(Int32 a_Hand); /** Writes the item data into a packet. */ - void WriteItem(cPacketizer & a_Pkt, const cItem & a_Item); + virtual void WriteItem(cPacketizer & a_Pkt, const cItem & a_Item); /** Writes the metadata for the specified entity, not including the terminating 0xff. */ virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity); -- cgit v1.2.3